chiark / gitweb /
dhcp: Add timeout and main loop support
[elogind.git] / src / libsystemd-dhcp / dhcp-option.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2013 Intel Corporation. All rights reserved.
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdint.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stdio.h>
26
27 #include "dhcp-internal.h"
28
29 int dhcp_option_append(uint8_t **buf, size_t *buflen, uint8_t code,
30                        size_t optlen, const void *optval)
31 {
32         if (!buf || !buflen)
33                 return -EINVAL;
34
35         switch (code) {
36
37         case DHCP_OPTION_PAD:
38         case DHCP_OPTION_END:
39                 if (*buflen < 1)
40                         return -ENOBUFS;
41
42                 (*buf)[0] = code;
43                 *buf += 1;
44                 *buflen -= 1;
45                 break;
46
47         default:
48                 if (*buflen < optlen + 2)
49                         return -ENOBUFS;
50
51                 if (!optval)
52                         return -EINVAL;
53
54                 (*buf)[0] = code;
55                 (*buf)[1] = optlen;
56                 memcpy(&(*buf)[2], optval, optlen);
57
58                 *buf += optlen + 2;
59                 *buflen -= (optlen + 2);
60
61                 break;
62         }
63
64         return 0;
65 }
66
67 static int parse_options(const uint8_t *buf, size_t buflen, uint8_t *overload,
68                          uint8_t *message_type, dhcp_option_cb_t cb,
69                          void *user_data)
70 {
71         const uint8_t *code = buf;
72         const uint8_t *len;
73
74         while (buflen > 0) {
75                 switch (*code) {
76                 case DHCP_OPTION_PAD:
77                         buflen -= 1;
78                         code++;
79                         break;
80
81                 case DHCP_OPTION_END:
82                         return 0;
83
84                 case DHCP_OPTION_MESSAGE_TYPE:
85                         if (buflen < 3)
86                                 return -ENOBUFS;
87                         buflen -= 3;
88
89                         len = code + 1;
90                         if (*len != 1)
91                                 return -EINVAL;
92
93                         if (message_type)
94                                 *message_type = *(len + 1);
95
96                         code += 3;
97
98                         break;
99
100                 case DHCP_OPTION_OVERLOAD:
101                         if (buflen < 3)
102                                 return -ENOBUFS;
103                         buflen -= 3;
104
105                         len = code + 1;
106                         if (*len != 1)
107                                 return -EINVAL;
108
109                         if (overload)
110                                 *overload = *(len + 1);
111
112                         code += 3;
113
114                         break;
115
116                 default:
117                         if (buflen < 3)
118                                 return -ENOBUFS;
119
120                         len = code + 1;
121
122                         if (buflen < (size_t)*len + 2)
123                                 return -EINVAL;
124                         buflen -= *len + 2;
125
126                         if (cb)
127                                 cb(*code, *len, len + 1, user_data);
128
129                         code += *len + 2;
130
131                         break;
132                 }
133         }
134
135         if (buflen)
136                 return -EINVAL;
137
138         return 0;
139 }
140
141 int dhcp_option_parse(DHCPMessage *message, size_t len,
142                       dhcp_option_cb_t cb, void *user_data)
143 {
144         uint8_t overload = 0;
145         uint8_t message_type = 0;
146         uint8_t *opt = (uint8_t *)(message + 1);
147         int res;
148
149         if (!message)
150                 return -EINVAL;
151
152         if (len < sizeof(DHCPMessage) + 4)
153                 return -EINVAL;
154
155         len -= sizeof(DHCPMessage) + 4;
156
157         if (opt[0] != 0x63 && opt[1] != 0x82 && opt[2] != 0x53 &&
158                         opt[3] != 0x63)
159                 return -EINVAL;
160
161         res = parse_options(&opt[4], len, &overload, &message_type,
162                         cb, user_data);
163         if (res < 0)
164                 return res;
165
166         if (overload & DHCP_OVERLOAD_FILE) {
167                 res = parse_options(message->file, sizeof(message->file),
168                                 NULL, &message_type, cb, user_data);
169                 if (res < 0)
170                         return res;
171         }
172
173         if (overload & DHCP_OVERLOAD_SNAME) {
174                 res = parse_options(message->sname, sizeof(message->sname),
175                                 NULL, &message_type, cb, user_data);
176                 if (res < 0)
177                         return res;
178         }
179
180         if (message_type)
181                 return message_type;
182
183         return -ENOMSG;
184 }