]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/dhcp-option.c
sd-network: add new library
[thirdparty/systemd.git] / src / libsystemd-network / dhcp-option.c
CommitLineData
524cf458
PF
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
29int 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
67static 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
141int 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}