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

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

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