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

1.1.1.4 ! misho       1: /* dnsmasq is Copyright (c) 2000-2021 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: 
1.1.1.4 ! misho      21: /* This file has code to fork a helper process which receives data via a pipe 
1.1       misho      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:   struct in6_addr addr6;
                     68: #ifdef HAVE_DHCP6
1.1.1.4 ! misho      69:   int vendorclass_count;
        !            70:   unsigned int iaid;
1.1.1.2   misho      71: #endif
1.1       misho      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;
1.1.1.4 ! misho      84:   unsigned char *alloc_buff = NULL;
        !            85:   
1.1       misho      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: 
1.1.1.4 ! misho     100:   /* ignore SIGTERM and SIGINT, so that we can clean up when the main process gets hit
1.1       misho     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);
1.1.1.4 ! misho     107:   sigaction(SIGINT, &sigact, NULL);
1.1       misho     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. */
1.1.1.4 ! misho     134:   close_fds(max_fd, pipefd[0], event_fd, err_fd);
1.1       misho     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;
1.1.1.4 ! misho     187:       unsigned char *end, *extradata;
1.1       misho     188:       int is6, err = 0;
1.1.1.4 ! misho     189:       int pipeout[2];
1.1       misho     190: 
1.1.1.4 ! misho     191:       /* Free rarely-allocated memory from previous iteration. */
        !           192:       if (alloc_buff)
        !           193:        {
        !           194:          free(alloc_buff);
        !           195:          alloc_buff = NULL;
        !           196:        }
1.1       misho     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:        }
1.1.1.3   misho     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 
1.1       misho     237:        continue;
                    238: 
                    239:        
1.1.1.2   misho     240:       /* stringify MAC into dhcp_buff */
                    241:       p = daemon->dhcp_buff;
                    242:       if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0) 
                    243:        p += sprintf(p, "%.2x-", data.hwaddr_type);
                    244:       for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
                    245:        {
                    246:          p += sprintf(p, "%.2x", data.hwaddr[i]);
                    247:          if (i != data.hwaddr_len - 1)
                    248:            p += sprintf(p, ":");
1.1       misho     249:        }
1.1.1.2   misho     250:       
1.1       misho     251:       /* supplied data may just exceed normal buffer (unlikely) */
                    252:       if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME && 
                    253:          !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
                    254:        continue;
                    255:       
                    256:       if (!read_write(pipefd[0], buf, 
                    257:                      data.hostname_len + data.ed_len + data.clid_len, 1))
                    258:        continue;
                    259: 
                    260:       /* CLID into packet */
1.1.1.2   misho     261:       for (p = daemon->packet, i = 0; i < data.clid_len; i++)
                    262:        {
                    263:          p += sprintf(p, "%.2x", buf[i]);
                    264:          if (i != data.clid_len - 1) 
1.1       misho     265:              p += sprintf(p, ":");
1.1.1.2   misho     266:        }
                    267: 
1.1       misho     268: #ifdef HAVE_DHCP6
1.1.1.2   misho     269:       if (is6)
1.1       misho     270:        {
                    271:          /* or IAID and server DUID for IPv6 */
1.1.1.2   misho     272:          sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.iaid);     
                    273:          for (p = daemon->dhcp_packet.iov_base, i = 0; i < daemon->duid_len; i++)
1.1       misho     274:            {
                    275:              p += sprintf(p, "%.2x", daemon->duid[i]);
                    276:              if (i != daemon->duid_len - 1) 
                    277:                p += sprintf(p, ":");
                    278:            }
                    279: 
                    280:        }
                    281: #endif
                    282: 
                    283:       buf += data.clid_len;
                    284: 
                    285:       if (data.hostname_len != 0)
                    286:        {
                    287:          char *dot;
                    288:          hostname = (char *)buf;
                    289:          hostname[data.hostname_len - 1] = 0;
                    290:          if (data.action != ACTION_TFTP)
                    291:            {
                    292:              if (!legal_hostname(hostname))
                    293:                hostname = NULL;
                    294:              else if ((dot = strchr(hostname, '.')))
                    295:                {
                    296:                  domain = dot+1;
                    297:                  *dot = 0;
                    298:                } 
                    299:            }
                    300:        }
                    301:     
                    302:       extradata = buf + data.hostname_len;
                    303:     
                    304:       if (!is6)
                    305:        inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
                    306:       else
