blob: 4be53c361ee19da598b090fbd1f85e0872e26c3f [file] [log] [blame]
Simon Kelleyc47e3ba2014-01-08 17:07:54 +00001/* dnsmasq is Copyright (c) 2000-2014 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 Kelley16972692006-10-16 20:04:18 +0100222 else
223 continue;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000224
Simon Kelleya9530962012-03-20 22:07:35 +0000225
Simon Kelley89500e32013-09-20 16:29:20 +0100226 /* stringify MAC into dhcp_buff */
227 p = daemon->dhcp_buff;
228 if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
229 p += sprintf(p, "%.2x-", data.hwaddr_type);
230 for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000231 {
Simon Kelley89500e32013-09-20 16:29:20 +0100232 p += sprintf(p, "%.2x", data.hwaddr[i]);
233 if (i != data.hwaddr_len - 1)
234 p += sprintf(p, ":");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000235 }
Simon Kelley89500e32013-09-20 16:29:20 +0100236
Simon Kelleyc72daea2012-01-05 21:33:27 +0000237 /* supplied data may just exceed normal buffer (unlikely) */
238 if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
239 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
Simon Kelley16972692006-10-16 20:04:18 +0100240 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000241
242 if (!read_write(pipefd[0], buf,
243 data.hostname_len + data.ed_len + data.clid_len, 1))
244 continue;
245
246 /* CLID into packet */
Simon Kelley89500e32013-09-20 16:29:20 +0100247 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
248 {
249 p += sprintf(p, "%.2x", buf[i]);
250 if (i != data.clid_len - 1)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000251 p += sprintf(p, ":");
Simon Kelley89500e32013-09-20 16:29:20 +0100252 }
253
Simon Kelleyceae00d2012-02-09 21:28:14 +0000254#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100255 if (is6)
Simon Kelley16972692006-10-16 20:04:18 +0100256 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000257 /* or IAID and server DUID for IPv6 */
Simon Kelley89500e32013-09-20 16:29:20 +0100258 sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.iaid);
259 for (p = daemon->dhcp_packet.iov_base, i = 0; i < daemon->duid_len; i++)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000260 {
261 p += sprintf(p, "%.2x", daemon->duid[i]);
262 if (i != daemon->duid_len - 1)
263 p += sprintf(p, ":");
264 }
Simon Kelleycaa94382012-02-15 10:29:50 +0000265
Simon Kelley16972692006-10-16 20:04:18 +0100266 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000267#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000268
269 buf += data.clid_len;
270
271 if (data.hostname_len != 0)
272 {
273 char *dot;
274 hostname = (char *)buf;
275 hostname[data.hostname_len - 1] = 0;
Simon Kelleya9530962012-03-20 22:07:35 +0000276 if (data.action != ACTION_TFTP)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000277 {
Simon Kelleya9530962012-03-20 22:07:35 +0000278 if (!legal_hostname(hostname))
279 hostname = NULL;
280 else if ((dot = strchr(hostname, '.')))
281 {
282 domain = dot+1;
283 *dot = 0;
284 }
285 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000286 }
287
288 extradata = buf + data.hostname_len;
289
Simon Kelleyceae00d2012-02-09 21:28:14 +0000290 if (!is6)
291 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
292#ifdef HAVE_DHCP6
293 else
Simon Kelley89500e32013-09-20 16:29:20 +0100294 inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000295#endif
296
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100297#ifdef HAVE_TFTP
Simon Kelleya9530962012-03-20 22:07:35 +0000298 /* file length */
299 if (data.action == ACTION_TFTP)
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100300 sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
301#endif
302
Simon Kelleyc72daea2012-01-05 21:33:27 +0000303#ifdef HAVE_LUASCRIPT
304 if (daemon->luascript)
305 {
Simon Kelleya9530962012-03-20 22:07:35 +0000306 if (data.action == ACTION_TFTP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000307 {
Simon Kelleya9530962012-03-20 22:07:35 +0000308 lua_getglobal(lua, "tftp");
309 if (lua_type(lua, -1) != LUA_TFUNCTION)
310 lua_pop(lua, 1); /* tftp function optional */
311 else
312 {
313 lua_pushstring(lua, action_str); /* arg1 - action */
314 lua_newtable(lua); /* arg2 - data table */
315 lua_pushstring(lua, daemon->addrbuff);
316 lua_setfield(lua, -2, "destination_address");
317 lua_pushstring(lua, hostname);
318 lua_setfield(lua, -2, "file_name");
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100319 lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
Simon Kelleya9530962012-03-20 22:07:35 +0000320 lua_setfield(lua, -2, "file_size");
321 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
322 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000323 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000324 else
Simon Kelleya9530962012-03-20 22:07:35 +0000325 {
326 lua_getglobal(lua, "lease"); /* function to call */
327 lua_pushstring(lua, action_str); /* arg1 - action */
328 lua_newtable(lua); /* arg2 - data table */
329
330 if (is6)
331 {
Simon Kelleya9530962012-03-20 22:07:35 +0000332 lua_pushstring(lua, daemon->packet);
Simon Kelley89500e32013-09-20 16:29:20 +0100333 lua_setfield(lua, -2, "client_duid");
334 lua_pushstring(lua, daemon->dhcp_packet.iov_base);
Simon Kelleya9530962012-03-20 22:07:35 +0000335 lua_setfield(lua, -2, "server_duid");
336 lua_pushstring(lua, daemon->dhcp_buff3);
337 lua_setfield(lua, -2, "iaid");
338 }
339
340 if (!is6 && data.clid_len != 0)
341 {
342 lua_pushstring(lua, daemon->packet);
343 lua_setfield(lua, -2, "client_id");
344 }
345
346 if (strlen(data.interface) != 0)
347 {
348 lua_pushstring(lua, data.interface);
349 lua_setfield(lua, -2, "interface");
350 }
351
352#ifdef HAVE_BROKEN_RTC
353 lua_pushnumber(lua, data.length);
354 lua_setfield(lua, -2, "lease_length");
355#else
356 lua_pushnumber(lua, data.expires);
357 lua_setfield(lua, -2, "lease_expires");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000358#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000359
360 if (hostname)
361 {
362 lua_pushstring(lua, hostname);
363 lua_setfield(lua, -2, "hostname");
364 }
365
366 if (domain)
367 {
368 lua_pushstring(lua, domain);
369 lua_setfield(lua, -2, "domain");
370 }
371
372 end = extradata + data.ed_len;
373 buf = extradata;
374
375 if (!is6)
376 buf = grab_extradata_lua(buf, end, "vendor_class");
377#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100378 else if (data.vendorclass_count != 0)
379 {
380 sprintf(daemon->dhcp_buff2, "vendor_class_id");
381 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
382 for (i = 0; i < data.vendorclass_count - 1; i++)
383 {
384 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
385 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
386 }
387 }
Simon Kelleya9530962012-03-20 22:07:35 +0000388#endif
389
390 buf = grab_extradata_lua(buf, end, "supplied_hostname");
391
392 if (!is6)
393 {
394 buf = grab_extradata_lua(buf, end, "cpewan_oui");
395 buf = grab_extradata_lua(buf, end, "cpewan_serial");
396 buf = grab_extradata_lua(buf, end, "cpewan_class");
Simon Kelleydd1721c2013-02-18 21:04:04 +0000397 buf = grab_extradata_lua(buf, end, "circuit_id");
398 buf = grab_extradata_lua(buf, end, "subscriber_id");
399 buf = grab_extradata_lua(buf, end, "remote_id");
Simon Kelleya9530962012-03-20 22:07:35 +0000400 }
401
402 buf = grab_extradata_lua(buf, end, "tags");
403
404 if (is6)
405 buf = grab_extradata_lua(buf, end, "relay_address");
406 else if (data.giaddr.s_addr != 0)
407 {
408 lua_pushstring(lua, inet_ntoa(data.giaddr));
409 lua_setfield(lua, -2, "relay_address");
410 }
411
412 for (i = 0; buf; i++)
413 {
414 sprintf(daemon->dhcp_buff2, "user_class%i", i);
415 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
416 }
417
418 if (data.action != ACTION_DEL && data.remaining_time != 0)
419 {
420 lua_pushnumber(lua, data.remaining_time);
421 lua_setfield(lua, -2, "time_remaining");
422 }
423
424 if (data.action == ACTION_OLD_HOSTNAME && hostname)
425 {
426 lua_pushstring(lua, hostname);
427 lua_setfield(lua, -2, "old_hostname");
428 }
429
Simon Kelley89500e32013-09-20 16:29:20 +0100430 if (!is6 || data.hwaddr_len != 0)
Simon Kelleya9530962012-03-20 22:07:35 +0000431 {
432 lua_pushstring(lua, daemon->dhcp_buff);
433 lua_setfield(lua, -2, "mac_address");
434 }
435
436 lua_pushstring(lua, daemon->addrbuff);
437 lua_setfield(lua, -2, "ip_address");
438
439 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000440 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000441 }
442#endif
443
444 /* no script, just lua */
445 if (!daemon->lease_change_command)
Simon Kelley316e2732010-01-22 20:16:09 +0000446 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000447
Simon Kelley5aabfc72007-08-29 11:24:47 +0100448 /* possible fork errors are all temporary resource problems */
449 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
450 sleep(2);
Simon Kelley316e2732010-01-22 20:16:09 +0000451
Simon Kelley5aabfc72007-08-29 11:24:47 +0100452 if (pid == -1)
453 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000454
Simon Kelley16972692006-10-16 20:04:18 +0100455 /* wait for child to complete */
456 if (pid != 0)
457 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100458 /* reap our children's children, if necessary */
459 while (1)
460 {
461 int status;
462 pid_t rc = wait(&status);
463
464 if (rc == pid)
465 {
466 /* On error send event back to main process for logging */
467 if (WIFSIGNALED(status))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000468 send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100469 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000470 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100471 break;
472 }
473
474 if (rc == -1 && errno != EINTR)
475 break;
476 }
477
Simon Kelley16972692006-10-16 20:04:18 +0100478 continue;
479 }
480
Simon Kelleya9530962012-03-20 22:07:35 +0000481 if (data.action != ACTION_TFTP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000482 {
Simon Kelley89500e32013-09-20 16:29:20 +0100483#ifdef HAVE_DHCP6
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100484 my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
485 my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err);
486 my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100487#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000488
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100489 my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
490 my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000491
492#ifdef HAVE_BROKEN_RTC
Simon Kelley208fb612013-02-21 22:26:18 +0000493 sprintf(daemon->dhcp_buff2, "%u", data.length);
Simon Kelleya9530962012-03-20 22:07:35 +0000494 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
495#else
Simon Kelley208fb612013-02-21 22:26:18 +0000496 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
Simon Kelleya9530962012-03-20 22:07:35 +0000497 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
498#endif
499
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100500 my_setenv("DNSMASQ_DOMAIN", domain, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000501
502 end = extradata + data.ed_len;
503 buf = extradata;
504
505 if (!is6)
506 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
507#ifdef HAVE_DHCP6
508 else
509 {
Simon Kelley89500e32013-09-20 16:29:20 +0100510 if (data.vendorclass_count != 0)
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000511 {
Simon Kelleya9530962012-03-20 22:07:35 +0000512 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
Simon Kelley89500e32013-09-20 16:29:20 +0100513 for (i = 0; i < data.vendorclass_count - 1; i++)
Simon Kelleya9530962012-03-20 22:07:35 +0000514 {
515 sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
516 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
517 }
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000518 }
519 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000520#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000521
522 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
523
524 if (!is6)
525 {
526 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
527 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
528 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000529 buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
530 buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
531 buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000532 }
533
534 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
Simon Kelleydd1721c2013-02-18 21:04:04 +0000535
Simon Kelleya9530962012-03-20 22:07:35 +0000536 if (is6)
537 buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100538 else
539 my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err);
Simon Kelleya9530962012-03-20 22:07:35 +0000540
541 for (i = 0; buf; i++)
542 {
543 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
544 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
545 }
546
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100547 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
548 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 +0000549
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100550 my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
551 if (data.action == ACTION_OLD_HOSTNAME)
552 hostname = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100553 }
554
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100555 my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
556
Simon Kelley5aabfc72007-08-29 11:24:47 +0100557 /* we need to have the event_fd around if exec fails */
558 if ((i = fcntl(event_fd, F_GETFD)) != -1)
559 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
560 close(pipefd[0]);
561
Simon Kelley16972692006-10-16 20:04:18 +0100562 p = strrchr(daemon->lease_change_command, '/');
Simon Kelley824af852008-02-12 20:43:05 +0000563 if (err == 0)
564 {
565 execl(daemon->lease_change_command,
566 p ? p+1 : daemon->lease_change_command,
Simon Kelley89500e32013-09-20 16:29:20 +0100567 action_str, is6 ? daemon->packet : daemon->dhcp_buff,
568 daemon->addrbuff, hostname, (char*)NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000569 err = errno;
570 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100571 /* failed, send event so the main process logs the problem */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000572 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100573 _exit(0);
574 }
575}
576
Simon Kelley824af852008-02-12 20:43:05 +0000577static void my_setenv(const char *name, const char *value, int *error)
578{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100579 if (*error == 0)
580 {
581 if (!value)
582 unsetenv(name);
583 else if (setenv(name, value, 1) != 0)
584 *error = errno;
585 }
Simon Kelley824af852008-02-12 20:43:05 +0000586}
587
Simon Kelley316e2732010-01-22 20:16:09 +0000588static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
589{
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100590 unsigned char *next = NULL;
591 char *val = NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000592
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100593 if (buf && (buf != end))
Simon Kelley316e2732010-01-22 20:16:09 +0000594 {
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100595 for (next = buf; ; next++)
596 if (next == end)
597 {
598 next = NULL;
599 break;
600 }
601 else if (*next == 0)
602 break;
Simon Kelley316e2732010-01-22 20:16:09 +0000603
Simon Kelleyd4da20f2013-10-04 10:12:49 +0100604 if (next && (next != buf))
605 {
606 char *p;
607 /* No "=" in value */
608 if ((p = strchr((char *)buf, '=')))
609 *p = 0;
610 val = (char *)buf;
611 }
612 }
613
614 my_setenv(env, val, err);
615
616 return next ? next + 1 : NULL;
Simon Kelley316e2732010-01-22 20:16:09 +0000617}
618
Simon Kelleyc72daea2012-01-05 21:33:27 +0000619#ifdef HAVE_LUASCRIPT
620static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
621{
622 unsigned char *next;
623
624 if (!buf || (buf == end))
625 return NULL;
626
627 for (next = buf; *next != 0; next++)
628 if (next == end)
629 return NULL;
630
631 if (next != buf)
632 {
633 lua_pushstring(lua, (char *)buf);
634 lua_setfield(lua, -2, field);
635 }
636
637 return next + 1;
638}
639#endif
640
Simon Kelleya9530962012-03-20 22:07:35 +0000641static void buff_alloc(size_t size)
642{
643 if (size > buf_size)
644 {
645 struct script_data *new;
646
647 /* start with reasonable size, will almost never need extending. */
648 if (size < sizeof(struct script_data) + 200)
649 size = sizeof(struct script_data) + 200;
650
651 if (!(new = whine_malloc(size)))
652 return;
653 if (buf)
654 free(buf);
655 buf = new;
656 buf_size = size;
657 }
658}
659
Simon Kelley16972692006-10-16 20:04:18 +0100660/* pack up lease data into a buffer */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100661void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
Simon Kelley16972692006-10-16 20:04:18 +0100662{
663 unsigned char *p;
Simon Kelley316e2732010-01-22 20:16:09 +0000664 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000665 int fd = daemon->dhcpfd;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100666#ifdef HAVE_DHCP6
Simon Kelleyceae00d2012-02-09 21:28:14 +0000667 if (!daemon->dhcp)
668 fd = daemon->dhcp6fd;
669#endif
670
Simon Kelley16972692006-10-16 20:04:18 +0100671 /* no script */
672 if (daemon->helperfd == -1)
673 return;
674
Simon Kelley316e2732010-01-22 20:16:09 +0000675 if (lease->extradata)
676 ed_len = lease->extradata_len;
Simon Kelley16972692006-10-16 20:04:18 +0100677 if (lease->clid)
678 clid_len = lease->clid_len;
679 if (hostname)
680 hostname_len = strlen(hostname) + 1;
681
Simon Kelleya9530962012-03-20 22:07:35 +0000682 buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
Simon Kelley16972692006-10-16 20:04:18 +0100683
684 buf->action = action;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000685 buf->flags = lease->flags;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100686#ifdef HAVE_DHCP6
Simon Kelley89500e32013-09-20 16:29:20 +0100687 buf->vendorclass_count = lease->vendorclass_count;
688 buf->addr6 = lease->addr6;
689 buf->iaid = lease->iaid;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100690#endif
Simon Kelley89500e32013-09-20 16:29:20 +0100691 buf->hwaddr_len = lease->hwaddr_len;
Simon Kelley16972692006-10-16 20:04:18 +0100692 buf->hwaddr_type = lease->hwaddr_type;
693 buf->clid_len = clid_len;
Simon Kelley316e2732010-01-22 20:16:09 +0000694 buf->ed_len = ed_len;
Simon Kelley16972692006-10-16 20:04:18 +0100695 buf->hostname_len = hostname_len;
696 buf->addr = lease->addr;
Simon Kelley1f15b812009-10-13 17:49:32 +0100697 buf->giaddr = lease->giaddr;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000698 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
699 if (!indextoname(fd, lease->last_interface, buf->interface))
Simon Kelley316e2732010-01-22 20:16:09 +0000700 buf->interface[0] = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000701
Simon Kelley16972692006-10-16 20:04:18 +0100702#ifdef HAVE_BROKEN_RTC
703 buf->length = lease->length;
704#else
705 buf->expires = lease->expires;
706#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000707
708 if (lease->expires != 0)
709 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
710 else
711 buf->remaining_time = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100712
Simon Kelley16972692006-10-16 20:04:18 +0100713 p = (unsigned char *)(buf+1);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100714 if (clid_len != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100715 {
716 memcpy(p, lease->clid, clid_len);
717 p += clid_len;
718 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100719 if (hostname_len != 0)
720 {
721 memcpy(p, hostname, hostname_len);
722 p += hostname_len;
723 }
Simon Kelley316e2732010-01-22 20:16:09 +0000724 if (ed_len != 0)
725 {
726 memcpy(p, lease->extradata, ed_len);
727 p += ed_len;
728 }
Simon Kelley16972692006-10-16 20:04:18 +0100729 bytes_in_buf = p - (unsigned char *)buf;
730}
731
Simon Kelleya9530962012-03-20 22:07:35 +0000732#ifdef HAVE_TFTP
733/* This nastily re-uses DHCP-fields for TFTP stuff */
734void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
735{
736 unsigned int filename_len;
737
738 /* no script */
739 if (daemon->helperfd == -1)
740 return;
741
742 filename_len = strlen(filename) + 1;
743 buff_alloc(sizeof(struct script_data) + filename_len);
744 memset(buf, 0, sizeof(struct script_data));
745
746 buf->action = ACTION_TFTP;
747 buf->hostname_len = filename_len;
Simon Kelley2f9fd1d2013-10-01 09:54:41 +0100748 buf->file_len = file_len;
Simon Kelleya9530962012-03-20 22:07:35 +0000749
750 if ((buf->flags = peer->sa.sa_family) == AF_INET)
751 buf->addr = peer->in.sin_addr;
752#ifdef HAVE_IPV6
753 else
Simon Kelleyb5d9a362013-09-24 09:44:33 +0100754 buf->addr6 = peer->in6.sin6_addr;
Simon Kelleya9530962012-03-20 22:07:35 +0000755#endif
756
757 memcpy((unsigned char *)(buf+1), filename, filename_len);
758
759 bytes_in_buf = sizeof(struct script_data) + filename_len;
760}
761#endif
762
Simon Kelley16972692006-10-16 20:04:18 +0100763int helper_buf_empty(void)
764{
765 return bytes_in_buf == 0;
766}
767
Simon Kelley5aabfc72007-08-29 11:24:47 +0100768void helper_write(void)
Simon Kelley16972692006-10-16 20:04:18 +0100769{
770 ssize_t rc;
771
772 if (bytes_in_buf == 0)
773 return;
774
775 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
776 {
777 if (bytes_in_buf != (size_t)rc)
778 memmove(buf, buf + rc, bytes_in_buf - rc);
779 bytes_in_buf -= rc;
780 }
781 else
782 {
783 if (errno == EAGAIN || errno == EINTR)
784 return;
785 bytes_in_buf = 0;
786 }
787}
788
Simon Kelley5aabfc72007-08-29 11:24:47 +0100789#endif
Simon Kelley16972692006-10-16 20:04:18 +0100790
791
Simon Kelleyc72daea2012-01-05 21:33:27 +0000792