1 From 87444dc6977b61096127dcdfe87dc6cf2c0167d6 Mon Sep 17 00:00:00 2001
2 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
3 Date: Sun, 16 Apr 2017 20:20:08 +0100
4 Subject: [PATCH] Capture and log STDOUT and STDERR output from dhcp-script.
6 (cherry picked from commit c77fb9d8f09d136fa71bde2469c4fd11cefa6f4a)
8 Compile-time check on buffer sizes for leasefile parsing code.
10 (cherry picked from commit bf4e62c19e619f7edf8d03d58d33a5752f190bfd)
12 Improve error handling with shcp-script "init" mode.
14 (cherry picked from commit 3a8b0f6fccf464b1ec6d24c0e00e540ab2b17705)
16 Tweak logging introduced in 3a8b0f6fccf464b1ec6d24c0e00e540ab2b17705
18 (cherry picked from commit efff74c1aea14757ce074db28e02671c7f7bb5f5)
20 Don't die() on failing to parse lease-script output.
22 (cherry picked from commit 05f76dab89d5b879519a4f45b0cccaa1fc3d162d)
25 src/dhcp-common.c | 16 +++---
26 src/dhcp-protocol.h | 4 ++
28 src/dnsmasq.h | 54 +++++++++---------
29 src/helper.c | 56 +++++++++++++++++-
30 src/lease.c | 159 +++++++++++++++++++++++++++++++---------------------
33 9 files changed, 202 insertions(+), 105 deletions(-)
35 diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
36 index 0521534..97d0a4f 100644
39 @@ -1551,8 +1551,8 @@ database.
42 All file descriptors are
43 -closed except stdin, stdout and stderr which are open to /dev/null
44 -(except in debug mode).
45 +closed except stdin, which is open to /dev/null, and stdout and stderr which capture output for logging by dnsmasq.
46 +(In debug mode, stdio, stdout and stderr file are left as those inherited from the invoker of dnsmasq).
48 The script is not invoked concurrently: at most one instance
49 of the script is ever running (dnsmasq waits for an instance of script to exit
50 diff --git a/src/dhcp-common.c b/src/dhcp-common.c
51 index 08528e8..ecc752b 100644
52 --- a/src/dhcp-common.c
53 +++ b/src/dhcp-common.c
56 void dhcp_common_init(void)
58 - /* These each hold a DHCP option max size 255
59 - and get a terminating zero added */
60 - daemon->dhcp_buff = safe_malloc(256);
61 - daemon->dhcp_buff2 = safe_malloc(256);
62 - daemon->dhcp_buff3 = safe_malloc(256);
63 + /* These each hold a DHCP option max size 255
64 + and get a terminating zero added */
65 + daemon->dhcp_buff = safe_malloc(DHCP_BUFF_SZ);
66 + daemon->dhcp_buff2 = safe_malloc(DHCP_BUFF_SZ);
67 + daemon->dhcp_buff3 = safe_malloc(DHCP_BUFF_SZ);
69 /* dhcp_packet is used by v4 and v6, outpacket only by v6
70 sizeof(struct dhcp_packet) is as good an initial size as any,
71 @@ -855,14 +855,14 @@ void log_context(int family, struct dhcp_context *context)
72 if (context->flags & CONTEXT_RA_STATELESS)
74 if (context->flags & CONTEXT_TEMPLATE)
75 - strncpy(daemon->dhcp_buff, context->template_interface, 256);
76 + strncpy(daemon->dhcp_buff, context->template_interface, DHCP_BUFF_SZ);
78 strcpy(daemon->dhcp_buff, daemon->addrbuff);
82 - inet_ntop(family, start, daemon->dhcp_buff, 256);
83 - inet_ntop(family, end, daemon->dhcp_buff3, 256);
84 + inet_ntop(family, start, daemon->dhcp_buff, DHCP_BUFF_SZ);
85 + inet_ntop(family, end, daemon->dhcp_buff3, DHCP_BUFF_SZ);
86 my_syslog(MS_DHCP | LOG_INFO,
87 (context->flags & CONTEXT_RA_STATELESS) ?
88 _("%s stateless on %s%.0s%.0s%s") :
89 diff --git a/src/dhcp-protocol.h b/src/dhcp-protocol.h
90 index a31d829..0ea449b 100644
91 --- a/src/dhcp-protocol.h
92 +++ b/src/dhcp-protocol.h
94 #define DHCP_CLIENT_ALTPORT 1068
97 +/* These each hold a DHCP option max size 255
98 + and get a terminating zero added */
99 +#define DHCP_BUFF_SZ 256
101 #define BOOTREQUEST 1
103 #define DHCP_COOKIE 0x63825363
104 diff --git a/src/dnsmasq.c b/src/dnsmasq.c
105 index 045ec53..9cd4052 100644
108 @@ -1294,6 +1294,7 @@ static void async_event(int pipe, time_t now)
109 daemon->tcp_pids[i] = 0;
112 +#if defined(HAVE_SCRIPT)
114 my_syslog(LOG_WARNING, _("script process killed by signal %d"), ev.data);
116 @@ -1307,12 +1308,19 @@ static void async_event(int pipe, time_t now)
117 daemon->lease_change_command, strerror(ev.data));
120 + case EVENT_SCRIPT_LOG:
121 + my_syslog(MS_SCRIPT | LOG_DEBUG, "%s", msg ? msg : "");
126 /* necessary for fatal errors in helper */
130 fatal_event(&ev, msg);
135 /* Note: this may leave TCP-handling processes with the old file still open.
136 diff --git a/src/dnsmasq.h b/src/dnsmasq.h
137 index 1896a64..0cfd3c6 100644
140 @@ -145,30 +145,31 @@ struct event_desc {
141 int event, data, msg_sz;
144 -#define EVENT_RELOAD 1
145 -#define EVENT_DUMP 2
146 -#define EVENT_ALARM 3
147 -#define EVENT_TERM 4
148 -#define EVENT_CHILD 5
149 -#define EVENT_REOPEN 6
150 -#define EVENT_EXITED 7
151 -#define EVENT_KILLED 8
152 -#define EVENT_EXEC_ERR 9
153 -#define EVENT_PIPE_ERR 10
154 -#define EVENT_USER_ERR 11
155 -#define EVENT_CAP_ERR 12
156 -#define EVENT_PIDFILE 13
157 -#define EVENT_HUSER_ERR 14
158 -#define EVENT_GROUP_ERR 15
159 -#define EVENT_DIE 16
160 -#define EVENT_LOG_ERR 17
161 -#define EVENT_FORK_ERR 18
162 -#define EVENT_LUA_ERR 19
163 -#define EVENT_TFTP_ERR 20
164 -#define EVENT_INIT 21
165 -#define EVENT_NEWADDR 22
166 -#define EVENT_NEWROUTE 23
167 -#define EVENT_TIME_ERR 24
168 +#define EVENT_RELOAD 1
169 +#define EVENT_DUMP 2
170 +#define EVENT_ALARM 3
171 +#define EVENT_TERM 4
172 +#define EVENT_CHILD 5
173 +#define EVENT_REOPEN 6
174 +#define EVENT_EXITED 7
175 +#define EVENT_KILLED 8
176 +#define EVENT_EXEC_ERR 9
177 +#define EVENT_PIPE_ERR 10
178 +#define EVENT_USER_ERR 11
179 +#define EVENT_CAP_ERR 12
180 +#define EVENT_PIDFILE 13
181 +#define EVENT_HUSER_ERR 14
182 +#define EVENT_GROUP_ERR 15
183 +#define EVENT_DIE 16
184 +#define EVENT_LOG_ERR 17
185 +#define EVENT_FORK_ERR 18
186 +#define EVENT_LUA_ERR 19
187 +#define EVENT_TFTP_ERR 20
188 +#define EVENT_INIT 21
189 +#define EVENT_NEWADDR 22
190 +#define EVENT_NEWROUTE 23
191 +#define EVENT_TIME_ERR 24
192 +#define EVENT_SCRIPT_LOG 25
196 @@ -242,8 +243,9 @@ struct event_desc {
198 /* extra flags for my_syslog, we use a couple of facilities since they are known
199 not to occupy the same bits as priorities, no matter how syslog.h is set up. */
200 -#define MS_TFTP LOG_USER
201 -#define MS_DHCP LOG_DAEMON
202 +#define MS_TFTP LOG_USER
203 +#define MS_DHCP LOG_DAEMON
204 +#define MS_SCRIPT LOG_MAIL
208 diff --git a/src/helper.c b/src/helper.c
209 index 9c37e37..de31383 100644
213 along with this program. If not, see <http://www.gnu.org/licenses/>.
220 @@ -135,7 +136,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
221 max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
222 max_fd != event_fd && max_fd != err_fd)
226 #ifdef HAVE_LUASCRIPT
227 if (daemon->luascript)
229 @@ -189,6 +190,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
230 unsigned char *buf = (unsigned char *)daemon->namebuff;
231 unsigned char *end, *extradata, *alloc_buff = NULL;
237 @@ -472,16 +474,54 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
238 if (!daemon->lease_change_command)
241 + /* Pipe to capture stdout and stderr from script */
242 + if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1)
245 /* possible fork errors are all temporary resource problems */
246 while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
252 + if (!option_bool(OPT_DEBUG))
260 /* wait for child to complete */
263 + if (!option_bool(OPT_DEBUG))
269 + /* Read lines sent to stdout/err by the script and pass them back to be logged */
270 + if (!(fp = fdopen(pipeout[0], "r")))
274 + while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
276 + /* do not include new lines, log will append them */
277 + size_t len = strlen(daemon->packet);
281 + if (daemon->packet[len] == '\n')
282 + daemon->packet[len] = 0;
284 + send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
290 /* reap our children's children, if necessary */
293 @@ -504,6 +544,15 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
298 + if (!option_bool(OPT_DEBUG))
300 + /* map stdout/stderr of script to pipeout */
302 + dup2(pipeout[1], STDOUT_FILENO);
303 + dup2(pipeout[1], STDERR_FILENO);
307 if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
309 @@ -579,7 +628,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
312 my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
316 /* we need to have the event_fd around if exec fails */
317 if ((i = fcntl(event_fd, F_GETFD)) != -1)
318 fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
319 diff --git a/src/lease.c b/src/lease.c
320 index 20cac90..64047f9 100644
324 static struct dhcp_lease *leases = NULL, *old_leases = NULL;
325 static int dns_dirty, file_dirty, leases_left;
327 -void lease_init(time_t now)
328 +static int read_leases(time_t now, FILE *leasestream)
331 struct all_addr addr;
332 struct dhcp_lease *lease;
333 int clid_len, hw_len, hw_type;
336 - leases_left = daemon->dhcp_max;
338 - if (option_bool(OPT_LEASE_RO))
340 - /* run "<lease_change_script> init" once to get the
341 - initial state of the database. If leasefile-ro is
342 - set without a script, we just do without any
345 - if (daemon->lease_change_command)
347 - strcpy(daemon->dhcp_buff, daemon->lease_change_command);
348 - strcat(daemon->dhcp_buff, " init");
349 - leasestream = popen(daemon->dhcp_buff, "r");
353 + char *domain = NULL;
355 + *daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0';
357 + /* client-id max length is 255 which is 255*2 digits + 254 colons
358 + borrow DNS packet buffer which is always larger than 1000 bytes
360 + Check various buffers are big enough for the code below */
362 +#if (DHCP_BUFF_SZ < 255) || (MAXDNAME < 64) || (PACKETSZ+MAXDNAME+RRFIXEDSZ < 764)
363 +# error Buffer size breakage in leasefile parsing.
366 - file_dirty = dns_dirty = 0;
373 - /* NOTE: need a+ mode to create file if it doesn't exist */
374 - leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
377 - die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
379 - /* a+ mode leaves pointer at end. */
380 - rewind(leasestream);
383 - /* client-id max length is 255 which is 255*2 digits + 254 colons
384 - borrow DNS packet buffer which is always larger than 1000 bytes */
386 - while (fscanf(leasestream, "%255s %255s", daemon->dhcp_buff3, daemon->dhcp_buff2) == 2)
387 + while ((items=fscanf(leasestream, "%255s %255s", daemon->dhcp_buff3, daemon->dhcp_buff2)) == 2)
389 + *daemon->namebuff = *daemon->dhcp_buff = *daemon->packet = '\0';
390 + hw_len = hw_type = clid_len = 0;
393 if (strcmp(daemon->dhcp_buff3, "duid") == 0)
395 daemon->duid_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, 130, NULL, NULL);
396 + if (daemon->duid_len < 0)
398 daemon->duid = safe_malloc(daemon->duid_len);
399 memcpy(daemon->duid, daemon->dhcp_buff2, daemon->duid_len);
404 - ei = atol(daemon->dhcp_buff3);
406 if (fscanf(leasestream, " %64s %255s %764s",
407 daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3)
412 - if (strcmp(daemon->packet, "*") != 0)
413 - clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
415 - if (inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4) &&
416 - (lease = lease4_allocate(addr.addr.addr4)))
417 + if (inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4))
419 + if ((lease = lease4_allocate(addr.addr.addr4)))
420 + domain = get_domain(lease->addr);
422 hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
423 /* For backwards compatibility, no explict MAC address type means ether. */
424 if (hw_type == 0 && hw_len != 0)
425 hw_type = ARPHRD_ETHER;
427 - lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet,
428 - hw_len, hw_type, clid_len, now, 0);
430 - if (strcmp(daemon->dhcp_buff, "*") != 0)
431 - lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL);
434 else if (inet_pton(AF_INET6, daemon->namebuff, &addr.addr.addr6))
436 char *s = daemon->dhcp_buff2;
437 int lease_type = LEASE_NA;
442 @@ -116,23 +84,30 @@ void lease_init(time_t now)
446 - iaid = strtoul(s, NULL, 10);
448 if ((lease = lease6_allocate(&addr.addr.addr6, lease_type)))
450 - lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, 0, clid_len, now, 0);
451 - lease_set_iaid(lease, iaid);
452 - if (strcmp(daemon->dhcp_buff, "*") != 0)
453 - lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL);
454 + lease_set_iaid(lease, strtoul(s, NULL, 10));
455 + domain = get_domain6((struct in6_addr *)lease->hwaddr);
464 die (_("too many stored leases"), NULL, EC_MISC);
467 + if (strcmp(daemon->packet, "*") != 0)
468 + clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
470 + lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet,
471 + hw_len, hw_type, clid_len, now, 0);
473 + if (strcmp(daemon->dhcp_buff, "*") != 0)
474 + lease_set_hostname(lease, daemon->dhcp_buff, 0, domain, NULL);
476 + ei = atol(daemon->dhcp_buff3);
478 #ifdef HAVE_BROKEN_RTC
480 lease->expires = (time_t)ei + now;
481 @@ -148,7 +123,62 @@ void lease_init(time_t now)
482 /* set these correctly: the "old" events are generated later from
483 the startup synthesised SIGHUP. */
484 lease->flags &= ~(LEASE_NEW | LEASE_CHANGED);
486 + *daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0';
489 + return (items == 0 || items == EOF);
492 +void lease_init(time_t now)
496 + leases_left = daemon->dhcp_max;
498 + if (option_bool(OPT_LEASE_RO))
500 + /* run "<lease_change_script> init" once to get the
501 + initial state of the database. If leasefile-ro is
502 + set without a script, we just do without any
505 + if (daemon->lease_change_command)
507 + strcpy(daemon->dhcp_buff, daemon->lease_change_command);
508 + strcat(daemon->dhcp_buff, " init");
509 + leasestream = popen(daemon->dhcp_buff, "r");
514 + file_dirty = dns_dirty = 0;
521 + /* NOTE: need a+ mode to create file if it doesn't exist */
522 + leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
525 + die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
527 + /* a+ mode leaves pointer at end. */
528 + rewind(leasestream);
533 + if (!read_leases(now, leasestream))
534 + my_syslog(MS_DHCP | LOG_ERR, _("failed to parse lease database, invalid line: %s %s %s %s ..."),
535 + daemon->dhcp_buff3, daemon->dhcp_buff2,
536 + daemon->namebuff, daemon->dhcp_buff);
538 + if (ferror(leasestream))
539 + die(_("failed to read lease file %s: %s"), daemon->lease_file, EC_FILE);
543 if (!daemon->lease_stream)
544 @@ -162,6 +192,7 @@ void lease_init(time_t now)
546 else if (WEXITSTATUS(rc) == 126)
549 die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE);
552 diff --git a/src/log.c b/src/log.c
553 index 8e66629..5fc860b 100644
556 @@ -288,7 +288,9 @@ void my_syslog(int priority, const char *format, ...)
558 else if ((LOG_FACMASK & priority) == MS_DHCP)
561 + else if ((LOG_FACMASK & priority) == MS_SCRIPT)
565 priority = LOG_PRI(priority);
567 diff --git a/src/rfc3315.c b/src/rfc3315.c
568 index 3f4d69c..a3715cd 100644
571 @@ -1975,7 +1975,7 @@ static void log6_packet(struct state *state, char *type, struct in6_addr *addr,
575 - inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, 255);
576 + inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, DHCP_BUFF_SZ - 1);
577 strcat(daemon->dhcp_buff2, " ");