1.1.1.2   misho     307:        inet_ntop(AF_INET6, &data.addr6, daemon->addrbuff, ADDRSTRLEN);
1.1       misho     308: 
1.1.1.2   misho     309: #ifdef HAVE_TFTP
1.1       misho     310:       /* file length */
                    311:       if (data.action == ACTION_TFTP)
1.1.1.2   misho     312:        sprintf(is6 ? daemon->packet : daemon->dhcp_buff, "%lu", (unsigned long)data.file_len);
                    313: #endif
                    314: 
1.1       misho     315: #ifdef HAVE_LUASCRIPT
                    316:       if (daemon->luascript)
                    317:        {
                    318:          if (data.action == ACTION_TFTP)
                    319:            {
                    320:              lua_getglobal(lua, "tftp"); 
                    321:              if (lua_type(lua, -1) != LUA_TFUNCTION)
                    322:                lua_pop(lua, 1); /* tftp function optional */
                    323:              else
                    324:                {
                    325:                  lua_pushstring(lua, action_str); /* arg1 - action */
                    326:                  lua_newtable(lua);               /* arg2 - data table */
                    327:                  lua_pushstring(lua, daemon->addrbuff);
                    328:                  lua_setfield(lua, -2, "destination_address");
                    329:                  lua_pushstring(lua, hostname);
                    330:                  lua_setfield(lua, -2, "file_name"); 
1.1.1.2   misho     331:                  lua_pushstring(lua, is6 ? daemon->packet : daemon->dhcp_buff);
1.1       misho     332:                  lua_setfield(lua, -2, "file_size");
                    333:                  lua_call(lua, 2, 0);  /* pass 2 values, expect 0 */
                    334:                }
                    335:            }
1.1.1.3   misho     336:          else if (data.action == ACTION_ARP)
                    337:            {
                    338:              lua_getglobal(lua, "arp"); 
                    339:              if (lua_type(lua, -1) != LUA_TFUNCTION)
                    340:                lua_pop(lua, 1); /* arp function optional */
                    341:              else
                    342:                {
                    343:                  lua_pushstring(lua, action_str); /* arg1 - action */
                    344:                  lua_newtable(lua);               /* arg2 - data table */
                    345:                  lua_pushstring(lua, daemon->addrbuff);
                    346:                  lua_setfield(lua, -2, "client_address");
                    347:                  lua_pushstring(lua, daemon->dhcp_buff);
                    348:                  lua_setfield(lua, -2, "mac_address");
                    349:                  lua_call(lua, 2, 0);  /* pass 2 values, expect 0 */
                    350:                }
                    351:            }
1.1       misho     352:          else
                    353:            {
                    354:              lua_getglobal(lua, "lease");     /* function to call */
                    355:              lua_pushstring(lua, action_str); /* arg1 - action */
                    356:              lua_newtable(lua);               /* arg2 - data table */
                    357:              
                    358:              if (is6)
                    359:                {
                    360:                  lua_pushstring(lua, daemon->packet);
1.1.1.2   misho     361:                  lua_setfield(lua, -2, "client_duid");
                    362:                  lua_pushstring(lua, daemon->dhcp_packet.iov_base);
1.1       misho     363:                  lua_setfield(lua, -2, "server_duid");
                    364:                  lua_pushstring(lua, daemon->dhcp_buff3);
                    365:                  lua_setfield(lua, -2, "iaid");
                    366:                }
                    367:              
                    368:              if (!is6 && data.clid_len != 0)
                    369:                {
                    370:                  lua_pushstring(lua, daemon->packet);
                    371:                  lua_setfield(lua, -2, "client_id");
                    372:                }
                    373:              
                    374:              if (strlen(data.interface) != 0)
                    375:                {
                    376:                  lua_pushstring(lua, data.interface);
                    377:                  lua_setfield(lua, -2, "interface");
                    378:                }
                    379:              
                    380: #ifdef HAVE_BROKEN_RTC 
                    381:              lua_pushnumber(lua, data.length);
                    382:              lua_setfield(lua, -2, "lease_length");
                    383: #else
                    384:              lua_pushnumber(lua, data.expires);
                    385:              lua_setfield(lua, -2, "lease_expires");
                    386: #endif
                    387:              
                    388:              if (hostname)
                    389:                {
                    390:                  lua_pushstring(lua, hostname);
                    391:                  lua_setfield(lua, -2, "hostname");
                    392:                }
                    393:              
                    394:              if (domain)
                    395:                {
                    396:                  lua_pushstring(lua, domain);
                    397:                  lua_setfield(lua, -2, "domain");
                    398:                }
                    399:              
                    400:              end = extradata + data.ed_len;
                    401:              buf = extradata;
                    402:              
                    403:              if (!is6)
                    404:                buf = grab_extradata_lua(buf, end, "vendor_class");
                    405: #ifdef HAVE_DHCP6
1.1.1.2   misho     406:              else  if (data.vendorclass_count != 0)
                    407:                {
                    408:                  sprintf(daemon->dhcp_buff2, "vendor_class_id");
                    409:                  buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
                    410:                  for (i = 0; i < data.vendorclass_count - 1; i++)
                    411:                    {
                    412:                      sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
                    413:                      buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
                    414:                    }
                    415:                }
1.1       misho     416: #endif
                    417:              
                    418:              buf = grab_extradata_lua(buf, end, "supplied_hostname");
                    419:              
                    420:              if (!is6)
                    421:                {
                    422:                  buf = grab_extradata_lua(buf, end, "cpewan_oui");
                    423:                  buf = grab_extradata_lua(buf, end, "cpewan_serial");   
                    424:                  buf = grab_extradata_lua(buf, end, "cpewan_class");
                    425:                  buf = grab_extradata_lua(buf, end, "circuit_id");
                    426:                  buf = grab_extradata_lua(buf, end, "subscriber_id");
                    427:                  buf = grab_extradata_lua(buf, end, "remote_id");
                    428:                }
                    429:              
                    430:              buf = grab_extradata_lua(buf, end, "tags");
                    431:              
                    432:              if (is6)
                    433:                buf = grab_extradata_lua(buf, end, "relay_address");
                    434:              else if (data.giaddr.s_addr != 0)
                    435:                {
                    436:                  lua_pushstring(lua, inet_ntoa(data.giaddr));
                    437:                  lua_setfield(lua, -2, "relay_address");
                    438:                }
                    439:              
                    440:              for (i = 0; buf; i++)
                    441:                {
                    442:                  sprintf(daemon->dhcp_buff2, "user_class%i", i);
                    443:                  buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
                    444:                }
                    445:              
                    446:              if (data.action != ACTION_DEL && data.remaining_time != 0)
                    447:                {
                    448:                  lua_pushnumber(lua, data.remaining_time);
                    449:                  lua_setfield(lua, -2, "time_remaining");
                    450:                }
                    451:              
                    452:              if (data.action == ACTION_OLD_HOSTNAME && hostname)
                    453:                {
                    454:                  lua_pushstring(lua, hostname);
                    455:                  lua_setfield(lua, -2, "old_hostname");
                    456:                }
                    457:              
1.1.1.2   misho     458:              if (!is6 || data.hwaddr_len != 0)
1.1       misho     459:                {
                    460:                  lua_pushstring(lua, daemon->dhcp_buff);
                    461:                  lua_setfield(lua, -2, "mac_address");
                    462:                }
                    463:              
                    464:              lua_pushstring(lua, daemon->addrbuff);
                    465:              lua_setfield(lua, -2, "ip_address");
                    466:            
                    467:              lua_call(lua, 2, 0);      /* pass 2 values, expect 0 */
                    468:            }
                    469:        }
                    470: #endif
                    471: 
                    472:       /* no script, just lua */
                    473:       if (!daemon->lease_change_command)
                    474:        continue;
                    475: 
