Annotation of embedaddon/dnsmasq/src/helper.c, revision 1.1.1.3

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

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