blob: 135111dc86975fb4954124a3b20357e96dffdc20 [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 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000220
221 /* expiry or length into dhcp_buff2 */
222#ifdef HAVE_BROKEN_RTC
223 sprintf(daemon->dhcp_buff2, "%u", data.length);
224#else
225 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
226#endif
227
228 /* supplied data may just exceed normal buffer (unlikely) */
229 if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
230 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
Simon Kelley16972692006-10-16 20:04:18 +0100231 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000232
233 if (!read_write(pipefd[0], buf,
234 data.hostname_len + data.ed_len + data.clid_len, 1))
235 continue;
236
237 /* CLID into packet */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000238 if (!is6)
239 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
240 {
241 p += sprintf(p, "%.2x", buf[i]);
242 if (i != data.clid_len - 1)
243 p += sprintf(p, ":");
244 }
245#ifdef HAVE_DHCP6
246 else
Simon Kelley16972692006-10-16 20:04:18 +0100247 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000248 /* or IAID and server DUID for IPv6 */
249 sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.hwaddr_type);
250 for (p = daemon->packet, i = 0; i < daemon->duid_len; i++)
251 {
252 p += sprintf(p, "%.2x", daemon->duid[i]);
253 if (i != daemon->duid_len - 1)
254 p += sprintf(p, ":");
255 }
Simon Kelleycaa94382012-02-15 10:29:50 +0000256
257 /* duid not MAC for IPv6 */
258 for (p = daemon->dhcp_buff, i = 0; i < data.clid_len; i++)
259 {
260 p += sprintf(p, "%.2x", buf[i]);
261 if (i != data.clid_len - 1)
262 p += sprintf(p, ":");
263 }
Simon Kelley16972692006-10-16 20:04:18 +0100264 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000265#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000266
267 buf += data.clid_len;
268
269 if (data.hostname_len != 0)
270 {
271 char *dot;
272 hostname = (char *)buf;
273 hostname[data.hostname_len - 1] = 0;
274 if (!legal_hostname(hostname))
275 hostname = NULL;
276 else if ((dot = strchr(hostname, '.')))
277 {
278 domain = dot+1;
279 *dot = 0;
280 }
281 }
282
283 extradata = buf + data.hostname_len;
284
Simon Kelleyceae00d2012-02-09 21:28:14 +0000285 if (!is6)
286 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
287#ifdef HAVE_DHCP6
288 else
289 inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
290#endif
291
Simon Kelleyc72daea2012-01-05 21:33:27 +0000292#ifdef HAVE_LUASCRIPT
293 if (daemon->luascript)
294 {
295 lua_getglobal(lua, "lease"); /* function to call */
296 lua_pushstring(lua, action_str); /* arg1 - action */
297 lua_newtable(lua); /* arg2 - data table */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000298
299 if (is6)
300 {
Simon Kelley57f460d2012-02-16 20:00:32 +0000301 lua_pushstring(lua, daemon->dhcp_buff);
302 lua_setfield(lua, -2, "client_duid");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000303 lua_pushstring(lua, daemon->packet);
Simon Kelley57f460d2012-02-16 20:00:32 +0000304 lua_setfield(lua, -2, "server_duid");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000305 lua_pushstring(lua, daemon->dhcp_buff3);
306 lua_setfield(lua, -2, "iaid");
307 }
308
309 if (!is6 && data.clid_len != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000310 {
311 lua_pushstring(lua, daemon->packet);
312 lua_setfield(lua, -2, "client_id");
313 }
314
315 if (strlen(data.interface) != 0)
316 {
317 lua_pushstring(lua, data.interface);
318 lua_setfield(lua, -2, "interface");
319 }
320
321#ifdef HAVE_BROKEN_RTC
322 lua_pushnumber(lua, data.length);
323 lua_setfield(lua, -2, "lease_length");
Simon Kelley16972692006-10-16 20:04:18 +0100324#else
Simon Kelleyc72daea2012-01-05 21:33:27 +0000325 lua_pushnumber(lua, data.expires);
326 lua_setfield(lua, -2, "lease_expires");
Simon Kelley16972692006-10-16 20:04:18 +0100327#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000328
329 if (hostname)
330 {
331 lua_pushstring(lua, hostname);
332 lua_setfield(lua, -2, "hostname");
333 }
334
335 if (domain)
336 {
337 lua_pushstring(lua, domain);
338 lua_setfield(lua, -2, "domain");
339 }
340
341 end = extradata + data.ed_len;
342 buf = extradata;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000343
344 if (!is6)
345 buf = grab_extradata_lua(buf, end, "vendor_class");
346#ifdef HAVE_DHCP6
347 else
348 for (i = 0; i < data.hwaddr_len; i++)
349 {
350 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
351 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
352 }
353#endif
354
Simon Kelleyc72daea2012-01-05 21:33:27 +0000355 buf = grab_extradata_lua(buf, end, "supplied_hostname");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000356
357 if (!is6)
358 {
359 buf = grab_extradata_lua(buf, end, "cpewan_oui");
360 buf = grab_extradata_lua(buf, end, "cpewan_serial");
361 buf = grab_extradata_lua(buf, end, "cpewan_class");
362 }
363
Simon Kelleyc72daea2012-01-05 21:33:27 +0000364 buf = grab_extradata_lua(buf, end, "tags");
Simon Kelleycaa94382012-02-15 10:29:50 +0000365
366 if (is6)
367 buf = grab_extradata_lua(buf, end, "relay_address");
368 else if (data.giaddr.s_addr != 0)
369 {
370 lua_pushstring(lua, inet_ntoa(data.giaddr));
371 lua_setfield(lua, -2, "relay_address");
372 }
373
Simon Kelleyc72daea2012-01-05 21:33:27 +0000374 for (i = 0; buf; i++)
375 {
376 sprintf(daemon->dhcp_buff2, "user_class%i", i);
377 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
378 }
379
Simon Kelleyc72daea2012-01-05 21:33:27 +0000380 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);
Simon Kelley57f460d2012-02-16 20:00:32 +0000445 my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000446 }
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 Kelleycaa94382012-02-15 10:29:50 +0000496 else if (data.giaddr.s_addr != 0)
497 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
498
Simon Kelley316e2732010-01-22 20:16:09 +0000499 for (i = 0; buf; i++)
500 {
501 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
502 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
503 }
504
Simon Kelleyc72daea2012-01-05 21:33:27 +0000505 if (data.action != ACTION_DEL && data.remaining_time != 0)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100506 {
507 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
508 my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
509 }
510
Simon Kelley16972692006-10-16 20:04:18 +0100511 if (data.action == ACTION_OLD_HOSTNAME && hostname)
512 {
Simon Kelley824af852008-02-12 20:43:05 +0000513 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
Simon Kelley16972692006-10-16 20:04:18 +0100514 hostname = NULL;
515 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100516
517 /* we need to have the event_fd around if exec fails */
518 if ((i = fcntl(event_fd, F_GETFD)) != -1)
519 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
520 close(pipefd[0]);
521
Simon Kelley16972692006-10-16 20:04:18 +0100522 p = strrchr(daemon->lease_change_command, '/');
Simon Kelley824af852008-02-12 20:43:05 +0000523 if (err == 0)
524 {
525 execl(daemon->lease_change_command,
526 p ? p+1 : daemon->lease_change_command,
Simon Kelleyceae00d2012-02-09 21:28:14 +0000527 action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000528 err = errno;
529 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100530 /* failed, send event so the main process logs the problem */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000531 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100532 _exit(0);
533 }
534}
535
Simon Kelley824af852008-02-12 20:43:05 +0000536static void my_setenv(const char *name, const char *value, int *error)
537{
Simon Kelley7622fc02009-06-04 20:32:05 +0100538 if (*error == 0 && setenv(name, value, 1) != 0)
539 *error = errno;
Simon Kelley824af852008-02-12 20:43:05 +0000540}
541
Simon Kelley316e2732010-01-22 20:16:09 +0000542static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
543{
544 unsigned char *next;
545
546 if (!buf || (buf == end))
547 return NULL;
548
549 for (next = buf; *next != 0; next++)
550 if (next == end)
551 return NULL;
552
553 if (next != buf)
554 {
555 char *p;
556 /* No "=" in value */
557 if ((p = strchr((char *)buf, '=')))
558 *p = 0;
559 my_setenv(env, (char *)buf, err);
560 }
561
562 return next + 1;
563}
564
Simon Kelleyc72daea2012-01-05 21:33:27 +0000565#ifdef HAVE_LUASCRIPT
566static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
567{
568 unsigned char *next;
569
570 if (!buf || (buf == end))
571 return NULL;
572
573 for (next = buf; *next != 0; next++)
574 if (next == end)
575 return NULL;
576
577 if (next != buf)
578 {
579 lua_pushstring(lua, (char *)buf);
580 lua_setfield(lua, -2, field);
581 }
582
583 return next + 1;
584}
585#endif
586
Simon Kelley16972692006-10-16 20:04:18 +0100587/* pack up lease data into a buffer */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100588void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
Simon Kelley16972692006-10-16 20:04:18 +0100589{
590 unsigned char *p;
591 size_t size;
Simon Kelley316e2732010-01-22 20:16:09 +0000592 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000593 int fd = daemon->dhcpfd;
594
595#ifdef HAVE_DHCP6
596 if (!daemon->dhcp)
597 fd = daemon->dhcp6fd;
598#endif
599
Simon Kelley16972692006-10-16 20:04:18 +0100600 /* no script */
601 if (daemon->helperfd == -1)
602 return;
603
Simon Kelley316e2732010-01-22 20:16:09 +0000604 if (lease->extradata)
605 ed_len = lease->extradata_len;
Simon Kelley16972692006-10-16 20:04:18 +0100606 if (lease->clid)
607 clid_len = lease->clid_len;
608 if (hostname)
609 hostname_len = strlen(hostname) + 1;
610
Simon Kelley316e2732010-01-22 20:16:09 +0000611 size = sizeof(struct script_data) + clid_len + ed_len + hostname_len;
Simon Kelley16972692006-10-16 20:04:18 +0100612
613 if (size > buf_size)
614 {
615 struct script_data *new;
616
Simon Kelley9009d742008-11-14 20:04:27 +0000617 /* start with reasonable size, will almost never need extending. */
Simon Kelley16972692006-10-16 20:04:18 +0100618 if (size < sizeof(struct script_data) + 200)
619 size = sizeof(struct script_data) + 200;
620
Simon Kelley5aabfc72007-08-29 11:24:47 +0100621 if (!(new = whine_malloc(size)))
Simon Kelley16972692006-10-16 20:04:18 +0100622 return;
623 if (buf)
624 free(buf);
625 buf = new;
626 buf_size = size;
627 }
628
629 buf->action = action;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000630 buf->flags = lease->flags;
Simon Kelley16972692006-10-16 20:04:18 +0100631 buf->hwaddr_len = lease->hwaddr_len;
632 buf->hwaddr_type = lease->hwaddr_type;
633 buf->clid_len = clid_len;
Simon Kelley316e2732010-01-22 20:16:09 +0000634 buf->ed_len = ed_len;
Simon Kelley16972692006-10-16 20:04:18 +0100635 buf->hostname_len = hostname_len;
636 buf->addr = lease->addr;
Simon Kelley1f15b812009-10-13 17:49:32 +0100637 buf->giaddr = lease->giaddr;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000638 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
639 if (!indextoname(fd, lease->last_interface, buf->interface))
Simon Kelley316e2732010-01-22 20:16:09 +0000640 buf->interface[0] = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000641
Simon Kelley16972692006-10-16 20:04:18 +0100642#ifdef HAVE_BROKEN_RTC
643 buf->length = lease->length;
644#else
645 buf->expires = lease->expires;
646#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000647
648 if (lease->expires != 0)
649 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
650 else
651 buf->remaining_time = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100652
Simon Kelley16972692006-10-16 20:04:18 +0100653 p = (unsigned char *)(buf+1);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100654 if (clid_len != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100655 {
656 memcpy(p, lease->clid, clid_len);
657 p += clid_len;
658 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100659 if (hostname_len != 0)
660 {
661 memcpy(p, hostname, hostname_len);
662 p += hostname_len;
663 }
Simon Kelley316e2732010-01-22 20:16:09 +0000664 if (ed_len != 0)
665 {
666 memcpy(p, lease->extradata, ed_len);
667 p += ed_len;
668 }
Simon Kelley16972692006-10-16 20:04:18 +0100669 bytes_in_buf = p - (unsigned char *)buf;
670}
671
672int helper_buf_empty(void)
673{
674 return bytes_in_buf == 0;
675}
676
Simon Kelley5aabfc72007-08-29 11:24:47 +0100677void helper_write(void)
Simon Kelley16972692006-10-16 20:04:18 +0100678{
679 ssize_t rc;
680
681 if (bytes_in_buf == 0)
682 return;
683
684 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
685 {
686 if (bytes_in_buf != (size_t)rc)
687 memmove(buf, buf + rc, bytes_in_buf - rc);
688 bytes_in_buf -= rc;
689 }
690 else
691 {
692 if (errno == EAGAIN || errno == EINTR)
693 return;
694 bytes_in_buf = 0;
695 }
696}
697
Simon Kelley5aabfc72007-08-29 11:24:47 +0100698#endif
Simon Kelley16972692006-10-16 20:04:18 +0100699
700
Simon Kelleyc72daea2012-01-05 21:33:27 +0000701