1.1.1.4 ! misho     476:       /* Pipe to capture stdout and stderr from script */
        !           477:       if (!option_bool(OPT_DEBUG) && pipe(pipeout) == -1)
        !           478:        continue;
        !           479:       
1.1       misho     480:       /* possible fork errors are all temporary resource problems */
                    481:       while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
                    482:        sleep(2);
                    483: 
                    484:       if (pid == -1)
1.1.1.4 ! misho     485:         {
        !           486:          if (!option_bool(OPT_DEBUG))
        !           487:            {
        !           488:              close(pipeout[0]);
        !           489:              close(pipeout[1]);
        !           490:            }
        !           491:          continue;
        !           492:         }
1.1       misho     493:       
                    494:       /* wait for child to complete */
                    495:       if (pid != 0)
                    496:        {
1.1.1.4 ! misho     497:          if (!option_bool(OPT_DEBUG))
        !           498:            {
        !           499:              FILE *fp;
        !           500:          
        !           501:              close(pipeout[1]);
        !           502:              
        !           503:              /* Read lines sent to stdout/err by the script and pass them back to be logged */
        !           504:              if (!(fp = fdopen(pipeout[0], "r")))
        !           505:                close(pipeout[0]);
        !           506:              else
        !           507:                {
        !           508:                  while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
        !           509:                    {
        !           510:                      /* do not include new lines, log will append them */
        !           511:                      size_t len = strlen(daemon->packet);
        !           512:                      if (len > 0)
        !           513:                        {
        !           514:                          --len;
        !           515:                          if (daemon->packet[len] == '\n')
        !           516:                            daemon->packet[len] = 0;
        !           517:                        }
        !           518:                      send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
        !           519:                    }
        !           520:                  fclose(fp);
        !           521:                }
        !           522:            }
        !           523:          
1.1       misho     524:          /* reap our children's children, if necessary */
                    525:          while (1)
                    526:            {
                    527:              int status;
                    528:              pid_t rc = wait(&status);
                    529:              
                    530:              if (rc == pid)
                    531:                {
                    532:                  /* On error send event back to main process for logging */
                    533:                  if (WIFSIGNALED(status))
                    534:                    send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
                    535:                  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
                    536:                    send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
                    537:                  break;
                    538:                }
                    539:              
                    540:              if (rc == -1 && errno != EINTR)
                    541:                break;
                    542:            }
                    543:          
                    544:          continue;
                    545:        }
