blob: ab691b7f516377569fe491cad4766914bb298606 [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 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
Simon Kelleyc72daea2012-01-05 21:33:27 +0000232 /* supplied data may just exceed normal buffer (unlikely) */
233 if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
234 !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
Simon Kelley16972692006-10-16 20:04:18 +0100235 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000236
237 if (!read_write(pipefd[0], buf,
238 data.hostname_len + data.ed_len + data.clid_len, 1))
239 continue;
240
241 /* CLID into packet */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000242 if (!is6)
243 for (p = daemon->packet, i = 0; i < data.clid_len; i++)
244 {
245 p += sprintf(p, "%.2x", buf[i]);
246 if (i != data.clid_len - 1)
247 p += sprintf(p, ":");
248 }
249#ifdef HAVE_DHCP6
250 else
Simon Kelley16972692006-10-16 20:04:18 +0100251 {
Simon Kelleyceae00d2012-02-09 21:28:14 +0000252 /* or IAID and server DUID for IPv6 */
253 sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.hwaddr_type);
254 for (p = daemon->packet, i = 0; i < daemon->duid_len; i++)
255 {
256 p += sprintf(p, "%.2x", daemon->duid[i]);
257 if (i != daemon->duid_len - 1)
258 p += sprintf(p, ":");
259 }
Simon Kelleycaa94382012-02-15 10:29:50 +0000260
261 /* duid not MAC for IPv6 */
262 for (p = daemon->dhcp_buff, i = 0; i < data.clid_len; i++)
263 {
264 p += sprintf(p, "%.2x", buf[i]);
265 if (i != data.clid_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;
Simon Kelleya9530962012-03-20 22:07:35 +0000278 if (data.action != ACTION_TFTP)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000279 {
Simon Kelleya9530962012-03-20 22:07:35 +0000280 if (!legal_hostname(hostname))
281 hostname = NULL;
282 else if ((dot = strchr(hostname, '.')))
283 {
284 domain = dot+1;
285 *dot = 0;
286 }
287 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000288 }
289
290 extradata = buf + data.hostname_len;
291
Simon Kelleyceae00d2012-02-09 21:28:14 +0000292 if (!is6)
293 inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
294#ifdef HAVE_DHCP6
295 else
296 inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
297#endif
298
Simon Kelleya9530962012-03-20 22:07:35 +0000299 /* file length */
300 if (data.action == ACTION_TFTP)
301 sprintf(daemon->dhcp_buff, "%u", data.hwaddr_len);
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");
319 lua_pushstring(lua, daemon->dhcp_buff);
320 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 {
332 lua_pushstring(lua, daemon->dhcp_buff);
333 lua_setfield(lua, -2, "client_duid");
334 lua_pushstring(lua, daemon->packet);
335 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
378 else
379 for (i = 0; i < data.hwaddr_len; i++)
380 {
381 sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
382 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
383 }
384#endif
385
386 buf = grab_extradata_lua(buf, end, "supplied_hostname");
387
388 if (!is6)
389 {
390 buf = grab_extradata_lua(buf, end, "cpewan_oui");
391 buf = grab_extradata_lua(buf, end, "cpewan_serial");
392 buf = grab_extradata_lua(buf, end, "cpewan_class");
Simon Kelleydd1721c2013-02-18 21:04:04 +0000393 buf = grab_extradata_lua(buf, end, "circuit_id");
394 buf = grab_extradata_lua(buf, end, "subscriber_id");
395 buf = grab_extradata_lua(buf, end, "remote_id");
Simon Kelleya9530962012-03-20 22:07:35 +0000396 }
397
398 buf = grab_extradata_lua(buf, end, "tags");
399
400 if (is6)
401 buf = grab_extradata_lua(buf, end, "relay_address");
402 else if (data.giaddr.s_addr != 0)
403 {
404 lua_pushstring(lua, inet_ntoa(data.giaddr));
405 lua_setfield(lua, -2, "relay_address");
406 }
407
408 for (i = 0; buf; i++)
409 {
410 sprintf(daemon->dhcp_buff2, "user_class%i", i);
411 buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
412 }
413
414 if (data.action != ACTION_DEL && data.remaining_time != 0)
415 {
416 lua_pushnumber(lua, data.remaining_time);
417 lua_setfield(lua, -2, "time_remaining");
418 }
419
420 if (data.action == ACTION_OLD_HOSTNAME && hostname)
421 {
422 lua_pushstring(lua, hostname);
423 lua_setfield(lua, -2, "old_hostname");
424 }
425
426 if (!is6)
427 {
428 lua_pushstring(lua, daemon->dhcp_buff);
429 lua_setfield(lua, -2, "mac_address");
430 }
431
432 lua_pushstring(lua, daemon->addrbuff);
433 lua_setfield(lua, -2, "ip_address");
434
435 lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
Simon Kelleyceae00d2012-02-09 21:28:14 +0000436 }
Simon Kelleyc72daea2012-01-05 21:33:27 +0000437 }
438#endif
439
440 /* no script, just lua */
441 if (!daemon->lease_change_command)
Simon Kelley316e2732010-01-22 20:16:09 +0000442 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000443
Simon Kelley5aabfc72007-08-29 11:24:47 +0100444 /* possible fork errors are all temporary resource problems */
445 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
446 sleep(2);
Simon Kelley316e2732010-01-22 20:16:09 +0000447
Simon Kelley5aabfc72007-08-29 11:24:47 +0100448 if (pid == -1)
449 continue;
Simon Kelleyc72daea2012-01-05 21:33:27 +0000450
Simon Kelley16972692006-10-16 20:04:18 +0100451 /* wait for child to complete */
452 if (pid != 0)
453 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100454 /* reap our children's children, if necessary */
455 while (1)
456 {
457 int status;
458 pid_t rc = wait(&status);
459
460 if (rc == pid)
461 {
462 /* On error send event back to main process for logging */
463 if (WIFSIGNALED(status))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000464 send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100465 else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
Simon Kelleyc72daea2012-01-05 21:33:27 +0000466 send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100467 break;
468 }
469
470 if (rc == -1 && errno != EINTR)
471 break;
472 }
473
Simon Kelley16972692006-10-16 20:04:18 +0100474 continue;
475 }
476
Simon Kelleya9530962012-03-20 22:07:35 +0000477 if (data.action != ACTION_TFTP)
Simon Kelleyceae00d2012-02-09 21:28:14 +0000478 {
Simon Kelleya9530962012-03-20 22:07:35 +0000479 if (is6)
Simon Kelleya5c72ab2012-02-10 13:42:47 +0000480 {
Simon Kelleya9530962012-03-20 22:07:35 +0000481 my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
482 my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
483 }
484
485 if (!is6 && data.clid_len != 0)
486 my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
487
488 if (strlen(data.interface) != 0)
489 my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
490
491#ifdef HAVE_BROKEN_RTC
Simon Kelley208fb612013-02-21 22:26:18 +0000492 sprintf(daemon->dhcp_buff2, "%u", data.length);
Simon Kelleya9530962012-03-20 22:07:35 +0000493 my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
494#else
Simon Kelley208fb612013-02-21 22:26:18 +0000495 sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
Simon Kelleya9530962012-03-20 22:07:35 +0000496 my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
497#endif
498
499 if (domain)
500 my_setenv("DNSMASQ_DOMAIN", domain, &err);
501
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 {
510 if (data.hwaddr_len != 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);
513 for (i = 0; i < data.hwaddr_len - 1; i++)
514 {
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);
538 else if (data.giaddr.s_addr != 0)
539 my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
540
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
547 if (data.action != ACTION_DEL && data.remaining_time != 0)
548 {
549 sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
550 my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
551 }
552
553 if (data.action == ACTION_OLD_HOSTNAME && hostname)
554 {
555 my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
556 hostname = NULL;
557 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100558 }
559
Simon Kelleya9530962012-03-20 22:07:35 +0000560 if (option_bool(OPT_LOG_OPTS))
561 my_setenv("DNSMASQ_LOG_DHCP", "1", &err);
562
Simon Kelley5aabfc72007-08-29 11:24:47 +0100563 /* we need to have the event_fd around if exec fails */
564 if ((i = fcntl(event_fd, F_GETFD)) != -1)
565 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
566 close(pipefd[0]);
567
Simon Kelley16972692006-10-16 20:04:18 +0100568 p = strrchr(daemon->lease_change_command, '/');
Simon Kelley824af852008-02-12 20:43:05 +0000569 if (err == 0)
570 {
571 execl(daemon->lease_change_command,
572 p ? p+1 : daemon->lease_change_command,
Simon Kelleyceae00d2012-02-09 21:28:14 +0000573 action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000574 err = errno;
575 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100576 /* failed, send event so the main process logs the problem */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000577 send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
Simon Kelley16972692006-10-16 20:04:18 +0100578 _exit(0);
579 }
580}
581
Simon Kelley824af852008-02-12 20:43:05 +0000582static void my_setenv(const char *name, const char *value, int *error)
583{
Simon Kelley7622fc02009-06-04 20:32:05 +0100584 if (*error == 0 && setenv(name, value, 1) != 0)
585 *error = errno;
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{
590 unsigned char *next;
591
592 if (!buf || (buf == end))
593 return NULL;
594
595 for (next = buf; *next != 0; next++)
596 if (next == end)
597 return NULL;
598
599 if (next != buf)
600 {
601 char *p;
602 /* No "=" in value */
603 if ((p = strchr((char *)buf, '=')))
604 *p = 0;
605 my_setenv(env, (char *)buf, err);
606 }
607
608 return next + 1;
609}
610
Simon Kelleyc72daea2012-01-05 21:33:27 +0000611#ifdef HAVE_LUASCRIPT
612static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
613{
614 unsigned char *next;
615
616 if (!buf || (buf == end))
617 return NULL;
618
619 for (next = buf; *next != 0; next++)
620 if (next == end)
621 return NULL;
622
623 if (next != buf)
624 {
625 lua_pushstring(lua, (char *)buf);
626 lua_setfield(lua, -2, field);
627 }
628
629 return next + 1;
630}
631#endif
632
Simon Kelleya9530962012-03-20 22:07:35 +0000633static void buff_alloc(size_t size)
634{
635 if (size > buf_size)
636 {
637 struct script_data *new;
638
639 /* start with reasonable size, will almost never need extending. */
640 if (size < sizeof(struct script_data) + 200)
641 size = sizeof(struct script_data) + 200;
642
643 if (!(new = whine_malloc(size)))
644 return;
645 if (buf)
646 free(buf);
647 buf = new;
648 buf_size = size;
649 }
650}
651
Simon Kelley16972692006-10-16 20:04:18 +0100652/* pack up lease data into a buffer */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100653void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
Simon Kelley16972692006-10-16 20:04:18 +0100654{
655 unsigned char *p;
Simon Kelley316e2732010-01-22 20:16:09 +0000656 unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000657 int fd = daemon->dhcpfd;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100658#ifdef HAVE_DHCP6
659 int is6 = !!(lease->flags & (LEASE_TA | LEASE_NA));
Simon Kelleyceae00d2012-02-09 21:28:14 +0000660
Simon Kelleyceae00d2012-02-09 21:28:14 +0000661 if (!daemon->dhcp)
662 fd = daemon->dhcp6fd;
663#endif
664
Simon Kelley16972692006-10-16 20:04:18 +0100665 /* no script */
666 if (daemon->helperfd == -1)
667 return;
668
Simon Kelley316e2732010-01-22 20:16:09 +0000669 if (lease->extradata)
670 ed_len = lease->extradata_len;
Simon Kelley16972692006-10-16 20:04:18 +0100671 if (lease->clid)
672 clid_len = lease->clid_len;
673 if (hostname)
674 hostname_len = strlen(hostname) + 1;
675
Simon Kelleya9530962012-03-20 22:07:35 +0000676 buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
Simon Kelley16972692006-10-16 20:04:18 +0100677
678 buf->action = action;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000679 buf->flags = lease->flags;
Simon Kelley6f9aaa92013-04-10 10:25:26 +0100680#ifdef HAVE_DHCP6
681 if (is6)
682 buf->hwaddr_len = lease->vendorclass_count;
683 else
684#endif
685 buf->hwaddr_len = lease->hwaddr_len;
Simon Kelley16972692006-10-16 20:04:18 +0100686 buf->hwaddr_type = lease->hwaddr_type;
687 buf->clid_len = clid_len;
Simon Kelley316e2732010-01-22 20:16:09 +0000688 buf->ed_len = ed_len;
Simon Kelley16972692006-10-16 20:04:18 +0100689 buf->hostname_len = hostname_len;
690 buf->addr = lease->addr;
Simon Kelley1f15b812009-10-13 17:49:32 +0100691 buf->giaddr = lease->giaddr;
Simon Kelleyceae00d2012-02-09 21:28:14 +0000692 memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
693 if (!indextoname(fd, lease->last_interface, buf->interface))
Simon Kelley316e2732010-01-22 20:16:09 +0000694 buf->interface[0] = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000695
Simon Kelley16972692006-10-16 20:04:18 +0100696#ifdef HAVE_BROKEN_RTC
697 buf->length = lease->length;
698#else
699 buf->expires = lease->expires;
700#endif
Simon Kelleyc72daea2012-01-05 21:33:27 +0000701
702 if (lease->expires != 0)
703 buf->remaining_time = (unsigned int)difftime(lease->expires, now);
704 else
705 buf->remaining_time = 0;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100706
Simon Kelley16972692006-10-16 20:04:18 +0100707 p = (unsigned char *)(buf+1);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100708 if (clid_len != 0)
Simon Kelley16972692006-10-16 20:04:18 +0100709 {
710 memcpy(p, lease->clid, clid_len);
711 p += clid_len;
712 }
Simon Kelley1f15b812009-10-13 17:49:32 +0100713 if (hostname_len != 0)
714 {
715 memcpy(p, hostname, hostname_len);
716 p += hostname_len;
717 }
Simon Kelley316e2732010-01-22 20:16:09 +0000718 if (ed_len != 0)
719 {
720 memcpy(p, lease->extradata, ed_len);
721 p += ed_len;
722 }
Simon Kelley16972692006-10-16 20:04:18 +0100723 bytes_in_buf = p - (unsigned char *)buf;
724}
725
Simon Kelleya9530962012-03-20 22:07:35 +0000726#ifdef HAVE_TFTP
727/* This nastily re-uses DHCP-fields for TFTP stuff */
728void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
729{
730 unsigned int filename_len;
731
732 /* no script */
733 if (daemon->helperfd == -1)
734 return;
735
736 filename_len = strlen(filename) + 1;
737 buff_alloc(sizeof(struct script_data) + filename_len);
738 memset(buf, 0, sizeof(struct script_data));
739
740 buf->action = ACTION_TFTP;
741 buf->hostname_len = filename_len;
742 buf->hwaddr_len = file_len;
743
744 if ((buf->flags = peer->sa.sa_family) == AF_INET)
745 buf->addr = peer->in.sin_addr;
746#ifdef HAVE_IPV6
747 else
748 memcpy(buf->hwaddr, &peer->in6.sin6_addr, IN6ADDRSZ);
749#endif
750
751 memcpy((unsigned char *)(buf+1), filename, filename_len);
752
753 bytes_in_buf = sizeof(struct script_data) + filename_len;
754}
755#endif
756
Simon Kelley16972692006-10-16 20:04:18 +0100757int helper_buf_empty(void)
758{
759 return bytes_in_buf == 0;
760}
761
Simon Kelley5aabfc72007-08-29 11:24:47 +0100762void helper_write(void)
Simon Kelley16972692006-10-16 20:04:18 +0100763{
764 ssize_t rc;
765
766 if (bytes_in_buf == 0)
767 return;
768
769 if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
770 {
771 if (bytes_in_buf != (size_t)rc)
772 memmove(buf, buf + rc, bytes_in_buf - rc);
773 bytes_in_buf -= rc;
774 }
775 else
776 {
777 if (errno == EAGAIN || errno == EINTR)
778 return;
779 bytes_in_buf = 0;
780 }
781}
782
Simon Kelley5aabfc72007-08-29 11:24:47 +0100783#endif
Simon Kelley16972692006-10-16 20:04:18 +0100784
785
Simon Kelleyc72daea2012-01-05 21:33:27 +0000786