]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp-option.c
sd-dhcp-client: move magic cookie into DHCPMessage struct
[thirdparty/systemd.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 **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))
153 return -EINVAL;
154
155 len -= sizeof(DHCPMessage);
156
157 res = parse_options(opt, len, &overload, &message_type,
158 cb, user_data);
159 if (res < 0)
160 return res;
161
162 if (overload & DHCP_OVERLOAD_FILE) {
163 res = parse_options(message->file, sizeof(message->file),
164 NULL, &message_type, cb, user_data);
165 if (res < 0)
166 return res;
167 }
168
169 if (overload & DHCP_OVERLOAD_SNAME) {
170 res = parse_options(message->sname, sizeof(message->sname),
171 NULL, &message_type, cb, user_data);
172 if (res < 0)
173 return res;
174 }
175
176 if (message_type)
177 return message_type;
178
179 return -ENOMSG;
180 }