chiark / gitweb /
sd-dhcp-client: move magic cookie into DHCPMessage struct
[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 **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 }