1.1.1.4 ! misho     546: 
        !           547:       if (!option_bool(OPT_DEBUG))
        !           548:        {
        !           549:          /* map stdout/stderr of script to pipeout */
        !           550:          close(pipeout[0]);
        !           551:          dup2(pipeout[1], STDOUT_FILENO);
        !           552:          dup2(pipeout[1], STDERR_FILENO);
        !           553:          close(pipeout[1]);
        !           554:        }
1.1       misho     555:       
1.1.1.3   misho     556:       if (data.action != ACTION_TFTP && data.action != ACTION_ARP)
1.1       misho     557:        {
1.1.1.2   misho     558: #ifdef HAVE_DHCP6
                    559:          my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err);
                    560:          my_setenv("DNSMASQ_SERVER_DUID", is6 ? daemon->dhcp_packet.iov_base : NULL, &err); 
                    561:          my_setenv("DNSMASQ_MAC", is6 && data.hwaddr_len != 0 ? daemon->dhcp_buff : NULL, &err);
                    562: #endif
1.1       misho     563:          
1.1.1.2   misho     564:          my_setenv("DNSMASQ_CLIENT_ID", !is6 && data.clid_len != 0 ? daemon->packet : NULL, &err);
                    565:          my_setenv("DNSMASQ_INTERFACE", strlen(data.interface) != 0 ? data.interface : NULL, &err);
