blob: 9c37e37a8e4c158e046c9e97b3ba14c47f393c19 [file] [log] [blame]
Simon Kelleyc49778d2016-01-06 18:52:33 +00001/* dnsmasq is Copyright (c) 2000-2016 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
Simon Kelley16972692006-10-16 20:04:18 +010021/* This file has code to fork a helper process which recieves 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
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 Kelley5aabfc72007-08-29 11:24:47 +0100100 /* ignore SIGTERM, so that we can clean up when the main process gets hit
101 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 Kelley16972692006-10-16 20:04:18 +0100107
Simon Kelley28866e92011-02-14 20:19:14 +0000108 if (!option_bool(OPT_DEBUG) && uid != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100109 {
110 gid_t dummy;
111 if (setgroups(0, &dummy) == -1 ||
112 setgid(gid) == -1 ||
113 setuid(uid) == -1)
114 {
Simon Kelley28866e92011-02-14 20:19:14 +0000115 if (option_bool(OPT_NO_FORK))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100116 /* send error to daemon process if no-fork */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000117 send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100118 else
119 {
120 /* kill daemon */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000121 send_event(event_fd, EVENT_DIE, 0, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100122 /* return error */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000123 send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100124 }
125 _exit(0);
126 }
127 }
128
Simon Kelleyc72daea2012-01-05 21:33:27 +0000129 /* close all the sockets etc, we don't need them here.
130 Don't close err_fd, in case the lua-init fails.
131 Note that we have to do this before lua init
132 so we don't close any lua fds. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100133 for (max_fd--; max_fd >= 0; max_fd--)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100134 if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
Simon Kelleyc72daea2012-01-05 21:33:27 +0000135 max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
136 max_fd != event_fd && max_fd != err_fd)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100137 close(max_fd);
Simon Kelley16972692006-10-16 20:04:18 +0100138
Simon Kelleyc72daea2012-01-05 21:33:27 +0000139#ifdef HAVE_LUASCRIPT
140 if (daemon->luascript)
141 {
142 const char *lua_err = NULL;
143 lua = lua_open();
144 luaL_openlibs(lua);
145
146 /* get Lua to load our script file */
147 if (luaL_dofile(lua, daemon->luascript) != 0)
148 lua_err = lua_tostring(lua, -1);
149 else
150 {
151 lua_getglobal(lua, "lease");
152 if (lua_type(lua, -1) != LUA_TFUNCTION)
153 lua_err = _("lease() function missing in Lua script");
154 }
155
156 if (lua_err)
157 {
158 if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
159 /* send error to daemon process if no-fork */
160 send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
161 else
162 {
163 /* kill daemon */
164 send_event(event_fd, EVENT_DIE, 0, NULL);
165 /* return error */
166 send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
167 }
168 _exit(0);
169 }
170
171 lua_pop(lua, 1); /* remove nil from stack */
172 lua_getglobal(lua, "init");
173 if (lua_type(lua, -1) == LUA_TFUNCTION)
174 lua_call(lua, 0, 0);
175 else
176 lua_pop(lua, 1); /* remove nil from stack */
177 }
178#endif
179
180 /* All init done, close our copy of the error pipe, so that main process can return */
181 if (err_fd != -1)
182 close(err_fd);
183
Simon Kelley16972692006-10-16 20:04:18 +0100184 /* loop here */
185 while(1)
186 {
187 struct script_data data;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000188 char *p, *action_str, *hostname = NULL, *domain = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100189 unsigned char *buf = (unsigned char *)daemon->namebuff;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000190 unsigned char *end, *extradata, *alloc_buff = NULL;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000191 int is6, err = 0;
Simon Kelley16972692006-10-16 20:04:18 +0100192
Simon Kelleyc72daea2012-01-05 21:33:27 +0000193 free(alloc_buff);
194
Simon Kelley16972692006-10-16 20:04:18 +0100195 /* we read zero bytes when pipe closed: this is our signal to exit */
196 if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000197 {
198#ifdef HAVE_LUASCRIPT
199 if (daemon->luascript)
200 {
201 lua_getglobal(lua, "shutdown");
202 if (lua_type(lua, -1) == LUA_TFUNCTION)
203 lua_call(lua, 0, 0);
204 }
205#endif
206 _exit(0);
207 }
Simon Kelleya9530962012-03-20 22:07:35 +0000208
209 is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
210
Simon Kelley16972692006-10-16 20:04:18 +0100211 if (data.action == ACTION_DEL)
212 action_str = "del";
213 else if (data.action == ACTION_ADD)
214 action_str = "add";
215 else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
216 action_str = "old";
Simon Kelleya9530962012-03-20 22:07:35 +0000217 else if (data.action == ACTION_TFTP)
218 {
219 action_str = "tftp";
220 is6 = (data.flags != AF_INET);
221 }
Simon Kelley33702ab2015-12-28 23:17:15 +0000222 else if (data.action == ACTION_ARP)
223 {
Simon Kelleye6e751b2016-02-01 17:59:07 +0000224 action_str = "arp-add";
Simon Kelley33702ab2015-12-28 23:17:15 +0000225 is6 = (data.flags != AF_INET);
226 }
Simon Kelleye6e751b2016-02-01 17:59:07 +0000227 else if (data.action == ACTION_ARP_DEL)
Simon Kelley33702ab2015-12-28 23:17:15 +0000228 {
Simon Kelleye6e751b2016-02-01 17:59:07 +0000229 action_str = "arp-del";
Simon Kelley33702ab2015-12-28 23:17:15 +0000230 is6 = (data.flags != AF_INET);
231 data.action = ACTION_ARP;
232 }
233 else
Simon Kelley16972692006-10-16 20:04:18 +0100234 continue;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000235
Simon Kelleya9530962012-03-20 22:07:35 +0000236
Simon Kelley89500e32013-09-20 16:29:20 +0100237 /* stringify MAC into dhcp_buff */
238 p = daemon->dhcp_buff;
239 if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
240 p += sprintf(p, "%.2x-", data.hwaddr_type);
241 for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000242 {
Simon Kelley89500e32013-09-20 16:29:20 +0100243 p += sprintf(p, "%.2x", data.hwaddr[i]);
244 if (i != data.hwaddr_len - 1)
245 p += sprintf(p, ":");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000246 }
Simon Kelley89500e32013-09-20 16:29:20 +0100247
Simon Kelleyc72daea2012-01-05 21:33:27 +0000248 /* supplied data may just exceed normal buffer (unlikely) */
249 if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
250 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
Simon Kelley16972692006-10-16 20:04:18 +0100251 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000252
253 if (!read_write(pipefd[0], buf,
254 data.hostname_len + data.ed_len + data.clid_len, 1))
255 continue;
256
257 /* CLID into packet */
Simon Kelley89500e32013-09-20 16:29:20 +0100258 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
259 {
260 p += sprintf(p, "%.2x", buf[i]);
261 if (i != data.clid_len - 1)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000262 p += sprintf(p, ":");
Simon Kelley89500e32013-09-20 16:29:20 +0100263 }
264
Simon Kelleyceae00d2012-02-09 21:28:14 +0000265#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100266 if (is6)
Simon Kelley16972692006-10-16 20:04:18 +0100267 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000268 /* or IAID and server DUID for IPv6 */
Simon Kelley89500e32013-09-20 16:29:20 +0100269 sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.iaid);
270 for (p = daemon->dhcp_packet.iov_base, i = 0; i < daemon->duid_len; i++)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000271 {
272 p += sprintf(p, "%.2x", daemon->duid[i]);
273 if (i != daemon->duid_len - 1)
274 p += sprintf(p, ":");
275 }
Simon Kelleycaa94382012-02-15 10:29:50 +0000276
Simon Kelley16972692006-10-16 20:04:18 +0100277 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000278#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000279
280 buf += data.clid_len;
281
282 if (data.hostname_len != 0)
283 {
284 char *dot;
285 hostname = (char *)buf;
286 hostname[data.hostname_len - 1] = 0;
Simon Kelleya9530962012-03-20 22:07:35 +0000287 if (data.action != ACTION_TFTP)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000288 {
Simon Kelleya9530962012-03-20 22:07:35 +0000289 if (!legal_hostname(hostname))
290 hostname = NULL;
291 else if ((dot = strchr(hostname, '.')))
292 {
293 domain = dot+1;
294 *dot = 0;
295 }
296 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000297 }
298
299 extradata = buf + data.hostname_len;
300
Simon Kelleyceae00d2012-02-09 21:28:14 +0000301 if (!is6)
302 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley1566bac2016-02-05 14:38:06 +0000303#ifdef HAVE_IPV6
Simon Kelleyceae00d2012-02-09 21:28:14 +0000304 else
Simon Kelley89500e32013-09-20 16:29:20 +0100305 inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000306#endif
307
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100308#ifdef HAVE_TFTP
Simon Kelleya9530962012-03-20 22:07:35 +0000309 /* file length */
310 if (data.action == ACTION_TFTP)
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100311 sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
312#endif
313
Simon Kelleyc72daea2012-01-05 21:33:27 +0000314#ifdef HAVE_LUASCRIPT
315 if (daemon->luascript)
316 {
Simon Kelleya9530962012-03-20 22:07:35 +0000317 if (data.action == ACTION_TFTP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000318 {
Simon Kelleya9530962012-03-20 22:07:35 +0000319 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");
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100330 lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
Simon Kelleya9530962012-03-20 22:07:35 +0000331 lua_setfield(lua, -2, "file_size");
332 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
333 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000334 }
Simon Kelley33702ab2015-12-28 23:17:15 +0000335 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 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000351 else
Simon Kelleya9530962012-03-20 22:07:35 +0000352 {
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 {
Simon Kelleya9530962012-03-20 22:07:35 +0000359 lua_pushstring(lua, daemon->packet);
Simon Kelley89500e32013-09-20 16:29:20 +0100360 lua_setfield(lua, -2, "client_duid");
361 lua_pushstring(lua, daemon->dhcp_packet.iov_base);
Simon Kelleya9530962012-03-20 22:07:35 +0000362 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");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000385#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000386
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
Simon Kelley89500e32013-09-20 16:29:20 +0100405 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 }
Simon Kelleya9530962012-03-20 22:07:35 +0000415#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");
Simon Kelleydd1721c2013-02-18 21:04:04 +0000424 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");
Simon Kelleya9530962012-03-20 22:07:35 +0000427 }
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 lua_pushstring(lua, inet_ntoa(data.giaddr));
436 lua_setfield(lua, -2, "relay_address");
437 }
438
439 for (i = 0; buf; i++)
440 {
441 sprintf(daemon->dhcp_buff2, "user_class%i", i);
442 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
443 }
444
445 if (data.action != ACTION_DEL && data.remaining_time != 0)
446 {
447 lua_pushnumber(lua, data.remaining_time);
448 lua_setfield(lua, -2, "time_remaining");
449 }
450
451 if (data.action == ACTION_OLD_HOSTNAME && hostname)
452 {
453 lua_pushstring(lua, hostname);
454 lua_setfield(lua, -2, "old_hostname");
455 }
456
Simon Kelley89500e32013-09-20 16:29:20 +0100457 if (!is6 || data.hwaddr_len != 0)
Simon Kelleya9530962012-03-20 22:07:35 +0000458 {
459 lua_pushstring(lua, daemon->dhcp_buff);
460 lua_setfield(lua, -2, "mac_address");
461 }
462
463 lua_pushstring(lua, daemon->addrbuff);
464 lua_setfield(lua, -2, "ip_address");
465
466 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000467 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000468 }
469#endif
470
471 /* no script, just lua */
472 if (!daemon->lease_change_command)
Simon Kelley316e2732010-01-22 20:16:09 +0000473 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000474
Simon Kelley5aabfc72007-08-29 11:24:47 +0100475 /* possible fork errors are all temporary resource problems */
476 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
477 sleep(2);
Simon Kelley316e2732010-01-22 20:16:09 +0000478
Simon Kelley5aabfc72007-08-29 11:24:47 +0100479 if (pid == -1)
480 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000481
Simon Kelley16972692006-10-16 20:04:18 +0100482 /* wait for child to complete */
483 if (pid != 0)
484 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100485 /* reap our children's children, if necessary */
486 while (1)
487 {
488 int status;
489 pid_t rc = wait(&status);
490
491 if (rc == pid)
492 {
493 /* On error send event back to main process for logging */
494 if (WIFSIGNALED(status))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000495 send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100496 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000497 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100498 break;
499 }
500
501 if (rc == -1 && errno != EINTR)
502 break;
503 }
504
Simon Kelley16972692006-10-16 20:04:18 +0100505 continue;
506 }
507
Simon Kelley33702ab2015-12-28 23:17:15 +0000508 if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000509 {
Simon Kelley89500e32013-09-20 16:29:20 +0100510#ifdef HAVE_DHCP6
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100511 my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
512 my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err);
513 my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100514#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000515
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100516 my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
517 my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000518
519#ifdef HAVE_BROKEN_RTC
Simon Kelley208fb612013-02-21 22:26:18 +0000520 sprintf(daemon->dhcp_buff2, "%u", data.length);
Simon Kelleya9530962012-03-20 22:07:35 +0000521 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
522#else
Simon Kelley208fb612013-02-21 22:26:18 +0000523 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
Simon Kelleya9530962012-03-20 22:07:35 +0000524 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
525#endif
526
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100527 my_setenv("DNSMASQ_DOMAIN", domain, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000528
529 end = extradata + data.ed_len;
530 buf = extradata;
531
532 if (!is6)
533 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
534#ifdef HAVE_DHCP6
535 else
536 {
Simon Kelley89500e32013-09-20 16:29:20 +0100537 if (data.vendorclass_count != 0)
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000538 {
Simon Kelleya9530962012-03-20 22:07:35 +0000539 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100540 for (i = 0; i < data.vendorclass_count - 1; i++)
Simon Kelleya9530962012-03-20 22:07:35 +0000541 {
542 sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
543 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
544 }
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000545 }
546 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000547#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000548
549 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
550
551 if (!is6)
552 {
553 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
554 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
555 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000556 buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
557 buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
558 buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000559 }
560
561 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000562
Simon Kelleya9530962012-03-20 22:07:35 +0000563 if (is6)
564 buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100565 else
566 my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000567
568 for (i = 0; buf; i++)
569 {
570 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
571 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
572 }
573
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100574 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
575 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 +0000576
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100577 my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
578 if (data.action == ACTION_OLD_HOSTNAME)
579 hostname = NULL;
Simon Kelley33702ab2015-12-28 23:17:15 +0000580
581 my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
582 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100583 /* we need to have the event_fd around if exec fails */
584 if ((i = fcntl(event_fd, F_GETFD)) != -1)
585 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
586 close(pipefd[0]);
587
Simon Kelley16972692006-10-16 20:04:18 +0100588 p = strrchr(daemon->lease_change_command, '/');
Simon Kelley824af852008-02-12 20:43:05 +0000589 if (err == 0)
590 {
591 execl(daemon->lease_change_command,
Simon Kelley33702ab2015-12-28 23:17:15 +0000592 p ? p+1 : daemon->lease_change_command, action_str,
593 (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
Simon Kelley89500e32013-09-20 16:29:20 +0100594 daemon->addrbuff, hostname, (char*)NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000595 err = errno;
596 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100597 /* failed, send event so the main process logs the problem */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000598 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100599 _exit(0);
600 }
601}
602
Simon Kelley824af852008-02-12 20:43:05 +0000603static void my_setenv(const char *name, const char *value, int *error)
604{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100605 if (*error == 0)
606 {
607 if (!value)
608 unsetenv(name);
609 else if (setenv(name, value, 1) != 0)
610 *error = errno;
611 }
Simon Kelley824af852008-02-12 20:43:05 +0000612}
613
Simon Kelley316e2732010-01-22 20:16:09 +0000614static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
615{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100616 unsigned char *next = NULL;
617 char *val = NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000618
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100619 if (buf && (buf != end))
Simon Kelley316e2732010-01-22 20:16:09 +0000620 {
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100621 for (next = buf; ; next++)
622 if (next == end)
623 {
624 next = NULL;
625 break;
626 }
627 else if (*next == 0)
628 break;
Simon Kelley316e2732010-01-22 20:16:09 +0000629
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100630 if (next && (next != buf))
631 {
632 char *p;
633 /* No "=" in value */
634 if ((p = strchr((char *)buf, '=')))
635 *p = 0;
636 val = (char *)buf;
637 }
638 }
639
640 my_setenv(env, val, err);
641
642 return next ? next + 1 : NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000643}
644
Simon Kelleyc72daea2012-01-05 21:33:27 +0000645#ifdef HAVE_LUASCRIPT
646static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
647{
648 unsigned char *next;
649
650 if (!buf || (buf == end))
651 return NULL;
652
653 for (next = buf; *next != 0; next++)
654 if (next == end)
655 return NULL;
656
657 if (next != buf)
658 {
659 lua_pushstring(lua, (char *)buf);
660 lua_setfield(lua, -2, field);
661 }
662
663 return next + 1;
664}
665#endif
666
Simon Kelleya9530962012-03-20 22:07:35 +0000667static void buff_alloc(size_t size)
668{
669 if (size > buf_size)
670 {
671 struct script_data *new;
672
673 /* start with reasonable size, will almost never need extending. */
674 if (size < sizeof(struct script_data) + 200)
675 size = sizeof(struct script_data) + 200;
676
677 if (!(new = whine_malloc(size)))
678 return;
679 if (buf)
680 free(buf);
681 buf = new;
682 buf_size = size;
683 }
684}
685
Simon Kelley16972692006-10-16 20:04:18 +0100686/* pack up lease data into a buffer */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100687void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
Simon Kelley16972692006-10-16 20:04:18 +0100688{
689 unsigned char *p;
Simon Kelley316e2732010-01-22 20:16:09 +0000690 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000691 int fd = daemon->dhcpfd;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100692#ifdef HAVE_DHCP6
Simon Kelleyceae00d2012-02-09 21:28:14 +0000693 if (!daemon->dhcp)
694 fd = daemon->dhcp6fd;
695#endif
696
Simon Kelley16972692006-10-16 20:04:18 +0100697 /* no script */
698 if (daemon->helperfd == -1)
699 return;
700
Simon Kelley316e2732010-01-22 20:16:09 +0000701 if (lease->extradata)
702 ed_len = lease->extradata_len;
Simon Kelley16972692006-10-16 20:04:18 +0100703 if (lease->clid)
704 clid_len = lease->clid_len;
705 if (hostname)
706 hostname_len = strlen(hostname) + 1;
707
Simon Kelleya9530962012-03-20 22:07:35 +0000708 buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
Simon Kelley16972692006-10-16 20:04:18 +0100709
710 buf->action = action;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000711 buf->flags = lease->flags;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100712#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100713 buf->vendorclass_count = lease->vendorclass_count;
714 buf->addr6 = lease->addr6;
715 buf->iaid = lease->iaid;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100716#endif
Simon Kelley89500e32013-09-20 16:29:20 +0100717 buf->hwaddr_len = lease->hwaddr_len;
Simon Kelley16972692006-10-16 20:04:18 +0100718 buf->hwaddr_type = lease->hwaddr_type;
719 buf->clid_len = clid_len;
Simon Kelley316e2732010-01-22 20:16:09 +0000720 buf->ed_len = ed_len;
Simon Kelley16972692006-10-16 20:04:18 +0100721 buf->hostname_len = hostname_len;
722 buf->addr = lease->addr;
Simon Kelley1f15b812009-10-13 17:49:32 +0100723 buf->giaddr = lease->giaddr;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000724 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
725 if (!indextoname(fd, lease->last_interface, buf->interface))
Simon Kelley316e2732010-01-22 20:16:09 +0000726 buf->interface[0] = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000727
Simon Kelley16972692006-10-16 20:04:18 +0100728#ifdef HAVE_BROKEN_RTC
729 buf->length = lease->length;
730#else
731 buf->expires = lease->expires;
732#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000733
734 if (lease->expires != 0)
735 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
736 else
737 buf->remaining_time = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100738
Simon Kelley16972692006-10-16 20:04:18 +0100739 p = (unsigned char *)(buf+1);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100740 if (clid_len != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100741 {
742 memcpy(p, lease->clid, clid_len);
743 p += clid_len;
744 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100745 if (hostname_len != 0)
746 {
747 memcpy(p, hostname, hostname_len);
748 p += hostname_len;
749 }
Simon Kelley316e2732010-01-22 20:16:09 +0000750 if (ed_len != 0)
751 {
752 memcpy(p, lease->extradata, ed_len);
753 p += ed_len;
754 }
Simon Kelley16972692006-10-16 20:04:18 +0100755 bytes_in_buf = p - (unsigned char *)buf;
756}
757
Simon Kelleya9530962012-03-20 22:07:35 +0000758#ifdef HAVE_TFTP
759/* This nastily re-uses DHCP-fields for TFTP stuff */
760void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
761{
762 unsigned int filename_len;
763
764 /* no script */
765 if (daemon->helperfd == -1)
766 return;
767
768 filename_len = strlen(filename) + 1;
769 buff_alloc(sizeof(struct script_data) + filename_len);
770 memset(buf, 0, sizeof(struct script_data));
771
772 buf->action = ACTION_TFTP;
773 buf->hostname_len = filename_len;
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100774 buf->file_len = file_len;
Simon Kelleya9530962012-03-20 22:07:35 +0000775
776 if ((buf->flags = peer->sa.sa_family) == AF_INET)
777 buf->addr = peer->in.sin_addr;
778#ifdef HAVE_IPV6
779 else
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100780 buf->addr6 = peer->in6.sin6_addr;
Simon Kelleya9530962012-03-20 22:07:35 +0000781#endif
782
783 memcpy((unsigned char *)(buf+1), filename, filename_len);
784
785 bytes_in_buf = sizeof(struct script_data) + filename_len;
786}
787#endif
788
Simon Kelley33702ab2015-12-28 23:17:15 +0000789void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr)
790{
791 /* no script */
792 if (daemon->helperfd == -1)
793 return;
794
795 buff_alloc(sizeof(struct script_data));
796 memset(buf, 0, sizeof(struct script_data));
797
798 buf->action = action;
799 buf->hwaddr_len = maclen;
800 buf->hwaddr_type = ARPHRD_ETHER;
801 if ((buf->flags = family) == AF_INET)
802 buf->addr = addr->addr.addr4;
803#ifdef HAVE_IPV6
804 else
805 buf->addr6 = addr->addr.addr6;
806#endif
807
808 memcpy(buf->hwaddr, mac, maclen);
809
810 bytes_in_buf = sizeof(struct script_data);
811}
812
Simon Kelley16972692006-10-16 20:04:18 +0100813int helper_buf_empty(void)
814{
815 return bytes_in_buf == 0;
816}
817
Simon Kelley5aabfc72007-08-29 11:24:47 +0100818void helper_write(void)
Simon Kelley16972692006-10-16 20:04:18 +0100819{
820 ssize_t rc;
821
822 if (bytes_in_buf == 0)
823 return;
824
825 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
826 {
827 if (bytes_in_buf != (size_t)rc)
828 memmove(buf, buf + rc, bytes_in_buf - rc);
829 bytes_in_buf -= rc;
830 }
831 else
832 {
833 if (errno == EAGAIN || errno == EINTR)
834 return;
835 bytes_in_buf = 0;
836 }
837}
838
Simon Kelley5aabfc72007-08-29 11:24:47 +0100839#endif
Simon Kelley16972692006-10-16 20:04:18 +0100840
841
Simon Kelleyc72daea2012-01-05 21:33:27 +0000842