File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / src / helper.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 29 19:37:40 2013 UTC (11 years ago) by misho
Branches: elwix, dnsmasq, MAIN
CVS tags: v2_66p0, v2_66, HEAD
dnsmasq

    1: /* dnsmasq is Copyright (c) 2000-2013 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 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: 
   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:   unsigned char hwaddr[DHCP_CHADDR_MAX];
   65:   char interface[IF_NAMESIZE];
   66: 
   67: };
   68: 
   69: static struct script_data *buf = NULL;
   70: static size_t bytes_in_buf = 0, buf_size = 0;
   71: 
   72: int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
   73: {
   74:   pid_t pid;
   75:   int i, pipefd[2];
   76:   struct sigaction sigact;
   77: 
   78:   /* create the pipe through which the main program sends us commands,
   79:      then fork our process. */
   80:   if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
   81:     {
   82:       send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
   83:       _exit(0);
   84:     }
   85: 
   86:   if (pid != 0)
   87:     {
   88:       close(pipefd[0]); /* close reader side */
   89:       return pipefd[1];
   90:     }
   91: 
   92:   /* ignore SIGTERM, so that we can clean up when the main process gets hit
   93:      and SIGALRM so that we can use sleep() */
   94:   sigact.sa_handler = SIG_IGN;
   95:   sigact.sa_flags = 0;
   96:   sigemptyset(&sigact.sa_mask);
   97:   sigaction(SIGTERM, &sigact, NULL);
   98:   sigaction(SIGALRM, &sigact, NULL);
   99: 
  100:   if (!option_bool(OPT_DEBUG) && uid != 0)
  101:     {
  102:       gid_t dummy;
  103:       if (setgroups(0, &dummy) == -1 || 
  104: 	  setgid(gid) == -1 || 
  105: 	  setuid(uid) == -1)
  106: 	{
  107: 	  if (option_bool(OPT_NO_FORK))
  108: 	    /* send error to daemon process if no-fork */
  109: 	    send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
  110: 	  else
  111: 	    {
  112: 	      /* kill daemon */
  113: 	      send_event(event_fd, EVENT_DIE, 0, NULL);
  114: 	      /* return error */
  115: 	      send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
  116: 	    }
  117: 	  _exit(0);
  118: 	}
  119:     }
  120: 
  121:   /* 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. */
  125:   for (max_fd--; max_fd >= 0; max_fd--)
  126:     if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && 
  127: 	max_fd != STDIN_FILENO && max_fd != pipefd[0] && 
  128: 	max_fd != event_fd && max_fd != err_fd)
  129:       close(max_fd);
  130:   
  131: #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:     
  176:   /* loop here */
  177:   while(1)
  178:     {
  179:       struct script_data data;
  180:       char *p, *action_str, *hostname = NULL, *domain = NULL;
  181:       unsigned char *buf = (unsigned char *)daemon->namebuff;
  182:       unsigned char *end, *extradata, *alloc_buff = NULL;
  183:       int is6, err = 0;
  184: 
  185:       free(alloc_buff);
  186:       
  187:       /* 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))
  189: 	{
  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: 	}
  200:  
  201:       is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
  202:       
  203:       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";
  209:       else if (data.action == ACTION_TFTP)
  210: 	{
  211: 	  action_str = "tftp";
  212: 	  is6 = (data.flags != AF_INET);
  213: 	}
  214:       else
  215: 	continue;
  216: 
  217:       	
  218:       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: 	}
  231:        
  232:       /* 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)))
  235: 	continue;
  236:       
  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 */
  242:       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
  251: 	{
  252: 	  /* 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: 	    }
  260: 
  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: 	    } 
  268: 	}
  269: #endif
  270: 
  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;
  278: 	  if (data.action != ACTION_TFTP)
  279: 	    {
  280: 	      if (!legal_hostname(hostname))
  281: 		hostname = NULL;
  282: 	      else if ((dot = strchr(hostname, '.')))
  283: 		{
  284: 		  domain = dot+1;
  285: 		  *dot = 0;
  286: 		} 
  287: 	    }
  288: 	}
  289:     
  290:       extradata = buf + data.hostname_len;
  291:     
  292:       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: 
  299:       /* file length */
  300:       if (data.action == ACTION_TFTP)
  301: 	sprintf(daemon->dhcp_buff, "%u", data.hwaddr_len);
  302:       
  303: #ifdef HAVE_LUASCRIPT
  304:       if (daemon->luascript)
  305: 	{
  306: 	  if (data.action == ACTION_TFTP)
  307: 	    {
  308: 	      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: 		}
  323: 	    }
  324: 	  else
  325: 	    {
  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");
  358: #endif
  359: 	      
  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");
  393: 		  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");
  396: 		}
  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 */
  436: 	    }
  437: 	}
  438: #endif
  439: 
  440:       /* no script, just lua */
  441:       if (!daemon->lease_change_command)
  442: 	continue;
  443: 
  444:       /* possible fork errors are all temporary resource problems */
  445:       while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
  446: 	sleep(2);
  447: 
  448:       if (pid == -1)
  449: 	continue;
  450:       
  451:       /* wait for child to complete */
  452:       if (pid != 0)
  453: 	{
  454: 	  /* 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))
  464: 		    send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
  465: 		  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
  466: 		    send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
  467: 		  break;
  468: 		}
  469: 	      
  470: 	      if (rc == -1 && errno != EINTR)
  471: 		break;
  472: 	    }
  473: 	  
  474: 	  continue;
  475: 	}
  476:       
  477:       if (data.action != ACTION_TFTP)
  478: 	{
  479: 	  if (is6)
  480: 	    {
  481: 	      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
  492: 	  sprintf(daemon->dhcp_buff2, "%u", data.length);
  493: 	  my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
  494: #else
  495: 	  sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
  496: 	  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)
  511: 		{
  512: 		  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: 		    }
  518: 		}
  519: 	    }
  520: #endif
  521: 	  
  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);
  529: 	      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);
  532: 	    }
  533: 	  
  534: 	  buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
  535: 
  536: 	  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: 	    }
  558: 	}
  559: 
  560:       if (option_bool(OPT_LOG_OPTS))
  561: 	my_setenv("DNSMASQ_LOG_DHCP", "1", &err);
  562: 	 
  563:       /* 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: 
  568:       p =  strrchr(daemon->lease_change_command, '/');
  569:       if (err == 0)
  570: 	{
  571: 	  execl(daemon->lease_change_command, 
  572: 		p ? p+1 : daemon->lease_change_command,
  573: 		action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL);
  574: 	  err = errno;
  575: 	}
  576:       /* failed, send event so the main process logs the problem */
  577:       send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
  578:       _exit(0); 
  579:     }
  580: }
  581: 
  582: static void my_setenv(const char *name, const char *value, int *error)
  583: {
  584:   if (*error == 0 && setenv(name, value, 1) != 0)
  585:     *error = errno;
  586: }
  587:  
  588: static 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: 
  611: #ifdef HAVE_LUASCRIPT
  612: static 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: 
  633: static 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: 
  652: /* pack up lease data into a buffer */    
  653: void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
  654: {
  655:   unsigned char *p;
  656:   unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
  657:   int fd = daemon->dhcpfd;
  658: #ifdef HAVE_DHCP6 
  659:   int is6 = !!(lease->flags & (LEASE_TA | LEASE_NA));
  660: 
  661:   if (!daemon->dhcp)
  662:     fd = daemon->dhcp6fd;
  663: #endif
  664: 
  665:   /* no script */
  666:   if (daemon->helperfd == -1)
  667:     return;
  668: 
  669:   if (lease->extradata)
  670:     ed_len = lease->extradata_len;
  671:   if (lease->clid)
  672:     clid_len = lease->clid_len;
  673:   if (hostname)
  674:     hostname_len = strlen(hostname) + 1;
  675: 
  676:   buff_alloc(sizeof(struct script_data) +  clid_len + ed_len + hostname_len);
  677: 
  678:   buf->action = action;
  679:   buf->flags = lease->flags;
  680: #ifdef HAVE_DHCP6 
  681:   if (is6)
  682:     buf->hwaddr_len = lease->vendorclass_count;
  683:   else
  684: #endif
  685:     buf->hwaddr_len = lease->hwaddr_len;
  686:   buf->hwaddr_type = lease->hwaddr_type;
  687:   buf->clid_len = clid_len;
  688:   buf->ed_len = ed_len;
  689:   buf->hostname_len = hostname_len;
  690:   buf->addr = lease->addr;
  691:   buf->giaddr = lease->giaddr;
  692:   memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
  693:   if (!indextoname(fd, lease->last_interface, buf->interface))
  694:     buf->interface[0] = 0;
  695:   
  696: #ifdef HAVE_BROKEN_RTC 
  697:   buf->length = lease->length;
  698: #else
  699:   buf->expires = lease->expires;
  700: #endif
  701: 
  702:   if (lease->expires != 0)
  703:     buf->remaining_time = (unsigned int)difftime(lease->expires, now);
  704:   else
  705:     buf->remaining_time = 0;
  706: 
  707:   p = (unsigned char *)(buf+1);
  708:   if (clid_len != 0)
  709:     {
  710:       memcpy(p, lease->clid, clid_len);
  711:       p += clid_len;
  712:     }
  713:   if (hostname_len != 0)
  714:     {
  715:       memcpy(p, hostname, hostname_len);
  716:       p += hostname_len;
  717:     }
  718:   if (ed_len != 0)
  719:     {
  720:       memcpy(p, lease->extradata, ed_len);
  721:       p += ed_len;
  722:     }
  723:   bytes_in_buf = p - (unsigned char *)buf;
  724: }
  725: 
  726: #ifdef HAVE_TFTP
  727: /* This nastily re-uses DHCP-fields for TFTP stuff */
  728: void 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: 
  757: int helper_buf_empty(void)
  758: {
  759:   return bytes_in_buf == 0;
  760: }
  761: 
  762: void helper_write(void)
  763: {
  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: 
  783: #endif
  784: 
  785: 
  786: 

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