1.1       misho     566:          
                    567: #ifdef HAVE_BROKEN_RTC
                    568:          sprintf(daemon->dhcp_buff2, "%u", data.length);
                    569:          my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
                    570: #else
                    571:          sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
                    572:          my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err); 
                    573: #endif
                    574:          
1.1.1.2   misho     575:          my_setenv("DNSMASQ_DOMAIN", domain, &err);
1.1       misho     576:          
                    577:          end = extradata + data.ed_len;
                    578:          buf = extradata;
                    579:          
                    580:          if (!is6)
                    581:            buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
                    582: #ifdef HAVE_DHCP6
                    583:          else
                    584:            {
1.1.1.2   misho     585:              if (data.vendorclass_count != 0)
1.1       misho     586:                {
                    587:                  buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
1.1.1.2   misho     588:                  for (i = 0; i < data.vendorclass_count - 1; i++)
1.1       misho     589:                    {
                    590:                      sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
                    591:                      buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
                    592:                    }
                    593:                }
                    594:            }
                    595: #endif
                    596:          
                    597:          buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
                    598:          
                    599:          if (!is6)
                    600:            {
                    601:              buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
                    602:              buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);   
                    603:              buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
                    604:              buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
                    605:              buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
                    606:              buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
1.1.1.4 ! misho     607:              buf = grab_extradata(buf, end, "DNSMASQ_REQUESTED_OPTIONS", &err);
1.1       misho     608:            }
                    609:          
                    610:          buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
                    611: 
                    612:          if (is6)
                    613:            buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
1.1.1.2   misho     614:          else 
                    615:            my_setenv("DNSMASQ_RELAY_ADDRESS", data.giaddr.s_addr != 0 ? inet_ntoa(data.giaddr) : NULL, &err); 
1.1       misho     616:          
                    617:          for (i = 0; buf; i++)
                    618:            {
                    619:              sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
                    620:              buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
                    621:            }
                    622:          
1.1.1.2   misho     623:          sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
                    624:          my_setenv("DNSMASQ_TIME_REMAINING", data.action != ACTION_DEL && data.remaining_time != 0 ? daemon->dhcp_buff2 : NULL, &err);
1.1       misho     625:          
1.1.1.2   misho     626:          my_setenv("DNSMASQ_OLD_HOSTNAME", data.action == ACTION_OLD_HOSTNAME ? hostname : NULL, &err);
                    627:          if (data.action == ACTION_OLD_HOSTNAME)
                    628:            hostname = NULL;
1.1.1.3   misho     629:          
                    630:          my_setenv("DNSMASQ_LOG_DHCP", option_bool(OPT_LOG_OPTS) ? "1" : NULL, &err);
1.1.1.4 ! misho     631:        }
        !           632:       
