chiark / gitweb /
24f678c100b41b39fe84b3cbd0cd9ba71e1e009f
[elogind.git] / src / libsystemd-network / 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 options[], size_t size, size_t *offset,
30                        uint8_t code, size_t optlen, const void *optval) {
31         assert(options);
32         assert(offset);
33
34         if (code != DHCP_OPTION_END)
35                 /* always make sure there is space for an END option */
36                 size --;
37
38         switch (code) {
39
40         case DHCP_OPTION_PAD:
41         case DHCP_OPTION_END:
42                 if (size - *offset < 1)
43                         return -ENOBUFS;
44
45                 options[*offset] = code;
46                 *offset += 1;
47                 break;
48
49         default:
50                 if (size - *offset < optlen + 2)
51                         return -ENOBUFS;
52
53                 assert(optval);
54
55                 options[*offset] = code;
56                 options[*offset + 1] = optlen;
57                 memcpy(&options[*offset + 2], optval, optlen);
58
59                 *offset += optlen + 2;
60
61                 break;
62         }
63
64         return 0;
65 }
66
67 static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload,
68                          uint8_t *message_type, dhcp_option_cb_t cb,
69                          void *user_data) {
70         uint8_t code, len;
71         size_t offset = 0;
72
73         while (offset < buflen) {
74                 switch (options[offset]) {
75                 case DHCP_OPTION_PAD:
76                         offset++;
77
78                         break;
79
80                 case DHCP_OPTION_END:
81                         return 0;
82
83                 case DHCP_OPTION_MESSAGE_TYPE:
84                         if (buflen < offset + 3)
85                                 return -ENOBUFS;
86
87                         len = options[++offset];
88                         if (len != 1)
89                                 return -EINVAL;
90
91                         if (message_type)
92                                 *message_type = options[++offset];
93                         else
94                                 offset++;
95
96                         offset++;
97
98                         break;
99
100                 case DHCP_OPTION_OVERLOAD:
101                         if (buflen < offset + 3)
102                                 return -ENOBUFS;
103
104                         len = options[++offset];
105                         if (len != 1)
106                                 return -EINVAL;
107
108                         if (overload)
109                                 *overload = options[++offset];
110                         else
111                                 offset++;
112
113                         offset++;
114
115                         break;
116
117                 default:
118                         if (buflen < offset + 3)
119                                 return -ENOBUFS;
120
121                         code = options[offset];
122                         len = options[++offset];
123
124                         if (buflen < ++offset + len)
125                                 return -EINVAL;
126
127                         if (cb)
128                                 cb(code, len, &options[offset], user_data);
129
130                         offset += len;
131
132                         break;
133                 }
134         }
135
136         if (offset < buflen)
137                 return -EINVAL;
138
139         return 0;
140 }
141
142 int dhcp_option_parse(DHCPMessage *message, size_t len,
143                       dhcp_option_cb_t cb, void *user_data) {
144         uint8_t overload = 0;
145         uint8_t message_type = 0;
146         int r;
147
148         if (!message)
149                 return -EINVAL;
150
151         if (len < sizeof(DHCPMessage))
152                 return -EINVAL;
153
154         len -= sizeof(DHCPMessage);
155
156         r = parse_options(message->options, len, &overload, &message_type,
157                           cb, user_data);
158         if (r < 0)
159                 return r;
160
161         if (overload & DHCP_OVERLOAD_FILE) {
162                 r = parse_options(message->file, sizeof(message->file),
163                                 NULL, &message_type, cb, user_data);
164                 if (r < 0)
165                         return r;
166         }
167
168         if (overload & DHCP_OVERLOAD_SNAME) {
169                 r = parse_options(message->sname, sizeof(message->sname),
170                                 NULL, &message_type, cb, user_data);
171                 if (r < 0)
172                         return r;
173         }
174
175         if (message_type)
176                 return message_type;
177
178         return -ENOMSG;
179 }