--- embedaddon/dnsmasq/src/helper.c 2016/11/02 09:57:01 1.1.1.3 +++ embedaddon/dnsmasq/src/helper.c 2023/09/27 11:02:07 1.1.1.5 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ #ifdef HAVE_SCRIPT -/* This file has code to fork a helper process which recieves data via a pipe +/* This file has code to fork a helper process which receives data via a pipe shared with the main process and which is responsible for calling a script when DHCP leases change. @@ -64,11 +64,10 @@ struct script_data #ifdef HAVE_TFTP off_t file_len; #endif -#ifdef HAVE_IPV6 struct in6_addr addr6; -#endif #ifdef HAVE_DHCP6 - int iaid, vendorclass_count; + int vendorclass_count; + unsigned int iaid; #endif unsigned char hwaddr[DHCP_CHADDR_MAX]; char interface[IF_NAMESIZE]; @@ -82,7 +81,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, pid_t pid; int i, pipefd[2]; struct sigaction sigact; - + unsigned char *alloc_buff = NULL; + /* create the pipe through which the main program sends us commands, then fork our process. */ if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1) @@ -97,13 +97,14 @@ int create_helper(int event_fd, int err_fd, uid_t uid, return pipefd[1]; } - /* ignore SIGTERM, so that we can clean up when the main process gets hit + /* ignore SIGTERM and SIGINT, so that we can clean up when the main process gets hit and SIGALRM so that we can use sleep() */ sigact.sa_handler = SIG_IGN; sigact.sa_flags = 0; sigemptyset(&sigact.sa_mask); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGALRM, &sigact, NULL); + sigaction(SIGINT, &sigact, NULL); if (!option_bool(OPT_DEBUG) && uid != 0) { @@ -130,11 +131,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, Don't close err_fd, in case the lua-init fails. Note that we have to do this before lua init so we don't close any lua fds. */ - for (max_fd--; max_fd >= 0; max_fd--) - if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && - max_fd != STDIN_FILENO && max_fd != pipefd[0] && - max_fd != event_fd && max_fd != err_fd) - close(max_fd); + close_fds(max_fd, pipefd[0], event_fd, err_fd); #ifdef HAVE_LUASCRIPT if (daemon->luascript) @@ -187,10 +184,16 @@ int create_helper(int event_fd, int err_fd, uid_t uid, struct script_data data; char *p, *action_str, *hostname = NULL, *domain = NULL; unsigned char *buf = (unsigned char *)daemon->namebuff; - unsigned char *end, *extradata, *alloc_buff = NULL; + unsigned char *end, *extradata; int is6, err = 0; + int pipeout[2]; - free(alloc_buff); + /* Free rarely-allocated memory from previous iteration. */ + if (alloc_buff) + { + free(alloc_buff); + alloc_buff = NULL; + } /* we read zero bytes when pipe closed: this is our signal to exit */ if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1)) @@ -230,9 +233,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, is6 = (data.flags != AF_INET); data.action = ACTION_ARP; } - else - continue; - + else if (data.action == ACTION_RELAY_SNOOP) + { + is6 = 1; + action_str = "relay-snoop"; + } + else + continue; /* stringify MAC into dhcp_buff */ p = daemon->dhcp_buff; @@ -284,7 +291,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, char *dot; hostname = (char *)buf; hostname[data.hostname_len - 1] = 0; - if (data.action != ACTION_TFTP) + if (data.action != ACTION_TFTP && data.action != ACTION_RELAY_SNOOP) { if (!legal_hostname(hostname)) hostname = NULL; @@ -300,10 +307,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, if (!is6) inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN); -#ifdef HAVE_IPV6 else inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN); -#endif #ifdef HAVE_TFTP /* file length */ @@ -332,6 +337,24 @@ int create_helper(int event_fd, int err_fd, uid_t uid, lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ } } + else if (data.action == ACTION_RELAY_SNOOP) + { + lua_getglobal(lua, "snoop"); + if (lua_type(lua, -1) != LUA_TFUNCTION) + lua_pop(lua, 1); /* tftp function optional */ + else + { + lua_pushstring(lua, action_str); /* arg1 - action */ + lua_newtable(lua); /* arg2 - data table */ + lua_pushstring(lua, daemon->addrbuff); + lua_setfield(lua, -2, "client_address"); + lua_pushstring(lua, hostname); + lua_setfield(lua, -2, "prefix"); + lua_pushstring(lua, data.interface); + lua_setfield(lua, -2, "client_interface"); + lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ + } + } else if (data.action == ACTION_ARP) { lua_getglobal(lua, "arp"); @@ -398,6 +421,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, end = extradata + data.ed_len; buf = extradata; + + lua_pushnumber(lua, data.ed_len == 0 ? 1 : 0); + lua_setfield(lua, -2, "data_missing"); if (!is6) buf = grab_extradata_lua(buf, end, "vendor_class"); @@ -425,14 +451,17 @@ int create_helper(int event_fd, int err_fd, uid_t uid, buf = grab_extradata_lua(buf, end, "subscriber_id"); buf = grab_extradata_lua(buf, end, "remote_id"); } - + + buf = grab_extradata_lua(buf, end, "requested_options"); + buf = grab_extradata_lua(buf, end, "mud_url"); buf = grab_extradata_lua(buf, end, "tags"); if (is6) buf = grab_extradata_lua(buf, end, "relay_address"); else if (data.giaddr.s_addr != 0) { - lua_pushstring(lua, inet_ntoa(data.giaddr)); + inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN); + lua_pushstring(lua, daemon->dhcp_buff2); lua_setfield(lua, -2, "relay_address"); } @@ -472,16 +501,54 @@ int create_helper(int event_fd, int err_fd, uid_t uid, if (!daemon->lease_change_command) continue; + /* Pipe to capture stdout and stderr from script */ + if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1) + continue; + /* possible fork errors are all temporary resource problems */ while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM)) sleep(2); if (pid == -1) - continue; + { + if (!option_bool(OPT_DEBUG)) + { + close(pipeout[0]); + close(pipeout[1]); + } + continue; + } /* wait for child to complete */ if (pid != 0) { + if (!option_bool(OPT_DEBUG)) + { + FILE *fp; + + close(pipeout[1]); + + /* Read lines sent to stdout/err by the script and pass them back to be logged */ + if (!(fp = fdopen(pipeout[0], "r"))) + close(pipeout[0]); + else + { + while (fgets(daemon->packet, daemon->packet_buff_sz, fp)) + { + /* do not include new lines, log will append them */ + size_t len = strlen(daemon->packet); + if (len > 0) + { + --len; + if (daemon->packet[len] == '\n') + daemon->packet[len] = 0; + } + send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet); + } + fclose(fp); + } + } + /* reap our children's children, if necessary */ while (1) { @@ -504,8 +571,17 @@ int create_helper(int event_fd, int err_fd, uid_t uid, continue; } + + if (!option_bool(OPT_DEBUG)) + { + /* map stdout/stderr of script to pipeout */ + close(pipeout[0]); + dup2(pipeout[1], STDOUT_FILENO); + dup2(pipeout[1], STDERR_FILENO); + close(pipeout[1]); + } - if (data.action != ACTION_TFTP && data.action != ACTION_ARP) + if (data.action != ACTION_TFTP && data.action != ACTION_ARP && data.action != ACTION_RELAY_SNOOP) { #ifdef HAVE_DHCP6 my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err); @@ -528,6 +604,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, end = extradata + data.ed_len; buf = extradata; + + if (data.ed_len == 0) + my_setenv("DNSMASQ_DATA_MISSING", "1", &err); if (!is6) buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err); @@ -558,12 +637,19 @@ int create_helper(int event_fd, int err_fd, uid_t uid, buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err); } + buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err); + buf = grab_extradata(buf, end, "DNSMASQ_MUD_URL", &err); buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err); - + if (is6) buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err); - else - my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err); + else + { + const char *giaddr = NULL; + if (data.giaddr.s_addr != 0) + giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN); + my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err); + } for (i = 0; buf; i++) { @@ -579,12 +665,16 @@ int create_helper(int event_fd, int err_fd, uid_t uid, hostname = NULL; my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err); - } + } + /* we need to have the event_fd around if exec fails */ if ((i = fcntl(event_fd, F_GETFD)) != -1) fcntl(event_fd, F_SETFD, i | FD_CLOEXEC); close(pipefd[0]); + if (data.action == ACTION_RELAY_SNOOP) + strcpy(daemon->packet, data.interface); + p = strrchr(daemon->lease_change_command, '/'); if (err == 0) { @@ -755,6 +845,29 @@ void queue_script(int action, struct dhcp_lease *lease bytes_in_buf = p - (unsigned char *)buf; } +#ifdef HAVE_DHCP6 +void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len) +{ + /* no script */ + if (daemon->helperfd == -1) + return; + + inet_ntop(AF_INET6, prefix, daemon->addrbuff, ADDRSTRLEN); + + /* 5 for /nnn and zero on the end of the prefix. */ + buff_alloc(sizeof(struct script_data) + ADDRSTRLEN + 5); + memset(buf, 0, sizeof(struct script_data)); + + buf->action = ACTION_RELAY_SNOOP; + buf->addr6 = *client; + buf->hostname_len = sprintf((char *)(buf+1), "%s/%u", daemon->addrbuff, prefix_len) + 1; + + indextoname(daemon->dhcp6fd, if_index, buf->interface); + + bytes_in_buf = sizeof(struct script_data) + buf->hostname_len; +} +#endif + #ifdef HAVE_TFTP /* This nastily re-uses DHCP-fields for TFTP stuff */ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer) @@ -775,10 +888,8 @@ void queue_tftp(off_t file_len, char *filename, union if ((buf->flags = peer->sa.sa_family) == AF_INET) buf->addr = peer->in.sin_addr; -#ifdef HAVE_IPV6 else buf->addr6 = peer->in6.sin6_addr; -#endif memcpy((unsigned char *)(buf+1), filename, filename_len); @@ -786,7 +897,7 @@ void queue_tftp(off_t file_len, char *filename, union } #endif -void queue_arp(int action, unsigned char *mac, int maclen, int family, struct all_addr *addr) +void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr) { /* no script */ if (daemon->helperfd == -1) @@ -799,11 +910,9 @@ void queue_arp(int action, unsigned char *mac, int mac buf->hwaddr_len = maclen; buf->hwaddr_type = ARPHRD_ETHER; if ((buf->flags = family) == AF_INET) - buf->addr = addr->addr.addr4; -#ifdef HAVE_IPV6 + buf->addr = addr->addr4; else - buf->addr6 = addr->addr.addr6; -#endif + buf->addr6 = addr->addr6; memcpy(buf->hwaddr, mac, maclen); @@ -836,7 +945,4 @@ void helper_write(void) } } -#endif - - - +#endif /* HAVE_SCRIPT */