1.1       misho     633:       /* we need to have the event_fd around if exec fails */
                    634:       if ((i = fcntl(event_fd, F_GETFD)) != -1)
                    635:        fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
                    636:       close(pipefd[0]);
                    637: 
                    638:       p =  strrchr(daemon->lease_change_command, '/');
                    639:       if (err == 0)
                    640:        {
                    641:          execl(daemon->lease_change_command, 
1.1.1.3   misho     642:                p ? p+1 : daemon->lease_change_command, action_str, 
                    643:                (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff, 
1.1.1.2   misho     644:                daemon->addrbuff, hostname, (char*)NULL);
1.1       misho     645:          err = errno;
                    646:        }
                    647:       /* failed, send event so the main process logs the problem */
                    648:       send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
                    649:       _exit(0); 
                    650:     }
                    651: }
                    652: 
                    653: static void my_setenv(const char *name, const char *value, int *error)
                    654: {
1.1.1.2   misho     655:   if (*error == 0)
                    656:     {
                    657:       if (!value)
                    658:        unsetenv(name);
                    659:       else if (setenv(name, value, 1) != 0)
                    660:        *error = errno;
                    661:     }
1.1       misho     662: }
                    663:  
                    664: static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end,  char *env, int *err)
                    665: {
1.1.1.2   misho     666:   unsigned char *next = NULL;
                    667:   char *val = NULL;
1.1       misho     668: 
1.1.1.2   misho     669:   if (buf && (buf != end))
1.1       misho     670:     {
1.1.1.2   misho     671:       for (next = buf; ; next++)
                    672:        if (next == end)
                    673:          {
                    674:            next = NULL;
                    675:            break;
                    676:          }
                    677:        else if (*next == 0)
                    678:          break;
1.1       misho     679: 
1.1.1.2   misho     680:       if (next && (next != buf))
                    681:        {
                    682:          char *p;
                    683:          /* No "=" in value */
                    684:          if ((p = strchr((char *)buf, '=')))
                    685:            *p = 0;
                    686:          val = (char *)buf;
                    687:        }
                    688:     }
                    689:   
                    690:   my_setenv(env, val, err);
                    691:    
                    692:   return next ? next + 1 : NULL;
1.1       misho     693: }
                    694: 
                    695: #ifdef HAVE_LUASCRIPT
                    696: static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
                    697: {
                    698:   unsigned char *next;
                    699: 
                    700:   if (!buf || (buf == end))
                    701:     return NULL;
                    702: 
                    703:   for (next = buf; *next != 0; next++)
                    704:     if (next == end)
                    705:       return NULL;
                    706:   
                    707:   if (next != buf)
                    708:     {
                    709:       lua_pushstring(lua,  (char *)buf);
                    710:       lua_setfield(lua, -2, field);
                    711:     }
                    712: 
                    713:   return next + 1;
                    714: }
                    715: #endif
                    716: 
                    717: static void buff_alloc(size_t size)
                    718: {
                    719:   if (size > buf_size)
                    720:     {
                    721:       struct script_data *new;
                    722:       
                    723:       /* start with reasonable size, will almost never need extending. */
                    724:       if (size < sizeof(struct script_data) + 200)
                    725:        size = sizeof(struct script_data) + 200;
                    726: 
                    727:       if (!(new = whine_malloc(size)))
                    728:        return;
                    729:       if (buf)
                    730:        free(buf);
                    731:       buf = new;
                    732:       buf_size = size;
                    733:     }
                    734: }
                    735: 
                    736: /* pack up lease data into a buffer */    
                    737: void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
                    738: {
                    739:   unsigned char *p;
                    740:   unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
                    741:   int fd = daemon->dhcpfd;
                    742: #ifdef HAVE_DHCP6 
                    743:   if (!daemon->dhcp)
                    744:     fd = daemon->dhcp6fd;
                    745: #endif
                    746: 
                    747:   /* no script */
                    748:   if (daemon->helperfd == -1)
                    749:     return;
                    750: 
                    751:   if (lease->extradata)
                    752:     ed_len = lease->extradata_len;
                    753:   if (lease->clid)
                    754:     clid_len = lease->clid_len;
                    755:   if (hostname)
                    756:     hostname_len = strlen(hostname) + 1;
                    757: 
                    758:   buff_alloc(sizeof(struct script_data) +  clid_len + ed_len + hostname_len);
                    759: 
                    760:   buf->action = action;
                    761:   buf->flags = lease->flags;
                    762: #ifdef HAVE_DHCP6 
1.1.1.2   misho     763:   buf->vendorclass_count = lease->vendorclass_count;
                    764:   buf->addr6 = lease->addr6;
                    765:   buf->iaid = lease->iaid;
1.1       misho     766: #endif
1.1.1.2   misho     767:   buf->hwaddr_len = lease->hwaddr_len;
1.1       misho     768:   buf->hwaddr_type = lease->hwaddr_type;
                    769:   buf->clid_len = clid_len;
                    770:   buf->ed_len = ed_len;
                    771:   buf->hostname_len = hostname_len;
                    772:   buf->addr = lease->addr;
                    773:   buf->giaddr = lease->giaddr;
                    774:   memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
                    775:   if (!indextoname(fd, lease->last_interface, buf->interface))
                    776:     buf->interface[0] = 0;
                    777:   
                    778: #ifdef HAVE_BROKEN_RTC 
                    779:   buf->length = lease->length;
                    780: #else
                    781:   buf->expires = lease->expires;
                    782: #endif
                    783: 
                    784:   if (lease->expires != 0)
                    785:     buf->remaining_time = (unsigned int)difftime(lease->expires, now);
                    786:   else
                    787:     buf->remaining_time = 0;
                    788: 
                    789:   p = (unsigned char *)(buf+1);
                    790:   if (clid_len != 0)
                    791:     {
                    792:       memcpy(p, lease->clid, clid_len);
                    793:       p += clid_len;
                    794:     }
                    795:   if (hostname_len != 0)
                    796:     {
                    797:       memcpy(p, hostname, hostname_len);
                    798:       p += hostname_len;
                    799:     }
                    800:   if (ed_len != 0)
                    801:     {
                    802:       memcpy(p, lease->extradata, ed_len);
                    803:       p += ed_len;
                    804:     }
                    805:   bytes_in_buf = p - (unsigned char *)buf;
                    806: }
                    807: 
                    808: #ifdef HAVE_TFTP
                    809: /* This nastily re-uses DHCP-fields for TFTP stuff */
                    810: void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
                    811: {
                    812:   unsigned int filename_len;
                    813: 
                    814:   /* no script */
                    815:   if (daemon->helperfd == -1)
                    816:     return;
                    817:   
                    818:   filename_len = strlen(filename) + 1;
                    819:   buff_alloc(sizeof(struct script_data) +  filename_len);
                    820:   memset(buf, 0, sizeof(struct script_data));
                    821: 
                    822:   buf->action = ACTION_TFTP;
                    823:   buf->hostname_len = filename_len;
1.1.1.2   misho     824:   buf->file_len = file_len;
1.1       misho     825: 
                    826:   if ((buf->flags = peer->sa.sa_family) == AF_INET)
                    827:     buf->addr = peer->in.sin_addr;
                    828:   else
1.1.1.2   misho     829:     buf->addr6 = peer->in6.sin6_addr;
1.1       misho     830: 
                    831:   memcpy((unsigned char *)(buf+1), filename, filename_len);
                    832:   
                    833:   bytes_in_buf = sizeof(struct script_data) +  filename_len;
                    834: }
                    835: #endif
