blob: fd7d949e1e77068b83526ca374ad100af54379c1 [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 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
37#include <lua.h>
38#include <lualib.h>
39#include <lauxlib.h>
40
41lua_State *lua;
42
43static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
44#endif
45
46
Simon Kelley16972692006-10-16 20:04:18 +010047struct script_data
48{
Simon Kelleyceae00d2012-02-09 21:28:14 +000049 int flags;
50 int action, hwaddr_len, hwaddr_type;
51 int clid_len, hostname_len, ed_len;
Simon Kelley1f15b812009-10-13 17:49:32 +010052 struct in_addr addr, giaddr;
Simon Kelley5aabfc72007-08-29 11:24:47 +010053 unsigned int remaining_time;
Simon Kelley16972692006-10-16 20:04:18 +010054#ifdef HAVE_BROKEN_RTC
55 unsigned int length;
56#else
57 time_t expires;
58#endif
59 unsigned char hwaddr[DHCP_CHADDR_MAX];
Simon Kelley824af852008-02-12 20:43:05 +000060 char interface[IF_NAMESIZE];
Simon Kelleyceae00d2012-02-09 21:28:14 +000061
Simon Kelley16972692006-10-16 20:04:18 +010062};
63
Simon Kelley5aabfc72007-08-29 11:24:47 +010064static struct script_data *buf = NULL;
65static size_t bytes_in_buf = 0, buf_size = 0;
Simon Kelley16972692006-10-16 20:04:18 +010066
Simon Kelley1a6bca82008-07-11 11:11:42 +010067int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
Simon Kelley16972692006-10-16 20:04:18 +010068{
69 pid_t pid;
70 int i, pipefd[2];
71 struct sigaction sigact;
Simon Kelley9e038942008-05-30 20:06:34 +010072
Simon Kelley5aabfc72007-08-29 11:24:47 +010073 /* create the pipe through which the main program sends us commands,
Simon Kelley1a6bca82008-07-11 11:11:42 +010074 then fork our process. */
Simon Kelley5aabfc72007-08-29 11:24:47 +010075 if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
76 {
Simon Kelleyc72daea2012-01-05 21:33:27 +000077 send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +010078 _exit(0);
Simon Kelley5aabfc72007-08-29 11:24:47 +010079 }
80
Simon Kelley16972692006-10-16 20:04:18 +010081 if (pid != 0)
82 {
83 close(pipefd[0]); /* close reader side */
84 return pipefd[1];
85 }
86
Simon Kelley5aabfc72007-08-29 11:24:47 +010087 /* ignore SIGTERM, so that we can clean up when the main process gets hit
88 and SIGALRM so that we can use sleep() */
Simon Kelley16972692006-10-16 20:04:18 +010089 sigact.sa_handler = SIG_IGN;
90 sigact.sa_flags = 0;
91 sigemptyset(&sigact.sa_mask);
92 sigaction(SIGTERM, &sigact, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +010093 sigaction(SIGALRM, &sigact, NULL);
Simon Kelley16972692006-10-16 20:04:18 +010094
Simon Kelley28866e92011-02-14 20:19:14 +000095 if (!option_bool(OPT_DEBUG) && uid != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +010096 {
97 gid_t dummy;
98 if (setgroups(0, &dummy) == -1 ||
99 setgid(gid) == -1 ||
100 setuid(uid) == -1)
101 {
Simon Kelley28866e92011-02-14 20:19:14 +0000102 if (option_bool(OPT_NO_FORK))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100103 /* send error to daemon process if no-fork */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000104 send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100105 else
106 {
107 /* kill daemon */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000108 send_event(event_fd, EVENT_DIE, 0, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100109 /* return error */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000110 send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100111 }
112 _exit(0);
113 }
114 }
115
Simon Kelleyc72daea2012-01-05 21:33:27 +0000116 /* close all the sockets etc, we don't need them here.
117 Don't close err_fd, in case the lua-init fails.
118 Note that we have to do this before lua init
119 so we don't close any lua fds. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100120 for (max_fd--; max_fd >= 0; max_fd--)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100121 if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
Simon Kelleyc72daea2012-01-05 21:33:27 +0000122 max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
123 max_fd != event_fd && max_fd != err_fd)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100124 close(max_fd);
Simon Kelley16972692006-10-16 20:04:18 +0100125
Simon Kelleyc72daea2012-01-05 21:33:27 +0000126#ifdef HAVE_LUASCRIPT
127 if (daemon->luascript)
128 {
129 const char *lua_err = NULL;
130 lua = lua_open();
131 luaL_openlibs(lua);
132
133 /* get Lua to load our script file */
134 if (luaL_dofile(lua, daemon->luascript) != 0)
135 lua_err = lua_tostring(lua, -1);
136 else
137 {
138 lua_getglobal(lua, "lease");
139 if (lua_type(lua, -1) != LUA_TFUNCTION)
140 lua_err = _("lease() function missing in Lua script");
141 }
142
143 if (lua_err)
144 {
145 if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
146 /* send error to daemon process if no-fork */
147 send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
148 else
149 {
150 /* kill daemon */
151 send_event(event_fd, EVENT_DIE, 0, NULL);
152 /* return error */
153 send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
154 }
155 _exit(0);
156 }
157
158 lua_pop(lua, 1); /* remove nil from stack */
159 lua_getglobal(lua, "init");
160 if (lua_type(lua, -1) == LUA_TFUNCTION)
161 lua_call(lua, 0, 0);
162 else
163 lua_pop(lua, 1); /* remove nil from stack */
164 }
165#endif
166
167 /* All init done, close our copy of the error pipe, so that main process can return */
168 if (err_fd != -1)
169 close(err_fd);
170
Simon Kelley16972692006-10-16 20:04:18 +0100171 /* loop here */
172 while(1)
173 {
174 struct script_data data;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000175 char *p, *action_str, *hostname = NULL, *domain = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100176 unsigned char *buf = (unsigned char *)daemon->namebuff;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000177 unsigned char *end, *extradata, *alloc_buff = NULL;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000178 int is6, err = 0;
Simon Kelley16972692006-10-16 20:04:18 +0100179
Simon Kelleyc72daea2012-01-05 21:33:27 +0000180 free(alloc_buff);
181
Simon Kelley16972692006-10-16 20:04:18 +0100182 /* we read zero bytes when pipe closed: this is our signal to exit */
183 if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000184 {
185#ifdef HAVE_LUASCRIPT
186 if (daemon->luascript)
187 {
188 lua_getglobal(lua, "shutdown");
189 if (lua_type(lua, -1) == LUA_TFUNCTION)
190 lua_call(lua, 0, 0);
191 }
192#endif
193 _exit(0);
194 }
195
Simon Kelley16972692006-10-16 20:04:18 +0100196 if (data.action == ACTION_DEL)
197 action_str = "del";
198 else if (data.action == ACTION_ADD)
199 action_str = "add";
200 else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
201 action_str = "old";
202 else
203 continue;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000204
205 is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
Simon Kelley16972692006-10-16 20:04:18 +0100206
Simon Kelleyceae00d2012-02-09 21:28:14 +0000207 if (!is6)
208 {
209 /* stringify MAC into dhcp_buff */
210 p = daemon->dhcp_buff;
211 if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
212 p += sprintf(p, "%.2x-", data.hwaddr_type);
213 for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
214 {
215 p += sprintf(p, "%.2x", data.hwaddr[i]);
216 if (i != data.hwaddr_len - 1)
217 p += sprintf(p, ":");
218 }
219 }
220#ifdef HAVE_DHCP6
221 else
222 {
223 /* duid not MAC for IPv6 */
224 for (p = daemon->dhcp_buff, i = 0; i < data.clid_len; i++)
225 {
226 p += sprintf(p, "%.2x", buf[i]);
227 if (i != data.clid_len - 1)
228 p += sprintf(p, ":");
229 }
230 }
231#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000232
233 /* expiry or length into dhcp_buff2 */
234#ifdef HAVE_BROKEN_RTC
235 sprintf(daemon->dhcp_buff2, "%u", data.length);
236#else
237 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
238#endif
239
240 /* supplied data may just exceed normal buffer (unlikely) */
241 if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
242 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
Simon Kelley16972692006-10-16 20:04:18 +0100243 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000244
245 if (!read_write(pipefd[0], buf,
246 data.hostname_len + data.ed_len + data.clid_len, 1))
247 continue;
248
249 /* CLID into packet */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000250 if (!is6)
251 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
252 {
253 p += sprintf(p, "%.2x", buf[i]);
254 if (i != data.clid_len - 1)
255 p += sprintf(p, ":");
256 }
257#ifdef HAVE_DHCP6
258 else
Simon Kelley16972692006-10-16 20:04:18 +0100259 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000260 /* or IAID and server DUID for IPv6 */
261 sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.hwaddr_type);
262 for (p = daemon->packet, i = 0; i < daemon->duid_len; i++)
263 {
264 p += sprintf(p, "%.2x", daemon->duid[i]);
265 if (i != daemon->duid_len - 1)
266 p += sprintf(p, ":");
267 }
Simon Kelley16972692006-10-16 20:04:18 +0100268 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000269#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000270
271 buf += data.clid_len;
272
273 if (data.hostname_len != 0)
274 {
275 char *dot;
276 hostname = (char *)buf;
277 hostname[data.hostname_len - 1] = 0;
278 if (!legal_hostname(hostname))
279 hostname = NULL;
280 else if ((dot = strchr(hostname, '.')))
281 {
282 domain = dot+1;
283 *dot = 0;
284 }
285 }
286
287 extradata = buf + data.hostname_len;
288
Simon Kelleyceae00d2012-02-09 21:28:14 +0000289 if (!is6)
290 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
291#ifdef HAVE_DHCP6
292 else
293 inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
294#endif
295
Simon Kelleyc72daea2012-01-05 21:33:27 +0000296#ifdef HAVE_LUASCRIPT
297 if (daemon->luascript)
298 {
299 lua_getglobal(lua, "lease"); /* function to call */
300 lua_pushstring(lua, action_str); /* arg1 - action */
301 lua_newtable(lua); /* arg2 - data table */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000302
303 if (is6)
304 {
305 lua_pushstring(lua, daemon->packet);
306 lua_setfield(lua, -2, "duid");
307 lua_pushstring(lua, daemon->dhcp_buff3);
308 lua_setfield(lua, -2, "iaid");
309 }
310
311 if (!is6 && data.clid_len != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000312 {
313 lua_pushstring(lua, daemon->packet);
314 lua_setfield(lua, -2, "client_id");
315 }
316
317 if (strlen(data.interface) != 0)
318 {
319 lua_pushstring(lua, data.interface);
320 lua_setfield(lua, -2, "interface");
321 }
322
323#ifdef HAVE_BROKEN_RTC
324 lua_pushnumber(lua, data.length);
325 lua_setfield(lua, -2, "lease_length");
Simon Kelley16972692006-10-16 20:04:18 +0100326#else
Simon Kelleyc72daea2012-01-05 21:33:27 +0000327 lua_pushnumber(lua, data.expires);
328 lua_setfield(lua, -2, "lease_expires");
Simon Kelley16972692006-10-16 20:04:18 +0100329#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000330
331 if (hostname)
332 {
333 lua_pushstring(lua, hostname);
334 lua_setfield(lua, -2, "hostname");
335 }
336
337 if (domain)
338 {
339 lua_pushstring(lua, domain);
340 lua_setfield(lua, -2, "domain");
341 }
342
343 end = extradata + data.ed_len;
344 buf = extradata;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000345
346 if (!is6)
347 buf = grab_extradata_lua(buf, end, "vendor_class");
348#ifdef HAVE_DHCP6
349 else
350 for (i = 0; i < data.hwaddr_len; i++)
351 {
352 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
353 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
354 }
355#endif
356
Simon Kelleyc72daea2012-01-05 21:33:27 +0000357 buf = grab_extradata_lua(buf, end, "supplied_hostname");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000358
359 if (!is6)
360 {
361 buf = grab_extradata_lua(buf, end, "cpewan_oui");
362 buf = grab_extradata_lua(buf, end, "cpewan_serial");
363 buf = grab_extradata_lua(buf, end, "cpewan_class");
364 }
365
Simon Kelleyc72daea2012-01-05 21:33:27 +0000366 buf = grab_extradata_lua(buf, end, "tags");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000367
Simon Kelleyc72daea2012-01-05 21:33:27 +0000368 for (i = 0; buf; i++)
369 {
370 sprintf(daemon->dhcp_buff2, "user_class%i", i);
371 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
372 }
373
Simon Kelleyceae00d2012-02-09 21:28:14 +0000374 if (!is6 && data.giaddr.s_addr != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000375 {
376 lua_pushstring(lua, inet_ntoa(data.giaddr));
377 lua_setfield(lua, -2, "relay_address");
378 }
379
380 if (data.action != ACTION_DEL && data.remaining_time != 0)
381 {
382 lua_pushnumber(lua, data.remaining_time);
383 lua_setfield(lua, -2, "time_remaining");
384 }
385
386 if (data.action == ACTION_OLD_HOSTNAME && hostname)
387 {
388 lua_pushstring(lua, hostname);
389 lua_setfield(lua, -2, "old_hostname");
390 }
391
Simon Kelleyceae00d2012-02-09 21:28:14 +0000392 if (!is6)
393 {
394 lua_pushstring(lua, daemon->dhcp_buff);
395 lua_setfield(lua, -2, "mac_address");
396 }
397
398 lua_pushstring(lua, daemon->addrbuff);
Simon Kelleyc72daea2012-01-05 21:33:27 +0000399 lua_setfield(lua, -2, "ip_address");
400
401 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
402 }
403#endif
404
405 /* no script, just lua */
406 if (!daemon->lease_change_command)
Simon Kelley316e2732010-01-22 20:16:09 +0000407 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000408
Simon Kelley5aabfc72007-08-29 11:24:47 +0100409 /* possible fork errors are all temporary resource problems */
410 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
411 sleep(2);
Simon Kelley316e2732010-01-22 20:16:09 +0000412
Simon Kelley5aabfc72007-08-29 11:24:47 +0100413 if (pid == -1)
414 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000415
Simon Kelley16972692006-10-16 20:04:18 +0100416 /* wait for child to complete */
417 if (pid != 0)
418 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100419 /* reap our children's children, if necessary */
420 while (1)
421 {
422 int status;
423 pid_t rc = wait(&status);
424
425 if (rc == pid)
426 {
427 /* On error send event back to main process for logging */
428 if (WIFSIGNALED(status))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000429 send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100430 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000431 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100432 break;
433 }
434
435 if (rc == -1 && errno != EINTR)
436 break;
437 }
438
Simon Kelley16972692006-10-16 20:04:18 +0100439 continue;
440 }
441
Simon Kelleyceae00d2012-02-09 21:28:14 +0000442 if (is6)
443 {
444 my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
445 my_setenv("DNSMASQ_DUID", daemon->packet, &err);
446 }
447
448 if (!is6 && data.clid_len != 0)
Simon Kelley824af852008-02-12 20:43:05 +0000449 my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
450
451 if (strlen(data.interface) != 0)
452 my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
453
Simon Kelley16972692006-10-16 20:04:18 +0100454#ifdef HAVE_BROKEN_RTC
Simon Kelley824af852008-02-12 20:43:05 +0000455 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
Simon Kelley16972692006-10-16 20:04:18 +0100456#else
Simon Kelley824af852008-02-12 20:43:05 +0000457 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
Simon Kelley16972692006-10-16 20:04:18 +0100458#endif
459
Simon Kelleyc72daea2012-01-05 21:33:27 +0000460 if (domain)
461 my_setenv("DNSMASQ_DOMAIN", domain, &err);
462
463 end = extradata + data.ed_len;
464 buf = extradata;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000465
466 if (!is6)
467 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
468#ifdef HAVE_DHCP6
469 else
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000470 {
471 if (data.hwaddr_len != 0)
472 {
473 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
474 for (i = 0; i < data.hwaddr_len - 1; i++)
475 {
476 sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
477 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
478 }
479 }
480 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000481#endif
482
Simon Kelley316e2732010-01-22 20:16:09 +0000483 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000484
485 if (!is6)
486 {
487 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
488 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
489 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
490 }
491
Simon Kelley316e2732010-01-22 20:16:09 +0000492 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
Simon Kelley07933802012-02-14 20:55:25 +0000493
494 if (is6)
495 buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
Simon Kelley316e2732010-01-22 20:16:09 +0000496
497 for (i = 0; buf; i++)
498 {
499 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
500 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
501 }
502
Simon Kelleyceae00d2012-02-09 21:28:14 +0000503 if (!is6 && data.giaddr.s_addr != 0)
Simon Kelley316e2732010-01-22 20:16:09 +0000504 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
505
Simon Kelleyc72daea2012-01-05 21:33:27 +0000506 if (data.action != ACTION_DEL && data.remaining_time != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100507 {
508 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
509 my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
510 }
511
Simon Kelley16972692006-10-16 20:04:18 +0100512 if (data.action == ACTION_OLD_HOSTNAME && hostname)
513 {
Simon Kelley824af852008-02-12 20:43:05 +0000514 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
Simon Kelley16972692006-10-16 20:04:18 +0100515 hostname = NULL;
516 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100517
518 /* we need to have the event_fd around if exec fails */
519 if ((i = fcntl(event_fd, F_GETFD)) != -1)
520 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
521 close(pipefd[0]);
522
Simon Kelley16972692006-10-16 20:04:18 +0100523 p = strrchr(daemon->lease_change_command, '/');
Simon Kelley824af852008-02-12 20:43:05 +0000524 if (err == 0)
525 {
526 execl(daemon->lease_change_command,
527 p ? p+1 : daemon->lease_change_command,
Simon Kelleyceae00d2012-02-09 21:28:14 +0000528 action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000529 err = errno;
530 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100531 /* failed, send event so the main process logs the problem */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000532 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100533 _exit(0);
534 }
535}
536
Simon Kelley824af852008-02-12 20:43:05 +0000537static void my_setenv(const char *name, const char *value, int *error)
538{
Simon Kelley7622fc02009-06-04 20:32:05 +0100539 if (*error == 0 && setenv(name, value, 1) != 0)
540 *error = errno;
Simon Kelley824af852008-02-12 20:43:05 +0000541}
542
Simon Kelley316e2732010-01-22 20:16:09 +0000543static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
544{
545 unsigned char *next;
546
547 if (!buf || (buf == end))
548 return NULL;
549
550 for (next = buf; *next != 0; next++)
551 if (next == end)
552 return NULL;
553
554 if (next != buf)
555 {
556 char *p;
557 /* No "=" in value */
558 if ((p = strchr((char *)buf, '=')))
559 *p = 0;
560 my_setenv(env, (char *)buf, err);
561 }
562
563 return next + 1;
564}
565
Simon Kelleyc72daea2012-01-05 21:33:27 +0000566#ifdef HAVE_LUASCRIPT
567static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
568{
569 unsigned char *next;
570
571 if (!buf || (buf == end))
572 return NULL;
573
574 for (next = buf; *next != 0; next++)
575 if (next == end)
576 return NULL;
577
578 if (next != buf)
579 {
580 lua_pushstring(lua, (char *)buf);
581 lua_setfield(lua, -2, field);
582 }
583
584 return next + 1;
585}
586#endif
587
Simon Kelley16972692006-10-16 20:04:18 +0100588/* pack up lease data into a buffer */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100589void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
Simon Kelley16972692006-10-16 20:04:18 +0100590{
591 unsigned char *p;
592 size_t size;
Simon Kelley316e2732010-01-22 20:16:09 +0000593 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000594 int fd = daemon->dhcpfd;
595
596#ifdef HAVE_DHCP6
597 if (!daemon->dhcp)
598 fd = daemon->dhcp6fd;
599#endif
600
Simon Kelley16972692006-10-16 20:04:18 +0100601 /* no script */
602 if (daemon->helperfd == -1)
603 return;
604
Simon Kelley316e2732010-01-22 20:16:09 +0000605 if (lease->extradata)
606 ed_len = lease->extradata_len;
Simon Kelley16972692006-10-16 20:04:18 +0100607 if (lease->clid)
608 clid_len = lease->clid_len;
609 if (hostname)
610 hostname_len = strlen(hostname) + 1;
611
Simon Kelley316e2732010-01-22 20:16:09 +0000612 size = sizeof(struct script_data) + clid_len + ed_len + hostname_len;
Simon Kelley16972692006-10-16 20:04:18 +0100613
614 if (size > buf_size)
615 {
616 struct script_data *new;
617
Simon Kelley9009d742008-11-14 20:04:27 +0000618 /* start with reasonable size, will almost never need extending. */
Simon Kelley16972692006-10-16 20:04:18 +0100619 if (size < sizeof(struct script_data) + 200)
620 size = sizeof(struct script_data) + 200;
621
Simon Kelley5aabfc72007-08-29 11:24:47 +0100622 if (!(new = whine_malloc(size)))
Simon Kelley16972692006-10-16 20:04:18 +0100623 return;
624 if (buf)
625 free(buf);
626 buf = new;
627 buf_size = size;
628 }
629
630 buf->action = action;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000631 buf->flags = lease->flags;
Simon Kelley16972692006-10-16 20:04:18 +0100632 buf->hwaddr_len = lease->hwaddr_len;
633 buf->hwaddr_type = lease->hwaddr_type;
634 buf->clid_len = clid_len;
Simon Kelley316e2732010-01-22 20:16:09 +0000635 buf->ed_len = ed_len;
Simon Kelley16972692006-10-16 20:04:18 +0100636 buf->hostname_len = hostname_len;
637 buf->addr = lease->addr;
Simon Kelley1f15b812009-10-13 17:49:32 +0100638 buf->giaddr = lease->giaddr;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000639 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
640 if (!indextoname(fd, lease->last_interface, buf->interface))
Simon Kelley316e2732010-01-22 20:16:09 +0000641 buf->interface[0] = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000642
Simon Kelley16972692006-10-16 20:04:18 +0100643#ifdef HAVE_BROKEN_RTC
644 buf->length = lease->length;
645#else
646 buf->expires = lease->expires;
647#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000648
649 if (lease->expires != 0)
650 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
651 else
652 buf->remaining_time = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100653
Simon Kelley16972692006-10-16 20:04:18 +0100654 p = (unsigned char *)(buf+1);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100655 if (clid_len != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100656 {
657 memcpy(p, lease->clid, clid_len);
658 p += clid_len;
659 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100660 if (hostname_len != 0)
661 {
662 memcpy(p, hostname, hostname_len);
663 p += hostname_len;
664 }
Simon Kelley316e2732010-01-22 20:16:09 +0000665 if (ed_len != 0)
666 {
667 memcpy(p, lease->extradata, ed_len);
668 p += ed_len;
669 }
Simon Kelley16972692006-10-16 20:04:18 +0100670 bytes_in_buf = p - (unsigned char *)buf;
671}
672
673int helper_buf_empty(void)
674{
675 return bytes_in_buf == 0;
676}
677
Simon Kelley5aabfc72007-08-29 11:24:47 +0100678void helper_write(void)
Simon Kelley16972692006-10-16 20:04:18 +0100679{
680 ssize_t rc;
681
682 if (bytes_in_buf == 0)
683 return;
684
685 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
686 {
687 if (bytes_in_buf != (size_t)rc)
688 memmove(buf, buf + rc, bytes_in_buf - rc);
689 bytes_in_buf -= rc;
690 }
691 else
692 {
693 if (errno == EAGAIN || errno == EINTR)
694 return;
695 bytes_in_buf = 0;
696 }
697}
698
Simon Kelley5aabfc72007-08-29 11:24:47 +0100699#endif
Simon Kelley16972692006-10-16 20:04:18 +0100700
701
Simon Kelleyc72daea2012-01-05 21:33:27 +0000702