chiark / gitweb /
7bf8812676d214644e85c5500e832dd9288e7d35
[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 *buf, size_t buflen, uint8_t *overload,
64                          uint8_t *message_type, dhcp_option_cb_t cb,
65                          void *user_data)
66 {
67         const uint8_t *code = buf;
68         const uint8_t *len;
69
70         while (buflen > 0) {
71                 switch (*code) {
72                 case DHCP_OPTION_PAD:
73                         buflen -= 1;
74                         code++;
75                         break;
76
77                 case DHCP_OPTION_END:
78                         return 0;
79
80                 case DHCP_OPTION_MESSAGE_TYPE:
81                         if (buflen < 3)
82                                 return -ENOBUFS;
83                         buflen -= 3;
84
85                         len = code + 1;
86                         if (*len != 1)
87                                 return -EINVAL;
88
89                         if (message_type)
90                                 *message_type = *(len + 1);
91
92                         code += 3;
93
94                         break;
95
96                 case DHCP_OPTION_OVERLOAD:
97                         if (buflen < 3)
98                                 return -ENOBUFS;
99                         buflen -= 3;
100
101                         len = code + 1;
102                         if (*len != 1)
103                                 return -EINVAL;
104
105                         if (overload)
106                                 *overload = *(len + 1);
107
108                         code += 3;
109
110                         break;
111
112                 default:
113                         if (buflen < 3)
114                                 return -ENOBUFS;
115
116                         len = code + 1;
117
118                         if (buflen < (size_t)*len + 2)
119                                 return -EINVAL;
120                         buflen -= *len + 2;
121
122                         if (cb)
123                                 cb(*code, *len, len + 1, user_data);
124
125                         code += *len + 2;
126
127                         break;
128                 }
129         }
130
131         if (buflen)
132                 return -EINVAL;
133
134         return 0;
135 }
136
137 int dhcp_option_parse(DHCPMessage *message, size_t len,
138                       dhcp_option_cb_t cb, void *user_data)
139 {
140         uint8_t overload = 0;
141         uint8_t message_type = 0;
142         int res;
143
144         if (!message)
145                 return -EINVAL;
146
147         if (len < sizeof(DHCPMessage))
148                 return -EINVAL;
149
150         len -= sizeof(DHCPMessage);
151
152         res = parse_options(message->options, len, &overload, &message_type,
153                             cb, user_data);
154         if (res < 0)
155                 return res;
156
157         if (overload & DHCP_OVERLOAD_FILE) {
158                 res = parse_options(message->file, sizeof(message->file),
159                                 NULL, &message_type, cb, user_data);
160                 if (res < 0)
161                         return res;
162         }
163
164         if (overload & DHCP_OVERLOAD_SNAME) {
165                 res = parse_options(message->sname, sizeof(message->sname),
166                                 NULL, &message_type, cb, user_data);
167                 if (res < 0)
168                         return res;
169         }
170
171         if (message_type)
172                 return message_type;
173
174         return -ENOMSG;
175 }