1.1.1.3   misho     836: 
1.1.1.4 ! misho     837: void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr)
1.1.1.3   misho     838: {
                    839:   /* no script */
                    840:   if (daemon->helperfd == -1)
                    841:     return;
                    842:   
                    843:   buff_alloc(sizeof(struct script_data));
                    844:   memset(buf, 0, sizeof(struct script_data));
                    845: 
                    846:   buf->action = action;
                    847:   buf->hwaddr_len = maclen;
                    848:   buf->hwaddr_type =  ARPHRD_ETHER; 
                    849:   if ((buf->flags = family) == AF_INET)
1.1.1.4 ! misho     850:     buf->addr = addr->addr4;
1.1.1.3   misho     851:   else
1.1.1.4 ! misho     852:     buf->addr6 = addr->addr6;
1.1.1.3   misho     853:   
                    854:   memcpy(buf->hwaddr, mac, maclen);
                    855:   
                    856:   bytes_in_buf = sizeof(struct script_data);
                    857: }
1.1       misho     858: 
                    859: int helper_buf_empty(void)
                    860: {
                    861:   return bytes_in_buf == 0;
                    862: }
                    863: 
                    864: void helper_write(void)
                    865: {
                    866:   ssize_t rc;
                    867: 
                    868:   if (bytes_in_buf == 0)
                    869:     return;
                    870:   
                    871:   if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
                    872:     {
                    873:       if (bytes_in_buf != (size_t)rc)
                    874:        memmove(buf, buf + rc, bytes_in_buf - rc); 
                    875:       bytes_in_buf -= rc;
                    876:     }
                    877:   else
                    878:     {
                    879:       if (errno == EAGAIN || errno == EINTR)
                    880:        return;
                    881:       bytes_in_buf = 0;
                    882:     }
                    883: }
                    884: 
                    885: #endif
                    886: 
                    887: 
                    888: 

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