blob: d02d53a70e82b91ac8db54745eea5f3aab074767 [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
Simon Kelley89500e32013-09-20 16:29:20 +010069 int iaid, vendorclass_count;
70#endif
Simon Kelley16972692006-10-16 20:04:18 +010071 unsigned char hwaddr[DHCP_CHADDR_MAX];
Simon Kelley824af852008-02-12 20:43:05 +000072 char interface[IF_NAMESIZE];
Simon Kelley16972692006-10-16 20:04:18 +010073};
74
Simon Kelley5aabfc72007-08-29 11:24:47 +010075static struct script_data *buf = NULL;
76static size_t bytes_in_buf = 0, buf_size = 0;
Simon Kelley16972692006-10-16 20:04:18 +010077
Simon Kelley1a6bca82008-07-11 11:11:42 +010078int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
Simon Kelley16972692006-10-16 20:04:18 +010079{
80 pid_t pid;
81 int i, pipefd[2];
82 struct sigaction sigact;
Simon Kelley9e038942008-05-30 20:06:34 +010083
Simon Kelley5aabfc72007-08-29 11:24:47 +010084 /* create the pipe through which the main program sends us commands,
Simon Kelley1a6bca82008-07-11 11:11:42 +010085 then fork our process. */
Simon Kelley5aabfc72007-08-29 11:24:47 +010086 if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
87 {
Simon Kelleyc72daea2012-01-05 21:33:27 +000088 send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +010089 _exit(0);
Simon Kelley5aabfc72007-08-29 11:24:47 +010090 }
91
Simon Kelley16972692006-10-16 20:04:18 +010092 if (pid != 0)
93 {
94 close(pipefd[0]); /* close reader side */
95 return pipefd[1];
96 }
97
Simon Kelley3c973ad2018-01-14 21:05:37 +000098 /* ignore SIGTERM and SIGINT, so that we can clean up when the main process gets hit
Simon Kelley5aabfc72007-08-29 11:24:47 +010099 and SIGALRM so that we can use sleep() */
Simon Kelley16972692006-10-16 20:04:18 +0100100 sigact.sa_handler = SIG_IGN;
101 sigact.sa_flags = 0;
102 sigemptyset(&sigact.sa_mask);
103 sigaction(SIGTERM, &sigact, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100104 sigaction(SIGALRM, &sigact, NULL);
Simon Kelley3c973ad2018-01-14 21:05:37 +0000105 sigaction(SIGINT, &sigact, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100106
Simon Kelley28866e92011-02-14 20:19:14 +0000107 if (!option_bool(OPT_DEBUG) && uid != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100108 {
109 gid_t dummy;
110 if (setgroups(0, &dummy) == -1 ||
111 setgid(gid) == -1 ||
112 setuid(uid) == -1)
113 {
Simon Kelley28866e92011-02-14 20:19:14 +0000114 if (option_bool(OPT_NO_FORK))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100115 /* send error to daemon process if no-fork */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000116 send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100117 else
118 {
119 /* kill daemon */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000120 send_event(event_fd, EVENT_DIE, 0, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100121 /* return error */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000122 send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100123 }
124 _exit(0);
125 }
126 }
127
Simon Kelleyc72daea2012-01-05 21:33:27 +0000128 /* close all the sockets etc, we don't need them here.
129 Don't close err_fd, in case the lua-init fails.
130 Note that we have to do this before lua init
131 so we don't close any lua fds. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100132 for (max_fd--; max_fd >= 0; max_fd--)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100133 if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
Simon Kelleyc72daea2012-01-05 21:33:27 +0000134 max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
135 max_fd != event_fd && max_fd != err_fd)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100136 close(max_fd);
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100137
Simon Kelleyc72daea2012-01-05 21:33:27 +0000138#ifdef HAVE_LUASCRIPT
139 if (daemon->luascript)
140 {
141 const char *lua_err = NULL;
142 lua = lua_open();
143 luaL_openlibs(lua);
144
145 /* get Lua to load our script file */
146 if (luaL_dofile(lua, daemon->luascript) != 0)
147 lua_err = lua_tostring(lua, -1);
148 else
149 {
150 lua_getglobal(lua, "lease");
151 if (lua_type(lua, -1) != LUA_TFUNCTION)
152 lua_err = _("lease() function missing in Lua script");
153 }
154
155 if (lua_err)
156 {
157 if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
158 /* send error to daemon process if no-fork */
159 send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
160 else
161 {
162 /* kill daemon */
163 send_event(event_fd, EVENT_DIE, 0, NULL);
164 /* return error */
165 send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
166 }
167 _exit(0);
168 }
169
170 lua_pop(lua, 1); /* remove nil from stack */
171 lua_getglobal(lua, "init");
172 if (lua_type(lua, -1) == LUA_TFUNCTION)
173 lua_call(lua, 0, 0);
174 else
175 lua_pop(lua, 1); /* remove nil from stack */
176 }
177#endif
178
179 /* All init done, close our copy of the error pipe, so that main process can return */
180 if (err_fd != -1)
181 close(err_fd);
182
Simon Kelley16972692006-10-16 20:04:18 +0100183 /* loop here */
184 while(1)
185 {
186 struct script_data data;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000187 char *p, *action_str, *hostname = NULL, *domain = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100188 unsigned char *buf = (unsigned char *)daemon->namebuff;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000189 unsigned char *end, *extradata, *alloc_buff = NULL;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000190 int is6, err = 0;
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100191 int pipeout[2];
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 Kelleyceae00d2012-02-09 21:28:14 +0000303 else
Simon Kelley89500e32013-09-20 16:29:20 +0100304 inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000305
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100306#ifdef HAVE_TFTP
Simon Kelleya9530962012-03-20 22:07:35 +0000307 /* file length */
308 if (data.action == ACTION_TFTP)
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100309 sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
310#endif
311
Simon Kelleyc72daea2012-01-05 21:33:27 +0000312#ifdef HAVE_LUASCRIPT
313 if (daemon->luascript)
314 {
Simon Kelleya9530962012-03-20 22:07:35 +0000315 if (data.action == ACTION_TFTP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000316 {
Simon Kelleya9530962012-03-20 22:07:35 +0000317 lua_getglobal(lua, "tftp");
318 if (lua_type(lua, -1) != LUA_TFUNCTION)
319 lua_pop(lua, 1); /* tftp function optional */
320 else
321 {
322 lua_pushstring(lua, action_str); /* arg1 - action */
323 lua_newtable(lua); /* arg2 - data table */
324 lua_pushstring(lua, daemon->addrbuff);
325 lua_setfield(lua, -2, "destination_address");
326 lua_pushstring(lua, hostname);
327 lua_setfield(lua, -2, "file_name");
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100328 lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
Simon Kelleya9530962012-03-20 22:07:35 +0000329 lua_setfield(lua, -2, "file_size");
330 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
331 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000332 }
Simon Kelley33702ab2015-12-28 23:17:15 +0000333 else if (data.action == ACTION_ARP)
334 {
335 lua_getglobal(lua, "arp");
336 if (lua_type(lua, -1) != LUA_TFUNCTION)
337 lua_pop(lua, 1); /* arp function optional */
338 else
339 {
340 lua_pushstring(lua, action_str); /* arg1 - action */
341 lua_newtable(lua); /* arg2 - data table */
342 lua_pushstring(lua, daemon->addrbuff);
343 lua_setfield(lua, -2, "client_address");
344 lua_pushstring(lua, daemon->dhcp_buff);
345 lua_setfield(lua, -2, "mac_address");
346 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
347 }
348 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000349 else
Simon Kelleya9530962012-03-20 22:07:35 +0000350 {
351 lua_getglobal(lua, "lease"); /* function to call */
352 lua_pushstring(lua, action_str); /* arg1 - action */
353 lua_newtable(lua); /* arg2 - data table */
354
355 if (is6)
356 {
Simon Kelleya9530962012-03-20 22:07:35 +0000357 lua_pushstring(lua, daemon->packet);
Simon Kelley89500e32013-09-20 16:29:20 +0100358 lua_setfield(lua, -2, "client_duid");
359 lua_pushstring(lua, daemon->dhcp_packet.iov_base);
Simon Kelleya9530962012-03-20 22:07:35 +0000360 lua_setfield(lua, -2, "server_duid");
361 lua_pushstring(lua, daemon->dhcp_buff3);
362 lua_setfield(lua, -2, "iaid");
363 }
364
365 if (!is6 && data.clid_len != 0)
366 {
367 lua_pushstring(lua, daemon->packet);
368 lua_setfield(lua, -2, "client_id");
369 }
370
371 if (strlen(data.interface) != 0)
372 {
373 lua_pushstring(lua, data.interface);
374 lua_setfield(lua, -2, "interface");
375 }
376
377#ifdef HAVE_BROKEN_RTC
378 lua_pushnumber(lua, data.length);
379 lua_setfield(lua, -2, "lease_length");
380#else
381 lua_pushnumber(lua, data.expires);
382 lua_setfield(lua, -2, "lease_expires");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000383#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000384
385 if (hostname)
386 {
387 lua_pushstring(lua, hostname);
388 lua_setfield(lua, -2, "hostname");
389 }
390
391 if (domain)
392 {
393 lua_pushstring(lua, domain);
394 lua_setfield(lua, -2, "domain");
395 }
396
397 end = extradata + data.ed_len;
398 buf = extradata;
399
400 if (!is6)
401 buf = grab_extradata_lua(buf, end, "vendor_class");
402#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100403 else if (data.vendorclass_count != 0)
404 {
405 sprintf(daemon->dhcp_buff2, "vendor_class_id");
406 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
407 for (i = 0; i < data.vendorclass_count - 1; i++)
408 {
409 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
410 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
411 }
412 }
Simon Kelleya9530962012-03-20 22:07:35 +0000413#endif
414
415 buf = grab_extradata_lua(buf, end, "supplied_hostname");
416
417 if (!is6)
418 {
419 buf = grab_extradata_lua(buf, end, "cpewan_oui");
420 buf = grab_extradata_lua(buf, end, "cpewan_serial");
421 buf = grab_extradata_lua(buf, end, "cpewan_class");
Simon Kelleydd1721c2013-02-18 21:04:04 +0000422 buf = grab_extradata_lua(buf, end, "circuit_id");
423 buf = grab_extradata_lua(buf, end, "subscriber_id");
424 buf = grab_extradata_lua(buf, end, "remote_id");
Simon Kelleya9530962012-03-20 22:07:35 +0000425 }
426
427 buf = grab_extradata_lua(buf, end, "tags");
428
429 if (is6)
430 buf = grab_extradata_lua(buf, end, "relay_address");
431 else if (data.giaddr.s_addr != 0)
432 {
433 lua_pushstring(lua, inet_ntoa(data.giaddr));
434 lua_setfield(lua, -2, "relay_address");
435 }
436
437 for (i = 0; buf; i++)
438 {
439 sprintf(daemon->dhcp_buff2, "user_class%i", i);
440 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
441 }
442
443 if (data.action != ACTION_DEL && data.remaining_time != 0)
444 {
445 lua_pushnumber(lua, data.remaining_time);
446 lua_setfield(lua, -2, "time_remaining");
447 }
448
449 if (data.action == ACTION_OLD_HOSTNAME && hostname)
450 {
451 lua_pushstring(lua, hostname);
452 lua_setfield(lua, -2, "old_hostname");
453 }
454
Simon Kelley89500e32013-09-20 16:29:20 +0100455 if (!is6 || data.hwaddr_len != 0)
Simon Kelleya9530962012-03-20 22:07:35 +0000456 {
457 lua_pushstring(lua, daemon->dhcp_buff);
458 lua_setfield(lua, -2, "mac_address");
459 }
460
461 lua_pushstring(lua, daemon->addrbuff);
462 lua_setfield(lua, -2, "ip_address");
463
464 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000465 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000466 }
467#endif
468
469 /* no script, just lua */
470 if (!daemon->lease_change_command)
Simon Kelley316e2732010-01-22 20:16:09 +0000471 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000472
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100473 /* Pipe to capture stdout and stderr from script */
474 if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1)
475 continue;
476
Simon Kelley5aabfc72007-08-29 11:24:47 +0100477 /* possible fork errors are all temporary resource problems */
478 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
479 sleep(2);
Simon Kelley316e2732010-01-22 20:16:09 +0000480
Simon Kelley5aabfc72007-08-29 11:24:47 +0100481 if (pid == -1)
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100482 {
483 if (!option_bool(OPT_DEBUG))
484 {
485 close(pipeout[0]);
486 close(pipeout[1]);
487 }
488 continue;
489 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000490
Simon Kelley16972692006-10-16 20:04:18 +0100491 /* wait for child to complete */
492 if (pid != 0)
493 {
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100494 if (!option_bool(OPT_DEBUG))
495 {
496 FILE *fp;
497
498 close(pipeout[1]);
499
500 /* Read lines sent to stdout/err by the script and pass them back to be logged */
501 if (!(fp = fdopen(pipeout[0], "r")))
502 close(pipeout[0]);
503 else
504 {
505 while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
506 {
507 /* do not include new lines, log will append them */
508 size_t len = strlen(daemon->packet);
509 if (len > 0)
510 {
511 --len;
512 if (daemon->packet[len] == '\n')
513 daemon->packet[len] = 0;
514 }
515 send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
516 }
517 fclose(fp);
518 }
519 }
520
Simon Kelley5aabfc72007-08-29 11:24:47 +0100521 /* reap our children's children, if necessary */
522 while (1)
523 {
524 int status;
525 pid_t rc = wait(&status);
526
527 if (rc == pid)
528 {
529 /* On error send event back to main process for logging */
530 if (WIFSIGNALED(status))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000531 send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100532 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000533 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100534 break;
535 }
536
537 if (rc == -1 && errno != EINTR)
538 break;
539 }
540
Simon Kelley16972692006-10-16 20:04:18 +0100541 continue;
542 }
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100543
544 if (!option_bool(OPT_DEBUG))
545 {
546 /* map stdout/stderr of script to pipeout */
547 close(pipeout[0]);
548 dup2(pipeout[1], STDOUT_FILENO);
549 dup2(pipeout[1], STDERR_FILENO);
550 close(pipeout[1]);
551 }
Simon Kelley16972692006-10-16 20:04:18 +0100552
Simon Kelley33702ab2015-12-28 23:17:15 +0000553 if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000554 {
Simon Kelley89500e32013-09-20 16:29:20 +0100555#ifdef HAVE_DHCP6
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100556 my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
557 my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err);
558 my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100559#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000560
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100561 my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
562 my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000563
564#ifdef HAVE_BROKEN_RTC
Simon Kelley208fb612013-02-21 22:26:18 +0000565 sprintf(daemon->dhcp_buff2, "%u", data.length);
Simon Kelleya9530962012-03-20 22:07:35 +0000566 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
567#else
Simon Kelley208fb612013-02-21 22:26:18 +0000568 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
Simon Kelleya9530962012-03-20 22:07:35 +0000569 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
570#endif
571
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100572 my_setenv("DNSMASQ_DOMAIN", domain, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000573
574 end = extradata + data.ed_len;
575 buf = extradata;
576
577 if (!is6)
578 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
579#ifdef HAVE_DHCP6
580 else
581 {
Simon Kelley89500e32013-09-20 16:29:20 +0100582 if (data.vendorclass_count != 0)
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000583 {
Simon Kelleya9530962012-03-20 22:07:35 +0000584 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100585 for (i = 0; i < data.vendorclass_count - 1; i++)
Simon Kelleya9530962012-03-20 22:07:35 +0000586 {
587 sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
588 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
589 }
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000590 }
591 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000592#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000593
594 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
595
596 if (!is6)
597 {
598 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
599 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
600 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000601 buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
602 buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
603 buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
ZHAO Yuf89cae32016-12-22 22:32:31 +0000604 buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000605 }
606
607 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000608
Simon Kelleya9530962012-03-20 22:07:35 +0000609 if (is6)
610 buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100611 else
612 my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000613
614 for (i = 0; buf; i++)
615 {
616 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
617 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
618 }
619
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100620 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
621 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 +0000622
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100623 my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
624 if (data.action == ACTION_OLD_HOSTNAME)
625 hostname = NULL;
Simon Kelley33702ab2015-12-28 23:17:15 +0000626
627 my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
Petr Menšíkc77fb9d2017-04-16 20:20:08 +0100628 }
629
Simon Kelley5aabfc72007-08-29 11:24:47 +0100630 /* we need to have the event_fd around if exec fails */
631 if ((i = fcntl(event_fd, F_GETFD)) != -1)
632 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
633 close(pipefd[0]);
634
Simon Kelley16972692006-10-16 20:04:18 +0100635 p = strrchr(daemon->lease_change_command, '/');
Simon Kelley824af852008-02-12 20:43:05 +0000636 if (err == 0)
637 {
638 execl(daemon->lease_change_command,
Simon Kelley33702ab2015-12-28 23:17:15 +0000639 p ? p+1 : daemon->lease_change_command, action_str,
640 (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff,
Simon Kelley89500e32013-09-20 16:29:20 +0100641 daemon->addrbuff, hostname, (char*)NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000642 err = errno;
643 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100644 /* failed, send event so the main process logs the problem */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000645 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100646 _exit(0);
647 }
648}
649
Simon Kelley824af852008-02-12 20:43:05 +0000650static void my_setenv(const char *name, const char *value, int *error)
651{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100652 if (*error == 0)
653 {
654 if (!value)
655 unsetenv(name);
656 else if (setenv(name, value, 1) != 0)
657 *error = errno;
658 }
Simon Kelley824af852008-02-12 20:43:05 +0000659}
660
Simon Kelley316e2732010-01-22 20:16:09 +0000661static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
662{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100663 unsigned char *next = NULL;
664 char *val = NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000665
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100666 if (buf && (buf != end))
Simon Kelley316e2732010-01-22 20:16:09 +0000667 {
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100668 for (next = buf; ; next++)
669 if (next == end)
670 {
671 next = NULL;
672 break;
673 }
674 else if (*next == 0)
675 break;
Simon Kelley316e2732010-01-22 20:16:09 +0000676
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100677 if (next && (next != buf))
678 {
679 char *p;
680 /* No "=" in value */
681 if ((p = strchr((char *)buf, '=')))
682 *p = 0;
683 val = (char *)buf;
684 }
685 }
686
687 my_setenv(env, val, err);
688
689 return next ? next + 1 : NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000690}
691
Simon Kelleyc72daea2012-01-05 21:33:27 +0000692#ifdef HAVE_LUASCRIPT
693static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
694{
695 unsigned char *next;
696
697 if (!buf || (buf == end))
698 return NULL;
699
700 for (next = buf; *next != 0; next++)
701 if (next == end)
702 return NULL;
703
704 if (next != buf)
705 {
706 lua_pushstring(lua, (char *)buf);
707 lua_setfield(lua, -2, field);
708 }
709
710 return next + 1;
711}
712#endif
713
Simon Kelleya9530962012-03-20 22:07:35 +0000714static void buff_alloc(size_t size)
715{
716 if (size > buf_size)
717 {
718 struct script_data *new;
719
720 /* start with reasonable size, will almost never need extending. */
721 if (size < sizeof(struct script_data) + 200)
722 size = sizeof(struct script_data) + 200;
723
724 if (!(new = whine_malloc(size)))
725 return;
726 if (buf)
727 free(buf);
728 buf = new;
729 buf_size = size;
730 }
731}
732
Simon Kelley16972692006-10-16 20:04:18 +0100733/* pack up lease data into a buffer */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100734void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
Simon Kelley16972692006-10-16 20:04:18 +0100735{
736 unsigned char *p;
Simon Kelley316e2732010-01-22 20:16:09 +0000737 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000738 int fd = daemon->dhcpfd;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100739#ifdef HAVE_DHCP6
Simon Kelleyceae00d2012-02-09 21:28:14 +0000740 if (!daemon->dhcp)
741 fd = daemon->dhcp6fd;
742#endif
743
Simon Kelley16972692006-10-16 20:04:18 +0100744 /* no script */
745 if (daemon->helperfd == -1)
746 return;
747
Simon Kelley316e2732010-01-22 20:16:09 +0000748 if (lease->extradata)
749 ed_len = lease->extradata_len;
Simon Kelley16972692006-10-16 20:04:18 +0100750 if (lease->clid)
751 clid_len = lease->clid_len;
752 if (hostname)
753 hostname_len = strlen(hostname) + 1;
754
Simon Kelleya9530962012-03-20 22:07:35 +0000755 buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
Simon Kelley16972692006-10-16 20:04:18 +0100756
757 buf->action = action;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000758 buf->flags = lease->flags;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100759#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100760 buf->vendorclass_count = lease->vendorclass_count;
761 buf->addr6 = lease->addr6;
762 buf->iaid = lease->iaid;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100763#endif
Simon Kelley89500e32013-09-20 16:29:20 +0100764 buf->hwaddr_len = lease->hwaddr_len;
Simon Kelley16972692006-10-16 20:04:18 +0100765 buf->hwaddr_type = lease->hwaddr_type;
766 buf->clid_len = clid_len;
Simon Kelley316e2732010-01-22 20:16:09 +0000767 buf->ed_len = ed_len;
Simon Kelley16972692006-10-16 20:04:18 +0100768 buf->hostname_len = hostname_len;
769 buf->addr = lease->addr;
Simon Kelley1f15b812009-10-13 17:49:32 +0100770 buf->giaddr = lease->giaddr;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000771 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
772 if (!indextoname(fd, lease->last_interface, buf->interface))
Simon Kelley316e2732010-01-22 20:16:09 +0000773 buf->interface[0] = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000774
Simon Kelley16972692006-10-16 20:04:18 +0100775#ifdef HAVE_BROKEN_RTC
776 buf->length = lease->length;
777#else
778 buf->expires = lease->expires;
779#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000780
781 if (lease->expires != 0)
782 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
783 else
784 buf->remaining_time = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100785
Simon Kelley16972692006-10-16 20:04:18 +0100786 p = (unsigned char *)(buf+1);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100787 if (clid_len != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100788 {
789 memcpy(p, lease->clid, clid_len);
790 p += clid_len;
791 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100792 if (hostname_len != 0)
793 {
794 memcpy(p, hostname, hostname_len);
795 p += hostname_len;
796 }
Simon Kelley316e2732010-01-22 20:16:09 +0000797 if (ed_len != 0)
798 {
799 memcpy(p, lease->extradata, ed_len);
800 p += ed_len;
801 }
Simon Kelley16972692006-10-16 20:04:18 +0100802 bytes_in_buf = p - (unsigned char *)buf;
803}
804
Simon Kelleya9530962012-03-20 22:07:35 +0000805#ifdef HAVE_TFTP
806/* This nastily re-uses DHCP-fields for TFTP stuff */
807void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
808{
809 unsigned int filename_len;
810
811 /* no script */
812 if (daemon->helperfd == -1)
813 return;
814
815 filename_len = strlen(filename) + 1;
816 buff_alloc(sizeof(struct script_data) + filename_len);
817 memset(buf, 0, sizeof(struct script_data));
818
819 buf->action = ACTION_TFTP;
820 buf->hostname_len = filename_len;
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100821 buf->file_len = file_len;
Simon Kelleya9530962012-03-20 22:07:35 +0000822
823 if ((buf->flags = peer->sa.sa_family) == AF_INET)
824 buf->addr = peer->in.sin_addr;
Simon Kelleya9530962012-03-20 22:07:35 +0000825 else
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100826 buf->addr6 = peer->in6.sin6_addr;
Simon Kelleya9530962012-03-20 22:07:35 +0000827
828 memcpy((unsigned char *)(buf+1), filename, filename_len);
829
830 bytes_in_buf = sizeof(struct script_data) + filename_len;
831}
832#endif
833
Simon Kelley33702ab2015-12-28 23:17:15 +0000834void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr)
835{
836 /* no script */
837 if (daemon->helperfd == -1)
838 return;
839
840 buff_alloc(sizeof(struct script_data));
841 memset(buf, 0, sizeof(struct script_data));
842
843 buf->action = action;
844 buf->hwaddr_len = maclen;
845 buf->hwaddr_type = ARPHRD_ETHER;
846 if ((buf->flags = family) == AF_INET)
847 buf->addr = addr->addr.addr4;
Simon Kelley33702ab2015-12-28 23:17:15 +0000848 else
849 buf->addr6 = addr->addr.addr6;
Simon Kelley33702ab2015-12-28 23:17:15 +0000850
851 memcpy(buf->hwaddr, mac, maclen);
852
853 bytes_in_buf = sizeof(struct script_data);
854}
855
Simon Kelley16972692006-10-16 20:04:18 +0100856int helper_buf_empty(void)
857{
858 return bytes_in_buf == 0;
859}
860
Simon Kelley5aabfc72007-08-29 11:24:47 +0100861void helper_write(void)
Simon Kelley16972692006-10-16 20:04:18 +0100862{
863 ssize_t rc;
864
865 if (bytes_in_buf == 0)
866 return;
867
868 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
869 {
870 if (bytes_in_buf != (size_t)rc)
871 memmove(buf, buf + rc, bytes_in_buf - rc);
872 bytes_in_buf -= rc;
873 }
874 else
875 {
876 if (errno == EAGAIN || errno == EINTR)
877 return;
878 bytes_in_buf = 0;
879 }
880}
881
Simon Kelley5aabfc72007-08-29 11:24:47 +0100882#endif
Simon Kelley16972692006-10-16 20:04:18 +0100883
884
Simon Kelleyc72daea2012-01-05 21:33:27 +0000885