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

1.1       misho       1: /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
                      2: 
                      3:    This program is free software; you can redistribute it and/or modify
                      4:    it under the terms of the GNU General Public License as published by
                      5:    the Free Software Foundation; version 2 dated June, 1991, or
                      6:    (at your option) version 3 dated 29 June, 2007.
                      7:  
                      8:    This program is distributed in the hope that it will be useful,
                      9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
                     10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     11:    GNU General Public License for more details.
                     12:      
                     13:    You should have received a copy of the GNU General Public License
                     14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
                     15: */
                     16: 
                     17: #include "dnsmasq.h"
                     18: 
                     19: #ifdef HAVE_SCRIPT
                     20: 
                     21: /* This file has code to fork a helper process which recieves data via a pipe 
                     22:    shared with the main process and which is responsible for calling a script when
                     23:    DHCP leases change.
                     24: 
                     25:    The helper process is forked before the main process drops root, so it retains root 
                     26:    privs to pass on to the script. For this reason it tries to be paranoid about 
                     27:    data received from the main process, in case that has been compromised. We don't
                     28:    want the helper to give an attacker root. In particular, the script to be run is
                     29:    not settable via the pipe, once the fork has taken place it is not alterable by the 
                     30:    main process.
                     31: */
                     32: 
                     33: static void my_setenv(const char *name, const char *value, int *error);
                     34: static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end,  char *env, int *err);
                     35: 
                     36: #ifdef HAVE_LUASCRIPT
                     37: #define LUA_COMPAT_ALL
                     38: #include <lua.h>  
                     39: #include <lualib.h>  
                     40: #include <lauxlib.h>  
                     41: 
                     42: #ifndef lua_open
                     43: #define lua_open()     luaL_newstate()
                     44: #endif
                     45: 
                     46: lua_State *lua;
                     47: 
                     48: static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
                     49: #endif
                     50: 
                     51: 
                     52: struct script_data
                     53: {
                     54:   int flags;
                     55:   int action, hwaddr_len, hwaddr_type;
                     56:   int clid_len, hostname_len, ed_len;
                     57:   struct in_addr addr, giaddr;
                     58:   unsigned int remaining_time;
                     59: #ifdef HAVE_BROKEN_RTC
                     60:   unsigned int length;
                     61: #else
                     62:   time_t expires;
                     63: #endif
                     64:   unsigned char hwaddr[DHCP_CHADDR_MAX];
                     65:   char interface[IF_NAMESIZE];
                     66: 
                     67: };
                     68: 
                     69: static struct script_data *buf = NULL;
                     70: static size_t bytes_in_buf = 0, buf_size = 0;
                     71: 
                     72: int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
                     73: {
                     74:   pid_t pid;
                     75:   int i, pipefd[2];
                     76:   struct sigaction sigact;
                     77: 
                     78:   /* create the pipe through which the main program sends us commands,
                     79:      then fork our process. */
                     80:   if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
                     81:     {
                     82:       send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
                     83:       _exit(0);
                     84:     }
                     85: 
                     86:   if (pid != 0)
                     87:     {
                     88:       close(pipefd[0]); /* close reader side */
                     89:       return pipefd[1];
                     90:     }
                     91: 
                     92:   /* ignore SIGTERM, so that we can clean up when the main process gets hit
                     93:      and SIGALRM so that we can use sleep() */
                     94:   sigact.sa_handler = SIG_IGN;
                     95:   sigact.sa_flags = 0;
                     96:   sigemptyset(&sigact.sa_mask);
                     97:   sigaction(SIGTERM, &sigact, NULL);
                     98:   sigaction(SIGALRM, &sigact, NULL);
                     99: 
                    100:   if (!option_bool(OPT_DEBUG) && uid != 0)
                    101:     {
                    102:       gid_t dummy;
                    103:       if (setgroups(0, &dummy) == -1 || 
                    104:          setgid(gid) == -1 || 
                    105:          setuid(uid) == -1)
                    106:        {
                    107:          if (option_bool(OPT_NO_FORK))
                    108:            /* send error to daemon process if no-fork */
                    109:            send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
                    110:          else
                    111:            {
                    112:              /* kill daemon */
                    113:              send_event(event_fd, EVENT_DIE, 0, NULL);
                    114:              /* return error */
                    115:              send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
                    116:            }
                    117:          _exit(0);
                    118:        }
                    119:     }
                    120: 
                    121:   /* close all the sockets etc, we don't need them here. 
                    122:      Don't close err_fd, in case the lua-init fails.
                    123:      Note that we have to do this before lua init
                    124:      so we don't close any lua fds. */
                    125:   for (max_fd--; max_fd >= 0; max_fd--)
                    126:     if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && 
                    127:        max_fd != STDIN_FILENO && max_fd != pipefd[0] && 
                    128:        max_fd != event_fd && max_fd != err_fd)
                    129:       close(max_fd);
                    130:   
                    131: #ifdef HAVE_LUASCRIPT
                    132:   if (daemon->luascript)
                    133:     {
                    134:       const char *lua_err = NULL;
                    135:       lua = lua_open();
                    136:       luaL_openlibs(lua);
                    137:       
                    138:       /* get Lua to load our script file */
                    139:       if (luaL_dofile(lua, daemon->luascript) != 0)
                    140:        lua_err = lua_tostring(lua, -1);
                    141:       else
                    142:        {
                    143:          lua_getglobal(lua, "lease");
                    144:          if (lua_type(lua, -1) != LUA_TFUNCTION) 
                    145:            lua_err = _("lease() function missing in Lua script");
                    146:        }
                    147:       
                    148:       if (lua_err)
                    149:        {
                    150:          if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
                    151:            /* send error to daemon process if no-fork */
                    152:            send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
                    153:          else
                    154:            {
                    155:              /* kill daemon */
                    156:              send_event(event_fd, EVENT_DIE, 0, NULL);
                    157:              /* return error */
                    158:              send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
                    159:            }
                    160:          _exit(0);
                    161:        }
                    162:       
                    163:       lua_pop(lua, 1);  /* remove nil from stack */
                    164:       lua_getglobal(lua, "init");
                    165:       if (lua_type(lua, -1) == LUA_TFUNCTION)
                    166:        lua_call(lua, 0, 0);
                    167:       else
                    168:        lua_pop(lua, 1);  /* remove nil from stack */   
                    169:     }
                    170: #endif
                    171: 
                    172:   /* All init done, close our copy of the error pipe, so that main process can return */
                    173:   if (err_fd != -1)
                    174:     close(err_fd);
                    175:     
                    176:   /* loop here */
                    177:   while(1)
                    178:     {
                    179:       struct script_data data;
                    180:       char *p, *action_str, *hostname = NULL, *domain = NULL;
                    181:       unsigned char *buf = (unsigned char *)daemon->namebuff;
                    182:       unsigned char *end, *extradata, *alloc_buff = NULL;
                    183:       int is6, err = 0;
                    184: 
                    185:       free(alloc_buff);
                    186:       
                    187:       /* we read zero bytes when pipe closed: this is our signal to exit */ 
                    188:       if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
                    189:        {
                    190: #ifdef HAVE_LUASCRIPT
                    191:          if (daemon->luascript)
                    192:            {
                    193:              lua_getglobal(lua, "shutdown");
                    194:              if (lua_type(lua, -1) == LUA_TFUNCTION)
                    195:                lua_call(lua, 0, 0);
                    196:            }
                    197: #endif
                    198:          _exit(0);
                    199:        }
                    200:  
                    201:       is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
                    202:       
                    203:       if (data.action == ACTION_DEL)
                    204:        action_str = "del";
                    205:       else if (data.action == ACTION_ADD)
                    206:        action_str = "add";
                    207:       else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
                    208:        action_str = "old";
                    209:       else if (data.action == ACTION_TFTP)
                    210:        {
                    211:          action_str = "tftp";
                    212:          is6 = (data.flags != AF_INET);
                    213:        }
                    214:       else
                    215:        continue;
                    216: 
                    217:        
                    218:       if (!is6)
                    219:        {
                    220:          /* stringify MAC into dhcp_buff */
                    221:          p = daemon->dhcp_buff;
                    222:          if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0) 
                    223:            p += sprintf(p, "%.2x-", data.hwaddr_type);
                    224:          for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
                    225:            {
                    226:              p += sprintf(p, "%.2x", data.hwaddr[i]);
                    227:              if (i != data.hwaddr_len - 1)
                    228:                p += sprintf(p, ":");
                    229:            }
                    230:        }
                    231:        
                    232:       /* supplied data may just exceed normal buffer (unlikely) */
                    233:       if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME && 
                    234:          !(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
                    235:        continue;
                    236:       
                    237:       if (!read_write(pipefd[0], buf, 
                    238:                      data.hostname_len + data.ed_len + data.clid_len, 1))
                    239:        continue;
                    240: 
                    241:       /* CLID into packet */
                    242:       if (!is6)
                    243:        for (p = daemon->packet, i = 0; i < data.clid_len; i++)
                    244:          {
                    245:            p += sprintf(p, "%.2x", buf[i]);
                    246:            if (i != data.clid_len - 1) 
                    247:              p += sprintf(p, ":");
                    248:          }
                    249: #ifdef HAVE_DHCP6
                    250:       else
                    251:        {
                    252:          /* or IAID and server DUID for IPv6 */
                    253:          sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.hwaddr_type);      
                    254:          for (p = daemon->packet, i = 0; i < daemon->duid_len; i++)
                    255:            {
                    256:              p += sprintf(p, "%.2x", daemon->duid[i]);
                    257:              if (i != daemon->duid_len - 1) 
                    258:                p += sprintf(p, ":");
                    259:            }
                    260: 
                    261:          /* duid not MAC for IPv6 */
                    262:          for (p = daemon->dhcp_buff, i = 0; i < data.clid_len; i++)
                    263:            {
                    264:              p += sprintf(p, "%.2x", buf[i]);
                    265:              if (i != data.clid_len - 1) 
                    266:                p += sprintf(p, ":");
                    267:            } 
                    268:        }
                    269: #endif
                    270: 
                    271:       buf += data.clid_len;
                    272: 
                    273:       if (data.hostname_len != 0)
                    274:        {
                    275:          char *dot;
                    276:          hostname = (char *)buf;
                    277:          hostname[data.hostname_len - 1] = 0;
                    278:          if (data.action != ACTION_TFTP)
                    279:            {
                    280:              if (!legal_hostname(hostname))
                    281:                hostname = NULL;
                    282:              else if ((dot = strchr(hostname, '.')))
                    283:                {
                    284:                  domain = dot+1;
                    285:                  *dot = 0;
                    286:                } 
                    287:            }
                    288:        }
                    289:     
                    290:       extradata = buf + data.hostname_len;
                    291:     
                    292:       if (!is6)
                    293:        inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
                    294: #ifdef HAVE_DHCP6
                    295:       else
                    296:        inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
                    297: #endif
                    298: 
                    299:       /* file length */
                    300:       if (data.action == ACTION_TFTP)
                    301:        sprintf(daemon->dhcp_buff, "%u", data.hwaddr_len);
                    302:       
                    303: #ifdef HAVE_LUASCRIPT
                    304:       if (daemon->luascript)
                    305:        {
                    306:          if (data.action == ACTION_TFTP)
                    307:            {
                    308:              lua_getglobal(lua, "tftp"); 
                    309:              if (lua_type(lua, -1) != LUA_TFUNCTION)
                    310:                lua_pop(lua, 1); /* tftp function optional */
                    311:              else
                    312:                {
                    313:                  lua_pushstring(lua, action_str); /* arg1 - action */
                    314:                  lua_newtable(lua);               /* arg2 - data table */
                    315:                  lua_pushstring(lua, daemon->addrbuff);
                    316:                  lua_setfield(lua, -2, "destination_address");
                    317:                  lua_pushstring(lua, hostname);
                    318:                  lua_setfield(lua, -2, "file_name"); 
                    319:                  lua_pushstring(lua, daemon->dhcp_buff);
                    320:                  lua_setfield(lua, -2, "file_size");
                    321:                  lua_call(lua, 2, 0);  /* pass 2 values, expect 0 */
                    322:                }
                    323:            }
                    324:          else
                    325:            {
                    326:              lua_getglobal(lua, "lease");     /* function to call */
                    327:              lua_pushstring(lua, action_str); /* arg1 - action */
                    328:              lua_newtable(lua);               /* arg2 - data table */
                    329:              
                    330:              if (is6)
                    331:                {
                    332:                  lua_pushstring(lua, daemon->dhcp_buff);
                    333:                  lua_setfield(lua, -2, "client_duid");
                    334:                  lua_pushstring(lua, daemon->packet);
                    335:                  lua_setfield(lua, -2, "server_duid");
                    336:                  lua_pushstring(lua, daemon->dhcp_buff3);
                    337:                  lua_setfield(lua, -2, "iaid");
                    338:                }
                    339:              
                    340:              if (!is6 && data.clid_len != 0)
                    341:                {
                    342:                  lua_pushstring(lua, daemon->packet);
                    343:                  lua_setfield(lua, -2, "client_id");
                    344:                }
                    345:              
                    346:              if (strlen(data.interface) != 0)
                    347:                {
                    348:                  lua_pushstring(lua, data.interface);
                    349:                  lua_setfield(lua, -2, "interface");
                    350:                }
                    351:              
                    352: #ifdef HAVE_BROKEN_RTC 
                    353:              lua_pushnumber(lua, data.length);
                    354:              lua_setfield(lua, -2, "lease_length");
                    355: #else
                    356:              lua_pushnumber(lua, data.expires);
                    357:              lua_setfield(lua, -2, "lease_expires");
                    358: #endif
                    359:              
                    360:              if (hostname)
                    361:                {
                    362:                  lua_pushstring(lua, hostname);
                    363:                  lua_setfield(lua, -2, "hostname");
                    364:                }
                    365:              
                    366:              if (domain)
                    367:                {
                    368:                  lua_pushstring(lua, domain);
                    369:                  lua_setfield(lua, -2, "domain");
                    370:                }
                    371:              
                    372:              end = extradata + data.ed_len;
                    373:              buf = extradata;
                    374:              
                    375:              if (!is6)
                    376:                buf = grab_extradata_lua(buf, end, "vendor_class");
                    377: #ifdef HAVE_DHCP6
                    378:              else
                    379:                for (i = 0; i < data.hwaddr_len; i++)
                    380:                  {
                    381:                    sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
                    382:                    buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
                    383:                  }
                    384: #endif
                    385:              
                    386:              buf = grab_extradata_lua(buf, end, "supplied_hostname");
                    387:              
                    388:              if (!is6)
                    389:                {
                    390:                  buf = grab_extradata_lua(buf, end, "cpewan_oui");
                    391:                  buf = grab_extradata_lua(buf, end, "cpewan_serial");   
                    392:                  buf = grab_extradata_lua(buf, end, "cpewan_class");
                    393:                  buf = grab_extradata_lua(buf, end, "circuit_id");
                    394:                  buf = grab_extradata_lua(buf, end, "subscriber_id");
                    395:                  buf = grab_extradata_lua(buf, end, "remote_id");
                    396:                }
                    397:              
                    398:              buf = grab_extradata_lua(buf, end, "tags");
                    399:              
                    400:              if (is6)
                    401:                buf = grab_extradata_lua(buf, end, "relay_address");
                    402:              else if (data.giaddr.s_addr != 0)
                    403:                {
                    404:                  lua_pushstring(lua, inet_ntoa(data.giaddr));
                    405:                  lua_setfield(lua, -2, "relay_address");
                    406:                }
                    407:              
                    408:              for (i = 0; buf; i++)
                    409:                {
                    410:                  sprintf(daemon->dhcp_buff2, "user_class%i", i);
                    411:                  buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
                    412:                }
                    413:              
                    414:              if (data.action != ACTION_DEL && data.remaining_time != 0)
                    415:                {
                    416:                  lua_pushnumber(lua, data.remaining_time);
                    417:                  lua_setfield(lua, -2, "time_remaining");
                    418:                }
                    419:              
                    420:              if (data.action == ACTION_OLD_HOSTNAME && hostname)
                    421:                {
                    422:                  lua_pushstring(lua, hostname);
                    423:                  lua_setfield(lua, -2, "old_hostname");
                    424:                }
                    425:              
                    426:              if (!is6)
                    427:                {
                    428:                  lua_pushstring(lua, daemon->dhcp_buff);
                    429:                  lua_setfield(lua, -2, "mac_address");
                    430:                }
                    431:              
                    432:              lua_pushstring(lua, daemon->addrbuff);
                    433:              lua_setfield(lua, -2, "ip_address");
                    434:            
                    435:              lua_call(lua, 2, 0);      /* pass 2 values, expect 0 */
                    436:            }
                    437:        }
                    438: #endif
                    439: 
                    440:       /* no script, just lua */
                    441:       if (!daemon->lease_change_command)
                    442:        continue;
                    443: 
                    444:       /* possible fork errors are all temporary resource problems */
                    445:       while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
                    446:        sleep(2);
                    447: 
                    448:       if (pid == -1)
                    449:        continue;
                    450:       
                    451:       /* wait for child to complete */
                    452:       if (pid != 0)
                    453:        {
                    454:          /* reap our children's children, if necessary */
                    455:          while (1)
                    456:            {
                    457:              int status;
                    458:              pid_t rc = wait(&status);
                    459:              
                    460:              if (rc == pid)
                    461:                {
                    462:                  /* On error send event back to main process for logging */
                    463:                  if (WIFSIGNALED(status))
                    464:                    send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
                    465:                  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
                    466:                    send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
                    467:                  break;
                    468:                }
                    469:              
                    470:              if (rc == -1 && errno != EINTR)
                    471:                break;
                    472:            }
                    473:          
                    474:          continue;
                    475:        }
                    476:       
                    477:       if (data.action != ACTION_TFTP)
                    478:        {
                    479:          if (is6)
                    480:            {
                    481:              my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
                    482:              my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
                    483:            }
                    484:          
                    485:          if (!is6 && data.clid_len != 0)
                    486:            my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
                    487:          
                    488:          if (strlen(data.interface) != 0)
                    489:            my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
                    490:          
                    491: #ifdef HAVE_BROKEN_RTC
                    492:          sprintf(daemon->dhcp_buff2, "%u", data.length);
                    493:          my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
                    494: #else
                    495:          sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
                    496:          my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err); 
                    497: #endif
                    498:          
                    499:          if (domain)
                    500:            my_setenv("DNSMASQ_DOMAIN", domain, &err);
                    501:          
                    502:          end = extradata + data.ed_len;
                    503:          buf = extradata;
                    504:          
                    505:          if (!is6)
                    506:            buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
                    507: #ifdef HAVE_DHCP6
                    508:          else
                    509:            {
                    510:              if (data.hwaddr_len != 0)
                    511:                {
                    512:                  buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
                    513:                  for (i = 0; i < data.hwaddr_len - 1; i++)
                    514:                    {
                    515:                      sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
                    516:                      buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
                    517:                    }
                    518:                }
                    519:            }
                    520: #endif
                    521:          
                    522:          buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
                    523:          
                    524:          if (!is6)
                    525:            {
                    526:              buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
                    527:              buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);   
                    528:              buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
                    529:              buf = grab_extradata(buf, end, "DNSMASQ_CIRCUIT_ID", &err);
                    530:              buf = grab_extradata(buf, end, "DNSMASQ_SUBSCRIBER_ID", &err);
                    531:              buf = grab_extradata(buf, end, "DNSMASQ_REMOTE_ID", &err);
                    532:            }
                    533:          
                    534:          buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
                    535: 
                    536:          if (is6)
                    537:            buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
                    538:          else if (data.giaddr.s_addr != 0)
                    539:            my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err); 
                    540:          
                    541:          for (i = 0; buf; i++)
                    542:            {
                    543:              sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
                    544:              buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
                    545:            }
                    546:          
                    547:          if (data.action != ACTION_DEL && data.remaining_time != 0)
                    548:            {
                    549:              sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
                    550:              my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
                    551:            }
                    552:          
                    553:          if (data.action == ACTION_OLD_HOSTNAME && hostname)
                    554:            {
                    555:              my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
                    556:              hostname = NULL;
                    557:            }
                    558:        }
                    559: 
                    560:       if (option_bool(OPT_LOG_OPTS))
                    561:        my_setenv("DNSMASQ_LOG_DHCP", "1", &err);
                    562:         
                    563:       /* we need to have the event_fd around if exec fails */
                    564:       if ((i = fcntl(event_fd, F_GETFD)) != -1)
                    565:        fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
                    566:       close(pipefd[0]);
                    567: 
                    568:       p =  strrchr(daemon->lease_change_command, '/');
                    569:       if (err == 0)
                    570:        {
                    571:          execl(daemon->lease_change_command, 
                    572:                p ? p+1 : daemon->lease_change_command,
                    573:                action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL);
                    574:          err = errno;
                    575:        }
                    576:       /* failed, send event so the main process logs the problem */
                    577:       send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
                    578:       _exit(0); 
                    579:     }
                    580: }
                    581: 
                    582: static void my_setenv(const char *name, const char *value, int *error)
                    583: {
                    584:   if (*error == 0 && setenv(name, value, 1) != 0)
                    585:     *error = errno;
                    586: }
                    587:  
                    588: static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end,  char *env, int *err)
                    589: {
                    590:   unsigned char *next;
                    591: 
                    592:   if (!buf || (buf == end))
                    593:     return NULL;
                    594: 
                    595:   for (next = buf; *next != 0; next++)
                    596:     if (next == end)
                    597:       return NULL;
                    598:   
                    599:   if (next != buf)
                    600:     {
                    601:       char *p;
                    602:       /* No "=" in value */
                    603:       if ((p = strchr((char *)buf, '=')))
                    604:        *p = 0;
                    605:       my_setenv(env, (char *)buf, err);
                    606:     }
                    607: 
                    608:   return next + 1;
                    609: }
                    610: 
                    611: #ifdef HAVE_LUASCRIPT
                    612: static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
                    613: {
                    614:   unsigned char *next;
                    615: 
                    616:   if (!buf || (buf == end))
                    617:     return NULL;
                    618: 
                    619:   for (next = buf; *next != 0; next++)
                    620:     if (next == end)
                    621:       return NULL;
                    622:   
                    623:   if (next != buf)
                    624:     {
                    625:       lua_pushstring(lua,  (char *)buf);
                    626:       lua_setfield(lua, -2, field);
                    627:     }
                    628: 
                    629:   return next + 1;
                    630: }
                    631: #endif
                    632: 
                    633: static void buff_alloc(size_t size)
                    634: {
                    635:   if (size > buf_size)
                    636:     {
                    637:       struct script_data *new;
                    638:       
                    639:       /* start with reasonable size, will almost never need extending. */
                    640:       if (size < sizeof(struct script_data) + 200)
                    641:        size = sizeof(struct script_data) + 200;
                    642: 
                    643:       if (!(new = whine_malloc(size)))
                    644:        return;
                    645:       if (buf)
                    646:        free(buf);
                    647:       buf = new;
                    648:       buf_size = size;
                    649:     }
                    650: }
                    651: 
                    652: /* pack up lease data into a buffer */    
                    653: void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
                    654: {
                    655:   unsigned char *p;
                    656:   unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
                    657:   int fd = daemon->dhcpfd;
                    658: #ifdef HAVE_DHCP6 
                    659:   int is6 = !!(lease->flags & (LEASE_TA | LEASE_NA));
                    660: 
                    661:   if (!daemon->dhcp)
                    662:     fd = daemon->dhcp6fd;
                    663: #endif
                    664: 
                    665:   /* no script */
                    666:   if (daemon->helperfd == -1)
                    667:     return;
                    668: 
                    669:   if (lease->extradata)
                    670:     ed_len = lease->extradata_len;
                    671:   if (lease->clid)
                    672:     clid_len = lease->clid_len;
                    673:   if (hostname)
                    674:     hostname_len = strlen(hostname) + 1;
                    675: 
                    676:   buff_alloc(sizeof(struct script_data) +  clid_len + ed_len + hostname_len);
                    677: 
                    678:   buf->action = action;
                    679:   buf->flags = lease->flags;
                    680: #ifdef HAVE_DHCP6 
                    681:   if (is6)
                    682:     buf->hwaddr_len = lease->vendorclass_count;
                    683:   else
                    684: #endif
                    685:     buf->hwaddr_len = lease->hwaddr_len;
                    686:   buf->hwaddr_type = lease->hwaddr_type;
                    687:   buf->clid_len = clid_len;
                    688:   buf->ed_len = ed_len;
                    689:   buf->hostname_len = hostname_len;
                    690:   buf->addr = lease->addr;
                    691:   buf->giaddr = lease->giaddr;
                    692:   memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
                    693:   if (!indextoname(fd, lease->last_interface, buf->interface))
                    694:     buf->interface[0] = 0;
                    695:   
                    696: #ifdef HAVE_BROKEN_RTC 
                    697:   buf->length = lease->length;
                    698: #else
                    699:   buf->expires = lease->expires;
                    700: #endif
                    701: 
                    702:   if (lease->expires != 0)
                    703:     buf->remaining_time = (unsigned int)difftime(lease->expires, now);
                    704:   else
                    705:     buf->remaining_time = 0;
                    706: 
                    707:   p = (unsigned char *)(buf+1);
                    708:   if (clid_len != 0)
                    709:     {
                    710:       memcpy(p, lease->clid, clid_len);
                    711:       p += clid_len;
                    712:     }
                    713:   if (hostname_len != 0)
                    714:     {
                    715:       memcpy(p, hostname, hostname_len);
                    716:       p += hostname_len;
                    717:     }
                    718:   if (ed_len != 0)
                    719:     {
                    720:       memcpy(p, lease->extradata, ed_len);
                    721:       p += ed_len;
                    722:     }
                    723:   bytes_in_buf = p - (unsigned char *)buf;
                    724: }
                    725: 
                    726: #ifdef HAVE_TFTP
                    727: /* This nastily re-uses DHCP-fields for TFTP stuff */
                    728: void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
                    729: {
                    730:   unsigned int filename_len;
                    731: 
                    732:   /* no script */
                    733:   if (daemon->helperfd == -1)
                    734:     return;
                    735:   
                    736:   filename_len = strlen(filename) + 1;
                    737:   buff_alloc(sizeof(struct script_data) +  filename_len);
                    738:   memset(buf, 0, sizeof(struct script_data));
                    739: 
                    740:   buf->action = ACTION_TFTP;
                    741:   buf->hostname_len = filename_len;
                    742:   buf->hwaddr_len = file_len;
                    743: 
                    744:   if ((buf->flags = peer->sa.sa_family) == AF_INET)
                    745:     buf->addr = peer->in.sin_addr;
                    746: #ifdef HAVE_IPV6
                    747:   else
                    748:     memcpy(buf->hwaddr, &peer->in6.sin6_addr, IN6ADDRSZ);
                    749: #endif
                    750: 
                    751:   memcpy((unsigned char *)(buf+1), filename, filename_len);
                    752:   
                    753:   bytes_in_buf = sizeof(struct script_data) +  filename_len;
                    754: }
                    755: #endif
                    756: 
                    757: int helper_buf_empty(void)
                    758: {
                    759:   return bytes_in_buf == 0;
                    760: }
                    761: 
                    762: void helper_write(void)
                    763: {
                    764:   ssize_t rc;
                    765: 
                    766:   if (bytes_in_buf == 0)
                    767:     return;
                    768:   
                    769:   if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
                    770:     {
                    771:       if (bytes_in_buf != (size_t)rc)
                    772:        memmove(buf, buf + rc, bytes_in_buf - rc); 
                    773:       bytes_in_buf -= rc;
                    774:     }
                    775:   else
                    776:     {
                    777:       if (errno == EAGAIN || errno == EINTR)
                    778:        return;
                    779:       bytes_in_buf = 0;
                    780:     }
                    781: }
                    782: 
                    783: #endif
                    784: 
                    785: 
                    786: 

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