blob: bcb315cfcfff11c2732a4d081e411124de267ab8 [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
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
64 unsigned char hwaddr[DHCP_CHADDR_MAX];
Simon Kelley824af852008-02-12 20:43:05 +000065 char interface[IF_NAMESIZE];
Simon Kelleyceae00d2012-02-09 21:28:14 +000066
Simon Kelley16972692006-10-16 20:04:18 +010067};
68
Simon Kelley5aabfc72007-08-29 11:24:47 +010069static struct script_data *buf = NULL;
70static size_t bytes_in_buf = 0, buf_size = 0;
Simon Kelley16972692006-10-16 20:04:18 +010071
Simon Kelley1a6bca82008-07-11 11:11:42 +010072int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
Simon Kelley16972692006-10-16 20:04:18 +010073{
74 pid_t pid;
75 int i, pipefd[2];
76 struct sigaction sigact;
Simon Kelley9e038942008-05-30 20:06:34 +010077
Simon Kelley5aabfc72007-08-29 11:24:47 +010078 /* create the pipe through which the main program sends us commands,
Simon Kelley1a6bca82008-07-11 11:11:42 +010079 then fork our process. */
Simon Kelley5aabfc72007-08-29 11:24:47 +010080 if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
81 {
Simon Kelleyc72daea2012-01-05 21:33:27 +000082 send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +010083 _exit(0);
Simon Kelley5aabfc72007-08-29 11:24:47 +010084 }
85
Simon Kelley16972692006-10-16 20:04:18 +010086 if (pid != 0)
87 {
88 close(pipefd[0]); /* close reader side */
89 return pipefd[1];
90 }
91
Simon Kelley5aabfc72007-08-29 11:24:47 +010092 /* ignore SIGTERM, so that we can clean up when the main process gets hit
93 and SIGALRM so that we can use sleep() */
Simon Kelley16972692006-10-16 20:04:18 +010094 sigact.sa_handler = SIG_IGN;
95 sigact.sa_flags = 0;
96 sigemptyset(&sigact.sa_mask);
97 sigaction(SIGTERM, &sigact, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +010098 sigaction(SIGALRM, &sigact, NULL);
Simon Kelley16972692006-10-16 20:04:18 +010099
Simon Kelley28866e92011-02-14 20:19:14 +0000100 if (!option_bool(OPT_DEBUG) && uid != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100101 {
102 gid_t dummy;
103 if (setgroups(0, &dummy) == -1 ||
104 setgid(gid) == -1 ||
105 setuid(uid) == -1)
106 {
Simon Kelley28866e92011-02-14 20:19:14 +0000107 if (option_bool(OPT_NO_FORK))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100108 /* send error to daemon process if no-fork */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000109 send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100110 else
111 {
112 /* kill daemon */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000113 send_event(event_fd, EVENT_DIE, 0, NULL);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100114 /* return error */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000115 send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100116 }
117 _exit(0);
118 }
119 }
120
Simon Kelleyc72daea2012-01-05 21:33:27 +0000121 /* close all the sockets etc, we don't need them here.
122 Don't close err_fd, in case the lua-init fails.
123 Note that we have to do this before lua init
124 so we don't close any lua fds. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100125 for (max_fd--; max_fd >= 0; max_fd--)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100126 if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
Simon Kelleyc72daea2012-01-05 21:33:27 +0000127 max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
128 max_fd != event_fd && max_fd != err_fd)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100129 close(max_fd);
Simon Kelley16972692006-10-16 20:04:18 +0100130
Simon Kelleyc72daea2012-01-05 21:33:27 +0000131#ifdef HAVE_LUASCRIPT
132 if (daemon->luascript)
133 {
134 const char *lua_err = NULL;
135 lua = lua_open();
136 luaL_openlibs(lua);
137
138 /* get Lua to load our script file */
139 if (luaL_dofile(lua, daemon->luascript) != 0)
140 lua_err = lua_tostring(lua, -1);
141 else
142 {
143 lua_getglobal(lua, "lease");
144 if (lua_type(lua, -1) != LUA_TFUNCTION)
145 lua_err = _("lease() function missing in Lua script");
146 }
147
148 if (lua_err)
149 {
150 if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
151 /* send error to daemon process if no-fork */
152 send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
153 else
154 {
155 /* kill daemon */
156 send_event(event_fd, EVENT_DIE, 0, NULL);
157 /* return error */
158 send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
159 }
160 _exit(0);
161 }
162
163 lua_pop(lua, 1); /* remove nil from stack */
164 lua_getglobal(lua, "init");
165 if (lua_type(lua, -1) == LUA_TFUNCTION)
166 lua_call(lua, 0, 0);
167 else
168 lua_pop(lua, 1); /* remove nil from stack */
169 }
170#endif
171
172 /* All init done, close our copy of the error pipe, so that main process can return */
173 if (err_fd != -1)
174 close(err_fd);
175
Simon Kelley16972692006-10-16 20:04:18 +0100176 /* loop here */
177 while(1)
178 {
179 struct script_data data;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000180 char *p, *action_str, *hostname = NULL, *domain = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100181 unsigned char *buf = (unsigned char *)daemon->namebuff;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000182 unsigned char *end, *extradata, *alloc_buff = NULL;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000183 int is6, err = 0;
Simon Kelley16972692006-10-16 20:04:18 +0100184
Simon Kelleyc72daea2012-01-05 21:33:27 +0000185 free(alloc_buff);
186
Simon Kelley16972692006-10-16 20:04:18 +0100187 /* we read zero bytes when pipe closed: this is our signal to exit */
188 if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000189 {
190#ifdef HAVE_LUASCRIPT
191 if (daemon->luascript)
192 {
193 lua_getglobal(lua, "shutdown");
194 if (lua_type(lua, -1) == LUA_TFUNCTION)
195 lua_call(lua, 0, 0);
196 }
197#endif
198 _exit(0);
199 }
Simon Kelleya9530962012-03-20 22:07:35 +0000200
201 is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
202
Simon Kelley16972692006-10-16 20:04:18 +0100203 if (data.action == ACTION_DEL)
204 action_str = "del";
205 else if (data.action == ACTION_ADD)
206 action_str = "add";
207 else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
208 action_str = "old";
Simon Kelleya9530962012-03-20 22:07:35 +0000209 else if (data.action == ACTION_TFTP)
210 {
211 action_str = "tftp";
212 is6 = (data.flags != AF_INET);
213 }
Simon Kelley16972692006-10-16 20:04:18 +0100214 else
215 continue;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000216
Simon Kelleya9530962012-03-20 22:07:35 +0000217
Simon Kelleyceae00d2012-02-09 21:28:14 +0000218 if (!is6)
219 {
220 /* stringify MAC into dhcp_buff */
221 p = daemon->dhcp_buff;
222 if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
223 p += sprintf(p, "%.2x-", data.hwaddr_type);
224 for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
225 {
226 p += sprintf(p, "%.2x", data.hwaddr[i]);
227 if (i != data.hwaddr_len - 1)
228 p += sprintf(p, ":");
229 }
230 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000231
232 /* expiry or length into dhcp_buff2 */
233#ifdef HAVE_BROKEN_RTC
234 sprintf(daemon->dhcp_buff2, "%u", data.length);
235#else
236 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
237#endif
238
239 /* supplied data may just exceed normal buffer (unlikely) */
240 if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
241 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
Simon Kelley16972692006-10-16 20:04:18 +0100242 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000243
244 if (!read_write(pipefd[0], buf,
245 data.hostname_len + data.ed_len + data.clid_len, 1))
246 continue;
247
248 /* CLID into packet */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000249 if (!is6)
250 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
251 {
252 p += sprintf(p, "%.2x", buf[i]);
253 if (i != data.clid_len - 1)
254 p += sprintf(p, ":");
255 }
256#ifdef HAVE_DHCP6
257 else
Simon Kelley16972692006-10-16 20:04:18 +0100258 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000259 /* or IAID and server DUID for IPv6 */
260 sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.hwaddr_type);
261 for (p = daemon->packet, i = 0; i < daemon->duid_len; i++)
262 {
263 p += sprintf(p, "%.2x", daemon->duid[i]);
264 if (i != daemon->duid_len - 1)
265 p += sprintf(p, ":");
266 }
Simon Kelleycaa94382012-02-15 10:29:50 +0000267
268 /* duid not MAC for IPv6 */
269 for (p = daemon->dhcp_buff, i = 0; i < data.clid_len; i++)
270 {
271 p += sprintf(p, "%.2x", buf[i]);
272 if (i != data.clid_len - 1)
273 p += sprintf(p, ":");
274 }
Simon Kelley16972692006-10-16 20:04:18 +0100275 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000276#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000277
278 buf += data.clid_len;
279
280 if (data.hostname_len != 0)
281 {
282 char *dot;
283 hostname = (char *)buf;
284 hostname[data.hostname_len - 1] = 0;
Simon Kelleya9530962012-03-20 22:07:35 +0000285 if (data.action != ACTION_TFTP)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000286 {
Simon Kelleya9530962012-03-20 22:07:35 +0000287 if (!legal_hostname(hostname))
288 hostname = NULL;
289 else if ((dot = strchr(hostname, '.')))
290 {
291 domain = dot+1;
292 *dot = 0;
293 }
294 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000295 }
296
297 extradata = buf + data.hostname_len;
298
Simon Kelleyceae00d2012-02-09 21:28:14 +0000299 if (!is6)
300 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
301#ifdef HAVE_DHCP6
302 else
303 inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
304#endif
305
Simon Kelleya9530962012-03-20 22:07:35 +0000306 /* file length */
307 if (data.action == ACTION_TFTP)
308 sprintf(daemon->dhcp_buff, "%u", data.hwaddr_len);
309
Simon Kelleyc72daea2012-01-05 21:33:27 +0000310#ifdef HAVE_LUASCRIPT
311 if (daemon->luascript)
312 {
Simon Kelleya9530962012-03-20 22:07:35 +0000313 if (data.action == ACTION_TFTP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000314 {
Simon Kelleya9530962012-03-20 22:07:35 +0000315 lua_getglobal(lua, "tftp");
316 if (lua_type(lua, -1) != LUA_TFUNCTION)
317 lua_pop(lua, 1); /* tftp function optional */
318 else
319 {
320 lua_pushstring(lua, action_str); /* arg1 - action */
321 lua_newtable(lua); /* arg2 - data table */
322 lua_pushstring(lua, daemon->addrbuff);
323 lua_setfield(lua, -2, "destination_address");
324 lua_pushstring(lua, hostname);
325 lua_setfield(lua, -2, "file_name");
326 lua_pushstring(lua, daemon->dhcp_buff);
327 lua_setfield(lua, -2, "file_size");
328 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
329 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000330 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000331 else
Simon Kelleya9530962012-03-20 22:07:35 +0000332 {
333 lua_getglobal(lua, "lease"); /* function to call */
334 lua_pushstring(lua, action_str); /* arg1 - action */
335 lua_newtable(lua); /* arg2 - data table */
336
337 if (is6)
338 {
339 lua_pushstring(lua, daemon->dhcp_buff);
340 lua_setfield(lua, -2, "client_duid");
341 lua_pushstring(lua, daemon->packet);
342 lua_setfield(lua, -2, "server_duid");
343 lua_pushstring(lua, daemon->dhcp_buff3);
344 lua_setfield(lua, -2, "iaid");
345 }
346
347 if (!is6 && data.clid_len != 0)
348 {
349 lua_pushstring(lua, daemon->packet);
350 lua_setfield(lua, -2, "client_id");
351 }
352
353 if (strlen(data.interface) != 0)
354 {
355 lua_pushstring(lua, data.interface);
356 lua_setfield(lua, -2, "interface");
357 }
358
359#ifdef HAVE_BROKEN_RTC
360 lua_pushnumber(lua, data.length);
361 lua_setfield(lua, -2, "lease_length");
362#else
363 lua_pushnumber(lua, data.expires);
364 lua_setfield(lua, -2, "lease_expires");
Simon Kelleyceae00d2012-02-09 21:28:14 +0000365#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000366
367 if (hostname)
368 {
369 lua_pushstring(lua, hostname);
370 lua_setfield(lua, -2, "hostname");
371 }
372
373 if (domain)
374 {
375 lua_pushstring(lua, domain);
376 lua_setfield(lua, -2, "domain");
377 }
378
379 end = extradata + data.ed_len;
380 buf = extradata;
381
382 if (!is6)
383 buf = grab_extradata_lua(buf, end, "vendor_class");
384#ifdef HAVE_DHCP6
385 else
386 for (i = 0; i < data.hwaddr_len; i++)
387 {
388 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
389 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
390 }
391#endif
392
393 buf = grab_extradata_lua(buf, end, "supplied_hostname");
394
395 if (!is6)
396 {
397 buf = grab_extradata_lua(buf, end, "cpewan_oui");
398 buf = grab_extradata_lua(buf, end, "cpewan_serial");
399 buf = grab_extradata_lua(buf, end, "cpewan_class");
400 }
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
430 if (!is6)
431 {
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 Kelleya9530962012-03-20 22:07:35 +0000483 if (is6)
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000484 {
Simon Kelleya9530962012-03-20 22:07:35 +0000485 my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
486 my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
487 }
488
489 if (!is6 && data.clid_len != 0)
490 my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
491
492 if (strlen(data.interface) != 0)
493 my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
494
495#ifdef HAVE_BROKEN_RTC
496 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
497#else
498 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
499#endif
500
501 if (domain)
502 my_setenv("DNSMASQ_DOMAIN", domain, &err);
503
504 end = extradata + data.ed_len;
505 buf = extradata;
506
507 if (!is6)
508 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
509#ifdef HAVE_DHCP6
510 else
511 {
512 if (data.hwaddr_len != 0)
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000513 {
Simon Kelleya9530962012-03-20 22:07:35 +0000514 buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
515 for (i = 0; i < data.hwaddr_len - 1; i++)
516 {
517 sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
518 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
519 }
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000520 }
521 }
Simon Kelleyceae00d2012-02-09 21:28:14 +0000522#endif
Simon Kelleya9530962012-03-20 22:07:35 +0000523
524 buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
525
526 if (!is6)
527 {
528 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
529 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
530 buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
531 }
532
533 buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
534
535 if (is6)
536 buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
537 else if (data.giaddr.s_addr != 0)
538 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
539
540 for (i = 0; buf; i++)
541 {
542 sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
543 buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
544 }
545
546 if (data.action != ACTION_DEL && data.remaining_time != 0)
547 {
548 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
549 my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
550 }
551
552 if (data.action == ACTION_OLD_HOSTNAME && hostname)
553 {
554 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
555 hostname = NULL;
556 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100557 }
558
Simon Kelleya9530962012-03-20 22:07:35 +0000559 if (option_bool(OPT_LOG_OPTS))
560 my_setenv("DNSMASQ_LOG_DHCP", "1", &err);
561
Simon Kelley5aabfc72007-08-29 11:24:47 +0100562 /* we need to have the event_fd around if exec fails */
563 if ((i = fcntl(event_fd, F_GETFD)) != -1)
564 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
565 close(pipefd[0]);
566
Simon Kelley16972692006-10-16 20:04:18 +0100567 p = strrchr(daemon->lease_change_command, '/');
Simon Kelley824af852008-02-12 20:43:05 +0000568 if (err == 0)
569 {
570 execl(daemon->lease_change_command,
571 p ? p+1 : daemon->lease_change_command,
Simon Kelleyceae00d2012-02-09 21:28:14 +0000572 action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000573 err = errno;
574 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100575 /* failed, send event so the main process logs the problem */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000576 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100577 _exit(0);
578 }
579}
580
Simon Kelley824af852008-02-12 20:43:05 +0000581static void my_setenv(const char *name, const char *value, int *error)
582{
Simon Kelley7622fc02009-06-04 20:32:05 +0100583 if (*error == 0 && setenv(name, value, 1) != 0)
584 *error = errno;
Simon Kelley824af852008-02-12 20:43:05 +0000585}
586
Simon Kelley316e2732010-01-22 20:16:09 +0000587static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
588{
589 unsigned char *next;
590
591 if (!buf || (buf == end))
592 return NULL;
593
594 for (next = buf; *next != 0; next++)
595 if (next == end)
596 return NULL;
597
598 if (next != buf)
599 {
600 char *p;
601 /* No "=" in value */
602 if ((p = strchr((char *)buf, '=')))
603 *p = 0;
604 my_setenv(env, (char *)buf, err);
605 }
606
607 return next + 1;
608}
609
Simon Kelleyc72daea2012-01-05 21:33:27 +0000610#ifdef HAVE_LUASCRIPT
611static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
612{
613 unsigned char *next;
614
615 if (!buf || (buf == end))
616 return NULL;
617
618 for (next = buf; *next != 0; next++)
619 if (next == end)
620 return NULL;
621
622 if (next != buf)
623 {
624 lua_pushstring(lua, (char *)buf);
625 lua_setfield(lua, -2, field);
626 }
627
628 return next + 1;
629}
630#endif
631
Simon Kelleya9530962012-03-20 22:07:35 +0000632static void buff_alloc(size_t size)
633{
634 if (size > buf_size)
635 {
636 struct script_data *new;
637
638 /* start with reasonable size, will almost never need extending. */
639 if (size < sizeof(struct script_data) + 200)
640 size = sizeof(struct script_data) + 200;
641
642 if (!(new = whine_malloc(size)))
643 return;
644 if (buf)
645 free(buf);
646 buf = new;
647 buf_size = size;
648 }
649}
650
Simon Kelley16972692006-10-16 20:04:18 +0100651/* pack up lease data into a buffer */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100652void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
Simon Kelley16972692006-10-16 20:04:18 +0100653{
654 unsigned char *p;
Simon Kelley316e2732010-01-22 20:16:09 +0000655 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000656 int fd = daemon->dhcpfd;
657
658#ifdef HAVE_DHCP6
659 if (!daemon->dhcp)
660 fd = daemon->dhcp6fd;
661#endif
662
Simon Kelley16972692006-10-16 20:04:18 +0100663 /* no script */
664 if (daemon->helperfd == -1)
665 return;
666
Simon Kelley316e2732010-01-22 20:16:09 +0000667 if (lease->extradata)
668 ed_len = lease->extradata_len;
Simon Kelley16972692006-10-16 20:04:18 +0100669 if (lease->clid)
670 clid_len = lease->clid_len;
671 if (hostname)
672 hostname_len = strlen(hostname) + 1;
673
Simon Kelleya9530962012-03-20 22:07:35 +0000674 buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
Simon Kelley16972692006-10-16 20:04:18 +0100675
676 buf->action = action;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000677 buf->flags = lease->flags;
Simon Kelley16972692006-10-16 20:04:18 +0100678 buf->hwaddr_len = lease->hwaddr_len;
679 buf->hwaddr_type = lease->hwaddr_type;
680 buf->clid_len = clid_len;
Simon Kelley316e2732010-01-22 20:16:09 +0000681 buf->ed_len = ed_len;
Simon Kelley16972692006-10-16 20:04:18 +0100682 buf->hostname_len = hostname_len;
683 buf->addr = lease->addr;
Simon Kelley1f15b812009-10-13 17:49:32 +0100684 buf->giaddr = lease->giaddr;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000685 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
686 if (!indextoname(fd, lease->last_interface, buf->interface))
Simon Kelley316e2732010-01-22 20:16:09 +0000687 buf->interface[0] = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000688
Simon Kelley16972692006-10-16 20:04:18 +0100689#ifdef HAVE_BROKEN_RTC
690 buf->length = lease->length;
691#else
692 buf->expires = lease->expires;
693#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000694
695 if (lease->expires != 0)
696 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
697 else
698 buf->remaining_time = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100699
Simon Kelley16972692006-10-16 20:04:18 +0100700 p = (unsigned char *)(buf+1);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100701 if (clid_len != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100702 {
703 memcpy(p, lease->clid, clid_len);
704 p += clid_len;
705 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100706 if (hostname_len != 0)
707 {
708 memcpy(p, hostname, hostname_len);
709 p += hostname_len;
710 }
Simon Kelley316e2732010-01-22 20:16:09 +0000711 if (ed_len != 0)
712 {
713 memcpy(p, lease->extradata, ed_len);
714 p += ed_len;
715 }
Simon Kelley16972692006-10-16 20:04:18 +0100716 bytes_in_buf = p - (unsigned char *)buf;
717}
718
Simon Kelleya9530962012-03-20 22:07:35 +0000719#ifdef HAVE_TFTP
720/* This nastily re-uses DHCP-fields for TFTP stuff */
721void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
722{
723 unsigned int filename_len;
724
725 /* no script */
726 if (daemon->helperfd == -1)
727 return;
728
729 filename_len = strlen(filename) + 1;
730 buff_alloc(sizeof(struct script_data) + filename_len);
731 memset(buf, 0, sizeof(struct script_data));
732
733 buf->action = ACTION_TFTP;
734 buf->hostname_len = filename_len;
735 buf->hwaddr_len = file_len;
736
737 if ((buf->flags = peer->sa.sa_family) == AF_INET)
738 buf->addr = peer->in.sin_addr;
739#ifdef HAVE_IPV6
740 else
741 memcpy(buf->hwaddr, &peer->in6.sin6_addr, IN6ADDRSZ);
742#endif
743
744 memcpy((unsigned char *)(buf+1), filename, filename_len);
745
746 bytes_in_buf = sizeof(struct script_data) + filename_len;
747}
748#endif
749
Simon Kelley16972692006-10-16 20:04:18 +0100750int helper_buf_empty(void)
751{
752 return bytes_in_buf == 0;
753}
754
Simon Kelley5aabfc72007-08-29 11:24:47 +0100755void helper_write(void)
Simon Kelley16972692006-10-16 20:04:18 +0100756{
757 ssize_t rc;
758
759 if (bytes_in_buf == 0)
760 return;
761
762 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
763 {
764 if (bytes_in_buf != (size_t)rc)
765 memmove(buf, buf + rc, bytes_in_buf - rc);
766 bytes_in_buf -= rc;
767 }
768 else
769 {
770 if (errno == EAGAIN || errno == EINTR)
771 return;
772 bytes_in_buf = 0;
773 }
774}
775
Simon Kelley5aabfc72007-08-29 11:24:47 +0100776#endif
Simon Kelley16972692006-10-16 20:04:18 +0100777
778
Simon Kelleyc72daea2012-01-05 21:33:27 +0000779