blob: c13407150cd27d746a2eb2b7d091442286303fb8 [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 Kelley903650a2013-10-03 11:43:09 +010067#ifdef HAVE_IPV6
Simon Kelley89500e32013-09-20 16:29:20 +010068 struct in6_addr addr6;
Simon Kelley903650a2013-10-03 11:43:09 +010069#endif
70#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +010071 int iaid, vendorclass_count;
72#endif
Simon Kelley16972692006-10-16 20:04:18 +010073 unsigned char hwaddr[DHCP_CHADDR_MAX];
Simon Kelley824af852008-02-12 20:43:05 +000074 char interface[IF_NAMESIZE];
Simon Kelley16972692006-10-16 20:04:18 +010075};
76
Simon Kelley5aabfc72007-08-29 11:24:47 +010077static struct script_data *buf = NULL;
78static size_t bytes_in_buf = 0, buf_size = 0;
Simon Kelley16972692006-10-16 20:04:18 +010079
Simon Kelley1a6bca82008-07-11 11:11:42 +010080int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
Simon Kelley16972692006-10-16 20:04:18 +010081{
82 pid_t pid;
83 int i, pipefd[2];
84 struct sigaction sigact;
Simon Kelley9e038942008-05-30 20:06:34 +010085
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 Kelleyc72daea2012-01-05 21:33:27 +0000191 unsigned char *end, *extradata, *alloc_buff = NULL;
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 Kelleyc72daea2012-01-05 21:33:27 +0000195 free(alloc_buff);
196
Simon Kelley16972692006-10-16 20:04:18 +0100197 /* we read zero bytes when pipe closed: this is our signal to exit */
198 if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000199 {
200#ifdef HAVE_LUASCRIPT
201 if (daemon->luascript)
202 {
203 lua_getglobal(lua, "shutdown");
204 if (lua_type(lua, -1) == LUA_TFUNCTION)
205 lua_call(lua, 0, 0);
206 }
207#endif
208 _exit(0);
209 }
Simon Kelleya9530962012-03-20 22:07:35 +0000210
211 is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
212
Simon Kelley16972692006-10-16 20:04:18 +0100213 if (data.action == ACTION_DEL)
214 action_str = "del";
215 else if (data.action == ACTION_ADD)
216 action_str = "add";
217 else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
218 action_str = "old";
Simon Kelleya9530962012-03-20 22:07:35 +0000219 else if (data.action == ACTION_TFTP)
220 {
221 action_str = "tftp";
222 is6 = (data.flags != AF_INET);
223 }
Simon Kelley33702ab2015-12-28 23:17:15 +0000224 else if (data.action == ACTION_ARP)
225 {
Simon Kelleye6e751b2016-02-01 17:59:07 +0000226 action_str = "arp-add";
Simon Kelley33702ab2015-12-28 23:17:15 +0000227 is6 = (data.flags != AF_INET);
228 }
Simon Kelleye6e751b2016-02-01 17:59:07 +0000229 else if (data.action == ACTION_ARP_DEL)
Simon Kelley33702ab2015-12-28 23:17:15 +0000230 {
Simon Kelleye6e751b2016-02-01 17:59:07 +0000231 action_str = "arp-del";
Simon Kelley33702ab2015-12-28 23:17:15 +0000232 is6 = (data.flags != AF_INET);
233 data.action = ACTION_ARP;
234 }
235 else
Simon Kelley16972692006-10-16 20:04:18 +0100236 continue;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000237
Simon Kelleya9530962012-03-20 22:07:35 +0000238
Simon Kelley89500e32013-09-20 16:29:20 +0100239 /* 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++)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000244 {
Simon Kelley89500e32013-09-20 16:29:20 +0100245 p += sprintf(p, "%.2x", data.hwaddr[i]);
246 if (i != data.hwaddr_len - 1)
247 p += sprintf(p, ":");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000248 }
Simon Kelley89500e32013-09-20 16:29:20 +0100249
Simon Kelleyc72daea2012-01-05 21:33:27 +0000250 /* 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)))
Simon Kelley16972692006-10-16 20:04:18 +0100253 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000254
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 */
Simon Kelley89500e32013-09-20 16:29:20 +0100260 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)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000264 p += sprintf(p, ":");
Simon Kelley89500e32013-09-20 16:29:20 +0100265 }
266
Simon Kelleyceae00d2012-02-09 21:28:14 +0000267#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100268 if (is6)
Simon Kelley16972692006-10-16 20:04:18 +0100269 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000270 /* or IAID and server DUID for IPv6 */
Simon Kelley89500e32013-09-20 16:29:20 +0100271 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++)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000273 {
274 p += sprintf(p, "%.2x", daemon->duid[i]);
275 if (i != daemon->duid_len - 1)
276 p += sprintf(p, ":");
277 }
Simon Kelleycaa94382012-02-15 10:29:50 +0000278
Simon Kelley16972692006-10-16 20:04:18 +0100279 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000280#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000281
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;
Simon Kelleya9530962012-03-20 22:07:35 +0000289 if (data.action != ACTION_TFTP)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000290 {
Simon Kelleya9530962012-03-20 22:07:35 +0000291 if (!legal_hostname(hostname))
292 hostname = NULL;
293 else if ((dot = strchr(hostname, '.')))
294 {
295 domain = dot+1;
296 *dot = 0;
297 }
298 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000299 }
300
301 extradata = buf + data.hostname_len;
302
Simon Kelleyceae00d2012-02-09 21:28:14 +0000303 if (!is6)
304 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley1566bac2016-02-05 14:38:06 +0000305#ifdef HAVE_IPV6
Simon Kelleyceae00d2012-02-09 21:28:14 +0000306 else
Simon Kelley89500e32013-09-20 16:29:20 +0100307 inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000308#endif
309
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100310#ifdef HAVE_TFTP
Simon Kelleya9530962012-03-20 22:07:35 +0000311 /* file length */
312 if (data.action == ACTION_TFTP)
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100313 sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
314#endif
315
Simon Kelleyc72daea2012-01-05 21:33:27 +0000316#ifdef HAVE_LUASCRIPT
317 if (daemon->luascript)
318 {
Simon Kelleya9530962012-03-20 22:07:35 +0000319 if (data.action == ACTION_TFTP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000320 {
Simon Kelleya9530962012-03-20 22:07:35 +0000321 lua_getglobal(lua, "tftp");
322 if (lua_type(lua, -1) != LUA_TFUNCTION)
323 lua_pop(lua, 1); /* tftp function optional */
324 else
325 {
326 lua_pushstring(lua, action_str); /* arg1 - action */
327 lua_newtable(lua); /* arg2 - data table */
328 lua_pushstring(lua, daemon->addrbuff);
329 lua_setfield(lua, -2, "destination_address");
330 lua_pushstring(lua, hostname);
331 lua_setfield(lua, -2, "file_name");
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100332 lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
Simon Kelleya9530962012-03-20 22:07:35 +0000333 lua_setfield(lua, -2, "file_size");
334 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
335 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000336 }
Simon Kelley33702ab2015-12-28 23:17:15 +0000337 else if (data.action == ACTION_ARP)
338 {
339 lua_getglobal(lua, "arp");
340 if (lua_type(lua, -1) != LUA_TFUNCTION)
341 lua_pop(lua, 1); /* arp function optional */
342 else
343 {
344 lua_pushstring(lua, action_str); /* arg1 - action */
345 lua_newtable(lua); /* arg2 - data table */
346 lua_pushstring(lua, daemon->addrbuff);
347 lua_setfield(lua, -2, "client_address");
348 lua_pushstring(lua, daemon->dhcp_buff);
349 lua_setfield(lua, -2, "mac_address");
350 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
351 }
352 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000353 else
Simon Kelleya9530962012-03-20 22:07:35 +0000354 {
355 lua_getglobal(lua, "lease"); /* function to call */
356 lua_pushstring(lua, action_str); /* arg1 - action */
357 lua_newtable(lua); /* arg2 - data table */
358
359 if (is6)
360 {
Simon Kelleya9530962012-03-20 22:07:35 +0000361 lua_pushstring(lua, daemon->packet);
Simon Kelley89500e32013-09-20 16:29:20 +0100362 lua_setfield(lua, -2, "client_duid");
363 lua_pushstring(lua, daemon->dhcp_packet.iov_base);
Simon Kelleya9530962012-03-20 22:07:35 +0000364 lua_setfield(lua, -2, "server_duid");
365 lua_pushstring(lua, daemon->dhcp_buff3);
366 lua_setfield(lua, -2, "iaid");
367 }
368
369 if (!is6 && data.clid_len != 0)
370 {
371 lua_pushstring(lua, daemon->packet);
372 lua_setfield(lua, -2, "client_id");
373 }
374
375 if (strlen(data.interface) != 0)
376 {
377 lua_pushstring(lua, data.interface);
378 lua_setfield(lua, -2, "interface");
379 }
380
381#ifdef HAVE_BROKEN_RTC
382 lua_pushnumber(lua, data.length);
383 lua_setfield(lua, -2, "lease_length");
384#else
385 lua_pushnumber(lua, data.expires);
386 lua_setfield(lua, -2, "lease_expires");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000387#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000388
389 if (hostname)
390 {
391 lua_pushstring(lua, hostname);
392 lua_setfield(lua, -2, "hostname");
393 }
394
395 if (domain)
396 {
397 lua_pushstring(lua, domain);
398 lua_setfield(lua, -2, "domain");
399 }
400
401 end = extradata + data.ed_len;
402 buf = extradata;
403
404 if (!is6)
405 buf = grab_extradata_lua(buf, end, "vendor_class");
406#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100407 else if (data.vendorclass_count != 0)
408 {
409 sprintf(daemon->dhcp_buff2, "vendor_class_id");
410 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
411 for (i = 0; i < data.vendorclass_count - 1; i++)
412 {
413 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
414 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
415 }
416 }
Simon Kelleya9530962012-03-20 22:07:35 +0000417#endif
418
419 buf = grab_extradata_lua(buf, end, "supplied_hostname");
420
421 if (!is6)
422 {
423 buf = grab_extradata_lua(buf, end, "cpewan_oui");
424 buf = grab_extradata_lua(buf, end, "cpewan_serial");
425 buf = grab_extradata_lua(buf, end, "cpewan_class");
Simon Kelleydd1721c2013-02-18 21:04:04 +0000426 buf = grab_extradata_lua(buf, end, "circuit_id");
427 buf = grab_extradata_lua(buf, end, "subscriber_id");
428 buf = grab_extradata_lua(buf, end, "remote_id");
Simon Kelleya9530962012-03-20 22:07:35 +0000429 }
430
431 buf = grab_extradata_lua(buf, end, "tags");
432
433 if (is6)
434 buf = grab_extradata_lua(buf, end, "relay_address");
435 else if (data.giaddr.s_addr != 0)
436 {
437 lua_pushstring(lua, inet_ntoa(data.giaddr));
438 lua_setfield(lua, -2, "relay_address");
439 }
440
441 for (i = 0; buf; i++)
442 {
443 sprintf(daemon->dhcp_buff2, "user_class%i", i);
444 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
445 }
446
447 if (data.action != ACTION_DEL && data.remaining_time != 0)
448 {
449 lua_pushnumber(lua, data.remaining_time);
450 lua_setfield(lua, -2, "time_remaining");
451 }
452
453 if (data.action == ACTION_OLD_HOSTNAME && hostname)
454 {
455 lua_pushstring(lua, hostname);
456 lua_setfield(lua, -2, "old_hostname");
457 }
458
Simon Kelley89500e32013-09-20 16:29:20 +0100459 if (!is6 || data.hwaddr_len != 0)
Simon Kelleya9530962012-03-20 22:07:35 +0000460 {
461 lua_pushstring(lua, daemon->dhcp_buff);
462 lua_setfield(lua, -2, "mac_address");
463 }
464
465 lua_pushstring(lua, daemon->addrbuff);
466 lua_setfield(lua, -2, "ip_address");
467
468 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000469 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000470 }
471#endif
472
473 /* no script, just lua */
474 if (!daemon->lease_change_command)
Simon Kelley316e2732010-01-22 20:16:09 +0000475 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000476
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100477 /* Pipe to capture stdout and stderr from script */
478 if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1)
479 continue;
480
Simon Kelley5aabfc72007-08-29 11:24:47 +0100481 /* possible fork errors are all temporary resource problems */
482 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
483 sleep(2);
Simon Kelley316e2732010-01-22 20:16:09 +0000484
Simon Kelley5aabfc72007-08-29 11:24:47 +0100485 if (pid == -1)
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100486 {
487 if (!option_bool(OPT_DEBUG))
488 {
489 close(pipeout[0]);
490 close(pipeout[1]);
491 }
492 continue;
493 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000494
Simon Kelley16972692006-10-16 20:04:18 +0100495 /* wait for child to complete */
496 if (pid != 0)
497 {
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100498 if (!option_bool(OPT_DEBUG))
499 {
500 FILE *fp;
501
502 close(pipeout[1]);
503
504 /* Read lines sent to stdout/err by the script and pass them back to be logged */
505 if (!(fp = fdopen(pipeout[0], "r")))
506 close(pipeout[0]);
507 else
508 {
509 while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
510 {
511 /* do not include new lines, log will append them */
512 size_t len = strlen(daemon->packet);
513 if (len > 0)
514 {
515 --len;
516 if (daemon->packet[len] == '\n')
517 daemon->packet[len] = 0;
518 }
519 send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
520 }
521 fclose(fp);
522 }
523 }
524
Simon Kelley5aabfc72007-08-29 11:24:47 +0100525 /* reap our children's children, if necessary */
526 while (1)
527 {
528 int status;
529 pid_t rc = wait(&status);
530
531 if (rc == pid)
532 {
533 /* On error send event back to main process for logging */
534 if (WIFSIGNALED(status))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000535 send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100536 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000537 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100538 break;
539 }
540
541 if (rc == -1 && errno != EINTR)
542 break;
543 }
544
Simon Kelley16972692006-10-16 20:04:18 +0100545 continue;
546 }
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100547
548 if (!option_bool(OPT_DEBUG))
549 {
550 /* map stdout/stderr of script to pipeout */
551 close(pipeout[0]);
552 dup2(pipeout[1], STDOUT_FILENO);
553 dup2(pipeout[1], STDERR_FILENO);
554 close(pipeout[1]);
555 }
Simon Kelley16972692006-10-16 20:04:18 +0100556
Simon Kelley33702ab2015-12-28 23:17:15 +0000557 if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000558 {
Simon Kelley89500e32013-09-20 16:29:20 +0100559#ifdef HAVE_DHCP6
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100560 my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
561 my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err);
562 my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100563#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000564
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100565 my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
566 my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000567
568#ifdef HAVE_BROKEN_RTC
Simon Kelley208fb612013-02-21 22:26:18 +0000569 sprintf(daemon->dhcp_buff2, "%u", data.length);
Simon Kelleya9530962012-03-20 22:07:35 +0000570 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
571#else
Simon Kelley208fb612013-02-21 22:26:18 +0000572 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
Simon Kelleya9530962012-03-20 22:07:35 +0000573 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
574#endif
575
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100576 my_setenv("DNSMASQ_DOMAIN", domain, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000577
578 end = extradata + data.ed_len;
579 buf = extradata;
580
581 if (!is6)
582 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
583#ifdef HAVE_DHCP6
584 else
585 {
Simon Kelley89500e32013-09-20 16:29:20 +0100586 if (data.vendorclass_count != 0)
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000587 {
Simon Kelleya9530962012-03-20 22:07:35 +0000588 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100589 for (i = 0; i < data.vendorclass_count - 1; i++)
Simon Kelleya9530962012-03-20 22:07:35 +0000590 {
591 sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
592 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
593 }
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000594 }
595 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000596#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000597
598 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
599
600 if (!is6)
601 {
602 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
603 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
604 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000605 buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
606 buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
607 buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
ZHAO Yuf89cae32016-12-22 22:32:31 +0000608 buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000609 }
610
611 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000612
Simon Kelleya9530962012-03-20 22:07:35 +0000613 if (is6)
614 buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100615 else
616 my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000617
618 for (i = 0; buf; i++)
619 {
620 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
621 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
622 }
623
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100624 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
625 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 +0000626
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100627 my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
628 if (data.action == ACTION_OLD_HOSTNAME)
629 hostname = NULL;
Simon Kelley33702ab2015-12-28 23:17:15 +0000630
631 my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100632 }
633
Simon Kelley5aabfc72007-08-29 11:24:47 +0100634 /* we need to have the event_fd around if exec fails */
635 if ((i = fcntl(event_fd, F_GETFD)) != -1)
636 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
637 close(pipefd[0]);
638
Simon Kelley16972692006-10-16 20:04:18 +0100639 p = strrchr(daemon->lease_change_command, '/');
Simon Kelley824af852008-02-12 20:43:05 +0000640 if (err == 0)
641 {
642 execl(daemon->lease_change_command,
Simon Kelley33702ab2015-12-28 23:17:15 +0000643 p ? p+1 : daemon->lease_change_command, action_str,
644 (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
Simon Kelley89500e32013-09-20 16:29:20 +0100645 daemon->addrbuff, hostname, (char*)NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000646 err = errno;
647 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100648 /* failed, send event so the main process logs the problem */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000649 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100650 _exit(0);
651 }
652}
653
Simon Kelley824af852008-02-12 20:43:05 +0000654static void my_setenv(const char *name, const char *value, int *error)
655{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100656 if (*error == 0)
657 {
658 if (!value)
659 unsetenv(name);
660 else if (setenv(name, value, 1) != 0)
661 *error = errno;
662 }
Simon Kelley824af852008-02-12 20:43:05 +0000663}
664
Simon Kelley316e2732010-01-22 20:16:09 +0000665static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
666{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100667 unsigned char *next = NULL;
668 char *val = NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000669
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100670 if (buf && (buf != end))
Simon Kelley316e2732010-01-22 20:16:09 +0000671 {
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100672 for (next = buf; ; next++)
673 if (next == end)
674 {
675 next = NULL;
676 break;
677 }
678 else if (*next == 0)
679 break;
Simon Kelley316e2732010-01-22 20:16:09 +0000680
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100681 if (next && (next != buf))
682 {
683 char *p;
684 /* No "=" in value */
685 if ((p = strchr((char *)buf, '=')))
686 *p = 0;
687 val = (char *)buf;
688 }
689 }
690
691 my_setenv(env, val, err);
692
693 return next ? next + 1 : NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000694}
695
Simon Kelleyc72daea2012-01-05 21:33:27 +0000696#ifdef HAVE_LUASCRIPT
697static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
698{
699 unsigned char *next;
700
701 if (!buf || (buf == end))
702 return NULL;
703
704 for (next = buf; *next != 0; next++)
705 if (next == end)
706 return NULL;
707
708 if (next != buf)
709 {
710 lua_pushstring(lua, (char *)buf);
711 lua_setfield(lua, -2, field);
712 }
713
714 return next + 1;
715}
716#endif
717
Simon Kelleya9530962012-03-20 22:07:35 +0000718static void buff_alloc(size_t size)
719{
720 if (size > buf_size)
721 {
722 struct script_data *new;
723
724 /* start with reasonable size, will almost never need extending. */
725 if (size < sizeof(struct script_data) + 200)
726 size = sizeof(struct script_data) + 200;
727
728 if (!(new = whine_malloc(size)))
729 return;
730 if (buf)
731 free(buf);
732 buf = new;
733 buf_size = size;
734 }
735}
736
Simon Kelley16972692006-10-16 20:04:18 +0100737/* pack up lease data into a buffer */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100738void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
Simon Kelley16972692006-10-16 20:04:18 +0100739{
740 unsigned char *p;
Simon Kelley316e2732010-01-22 20:16:09 +0000741 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000742 int fd = daemon->dhcpfd;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100743#ifdef HAVE_DHCP6
Simon Kelleyceae00d2012-02-09 21:28:14 +0000744 if (!daemon->dhcp)
745 fd = daemon->dhcp6fd;
746#endif
747
Simon Kelley16972692006-10-16 20:04:18 +0100748 /* no script */
749 if (daemon->helperfd == -1)
750 return;
751
Simon Kelley316e2732010-01-22 20:16:09 +0000752 if (lease->extradata)
753 ed_len = lease->extradata_len;
Simon Kelley16972692006-10-16 20:04:18 +0100754 if (lease->clid)
755 clid_len = lease->clid_len;
756 if (hostname)
757 hostname_len = strlen(hostname) + 1;
758
Simon Kelleya9530962012-03-20 22:07:35 +0000759 buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
Simon Kelley16972692006-10-16 20:04:18 +0100760
761 buf->action = action;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000762 buf->flags = lease->flags;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100763#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100764 buf->vendorclass_count = lease->vendorclass_count;
765 buf->addr6 = lease->addr6;
766 buf->iaid = lease->iaid;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100767#endif
Simon Kelley89500e32013-09-20 16:29:20 +0100768 buf->hwaddr_len = lease->hwaddr_len;
Simon Kelley16972692006-10-16 20:04:18 +0100769 buf->hwaddr_type = lease->hwaddr_type;
770 buf->clid_len = clid_len;
Simon Kelley316e2732010-01-22 20:16:09 +0000771 buf->ed_len = ed_len;
Simon Kelley16972692006-10-16 20:04:18 +0100772 buf->hostname_len = hostname_len;
773 buf->addr = lease->addr;
Simon Kelley1f15b812009-10-13 17:49:32 +0100774 buf->giaddr = lease->giaddr;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000775 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
776 if (!indextoname(fd, lease->last_interface, buf->interface))
Simon Kelley316e2732010-01-22 20:16:09 +0000777 buf->interface[0] = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000778
Simon Kelley16972692006-10-16 20:04:18 +0100779#ifdef HAVE_BROKEN_RTC
780 buf->length = lease->length;
781#else
782 buf->expires = lease->expires;
783#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000784
785 if (lease->expires != 0)
786 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
787 else
788 buf->remaining_time = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100789
Simon Kelley16972692006-10-16 20:04:18 +0100790 p = (unsigned char *)(buf+1);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100791 if (clid_len != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100792 {
793 memcpy(p, lease->clid, clid_len);
794 p += clid_len;
795 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100796 if (hostname_len != 0)
797 {
798 memcpy(p, hostname, hostname_len);
799 p += hostname_len;
800 }
Simon Kelley316e2732010-01-22 20:16:09 +0000801 if (ed_len != 0)
802 {
803 memcpy(p, lease->extradata, ed_len);
804 p += ed_len;
805 }
Simon Kelley16972692006-10-16 20:04:18 +0100806 bytes_in_buf = p - (unsigned char *)buf;
807}
808
Simon Kelleya9530962012-03-20 22:07:35 +0000809#ifdef HAVE_TFTP
810/* This nastily re-uses DHCP-fields for TFTP stuff */
811void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
812{
813 unsigned int filename_len;
814
815 /* no script */
816 if (daemon->helperfd == -1)
817 return;
818
819 filename_len = strlen(filename) + 1;
820 buff_alloc(sizeof(struct script_data) + filename_len);
821 memset(buf, 0, sizeof(struct script_data));
822
823 buf->action = ACTION_TFTP;
824 buf->hostname_len = filename_len;
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100825 buf->file_len = file_len;
Simon Kelleya9530962012-03-20 22:07:35 +0000826
827 if ((buf->flags = peer->sa.sa_family) == AF_INET)
828 buf->addr = peer->in.sin_addr;
829#ifdef HAVE_IPV6
830 else
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100831 buf->addr6 = peer->in6.sin6_addr;
Simon Kelleya9530962012-03-20 22:07:35 +0000832#endif
833
834 memcpy((unsigned char *)(buf+1), filename, filename_len);
835
836 bytes_in_buf = sizeof(struct script_data) + filename_len;
837}
838#endif
839
Simon Kelley33702ab2015-12-28 23:17:15 +0000840void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr)
841{
842 /* no script */
843 if (daemon->helperfd == -1)
844 return;
845
846 buff_alloc(sizeof(struct script_data));
847 memset(buf, 0, sizeof(struct script_data));
848
849 buf->action = action;
850 buf->hwaddr_len = maclen;
851 buf->hwaddr_type = ARPHRD_ETHER;
852 if ((buf->flags = family) == AF_INET)
853 buf->addr = addr->addr.addr4;
854#ifdef HAVE_IPV6
855 else
856 buf->addr6 = addr->addr.addr6;
857#endif
858
859 memcpy(buf->hwaddr, mac, maclen);
860
861 bytes_in_buf = sizeof(struct script_data);
862}
863
Simon Kelley16972692006-10-16 20:04:18 +0100864int helper_buf_empty(void)
865{
866 return bytes_in_buf == 0;
867}
868
Simon Kelley5aabfc72007-08-29 11:24:47 +0100869void helper_write(void)
Simon Kelley16972692006-10-16 20:04:18 +0100870{
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
Simon Kelley5aabfc72007-08-29 11:24:47 +0100890#endif
Simon Kelley16972692006-10-16 20:04:18 +0100891
892
Simon Kelleyc72daea2012-01-05 21:33:27 +0000893