Annotation of embedaddon/dnsmasq/src/helper.c, revision 1.1.1.5
1.1.1.5 ! misho 1: /* dnsmasq is Copyright (c) 2000-2022 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: }
1.1.1.5 ! misho 236: else if (data.action == ACTION_RELAY_SNOOP)
! 237: {
! 238: is6 = 1;
! 239: action_str = "relay-snoop";
! 240: }
! 241: else
! 242: continue;
1.1 misho 243:
1.1.1.2 misho 244: /* stringify MAC into dhcp_buff */
245: p = daemon->dhcp_buff;
246: if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
247: p += sprintf(p, "%.2x-", data.hwaddr_type);
248: for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
249: {
250: p += sprintf(p, "%.2x", data.hwaddr[i]);
251: if (i != data.hwaddr_len - 1)
252: p += sprintf(p, ":");
1.1 misho 253: }
1.1.1.2 misho 254:
1.1 misho 255: /* supplied data may just exceed normal buffer (unlikely) */
256: if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
257: !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
258: continue;
259:
260: if (!read_write(pipefd[0], buf,
261: data.hostname_len + data.ed_len + data.clid_len, 1))
262: continue;
263:
264: /* CLID into packet */
1.1.1.2 misho 265: for (p = daemon->packet, i = 0; i < data.clid_len; i++)
266: {
267: p += sprintf(p, "%.2x", buf[i]);
268: if (i != data.clid_len - 1)
1.1 misho 269: p += sprintf(p, ":");
1.1.1.2 misho 270: }
271:
1.1 misho 272: #ifdef HAVE_DHCP6
1.1.1.2 misho 273: if (is6)
1.1 misho 274: {
275: /* or IAID and server DUID for IPv6 */
1.1.1.2 misho 276: sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.iaid);
277: for (p = daemon->dhcp_packet.iov_base, i = 0; i < daemon->duid_len; i++)
1.1 misho 278: {
279: p += sprintf(p, "%.2x", daemon->duid[i]);
280: if (i != daemon->duid_len - 1)
281: p += sprintf(p, ":");
282: }
283:
284: }
285: #endif
286:
287: buf += data.clid_len;
288:
289: if (data.hostname_len != 0)
290: {
291: char *dot;
292: hostname = (char *)buf;
293: hostname[data.hostname_len - 1] = 0;
1.1.1.5 ! misho 294: if (data.action != ACTION_TFTP && data.action != ACTION_RELAY_SNOOP)
1.1 misho 295: {
296: if (!legal_hostname(hostname))
297: hostname = NULL;
298: else if ((dot = strchr(hostname, '.')))
299: {
300: domain = dot+1;
301: *dot = 0;
302: }
303: }
304: }
305:
306: extradata = buf + data.hostname_len;
307:
308: if (!is6)
309: inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
310: else
1.1.1.2 misho 311: inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
1.1 misho 312:
1.1.1.2 misho 313: #ifdef HAVE_TFTP
1.1 misho 314: /* file length */
315: if (data.action == ACTION_TFTP)
1.1.1.2 misho 316: sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
317: #endif
318:
1.1 misho 319: #ifdef HAVE_LUASCRIPT
320: if (daemon->luascript)
321: {
322: if (data.action == ACTION_TFTP)
323: {
324: lua_getglobal(lua, "tftp");
325: if (lua_type(lua, -1) != LUA_TFUNCTION)
326: lua_pop(lua, 1); /* tftp function optional */
327: else
328: {
329: lua_pushstring(lua, action_str); /* arg1 - action */
330: lua_newtable(lua); /* arg2 - data table */
331: lua_pushstring(lua, daemon->addrbuff);
332: lua_setfield(lua, -2, "destination_address");
333: lua_pushstring(lua, hostname);
334: lua_setfield(lua, -2, "file_name");
1.1.1.2 misho 335: lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
1.1 misho 336: lua_setfield(lua, -2, "file_size");
337: lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
338: }
339: }
1.1.1.5 ! misho 340: else if (data.action == ACTION_RELAY_SNOOP)
! 341: {
! 342: lua_getglobal(lua, "snoop");
! 343: if (lua_type(lua, -1) != LUA_TFUNCTION)
! 344: lua_pop(lua, 1); /* tftp function optional */
! 345: else
! 346: {
! 347: lua_pushstring(lua, action_str); /* arg1 - action */
! 348: lua_newtable(lua); /* arg2 - data table */
! 349: lua_pushstring(lua, daemon->addrbuff);
! 350: lua_setfield(lua, -2, "client_address");
! 351: lua_pushstring(lua, hostname);
! 352: lua_setfield(lua, -2, "prefix");
! 353: lua_pushstring(lua, data.interface);
! 354: lua_setfield(lua, -2, "client_interface");
! 355: lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
! 356: }
! 357: }
1.1.1.3 misho 358: else if (data.action == ACTION_ARP)
359: {
360: lua_getglobal(lua, "arp");
361: if (lua_type(lua, -1) != LUA_TFUNCTION)
362: lua_pop(lua, 1); /* arp function optional */
363: else
364: {
365: lua_pushstring(lua, action_str); /* arg1 - action */
366: lua_newtable(lua); /* arg2 - data table */
367: lua_pushstring(lua, daemon->addrbuff);
368: lua_setfield(lua, -2, "client_address");
369: lua_pushstring(lua, daemon->dhcp_buff);
370: lua_setfield(lua, -2, "mac_address");
371: lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
372: }
373: }
1.1 misho 374: else
375: {
376: lua_getglobal(lua, "lease"); /* function to call */
377: lua_pushstring(lua, action_str); /* arg1 - action */
378: lua_newtable(lua); /* arg2 - data table */
379:
380: if (is6)
381: {
382: lua_pushstring(lua, daemon->packet);
1.1.1.2 misho 383: lua_setfield(lua, -2, "client_duid");
384: lua_pushstring(lua, daemon->dhcp_packet.iov_base);
1.1 misho 385: lua_setfield(lua, -2, "server_duid");
386: lua_pushstring(lua, daemon->dhcp_buff3);
387: lua_setfield(lua, -2, "iaid");
388: }
389:
390: if (!is6 && data.clid_len != 0)
391: {
392: lua_pushstring(lua, daemon->packet);
393: lua_setfield(lua, -2, "client_id");
394: }
395:
396: if (strlen(data.interface) != 0)
397: {
398: lua_pushstring(lua, data.interface);
399: lua_setfield(lua, -2, "interface");
400: }
401:
402: #ifdef HAVE_BROKEN_RTC
403: lua_pushnumber(lua, data.length);
404: lua_setfield(lua, -2, "lease_length");
405: #else
406: lua_pushnumber(lua, data.expires);
407: lua_setfield(lua, -2, "lease_expires");
408: #endif
409:
410: if (hostname)
411: {
412: lua_pushstring(lua, hostname);
413: lua_setfield(lua, -2, "hostname");
414: }
415:
416: if (domain)
417: {
418: lua_pushstring(lua, domain);
419: lua_setfield(lua, -2, "domain");
420: }
421:
422: end = extradata + data.ed_len;
423: buf = extradata;
1.1.1.5 ! misho 424:
! 425: lua_pushnumber(lua, data.ed_len == 0 ? 1 : 0);
! 426: lua_setfield(lua, -2, "data_missing");
1.1 misho 427:
428: if (!is6)
429: buf = grab_extradata_lua(buf, end, "vendor_class");
430: #ifdef HAVE_DHCP6
1.1.1.2 misho 431: else if (data.vendorclass_count != 0)
432: {
433: sprintf(daemon->dhcp_buff2, "vendor_class_id");
434: buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
435: for (i = 0; i < data.vendorclass_count - 1; i++)
436: {
437: sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
438: buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
439: }
440: }
1.1 misho 441: #endif
442:
443: buf = grab_extradata_lua(buf, end, "supplied_hostname");
444:
445: if (!is6)
446: {
447: buf = grab_extradata_lua(buf, end, "cpewan_oui");
448: buf = grab_extradata_lua(buf, end, "cpewan_serial");
449: buf = grab_extradata_lua(buf, end, "cpewan_class");
450: buf = grab_extradata_lua(buf, end, "circuit_id");
451: buf = grab_extradata_lua(buf, end, "subscriber_id");
452: buf = grab_extradata_lua(buf, end, "remote_id");
453: }
1.1.1.5 ! misho 454:
! 455: buf = grab_extradata_lua(buf, end, "requested_options");
! 456: buf = grab_extradata_lua(buf, end, "mud_url");
1.1 misho 457: buf = grab_extradata_lua(buf, end, "tags");
458:
459: if (is6)
460: buf = grab_extradata_lua(buf, end, "relay_address");
461: else if (data.giaddr.s_addr != 0)
462: {
1.1.1.5 ! misho 463: inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN);
! 464: lua_pushstring(lua, daemon->dhcp_buff2);
1.1 misho 465: lua_setfield(lua, -2, "relay_address");
466: }
467:
468: for (i = 0; buf; i++)
469: {
470: sprintf(daemon->dhcp_buff2, "user_class%i", i);
471: buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
472: }
473:
474: if (data.action != ACTION_DEL && data.remaining_time != 0)
475: {
476: lua_pushnumber(lua, data.remaining_time);
477: lua_setfield(lua, -2, "time_remaining");
478: }
479:
480: if (data.action == ACTION_OLD_HOSTNAME && hostname)
481: {
482: lua_pushstring(lua, hostname);
483: lua_setfield(lua, -2, "old_hostname");
484: }
485:
1.1.1.2 misho 486: if (!is6 || data.hwaddr_len != 0)
1.1 misho 487: {
488: lua_pushstring(lua, daemon->dhcp_buff);
489: lua_setfield(lua, -2, "mac_address");
490: }
491:
492: lua_pushstring(lua, daemon->addrbuff);
493: lua_setfield(lua, -2, "ip_address");
494:
495: lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
496: }
497: }
498: #endif
499:
500: /* no script, just lua */
501: if (!daemon->lease_change_command)
502: continue;
503:
1.1.1.4 misho 504: /* Pipe to capture stdout and stderr from script */
505: if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1)
506: continue;
507:
1.1 misho 508: /* possible fork errors are all temporary resource problems */
509: while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
510: sleep(2);
511:
512: if (pid == -1)
1.1.1.4 misho 513: {
514: if (!option_bool(OPT_DEBUG))
515: {
516: close(pipeout[0]);
517: close(pipeout[1]);
518: }
519: continue;
520: }
1.1 misho 521:
522: /* wait for child to complete */
523: if (pid != 0)
524: {
1.1.1.4 misho 525: if (!option_bool(OPT_DEBUG))
526: {
527: FILE *fp;
528:
529: close(pipeout[1]);
530:
531: /* Read lines sent to stdout/err by the script and pass them back to be logged */
532: if (!(fp = fdopen(pipeout[0], "r")))
533: close(pipeout[0]);
534: else
535: {
536: while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
537: {
538: /* do not include new lines, log will append them */
539: size_t len = strlen(daemon->packet);
540: if (len > 0)
541: {
542: --len;
543: if (daemon->packet[len] == '\n')
544: daemon->packet[len] = 0;
545: }
546: send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
547: }
548: fclose(fp);
549: }
550: }
551:
1.1 misho 552: /* reap our children's children, if necessary */
553: while (1)
554: {
555: int status;
556: pid_t rc = wait(&status);
557:
558: if (rc == pid)
559: {
560: /* On error send event back to main process for logging */
561: if (WIFSIGNALED(status))
562: send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
563: else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
564: send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
565: break;
566: }
567:
568: if (rc == -1 && errno != EINTR)
569: break;
570: }
571:
572: continue;
573: }
1.1.1.4 misho 574:
575: if (!option_bool(OPT_DEBUG))
576: {
577: /* map stdout/stderr of script to pipeout */
578: close(pipeout[0]);
579: dup2(pipeout[1], STDOUT_FILENO);
580: dup2(pipeout[1], STDERR_FILENO);
581: close(pipeout[1]);
582: }
1.1 misho 583:
1.1.1.5 ! misho 584: if (data.action != ACTION_TFTP && data.action != ACTION_ARP && data.action != ACTION_RELAY_SNOOP)
1.1 misho 585: {
1.1.1.2 misho 586: #ifdef HAVE_DHCP6
587: my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
588: my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err);
589: my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
590: #endif
1.1 misho 591:
1.1.1.2 misho 592: my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
593: my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
1.1 misho 594:
595: #ifdef HAVE_BROKEN_RTC
596: sprintf(daemon->dhcp_buff2, "%u", data.length);
597: my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
598: #else
599: sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
600: my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
601: #endif
602:
1.1.1.2 misho 603: my_setenv("DNSMASQ_DOMAIN", domain, &err);
1.1 misho 604:
605: end = extradata + data.ed_len;
606: buf = extradata;
1.1.1.5 ! misho 607:
! 608: if (data.ed_len == 0)
! 609: my_setenv("DNSMASQ_DATA_MISSING", "1", &err);
1.1 misho 610:
611: if (!is6)
612: buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
613: #ifdef HAVE_DHCP6
614: else
615: {
1.1.1.2 misho 616: if (data.vendorclass_count != 0)
1.1 misho 617: {
618: buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
1.1.1.2 misho 619: for (i = 0; i < data.vendorclass_count - 1; i++)
1.1 misho 620: {
621: sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
622: buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
623: }
624: }
625: }
626: #endif
627:
628: buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
629:
630: if (!is6)
631: {
632: buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
633: buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
634: buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
635: buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
636: buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
637: buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
638: }
639:
1.1.1.5 ! misho 640: buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
! 641: buf = grab_extradata(buf, end, "DNSMASQ_MUD_URL", &err);
1.1 misho 642: buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
1.1.1.5 ! misho 643:
1.1 misho 644: if (is6)
645: buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
1.1.1.5 ! misho 646: else
! 647: {
! 648: const char *giaddr = NULL;
! 649: if (data.giaddr.s_addr != 0)
! 650: giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN);
! 651: my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err);
! 652: }
1.1 misho 653:
654: for (i = 0; buf; i++)
655: {
656: sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
657: buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
658: }
659:
1.1.1.2 misho 660: sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
661: my_setenv("DNSMASQ_TIME_REMAINING", data.action != ACTION_DEL && data.remaining_time != 0 ? daemon->dhcp_buff2 : NULL, &err);
1.1 misho 662:
1.1.1.2 misho 663: my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
664: if (data.action == ACTION_OLD_HOSTNAME)
665: hostname = NULL;
1.1.1.3 misho 666:
667: my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
1.1.1.4 misho 668: }
669:
1.1 misho 670: /* we need to have the event_fd around if exec fails */
671: if ((i = fcntl(event_fd, F_GETFD)) != -1)
672: fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
673: close(pipefd[0]);
674:
1.1.1.5 ! misho 675: if (data.action == ACTION_RELAY_SNOOP)
! 676: strcpy(daemon->packet, data.interface);
! 677:
1.1 misho 678: p = strrchr(daemon->lease_change_command, '/');
679: if (err == 0)
680: {
681: execl(daemon->lease_change_command,
1.1.1.3 misho 682: p ? p+1 : daemon->lease_change_command, action_str,
683: (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
1.1.1.2 misho 684: daemon->addrbuff, hostname, (char*)NULL);
1.1 misho 685: err = errno;
686: }
687: /* failed, send event so the main process logs the problem */
688: send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
689: _exit(0);
690: }
691: }
692:
693: static void my_setenv(const char *name, const char *value, int *error)
694: {
1.1.1.2 misho 695: if (*error == 0)
696: {
697: if (!value)
698: unsetenv(name);
699: else if (setenv(name, value, 1) != 0)
700: *error = errno;
701: }
1.1 misho 702: }
703:
704: static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
705: {
1.1.1.2 misho 706: unsigned char *next = NULL;
707: char *val = NULL;
1.1 misho 708:
1.1.1.2 misho 709: if (buf && (buf != end))
1.1 misho 710: {
1.1.1.2 misho 711: for (next = buf; ; next++)
712: if (next == end)
713: {
714: next = NULL;
715: break;
716: }
717: else if (*next == 0)
718: break;
1.1 misho 719:
1.1.1.2 misho 720: if (next && (next != buf))
721: {
722: char *p;
723: /* No "=" in value */
724: if ((p = strchr((char *)buf, '=')))
725: *p = 0;
726: val = (char *)buf;
727: }
728: }
729:
730: my_setenv(env, val, err);
731:
732: return next ? next + 1 : NULL;
1.1 misho 733: }
734:
735: #ifdef HAVE_LUASCRIPT
736: static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
737: {
738: unsigned char *next;
739:
740: if (!buf || (buf == end))
741: return NULL;
742:
743: for (next = buf; *next != 0; next++)
744: if (next == end)
745: return NULL;
746:
747: if (next != buf)
748: {
749: lua_pushstring(lua, (char *)buf);
750: lua_setfield(lua, -2, field);
751: }
752:
753: return next + 1;
754: }
755: #endif
756:
757: static void buff_alloc(size_t size)
758: {
759: if (size > buf_size)
760: {
761: struct script_data *new;
762:
763: /* start with reasonable size, will almost never need extending. */
764: if (size < sizeof(struct script_data) + 200)
765: size = sizeof(struct script_data) + 200;
766:
767: if (!(new = whine_malloc(size)))
768: return;
769: if (buf)
770: free(buf);
771: buf = new;
772: buf_size = size;
773: }
774: }
775:
776: /* pack up lease data into a buffer */
777: void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
778: {
779: unsigned char *p;
780: unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
781: int fd = daemon->dhcpfd;
782: #ifdef HAVE_DHCP6
783: if (!daemon->dhcp)
784: fd = daemon->dhcp6fd;
785: #endif
786:
787: /* no script */
788: if (daemon->helperfd == -1)
789: return;
790:
791: if (lease->extradata)
792: ed_len = lease->extradata_len;
793: if (lease->clid)
794: clid_len = lease->clid_len;
795: if (hostname)
796: hostname_len = strlen(hostname) + 1;
797:
798: buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
799:
800: buf->action = action;
801: buf->flags = lease->flags;
802: #ifdef HAVE_DHCP6
1.1.1.2 misho 803: buf->vendorclass_count = lease->vendorclass_count;
804: buf->addr6 = lease->addr6;
805: buf->iaid = lease->iaid;
1.1 misho 806: #endif
1.1.1.2 misho 807: buf->hwaddr_len = lease->hwaddr_len;
1.1 misho 808: buf->hwaddr_type = lease->hwaddr_type;
809: buf->clid_len = clid_len;
810: buf->ed_len = ed_len;
811: buf->hostname_len = hostname_len;
812: buf->addr = lease->addr;
813: buf->giaddr = lease->giaddr;
814: memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
815: if (!indextoname(fd, lease->last_interface, buf->interface))
816: buf->interface[0] = 0;
817:
818: #ifdef HAVE_BROKEN_RTC
819: buf->length = lease->length;
820: #else
821: buf->expires = lease->expires;
822: #endif
823:
824: if (lease->expires != 0)
825: buf->remaining_time = (unsigned int)difftime(lease->expires, now);
826: else
827: buf->remaining_time = 0;
828:
829: p = (unsigned char *)(buf+1);
830: if (clid_len != 0)
831: {
832: memcpy(p, lease->clid, clid_len);
833: p += clid_len;
834: }
835: if (hostname_len != 0)
836: {
837: memcpy(p, hostname, hostname_len);
838: p += hostname_len;
839: }
840: if (ed_len != 0)
841: {
842: memcpy(p, lease->extradata, ed_len);
843: p += ed_len;
844: }
845: bytes_in_buf = p - (unsigned char *)buf;
846: }
847:
1.1.1.5 ! misho 848: #ifdef HAVE_DHCP6
! 849: void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len)
! 850: {
! 851: /* no script */
! 852: if (daemon->helperfd == -1)
! 853: return;
! 854:
! 855: inet_ntop(AF_INET6, prefix, daemon->addrbuff, ADDRSTRLEN);
! 856:
! 857: /* 5 for /nnn and zero on the end of the prefix. */
! 858: buff_alloc(sizeof(struct script_data) + ADDRSTRLEN + 5);
! 859: memset(buf, 0, sizeof(struct script_data));
! 860:
! 861: buf->action = ACTION_RELAY_SNOOP;
! 862: buf->addr6 = *client;
! 863: buf->hostname_len = sprintf((char *)(buf+1), "%s/%u", daemon->addrbuff, prefix_len) + 1;
! 864:
! 865: indextoname(daemon->dhcp6fd, if_index, buf->interface);
! 866:
! 867: bytes_in_buf = sizeof(struct script_data) + buf->hostname_len;
! 868: }
! 869: #endif
! 870:
1.1 misho 871: #ifdef HAVE_TFTP
872: /* This nastily re-uses DHCP-fields for TFTP stuff */
873: void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
874: {
875: unsigned int filename_len;
876:
877: /* no script */
878: if (daemon->helperfd == -1)
879: return;
880:
881: filename_len = strlen(filename) + 1;
882: buff_alloc(sizeof(struct script_data) + filename_len);
883: memset(buf, 0, sizeof(struct script_data));
884:
885: buf->action = ACTION_TFTP;
886: buf->hostname_len = filename_len;
1.1.1.2 misho 887: buf->file_len = file_len;
1.1 misho 888:
889: if ((buf->flags = peer->sa.sa_family) == AF_INET)
890: buf->addr = peer->in.sin_addr;
891: else
1.1.1.2 misho 892: buf->addr6 = peer->in6.sin6_addr;
1.1 misho 893:
894: memcpy((unsigned char *)(buf+1), filename, filename_len);
895:
896: bytes_in_buf = sizeof(struct script_data) + filename_len;
897: }
898: #endif
1.1.1.3 misho 899:
1.1.1.4 misho 900: void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr)
1.1.1.3 misho 901: {
902: /* no script */
903: if (daemon->helperfd == -1)
904: return;
905:
906: buff_alloc(sizeof(struct script_data));
907: memset(buf, 0, sizeof(struct script_data));
908:
909: buf->action = action;
910: buf->hwaddr_len = maclen;
911: buf->hwaddr_type = ARPHRD_ETHER;
912: if ((buf->flags = family) == AF_INET)
1.1.1.4 misho 913: buf->addr = addr->addr4;
1.1.1.3 misho 914: else
1.1.1.4 misho 915: buf->addr6 = addr->addr6;
1.1.1.3 misho 916:
917: memcpy(buf->hwaddr, mac, maclen);
918:
919: bytes_in_buf = sizeof(struct script_data);
920: }
1.1 misho 921:
922: int helper_buf_empty(void)
923: {
924: return bytes_in_buf == 0;
925: }
926:
927: void helper_write(void)
928: {
929: ssize_t rc;
930:
931: if (bytes_in_buf == 0)
932: return;
933:
934: if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
935: {
936: if (bytes_in_buf != (size_t)rc)
937: memmove(buf, buf + rc, bytes_in_buf - rc);
938: bytes_in_buf -= rc;
939: }
940: else
941: {
942: if (errno == EAGAIN || errno == EINTR)
943: return;
944: bytes_in_buf = 0;
945: }
946: }
947:
1.1.1.5 ! misho 948: #endif /* HAVE_SCRIPT */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>