Make sure we have a show_usage function prototype
[oweals/busybox.git] / networking / udhcp / options.c
1 /* 
2  * options.c -- DHCP server option packet tools 
3  * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
4  */
5  
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "debug.h"
11 #include "dhcpd.h"
12 #include "files.h"
13 #include "options.h"
14 #include "leases.h"
15
16 #include "config.h"
17
18 /* supported options are easily added here */
19 struct dhcp_option options[] = {
20         /* name[10]     flags                                   code */
21 #ifdef CONFIG_FEATURE_UDHCPC_IP
22         {"subnet",      OPTION_IP | OPTION_REQ | OPTION_PREFIX, 0x01},
23 #else
24         {"subnet",      OPTION_IP | OPTION_REQ,                 0x01},
25 #endif
26         {"timezone",    OPTION_S32,                             0x02},
27         {"router",      OPTION_IP | OPTION_LIST | OPTION_REQ,   0x03},
28         {"timesvr",     OPTION_IP | OPTION_LIST,                0x04},
29         {"namesvr",     OPTION_IP | OPTION_LIST,                0x05},
30         {"dns",         OPTION_IP | OPTION_LIST | OPTION_REQ,   0x06},
31         {"logsvr",      OPTION_IP | OPTION_LIST,                0x07},
32         {"cookiesvr",   OPTION_IP | OPTION_LIST,                0x08},
33         {"lprsvr",      OPTION_IP | OPTION_LIST,                0x09},
34         {"hostname",    OPTION_STRING | OPTION_REQ,             0x0c},
35         {"bootsize",    OPTION_U16,                             0x0d},
36         {"domain",      OPTION_STRING | OPTION_REQ,             0x0f},
37         {"swapsvr",     OPTION_IP,                              0x10},
38         {"rootpath",    OPTION_STRING,                          0x11},
39         {"ipttl",       OPTION_U8,                              0x17},
40         {"mtu",         OPTION_U16,                             0x1a},
41         {"broadcast",   OPTION_IP | OPTION_REQ,                 0x1c},
42         {"ntpsrv",      OPTION_IP | OPTION_LIST,                0x2a},
43         {"wins",        OPTION_IP | OPTION_LIST,                0x2c},
44         {"requestip",   OPTION_IP,                              0x32},
45         {"lease",       OPTION_U32,                             0x33},
46         {"dhcptype",    OPTION_U8,                              0x35},
47         {"serverid",    OPTION_IP,                              0x36},
48         {"message",     OPTION_STRING,                          0x38},
49         {"tftp",        OPTION_STRING,                          0x42},
50         {"bootfile",    OPTION_STRING,                          0x43},
51         {"",            0x00,                           0x00}
52 };
53
54 /* Lengths of the different option types */
55 int option_lengths[] = {
56         [OPTION_IP] =           4,
57         [OPTION_IP_PAIR] =      8,
58         [OPTION_BOOLEAN] =      1,
59         [OPTION_STRING] =       1,
60         [OPTION_U8] =           1,
61         [OPTION_U16] =          2,
62         [OPTION_S16] =          2,
63         [OPTION_U32] =          4,
64         [OPTION_S32] =          4
65 };
66
67
68 /* get an option with bounds checking (warning, not aligned). */
69 unsigned char *get_option(struct dhcpMessage *packet, int code)
70 {
71         int i, length;
72         unsigned char *optionptr;
73         int over = 0, done = 0, curr = OPTION_FIELD;
74         
75         optionptr = packet->options;
76         i = 0;
77         length = 308;
78         while (!done) {
79                 if (i >= length) {
80                         LOG(LOG_WARNING, "bogus packet, option fields too long.");
81                         return NULL;
82                 }
83                 if (optionptr[i + OPT_CODE] == code) {
84                         if (i + 1 + optionptr[i + OPT_LEN] >= length) {
85                                 LOG(LOG_WARNING, "bogus packet, option fields too long.");
86                                 return NULL;
87                         }
88                         return optionptr + i + 2;
89                 }                       
90                 switch (optionptr[i + OPT_CODE]) {
91                 case DHCP_PADDING:
92                         i++;
93                         break;
94                 case DHCP_OPTION_OVER:
95                         if (i + 1 + optionptr[i + OPT_LEN] >= length) {
96                                 LOG(LOG_WARNING, "bogus packet, option fields too long.");
97                                 return NULL;
98                         }
99                         over = optionptr[i + 3];
100                         i += optionptr[OPT_LEN] + 2;
101                         break;
102                 case DHCP_END:
103                         if (curr == OPTION_FIELD && over & FILE_FIELD) {
104                                 optionptr = packet->file;
105                                 i = 0;
106                                 length = 128;
107                                 curr = FILE_FIELD;
108                         } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
109                                 optionptr = packet->sname;
110                                 i = 0;
111                                 length = 64;
112                                 curr = SNAME_FIELD;
113                         } else done = 1;
114                         break;
115                 default:
116                         i += optionptr[OPT_LEN + i] + 2;
117                 }
118         }
119         return NULL;
120 }
121
122
123 /* return the position of the 'end' option (no bounds checking) */
124 int end_option(unsigned char *optionptr) 
125 {
126         int i = 0;
127         
128         while (optionptr[i] != DHCP_END) {
129                 if (optionptr[i] == DHCP_PADDING) i++;
130                 else i += optionptr[i + OPT_LEN] + 2;
131         }
132         return i;
133 }
134
135
136 /* add an option string to the options (an option string contains an option code,
137  * length, then data) */
138 int add_option_string(unsigned char *optionptr, unsigned char *string)
139 {
140         int end = end_option(optionptr);
141         
142         /* end position + string length + option code/length + end option */
143         if (end + string[OPT_LEN] + 2 + 1 >= 308) {
144                 LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]);
145                 return 0;
146         }
147         DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
148         memcpy(optionptr + end, string, string[OPT_LEN] + 2);
149         optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
150         return string[OPT_LEN] + 2;
151 }
152
153
154 /* add a one to four byte option to a packet */
155 int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data)
156 {
157         char length = 0;
158         int i;
159         unsigned char option[2 + 4];
160         unsigned char *u8;
161         u_int16_t *u16;
162         u_int32_t *u32;
163         u_int32_t aligned;
164         u8 = (unsigned char *) &aligned;
165         u16 = (u_int16_t *) &aligned;
166         u32 = &aligned;
167
168         for (i = 0; options[i].code; i++)
169                 if (options[i].code == code) {
170                         length = option_lengths[options[i].flags & TYPE_MASK];
171                 }
172                 
173         if (!length) {
174                 DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
175                 return 0;
176         }
177         
178         option[OPT_CODE] = code;
179         option[OPT_LEN] = length;
180
181         switch (length) {
182                 case 1: *u8 =  data; break;
183                 case 2: *u16 = data; break;
184                 case 4: *u32 = data; break;
185         }
186         memcpy(option + 2, &aligned, length);
187         return add_option_string(optionptr, option);
188 }
189
190
191 /* find option 'code' in opt_list */
192 struct option_set *find_option(struct option_set *opt_list, char code)
193 {
194         while (opt_list && opt_list->data[OPT_CODE] < code)
195                 opt_list = opt_list->next;
196
197         if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
198         else return NULL;
199 }
200
201
202 /* add an option to the opt_list */
203 void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length)
204 {
205         struct option_set *existing, *new, **curr;
206
207         /* add it to an existing option */
208         if ((existing = find_option(*opt_list, option->code))) {
209                 DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name);
210                 if (option->flags & OPTION_LIST) {
211                         if (existing->data[OPT_LEN] + length <= 255) {
212                                 existing->data = realloc(existing->data, 
213                                                 existing->data[OPT_LEN] + length + 2);
214                                 memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
215                                 existing->data[OPT_LEN] += length;
216                         } /* else, ignore the data, we could put this in a second option in the future */
217                 } /* else, ignore the new data */
218         } else {
219                 DEBUG(LOG_INFO, "Attaching option %s to list", option->name);
220                 
221                 /* make a new option */
222                 new = malloc(sizeof(struct option_set));
223                 new->data = malloc(length + 2);
224                 new->data[OPT_CODE] = option->code;
225                 new->data[OPT_LEN] = length;
226                 memcpy(new->data + 2, buffer, length);
227                 
228                 curr = opt_list;
229                 while (*curr && (*curr)->data[OPT_CODE] < option->code)
230                         curr = &(*curr)->next;
231                         
232                 new->next = *curr;
233                 *curr = new;            
234         }
235 }