1/* script.c
2 *
3 * Functions to call the DHCP client notification scripts
4 *
5 * Russ Dill <Russ.Dill@asu.edu> July 2001
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <string.h>
23#include <unistd.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29#include <sys/types.h>
30#include <sys/wait.h>
31#include <errno.h>
32
33#include "options.h"
34#include "dhcpd.h"
35#include "dhcpc.h"
36#include "packet.h"
37#include "options.h"
38#include "debug.h"
39
40/* get a rough idea of how long an option will be (rounding up...) */
41static int max_option_length[] = {
42	[OPTION_IP] =		sizeof("255.255.255.255 "),
43	[OPTION_IP_PAIR] =	sizeof("255.255.255.255 ") * 2,
44	[OPTION_STRING] =	1,
45	[OPTION_BOOLEAN] =	sizeof("yes "),
46	[OPTION_U8] =		sizeof("255 "),
47	[OPTION_U16] =		sizeof("65535 "),
48	[OPTION_S16] =		sizeof("-32768 "),
49	[OPTION_U32] =		sizeof("4294967295 "),
50	[OPTION_S32] =		sizeof("-2147483684 "),
51};
52
53
54static int upper_length(int length, struct dhcp_option *option)
55{
56	return max_option_length[option->flags & TYPE_MASK] *
57	       (length / option_lengths[option->flags & TYPE_MASK]);
58}
59
60
61static int sprintip(char *dest, char *pre, unsigned char *ip) {
62	return sprintf(dest, "%s%d.%d.%d.%d ", pre, ip[0], ip[1], ip[2], ip[3]);
63}
64
65/* Fill dest with the text of option 'option'. */
66static void fill_options(char *dest, unsigned char *option, struct dhcp_option *type_p)
67{
68	int type, optlen;
69	u_int16_t val_u16;
70	int16_t val_s16;
71	u_int32_t val_u32;
72	int32_t val_s32;
73	int len = option[OPT_LEN - 2];
74
75	dest += sprintf(dest, "%s=", type_p->name);
76
77	type = type_p->flags & TYPE_MASK;
78
79    /* foxconn added start by EricHuang, 12/20/2007 */
80    if ( strcmp(type_p->name, "rfc3442") == 0 )
81    {
82        FILE *fp=0;
83
84        /*foxconn added start, water, 03/08/10, @option249*/
85        /*fp = fopen("/tmp/udhcpc.routes", "w+");*/
86        fp = fopen("/tmp/udhcpc.routes", "a+");
87        /*foxconn added end, water, 03/08/10*/
88        if (fp)
89        {
90            fwrite(option, 1, len, fp);
91            fclose(fp);
92        }
93
94        dest += sprintf(dest, "%d", len); /* only save the length here ... */
95        return;
96    }
97    /* foxconn added end by EricHuang, 12/20/2007 */
98
99    /* foxconn added start by EricHuang, 03/12/2008 */
100    if ( strcmp(type_p->name, "sroute") == 0 )
101    {
102        FILE *fp=0;
103
104        fp = fopen("/tmp/udhcpc.routes2", "w+");
105        if (fp)
106        {
107            fwrite(option, 1, len, fp);
108            fclose(fp);
109        }
110
111        dest += sprintf(dest, "%d", len); /* only save the length here ... */
112        return;
113    }
114    /* foxconn added end by EricHuang, 03/12/2008 */
115
116
117	optlen = option_lengths[type];
118	for(;;) {
119		switch (type) {
120		case OPTION_IP_PAIR:
121			dest += sprintip(dest, "", option);
122			*(dest++) = '/';
123			option += 4;
124			optlen = 4;
125		case OPTION_IP:	/* Works regardless of host byte order. */
126			dest += sprintip(dest, "", option);
127 			break;
128		case OPTION_BOOLEAN:
129			dest += sprintf(dest, *option ? "yes " : "no ");
130			break;
131		case OPTION_U8:
132			dest += sprintf(dest, "%u ", *option);
133			break;
134		case OPTION_U16:
135			memcpy(&val_u16, option, 2);
136			dest += sprintf(dest, "%u ", ntohs(val_u16));
137			break;
138		case OPTION_S16:
139			memcpy(&val_s16, option, 2);
140			dest += sprintf(dest, "%d ", ntohs(val_s16));
141			break;
142		case OPTION_U32:
143			memcpy(&val_u32, option, 4);
144			dest += sprintf(dest, "%lu ", (unsigned long) ntohl(val_u32));
145			break;
146		case OPTION_S32:
147			memcpy(&val_s32, option, 4);
148			dest += sprintf(dest, "%ld ", (long) ntohl(val_s32));
149			break;
150		case OPTION_STRING:
151			memcpy(dest, option, len);
152			dest[len] = '\0';
153			return;	 /* Short circuit this case */
154		}
155		option += optlen;
156		len -= optlen;
157		if (len <= 0) break;
158	}
159}
160
161
162static char *find_env(const char *prefix, char *defaultstr)
163{
164	extern char **environ;
165	char **ptr;
166	const int len = strlen(prefix);
167
168	for (ptr = environ; *ptr != NULL; ptr++) {
169		if (strncmp(prefix, *ptr, len) == 0)
170			return *ptr;
171	}
172	return defaultstr;
173}
174
175
176/* put all the paramaters into an environment */
177static char **fill_envp(struct dhcpMessage *packet)
178{
179	int num_options = 0;
180	int i, j;
181	char **envp;
182	unsigned char *temp;
183	char over = 0;
184
185	if (packet == NULL)
186		num_options = 0;
187	else {
188		for (i = 0; options[i].code; i++)
189			if (get_option(packet, options[i].code))
190				num_options++;
191		if (packet->siaddr) num_options++;
192		if ((temp = get_option(packet, DHCP_OPTION_OVER)))
193			over = *temp;
194		if (!(over & FILE_FIELD) && packet->file[0]) num_options++;
195		if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;
196	}
197
198	envp = xmalloc((num_options + 5) * sizeof(char *));
199	envp[0] = xmalloc(sizeof("interface=") + strlen(client_config.interface));
200	sprintf(envp[0], "interface=%s", client_config.interface);
201	envp[1] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin");
202	envp[2] = find_env("HOME", "HOME=/");
203
204	if (packet == NULL) {
205		envp[3] = NULL;
206		return envp;
207	}
208
209	envp[3] = xmalloc(sizeof("ip=255.255.255.255"));
210	sprintip(envp[3], "ip=", (unsigned char *) &packet->yiaddr);
211	for (i = 0, j = 4; options[i].code; i++) {
212		if ((temp = get_option(packet, options[i].code))) {
213			envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], &options[i]) + strlen(options[i].name) + 2);
214			fill_options(envp[j], temp, &options[i]);
215			j++;
216		}
217	}
218	if (packet->siaddr) {
219		envp[j] = xmalloc(sizeof("siaddr=255.255.255.255"));
220		sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->siaddr);
221	}
222	if (!(over & FILE_FIELD) && packet->file[0]) {
223		/* watch out for invalid packets */
224		packet->file[sizeof(packet->file) - 1] = '\0';
225		envp[j] = xmalloc(sizeof("boot_file=") + strlen(packet->file));
226		sprintf(envp[j++], "boot_file=%s", packet->file);
227	}
228	if (!(over & SNAME_FIELD) && packet->sname[0]) {
229		/* watch out for invalid packets */
230		packet->sname[sizeof(packet->sname) - 1] = '\0';
231		envp[j] = xmalloc(sizeof("sname=") + strlen(packet->sname));
232		sprintf(envp[j++], "sname=%s", packet->sname);
233	}
234	envp[j] = NULL;
235	return envp;
236}
237
238
239/* Call a script with a par file and env vars */
240void run_script(struct dhcpMessage *packet, const char *name)
241{
242	int pid;
243	char **envp;
244
245	if (client_config.script == NULL)
246		return;
247
248	/* call script */
249	pid = fork();
250	if (pid) {
251		waitpid(pid, NULL, 0);
252		return;
253	} else if (pid == 0) {
254		envp = fill_envp(packet);
255
256		/* close fd's? */
257
258		/* exec script */
259		DEBUG(LOG_INFO, "execle'ing %s", client_config.script);
260		execle(client_config.script, client_config.script,
261		       name, NULL, envp);
262		LOG(LOG_ERR, "script %s failed: %s",
263		    client_config.script, strerror(errno));
264		exit(1);
265	}
266}
267