udhcp: shorten mac len from 16 to 6 in lease file
[oweals/busybox.git] / networking / udhcp / script.c
1 /* vi: set sw=4 ts=4: */
2 /* script.c
3  *
4  * Functions to call the DHCP client notification scripts
5  *
6  * Russ Dill <Russ.Dill@asu.edu> July 2001
7  *
8  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9  */
10
11 #include "common.h"
12 #include "dhcpc.h"
13 #include "options.h"
14
15
16 /* get a rough idea of how long an option will be (rounding up...) */
17 static const uint8_t max_option_length[] = {
18         [OPTION_IP] =           sizeof("255.255.255.255 "),
19         [OPTION_IP_PAIR] =      sizeof("255.255.255.255 ") * 2,
20         [OPTION_STRING] =       1,
21 #if ENABLE_FEATURE_UDHCP_RFC3397
22         [OPTION_STR1035] =      1,
23 #endif
24         [OPTION_BOOLEAN] =      sizeof("yes "),
25         [OPTION_U8] =           sizeof("255 "),
26         [OPTION_U16] =          sizeof("65535 "),
27         [OPTION_S16] =          sizeof("-32768 "),
28         [OPTION_U32] =          sizeof("4294967295 "),
29         [OPTION_S32] =          sizeof("-2147483684 "),
30 };
31
32
33 static inline int upper_length(int length, int opt_index)
34 {
35         return max_option_length[opt_index] *
36                 (length / dhcp_option_lengths[opt_index]);
37 }
38
39
40 /* note: ip is a pointer to an IP in network order, possibly misaliged */
41 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
42 {
43         return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
44 }
45
46
47 /* really simple implementation, just count the bits */
48 static int mton(uint32_t mask)
49 {
50         int i = 0;
51         mask = ntohl(mask); /* 111110000-like bit pattern */
52         while (mask) {
53                 i++;
54                 mask <<= 1;
55         }
56         return i;
57 }
58
59
60 /* Allocate and fill with the text of option 'option'. */
61 static char *alloc_fill_opts(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name)
62 {
63         int len, type, optlen;
64         uint16_t val_u16;
65         int16_t val_s16;
66         uint32_t val_u32;
67         int32_t val_s32;
68         char *dest, *ret;
69
70         len = option[OPT_LEN - 2];
71         type = type_p->flags & TYPE_MASK;
72         optlen = dhcp_option_lengths[type];
73
74         dest = ret = xmalloc(upper_length(len, type) + strlen(opt_name) + 2);
75         dest += sprintf(ret, "%s=", opt_name);
76
77         for (;;) {
78                 switch (type) {
79                 case OPTION_IP_PAIR:
80                         dest += sprint_nip(dest, "", option);
81                         *dest++ = '/';
82                         option += 4;
83                         optlen = 4;
84                 case OPTION_IP: /* Works regardless of host byte order. */
85                         dest += sprint_nip(dest, "", option);
86                         break;
87                 case OPTION_BOOLEAN:
88                         dest += sprintf(dest, *option ? "yes" : "no");
89                         break;
90                 case OPTION_U8:
91                         dest += sprintf(dest, "%u", *option);
92                         break;
93                 case OPTION_U16:
94                         move_from_unaligned16(val_u16, option);
95                         dest += sprintf(dest, "%u", ntohs(val_u16));
96                         break;
97                 case OPTION_S16:
98                         move_from_unaligned16(val_s16, option);
99                         dest += sprintf(dest, "%d", ntohs(val_s16));
100                         break;
101                 case OPTION_U32:
102                         move_from_unaligned32(val_u32, option);
103                         dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
104                         break;
105                 case OPTION_S32:
106                         move_from_unaligned32(val_s32, option);
107                         dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
108                         break;
109                 case OPTION_STRING:
110                         memcpy(dest, option, len);
111                         dest[len] = '\0';
112                         return ret;      /* Short circuit this case */
113 #if ENABLE_FEATURE_UDHCP_RFC3397
114                 case OPTION_STR1035:
115                         /* unpack option into dest; use ret for prefix (i.e., "optname=") */
116                         dest = dname_dec(option, len, ret);
117                         free(ret);
118                         return dest;
119 #endif
120                 }
121                 option += optlen;
122                 len -= optlen;
123                 if (len <= 0)
124                         break;
125                 dest += sprintf(dest, " ");
126         }
127         return ret;
128 }
129
130
131 /* put all the parameters into an environment */
132 static char **fill_envp(struct dhcp_packet *packet)
133 {
134         int num_options = 0;
135         int i;
136         char **envp, **curr;
137         const char *opt_name;
138         uint8_t *temp;
139         uint8_t over = 0;
140
141         if (packet) {
142                 for (i = 0; dhcp_options[i].code; i++) {
143                         if (get_option(packet, dhcp_options[i].code)) {
144                                 num_options++;
145                                 if (dhcp_options[i].code == DHCP_SUBNET)
146                                         num_options++; /* for mton */
147                         }
148                 }
149                 if (packet->siaddr_nip)
150                         num_options++;
151                 temp = get_option(packet, DHCP_OPTION_OVERLOAD);
152                 if (temp)
153                         over = *temp;
154                 if (!(over & FILE_FIELD) && packet->file[0])
155                         num_options++;
156                 if (!(over & SNAME_FIELD) && packet->sname[0])
157                         num_options++;
158         }
159
160         curr = envp = xzalloc(sizeof(char *) * (num_options + 3));
161         *curr = xasprintf("interface=%s", client_config.interface);
162         putenv(*curr++);
163
164         if (packet == NULL)
165                 return envp;
166
167         *curr = xmalloc(sizeof("ip=255.255.255.255"));
168         sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr);
169         putenv(*curr++);
170
171         opt_name = dhcp_option_strings;
172         i = 0;
173         while (*opt_name) {
174                 temp = get_option(packet, dhcp_options[i].code);
175                 if (!temp)
176                         goto next;
177                 *curr = alloc_fill_opts(temp, &dhcp_options[i], opt_name);
178                 putenv(*curr++);
179
180                 /* Fill in a subnet bits option for things like /24 */
181                 if (dhcp_options[i].code == DHCP_SUBNET) {
182                         uint32_t subnet;
183                         move_from_unaligned32(subnet, temp);
184                         *curr = xasprintf("mask=%d", mton(subnet));
185                         putenv(*curr++);
186                 }
187  next:
188                 opt_name += strlen(opt_name) + 1;
189                 i++;
190         }
191         if (packet->siaddr_nip) {
192                 *curr = xmalloc(sizeof("siaddr=255.255.255.255"));
193                 sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip);
194                 putenv(*curr++);
195         }
196         if (!(over & FILE_FIELD) && packet->file[0]) {
197                 /* watch out for invalid packets */
198                 packet->file[sizeof(packet->file) - 1] = '\0';
199                 *curr = xasprintf("boot_file=%s", packet->file);
200                 putenv(*curr++);
201         }
202         if (!(over & SNAME_FIELD) && packet->sname[0]) {
203                 /* watch out for invalid packets */
204                 packet->sname[sizeof(packet->sname) - 1] = '\0';
205                 *curr = xasprintf("sname=%s", packet->sname);
206                 putenv(*curr++);
207         }
208         return envp;
209 }
210
211
212 /* Call a script with a par file and env vars */
213 void FAST_FUNC udhcp_run_script(struct dhcp_packet *packet, const char *name)
214 {
215         char **envp, **curr;
216         char *argv[3];
217
218         if (client_config.script == NULL)
219                 return;
220
221         log1("Executing %s", client_config.script);
222
223         envp = fill_envp(packet);
224
225         /* call script */
226         argv[0] = (char*) client_config.script;
227         argv[1] = (char*) name;
228         argv[2] = NULL;
229         wait4pid(spawn(argv));
230
231         for (curr = envp; *curr; curr++) {
232                 bb_unsetenv(*curr);
233                 free(*curr);
234         }
235         free(envp);
236 }