14 |
|
|
15 |
|
|
16 |
/* get a rough idea of how long an option will be (rounding up...) */ |
/* get a rough idea of how long an option will be (rounding up...) */ |
17 |
static const uint8_t max_option_length[] = { |
static const uint8_t len_of_option_as_string[] = { |
18 |
[OPTION_IP] = sizeof("255.255.255.255 "), |
[OPTION_IP] = sizeof("255.255.255.255 "), |
19 |
[OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, |
[OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, |
20 |
|
[OPTION_STATIC_ROUTES]= sizeof("255.255.255.255/32 255.255.255.255 "), |
21 |
[OPTION_STRING] = 1, |
[OPTION_STRING] = 1, |
22 |
#if ENABLE_FEATURE_UDHCP_RFC3397 |
#if ENABLE_FEATURE_UDHCP_RFC3397 |
23 |
[OPTION_STR1035] = 1, |
[OPTION_STR1035] = 1, |
31 |
}; |
}; |
32 |
|
|
33 |
|
|
34 |
static inline int upper_length(int length, int opt_index) |
/* note: ip is a pointer to an IP in network order, possibly misaliged */ |
35 |
|
static int sprint_nip(char *dest, const char *pre, const uint8_t *ip) |
36 |
{ |
{ |
37 |
return max_option_length[opt_index] * |
return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]); |
|
(length / dhcp_option_lengths[opt_index]); |
|
|
} |
|
|
|
|
|
|
|
|
static int sprintip(char *dest, const char *pre, const uint8_t *ip) |
|
|
{ |
|
|
return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]); |
|
38 |
} |
} |
39 |
|
|
40 |
|
|
51 |
} |
} |
52 |
|
|
53 |
|
|
54 |
/* Allocate and fill with the text of option 'option'. */ |
/* Create "opt_name=opt_value" string */ |
55 |
static char *alloc_fill_opts(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name) |
static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_option *type_p, const char *opt_name) |
56 |
{ |
{ |
57 |
|
unsigned upper_length; |
58 |
int len, type, optlen; |
int len, type, optlen; |
59 |
uint16_t val_u16; |
uint16_t val_u16; |
60 |
int16_t val_s16; |
int16_t val_s16; |
62 |
int32_t val_s32; |
int32_t val_s32; |
63 |
char *dest, *ret; |
char *dest, *ret; |
64 |
|
|
65 |
len = option[OPT_LEN - 2]; |
/* option points to OPT_DATA, need to go back and get OPT_LEN */ |
66 |
|
len = option[OPT_LEN - OPT_DATA]; |
67 |
type = type_p->flags & TYPE_MASK; |
type = type_p->flags & TYPE_MASK; |
68 |
optlen = dhcp_option_lengths[type]; |
optlen = dhcp_option_lengths[type]; |
69 |
|
upper_length = len_of_option_as_string[type] * (len / optlen); |
70 |
|
|
71 |
dest = ret = xmalloc(upper_length(len, type) + strlen(opt_name) + 2); |
dest = ret = xmalloc(upper_length + strlen(opt_name) + 2); |
72 |
dest += sprintf(ret, "%s=", opt_name); |
dest += sprintf(ret, "%s=", opt_name); |
73 |
|
|
74 |
for (;;) { |
while (len >= optlen) { |
75 |
switch (type) { |
switch (type) { |
76 |
case OPTION_IP_PAIR: |
case OPTION_IP_PAIR: |
77 |
dest += sprintip(dest, "", option); |
dest += sprint_nip(dest, "", option); |
78 |
*dest++ = '/'; |
*dest++ = '/'; |
79 |
option += 4; |
option += 4; |
80 |
optlen = 4; |
optlen = 4; |
81 |
case OPTION_IP: /* Works regardless of host byte order. */ |
case OPTION_IP: /* Works regardless of host byte order. */ |
82 |
dest += sprintip(dest, "", option); |
dest += sprint_nip(dest, "", option); |
83 |
break; |
break; |
84 |
case OPTION_BOOLEAN: |
case OPTION_BOOLEAN: |
85 |
dest += sprintf(dest, *option ? "yes" : "no"); |
dest += sprintf(dest, *option ? "yes" : "no"); |
88 |
dest += sprintf(dest, "%u", *option); |
dest += sprintf(dest, "%u", *option); |
89 |
break; |
break; |
90 |
case OPTION_U16: |
case OPTION_U16: |
91 |
memcpy(&val_u16, option, 2); |
move_from_unaligned16(val_u16, option); |
92 |
dest += sprintf(dest, "%u", ntohs(val_u16)); |
dest += sprintf(dest, "%u", ntohs(val_u16)); |
93 |
break; |
break; |
94 |
case OPTION_S16: |
case OPTION_S16: |
95 |
memcpy(&val_s16, option, 2); |
move_from_unaligned16(val_s16, option); |
96 |
dest += sprintf(dest, "%d", ntohs(val_s16)); |
dest += sprintf(dest, "%d", ntohs(val_s16)); |
97 |
break; |
break; |
98 |
case OPTION_U32: |
case OPTION_U32: |
99 |
memcpy(&val_u32, option, 4); |
move_from_unaligned32(val_u32, option); |
100 |
dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32)); |
dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32)); |
101 |
break; |
break; |
102 |
case OPTION_S32: |
case OPTION_S32: |
103 |
memcpy(&val_s32, option, 4); |
move_from_unaligned32(val_s32, option); |
104 |
dest += sprintf(dest, "%ld", (long) ntohl(val_s32)); |
dest += sprintf(dest, "%ld", (long) ntohl(val_s32)); |
105 |
break; |
break; |
106 |
case OPTION_STRING: |
case OPTION_STRING: |
107 |
memcpy(dest, option, len); |
memcpy(dest, option, len); |
108 |
dest[len] = '\0'; |
dest[len] = '\0'; |
109 |
return ret; /* Short circuit this case */ |
return ret; /* Short circuit this case */ |
110 |
|
case OPTION_STATIC_ROUTES: { |
111 |
|
/* Option binary format: |
112 |
|
* mask [one byte, 0..32] |
113 |
|
* ip [big endian, 0..4 bytes depending on mask] |
114 |
|
* router [big endian, 4 bytes] |
115 |
|
* may be repeated |
116 |
|
* |
117 |
|
* We convert it to a string "IP/MASK ROUTER IP2/MASK2 ROUTER2" |
118 |
|
*/ |
119 |
|
const char *pfx = ""; |
120 |
|
|
121 |
|
while (len >= 1 + 4) { /* mask + 0-byte ip + router */ |
122 |
|
uint32_t nip; |
123 |
|
uint8_t *p; |
124 |
|
unsigned mask; |
125 |
|
int bytes; |
126 |
|
|
127 |
|
mask = *option++; |
128 |
|
if (mask > 32) |
129 |
|
break; |
130 |
|
len--; |
131 |
|
|
132 |
|
nip = 0; |
133 |
|
p = (void*) &nip; |
134 |
|
bytes = (mask + 7) / 8; /* 0 -> 0, 1..8 -> 1, 9..16 -> 2 etc */ |
135 |
|
while (--bytes >= 0) { |
136 |
|
*p++ = *option++; |
137 |
|
len--; |
138 |
|
} |
139 |
|
if (len < 4) |
140 |
|
break; |
141 |
|
|
142 |
|
/* print ip/mask */ |
143 |
|
dest += sprint_nip(dest, pfx, (void*) &nip); |
144 |
|
pfx = " "; |
145 |
|
dest += sprintf(dest, "/%u ", mask); |
146 |
|
/* print router */ |
147 |
|
dest += sprint_nip(dest, "", option); |
148 |
|
option += 4; |
149 |
|
len -= 4; |
150 |
|
} |
151 |
|
|
152 |
|
return ret; |
153 |
|
} |
154 |
#if ENABLE_FEATURE_UDHCP_RFC3397 |
#if ENABLE_FEATURE_UDHCP_RFC3397 |
155 |
case OPTION_STR1035: |
case OPTION_STR1035: |
156 |
/* unpack option into dest; use ret for prefix (i.e., "optname=") */ |
/* unpack option into dest; use ret for prefix (i.e., "optname=") */ |
157 |
dest = dname_dec(option, len, ret); |
dest = dname_dec(option, len, ret); |
158 |
free(ret); |
if (dest) { |
159 |
return dest; |
free(ret); |
160 |
|
return dest; |
161 |
|
} |
162 |
|
/* error. return "optname=" string */ |
163 |
|
return ret; |
164 |
#endif |
#endif |
165 |
} |
} |
166 |
option += optlen; |
option += optlen; |
167 |
len -= optlen; |
len -= optlen; |
168 |
if (len <= 0) break; |
if (len <= 0) |
169 |
dest += sprintf(dest, " "); |
break; |
170 |
|
*dest++ = ' '; |
171 |
|
*dest = '\0'; |
172 |
} |
} |
173 |
return ret; |
return ret; |
174 |
} |
} |
175 |
|
|
176 |
|
|
177 |
/* put all the parameters into an environment */ |
/* put all the parameters into an environment */ |
178 |
static char **fill_envp(struct dhcpMessage *packet) |
static char **fill_envp(struct dhcp_packet *packet) |
179 |
{ |
{ |
180 |
int num_options = 0; |
int num_options = 0; |
181 |
int i, j; |
int i; |
182 |
char **envp; |
char **envp, **curr; |
|
char *var; |
|
183 |
const char *opt_name; |
const char *opt_name; |
184 |
uint8_t *temp; |
uint8_t *temp; |
185 |
char over = 0; |
uint8_t over = 0; |
186 |
|
|
187 |
if (packet) { |
if (packet) { |
188 |
for (i = 0; dhcp_options[i].code; i++) { |
for (i = 0; dhcp_options[i].code; i++) { |
192 |
num_options++; /* for mton */ |
num_options++; /* for mton */ |
193 |
} |
} |
194 |
} |
} |
195 |
if (packet->siaddr) |
if (packet->siaddr_nip) |
196 |
num_options++; |
num_options++; |
197 |
temp = get_option(packet, DHCP_OPTION_OVER); |
temp = get_option(packet, DHCP_OPTION_OVERLOAD); |
198 |
if (temp) |
if (temp) |
199 |
over = *temp; |
over = *temp; |
200 |
if (!(over & FILE_FIELD) && packet->file[0]) |
if (!(over & FILE_FIELD) && packet->file[0]) |
203 |
num_options++; |
num_options++; |
204 |
} |
} |
205 |
|
|
206 |
envp = xzalloc(sizeof(char *) * (num_options + 5)); |
curr = envp = xzalloc(sizeof(char *) * (num_options + 3)); |
207 |
j = 0; |
*curr = xasprintf("interface=%s", client_config.interface); |
208 |
envp[j++] = xasprintf("interface=%s", client_config.interface); |
putenv(*curr++); |
|
var = getenv("PATH"); |
|
|
if (var) |
|
|
envp[j++] = xasprintf("PATH=%s", var); |
|
|
var = getenv("HOME"); |
|
|
if (var) |
|
|
envp[j++] = xasprintf("HOME=%s", var); |
|
209 |
|
|
210 |
if (packet == NULL) |
if (packet == NULL) |
211 |
return envp; |
return envp; |
212 |
|
|
213 |
envp[j] = xmalloc(sizeof("ip=255.255.255.255")); |
*curr = xmalloc(sizeof("ip=255.255.255.255")); |
214 |
sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr); |
sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr); |
215 |
|
putenv(*curr++); |
216 |
|
|
217 |
opt_name = dhcp_option_strings; |
opt_name = dhcp_option_strings; |
218 |
i = 0; |
i = 0; |
220 |
temp = get_option(packet, dhcp_options[i].code); |
temp = get_option(packet, dhcp_options[i].code); |
221 |
if (!temp) |
if (!temp) |
222 |
goto next; |
goto next; |
223 |
envp[j++] = alloc_fill_opts(temp, &dhcp_options[i], opt_name); |
*curr = xmalloc_optname_optval(temp, &dhcp_options[i], opt_name); |
224 |
|
putenv(*curr++); |
225 |
|
|
226 |
/* Fill in a subnet bits option for things like /24 */ |
/* Fill in a subnet bits option for things like /24 */ |
227 |
if (dhcp_options[i].code == DHCP_SUBNET) { |
if (dhcp_options[i].code == DHCP_SUBNET) { |
228 |
uint32_t subnet; |
uint32_t subnet; |
229 |
memcpy(&subnet, temp, 4); |
move_from_unaligned32(subnet, temp); |
230 |
envp[j++] = xasprintf("mask=%d", mton(subnet)); |
*curr = xasprintf("mask=%d", mton(subnet)); |
231 |
|
putenv(*curr++); |
232 |
} |
} |
233 |
next: |
next: |
234 |
opt_name += strlen(opt_name) + 1; |
opt_name += strlen(opt_name) + 1; |
235 |
i++; |
i++; |
236 |
} |
} |
237 |
if (packet->siaddr) { |
if (packet->siaddr_nip) { |
238 |
envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); |
*curr = xmalloc(sizeof("siaddr=255.255.255.255")); |
239 |
sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr); |
sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip); |
240 |
|
putenv(*curr++); |
241 |
} |
} |
242 |
if (!(over & FILE_FIELD) && packet->file[0]) { |
if (!(over & FILE_FIELD) && packet->file[0]) { |
243 |
/* watch out for invalid packets */ |
/* watch out for invalid packets */ |
244 |
packet->file[sizeof(packet->file) - 1] = '\0'; |
packet->file[sizeof(packet->file) - 1] = '\0'; |
245 |
envp[j++] = xasprintf("boot_file=%s", packet->file); |
*curr = xasprintf("boot_file=%s", packet->file); |
246 |
|
putenv(*curr++); |
247 |
} |
} |
248 |
if (!(over & SNAME_FIELD) && packet->sname[0]) { |
if (!(over & SNAME_FIELD) && packet->sname[0]) { |
249 |
/* watch out for invalid packets */ |
/* watch out for invalid packets */ |
250 |
packet->sname[sizeof(packet->sname) - 1] = '\0'; |
packet->sname[sizeof(packet->sname) - 1] = '\0'; |
251 |
envp[j++] = xasprintf("sname=%s", packet->sname); |
*curr = xasprintf("sname=%s", packet->sname); |
252 |
|
putenv(*curr++); |
253 |
} |
} |
254 |
return envp; |
return envp; |
255 |
} |
} |
256 |
|
|
257 |
|
|
258 |
/* Call a script with a par file and env vars */ |
/* Call a script with a par file and env vars */ |
259 |
void FAST_FUNC udhcp_run_script(struct dhcpMessage *packet, const char *name) |
void FAST_FUNC udhcp_run_script(struct dhcp_packet *packet, const char *name) |
260 |
{ |
{ |
|
int pid; |
|
261 |
char **envp, **curr; |
char **envp, **curr; |
262 |
|
char *argv[3]; |
263 |
|
|
264 |
if (client_config.script == NULL) |
if (client_config.script == NULL) |
265 |
return; |
return; |
266 |
|
|
|
DEBUG("vfork'ing and execle'ing %s", client_config.script); |
|
|
|
|
267 |
envp = fill_envp(packet); |
envp = fill_envp(packet); |
268 |
|
|
269 |
/* call script */ |
/* call script */ |
270 |
// can we use wait4pid(spawn(...)) here? |
log1("Executing %s %s", client_config.script, name); |
271 |
pid = vfork(); |
argv[0] = (char*) client_config.script; |
272 |
if (pid < 0) return; |
argv[1] = (char*) name; |
273 |
if (pid == 0) { |
argv[2] = NULL; |
274 |
/* close fd's? */ |
wait4pid(spawn(argv)); |
275 |
/* exec script */ |
|
276 |
execle(client_config.script, client_config.script, |
for (curr = envp; *curr; curr++) { |
277 |
name, NULL, envp); |
log2(" %s", *curr); |
278 |
bb_perror_msg_and_die("exec %s", client_config.script); |
bb_unsetenv(*curr); |
|
} |
|
|
safe_waitpid(pid, NULL, 0); |
|
|
for (curr = envp; *curr; curr++) |
|
279 |
free(*curr); |
free(*curr); |
280 |
|
} |
281 |
free(envp); |
free(envp); |
282 |
} |
} |