blob: 62ac9cf546e39bb92cefb561a2daac708b0bc4c6 [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
Simon Kelley16972692006-10-16 20:04:18 +01002
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
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley16972692006-10-16 20:04:18 +01008 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.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 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/>.
Simon Kelley16972692006-10-16 20:04:18 +010015*/
16
17#include "dnsmasq.h"
18
Simon Kelleyc72daea2012-01-05 21:33:27 +000019#ifdef HAVE_SCRIPT
20
Josh Soref730c6742017-02-06 16:14:04 +000021/* This file has code to fork a helper process which receives data via a pipe
Simon Kelley16972692006-10-16 20:04:18 +010022 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
Simon Kelley824af852008-02-12 20:43:05 +000033static void my_setenv(const char *name, const char *value, int *error);
Simon Kelley316e2732010-01-22 20:16:09 +000034static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err);
Simon Kelley9009d742008-11-14 20:04:27 +000035
Simon Kelleyc72daea2012-01-05 21:33:27 +000036#ifdef HAVE_LUASCRIPT
Simon Kelley289a2532012-09-20 15:29:35 +010037#define LUA_COMPAT_ALL
Simon Kelleyc72daea2012-01-05 21:33:27 +000038#include <lua.h>
39#include <lualib.h>
40#include <lauxlib.h>
41
Simon Kelley289a2532012-09-20 15:29:35 +010042#ifndef lua_open
43#define lua_open() luaL_newstate()
44#endif
45
Simon Kelleyc72daea2012-01-05 21:33:27 +000046lua_State *lua;
47
48static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
49#endif
50
51
Simon Kelley16972692006-10-16 20:04:18 +010052struct script_data
53{
Simon Kelleyceae00d2012-02-09 21:28:14 +000054 int flags;
55 int action, hwaddr_len, hwaddr_type;
56 int clid_len, hostname_len, ed_len;
Simon Kelley1f15b812009-10-13 17:49:32 +010057 struct in_addr addr, giaddr;
Simon Kelley5aabfc72007-08-29 11:24:47 +010058 unsigned int remaining_time;
Simon Kelley16972692006-10-16 20:04:18 +010059#ifdef HAVE_BROKEN_RTC
60 unsigned int length;
61#else
62 time_t expires;
63#endif
Simon Kelley2f9fd1d2013-10-01 09:54:41 +010064#ifdef HAVE_TFTP
65 off_t file_len;
66#endif
Simon Kelley89500e32013-09-20 16:29:20 +010067 struct in6_addr addr6;
Simon Kelley903650a2013-10-03 11:43:09 +010068#ifdef HAVE_DHCP6
Dominik DL6ER456a3192019-10-20 18:51:52 +020069 int vendorclass_count;
70 unsigned int iaid;
Simon Kelley89500e32013-09-20 16:29:20 +010071#endif
Simon Kelley16972692006-10-16 20:04:18 +010072 unsigned char hwaddr[DHCP_CHADDR_MAX];
Simon Kelley824af852008-02-12 20:43:05 +000073 char interface[IF_NAMESIZE];
Simon Kelley16972692006-10-16 20:04:18 +010074};
75
Simon Kelley5aabfc72007-08-29 11:24:47 +010076static struct script_data *buf = NULL;
77static size_t bytes_in_buf = 0, buf_size = 0;
Simon Kelley16972692006-10-16 20:04:18 +010078
Simon Kelley1a6bca82008-07-11 11:11:42 +010079int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
Simon Kelley16972692006-10-16 20:04:18 +010080{
81 pid_t pid;
82 int i, pipefd[2];
83 struct sigaction sigact;
Simon Kelley69bc9472019-08-14 20:44:50 +010084 unsigned char *alloc_buff = NULL;
85
Simon Kelley5aabfc72007-08-29 11:24:47 +010086 /* create the pipe through which the main program sends us commands,
Simon Kelley1a6bca82008-07-11 11:11:42 +010087 then fork our process. */
Simon Kelley5aabfc72007-08-29 11:24:47 +010088 if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
89 {
Simon Kelleyc72daea2012-01-05 21:33:27 +000090 send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +010091 _exit(0);
Simon Kelley5aabfc72007-08-29 11:24:47 +010092 }
93
Simon Kelley16972692006-10-16 20:04:18 +010094 if (pid != 0)
95 {
96 close(pipefd[0]); /* close reader side */
97 return pipefd[1];
98 }
99
Simon Kelley3c973ad2018-01-14 21:05:37 +0000100 /* ignore SIGTERM and SIGINT, so that we can clean up when the main process gets hit
Simon Kelley5aabfc72007-08-29 11:24:47 +0100101 and SIGALRM so that we can use sleep() */
Simon Kelley16972692006-10-16 20:04:18 +0100102 sigact.sa_handler = SIG_IGN;
103 sigact.sa_flags = 0;
104 sigemptyset(&sigact.sa_mask);
105 sigaction(SIGTERM, &sigact, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100106 sigaction(SIGALRM, &sigact, NULL);
Simon Kelley3c973ad2018-01-14 21:05:37 +0000107 sigaction(SIGINT, &sigact, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100108
Simon Kelley28866e92011-02-14 20:19:14 +0000109 if (!option_bool(OPT_DEBUG) && uid != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100110 {
111 gid_t dummy;
112 if (setgroups(0, &dummy) == -1 ||
113 setgid(gid) == -1 ||
114 setuid(uid) == -1)
115 {
Simon Kelley28866e92011-02-14 20:19:14 +0000116 if (option_bool(OPT_NO_FORK))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100117 /* send error to daemon process if no-fork */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000118 send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100119 else
120 {
121 /* kill daemon */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000122 send_event(event_fd, EVENT_DIE, 0, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100123 /* return error */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000124 send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100125 }
126 _exit(0);
127 }
128 }
129
Simon Kelleyc72daea2012-01-05 21:33:27 +0000130 /* 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. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100134 for (max_fd--; max_fd >= 0; max_fd--)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100135 if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
Simon Kelleyc72daea2012-01-05 21:33:27 +0000136 max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
137 max_fd != event_fd && max_fd != err_fd)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100138 close(max_fd);
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100139
Simon Kelleyc72daea2012-01-05 21:33:27 +0000140#ifdef HAVE_LUASCRIPT
141 if (daemon->luascript)
142 {
143 const char *lua_err = NULL;
144 lua = lua_open();
145 luaL_openlibs(lua);
146
147 /* get Lua to load our script file */
148 if (luaL_dofile(lua, daemon->luascript) != 0)
149 lua_err = lua_tostring(lua, -1);
150 else
151 {
152 lua_getglobal(lua, "lease");
153 if (lua_type(lua, -1) != LUA_TFUNCTION)
154 lua_err = _("lease() function missing in Lua script");
155 }
156
157 if (lua_err)
158 {
159 if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
160 /* send error to daemon process if no-fork */
161 send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
162 else
163 {
164 /* kill daemon */
165 send_event(event_fd, EVENT_DIE, 0, NULL);
166 /* return error */
167 send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
168 }
169 _exit(0);
170 }
171
172 lua_pop(lua, 1); /* remove nil from stack */
173 lua_getglobal(lua, "init");
174 if (lua_type(lua, -1) == LUA_TFUNCTION)
175 lua_call(lua, 0, 0);
176 else
177 lua_pop(lua, 1); /* remove nil from stack */
178 }
179#endif
180
181 /* All init done, close our copy of the error pipe, so that main process can return */
182 if (err_fd != -1)
183 close(err_fd);
184
Simon Kelley16972692006-10-16 20:04:18 +0100185 /* loop here */
186 while(1)
187 {
188 struct script_data data;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000189 char *p, *action_str, *hostname = NULL, *domain = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100190 unsigned char *buf = (unsigned char *)daemon->namebuff;
Simon Kelley69bc9472019-08-14 20:44:50 +0100191 unsigned char *end, *extradata;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000192 int is6, err = 0;
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100193 int pipeout[2];
Simon Kelley16972692006-10-16 20:04:18 +0100194
Simon Kelley69bc9472019-08-14 20:44:50 +0100195 /* Free rarely-allocated memory from previous iteration. */
196 if (alloc_buff)
197 {
198 free(alloc_buff);
199 alloc_buff = NULL;
200 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000201
Simon Kelley16972692006-10-16 20:04:18 +0100202 /* we read zero bytes when pipe closed: this is our signal to exit */
203 if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000204 {
205#ifdef HAVE_LUASCRIPT
206 if (daemon->luascript)
207 {
208 lua_getglobal(lua, "shutdown");
209 if (lua_type(lua, -1) == LUA_TFUNCTION)
210 lua_call(lua, 0, 0);
211 }
212#endif
213 _exit(0);
214 }
Simon Kelleya9530962012-03-20 22:07:35 +0000215
216 is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
217
Simon Kelley16972692006-10-16 20:04:18 +0100218 if (data.action == ACTION_DEL)
219 action_str = "del";
220 else if (data.action == ACTION_ADD)
221 action_str = "add";
222 else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
223 action_str = "old";
Simon Kelleya9530962012-03-20 22:07:35 +0000224 else if (data.action == ACTION_TFTP)
225 {
226 action_str = "tftp";
227 is6 = (data.flags != AF_INET);
228 }
Simon Kelley33702ab2015-12-28 23:17:15 +0000229 else if (data.action == ACTION_ARP)
230 {
Simon Kelleye6e751b2016-02-01 17:59:07 +0000231 action_str = "arp-add";
Simon Kelley33702ab2015-12-28 23:17:15 +0000232 is6 = (data.flags != AF_INET);
233 }
Simon Kelleye6e751b2016-02-01 17:59:07 +0000234 else if (data.action == ACTION_ARP_DEL)
Simon Kelley33702ab2015-12-28 23:17:15 +0000235 {
Simon Kelleye6e751b2016-02-01 17:59:07 +0000236 action_str = "arp-del";
Simon Kelley33702ab2015-12-28 23:17:15 +0000237 is6 = (data.flags != AF_INET);
238 data.action = ACTION_ARP;
239 }
240 else
Simon Kelley16972692006-10-16 20:04:18 +0100241 continue;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000242
Simon Kelleya9530962012-03-20 22:07:35 +0000243
Simon Kelley89500e32013-09-20 16:29:20 +0100244 /* 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++)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000249 {
Simon Kelley89500e32013-09-20 16:29:20 +0100250 p += sprintf(p, "%.2x", data.hwaddr[i]);
251 if (i != data.hwaddr_len - 1)
252 p += sprintf(p, ":");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000253 }
Simon Kelley89500e32013-09-20 16:29:20 +0100254
Simon Kelleyc72daea2012-01-05 21:33:27 +0000255 /* 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)))
Simon Kelley16972692006-10-16 20:04:18 +0100258 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000259
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 */
Simon Kelley89500e32013-09-20 16:29:20 +0100265 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)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000269 p += sprintf(p, ":");
Simon Kelley89500e32013-09-20 16:29:20 +0100270 }
271
Simon Kelleyceae00d2012-02-09 21:28:14 +0000272#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100273 if (is6)
Simon Kelley16972692006-10-16 20:04:18 +0100274 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000275 /* or IAID and server DUID for IPv6 */
Simon Kelley89500e32013-09-20 16:29:20 +0100276 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++)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000278 {
279 p += sprintf(p, "%.2x", daemon->duid[i]);
280 if (i != daemon->duid_len - 1)
281 p += sprintf(p, ":");
282 }
Simon Kelleycaa94382012-02-15 10:29:50 +0000283
Simon Kelley16972692006-10-16 20:04:18 +0100284 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000285#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000286
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;
Simon Kelleya9530962012-03-20 22:07:35 +0000294 if (data.action != ACTION_TFTP)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000295 {
Simon Kelleya9530962012-03-20 22:07:35 +0000296 if (!legal_hostname(hostname))
297 hostname = NULL;
298 else if ((dot = strchr(hostname, '.')))
299 {
300 domain = dot+1;
301 *dot = 0;
302 }
303 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000304 }
305
306 extradata = buf + data.hostname_len;
307
Simon Kelleyceae00d2012-02-09 21:28:14 +0000308 if (!is6)
309 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000310 else
Simon Kelley89500e32013-09-20 16:29:20 +0100311 inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000312
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100313#ifdef HAVE_TFTP
Simon Kelleya9530962012-03-20 22:07:35 +0000314 /* file length */
315 if (data.action == ACTION_TFTP)
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100316 sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
317#endif
318
Simon Kelleyc72daea2012-01-05 21:33:27 +0000319#ifdef HAVE_LUASCRIPT
320 if (daemon->luascript)
321 {
Simon Kelleya9530962012-03-20 22:07:35 +0000322 if (data.action == ACTION_TFTP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000323 {
Simon Kelleya9530962012-03-20 22:07:35 +0000324 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");
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100335 lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
Simon Kelleya9530962012-03-20 22:07:35 +0000336 lua_setfield(lua, -2, "file_size");
337 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
338 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000339 }
Simon Kelley33702ab2015-12-28 23:17:15 +0000340 else if (data.action == ACTION_ARP)
341 {
342 lua_getglobal(lua, "arp");
343 if (lua_type(lua, -1) != LUA_TFUNCTION)
344 lua_pop(lua, 1); /* arp 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, daemon->dhcp_buff);
352 lua_setfield(lua, -2, "mac_address");
353 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
354 }
355 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000356 else
Simon Kelleya9530962012-03-20 22:07:35 +0000357 {
358 lua_getglobal(lua, "lease"); /* function to call */
359 lua_pushstring(lua, action_str); /* arg1 - action */
360 lua_newtable(lua); /* arg2 - data table */
361
362 if (is6)
363 {
Simon Kelleya9530962012-03-20 22:07:35 +0000364 lua_pushstring(lua, daemon->packet);
Simon Kelley89500e32013-09-20 16:29:20 +0100365 lua_setfield(lua, -2, "client_duid");
366 lua_pushstring(lua, daemon->dhcp_packet.iov_base);
Simon Kelleya9530962012-03-20 22:07:35 +0000367 lua_setfield(lua, -2, "server_duid");
368 lua_pushstring(lua, daemon->dhcp_buff3);
369 lua_setfield(lua, -2, "iaid");
370 }
371
372 if (!is6 && data.clid_len != 0)
373 {
374 lua_pushstring(lua, daemon->packet);
375 lua_setfield(lua, -2, "client_id");
376 }
377
378 if (strlen(data.interface) != 0)
379 {
380 lua_pushstring(lua, data.interface);
381 lua_setfield(lua, -2, "interface");
382 }
383
384#ifdef HAVE_BROKEN_RTC
385 lua_pushnumber(lua, data.length);
386 lua_setfield(lua, -2, "lease_length");
387#else
388 lua_pushnumber(lua, data.expires);
389 lua_setfield(lua, -2, "lease_expires");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000390#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000391
392 if (hostname)
393 {
394 lua_pushstring(lua, hostname);
395 lua_setfield(lua, -2, "hostname");
396 }
397
398 if (domain)
399 {
400 lua_pushstring(lua, domain);
401 lua_setfield(lua, -2, "domain");
402 }
403
404 end = extradata + data.ed_len;
405 buf = extradata;
406
407 if (!is6)
408 buf = grab_extradata_lua(buf, end, "vendor_class");
409#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100410 else if (data.vendorclass_count != 0)
411 {
412 sprintf(daemon->dhcp_buff2, "vendor_class_id");
413 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
414 for (i = 0; i < data.vendorclass_count - 1; i++)
415 {
416 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
417 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
418 }
419 }
Simon Kelleya9530962012-03-20 22:07:35 +0000420#endif
421
422 buf = grab_extradata_lua(buf, end, "supplied_hostname");
423
424 if (!is6)
425 {
426 buf = grab_extradata_lua(buf, end, "cpewan_oui");
427 buf = grab_extradata_lua(buf, end, "cpewan_serial");
428 buf = grab_extradata_lua(buf, end, "cpewan_class");
Simon Kelleydd1721c2013-02-18 21:04:04 +0000429 buf = grab_extradata_lua(buf, end, "circuit_id");
430 buf = grab_extradata_lua(buf, end, "subscriber_id");
431 buf = grab_extradata_lua(buf, end, "remote_id");
Simon Kelleya9530962012-03-20 22:07:35 +0000432 }
433
434 buf = grab_extradata_lua(buf, end, "tags");
435
436 if (is6)
437 buf = grab_extradata_lua(buf, end, "relay_address");
438 else if (data.giaddr.s_addr != 0)
439 {
440 lua_pushstring(lua, inet_ntoa(data.giaddr));
441 lua_setfield(lua, -2, "relay_address");
442 }
443
444 for (i = 0; buf; i++)
445 {
446 sprintf(daemon->dhcp_buff2, "user_class%i", i);
447 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
448 }
449
450 if (data.action != ACTION_DEL && data.remaining_time != 0)
451 {
452 lua_pushnumber(lua, data.remaining_time);
453 lua_setfield(lua, -2, "time_remaining");
454 }
455
456 if (data.action == ACTION_OLD_HOSTNAME && hostname)
457 {
458 lua_pushstring(lua, hostname);
459 lua_setfield(lua, -2, "old_hostname");
460 }
461
Simon Kelley89500e32013-09-20 16:29:20 +0100462 if (!is6 || data.hwaddr_len != 0)
Simon Kelleya9530962012-03-20 22:07:35 +0000463 {
464 lua_pushstring(lua, daemon->dhcp_buff);
465 lua_setfield(lua, -2, "mac_address");
466 }
467
468 lua_pushstring(lua, daemon->addrbuff);
469 lua_setfield(lua, -2, "ip_address");
470
471 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000472 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000473 }
474#endif
475
476 /* no script, just lua */
477 if (!daemon->lease_change_command)
Simon Kelley316e2732010-01-22 20:16:09 +0000478 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000479
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100480 /* Pipe to capture stdout and stderr from script */
481 if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1)
482 continue;
483
Simon Kelley5aabfc72007-08-29 11:24:47 +0100484 /* possible fork errors are all temporary resource problems */
485 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
486 sleep(2);
Simon Kelley316e2732010-01-22 20:16:09 +0000487
Simon Kelley5aabfc72007-08-29 11:24:47 +0100488 if (pid == -1)
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100489 {
490 if (!option_bool(OPT_DEBUG))
491 {
492 close(pipeout[0]);
493 close(pipeout[1]);
494 }
495 continue;
496 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000497
Simon Kelley16972692006-10-16 20:04:18 +0100498 /* wait for child to complete */
499 if (pid != 0)
500 {
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100501 if (!option_bool(OPT_DEBUG))
502 {
503 FILE *fp;
504
505 close(pipeout[1]);
506
507 /* Read lines sent to stdout/err by the script and pass them back to be logged */
508 if (!(fp = fdopen(pipeout[0], "r")))
509 close(pipeout[0]);
510 else
511 {
512 while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
513 {
514 /* do not include new lines, log will append them */
515 size_t len = strlen(daemon->packet);
516 if (len > 0)
517 {
518 --len;
519 if (daemon->packet[len] == '\n')
520 daemon->packet[len] = 0;
521 }
522 send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
523 }
524 fclose(fp);
525 }
526 }
527
Simon Kelley5aabfc72007-08-29 11:24:47 +0100528 /* reap our children's children, if necessary */
529 while (1)
530 {
531 int status;
532 pid_t rc = wait(&status);
533
534 if (rc == pid)
535 {
536 /* On error send event back to main process for logging */
537 if (WIFSIGNALED(status))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000538 send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100539 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000540 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100541 break;
542 }
543
544 if (rc == -1 && errno != EINTR)
545 break;
546 }
547
Simon Kelley16972692006-10-16 20:04:18 +0100548 continue;
549 }
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100550
551 if (!option_bool(OPT_DEBUG))
552 {
553 /* map stdout/stderr of script to pipeout */
554 close(pipeout[0]);
555 dup2(pipeout[1], STDOUT_FILENO);
556 dup2(pipeout[1], STDERR_FILENO);
557 close(pipeout[1]);
558 }
Simon Kelley16972692006-10-16 20:04:18 +0100559
Simon Kelley33702ab2015-12-28 23:17:15 +0000560 if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000561 {
Simon Kelley89500e32013-09-20 16:29:20 +0100562#ifdef HAVE_DHCP6
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100563 my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
564 my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err);
565 my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100566#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000567
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100568 my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
569 my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000570
571#ifdef HAVE_BROKEN_RTC
Simon Kelley208fb612013-02-21 22:26:18 +0000572 sprintf(daemon->dhcp_buff2, "%u", data.length);
Simon Kelleya9530962012-03-20 22:07:35 +0000573 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
574#else
Simon Kelley208fb612013-02-21 22:26:18 +0000575 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
Simon Kelleya9530962012-03-20 22:07:35 +0000576 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
577#endif
578
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100579 my_setenv("DNSMASQ_DOMAIN", domain, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000580
581 end = extradata + data.ed_len;
582 buf = extradata;
583
584 if (!is6)
585 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
586#ifdef HAVE_DHCP6
587 else
588 {
Simon Kelley89500e32013-09-20 16:29:20 +0100589 if (data.vendorclass_count != 0)
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000590 {
Simon Kelleya9530962012-03-20 22:07:35 +0000591 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100592 for (i = 0; i < data.vendorclass_count - 1; i++)
Simon Kelleya9530962012-03-20 22:07:35 +0000593 {
594 sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
595 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
596 }
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000597 }
598 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000599#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000600
601 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
602
603 if (!is6)
604 {
605 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
606 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
607 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000608 buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
609 buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
610 buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
ZHAO Yuf89cae32016-12-22 22:32:31 +0000611 buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000612 }
613
614 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000615
Simon Kelleya9530962012-03-20 22:07:35 +0000616 if (is6)
617 buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100618 else
619 my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000620
621 for (i = 0; buf; i++)
622 {
623 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
624 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
625 }
626
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100627 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
628 my_setenv("DNSMASQ_TIME_REMAINING", data.action != ACTION_DEL && data.remaining_time != 0 ? daemon->dhcp_buff2 : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000629
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100630 my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
631 if (data.action == ACTION_OLD_HOSTNAME)
632 hostname = NULL;
Simon Kelley33702ab2015-12-28 23:17:15 +0000633
634 my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100635 }
636
Simon Kelley5aabfc72007-08-29 11:24:47 +0100637 /* we need to have the event_fd around if exec fails */
638 if ((i = fcntl(event_fd, F_GETFD)) != -1)
639 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
640 close(pipefd[0]);
641
Simon Kelley16972692006-10-16 20:04:18 +0100642 p = strrchr(daemon->lease_change_command, '/');
Simon Kelley824af852008-02-12 20:43:05 +0000643 if (err == 0)
644 {
645 execl(daemon->lease_change_command,
Simon Kelley33702ab2015-12-28 23:17:15 +0000646 p ? p+1 : daemon->lease_change_command, action_str,
647 (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
Simon Kelley89500e32013-09-20 16:29:20 +0100648 daemon->addrbuff, hostname, (char*)NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000649 err = errno;
650 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100651 /* failed, send event so the main process logs the problem */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000652 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100653 _exit(0);
654 }
655}
656
Simon Kelley824af852008-02-12 20:43:05 +0000657static void my_setenv(const char *name, const char *value, int *error)
658{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100659 if (*error == 0)
660 {
661 if (!value)
662 unsetenv(name);
663 else if (setenv(name, value, 1) != 0)
664 *error = errno;
665 }
Simon Kelley824af852008-02-12 20:43:05 +0000666}
667
Simon Kelley316e2732010-01-22 20:16:09 +0000668static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
669{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100670 unsigned char *next = NULL;
671 char *val = NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000672
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100673 if (buf && (buf != end))
Simon Kelley316e2732010-01-22 20:16:09 +0000674 {
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100675 for (next = buf; ; next++)
676 if (next == end)
677 {
678 next = NULL;
679 break;
680 }
681 else if (*next == 0)
682 break;
Simon Kelley316e2732010-01-22 20:16:09 +0000683
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100684 if (next && (next != buf))
685 {
686 char *p;
687 /* No "=" in value */
688 if ((p = strchr((char *)buf, '=')))
689 *p = 0;
690 val = (char *)buf;
691 }
692 }
693
694 my_setenv(env, val, err);
695
696 return next ? next + 1 : NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000697}
698
Simon Kelleyc72daea2012-01-05 21:33:27 +0000699#ifdef HAVE_LUASCRIPT
700static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
701{
702 unsigned char *next;
703
704 if (!buf || (buf == end))
705 return NULL;
706
707 for (next = buf; *next != 0; next++)
708 if (next == end)
709 return NULL;
710
711 if (next != buf)
712 {
713 lua_pushstring(lua, (char *)buf);
714 lua_setfield(lua, -2, field);
715 }
716
717 return next + 1;
718}
719#endif
720
Simon Kelleya9530962012-03-20 22:07:35 +0000721static void buff_alloc(size_t size)
722{
723 if (size > buf_size)
724 {
725 struct script_data *new;
726
727 /* start with reasonable size, will almost never need extending. */
728 if (size < sizeof(struct script_data) + 200)
729 size = sizeof(struct script_data) + 200;
730
731 if (!(new = whine_malloc(size)))
732 return;
733 if (buf)
734 free(buf);
735 buf = new;
736 buf_size = size;
737 }
738}
739
Simon Kelley16972692006-10-16 20:04:18 +0100740/* pack up lease data into a buffer */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100741void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
Simon Kelley16972692006-10-16 20:04:18 +0100742{
743 unsigned char *p;
Simon Kelley316e2732010-01-22 20:16:09 +0000744 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000745 int fd = daemon->dhcpfd;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100746#ifdef HAVE_DHCP6
Simon Kelleyceae00d2012-02-09 21:28:14 +0000747 if (!daemon->dhcp)
748 fd = daemon->dhcp6fd;
749#endif
750
Simon Kelley16972692006-10-16 20:04:18 +0100751 /* no script */
752 if (daemon->helperfd == -1)
753 return;
754
Simon Kelley316e2732010-01-22 20:16:09 +0000755 if (lease->extradata)
756 ed_len = lease->extradata_len;
Simon Kelley16972692006-10-16 20:04:18 +0100757 if (lease->clid)
758 clid_len = lease->clid_len;
759 if (hostname)
760 hostname_len = strlen(hostname) + 1;
761
Simon Kelleya9530962012-03-20 22:07:35 +0000762 buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
Simon Kelley16972692006-10-16 20:04:18 +0100763
764 buf->action = action;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000765 buf->flags = lease->flags;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100766#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100767 buf->vendorclass_count = lease->vendorclass_count;
768 buf->addr6 = lease->addr6;
769 buf->iaid = lease->iaid;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100770#endif
Simon Kelley89500e32013-09-20 16:29:20 +0100771 buf->hwaddr_len = lease->hwaddr_len;
Simon Kelley16972692006-10-16 20:04:18 +0100772 buf->hwaddr_type = lease->hwaddr_type;
773 buf->clid_len = clid_len;
Simon Kelley316e2732010-01-22 20:16:09 +0000774 buf->ed_len = ed_len;
Simon Kelley16972692006-10-16 20:04:18 +0100775 buf->hostname_len = hostname_len;
776 buf->addr = lease->addr;
Simon Kelley1f15b812009-10-13 17:49:32 +0100777 buf->giaddr = lease->giaddr;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000778 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
779 if (!indextoname(fd, lease->last_interface, buf->interface))
Simon Kelley316e2732010-01-22 20:16:09 +0000780 buf->interface[0] = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000781
Simon Kelley16972692006-10-16 20:04:18 +0100782#ifdef HAVE_BROKEN_RTC
783 buf->length = lease->length;
784#else
785 buf->expires = lease->expires;
786#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000787
788 if (lease->expires != 0)
789 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
790 else
791 buf->remaining_time = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100792
Simon Kelley16972692006-10-16 20:04:18 +0100793 p = (unsigned char *)(buf+1);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100794 if (clid_len != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100795 {
796 memcpy(p, lease->clid, clid_len);
797 p += clid_len;
798 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100799 if (hostname_len != 0)
800 {
801 memcpy(p, hostname, hostname_len);
802 p += hostname_len;
803 }
Simon Kelley316e2732010-01-22 20:16:09 +0000804 if (ed_len != 0)
805 {
806 memcpy(p, lease->extradata, ed_len);
807 p += ed_len;
808 }
Simon Kelley16972692006-10-16 20:04:18 +0100809 bytes_in_buf = p - (unsigned char *)buf;
810}
811
Simon Kelleya9530962012-03-20 22:07:35 +0000812#ifdef HAVE_TFTP
813/* This nastily re-uses DHCP-fields for TFTP stuff */
814void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
815{
816 unsigned int filename_len;
817
818 /* no script */
819 if (daemon->helperfd == -1)
820 return;
821
822 filename_len = strlen(filename) + 1;
823 buff_alloc(sizeof(struct script_data) + filename_len);
824 memset(buf, 0, sizeof(struct script_data));
825
826 buf->action = ACTION_TFTP;
827 buf->hostname_len = filename_len;
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100828 buf->file_len = file_len;
Simon Kelleya9530962012-03-20 22:07:35 +0000829
830 if ((buf->flags = peer->sa.sa_family) == AF_INET)
831 buf->addr = peer->in.sin_addr;
Simon Kelleya9530962012-03-20 22:07:35 +0000832 else
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100833 buf->addr6 = peer->in6.sin6_addr;
Simon Kelleya9530962012-03-20 22:07:35 +0000834
835 memcpy((unsigned char *)(buf+1), filename, filename_len);
836
837 bytes_in_buf = sizeof(struct script_data) + filename_len;
838}
839#endif
840
Simon Kelleycc921df2019-01-02 22:48:59 +0000841void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr)
Simon Kelley33702ab2015-12-28 23:17:15 +0000842{
843 /* no script */
844 if (daemon->helperfd == -1)
845 return;
846
847 buff_alloc(sizeof(struct script_data));
848 memset(buf, 0, sizeof(struct script_data));
849
850 buf->action = action;
851 buf->hwaddr_len = maclen;
852 buf->hwaddr_type = ARPHRD_ETHER;
853 if ((buf->flags = family) == AF_INET)
Simon Kelleycc921df2019-01-02 22:48:59 +0000854 buf->addr = addr->addr4;
Simon Kelley33702ab2015-12-28 23:17:15 +0000855 else
Simon Kelleycc921df2019-01-02 22:48:59 +0000856 buf->addr6 = addr->addr6;
Simon Kelley33702ab2015-12-28 23:17:15 +0000857
858 memcpy(buf->hwaddr, mac, maclen);
859
860 bytes_in_buf = sizeof(struct script_data);
861}
862
Simon Kelley16972692006-10-16 20:04:18 +0100863int helper_buf_empty(void)
864{
865 return bytes_in_buf == 0;
866}
867
Simon Kelley5aabfc72007-08-29 11:24:47 +0100868void helper_write(void)
Simon Kelley16972692006-10-16 20:04:18 +0100869{
870 ssize_t rc;
871
872 if (bytes_in_buf == 0)
873 return;
874
875 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
876 {
877 if (bytes_in_buf != (size_t)rc)
878 memmove(buf, buf + rc, bytes_in_buf - rc);
879 bytes_in_buf -= rc;
880 }
881 else
882 {
883 if (errno == EAGAIN || errno == EINTR)
884 return;
885 bytes_in_buf = 0;
886 }
887}
888
Simon Kelley5aabfc72007-08-29 11:24:47 +0100889#endif
Simon Kelley16972692006-10-16 20:04:18 +0100890
891
Simon Kelleyc72daea2012-01-05 21:33:27 +0000892