blob: 02340a01c00031db0cc682c8a4a279cfc1db574e [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
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
21/* This file has code to fork a helper process which receives data via a pipe
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
33static void my_setenv(const char *name, const char *value, int *error);
34static 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
46lua_State *lua;
47
48static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
49#endif
50
51
52struct 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
64#ifdef HAVE_TFTP
65 off_t file_len;
66#endif
67 struct in6_addr addr6;
68#ifdef HAVE_DHCP6
69 int vendorclass_count;
70 unsigned int iaid;
71#endif
72 unsigned char hwaddr[DHCP_CHADDR_MAX];
73 char interface[IF_NAMESIZE];
74};
75
76static struct script_data *buf = NULL;
77static size_t bytes_in_buf = 0, buf_size = 0;
78
79int 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;
84 unsigned char *alloc_buff = NULL;
85
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
100 /* ignore SIGTERM and SIGINT, so that we can clean up when the main process gets hit
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);
107 sigaction(SIGINT, &sigact, NULL);
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. */
134 close_fds(max_fd, pipefd[0], event_fd, err_fd);
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;
187 unsigned char *end, *extradata;
188 int is6, err = 0;
189 int pipeout[2];
190
191 /* Free rarely-allocated memory from previous iteration. */
192 if (alloc_buff)
193 {
194 free(alloc_buff);
195 alloc_buff = NULL;
196 }
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 }
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
237 continue;
238
239 /* stringify MAC into dhcp_buff */
240 p = daemon->dhcp_buff;
241 if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
242 p += sprintf(p, "%.2x-", data.hwaddr_type);
243 for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
244 {
245 p += sprintf(p, "%.2x", data.hwaddr[i]);
246 if (i != data.hwaddr_len - 1)
247 p += sprintf(p, ":");
248 }
249
250 /* supplied data may just exceed normal buffer (unlikely) */
251 if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
252 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
253 continue;
254
255 if (!read_write(pipefd[0], buf,
256 data.hostname_len + data.ed_len + data.clid_len, 1))
257 continue;
258
259 /* CLID into packet */
260 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
261 {
262 p += sprintf(p, "%.2x", buf[i]);
263 if (i != data.clid_len - 1)
264 p += sprintf(p, ":");
265 }
266
267#ifdef HAVE_DHCP6
268 if (is6)
269 {
270 /* or IAID and server DUID for IPv6 */
271 sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.iaid);
272 for (p = daemon->dhcp_packet.iov_base, i = 0; i < daemon->duid_len; i++)
273 {
274 p += sprintf(p, "%.2x", daemon->duid[i]);
275 if (i != daemon->duid_len - 1)
276 p += sprintf(p, ":");
277 }
278
279 }
280#endif
281
282 buf += data.clid_len;
283
284 if (data.hostname_len != 0)
285 {
286 char *dot;
287 hostname = (char *)buf;
288 hostname[data.hostname_len - 1] = 0;
289 if (data.action != ACTION_TFTP)
290 {
291 if (!legal_hostname(hostname))
292 hostname = NULL;
293 else if ((dot = strchr(hostname, '.')))
294 {
295 domain = dot+1;
296 *dot = 0;
297 }
298 }
299 }
300
301 extradata = buf + data.hostname_len;
302
303 if (!is6)
304 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
305 else
306 inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
307
308#ifdef HAVE_TFTP
309 /* file length */
310 if (data.action == ACTION_TFTP)
311 sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
312#endif
313
314#ifdef HAVE_LUASCRIPT
315 if (daemon->luascript)
316 {
317 if (data.action == ACTION_TFTP)
318 {
319 lua_getglobal(lua, "tftp");
320 if (lua_type(lua, -1) != LUA_TFUNCTION)
321 lua_pop(lua, 1); /* tftp function optional */
322 else
323 {
324 lua_pushstring(lua, action_str); /* arg1 - action */
325 lua_newtable(lua); /* arg2 - data table */
326 lua_pushstring(lua, daemon->addrbuff);
327 lua_setfield(lua, -2, "destination_address");
328 lua_pushstring(lua, hostname);
329 lua_setfield(lua, -2, "file_name");
330 lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
331 lua_setfield(lua, -2, "file_size");
332 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
333 }
334 }
335 else if (data.action == ACTION_ARP)
336 {
337 lua_getglobal(lua, "arp");
338 if (lua_type(lua, -1) != LUA_TFUNCTION)
339 lua_pop(lua, 1); /* arp function optional */
340 else
341 {
342 lua_pushstring(lua, action_str); /* arg1 - action */
343 lua_newtable(lua); /* arg2 - data table */
344 lua_pushstring(lua, daemon->addrbuff);
345 lua_setfield(lua, -2, "client_address");
346 lua_pushstring(lua, daemon->dhcp_buff);
347 lua_setfield(lua, -2, "mac_address");
348 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
349 }
350 }
351 else
352 {
353 lua_getglobal(lua, "lease"); /* function to call */
354 lua_pushstring(lua, action_str); /* arg1 - action */
355 lua_newtable(lua); /* arg2 - data table */
356
357 if (is6)
358 {
359 lua_pushstring(lua, daemon->packet);
360 lua_setfield(lua, -2, "client_duid");
361 lua_pushstring(lua, daemon->dhcp_packet.iov_base);
362 lua_setfield(lua, -2, "server_duid");
363 lua_pushstring(lua, daemon->dhcp_buff3);
364 lua_setfield(lua, -2, "iaid");
365 }
366
367 if (!is6 && data.clid_len != 0)
368 {
369 lua_pushstring(lua, daemon->packet);
370 lua_setfield(lua, -2, "client_id");
371 }
372
373 if (strlen(data.interface) != 0)
374 {
375 lua_pushstring(lua, data.interface);
376 lua_setfield(lua, -2, "interface");
377 }
378
379#ifdef HAVE_BROKEN_RTC
380 lua_pushnumber(lua, data.length);
381 lua_setfield(lua, -2, "lease_length");
382#else
383 lua_pushnumber(lua, data.expires);
384 lua_setfield(lua, -2, "lease_expires");
385#endif
386
387 if (hostname)
388 {
389 lua_pushstring(lua, hostname);
390 lua_setfield(lua, -2, "hostname");
391 }
392
393 if (domain)
394 {
395 lua_pushstring(lua, domain);
396 lua_setfield(lua, -2, "domain");
397 }
398
399 end = extradata + data.ed_len;
400 buf = extradata;
401
402 if (!is6)
403 buf = grab_extradata_lua(buf, end, "vendor_class");
404#ifdef HAVE_DHCP6
405 else if (data.vendorclass_count != 0)
406 {
407 sprintf(daemon->dhcp_buff2, "vendor_class_id");
408 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
409 for (i = 0; i < data.vendorclass_count - 1; i++)
410 {
411 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
412 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
413 }
414 }
415#endif
416
417 buf = grab_extradata_lua(buf, end, "supplied_hostname");
418
419 if (!is6)
420 {
421 buf = grab_extradata_lua(buf, end, "cpewan_oui");
422 buf = grab_extradata_lua(buf, end, "cpewan_serial");
423 buf = grab_extradata_lua(buf, end, "cpewan_class");
424 buf = grab_extradata_lua(buf, end, "circuit_id");
425 buf = grab_extradata_lua(buf, end, "subscriber_id");
426 buf = grab_extradata_lua(buf, end, "remote_id");
427 }
428
429 buf = grab_extradata_lua(buf, end, "tags");
430
431 if (is6)
432 buf = grab_extradata_lua(buf, end, "relay_address");
433 else if (data.giaddr.s_addr != 0)
434 {
435 inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN);
436 lua_pushstring(lua, daemon->addrbuff);
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
458 if (!is6 || data.hwaddr_len != 0)
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
476 /* Pipe to capture stdout and stderr from script */
477 if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1)
478 continue;
479
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)
485 {
486 if (!option_bool(OPT_DEBUG))
487 {
488 close(pipeout[0]);
489 close(pipeout[1]);
490 }
491 continue;
492 }
493
494 /* wait for child to complete */
495 if (pid != 0)
496 {
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
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 }
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 }
555
556 if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
557 {
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
563
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);
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
575 my_setenv("DNSMASQ_DOMAIN", domain, &err);
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 {
585 if (data.vendorclass_count != 0)
586 {
587 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
588 for (i = 0; i < data.vendorclass_count - 1; i++)
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);
607 buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
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);
614 else
615 {
616 const char *giaddr = NULL;
617 if (data.giaddr.s_addr != 0)
618 giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN);
619 my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err);
620 }
621
622 for (i = 0; buf; i++)
623 {
624 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
625 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
626 }
627
628 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
629 my_setenv("DNSMASQ_TIME_REMAINING", data.action != ACTION_DEL && data.remaining_time != 0 ? daemon->dhcp_buff2 : NULL, &err);
630
631 my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
632 if (data.action == ACTION_OLD_HOSTNAME)
633 hostname = NULL;
634
635 my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
636 }
637
638 /* we need to have the event_fd around if exec fails */
639 if ((i = fcntl(event_fd, F_GETFD)) != -1)
640 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
641 close(pipefd[0]);
642
643 p = strrchr(daemon->lease_change_command, '/');
644 if (err == 0)
645 {
646 execl(daemon->lease_change_command,
647 p ? p+1 : daemon->lease_change_command, action_str,
648 (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
649 daemon->addrbuff, hostname, (char*)NULL);
650 err = errno;
651 }
652 /* failed, send event so the main process logs the problem */
653 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
654 _exit(0);
655 }
656}
657
658static void my_setenv(const char *name, const char *value, int *error)
659{
660 if (*error == 0)
661 {
662 if (!value)
663 unsetenv(name);
664 else if (setenv(name, value, 1) != 0)
665 *error = errno;
666 }
667}
668
669static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
670{
671 unsigned char *next = NULL;
672 char *val = NULL;
673
674 if (buf && (buf != end))
675 {
676 for (next = buf; ; next++)
677 if (next == end)
678 {
679 next = NULL;
680 break;
681 }
682 else if (*next == 0)
683 break;
684
685 if (next && (next != buf))
686 {
687 char *p;
688 /* No "=" in value */
689 if ((p = strchr((char *)buf, '=')))
690 *p = 0;
691 val = (char *)buf;
692 }
693 }
694
695 my_setenv(env, val, err);
696
697 return next ? next + 1 : NULL;
698}
699
700#ifdef HAVE_LUASCRIPT
701static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
702{
703 unsigned char *next;
704
705 if (!buf || (buf == end))
706 return NULL;
707
708 for (next = buf; *next != 0; next++)
709 if (next == end)
710 return NULL;
711
712 if (next != buf)
713 {
714 lua_pushstring(lua, (char *)buf);
715 lua_setfield(lua, -2, field);
716 }
717
718 return next + 1;
719}
720#endif
721
722static void buff_alloc(size_t size)
723{
724 if (size > buf_size)
725 {
726 struct script_data *new;
727
728 /* start with reasonable size, will almost never need extending. */
729 if (size < sizeof(struct script_data) + 200)
730 size = sizeof(struct script_data) + 200;
731
732 if (!(new = whine_malloc(size)))
733 return;
734 if (buf)
735 free(buf);
736 buf = new;
737 buf_size = size;
738 }
739}
740
741/* pack up lease data into a buffer */
742void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
743{
744 unsigned char *p;
745 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
746 int fd = daemon->dhcpfd;
747#ifdef HAVE_DHCP6
748 if (!daemon->dhcp)
749 fd = daemon->dhcp6fd;
750#endif
751
752 /* no script */
753 if (daemon->helperfd == -1)
754 return;
755
756 if (lease->extradata)
757 ed_len = lease->extradata_len;
758 if (lease->clid)
759 clid_len = lease->clid_len;
760 if (hostname)
761 hostname_len = strlen(hostname) + 1;
762
763 buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
764
765 buf->action = action;
766 buf->flags = lease->flags;
767#ifdef HAVE_DHCP6
768 buf->vendorclass_count = lease->vendorclass_count;
769 buf->addr6 = lease->addr6;
770 buf->iaid = lease->iaid;
771#endif
772 buf->hwaddr_len = lease->hwaddr_len;
773 buf->hwaddr_type = lease->hwaddr_type;
774 buf->clid_len = clid_len;
775 buf->ed_len = ed_len;
776 buf->hostname_len = hostname_len;
777 buf->addr = lease->addr;
778 buf->giaddr = lease->giaddr;
779 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
780 if (!indextoname(fd, lease->last_interface, buf->interface))
781 buf->interface[0] = 0;
782
783#ifdef HAVE_BROKEN_RTC
784 buf->length = lease->length;
785#else
786 buf->expires = lease->expires;
787#endif
788
789 if (lease->expires != 0)
790 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
791 else
792 buf->remaining_time = 0;
793
794 p = (unsigned char *)(buf+1);
795 if (clid_len != 0)
796 {
797 memcpy(p, lease->clid, clid_len);
798 p += clid_len;
799 }
800 if (hostname_len != 0)
801 {
802 memcpy(p, hostname, hostname_len);
803 p += hostname_len;
804 }
805 if (ed_len != 0)
806 {
807 memcpy(p, lease->extradata, ed_len);
808 p += ed_len;
809 }
810 bytes_in_buf = p - (unsigned char *)buf;
811}
812
813#ifdef HAVE_TFTP
814/* This nastily re-uses DHCP-fields for TFTP stuff */
815void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
816{
817 unsigned int filename_len;
818
819 /* no script */
820 if (daemon->helperfd == -1)
821 return;
822
823 filename_len = strlen(filename) + 1;
824 buff_alloc(sizeof(struct script_data) + filename_len);
825 memset(buf, 0, sizeof(struct script_data));
826
827 buf->action = ACTION_TFTP;
828 buf->hostname_len = filename_len;
829 buf->file_len = file_len;
830
831 if ((buf->flags = peer->sa.sa_family) == AF_INET)
832 buf->addr = peer->in.sin_addr;
833 else
834 buf->addr6 = peer->in6.sin6_addr;
835
836 memcpy((unsigned char *)(buf+1), filename, filename_len);
837
838 bytes_in_buf = sizeof(struct script_data) + filename_len;
839}
840#endif
841
842void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr)
843{
844 /* no script */
845 if (daemon->helperfd == -1)
846 return;
847
848 buff_alloc(sizeof(struct script_data));
849 memset(buf, 0, sizeof(struct script_data));
850
851 buf->action = action;
852 buf->hwaddr_len = maclen;
853 buf->hwaddr_type = ARPHRD_ETHER;
854 if ((buf->flags = family) == AF_INET)
855 buf->addr = addr->addr4;
856 else
857 buf->addr6 = addr->addr6;
858
859 memcpy(buf->hwaddr, mac, maclen);
860
861 bytes_in_buf = sizeof(struct script_data);
862}
863
864int helper_buf_empty(void)
865{
866 return bytes_in_buf == 0;
867}
868
869void helper_write(void)
870{
871 ssize_t rc;
872
873 if (bytes_in_buf == 0)
874 return;
875
876 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
877 {
878 if (bytes_in_buf != (size_t)rc)
879 memmove(buf, buf + rc, bytes_in_buf - rc);
880 bytes_in_buf -= rc;
881 }
882 else
883 {
884 if (errno == EAGAIN || errno == EINTR)
885 return;
886 bytes_in_buf = 0;
887 }
888}
889
890#endif /* HAVE_SCRIPT */