Implement optional syslog logging using ordinary
[oweals/busybox.git] / networking / udhcp / options.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * options.c -- DHCP server option packet tools
4  * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
5  */
6
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "common.h"
11 #include "dhcpd.h"
12 #include "options.h"
13 #include "files.h"
14
15
16 /* supported options are easily added here */
17 struct dhcp_option dhcp_options[] = {
18         /* name[10]     flags                                   code */
19         {"subnet",      OPTION_IP | OPTION_REQ,                 0x01},
20         {"timezone",    OPTION_S32,                             0x02},
21         {"router",      OPTION_IP | OPTION_LIST | OPTION_REQ,   0x03},
22         {"timesvr",     OPTION_IP | OPTION_LIST,                0x04},
23         {"namesvr",     OPTION_IP | OPTION_LIST,                0x05},
24         {"dns",         OPTION_IP | OPTION_LIST | OPTION_REQ,   0x06},
25         {"logsvr",      OPTION_IP | OPTION_LIST,                0x07},
26         {"cookiesvr",   OPTION_IP | OPTION_LIST,                0x08},
27         {"lprsvr",      OPTION_IP | OPTION_LIST,                0x09},
28         {"hostname",    OPTION_STRING | OPTION_REQ,             0x0c},
29         {"bootsize",    OPTION_U16,                             0x0d},
30         {"domain",      OPTION_STRING | OPTION_REQ,             0x0f},
31         {"swapsvr",     OPTION_IP,                              0x10},
32         {"rootpath",    OPTION_STRING,                          0x11},
33         {"ipttl",       OPTION_U8,                              0x17},
34         {"mtu",         OPTION_U16,                             0x1a},
35         {"broadcast",   OPTION_IP | OPTION_REQ,                 0x1c},
36         {"nisdomain",   OPTION_STRING | OPTION_REQ,             0x28},
37         {"nissrv",      OPTION_IP | OPTION_LIST | OPTION_REQ,   0x29},
38         {"ntpsrv",      OPTION_IP | OPTION_LIST | OPTION_REQ,   0x2a},
39         {"wins",        OPTION_IP | OPTION_LIST,                0x2c},
40         {"requestip",   OPTION_IP,                              0x32},
41         {"lease",       OPTION_U32,                             0x33},
42         {"dhcptype",    OPTION_U8,                              0x35},
43         {"serverid",    OPTION_IP,                              0x36},
44         {"message",     OPTION_STRING,                          0x38},
45         {"tftp",        OPTION_STRING,                          0x42},
46         {"bootfile",    OPTION_STRING,                          0x43},
47         {"",            0x00,                           0x00}
48 };
49
50 /* Lengths of the different option types */
51 int option_lengths[] = {
52         [OPTION_IP] =           4,
53         [OPTION_IP_PAIR] =      8,
54         [OPTION_BOOLEAN] =      1,
55         [OPTION_STRING] =       1,
56         [OPTION_U8] =           1,
57         [OPTION_U16] =          2,
58         [OPTION_S16] =          2,
59         [OPTION_U32] =          4,
60         [OPTION_S32] =          4
61 };
62
63
64 /* get an option with bounds checking (warning, not aligned). */
65 uint8_t *get_option(struct dhcpMessage *packet, int code)
66 {
67         int i, length;
68         uint8_t *optionptr;
69         int over = 0, done = 0, curr = OPTION_FIELD;
70
71         optionptr = packet->options;
72         i = 0;
73         length = 308;
74         while (!done) {
75                 if (i >= length) {
76                         bb_error_msg("Bogus packet, option fields too long");
77                         return NULL;
78                 }
79                 if (optionptr[i + OPT_CODE] == code) {
80                         if (i + 1 + optionptr[i + OPT_LEN] >= length) {
81                                 bb_error_msg("Bogus packet, option fields too long");
82                                 return NULL;
83                         }
84                         return optionptr + i + 2;
85                 }
86                 switch (optionptr[i + OPT_CODE]) {
87                 case DHCP_PADDING:
88                         i++;
89                         break;
90                 case DHCP_OPTION_OVER:
91                         if (i + 1 + optionptr[i + OPT_LEN] >= length) {
92                                 bb_error_msg("Bogus packet, option fields too long");
93                                 return NULL;
94                         }
95                         over = optionptr[i + 3];
96                         i += optionptr[OPT_LEN] + 2;
97                         break;
98                 case DHCP_END:
99                         if (curr == OPTION_FIELD && over & FILE_FIELD) {
100                                 optionptr = packet->file;
101                                 i = 0;
102                                 length = 128;
103                                 curr = FILE_FIELD;
104                         } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
105                                 optionptr = packet->sname;
106                                 i = 0;
107                                 length = 64;
108                                 curr = SNAME_FIELD;
109                         } else done = 1;
110                         break;
111                 default:
112                         i += optionptr[OPT_LEN + i] + 2;
113                 }
114         }
115         return NULL;
116 }
117
118
119 /* return the position of the 'end' option (no bounds checking) */
120 int end_option(uint8_t *optionptr)
121 {
122         int i = 0;
123
124         while (optionptr[i] != DHCP_END) {
125                 if (optionptr[i] == DHCP_PADDING) i++;
126                 else i += optionptr[i + OPT_LEN] + 2;
127         }
128         return i;
129 }
130
131
132 /* add an option string to the options (an option string contains an option code,
133  * length, then data) */
134 int add_option_string(uint8_t *optionptr, uint8_t *string)
135 {
136         int end = end_option(optionptr);
137
138         /* end position + string length + option code/length + end option */
139         if (end + string[OPT_LEN] + 2 + 1 >= 308) {
140                 bb_error_msg("Option 0x%02x did not fit into the packet",
141                                 string[OPT_CODE]);
142                 return 0;
143         }
144         DEBUG("adding option 0x%02x", string[OPT_CODE]);
145         memcpy(optionptr + end, string, string[OPT_LEN] + 2);
146         optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
147         return string[OPT_LEN] + 2;
148 }
149
150
151 /* add a one to four byte option to a packet */
152 int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
153 {
154         struct dhcp_option *dh;
155
156         for (dh=dhcp_options; dh->code; dh++) {
157                 if (dh->code == code) {
158                         uint8_t option[6], len;
159
160                         option[OPT_CODE] = code;
161                         len = option_lengths[dh->flags & TYPE_MASK];
162                         option[OPT_LEN] = len;
163                         if (BB_BIG_ENDIAN) data <<= 8 * (4 - len);
164                         /* This memcpy is for broken processors which can't
165                          * handle a simple unaligned 32-bit assignment */
166                         memcpy(&option[OPT_DATA], &data, 4);
167                         return add_option_string(optionptr, option);
168                 }
169         }
170
171         bb_error_msg("Could not add option 0x%02x", code);
172         return 0;
173 }