Tomato 1.28
[tomato.git] / release / src / router / dnsmasq / src / helper.c
blobefc0ab3f7b691f4646f5b247ea23e382c413cdcd
1 /* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "dnsmasq.h"
19 /* This file has code to fork a helper process which recieves data via a pipe
20 shared with the main process and which is responsible for calling a script when
21 DHCP leases change.
23 The helper process is forked before the main process drops root, so it retains root
24 privs to pass on to the script. For this reason it tries to be paranoid about
25 data received from the main process, in case that has been compromised. We don't
26 want the helper to give an attacker root. In particular, the script to be run is
27 not settable via the pipe, once the fork has taken place it is not alterable by the
28 main process.
31 #if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
33 static void my_setenv(const char *name, const char *value, int *error);
34 static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err);
36 struct script_data
38 unsigned char action, hwaddr_len, hwaddr_type;
39 unsigned char clid_len, hostname_len, ed_len;
40 struct in_addr addr, giaddr;
41 unsigned int remaining_time;
42 #ifdef HAVE_BROKEN_RTC
43 unsigned int length;
44 #else
45 time_t expires;
46 #endif
47 unsigned char hwaddr[DHCP_CHADDR_MAX];
48 char interface[IF_NAMESIZE];
51 static struct script_data *buf = NULL;
52 static size_t bytes_in_buf = 0, buf_size = 0;
54 int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
56 pid_t pid;
57 int i, pipefd[2];
58 struct sigaction sigact;
60 /* create the pipe through which the main program sends us commands,
61 then fork our process. */
62 if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
64 send_event(err_fd, EVENT_PIPE_ERR, errno);
65 _exit(0);
68 if (pid != 0)
70 close(pipefd[0]); /* close reader side */
71 return pipefd[1];
74 /* ignore SIGTERM, so that we can clean up when the main process gets hit
75 and SIGALRM so that we can use sleep() */
76 sigact.sa_handler = SIG_IGN;
77 sigact.sa_flags = 0;
78 sigemptyset(&sigact.sa_mask);
79 sigaction(SIGTERM, &sigact, NULL);
80 sigaction(SIGALRM, &sigact, NULL);
82 if (!(daemon->options & OPT_DEBUG) && uid != 0)
84 gid_t dummy;
85 if (setgroups(0, &dummy) == -1 ||
86 setgid(gid) == -1 ||
87 setuid(uid) == -1)
89 if (daemon->options & OPT_NO_FORK)
90 /* send error to daemon process if no-fork */
91 send_event(event_fd, EVENT_HUSER_ERR, errno);
92 else
94 /* kill daemon */
95 send_event(event_fd, EVENT_DIE, 0);
96 /* return error */
97 send_event(err_fd, EVENT_HUSER_ERR, errno);
99 _exit(0);
103 /* close all the sockets etc, we don't need them here. This closes err_fd, so that
104 main process can return. */
105 for (max_fd--; max_fd >= 0; max_fd--)
106 if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
107 max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd)
108 close(max_fd);
110 /* loop here */
111 while(1)
113 struct script_data data;
114 char *p, *action_str, *hostname = NULL;
115 unsigned char *buf = (unsigned char *)daemon->namebuff;
116 unsigned char *end, *alloc_buff = NULL;
117 int err = 0;
119 /* we read zero bytes when pipe closed: this is our signal to exit */
120 if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
121 _exit(0);
123 if (data.action == ACTION_DEL)
124 action_str = "del";
125 else if (data.action == ACTION_ADD)
126 action_str = "add";
127 else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
128 action_str = "old";
129 else
130 continue;
132 /* stringify MAC into dhcp_buff */
133 p = daemon->dhcp_buff;
134 if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
135 p += sprintf(p, "%.2x-", data.hwaddr_type);
136 for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
138 p += sprintf(p, "%.2x", data.hwaddr[i]);
139 if (i != data.hwaddr_len - 1)
140 p += sprintf(p, ":");
143 /* and CLID into packet, avoid overwrite from bad data */
144 if ((data.clid_len > daemon->packet_buff_sz) || !read_write(pipefd[0], buf, data.clid_len, 1))
145 continue;
146 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
148 p += sprintf(p, "%.2x", buf[i]);
149 if (i != data.clid_len - 1)
150 p += sprintf(p, ":");
153 /* and expiry or length into dhcp_buff2 */
154 #ifdef HAVE_BROKEN_RTC
155 sprintf(daemon->dhcp_buff2, "%u", data.length);
156 #else
157 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
158 #endif
160 /* supplied data may just exceed normal buffer (unlikely) */
161 if ((data.hostname_len + data.ed_len) > daemon->packet_buff_sz &&
162 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len)))
163 continue;
165 if (!read_write(pipefd[0], buf,
166 data.hostname_len + data.ed_len, 1))
167 continue;
169 /* possible fork errors are all temporary resource problems */
170 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
171 sleep(2);
173 free(alloc_buff);
175 if (pid == -1)
176 continue;
178 /* wait for child to complete */
179 if (pid != 0)
181 /* reap our children's children, if necessary */
182 while (1)
184 int status;
185 pid_t rc = wait(&status);
187 if (rc == pid)
189 /* On error send event back to main process for logging */
190 if (WIFSIGNALED(status))
191 send_event(event_fd, EVENT_KILLED, WTERMSIG(status));
192 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
193 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status));
194 break;
197 if (rc == -1 && errno != EINTR)
198 break;
201 continue;
204 if (data.clid_len != 0)
205 my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
207 if (strlen(data.interface) != 0)
208 my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
210 #ifdef HAVE_BROKEN_RTC
211 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
212 #else
213 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
214 #endif
216 if (data.hostname_len != 0)
218 char *dot;
219 hostname = (char *)buf;
220 hostname[data.hostname_len - 1] = 0;
221 if (!legal_hostname(hostname))
222 hostname = NULL;
223 else if ((dot = strchr(hostname, '.')))
225 my_setenv("DNSMASQ_DOMAIN", dot+1, &err);
226 *dot = 0;
228 buf += data.hostname_len;
231 end = buf + data.ed_len;
232 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
233 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
234 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
235 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
236 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
237 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
239 for (i = 0; buf; i++)
241 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
242 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
245 if (data.giaddr.s_addr != 0)
246 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
248 if (data.action != ACTION_DEL)
250 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
251 my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
254 if (data.action == ACTION_OLD_HOSTNAME && hostname)
256 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
257 hostname = NULL;
260 /* we need to have the event_fd around if exec fails */
261 if ((i = fcntl(event_fd, F_GETFD)) != -1)
262 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
263 close(pipefd[0]);
265 p = strrchr(daemon->lease_change_command, '/');
266 if (err == 0)
268 execl(daemon->lease_change_command,
269 p ? p+1 : daemon->lease_change_command,
270 action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL);
271 err = errno;
273 /* failed, send event so the main process logs the problem */
274 send_event(event_fd, EVENT_EXEC_ERR, err);
275 _exit(0);
279 static void my_setenv(const char *name, const char *value, int *error)
281 if (*error == 0 && setenv(name, value, 1) != 0)
282 *error = errno;
285 static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
287 unsigned char *next;
289 if (!buf || (buf == end))
290 return NULL;
292 for (next = buf; *next != 0; next++)
293 if (next == end)
294 return NULL;
296 if (next != buf)
298 char *p;
299 /* No "=" in value */
300 if ((p = strchr((char *)buf, '=')))
301 *p = 0;
302 my_setenv(env, (char *)buf, err);
305 return next + 1;
308 /* pack up lease data into a buffer */
309 void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
311 unsigned char *p;
312 size_t size;
313 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
315 /* no script */
316 if (daemon->helperfd == -1)
317 return;
319 if (lease->extradata)
320 ed_len = lease->extradata_len;
321 if (lease->clid)
322 clid_len = lease->clid_len;
323 if (hostname)
324 hostname_len = strlen(hostname) + 1;
326 size = sizeof(struct script_data) + clid_len + ed_len + hostname_len;
328 if (size > buf_size)
330 struct script_data *new;
332 /* start with reasonable size, will almost never need extending. */
333 if (size < sizeof(struct script_data) + 200)
334 size = sizeof(struct script_data) + 200;
336 if (!(new = whine_malloc(size)))
337 return;
338 if (buf)
339 free(buf);
340 buf = new;
341 buf_size = size;
344 buf->action = action;
345 buf->hwaddr_len = lease->hwaddr_len;
346 buf->hwaddr_type = lease->hwaddr_type;
347 buf->clid_len = clid_len;
348 buf->ed_len = ed_len;
349 buf->hostname_len = hostname_len;
350 buf->addr = lease->addr;
351 buf->giaddr = lease->giaddr;
352 memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len);
353 if (!indextoname(daemon->dhcpfd, lease->last_interface, buf->interface))
354 buf->interface[0] = 0;
356 #ifdef HAVE_BROKEN_RTC
357 buf->length = lease->length;
358 #else
359 buf->expires = lease->expires;
360 #endif
361 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
363 p = (unsigned char *)(buf+1);
364 if (clid_len != 0)
366 memcpy(p, lease->clid, clid_len);
367 p += clid_len;
369 if (hostname_len != 0)
371 memcpy(p, hostname, hostname_len);
372 p += hostname_len;
374 if (ed_len != 0)
376 memcpy(p, lease->extradata, ed_len);
377 p += ed_len;
379 bytes_in_buf = p - (unsigned char *)buf;
382 int helper_buf_empty(void)
384 return bytes_in_buf == 0;
387 void helper_write(void)
389 ssize_t rc;
391 if (bytes_in_buf == 0)
392 return;
394 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
396 if (bytes_in_buf != (size_t)rc)
397 memmove(buf, buf + rc, bytes_in_buf - rc);
398 bytes_in_buf -= rc;
400 else
402 if (errno == EAGAIN || errno == EINTR)
403 return;
404 bytes_in_buf = 0;
408 #endif