Annotation of embedaddon/dnsmasq/src/helper.c, revision 1.1.1.4
1.1.1.4 ! misho 1: /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
1.1 misho 2:
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.
7:
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.
12:
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/>.
15: */
16:
17: #include "dnsmasq.h"
18:
19: #ifdef HAVE_SCRIPT
20:
1.1.1.4 ! misho 21: /* This file has code to fork a helper process which receives data via a pipe
1.1 misho 22: shared with the main process and which is responsible for calling a script when
23: DHCP leases change.
24:
25: The helper process is forked before the main process drops root, so it retains root
26: privs to pass on to the script. For this reason it tries to be paranoid about
27: data received from the main process, in case that has been compromised. We don't
28: want the helper to give an attacker root. In particular, the script to be run is
29: not settable via the pipe, once the fork has taken place it is not alterable by the
30: main process.
31: */
32:
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);
35:
36: #ifdef HAVE_LUASCRIPT
37: #define LUA_COMPAT_ALL
38: #include <lua.h>
39: #include <lualib.h>
40: #include <lauxlib.h>
41:
42: #ifndef lua_open
43: #define lua_open() luaL_newstate()
44: #endif
45:
46: lua_State *lua;
47:
48: static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
49: #endif
50:
51:
52: struct script_data
53: {
54: int flags;
55: int action, hwaddr_len, hwaddr_type;
56: int clid_len, hostname_len, ed_len;
57: struct in_addr addr, giaddr;
58: unsigned int remaining_time;
59: #ifdef HAVE_BROKEN_RTC
60: unsigned int length;
61: #else
62: time_t expires;
63: #endif
1.1.1.2 misho 64: #ifdef HAVE_TFTP
65: off_t file_len;
66: #endif
67: struct in6_addr addr6;
68: #ifdef HAVE_DHCP6
1.1.1.4 ! misho 69: int vendorclass_count;
! 70: unsigned int iaid;
1.1.1.2 misho 71: #endif
1.1 misho 72: unsigned char hwaddr[DHCP_CHADDR_MAX];
73: char interface[IF_NAMESIZE];
74: };
75:
76: static struct script_data *buf = NULL;
77: static size_t bytes_in_buf = 0, buf_size = 0;
78:
79: int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
80: {
81: pid_t pid;
82: int i, pipefd[2];
83: struct sigaction sigact;
1.1.1.4 ! misho 84: unsigned char *alloc_buff = NULL;
! 85:
1.1 misho 86: /* create the pipe through which the main program sends us commands,
87: then fork our process. */
88: if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
89: {
90: send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
91: _exit(0);
92: }
93:
94: if (pid != 0)
95: {
96: close(pipefd[0]); /* close reader side */
97: return pipefd[1];
98: }
99:
1.1.1.4 ! misho 100: /* ignore SIGTERM and SIGINT, so that we can clean up when the main process gets hit
1.1 misho 101: and SIGALRM so that we can use sleep() */
102: sigact.sa_handler = SIG_IGN;
103: sigact.sa_flags = 0;
104: sigemptyset(&sigact.sa_mask);
105: sigaction(SIGTERM, &sigact, NULL);
106: sigaction(SIGALRM, &sigact, NULL);
1.1.1.4 ! misho 107: sigaction(SIGINT, &sigact, NULL);
1.1 misho 108:
109: if (!option_bool(OPT_DEBUG) && uid != 0)
110: {
111: gid_t dummy;
112: if (setgroups(0, &dummy) == -1 ||
113: setgid(gid) == -1 ||
114: setuid(uid) == -1)
115: {
116: if (option_bool(OPT_NO_FORK))
117: /* send error to daemon process if no-fork */
118: send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
119: else
120: {
121: /* kill daemon */
122: send_event(event_fd, EVENT_DIE, 0, NULL);
123: /* return error */
124: send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
125: }
126: _exit(0);
127: }
128: }
129:
130: /* close all the sockets etc, we don't need them here.
131: Don't close err_fd, in case the lua-init fails.
132: Note that we have to do this before lua init
133: so we don't close any lua fds. */
1.1.1.4 ! misho 134: close_fds(max_fd, pipefd[0], event_fd, err_fd);
1.1 misho 135:
136: #ifdef HAVE_LUASCRIPT
137: if (daemon->luascript)
138: {
139: const char *lua_err = NULL;
140: lua = lua_open();
141: luaL_openlibs(lua);
142:
143: /* get Lua to load our script file */
144: if (luaL_dofile(lua, daemon->luascript) != 0)
145: lua_err = lua_tostring(lua, -1);
146: else
147: {
148: lua_getglobal(lua, "lease");
149: if (lua_type(lua, -1) != LUA_TFUNCTION)
150: lua_err = _("lease() function missing in Lua script");
151: }
152:
153: if (lua_err)
154: {
155: if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
156: /* send error to daemon process if no-fork */
157: send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
158: else
159: {
160: /* kill daemon */
161: send_event(event_fd, EVENT_DIE, 0, NULL);
162: /* return error */
163: send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
164: }
165: _exit(0);
166: }
167:
168: lua_pop(lua, 1); /* remove nil from stack */
169: lua_getglobal(lua, "init");
170: if (lua_type(lua, -1) == LUA_TFUNCTION)
171: lua_call(lua, 0, 0);
172: else
173: lua_pop(lua, 1); /* remove nil from stack */
174: }
175: #endif
176:
177: /* All init done, close our copy of the error pipe, so that main process can return */
178: if (err_fd != -1)
179: close(err_fd);
180:
181: /* loop here */
182: while(1)
183: {
184: struct script_data data;
185: char *p, *action_str, *hostname = NULL, *domain = NULL;
186: unsigned char *buf = (unsigned char *)daemon->namebuff;
1.1.1.4 ! misho 187: unsigned char *end, *extradata;
1.1 misho 188: int is6, err = 0;
1.1.1.4 ! misho 189: int pipeout[2];
1.1 misho 190:
1.1.1.4 ! misho 191: /* Free rarely-allocated memory from previous iteration. */
! 192: if (alloc_buff)
! 193: {
! 194: free(alloc_buff);
! 195: alloc_buff = NULL;
! 196: }
1.1 misho 197:
198: /* we read zero bytes when pipe closed: this is our signal to exit */
199: if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
200: {
201: #ifdef HAVE_LUASCRIPT
202: if (daemon->luascript)
203: {
204: lua_getglobal(lua, "shutdown");
205: if (lua_type(lua, -1) == LUA_TFUNCTION)
206: lua_call(lua, 0, 0);
207: }
208: #endif
209: _exit(0);
210: }
211:
212: is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
213:
214: if (data.action == ACTION_DEL)
215: action_str = "del";
216: else if (data.action == ACTION_ADD)
217: action_str = "add";
218: else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
219: action_str = "old";
220: else if (data.action == ACTION_TFTP)
221: {
222: action_str = "tftp";
223: is6 = (data.flags != AF_INET);
224: }
1.1.1.3 misho 225: else if (data.action == ACTION_ARP)
226: {
227: action_str = "arp-add";
228: is6 = (data.flags != AF_INET);
229: }
230: else if (data.action == ACTION_ARP_DEL)
231: {
232: action_str = "arp-del";
233: is6 = (data.flags != AF_INET);
234: data.action = ACTION_ARP;
235: }
236: else
1.1 misho 237: continue;
238:
239:
1.1.1.2 misho 240: /* stringify MAC into dhcp_buff */
241: p = daemon->dhcp_buff;
242: if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
243: p += sprintf(p, "%.2x-", data.hwaddr_type);
244: for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
245: {
246: p += sprintf(p, "%.2x", data.hwaddr[i]);
247: if (i != data.hwaddr_len - 1)
248: p += sprintf(p, ":");
1.1 misho 249: }
1.1.1.2 misho 250:
1.1 misho 251: /* supplied data may just exceed normal buffer (unlikely) */
252: if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
253: !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
254: continue;
255:
256: if (!read_write(pipefd[0], buf,
257: data.hostname_len + data.ed_len + data.clid_len, 1))
258: continue;
259:
260: /* CLID into packet */
1.1.1.2 misho 261: for (p = daemon->packet, i = 0; i < data.clid_len; i++)
262: {
263: p += sprintf(p, "%.2x", buf[i]);
264: if (i != data.clid_len - 1)
1.1 misho 265: p += sprintf(p, ":");
1.1.1.2 misho 266: }
267:
1.1 misho 268: #ifdef HAVE_DHCP6
1.1.1.2 misho 269: if (is6)
1.1 misho 270: {
271: /* or IAID and server DUID for IPv6 */
1.1.1.2 misho 272: sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.iaid);
273: for (p = daemon->dhcp_packet.iov_base, i = 0; i < daemon->duid_len; i++)
1.1 misho 274: {
275: p += sprintf(p, "%.2x", daemon->duid[i]);
276: if (i != daemon->duid_len - 1)
277: p += sprintf(p, ":");
278: }
279:
280: }
281: #endif
282:
283: buf += data.clid_len;
284:
285: if (data.hostname_len != 0)
286: {
287: char *dot;
288: hostname = (char *)buf;
289: hostname[data.hostname_len - 1] = 0;
290: if (data.action != ACTION_TFTP)
291: {
292: if (!legal_hostname(hostname))
293: hostname = NULL;
294: else if ((dot = strchr(hostname, '.')))
295: {
296: domain = dot+1;
297: *dot = 0;
298: }
299: }
300: }
301:
302: extradata = buf + data.hostname_len;
303:
304: if (!is6)
305: inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
306: else
1.1.1.2 misho 307: inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
1.1 misho 308:
1.1.1.2 misho 309: #ifdef HAVE_TFTP
1.1 misho 310: /* file length */
311: if (data.action == ACTION_TFTP)
1.1.1.2 misho 312: sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
313: #endif
314:
1.1 misho 315: #ifdef HAVE_LUASCRIPT
316: if (daemon->luascript)
317: {
318: if (data.action == ACTION_TFTP)
319: {
320: lua_getglobal(lua, "tftp");
321: if (lua_type(lua, -1) != LUA_TFUNCTION)
322: lua_pop(lua, 1); /* tftp function optional */
323: else
324: {
325: lua_pushstring(lua, action_str); /* arg1 - action */
326: lua_newtable(lua); /* arg2 - data table */
327: lua_pushstring(lua, daemon->addrbuff);
328: lua_setfield(lua, -2, "destination_address");
329: lua_pushstring(lua, hostname);
330: lua_setfield(lua, -2, "file_name");
1.1.1.2 misho 331: lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
1.1 misho 332: lua_setfield(lua, -2, "file_size");
333: lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
334: }
335: }
1.1.1.3 misho 336: else if (data.action == ACTION_ARP)
337: {
338: lua_getglobal(lua, "arp");
339: if (lua_type(lua, -1) != LUA_TFUNCTION)
340: lua_pop(lua, 1); /* arp function optional */
341: else
342: {
343: lua_pushstring(lua, action_str); /* arg1 - action */
344: lua_newtable(lua); /* arg2 - data table */
345: lua_pushstring(lua, daemon->addrbuff);
346: lua_setfield(lua, -2, "client_address");
347: lua_pushstring(lua, daemon->dhcp_buff);
348: lua_setfield(lua, -2, "mac_address");
349: lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
350: }
351: }
1.1 misho 352: else
353: {
354: lua_getglobal(lua, "lease"); /* function to call */
355: lua_pushstring(lua, action_str); /* arg1 - action */
356: lua_newtable(lua); /* arg2 - data table */
357:
358: if (is6)
359: {
360: lua_pushstring(lua, daemon->packet);
1.1.1.2 misho 361: lua_setfield(lua, -2, "client_duid");
362: lua_pushstring(lua, daemon->dhcp_packet.iov_base);
1.1 misho 363: lua_setfield(lua, -2, "server_duid");
364: lua_pushstring(lua, daemon->dhcp_buff3);
365: lua_setfield(lua, -2, "iaid");
366: }
367:
368: if (!is6 && data.clid_len != 0)
369: {
370: lua_pushstring(lua, daemon->packet);
371: lua_setfield(lua, -2, "client_id");
372: }
373:
374: if (strlen(data.interface) != 0)
375: {
376: lua_pushstring(lua, data.interface);
377: lua_setfield(lua, -2, "interface");
378: }
379:
380: #ifdef HAVE_BROKEN_RTC
381: lua_pushnumber(lua, data.length);
382: lua_setfield(lua, -2, "lease_length");
383: #else
384: lua_pushnumber(lua, data.expires);
385: lua_setfield(lua, -2, "lease_expires");
386: #endif
387:
388: if (hostname)
389: {
390: lua_pushstring(lua, hostname);
391: lua_setfield(lua, -2, "hostname");
392: }
393:
394: if (domain)
395: {
396: lua_pushstring(lua, domain);
397: lua_setfield(lua, -2, "domain");
398: }
399:
400: end = extradata + data.ed_len;
401: buf = extradata;
402:
403: if (!is6)
404: buf = grab_extradata_lua(buf, end, "vendor_class");
405: #ifdef HAVE_DHCP6
1.1.1.2 misho 406: else if (data.vendorclass_count != 0)
407: {
408: sprintf(daemon->dhcp_buff2, "vendor_class_id");
409: buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
410: for (i = 0; i < data.vendorclass_count - 1; i++)
411: {
412: sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
413: buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
414: }
415: }
1.1 misho 416: #endif
417:
418: buf = grab_extradata_lua(buf, end, "supplied_hostname");
419:
420: if (!is6)
421: {
422: buf = grab_extradata_lua(buf, end, "cpewan_oui");
423: buf = grab_extradata_lua(buf, end, "cpewan_serial");
424: buf = grab_extradata_lua(buf, end, "cpewan_class");
425: buf = grab_extradata_lua(buf, end, "circuit_id");
426: buf = grab_extradata_lua(buf, end, "subscriber_id");
427: buf = grab_extradata_lua(buf, end, "remote_id");
428: }
429:
430: buf = grab_extradata_lua(buf, end, "tags");
431:
432: if (is6)
433: buf = grab_extradata_lua(buf, end, "relay_address");
434: else if (data.giaddr.s_addr != 0)
435: {
436: lua_pushstring(lua, inet_ntoa(data.giaddr));
437: lua_setfield(lua, -2, "relay_address");
438: }
439:
440: for (i = 0; buf; i++)
441: {
442: sprintf(daemon->dhcp_buff2, "user_class%i", i);
443: buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
444: }
445:
446: if (data.action != ACTION_DEL && data.remaining_time != 0)
447: {
448: lua_pushnumber(lua, data.remaining_time);
449: lua_setfield(lua, -2, "time_remaining");
450: }
451:
452: if (data.action == ACTION_OLD_HOSTNAME && hostname)
453: {
454: lua_pushstring(lua, hostname);
455: lua_setfield(lua, -2, "old_hostname");
456: }
457:
1.1.1.2 misho 458: if (!is6 || data.hwaddr_len != 0)
1.1 misho 459: {
460: lua_pushstring(lua, daemon->dhcp_buff);
461: lua_setfield(lua, -2, "mac_address");
462: }
463:
464: lua_pushstring(lua, daemon->addrbuff);
465: lua_setfield(lua, -2, "ip_address");
466:
467: lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
468: }
469: }
470: #endif
471:
472: /* no script, just lua */
473: if (!daemon->lease_change_command)
474: continue;
475:
1.1.1.4 ! misho 476: /* Pipe to capture stdout and stderr from script */
! 477: if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1)
! 478: continue;
! 479:
1.1 misho 480: /* possible fork errors are all temporary resource problems */
481: while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
482: sleep(2);
483:
484: if (pid == -1)
1.1.1.4 ! misho 485: {
! 486: if (!option_bool(OPT_DEBUG))
! 487: {
! 488: close(pipeout[0]);
! 489: close(pipeout[1]);
! 490: }
! 491: continue;
! 492: }
1.1 misho 493:
494: /* wait for child to complete */
495: if (pid != 0)
496: {
1.1.1.4 ! misho 497: if (!option_bool(OPT_DEBUG))
! 498: {
! 499: FILE *fp;
! 500:
! 501: close(pipeout[1]);
! 502:
! 503: /* Read lines sent to stdout/err by the script and pass them back to be logged */
! 504: if (!(fp = fdopen(pipeout[0], "r")))
! 505: close(pipeout[0]);
! 506: else
! 507: {
! 508: while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
! 509: {
! 510: /* do not include new lines, log will append them */
! 511: size_t len = strlen(daemon->packet);
! 512: if (len > 0)
! 513: {
! 514: --len;
! 515: if (daemon->packet[len] == '\n')
! 516: daemon->packet[len] = 0;
! 517: }
! 518: send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
! 519: }
! 520: fclose(fp);
! 521: }
! 522: }
! 523:
1.1 misho 524: /* reap our children's children, if necessary */
525: while (1)
526: {
527: int status;
528: pid_t rc = wait(&status);
529:
530: if (rc == pid)
531: {
532: /* On error send event back to main process for logging */
533: if (WIFSIGNALED(status))
534: send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
535: else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
536: send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
537: break;
538: }
539:
540: if (rc == -1 && errno != EINTR)
541: break;
542: }
543:
544: continue;
545: }
1.1.1.4 ! misho 546:
! 547: if (!option_bool(OPT_DEBUG))
! 548: {
! 549: /* map stdout/stderr of script to pipeout */
! 550: close(pipeout[0]);
! 551: dup2(pipeout[1], STDOUT_FILENO);
! 552: dup2(pipeout[1], STDERR_FILENO);
! 553: close(pipeout[1]);
! 554: }
1.1 misho 555:
1.1.1.3 misho 556: if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
1.1 misho 557: {
1.1.1.2 misho 558: #ifdef HAVE_DHCP6
559: my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
560: my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err);
561: my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
562: #endif
1.1 misho 563:
1.1.1.2 misho 564: my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
565: my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
1.1 misho 566:
567: #ifdef HAVE_BROKEN_RTC
568: sprintf(daemon->dhcp_buff2, "%u", data.length);
569: my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
570: #else
571: sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
572: my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
573: #endif
574:
1.1.1.2 misho 575: my_setenv("DNSMASQ_DOMAIN", domain, &err);
1.1 misho 576:
577: end = extradata + data.ed_len;
578: buf = extradata;
579:
580: if (!is6)
581: buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
582: #ifdef HAVE_DHCP6
583: else
584: {
1.1.1.2 misho 585: if (data.vendorclass_count != 0)
1.1 misho 586: {
587: buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
1.1.1.2 misho 588: for (i = 0; i < data.vendorclass_count - 1; i++)
1.1 misho 589: {
590: sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
591: buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
592: }
593: }
594: }
595: #endif
596:
597: buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
598:
599: if (!is6)
600: {
601: buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
602: buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
603: buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
604: buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
605: buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
606: buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
1.1.1.4 ! misho 607: buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
1.1 misho 608: }
609:
610: buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
611:
612: if (is6)
613: buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
1.1.1.2 misho 614: else
615: my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
1.1 misho 616:
617: for (i = 0; buf; i++)
618: {
619: sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
620: buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
621: }
622:
1.1.1.2 misho 623: sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
624: my_setenv("DNSMASQ_TIME_REMAINING", data.action != ACTION_DEL && data.remaining_time != 0 ? daemon->dhcp_buff2 : NULL, &err);
1.1 misho 625:
1.1.1.2 misho 626: my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
627: if (data.action == ACTION_OLD_HOSTNAME)
628: hostname = NULL;
1.1.1.3 misho 629:
630: my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
1.1.1.4 ! misho 631: }
! 632:
1.1 misho 633: /* we need to have the event_fd around if exec fails */
634: if ((i = fcntl(event_fd, F_GETFD)) != -1)
635: fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
636: close(pipefd[0]);
637:
638: p = strrchr(daemon->lease_change_command, '/');
639: if (err == 0)
640: {
641: execl(daemon->lease_change_command,
1.1.1.3 misho 642: p ? p+1 : daemon->lease_change_command, action_str,
643: (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
1.1.1.2 misho 644: daemon->addrbuff, hostname, (char*)NULL);
1.1 misho 645: err = errno;
646: }
647: /* failed, send event so the main process logs the problem */
648: send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
649: _exit(0);
650: }
651: }
652:
653: static void my_setenv(const char *name, const char *value, int *error)
654: {
1.1.1.2 misho 655: if (*error == 0)
656: {
657: if (!value)
658: unsetenv(name);
659: else if (setenv(name, value, 1) != 0)
660: *error = errno;
661: }
1.1 misho 662: }
663:
664: static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
665: {
1.1.1.2 misho 666: unsigned char *next = NULL;
667: char *val = NULL;
1.1 misho 668:
1.1.1.2 misho 669: if (buf && (buf != end))
1.1 misho 670: {
1.1.1.2 misho 671: for (next = buf; ; next++)
672: if (next == end)
673: {
674: next = NULL;
675: break;
676: }
677: else if (*next == 0)
678: break;
1.1 misho 679:
1.1.1.2 misho 680: if (next && (next != buf))
681: {
682: char *p;
683: /* No "=" in value */
684: if ((p = strchr((char *)buf, '=')))
685: *p = 0;
686: val = (char *)buf;
687: }
688: }
689:
690: my_setenv(env, val, err);
691:
692: return next ? next + 1 : NULL;
1.1 misho 693: }
694:
695: #ifdef HAVE_LUASCRIPT
696: static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
697: {
698: unsigned char *next;
699:
700: if (!buf || (buf == end))
701: return NULL;
702:
703: for (next = buf; *next != 0; next++)
704: if (next == end)
705: return NULL;
706:
707: if (next != buf)
708: {
709: lua_pushstring(lua, (char *)buf);
710: lua_setfield(lua, -2, field);
711: }
712:
713: return next + 1;
714: }
715: #endif
716:
717: static void buff_alloc(size_t size)
718: {
719: if (size > buf_size)
720: {
721: struct script_data *new;
722:
723: /* start with reasonable size, will almost never need extending. */
724: if (size < sizeof(struct script_data) + 200)
725: size = sizeof(struct script_data) + 200;
726:
727: if (!(new = whine_malloc(size)))
728: return;
729: if (buf)
730: free(buf);
731: buf = new;
732: buf_size = size;
733: }
734: }
735:
736: /* pack up lease data into a buffer */
737: void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
738: {
739: unsigned char *p;
740: unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
741: int fd = daemon->dhcpfd;
742: #ifdef HAVE_DHCP6
743: if (!daemon->dhcp)
744: fd = daemon->dhcp6fd;
745: #endif
746:
747: /* no script */
748: if (daemon->helperfd == -1)
749: return;
750:
751: if (lease->extradata)
752: ed_len = lease->extradata_len;
753: if (lease->clid)
754: clid_len = lease->clid_len;
755: if (hostname)
756: hostname_len = strlen(hostname) + 1;
757:
758: buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
759:
760: buf->action = action;
761: buf->flags = lease->flags;
762: #ifdef HAVE_DHCP6
1.1.1.2 misho 763: buf->vendorclass_count = lease->vendorclass_count;
764: buf->addr6 = lease->addr6;
765: buf->iaid = lease->iaid;
1.1 misho 766: #endif
1.1.1.2 misho 767: buf->hwaddr_len = lease->hwaddr_len;
1.1 misho 768: buf->hwaddr_type = lease->hwaddr_type;
769: buf->clid_len = clid_len;
770: buf->ed_len = ed_len;
771: buf->hostname_len = hostname_len;
772: buf->addr = lease->addr;
773: buf->giaddr = lease->giaddr;
774: memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
775: if (!indextoname(fd, lease->last_interface, buf->interface))
776: buf->interface[0] = 0;
777:
778: #ifdef HAVE_BROKEN_RTC
779: buf->length = lease->length;
780: #else
781: buf->expires = lease->expires;
782: #endif
783:
784: if (lease->expires != 0)
785: buf->remaining_time = (unsigned int)difftime(lease->expires, now);
786: else
787: buf->remaining_time = 0;
788:
789: p = (unsigned char *)(buf+1);
790: if (clid_len != 0)
791: {
792: memcpy(p, lease->clid, clid_len);
793: p += clid_len;
794: }
795: if (hostname_len != 0)
796: {
797: memcpy(p, hostname, hostname_len);
798: p += hostname_len;
799: }
800: if (ed_len != 0)
801: {
802: memcpy(p, lease->extradata, ed_len);
803: p += ed_len;
804: }
805: bytes_in_buf = p - (unsigned char *)buf;
806: }
807:
808: #ifdef HAVE_TFTP
809: /* This nastily re-uses DHCP-fields for TFTP stuff */
810: void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
811: {
812: unsigned int filename_len;
813:
814: /* no script */
815: if (daemon->helperfd == -1)
816: return;
817:
818: filename_len = strlen(filename) + 1;
819: buff_alloc(sizeof(struct script_data) + filename_len);
820: memset(buf, 0, sizeof(struct script_data));
821:
822: buf->action = ACTION_TFTP;
823: buf->hostname_len = filename_len;
1.1.1.2 misho 824: buf->file_len = file_len;
1.1 misho 825:
826: if ((buf->flags = peer->sa.sa_family) == AF_INET)
827: buf->addr = peer->in.sin_addr;
828: else
1.1.1.2 misho 829: buf->addr6 = peer->in6.sin6_addr;
1.1 misho 830:
831: memcpy((unsigned char *)(buf+1), filename, filename_len);
832:
833: bytes_in_buf = sizeof(struct script_data) + filename_len;
834: }
835: #endif
1.1.1.3 misho 836:
1.1.1.4 ! misho 837: void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr)
1.1.1.3 misho 838: {
839: /* no script */
840: if (daemon->helperfd == -1)
841: return;
842:
843: buff_alloc(sizeof(struct script_data));
844: memset(buf, 0, sizeof(struct script_data));
845:
846: buf->action = action;
847: buf->hwaddr_len = maclen;
848: buf->hwaddr_type = ARPHRD_ETHER;
849: if ((buf->flags = family) == AF_INET)
1.1.1.4 ! misho 850: buf->addr = addr->addr4;
1.1.1.3 misho 851: else
1.1.1.4 ! misho 852: buf->addr6 = addr->addr6;
1.1.1.3 misho 853:
854: memcpy(buf->hwaddr, mac, maclen);
855:
856: bytes_in_buf = sizeof(struct script_data);
857: }
1.1 misho 858:
859: int helper_buf_empty(void)
860: {
861: return bytes_in_buf == 0;
862: }
863:
864: void helper_write(void)
865: {
866: ssize_t rc;
867:
868: if (bytes_in_buf == 0)
869: return;
870:
871: if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
872: {
873: if (bytes_in_buf != (size_t)rc)
874: memmove(buf, buf + rc, bytes_in_buf - rc);
875: bytes_in_buf -= rc;
876: }
877: else
878: {
879: if (errno == EAGAIN || errno == EINTR)
880: return;
881: bytes_in_buf = 0;
882: }
883: }
884:
885: #endif
886:
887:
888:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>