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