File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / src / helper.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:02:07 2023 UTC (9 months, 2 weeks ago) by misho
Branches: elwix, dnsmasq, MAIN
CVS tags: v8_2p1, HEAD
Version 8.2p1

    1: /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
    2: 
    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
    5:    the Free Software Foundation; version 2 dated June, 1991, or
    6:    (at your option) version 3 dated 29 June, 2007.
    7:  
    8:    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.
   12:      
   13:    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/>.
   15: */
   16: 
   17: #include "dnsmasq.h"
   18: 
   19: #ifdef HAVE_SCRIPT
   20: 
   21: /* This file has code to fork a helper process which receives 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: 
   33: static void my_setenv(const char *name, const char *value, int *error);
   34: static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end,  char *env, int *err);
   35: 
   36: #ifdef HAVE_LUASCRIPT
   37: #define LUA_COMPAT_ALL
   38: #include <lua.h>  
   39: #include <lualib.h>  
   40: #include <lauxlib.h>  
   41: 
   42: #ifndef lua_open
   43: #define lua_open()     luaL_newstate()
   44: #endif
   45: 
   46: lua_State *lua;
   47: 
   48: static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
   49: #endif
   50: 
   51: 
   52: struct script_data
   53: {
   54:   int flags;
   55:   int action, hwaddr_len, hwaddr_type;
   56:   int clid_len, hostname_len, ed_len;
   57:   struct in_addr addr, giaddr;
   58:   unsigned int remaining_time;
   59: #ifdef HAVE_BROKEN_RTC
   60:   unsigned int length;
   61: #else
   62:   time_t expires;
   63: #endif
   64: #ifdef HAVE_TFTP
   65:   off_t file_len;
   66: #endif
   67:   struct in6_addr addr6;
   68: #ifdef HAVE_DHCP6
   69:   int vendorclass_count;
   70:   unsigned int iaid;
   71: #endif
   72:   unsigned char hwaddr[DHCP_CHADDR_MAX];
   73:   char interface[IF_NAMESIZE];
   74: };
   75: 
   76: static struct script_data *buf = NULL;
   77: static size_t bytes_in_buf = 0, buf_size = 0;
   78: 
   79: int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
   80: {
   81:   pid_t pid;
   82:   int i, pipefd[2];
   83:   struct sigaction sigact;
   84:   unsigned char *alloc_buff = NULL;
   85:   
   86:   /* create the pipe through which the main program sends us commands,
   87:      then fork our process. */
   88:   if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
   89:     {
   90:       send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
   91:       _exit(0);
   92:     }
   93: 
   94:   if (pid != 0)
   95:     {
   96:       close(pipefd[0]); /* close reader side */
   97:       return pipefd[1];
   98:     }
   99: 
  100:   /* ignore SIGTERM and SIGINT, so that we can clean up when the main process gets hit
  101:      and SIGALRM so that we can use sleep() */
  102:   sigact.sa_handler = SIG_IGN;
  103:   sigact.sa_flags = 0;
  104:   sigemptyset(&sigact.sa_mask);
  105:   sigaction(SIGTERM, &sigact, NULL);
  106:   sigaction(SIGALRM, &sigact, NULL);
  107:   sigaction(SIGINT, &sigact, NULL);
  108: 
  109:   if (!option_bool(OPT_DEBUG) && uid != 0)
  110:     {
  111:       gid_t dummy;
  112:       if (setgroups(0, &dummy) == -1 || 
  113: 	  setgid(gid) == -1 || 
  114: 	  setuid(uid) == -1)
  115: 	{
  116: 	  if (option_bool(OPT_NO_FORK))
  117: 	    /* send error to daemon process if no-fork */
  118: 	    send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
  119: 	  else
  120: 	    {
  121: 	      /* kill daemon */
  122: 	      send_event(event_fd, EVENT_DIE, 0, NULL);
  123: 	      /* return error */
  124: 	      send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
  125: 	    }
  126: 	  _exit(0);
  127: 	}
  128:     }
  129: 
  130:   /* close all the sockets etc, we don't need them here. 
  131:      Don't close err_fd, in case the lua-init fails.
  132:      Note that we have to do this before lua init
  133:      so we don't close any lua fds. */
  134:   close_fds(max_fd, pipefd[0], event_fd, err_fd);
  135:   
  136: #ifdef HAVE_LUASCRIPT
  137:   if (daemon->luascript)
  138:     {
  139:       const char *lua_err = NULL;
  140:       lua = lua_open();
  141:       luaL_openlibs(lua);
  142:       
  143:       /* get Lua to load our script file */
  144:       if (luaL_dofile(lua, daemon->luascript) != 0)
  145: 	lua_err = lua_tostring(lua, -1);
  146:       else
  147: 	{
  148: 	  lua_getglobal(lua, "lease");
  149: 	  if (lua_type(lua, -1) != LUA_TFUNCTION) 
  150: 	    lua_err = _("lease() function missing in Lua script");
  151: 	}
  152:       
  153:       if (lua_err)
  154: 	{
  155: 	  if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
  156: 	    /* send error to daemon process if no-fork */
  157: 	    send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
  158: 	  else
  159: 	    {
  160: 	      /* kill daemon */
  161: 	      send_event(event_fd, EVENT_DIE, 0, NULL);
  162: 	      /* return error */
  163: 	      send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
  164: 	    }
  165: 	  _exit(0);
  166: 	}
  167:       
  168:       lua_pop(lua, 1);  /* remove nil from stack */
  169:       lua_getglobal(lua, "init");
  170:       if (lua_type(lua, -1) == LUA_TFUNCTION)
  171: 	lua_call(lua, 0, 0);
  172:       else
  173: 	lua_pop(lua, 1);  /* remove nil from stack */	
  174:     }
  175: #endif
  176: 
  177:   /* All init done, close our copy of the error pipe, so that main process can return */
  178:   if (err_fd != -1)
  179:     close(err_fd);
  180:     
  181:   /* loop here */
  182:   while(1)
  183:     {
  184:       struct script_data data;
  185:       char *p, *action_str, *hostname = NULL, *domain = NULL;
  186:       unsigned char *buf = (unsigned char *)daemon->namebuff;
  187:       unsigned char *end, *extradata;
  188:       int is6, err = 0;
  189:       int pipeout[2];
  190: 
  191:       /* Free rarely-allocated memory from previous iteration. */
  192:       if (alloc_buff)
  193: 	{
  194: 	  free(alloc_buff);
  195: 	  alloc_buff = NULL;
  196: 	}
  197:       
  198:       /* we read zero bytes when pipe closed: this is our signal to exit */ 
  199:       if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
  200: 	{
  201: #ifdef HAVE_LUASCRIPT
  202: 	  if (daemon->luascript)
  203: 	    {
  204: 	      lua_getglobal(lua, "shutdown");
  205: 	      if (lua_type(lua, -1) == LUA_TFUNCTION)
  206: 		lua_call(lua, 0, 0);
  207: 	    }
  208: #endif
  209: 	  _exit(0);
  210: 	}
  211:  
  212:       is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
  213:       
  214:       if (data.action == ACTION_DEL)
  215: 	action_str = "del";
  216:       else if (data.action == ACTION_ADD)
  217: 	action_str = "add";
  218:       else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
  219: 	action_str = "old";
  220:       else if (data.action == ACTION_TFTP)
  221: 	{
  222: 	  action_str = "tftp";
  223: 	  is6 = (data.flags != AF_INET);
  224: 	}
  225:       else if (data.action == ACTION_ARP)
  226: 	{
  227: 	  action_str = "arp-add";
  228: 	  is6 = (data.flags != AF_INET);
  229: 	}
  230:        else if (data.action == ACTION_ARP_DEL)
  231: 	{
  232: 	  action_str = "arp-del";
  233: 	  is6 = (data.flags != AF_INET);
  234: 	  data.action = ACTION_ARP;
  235: 	}
  236:        else if (data.action == ACTION_RELAY_SNOOP)
  237: 	 {
  238: 	   is6 = 1;
  239: 	   action_str = "relay-snoop";
  240: 	 }
  241:        else
  242: 	 continue;
  243:       	
  244:       /* stringify MAC into dhcp_buff */
  245:       p = daemon->dhcp_buff;
  246:       if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0) 
  247: 	p += sprintf(p, "%.2x-", data.hwaddr_type);
  248:       for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
  249: 	{
  250: 	  p += sprintf(p, "%.2x", data.hwaddr[i]);
  251: 	  if (i != data.hwaddr_len - 1)
  252: 	    p += sprintf(p, ":");
  253: 	}
  254:       
  255:       /* supplied data may just exceed normal buffer (unlikely) */
  256:       if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME && 
  257: 	  !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
  258: 	continue;
  259:       
  260:       if (!read_write(pipefd[0], buf, 
  261: 		      data.hostname_len + data.ed_len + data.clid_len, 1))
  262: 	continue;
  263: 
  264:       /* CLID into packet */
  265:       for (p = daemon->packet, i = 0; i < data.clid_len; i++)
  266: 	{
  267: 	  p += sprintf(p, "%.2x", buf[i]);
  268: 	  if (i != data.clid_len - 1) 
  269: 	      p += sprintf(p, ":");
  270: 	}
  271: 
  272: #ifdef HAVE_DHCP6
  273:       if (is6)
  274: 	{
  275: 	  /* or IAID and server DUID for IPv6 */
  276: 	  sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.iaid);	
  277: 	  for (p = daemon->dhcp_packet.iov_base, i = 0; i < daemon->duid_len; i++)
  278: 	    {
  279: 	      p += sprintf(p, "%.2x", daemon->duid[i]);
  280: 	      if (i != daemon->duid_len - 1) 
  281: 		p += sprintf(p, ":");
  282: 	    }
  283: 
  284: 	}
  285: #endif
  286: 
  287:       buf += data.clid_len;
  288: 
  289:       if (data.hostname_len != 0)
  290: 	{
  291: 	  char *dot;
  292: 	  hostname = (char *)buf;
  293: 	  hostname[data.hostname_len - 1] = 0;
  294: 	  if (data.action != ACTION_TFTP && data.action != ACTION_RELAY_SNOOP)
  295: 	    {
  296: 	      if (!legal_hostname(hostname))
  297: 		hostname = NULL;
  298: 	      else if ((dot = strchr(hostname, '.')))
  299: 		{
  300: 		  domain = dot+1;
  301: 		  *dot = 0;
  302: 		} 
  303: 	    }
  304: 	}
  305:     
  306:       extradata = buf + data.hostname_len;
  307:     
  308:       if (!is6)
  309: 	inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
  310:       else
  311: 	inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
  312: 
  313: #ifdef HAVE_TFTP
  314:       /* file length */
  315:       if (data.action == ACTION_TFTP)
  316: 	sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
  317: #endif
  318: 
  319: #ifdef HAVE_LUASCRIPT
  320:       if (daemon->luascript)
  321: 	{
  322: 	  if (data.action == ACTION_TFTP)
  323: 	    {
  324: 	      lua_getglobal(lua, "tftp"); 
  325: 	      if (lua_type(lua, -1) != LUA_TFUNCTION)
  326: 		lua_pop(lua, 1); /* tftp function optional */
  327: 	      else
  328: 		{
  329: 		  lua_pushstring(lua, action_str); /* arg1 - action */
  330: 		  lua_newtable(lua);               /* arg2 - data table */
  331: 		  lua_pushstring(lua, daemon->addrbuff);
  332: 		  lua_setfield(lua, -2, "destination_address");
  333: 		  lua_pushstring(lua, hostname);
  334: 		  lua_setfield(lua, -2, "file_name"); 
  335: 		  lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
  336: 		  lua_setfield(lua, -2, "file_size");
  337: 		  lua_call(lua, 2, 0);	/* pass 2 values, expect 0 */
  338: 		}
  339: 	    }
  340: 	  else if (data.action == ACTION_RELAY_SNOOP)
  341: 	    {
  342: 	      lua_getglobal(lua, "snoop"); 
  343: 	      if (lua_type(lua, -1) != LUA_TFUNCTION)
  344: 		lua_pop(lua, 1); /* tftp function optional */
  345: 	      else
  346: 		{
  347: 		  lua_pushstring(lua, action_str); /* arg1 - action */
  348: 		  lua_newtable(lua);               /* arg2 - data table */
  349: 		  lua_pushstring(lua, daemon->addrbuff);
  350: 		  lua_setfield(lua, -2, "client_address");
  351: 		  lua_pushstring(lua, hostname);
  352: 		  lua_setfield(lua, -2, "prefix"); 
  353: 		  lua_pushstring(lua, data.interface);
  354: 		  lua_setfield(lua, -2, "client_interface");
  355: 		  lua_call(lua, 2, 0);	/* pass 2 values, expect 0 */
  356: 		}
  357: 	    }
  358: 	  else if (data.action == ACTION_ARP)
  359: 	    {
  360: 	      lua_getglobal(lua, "arp"); 
  361: 	      if (lua_type(lua, -1) != LUA_TFUNCTION)
  362: 		lua_pop(lua, 1); /* arp function optional */
  363: 	      else
  364: 		{
  365: 		  lua_pushstring(lua, action_str); /* arg1 - action */
  366: 		  lua_newtable(lua);               /* arg2 - data table */
  367: 		  lua_pushstring(lua, daemon->addrbuff);
  368: 		  lua_setfield(lua, -2, "client_address");
  369: 		  lua_pushstring(lua, daemon->dhcp_buff);
  370: 		  lua_setfield(lua, -2, "mac_address");
  371: 		  lua_call(lua, 2, 0);	/* pass 2 values, expect 0 */
  372: 		}
  373: 	    }
  374: 	  else
  375: 	    {
  376: 	      lua_getglobal(lua, "lease");     /* function to call */
  377: 	      lua_pushstring(lua, action_str); /* arg1 - action */
  378: 	      lua_newtable(lua);               /* arg2 - data table */
  379: 	      
  380: 	      if (is6)
  381: 		{
  382: 		  lua_pushstring(lua, daemon->packet);
  383: 		  lua_setfield(lua, -2, "client_duid");
  384: 		  lua_pushstring(lua, daemon->dhcp_packet.iov_base);
  385: 		  lua_setfield(lua, -2, "server_duid");
  386: 		  lua_pushstring(lua, daemon->dhcp_buff3);
  387: 		  lua_setfield(lua, -2, "iaid");
  388: 		}
  389: 	      
  390: 	      if (!is6 && data.clid_len != 0)
  391: 		{
  392: 		  lua_pushstring(lua, daemon->packet);
  393: 		  lua_setfield(lua, -2, "client_id");
  394: 		}
  395: 	      
  396: 	      if (strlen(data.interface) != 0)
  397: 		{
  398: 		  lua_pushstring(lua, data.interface);
  399: 		  lua_setfield(lua, -2, "interface");
  400: 		}
  401: 	      
  402: #ifdef HAVE_BROKEN_RTC	
  403: 	      lua_pushnumber(lua, data.length);
  404: 	      lua_setfield(lua, -2, "lease_length");
  405: #else
  406: 	      lua_pushnumber(lua, data.expires);
  407: 	      lua_setfield(lua, -2, "lease_expires");
  408: #endif
  409: 	      
  410: 	      if (hostname)
  411: 		{
  412: 		  lua_pushstring(lua, hostname);
  413: 		  lua_setfield(lua, -2, "hostname");
  414: 		}
  415: 	      
  416: 	      if (domain)
  417: 		{
  418: 		  lua_pushstring(lua, domain);
  419: 		  lua_setfield(lua, -2, "domain");
  420: 		}
  421: 	      
  422: 	      end = extradata + data.ed_len;
  423: 	      buf = extradata;
  424: 
  425: 	      lua_pushnumber(lua, data.ed_len == 0 ? 1 : 0);
  426: 	      lua_setfield(lua, -2, "data_missing");
  427: 	      
  428: 	      if (!is6)
  429: 		buf = grab_extradata_lua(buf, end, "vendor_class");
  430: #ifdef HAVE_DHCP6
  431: 	      else  if (data.vendorclass_count != 0)
  432: 		{
  433: 		  sprintf(daemon->dhcp_buff2, "vendor_class_id");
  434: 		  buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
  435: 		  for (i = 0; i < data.vendorclass_count - 1; i++)
  436: 		    {
  437: 		      sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
  438: 		      buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
  439: 		    }
  440: 		}
  441: #endif
  442: 	      
  443: 	      buf = grab_extradata_lua(buf, end, "supplied_hostname");
  444: 	      
  445: 	      if (!is6)
  446: 		{
  447: 		  buf = grab_extradata_lua(buf, end, "cpewan_oui");
  448: 		  buf = grab_extradata_lua(buf, end, "cpewan_serial");   
  449: 		  buf = grab_extradata_lua(buf, end, "cpewan_class");
  450: 		  buf = grab_extradata_lua(buf, end, "circuit_id");
  451: 		  buf = grab_extradata_lua(buf, end, "subscriber_id");
  452: 		  buf = grab_extradata_lua(buf, end, "remote_id");
  453: 		}
  454: 
  455: 	      buf = grab_extradata_lua(buf, end, "requested_options");
  456: 	      buf = grab_extradata_lua(buf, end, "mud_url");
  457: 	      buf = grab_extradata_lua(buf, end, "tags");
  458: 	      
  459: 	      if (is6)
  460: 		buf = grab_extradata_lua(buf, end, "relay_address");
  461: 	      else if (data.giaddr.s_addr != 0)
  462: 		{
  463: 		  inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN);
  464: 		  lua_pushstring(lua, daemon->dhcp_buff2);
  465: 		  lua_setfield(lua, -2, "relay_address");
  466: 		}
  467: 	      
  468: 	      for (i = 0; buf; i++)
  469: 		{
  470: 		  sprintf(daemon->dhcp_buff2, "user_class%i", i);
  471: 		  buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
  472: 		}
  473: 	      
  474: 	      if (data.action != ACTION_DEL && data.remaining_time != 0)
  475: 		{
  476: 		  lua_pushnumber(lua, data.remaining_time);
  477: 		  lua_setfield(lua, -2, "time_remaining");
  478: 		}
  479: 	      
  480: 	      if (data.action == ACTION_OLD_HOSTNAME && hostname)
  481: 		{
  482: 		  lua_pushstring(lua, hostname);
  483: 		  lua_setfield(lua, -2, "old_hostname");
  484: 		}
  485: 	      
  486: 	      if (!is6 || data.hwaddr_len != 0)
  487: 		{
  488: 		  lua_pushstring(lua, daemon->dhcp_buff);
  489: 		  lua_setfield(lua, -2, "mac_address");
  490: 		}
  491: 	      
  492: 	      lua_pushstring(lua, daemon->addrbuff);
  493: 	      lua_setfield(lua, -2, "ip_address");
  494: 	    
  495: 	      lua_call(lua, 2, 0);	/* pass 2 values, expect 0 */
  496: 	    }
  497: 	}
  498: #endif
  499: 
  500:       /* no script, just lua */
  501:       if (!daemon->lease_change_command)
  502: 	continue;
  503: 
  504:       /* Pipe to capture stdout and stderr from script */
  505:       if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1)
  506: 	continue;
  507:       
  508:       /* possible fork errors are all temporary resource problems */
  509:       while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
  510: 	sleep(2);
  511: 
  512:       if (pid == -1)
  513:         {
  514: 	  if (!option_bool(OPT_DEBUG))
  515: 	    {
  516: 	      close(pipeout[0]);
  517: 	      close(pipeout[1]);
  518: 	    }
  519: 	  continue;
  520:         }
  521:       
  522:       /* wait for child to complete */
  523:       if (pid != 0)
  524: 	{
  525: 	  if (!option_bool(OPT_DEBUG))
  526: 	    {
  527: 	      FILE *fp;
  528: 	  
  529: 	      close(pipeout[1]);
  530: 	      
  531: 	      /* Read lines sent to stdout/err by the script and pass them back to be logged */
  532: 	      if (!(fp = fdopen(pipeout[0], "r")))
  533: 		close(pipeout[0]);
  534: 	      else
  535: 		{
  536: 		  while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
  537: 		    {
  538: 		      /* do not include new lines, log will append them */
  539: 		      size_t len = strlen(daemon->packet);
  540: 		      if (len > 0)
  541: 			{
  542: 			  --len;
  543: 			  if (daemon->packet[len] == '\n')
  544: 			    daemon->packet[len] = 0;
  545: 			}
  546: 		      send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
  547: 		    }
  548: 		  fclose(fp);
  549: 		}
  550: 	    }
  551: 	  
  552: 	  /* reap our children's children, if necessary */
  553: 	  while (1)
  554: 	    {
  555: 	      int status;
  556: 	      pid_t rc = wait(&status);
  557: 	      
  558: 	      if (rc == pid)
  559: 		{
  560: 		  /* On error send event back to main process for logging */
  561: 		  if (WIFSIGNALED(status))
  562: 		    send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
  563: 		  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
  564: 		    send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
  565: 		  break;
  566: 		}
  567: 	      
  568: 	      if (rc == -1 && errno != EINTR)
  569: 		break;
  570: 	    }
  571: 	  
  572: 	  continue;
  573: 	}
  574: 
  575:       if (!option_bool(OPT_DEBUG))
  576: 	{
  577: 	  /* map stdout/stderr of script to pipeout */
  578: 	  close(pipeout[0]);
  579: 	  dup2(pipeout[1], STDOUT_FILENO);
  580: 	  dup2(pipeout[1], STDERR_FILENO);
  581: 	  close(pipeout[1]);
  582: 	}
  583:       
  584:       if (data.action != ACTION_TFTP && data.action != ACTION_ARP && data.action != ACTION_RELAY_SNOOP)
  585: 	{
  586: #ifdef HAVE_DHCP6
  587: 	  my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
  588: 	  my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err); 
  589: 	  my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
  590: #endif
  591: 	  
  592: 	  my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
  593: 	  my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
  594: 	  
  595: #ifdef HAVE_BROKEN_RTC
  596: 	  sprintf(daemon->dhcp_buff2, "%u", data.length);
  597: 	  my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
  598: #else
  599: 	  sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
  600: 	  my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err); 
  601: #endif
  602: 	  
  603: 	  my_setenv("DNSMASQ_DOMAIN", domain, &err);
  604: 	  
  605: 	  end = extradata + data.ed_len;
  606: 	  buf = extradata;
  607: 
  608: 	  if (data.ed_len == 0)
  609: 	    my_setenv("DNSMASQ_DATA_MISSING", "1", &err);
  610: 	  
  611: 	  if (!is6)
  612: 	    buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
  613: #ifdef HAVE_DHCP6
  614: 	  else
  615: 	    {
  616: 	      if (data.vendorclass_count != 0)
  617: 		{
  618: 		  buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
  619: 		  for (i = 0; i < data.vendorclass_count - 1; i++)
  620: 		    {
  621: 		      sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
  622: 		      buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
  623: 		    }
  624: 		}
  625: 	    }
  626: #endif
  627: 	  
  628: 	  buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
  629: 	  
  630: 	  if (!is6)
  631: 	    {
  632: 	      buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
  633: 	      buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);   
  634: 	      buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
  635: 	      buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
  636: 	      buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
  637: 	      buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
  638: 	    }
  639: 	  
  640: 	  buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
  641: 	  buf = grab_extradata(buf, end, "DNSMASQ_MUD_URL", &err);
  642: 	  buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
  643: 	  	  
  644: 	  if (is6)
  645: 	    buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
  646: 	  else
  647: 	    {
  648: 	      const char *giaddr = NULL;
  649: 	      if (data.giaddr.s_addr != 0)
  650: 		  giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN);
  651: 	      my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err);
  652: 	    }
  653: 	  
  654: 	  for (i = 0; buf; i++)
  655: 	    {
  656: 	      sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
  657: 	      buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
  658: 	    }
  659: 	  
  660: 	  sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
  661: 	  my_setenv("DNSMASQ_TIME_REMAINING", data.action != ACTION_DEL && data.remaining_time != 0 ? daemon->dhcp_buff2 : NULL, &err);
  662: 	  
  663: 	  my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
  664: 	  if (data.action == ACTION_OLD_HOSTNAME)
  665: 	    hostname = NULL;
  666: 	  
  667: 	  my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
  668: 	}
  669:       
  670:       /* we need to have the event_fd around if exec fails */
  671:       if ((i = fcntl(event_fd, F_GETFD)) != -1)
  672: 	fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
  673:       close(pipefd[0]);
  674: 
  675:       if (data.action == ACTION_RELAY_SNOOP)
  676: 	strcpy(daemon->packet, data.interface);
  677:       
  678:       p =  strrchr(daemon->lease_change_command, '/');
  679:       if (err == 0)
  680: 	{
  681: 	  execl(daemon->lease_change_command, 
  682: 		p ? p+1 : daemon->lease_change_command, action_str, 
  683: 		(is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff, 
  684: 		daemon->addrbuff, hostname, (char*)NULL);
  685: 	  err = errno;
  686: 	}
  687:       /* failed, send event so the main process logs the problem */
  688:       send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
  689:       _exit(0); 
  690:     }
  691: }
  692: 
  693: static void my_setenv(const char *name, const char *value, int *error)
  694: {
  695:   if (*error == 0)
  696:     {
  697:       if (!value)
  698: 	unsetenv(name);
  699:       else if (setenv(name, value, 1) != 0)
  700: 	*error = errno;
  701:     }
  702: }
  703:  
  704: static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end,  char *env, int *err)
  705: {
  706:   unsigned char *next = NULL;
  707:   char *val = NULL;
  708: 
  709:   if (buf && (buf != end))
  710:     {
  711:       for (next = buf; ; next++)
  712: 	if (next == end)
  713: 	  {
  714: 	    next = NULL;
  715: 	    break;
  716: 	  }
  717: 	else if (*next == 0)
  718: 	  break;
  719: 
  720:       if (next && (next != buf))
  721: 	{
  722: 	  char *p;
  723: 	  /* No "=" in value */
  724: 	  if ((p = strchr((char *)buf, '=')))
  725: 	    *p = 0;
  726: 	  val = (char *)buf;
  727: 	}
  728:     }
  729:   
  730:   my_setenv(env, val, err);
  731:    
  732:   return next ? next + 1 : NULL;
  733: }
  734: 
  735: #ifdef HAVE_LUASCRIPT
  736: static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
  737: {
  738:   unsigned char *next;
  739: 
  740:   if (!buf || (buf == end))
  741:     return NULL;
  742: 
  743:   for (next = buf; *next != 0; next++)
  744:     if (next == end)
  745:       return NULL;
  746:   
  747:   if (next != buf)
  748:     {
  749:       lua_pushstring(lua,  (char *)buf);
  750:       lua_setfield(lua, -2, field);
  751:     }
  752: 
  753:   return next + 1;
  754: }
  755: #endif
  756: 
  757: static void buff_alloc(size_t size)
  758: {
  759:   if (size > buf_size)
  760:     {
  761:       struct script_data *new;
  762:       
  763:       /* start with reasonable size, will almost never need extending. */
  764:       if (size < sizeof(struct script_data) + 200)
  765: 	size = sizeof(struct script_data) + 200;
  766: 
  767:       if (!(new = whine_malloc(size)))
  768: 	return;
  769:       if (buf)
  770: 	free(buf);
  771:       buf = new;
  772:       buf_size = size;
  773:     }
  774: }
  775: 
  776: /* pack up lease data into a buffer */    
  777: void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
  778: {
  779:   unsigned char *p;
  780:   unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
  781:   int fd = daemon->dhcpfd;
  782: #ifdef HAVE_DHCP6 
  783:   if (!daemon->dhcp)
  784:     fd = daemon->dhcp6fd;
  785: #endif
  786: 
  787:   /* no script */
  788:   if (daemon->helperfd == -1)
  789:     return;
  790: 
  791:   if (lease->extradata)
  792:     ed_len = lease->extradata_len;
  793:   if (lease->clid)
  794:     clid_len = lease->clid_len;
  795:   if (hostname)
  796:     hostname_len = strlen(hostname) + 1;
  797: 
  798:   buff_alloc(sizeof(struct script_data) +  clid_len + ed_len + hostname_len);
  799: 
  800:   buf->action = action;
  801:   buf->flags = lease->flags;
  802: #ifdef HAVE_DHCP6 
  803:   buf->vendorclass_count = lease->vendorclass_count;
  804:   buf->addr6 = lease->addr6;
  805:   buf->iaid = lease->iaid;
  806: #endif
  807:   buf->hwaddr_len = lease->hwaddr_len;
  808:   buf->hwaddr_type = lease->hwaddr_type;
  809:   buf->clid_len = clid_len;
  810:   buf->ed_len = ed_len;
  811:   buf->hostname_len = hostname_len;
  812:   buf->addr = lease->addr;
  813:   buf->giaddr = lease->giaddr;
  814:   memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
  815:   if (!indextoname(fd, lease->last_interface, buf->interface))
  816:     buf->interface[0] = 0;
  817:   
  818: #ifdef HAVE_BROKEN_RTC 
  819:   buf->length = lease->length;
  820: #else
  821:   buf->expires = lease->expires;
  822: #endif
  823: 
  824:   if (lease->expires != 0)
  825:     buf->remaining_time = (unsigned int)difftime(lease->expires, now);
  826:   else
  827:     buf->remaining_time = 0;
  828: 
  829:   p = (unsigned char *)(buf+1);
  830:   if (clid_len != 0)
  831:     {
  832:       memcpy(p, lease->clid, clid_len);
  833:       p += clid_len;
  834:     }
  835:   if (hostname_len != 0)
  836:     {
  837:       memcpy(p, hostname, hostname_len);
  838:       p += hostname_len;
  839:     }
  840:   if (ed_len != 0)
  841:     {
  842:       memcpy(p, lease->extradata, ed_len);
  843:       p += ed_len;
  844:     }
  845:   bytes_in_buf = p - (unsigned char *)buf;
  846: }
  847: 
  848: #ifdef HAVE_DHCP6
  849: void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len)
  850: {
  851:   /* no script */
  852:   if (daemon->helperfd == -1)
  853:     return;
  854:   
  855:   inet_ntop(AF_INET6, prefix, daemon->addrbuff, ADDRSTRLEN);
  856: 
  857:   /* 5 for /nnn and zero on the end of the prefix. */
  858:   buff_alloc(sizeof(struct script_data) + ADDRSTRLEN + 5);
  859:   memset(buf, 0, sizeof(struct script_data));
  860: 
  861:   buf->action = ACTION_RELAY_SNOOP;
  862:   buf->addr6 = *client;
  863:   buf->hostname_len = sprintf((char *)(buf+1), "%s/%u", daemon->addrbuff, prefix_len) + 1;
  864:   
  865:   indextoname(daemon->dhcp6fd, if_index, buf->interface);
  866: 
  867:   bytes_in_buf = sizeof(struct script_data) + buf->hostname_len;
  868: }
  869: #endif
  870: 
  871: #ifdef HAVE_TFTP
  872: /* This nastily re-uses DHCP-fields for TFTP stuff */
  873: void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
  874: {
  875:   unsigned int filename_len;
  876: 
  877:   /* no script */
  878:   if (daemon->helperfd == -1)
  879:     return;
  880:   
  881:   filename_len = strlen(filename) + 1;
  882:   buff_alloc(sizeof(struct script_data) +  filename_len);
  883:   memset(buf, 0, sizeof(struct script_data));
  884: 
  885:   buf->action = ACTION_TFTP;
  886:   buf->hostname_len = filename_len;
  887:   buf->file_len = file_len;
  888: 
  889:   if ((buf->flags = peer->sa.sa_family) == AF_INET)
  890:     buf->addr = peer->in.sin_addr;
  891:   else
  892:     buf->addr6 = peer->in6.sin6_addr;
  893: 
  894:   memcpy((unsigned char *)(buf+1), filename, filename_len);
  895:   
  896:   bytes_in_buf = sizeof(struct script_data) +  filename_len;
  897: }
  898: #endif
  899: 
  900: void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr)
  901: {
  902:   /* no script */
  903:   if (daemon->helperfd == -1)
  904:     return;
  905:   
  906:   buff_alloc(sizeof(struct script_data));
  907:   memset(buf, 0, sizeof(struct script_data));
  908: 
  909:   buf->action = action;
  910:   buf->hwaddr_len = maclen;
  911:   buf->hwaddr_type =  ARPHRD_ETHER; 
  912:   if ((buf->flags = family) == AF_INET)
  913:     buf->addr = addr->addr4;
  914:   else
  915:     buf->addr6 = addr->addr6;
  916:   
  917:   memcpy(buf->hwaddr, mac, maclen);
  918:   
  919:   bytes_in_buf = sizeof(struct script_data);
  920: }
  921: 
  922: int helper_buf_empty(void)
  923: {
  924:   return bytes_in_buf == 0;
  925: }
  926: 
  927: void helper_write(void)
  928: {
  929:   ssize_t rc;
  930: 
  931:   if (bytes_in_buf == 0)
  932:     return;
  933:   
  934:   if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
  935:     {
  936:       if (bytes_in_buf != (size_t)rc)
  937: 	memmove(buf, buf + rc, bytes_in_buf - rc); 
  938:       bytes_in_buf -= rc;
  939:     }
  940:   else
  941:     {
  942:       if (errno == EAGAIN || errno == EINTR)
  943: 	return;
  944:       bytes_in_buf = 0;
  945:     }
  946: }
  947: 
  948: #endif /* HAVE_SCRIPT */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>