Annotation of embedaddon/miniupnpd/minissdpd/minissdpd.c, revision 1.1.1.1

1.1       misho       1: /* $Id: minissdpd.c,v 1.61 2021/11/04 23:27:28 nanard Exp $ */
                      2: /* vim: tabstop=4 shiftwidth=4 noexpandtab
                      3:  * MiniUPnP project
                      4:  * (c) 2007-2022 Thomas Bernard
                      5:  * website : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
                      6:  * This software is subject to the conditions detailed
                      7:  * in the LICENCE file provided within the distribution */
                      8: 
                      9: #include "config.h"
                     10: 
                     11: #include <stdlib.h>
                     12: #include <stdio.h>
                     13: #include <string.h>
                     14: #include <signal.h>
                     15: #include <errno.h>
                     16: #include <sys/time.h>
                     17: #include <sys/types.h>
                     18: #include <sys/socket.h>
                     19: #include <unistd.h>
                     20: #include <netinet/in.h>
                     21: #include <arpa/inet.h>
                     22: #include <syslog.h>
                     23: #include <ctype.h>
                     24: #include <time.h>
                     25: #include <sys/queue.h>
                     26: /* for chmod : */
                     27: #include <sys/stat.h>
                     28: /* unix sockets */
                     29: #include <sys/un.h>
                     30: /* for getpwnam() and getgrnam() */
                     31: #if 0
                     32: #include <pwd.h>
                     33: #include <grp.h>
                     34: #endif
                     35: 
                     36: /* LOG_PERROR does not exist on Solaris */
                     37: #ifndef LOG_PERROR
                     38: #define LOG_PERROR 0
                     39: #endif /* LOG_PERROR */
                     40: 
                     41: #include "getifaddr.h"
                     42: #include "upnputils.h"
                     43: #include "openssdpsocket.h"
                     44: #include "daemonize.h"
                     45: #include "codelength.h"
                     46: #include "ifacewatch.h"
                     47: #include "minissdpdtypes.h"
                     48: #include "asyncsendto.h"
                     49: 
                     50: #define SET_MAX(max, x)    if((x) > (max)) (max) = (x)
                     51: #ifndef MIN
                     52: #define MIN(x,y) (((x)<(y))?(x):(y))
                     53: #endif
                     54: 
                     55: /* current request management structure */
                     56: struct reqelem {
                     57:    int socket;
                     58:    int is_notify;  /* has subscribed to notifications */
                     59:    LIST_ENTRY(reqelem) entries;
                     60:    unsigned char * output_buffer;
                     61:    int output_buffer_offset;
                     62:    int output_buffer_len;
                     63: };
                     64: 
                     65: /* device data structures */
                     66: struct header {
                     67:    const char * p; /* string pointer */
                     68:    int l;          /* string length */
                     69: };
                     70: 
                     71: #define HEADER_NT  0
                     72: #define HEADER_USN 1
                     73: #define HEADER_LOCATION    2
                     74: 
                     75: struct device {
                     76:    struct device * next;
                     77:    time_t t;                 /* validity time */
                     78:    struct header headers[3]; /* NT, USN and LOCATION headers */
                     79:    char data[];
                     80: };
                     81: 
                     82: /* Services stored for answering to M-SEARCH */
                     83: struct service {
                     84:    char * st;  /* Service type */
                     85:    char * usn; /* Unique identifier */
                     86:    char * server;  /* Server string */
                     87:    char * location;    /* URL */
                     88:    LIST_ENTRY(service) entries;
                     89: };
                     90: LIST_HEAD(servicehead, service) servicelisthead;
                     91: 
                     92: #define NTS_SSDP_ALIVE 1
                     93: #define NTS_SSDP_BYEBYE    2
                     94: #define NTS_SSDP_UPDATE    3
                     95: 
                     96: /* request types */
                     97: enum request_type {
                     98:    MINISSDPD_GET_VERSION = 0,
                     99:    MINISSDPD_SEARCH_TYPE = 1,
                    100:    MINISSDPD_SEARCH_USN = 2,
                    101:    MINISSDPD_SEARCH_ALL = 3,
                    102:    MINISSDPD_SUBMIT = 4,
                    103:    MINISSDPD_NOTIF = 5
                    104: };
                    105: 
                    106: /* discovered device list kept in memory */
                    107: struct device * devlist = 0;
                    108: 
                    109: /* bootid and configid */
                    110: unsigned int upnp_bootid = 1;
                    111: unsigned int upnp_configid = 1337;
                    112: 
                    113: /* LAN interfaces/addresses */
                    114: struct lan_addr_list lan_addrs;
                    115: 
                    116: /* connected clients */
                    117: LIST_HEAD(reqstructhead, reqelem) reqlisthead;
                    118: 
                    119: /* functions prototypes */
                    120: 
                    121: #define NOTIF_NEW    1
                    122: #define NOTIF_UPDATE 2
                    123: #define NOTIF_REMOVE 3
                    124: static void
                    125: sendNotifications(int notif_type, const struct device * dev, const struct service * serv);
                    126: 
                    127: /* functions */
                    128: 
                    129: /* parselanaddr()
                    130:  * parse address with mask
                    131:  * ex: 192.168.1.1/24 or 192.168.1.1/255.255.255.0
                    132:  *
                    133:  * Can also use the interface name (ie eth0)
                    134:  *
                    135:  * return value :
                    136:  *    0 : ok
                    137:  *   -1 : error */
                    138: static int
                    139: parselanaddr(struct lan_addr_s * lan_addr, const char * str)
                    140: {
                    141:    const char * p;
                    142:    int n;
                    143:    char tmp[16];
                    144: 
                    145:    memset(lan_addr, 0, sizeof(struct lan_addr_s));
                    146:    p = str;
                    147:    while(*p && *p != '/' && !isspace(*p))
                    148:        p++;
                    149:    n = p - str;
                    150:    if(!isdigit(str[0]) && n < (int)sizeof(lan_addr->ifname)) {
                    151:        /* not starting with a digit : suppose it is an interface name */
                    152:        memcpy(lan_addr->ifname, str, n);
                    153:        lan_addr->ifname[n] = '\0';
                    154:        if(getifaddr(lan_addr->ifname, lan_addr->str, sizeof(lan_addr->str),
                    155:                     &lan_addr->addr, &lan_addr->mask) < 0)
                    156:            goto parselan_error;
                    157:        /*printf("%s => %s\n", lan_addr->ifname, lan_addr->str);*/
                    158:    } else {
                    159:        if(n>15)
                    160:            goto parselan_error;
                    161:        memcpy(lan_addr->str, str, n);
                    162:        lan_addr->str[n] = '\0';
                    163:        if(!inet_aton(lan_addr->str, &lan_addr->addr))
                    164:            goto parselan_error;
                    165:    }
                    166:    if(*p == '/') {
                    167:        const char * q = ++p;
                    168:        while(*p && isdigit(*p))
                    169:            p++;
                    170:        if(*p=='.') {
                    171:            /* parse mask in /255.255.255.0 format */
                    172:            while(*p && (*p=='.' || isdigit(*p)))
                    173:                p++;
                    174:            n = p - q;
                    175:            if(n>15)
                    176:                goto parselan_error;
                    177:            memcpy(tmp, q, n);
                    178:            tmp[n] = '\0';
                    179:            if(!inet_aton(tmp, &lan_addr->mask))
                    180:                goto parselan_error;
                    181:        } else {
                    182:            /* it is a /24 format */
                    183:            int nbits = atoi(q);
                    184:            if(nbits > 32 || nbits < 0)
                    185:                goto parselan_error;
                    186:            lan_addr->mask.s_addr = htonl(nbits ? (0xffffffffu << (32 - nbits)) : 0);
                    187:        }
                    188:    } else if(lan_addr->mask.s_addr == 0) {
                    189:        /* by default, networks are /24 */
                    190:        lan_addr->mask.s_addr = htonl(0xffffff00u);
                    191:    }
                    192: #ifdef ENABLE_IPV6
                    193:    if(lan_addr->ifname[0] != '\0') {
                    194:        lan_addr->index = if_nametoindex(lan_addr->ifname);
                    195:        if(lan_addr->index == 0)
                    196:            fprintf(stderr, "Cannot get index for network interface %s\n",
                    197:                    lan_addr->ifname);
                    198:    } else {
                    199:        fprintf(stderr,
                    200:                "Error: please specify LAN network interface by name instead of IPv4 address : %s\n",
                    201:                str);
                    202:        return -1;
                    203:    }
                    204: #endif /* ENABLE_IPV6 */
                    205:    return 0;
                    206: parselan_error:
                    207:    fprintf(stderr, "Error parsing address/mask (or interface name) : %s\n",
                    208:            str);
                    209:    return -1;
                    210: }
                    211: 
                    212: static int
                    213: write_buffer(struct reqelem * req)
                    214: {
                    215:    if(req->output_buffer && req->output_buffer_len > 0) {
                    216:        int n = write(req->socket,
                    217:                      req->output_buffer + req->output_buffer_offset,
                    218:                      req->output_buffer_len);
                    219:        if(n >= 0) {
                    220:            req->output_buffer_offset += n;
                    221:            req->output_buffer_len -= n;
                    222:        } else if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
                    223:            return 0;
                    224:        }
                    225:        return n;
                    226:    } else {
                    227:        return 0;
                    228:    }
                    229: }
                    230: 
                    231: static int
                    232: add_to_buffer(struct reqelem * req, const unsigned char * data, int len)
                    233: {
                    234:    unsigned char * tmp;
                    235:    if(req->output_buffer_offset > 0) {
                    236:        memmove(req->output_buffer, req->output_buffer + req->output_buffer_offset, req->output_buffer_len);
                    237:        req->output_buffer_offset = 0;
                    238:    }
                    239:    tmp = realloc(req->output_buffer, req->output_buffer_len + len);
                    240:    if(tmp == NULL) {
                    241:        syslog(LOG_ERR, "%s: failed to allocate %d bytes",
                    242:               __func__, req->output_buffer_len + len);
                    243:        return -1;
                    244:    }
                    245:    req->output_buffer = tmp;
                    246:    memcpy(req->output_buffer + req->output_buffer_len, data, len);
                    247:    req->output_buffer_len += len;
                    248:    return len;
                    249: }
                    250: 
                    251: static int
                    252: write_or_buffer(struct reqelem * req, const unsigned char * data, int len)
                    253: {
                    254:    if(write_buffer(req) < 0)
                    255:        return -1;
                    256:    if(req->output_buffer && req->output_buffer_len > 0) {
                    257:        return add_to_buffer(req, data, len);
                    258:    } else {
                    259:        int n = write(req->socket, data, len);
                    260:        if(n == len)
                    261:            return len;
                    262:        if(n < 0) {
                    263:            if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
                    264:                n = add_to_buffer(req, data, len);
                    265:                if(n < 0) return n;
                    266:            } else {
                    267:                return n;
                    268:            }
                    269:        } else {
                    270:            n = add_to_buffer(req, data + n, len - n);
                    271:            if(n < 0) return n;
                    272:        }
                    273:    }
                    274:    return len;
                    275: }
                    276: 
                    277: static const char *
                    278: nts_to_str(int nts)
                    279: {
                    280:    switch(nts)
                    281:    {
                    282:    case NTS_SSDP_ALIVE:
                    283:        return "ssdp:alive";
                    284:    case NTS_SSDP_BYEBYE:
                    285:        return "ssdp:byebye";
                    286:    case NTS_SSDP_UPDATE:
                    287:        return "ssdp:update";
                    288:    }
                    289:    return "unknown";
                    290: }
                    291: 
                    292: /* updateDevice() :
                    293:  * adds or updates the device to the list.
                    294:  * return value :
                    295:  *   0 : the device was updated (or nothing done)
                    296:  *   1 : the device was new    */
                    297: static int
                    298: updateDevice(const struct header * headers, time_t t)
                    299: {
                    300:    struct device ** pp = &devlist;
                    301:    struct device * p = *pp;    /* = devlist; */
                    302:    while(p)
                    303:    {
                    304:        if(  p->headers[HEADER_NT].l == headers[HEADER_NT].l
                    305:          && (0==memcmp(p->headers[HEADER_NT].p, headers[HEADER_NT].p, headers[HEADER_NT].l))
                    306:          && p->headers[HEADER_USN].l == headers[HEADER_USN].l
                    307:          && (0==memcmp(p->headers[HEADER_USN].p, headers[HEADER_USN].p, headers[HEADER_USN].l)) )
                    308:        {
                    309:            /*printf("found! %d\n", (int)(t - p->t));*/
                    310:            syslog(LOG_DEBUG, "device updated : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p);
                    311:            p->t = t;
                    312:            /* update Location ! */
                    313:            if(headers[HEADER_LOCATION].l > p->headers[HEADER_LOCATION].l)
                    314:            {
                    315:                struct device * tmp;
                    316:                tmp = realloc(p, sizeof(struct device)
                    317:                    + headers[0].l+headers[1].l+headers[2].l);
                    318:                if(!tmp)    /* allocation error */
                    319:                {
                    320:                    syslog(LOG_ERR, "updateDevice() : memory allocation error");
                    321:                    *pp = p->next;  /* remove "p" from the list */
                    322:                    free(p);
                    323:                    return 0;
                    324:                }
                    325:                p = tmp;
                    326:                *pp = p;
                    327:            }
                    328:            memcpy(p->data + p->headers[0].l + p->headers[1].l,
                    329:                   headers[2].p, headers[2].l);
                    330:            /* TODO : check p->headers[HEADER_LOCATION].l */
                    331:            return 0;
                    332:        }
                    333:        pp = &p->next;
                    334:        p = *pp;    /* p = p->next; */
                    335:    }
                    336:    syslog(LOG_INFO, "new device discovered : %.*s",
                    337:           headers[HEADER_USN].l, headers[HEADER_USN].p);
                    338:    /* add */
                    339:    {
                    340:        char * pc;
                    341:        int i;
                    342:        p = malloc(  sizeof(struct device)
                    343:                   + headers[0].l+headers[1].l+headers[2].l );
                    344:        if(!p) {
                    345:            syslog(LOG_ERR, "updateDevice(): cannot allocate memory");
                    346:            return -1;
                    347:        }
                    348:        p->next = devlist;
                    349:        p->t = t;
                    350:        pc = p->data;
                    351:        for(i = 0; i < 3; i++)
                    352:        {
                    353:            p->headers[i].p = pc;
                    354:            p->headers[i].l = headers[i].l;
                    355:            memcpy(pc, headers[i].p, headers[i].l);
                    356:            pc += headers[i].l;
                    357:        }
                    358:        devlist = p;
                    359:        sendNotifications(NOTIF_NEW, p, NULL);
                    360:    }
                    361:    return 1;
                    362: }
                    363: 
                    364: /* removeDevice() :
                    365:  * remove a device from the list
                    366:  * return value :
                    367:  *    0 : no device removed
                    368:  *   -1 : device removed */
                    369: static int
                    370: removeDevice(const struct header * headers)
                    371: {
                    372:    struct device ** pp = &devlist;
                    373:    struct device * p = *pp;    /* = devlist */
                    374:    while(p)
                    375:    {
                    376:        if(  p->headers[HEADER_NT].l == headers[HEADER_NT].l
                    377:          && (0==memcmp(p->headers[HEADER_NT].p, headers[HEADER_NT].p, headers[HEADER_NT].l))
                    378:          && p->headers[HEADER_USN].l == headers[HEADER_USN].l
                    379:          && (0==memcmp(p->headers[HEADER_USN].p, headers[HEADER_USN].p, headers[HEADER_USN].l)) )
                    380:        {
                    381:            syslog(LOG_INFO, "remove device : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p);
                    382:            sendNotifications(NOTIF_REMOVE, p, NULL);
                    383:            *pp = p->next;
                    384:            free(p);
                    385:            return -1;
                    386:        }
                    387:        pp = &p->next;
                    388:        p = *pp;    /* p = p->next; */
                    389:    }
                    390:    syslog(LOG_WARNING, "device not found for removing : %.*s", headers[HEADER_USN].l, headers[HEADER_USN].p);
                    391:    return 0;
                    392: }
                    393: 
                    394: /* sent notifications to client having subscribed */
                    395: static void
                    396: sendNotifications(int notif_type, const struct device * dev, const struct service * serv)
                    397: {
                    398:    struct reqelem * req;
                    399:    unsigned int m;
                    400:    unsigned char rbuf[RESPONSE_BUFFER_SIZE];
                    401:    unsigned char * rp;
                    402: 
                    403:    for(req = reqlisthead.lh_first; req; req = req->entries.le_next) {
                    404:        if(!req->is_notify) continue;
                    405:        rbuf[0] = '\xff'; /* special code for notifications */
                    406:        rbuf[1] = (unsigned char)notif_type;
                    407:        rbuf[2] = 0;
                    408:        rp = rbuf + 3;
                    409:        if(dev) {
                    410:            /* response :
                    411:             * 1 - Location
                    412:             * 2 - NT (device/service type)
                    413:             * 3 - usn */
                    414:            m = dev->headers[HEADER_LOCATION].l;
                    415:            CODELENGTH(m, rp);
                    416:            memcpy(rp, dev->headers[HEADER_LOCATION].p, dev->headers[HEADER_LOCATION].l);
                    417:            rp += dev->headers[HEADER_LOCATION].l;
                    418:            m = dev->headers[HEADER_NT].l;
                    419:            CODELENGTH(m, rp);
                    420:            memcpy(rp, dev->headers[HEADER_NT].p, dev->headers[HEADER_NT].l);
                    421:            rp += dev->headers[HEADER_NT].l;
                    422:            m = dev->headers[HEADER_USN].l;
                    423:            CODELENGTH(m, rp);
                    424:            memcpy(rp, dev->headers[HEADER_USN].p, dev->headers[HEADER_USN].l);
                    425:            rp += dev->headers[HEADER_USN].l;
                    426:            rbuf[2]++;
                    427:        }
                    428:        if(serv) {
                    429:            /* response :
                    430:             * 1 - Location
                    431:             * 2 - NT (device/service type)
                    432:             * 3 - usn */
                    433:            m = strlen(serv->location);
                    434:            CODELENGTH(m, rp);
                    435:            memcpy(rp, serv->location, m);
                    436:            rp += m;
                    437:            m = strlen(serv->st);
                    438:            CODELENGTH(m, rp);
                    439:            memcpy(rp, serv->st, m);
                    440:            rp += m;
                    441:            m = strlen(serv->usn);
                    442:            CODELENGTH(m, rp);
                    443:            memcpy(rp, serv->usn, m);
                    444:            rp += m;
                    445:            rbuf[2]++;
                    446:        }
                    447:        if(rbuf[2] > 0) {
                    448:            if(write_or_buffer(req, rbuf, rp - rbuf) < 0) {
                    449:                syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
                    450:                /*goto error;*/
                    451:            }
                    452:        }
                    453:    }
                    454: }
                    455: 
                    456: /* SendSSDPMSEARCHResponse() :
                    457:  * build and send response to M-SEARCH SSDP packets. */
                    458: static void
                    459: SendSSDPMSEARCHResponse(int s, const struct sockaddr * sockname,
                    460:                         const char * st, size_t st_len, const char * usn,
                    461:                         const char * server, const char * location)
                    462: {
                    463:    int l, n;
                    464:    char buf[1024];
                    465:    socklen_t sockname_len;
                    466:    /*
                    467:     * follow guideline from document "UPnP Device Architecture 1.0"
                    468:     * uppercase is recommended.
                    469:     * DATE: is recommended
                    470:     * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
                    471:     * - check what to put in the 'Cache-Control' header
                    472:     *
                    473:     * have a look at the document "UPnP Device Architecture v1.1 */
                    474:    l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
                    475:        "CACHE-CONTROL: max-age=120\r\n"
                    476:        /*"DATE: ...\r\n"*/
                    477:        "ST: %.*s\r\n"
                    478:        "USN: %s\r\n"
                    479:        "EXT:\r\n"
                    480:        "SERVER: %s\r\n"
                    481:        "LOCATION: %s\r\n"
                    482:        "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
                    483:        "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
                    484:        "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
                    485:        "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
                    486:        "\r\n",
                    487:        (int)st_len, st, usn,
                    488:        server, location,
                    489:        upnp_bootid, upnp_bootid, upnp_configid);
                    490: #ifdef ENABLE_IPV6
                    491:    sockname_len = (sockname->sa_family == PF_INET6)
                    492:                 ? sizeof(struct sockaddr_in6)
                    493:                 : sizeof(struct sockaddr_in);
                    494: #else  /* ENABLE_IPV6 */
                    495:    sockname_len = sizeof(struct sockaddr_in);
                    496: #endif /* ENABLE_IPV6 */
                    497:    n = sendto_or_schedule(s, buf, l, 0, sockname, sockname_len);
                    498:    if(n < 0) {
                    499:        syslog(LOG_ERR, "%s: sendto(udp): %m", __func__);
                    500:    }
                    501: }
                    502: 
                    503: /* Process M-SEARCH requests */
                    504: static void
                    505: processMSEARCH(int s, const char * st, size_t st_len,
                    506:                const struct sockaddr * addr)
                    507: {
                    508:    struct service * serv;
                    509: #ifdef ENABLE_IPV6
                    510:    char buf[64];
                    511: #endif /* ENABLE_IPV6 */
                    512: 
                    513:    if(!st || st_len==0)
                    514:        return;
                    515: #ifdef ENABLE_IPV6
                    516:    sockaddr_to_string(addr, buf, sizeof(buf));
                    517:    syslog(LOG_INFO, "SSDP M-SEARCH from %s ST:%.*s",
                    518:           buf, (int)st_len, st);
                    519: #else  /* ENABLE_IPV6 */
                    520:    syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s",
                    521:           inet_ntoa(((const struct sockaddr_in *)addr)->sin_addr),
                    522:           ntohs(((const struct sockaddr_in *)addr)->sin_port),
                    523:           (int)st_len, st);
                    524: #endif /* ENABLE_IPV6 */
                    525:    if(st_len==8 && (0==memcmp(st, "ssdp:all", 8))) {
                    526:        /* send a response for all services */
                    527:        for(serv = servicelisthead.lh_first;
                    528:            serv;
                    529:            serv = serv->entries.le_next) {
                    530:            SendSSDPMSEARCHResponse(s, addr,
                    531:                                    serv->st, strlen(serv->st), serv->usn,
                    532:                                    serv->server, serv->location);
                    533:        }
                    534:    } else if(st_len > 5 && (0==memcmp(st, "uuid:", 5))) {
                    535:        /* find a matching UUID value */
                    536:        for(serv = servicelisthead.lh_first;
                    537:            serv;
                    538:            serv = serv->entries.le_next) {
                    539:            if(0 == strncmp(serv->usn, st, st_len)) {
                    540:                SendSSDPMSEARCHResponse(s, addr,
                    541:                                        serv->st, strlen(serv->st), serv->usn,
                    542:                                        serv->server, serv->location);
                    543:            }
                    544:        }
                    545:    } else {
                    546:        size_t l;
                    547:        int st_ver = 0;
                    548:        char atoi_buffer[8];
                    549: 
                    550:        /* remove version at the end of the ST string */
                    551:        for (l = st_len; l > 0; l--) {
                    552:            if (st[l-1] == ':') {
                    553:                memset(atoi_buffer, 0, sizeof(atoi_buffer));
                    554:                memcpy(atoi_buffer, st + l, MIN((sizeof(atoi_buffer) - 1), st_len - l));
                    555:                st_ver = atoi(atoi_buffer);
                    556:                break;
                    557:            }
                    558:        }
                    559:        if (l == 0)
                    560:            l = st_len;
                    561:        /* answer for each matching service */
                    562:        /* From UPnP Device Architecture v1.1 :
                    563:         * 1.3.2 [...] Updated versions of device and service types
                    564:         * are REQUIRED to be full backward compatible with
                    565:         * previous versions. Devices MUST respond to M-SEARCH
                    566:         * requests for any supported version. For example, if a
                    567:         * device implements “urn:schemas-upnporg:service:xyz:2”,
                    568:         * it MUST respond to search requests for both that type
                    569:         * and “urn:schemas-upnp-org:service:xyz:1”. The response
                    570:         * MUST specify the same version as was contained in the
                    571:         * search request. [...] */
                    572:        for(serv = servicelisthead.lh_first;
                    573:            serv;
                    574:            serv = serv->entries.le_next) {
                    575:            if(0 == strncmp(serv->st, st, l)) {
                    576:                syslog(LOG_DEBUG, "Found matching service : %s %s (v %d)", serv->st, serv->location, st_ver);
                    577:                SendSSDPMSEARCHResponse(s, addr,
                    578:                                        st, st_len, serv->usn,
                    579:                                        serv->server, serv->location);
                    580:            }
                    581:        }
                    582:    }
                    583: }
                    584: 
                    585: /**
                    586:  * helper function.
                    587:  * reject any non ASCII or non printable character.
                    588:  */
                    589: static int
                    590: containsForbiddenChars(const unsigned char * p, int len)
                    591: {
                    592:    while(len > 0) {
                    593:        if(*p < ' ' || *p >= '\x7f')
                    594:            return 1;
                    595:        p++;
                    596:        len--;
                    597:    }
                    598:    return 0;
                    599: }
                    600: 
                    601: #define METHOD_MSEARCH 1
                    602: #define METHOD_NOTIFY 2
                    603: 
                    604: /* ParseSSDPPacket() :
                    605:  * parse a received SSDP Packet and call
                    606:  * updateDevice() or removeDevice() as needed
                    607:  * return value :
                    608:  *    -1 : a device was removed
                    609:  *     0 : no device removed nor added
                    610:  *     1 : a device was added.  */
                    611: static int
                    612: ParseSSDPPacket(int s, const char * p, ssize_t n,
                    613:                 const struct sockaddr * addr,
                    614:                 const char * searched_device)
                    615: {
                    616:    const char * linestart;
                    617:    const char * lineend;
                    618:    const char * nameend;
                    619:    const char * valuestart;
                    620:    struct header headers[3];
                    621:    int i, r = 0;
                    622:    int methodlen;
                    623:    int nts = -1;
                    624:    int method = -1;
                    625:    unsigned int lifetime = 180;    /* 3 minutes by default */
                    626:    const char * st = NULL;
                    627:    int st_len = 0;
                    628: 
                    629:    /* first check from what subnet is the sender */
                    630:    if(get_lan_for_peer(addr) == NULL) {
                    631:        char addr_str[64];
                    632:        sockaddr_to_string(addr, addr_str, sizeof(addr_str));
                    633:        syslog(LOG_WARNING, "peer %s is not from a LAN",
                    634:               addr_str);
                    635:        return 0;
                    636:    }
                    637: 
                    638:    /* do the parsing */
                    639:    memset(headers, 0, sizeof(headers));
                    640:    for(methodlen = 0;
                    641:        methodlen < n && (isalpha(p[methodlen]) || p[methodlen]=='-');
                    642:        methodlen++);
                    643:    if(methodlen==8 && 0==memcmp(p, "M-SEARCH", 8))
                    644:        method = METHOD_MSEARCH;
                    645:    else if(methodlen==6 && 0==memcmp(p, "NOTIFY", 6))
                    646:        method = METHOD_NOTIFY;
                    647:    else if(methodlen==4 && 0==memcmp(p, "HTTP", 4)) {
                    648:        /* answer to a M-SEARCH => process it as a NOTIFY
                    649:         * with NTS: ssdp:alive */
                    650:        method = METHOD_NOTIFY;
                    651:        nts = NTS_SSDP_ALIVE;
                    652:    }
                    653:    linestart = p;
                    654:    while(linestart < p + n - 2) {
                    655:        /* start parsing the line : detect line end */
                    656:        lineend = linestart;
                    657:        while(lineend < p + n && *lineend != '\n' && *lineend != '\r')
                    658:            lineend++;
                    659:        /*printf("line: '%.*s'\n", lineend - linestart, linestart);*/
                    660:        /* detect name end : ':' character */
                    661:        nameend = linestart;
                    662:        while(nameend < lineend && *nameend != ':')
                    663:            nameend++;
                    664:        /* detect value */
                    665:        if(nameend < lineend)
                    666:            valuestart = nameend + 1;
                    667:        else
                    668:            valuestart = nameend;
                    669:        /* trim spaces */
                    670:        while(valuestart < lineend && isspace(*valuestart))
                    671:            valuestart++;
                    672:        /* suppress leading " if needed */
                    673:        if(valuestart < lineend && *valuestart=='\"')
                    674:            valuestart++;
                    675:        if(nameend > linestart && valuestart < lineend) {
                    676:            int l = nameend - linestart;    /* header name length */
                    677:            int m = lineend - valuestart;   /* header value length */
                    678:            /* suppress tailing spaces */
                    679:            while(m>0 && isspace(valuestart[m-1]))
                    680:                m--;
                    681:            /* suppress tailing ' if needed */
                    682:            if(m>0 && valuestart[m-1] == '\"')
                    683:                m--;
                    684:            i = -1;
                    685:            /*printf("--%.*s: (%d)%.*s--\n", l, linestart,
                    686:                                       m, m, valuestart);*/
                    687:            if(l==2 && 0==strncasecmp(linestart, "nt", 2))
                    688:                i = HEADER_NT;
                    689:            else if(l==3 && 0==strncasecmp(linestart, "usn", 3))
                    690:                i = HEADER_USN;
                    691:            else if(l==3 && 0==strncasecmp(linestart, "nts", 3)) {
                    692:                if(m==10 && 0==strncasecmp(valuestart, "ssdp:alive", 10))
                    693:                    nts = NTS_SSDP_ALIVE;
                    694:                else if(m==11 && 0==strncasecmp(valuestart, "ssdp:byebye", 11))
                    695:                    nts = NTS_SSDP_BYEBYE;
                    696:                else if(m==11 && 0==strncasecmp(valuestart, "ssdp:update", 11))
                    697:                    nts = NTS_SSDP_UPDATE;
                    698:            }
                    699:            else if(l==8 && 0==strncasecmp(linestart, "location", 8))
                    700:                i = HEADER_LOCATION;
                    701:            else if(l==13 && 0==strncasecmp(linestart, "cache-control", 13)) {
                    702:                /* parse "name1=value1, name_alone, name2=value2" string */
                    703:                const char * name = valuestart; /* name */
                    704:                const char * val;               /* value */
                    705:                int rem = m;    /* remaining bytes to process */
                    706:                while(rem > 0) {
                    707:                    val = name;
                    708:                    while(val < name + rem && *val != '=' && *val != ',')
                    709:                        val++;
                    710:                    if(val >= name + rem)
                    711:                        break;
                    712:                    if(*val == '=') {
                    713:                        while(val < name + rem && (*val == '=' || isspace(*val)))
                    714:                            val++;
                    715:                        if(val >= name + rem)
                    716:                            break;
                    717:                        if(0==strncasecmp(name, "max-age", 7))
                    718:                            lifetime = (unsigned int)strtoul(val, 0, 0);
                    719:                        /* move to the next name=value pair */
                    720:                        while(rem > 0 && *name != ',') {
                    721:                            rem--;
                    722:                            name++;
                    723:                        }
                    724:                        /* skip spaces */
                    725:                        while(rem > 0 && (*name == ',' || isspace(*name))) {
                    726:                            rem--;
                    727:                            name++;
                    728:                        }
                    729:                    } else {
                    730:                        rem -= (val - name);
                    731:                        name = val;
                    732:                        while(rem > 0 && (*name == ',' || isspace(*name))) {
                    733:                            rem--;
                    734:                            name++;
                    735:                        }
                    736:                    }
                    737:                }
                    738:                /*syslog(LOG_DEBUG, "**%.*s**%u", m, valuestart, lifetime);*/
                    739:            } else if(l==2 && 0==strncasecmp(linestart, "st", 2)) {
                    740:                st = valuestart;
                    741:                st_len = m;
                    742:                if(method == METHOD_NOTIFY)
                    743:                    i = HEADER_NT;  /* it was a M-SEARCH response */
                    744:            }
                    745:            if(i>=0) {
                    746:                headers[i].p = valuestart;
                    747:                headers[i].l = m;
                    748:            }
                    749:        }
                    750:        linestart = lineend;
                    751:        while((linestart < p + n) && (*linestart == '\n' || *linestart == '\r'))
                    752:            linestart++;
                    753:    }
                    754: #if 0
                    755:    printf("NTS=%d\n", nts);
                    756:    for(i=0; i<3; i++) {
                    757:        if(headers[i].p)
                    758:            printf("%d-'%.*s'\n", i, headers[i].l, headers[i].p);
                    759:    }
                    760: #endif
                    761:    syslog(LOG_DEBUG,"SSDP request: '%.*s' (%d) %s %s=%.*s",
                    762:           methodlen, p, method, nts_to_str(nts),
                    763:           (method==METHOD_NOTIFY)?"nt":"st",
                    764:           (method==METHOD_NOTIFY)?headers[HEADER_NT].l:st_len,
                    765:           (method==METHOD_NOTIFY)?headers[HEADER_NT].p:st);
                    766:    switch(method) {
                    767:    case METHOD_NOTIFY:
                    768:        if(nts==NTS_SSDP_ALIVE || nts==NTS_SSDP_UPDATE) {
                    769:            if(headers[HEADER_NT].p && headers[HEADER_USN].p && headers[HEADER_LOCATION].p) {
                    770:                /* filter if needed */
                    771:                if(searched_device &&
                    772:                   0 != memcmp(headers[HEADER_NT].p, searched_device, headers[HEADER_NT].l))
                    773:                    break;
                    774:                r = updateDevice(headers, time(NULL) + lifetime);
                    775:            } else {
                    776:                syslog(LOG_WARNING, "missing header nt=%p usn=%p location=%p",
                    777:                       headers[HEADER_NT].p, headers[HEADER_USN].p,
                    778:                       headers[HEADER_LOCATION].p);
                    779:            }
                    780:        } else if(nts==NTS_SSDP_BYEBYE) {
                    781:            if(headers[HEADER_NT].p && headers[HEADER_USN].p) {
                    782:                r = removeDevice(headers);
                    783:            } else {
                    784:                syslog(LOG_WARNING, "missing header nt=%p usn=%p",
                    785:                       headers[HEADER_NT].p, headers[HEADER_USN].p);
                    786:            }
                    787:        }
                    788:        break;
                    789:    case METHOD_MSEARCH:
                    790:        processMSEARCH(s, st, st_len, addr);
                    791:        break;
                    792:    default:
                    793:        {
                    794:            char addr_str[64];
                    795:            sockaddr_to_string(addr, addr_str, sizeof(addr_str));
                    796:            syslog(LOG_WARNING, "method %.*s, don't know what to do (from %s)",
                    797:                   methodlen, p, addr_str);
                    798:        }
                    799:    }
                    800:    return r;
                    801: }
                    802: 
                    803: /* OpenUnixSocket()
                    804:  * open the unix socket and call bind() and listen()
                    805:  * return -1 in case of error */
                    806: static int
                    807: OpenUnixSocket(const char * path)
                    808: {
                    809:    struct sockaddr_un addr;
                    810:    int s;
                    811:    int rv;
                    812:    s = socket(AF_UNIX, SOCK_STREAM, 0);
                    813:    if(s < 0)
                    814:    {
                    815:        syslog(LOG_ERR, "socket(AF_UNIX): %m");
                    816:        return -1;
                    817:    }
                    818:    /* unlink the socket pseudo file before binding */
                    819:    rv = unlink(path);
                    820:    if(rv < 0 && errno != ENOENT)
                    821:    {
                    822:        syslog(LOG_ERR, "unlink(unixsocket, \"%s\"): %m", path);
                    823:        close(s);
                    824:        return -1;
                    825:    }
                    826:    addr.sun_family = AF_UNIX;
                    827:    strncpy(addr.sun_path, path, sizeof(addr.sun_path));
                    828:    if(bind(s, (struct sockaddr *)&addr,
                    829:               sizeof(struct sockaddr_un)) < 0)
                    830:    {
                    831:        syslog(LOG_ERR, "bind(unixsocket, \"%s\"): %m", path);
                    832:        close(s);
                    833:        return -1;
                    834:    }
                    835:    else if(listen(s, 5) < 0)
                    836:    {
                    837:        syslog(LOG_ERR, "listen(unixsocket): %m");
                    838:        close(s);
                    839:        return -1;
                    840:    }
                    841:    /* Change rights so everyone can communicate with us */
                    842:    if(chmod(path, 0666) < 0)
                    843:    {
                    844:        syslog(LOG_WARNING, "chmod(\"%s\"): %m", path);
                    845:    }
                    846:    return s;
                    847: }
                    848: 
                    849: static ssize_t processRequestSub(struct reqelem * req, const unsigned char * buf, ssize_t n);
                    850: 
                    851: /* processRequest() :
                    852:  * process the request coming from a unix socket */
                    853: void processRequest(struct reqelem * req)
                    854: {
                    855:    ssize_t n, r;
                    856:    unsigned char buf[2048];
                    857:    const unsigned char * p;
                    858: 
                    859:    n = read(req->socket, buf, sizeof(buf));
                    860:    if(n<0) {
                    861:        if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
                    862:            return; /* try again later */
                    863:        syslog(LOG_ERR, "(s=%d) processRequest(): read(): %m", req->socket);
                    864:        goto error;
                    865:    }
                    866:    if(n==0) {
                    867:        syslog(LOG_INFO, "(s=%d) request connection closed", req->socket);
                    868:        goto error;
                    869:    }
                    870:    p = buf;
                    871:    while (n > 0)
                    872:    {
                    873:        r = processRequestSub(req, p, n);
                    874:        if (r < 0)
                    875:            goto error;
                    876:        p += r;
                    877:        n -= r;
                    878:    }
                    879:    return;
                    880: error:
                    881:    close(req->socket);
                    882:    req->socket = -1;
                    883: }
                    884: 
                    885: static ssize_t processRequestSub(struct reqelem * req, const unsigned char * buf, ssize_t n)
                    886: {
                    887:    unsigned int l, m;
                    888:    unsigned int baselen;   /* without the version */
                    889:    const unsigned char * p;
                    890:    enum request_type type;
                    891:    struct device * d = devlist;
                    892:    unsigned char rbuf[RESPONSE_BUFFER_SIZE];
                    893:    unsigned char * rp;
                    894:    unsigned char nrep = 0;
                    895:    time_t t;
                    896:    struct service * newserv = NULL;
                    897:    struct service * serv;
                    898: 
                    899:    t = time(NULL);
                    900:    type = buf[0];
                    901:    p = buf + 1;
                    902:    DECODELENGTH_CHECKLIMIT(l, p, buf + n);
                    903:    if(l > (unsigned)(buf+n-p)) {
                    904:        syslog(LOG_WARNING, "bad request (length encoding l=%u n=%u)",
                    905:               l, (unsigned)n);
                    906:        goto error;
                    907:    }
                    908:    if(l == 0 && type != MINISSDPD_SEARCH_ALL
                    909:       && type != MINISSDPD_GET_VERSION && type != MINISSDPD_NOTIF) {
                    910:        syslog(LOG_WARNING, "bad request (length=0, type=%d)", type);
                    911:        goto error;
                    912:    }
                    913:    syslog(LOG_INFO, "(s=%d) request type=%d str='%.*s'",
                    914:           req->socket, type, l, p);
                    915:    switch(type) {
                    916:    case MINISSDPD_GET_VERSION:
                    917:        rp = rbuf;
                    918:        CODELENGTH((sizeof(MINISSDPD_VERSION) - 1), rp);
                    919:        memcpy(rp, MINISSDPD_VERSION, sizeof(MINISSDPD_VERSION) - 1);
                    920:        rp += (sizeof(MINISSDPD_VERSION) - 1);
                    921:        if(write_or_buffer(req, rbuf, rp - rbuf) < 0) {
                    922:            syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
                    923:            goto error;
                    924:        }
                    925:        p += l;
                    926:        break;
                    927:    case MINISSDPD_SEARCH_TYPE: /* request by type */
                    928:    case MINISSDPD_SEARCH_USN:  /* request by USN (unique id) */
                    929:    case MINISSDPD_SEARCH_ALL:  /* everything */
                    930:        rp = rbuf+1;
                    931:        /* From UPnP Device Architecture v1.1 :
                    932:         * 1.3.2 [...] Updated versions of device and service types
                    933:         * are REQUIRED to be full backward compatible with
                    934:         * previous versions. Devices MUST respond to M-SEARCH
                    935:         * requests for any supported version. For example, if a
                    936:         * device implements “urn:schemas-upnporg:service:xyz:2”,
                    937:         * it MUST respond to search requests for both that type
                    938:         * and “urn:schemas-upnp-org:service:xyz:1”. The response
                    939:         * MUST specify the same version as was contained in the
                    940:         * search request. [...] */
                    941:        baselen = l;    /* remove the version */
                    942:        while(baselen > 0) {
                    943:            if(p[baselen-1] == ':')
                    944:                break;
                    945:            if(!(p[baselen-1] >= '0' && p[baselen-1] <= '9'))
                    946:                break;
                    947:            baselen--;
                    948:        }
                    949:        while(d && (nrep < 255)) {
                    950:            if(d->t < t) {
                    951:                syslog(LOG_INFO, "outdated device");
                    952:            } else {
                    953:                /* test if we can put more responses in the buffer */
                    954:                if(d->headers[HEADER_LOCATION].l + d->headers[HEADER_NT].l
                    955:                  + d->headers[HEADER_USN].l + 6
                    956:                  + (rp - rbuf) >= (int)sizeof(rbuf))
                    957:                    break;
                    958:                if( (type==MINISSDPD_SEARCH_TYPE && 0==memcmp(d->headers[HEADER_NT].p, p, baselen))
                    959:                  ||(type==MINISSDPD_SEARCH_USN && 0==memcmp(d->headers[HEADER_USN].p, p, l))
                    960:                  ||(type==MINISSDPD_SEARCH_ALL) ) {
                    961:                    /* response :
                    962:                     * 1 - Location
                    963:                     * 2 - NT (device/service type)
                    964:                     * 3 - usn */
                    965:                    m = d->headers[HEADER_LOCATION].l;
                    966:                    CODELENGTH(m, rp);
                    967:                    memcpy(rp, d->headers[HEADER_LOCATION].p, d->headers[HEADER_LOCATION].l);
                    968:                    rp += d->headers[HEADER_LOCATION].l;
                    969:                    m = d->headers[HEADER_NT].l;
                    970:                    CODELENGTH(m, rp);
                    971:                    memcpy(rp, d->headers[HEADER_NT].p, d->headers[HEADER_NT].l);
                    972:                    rp += d->headers[HEADER_NT].l;
                    973:                    m = d->headers[HEADER_USN].l;
                    974:                    CODELENGTH(m, rp);
                    975:                    memcpy(rp, d->headers[HEADER_USN].p, d->headers[HEADER_USN].l);
                    976:                    rp += d->headers[HEADER_USN].l;
                    977:                    nrep++;
                    978:                }
                    979:            }
                    980:            d = d->next;
                    981:        }
                    982:        /* Also look in service list */
                    983:        for(serv = servicelisthead.lh_first;
                    984:            serv && (nrep < 255);
                    985:            serv = serv->entries.le_next) {
                    986:            /* test if we can put more responses in the buffer */
                    987:            if(strlen(serv->location) + strlen(serv->st)
                    988:              + strlen(serv->usn) + 6 + (rp - rbuf) >= sizeof(rbuf))
                    989:                break;
                    990:            if( (type==MINISSDPD_SEARCH_TYPE && 0==strncmp(serv->st, (const char *)p, l))
                    991:              ||(type==MINISSDPD_SEARCH_USN && 0==strncmp(serv->usn, (const char *)p, l))
                    992:              ||(type==MINISSDPD_SEARCH_ALL) ) {
                    993:                /* response :
                    994:                 * 1 - Location
                    995:                 * 2 - NT (device/service type)
                    996:                 * 3 - usn */
                    997:                m = strlen(serv->location);
                    998:                CODELENGTH(m, rp);
                    999:                memcpy(rp, serv->location, m);
                   1000:                rp += m;
                   1001:                m = strlen(serv->st);
                   1002:                CODELENGTH(m, rp);
                   1003:                memcpy(rp, serv->st, m);
                   1004:                rp += m;
                   1005:                m = strlen(serv->usn);
                   1006:                CODELENGTH(m, rp);
                   1007:                memcpy(rp, serv->usn, m);
                   1008:                rp += m;
                   1009:                nrep++;
                   1010:            }
                   1011:        }
                   1012:        rbuf[0] = nrep;
                   1013:        syslog(LOG_DEBUG, "(s=%d) response : %d device%s",
                   1014:               req->socket, nrep, (nrep > 1) ? "s" : "");
                   1015:        if(write_or_buffer(req, rbuf, rp - rbuf) < 0) {
                   1016:            syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
                   1017:            goto error;
                   1018:        }
                   1019:        p += l;
                   1020:        break;
                   1021:    case MINISSDPD_SUBMIT:  /* submit service */
                   1022:        newserv = malloc(sizeof(struct service));
                   1023:        if(!newserv) {
                   1024:            syslog(LOG_ERR, "cannot allocate memory");
                   1025:            goto error;
                   1026:        }
                   1027:        memset(newserv, 0, sizeof(struct service)); /* set pointers to NULL */
                   1028:        if(containsForbiddenChars(p, l)) {
                   1029:            syslog(LOG_ERR, "bad request (st contains forbidden chars)");
                   1030:            goto error;
                   1031:        }
                   1032:        newserv->st = malloc(l + 1);
                   1033:        if(!newserv->st) {
                   1034:            syslog(LOG_ERR, "cannot allocate memory");
                   1035:            goto error;
                   1036:        }
                   1037:        memcpy(newserv->st, p, l);
                   1038:        newserv->st[l] = '\0';
                   1039:        p += l;
                   1040:        if(p >= buf + n) {
                   1041:            syslog(LOG_WARNING, "bad request (missing usn)");
                   1042:            goto error;
                   1043:        }
                   1044:        DECODELENGTH_CHECKLIMIT(l, p, buf + n);
                   1045:        if(l > (unsigned)(buf+n-p)) {
                   1046:            syslog(LOG_WARNING, "bad request (length encoding)");
                   1047:            goto error;
                   1048:        }
                   1049:        if(containsForbiddenChars(p, l)) {
                   1050:            syslog(LOG_ERR, "bad request (usn contains forbidden chars)");
                   1051:            goto error;
                   1052:        }
                   1053:        syslog(LOG_INFO, "usn='%.*s'", l, p);
                   1054:        newserv->usn = malloc(l + 1);
                   1055:        if(!newserv->usn) {
                   1056:            syslog(LOG_ERR, "cannot allocate memory");
                   1057:            goto error;
                   1058:        }
                   1059:        memcpy(newserv->usn, p, l);
                   1060:        newserv->usn[l] = '\0';
                   1061:        p += l;
                   1062:        DECODELENGTH_CHECKLIMIT(l, p, buf + n);
                   1063:        if(l > (unsigned)(buf+n-p)) {
                   1064:            syslog(LOG_WARNING, "bad request (length encoding)");
                   1065:            goto error;
                   1066:        }
                   1067:        if(containsForbiddenChars(p, l)) {
                   1068:            syslog(LOG_ERR, "bad request (server contains forbidden chars)");
                   1069:            goto error;
                   1070:        }
                   1071:        syslog(LOG_INFO, "server='%.*s'", l, p);
                   1072:        newserv->server = malloc(l + 1);
                   1073:        if(!newserv->server) {
                   1074:            syslog(LOG_ERR, "cannot allocate memory");
                   1075:            goto error;
                   1076:        }
                   1077:        memcpy(newserv->server, p, l);
                   1078:        newserv->server[l] = '\0';
                   1079:        p += l;
                   1080:        DECODELENGTH_CHECKLIMIT(l, p, buf + n);
                   1081:        if(l > (unsigned)(buf+n-p)) {
                   1082:            syslog(LOG_WARNING, "bad request (length encoding)");
                   1083:            goto error;
                   1084:        }
                   1085:        if(containsForbiddenChars(p, l)) {
                   1086:            syslog(LOG_ERR, "bad request (location contains forbidden chars)");
                   1087:            goto error;
                   1088:        }
                   1089:        syslog(LOG_INFO, "location='%.*s'", l, p);
                   1090:        newserv->location = malloc(l + 1);
                   1091:        if(!newserv->location) {
                   1092:            syslog(LOG_ERR, "cannot allocate memory");
                   1093:            goto error;
                   1094:        }
                   1095:        memcpy(newserv->location, p, l);
                   1096:        newserv->location[l] = '\0';
                   1097:        p += l;
                   1098:        /* look in service list for duplicate */
                   1099:        for(serv = servicelisthead.lh_first;
                   1100:            serv;
                   1101:            serv = serv->entries.le_next) {
                   1102:            if(0 == strcmp(newserv->usn, serv->usn)
                   1103:              && 0 == strcmp(newserv->st, serv->st)) {
                   1104:                syslog(LOG_INFO, "Service already in the list. Updating...");
                   1105:                free(newserv->st);
                   1106:                free(newserv->usn);
                   1107:                free(serv->server);
                   1108:                serv->server = newserv->server;
                   1109:                free(serv->location);
                   1110:                serv->location = newserv->location;
                   1111:                free(newserv);
                   1112:                newserv = NULL;
                   1113:                return (p - buf);
                   1114:            }
                   1115:        }
                   1116:        /* Inserting new service */
                   1117:        LIST_INSERT_HEAD(&servicelisthead, newserv, entries);
                   1118:        sendNotifications(NOTIF_NEW, NULL, newserv);
                   1119:        newserv = NULL;
                   1120:        break;
                   1121:    case MINISSDPD_NOTIF:   /* switch socket to notify */
                   1122:        rbuf[0] = '\0';
                   1123:        if(write_or_buffer(req, rbuf, 1) < 0) {
                   1124:            syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
                   1125:            goto error;
                   1126:        }
                   1127:        req->is_notify = 1;
                   1128:        p += l;
                   1129:        break;
                   1130:    default:
                   1131:        syslog(LOG_WARNING, "Unknown request type %d", type);
                   1132:        rbuf[0] = '\0';
                   1133:        if(write_or_buffer(req, rbuf, 1) < 0) {
                   1134:            syslog(LOG_ERR, "(s=%d) write: %m", req->socket);
                   1135:            goto error;
                   1136:        }
                   1137:    }
                   1138:    return (p - buf);
                   1139: error:
                   1140:    if(newserv) {
                   1141:        free(newserv->st);
                   1142:        free(newserv->usn);
                   1143:        free(newserv->server);
                   1144:        free(newserv->location);
                   1145:        free(newserv);
                   1146:        newserv = NULL;
                   1147:    }
                   1148:    return -1;
                   1149: }
                   1150: 
                   1151: static volatile sig_atomic_t quitting = 0;
                   1152: /* SIGTERM signal handler */
                   1153: static void
                   1154: sigterm(int sig)
                   1155: {
                   1156:    (void)sig;
                   1157:    /*int save_errno = errno;*/
                   1158:    /*signal(sig, SIG_IGN);*/
                   1159: #if 0
                   1160:    /* calling syslog() is forbidden in a signal handler according to
                   1161:     * signal(3) */
                   1162:    syslog(LOG_NOTICE, "received signal %d, good-bye", sig);
                   1163: #endif
                   1164:    quitting = 1;
                   1165:    /*errno = save_errno;*/
                   1166: }
                   1167: 
                   1168: #define PORT 1900
                   1169: #define XSTR(s) STR(s)
                   1170: #define STR(s) #s
                   1171: #define UPNP_MCAST_ADDR "239.255.255.250"
                   1172: /* for IPv6 */
                   1173: #define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
                   1174: #define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
                   1175: 
                   1176: /* send the M-SEARCH request for devices
                   1177:  * either all devices (third argument is NULL or "*") or a specific one */
                   1178: static void ssdpDiscover(int s, int ipv6, const char * search)
                   1179: {
                   1180:    static const char MSearchMsgFmt[] =
                   1181:    "M-SEARCH * HTTP/1.1\r\n"
                   1182:    "HOST: %s:" XSTR(PORT) "\r\n"
                   1183:    "ST: %s\r\n"
                   1184:    "MAN: \"ssdp:discover\"\r\n"
                   1185:    "MX: %u\r\n"
                   1186:    "\r\n";
                   1187:    char bufr[512];
                   1188:    int n;
                   1189:    int mx = 3;
                   1190:    int linklocal = 1;
                   1191:    struct sockaddr_storage sockudp_w;
                   1192: 
                   1193:    {
                   1194:        n = snprintf(bufr, sizeof(bufr),
                   1195:                     MSearchMsgFmt,
                   1196:                     ipv6 ?
                   1197:                     (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" :  "[" UPNP_MCAST_SL_ADDR "]")
                   1198:                     : UPNP_MCAST_ADDR,
                   1199:                     (search ? search : "ssdp:all"), mx);
                   1200:        memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
                   1201:        if(ipv6) {
                   1202:            struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
                   1203:            p->sin6_family = AF_INET6;
                   1204:            p->sin6_port = htons(PORT);
                   1205:            inet_pton(AF_INET6,
                   1206:                      linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
                   1207:                      &(p->sin6_addr));
                   1208:        } else {
                   1209:            struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
                   1210:            p->sin_family = AF_INET;
                   1211:            p->sin_port = htons(PORT);
                   1212:            p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
                   1213:        }
                   1214: 
                   1215:        n = sendto_or_schedule(s, bufr, n, 0, (const struct sockaddr *)&sockudp_w,
                   1216:                               ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
                   1217:        if (n < 0) {
                   1218:            syslog(LOG_ERR, "%s: sendto(s=%d, ipv6=%d): %m", __func__, s, ipv6);
                   1219:        }
                   1220:    }
                   1221: }
                   1222: 
                   1223: /* main(): program entry point */
                   1224: int main(int argc, char * * argv)
                   1225: {
                   1226:    int ret = 0;
                   1227: #ifndef NO_BACKGROUND_NO_PIDFILE
                   1228:    int pid;
                   1229: #endif
                   1230:    struct sigaction sa;
                   1231:    char buf[1500];
                   1232:    ssize_t n;
                   1233:    int s_ssdp = -1;    /* udp socket receiving ssdp packets */
                   1234: #ifdef ENABLE_IPV6
                   1235:    int s_ssdp6 = -1;   /* udp socket receiving ssdp packets IPv6*/
                   1236: #else  /* ENABLE_IPV6 */
                   1237: #define s_ssdp6 (-1)
                   1238: #endif /* ENABLE_IPV6 */
                   1239:    int s_unix = -1;    /* unix socket communicating with clients */
                   1240:    int s_ifacewatch = -1;  /* socket to receive Route / network interface config changes */
                   1241:    struct reqelem * req;
                   1242:    struct reqelem * reqnext;
                   1243:    fd_set readfds;
                   1244:    fd_set writefds;
                   1245:    struct timeval now;
                   1246:    int max_fd;
                   1247:    struct lan_addr_s * lan_addr;
                   1248:    int i;
                   1249:    const char * sockpath = "/var/run/minissdpd.sock";
                   1250: #ifndef NO_BACKGROUND_NO_PIDFILE
                   1251:    const char * pidfilename = "/var/run/minissdpd.pid";
                   1252: #endif
                   1253:    int debug_flag = 0;
                   1254: #ifdef ENABLE_IPV6
                   1255:    int ipv6 = 0;
                   1256: #endif /* ENABLE_IPV6 */
                   1257:    int deltadev = 0;
                   1258:    struct sockaddr_in sendername;
                   1259:    socklen_t sendername_len;
                   1260: #ifdef ENABLE_IPV6
                   1261:    struct sockaddr_in6 sendername6;
                   1262:    socklen_t sendername6_len;
                   1263: #endif /* ENABLE_IPV6 */
                   1264:    unsigned char ttl = 2;  /* UDA says it should default to 2 */
                   1265:    const char * searched_device = NULL;    /* if not NULL, search/filter a specific device type */
                   1266:    int opt;
                   1267: 
                   1268:    LIST_INIT(&reqlisthead);
                   1269:    LIST_INIT(&servicelisthead);
                   1270:    LIST_INIT(&lan_addrs);
                   1271:    /* process command line */
                   1272: #define OPTSTRING "d6i:s:p:t:f:"
                   1273:    while ((opt = getopt(argc, argv, "di:s:t:f:"
                   1274: #ifdef ENABLE_IPV6
                   1275:                                     "6"
                   1276: #endif
                   1277: #ifndef NO_BACKGROUND_NO_PIDFILE
                   1278:                                     "p:"
                   1279: #endif
                   1280: 
                   1281:                         )) != -1)
                   1282:    {
                   1283:        switch(opt)
                   1284:        {
                   1285:        case 'd':
                   1286:            debug_flag = 1;
                   1287:            break;
                   1288: #ifdef ENABLE_IPV6
                   1289:        case '6':
                   1290:            ipv6 = 1;
                   1291:            break;
                   1292: #endif /* ENABLE_IPV6 */
                   1293:        case 'i':
                   1294:            lan_addr = malloc(sizeof(struct lan_addr_s));
                   1295:            if(lan_addr == NULL) {
                   1296:                fprintf(stderr, "malloc(%d) FAILED\n", (int)sizeof(struct lan_addr_s));
                   1297:                break;
                   1298:            }
                   1299:            if(parselanaddr(lan_addr, optarg) != 0) {
                   1300:                fprintf(stderr, "can't parse \"%s\" as a valid "
                   1301: #ifndef ENABLE_IPV6
                   1302:                        "address or "
                   1303: #endif
                   1304:                        "interface name\n", optarg);
                   1305:                free(lan_addr);
                   1306:            } else {
                   1307:                LIST_INSERT_HEAD(&lan_addrs, lan_addr, list);
                   1308:            }
                   1309:            break;
                   1310:        case 's':
                   1311:            sockpath = optarg;
                   1312:            break;
                   1313: #ifndef NO_BACKGROUND_NO_PIDFILE
                   1314:        case 'p':
                   1315:            pidfilename = optarg;
                   1316:            break;
                   1317: #endif
                   1318:        case 't':
                   1319:            ttl = (unsigned char)atoi(optarg);
                   1320:            break;
                   1321:        case 'f':
                   1322:            searched_device = optarg;
                   1323:            break;
                   1324:        }
                   1325:    }
                   1326:    if(lan_addrs.lh_first == NULL)
                   1327:    {
                   1328:        fprintf(stderr,
                   1329:                "Usage: %s [-d] "
                   1330: #ifdef ENABLE_IPV6
                   1331:                "[-6] "
                   1332: #endif /* ENABLE_IPV6 */
                   1333:                "[-s socket] "
                   1334: #ifndef NO_BACKGROUND_NO_PIDFILE
                   1335:                "[-p pidfile] "
                   1336: #endif
                   1337:                "[-t TTL] "
                   1338:                "[-f device] "
                   1339:                "-i <interface> [-i <interface2>] ...\n",
                   1340:                argv[0]);
                   1341:        fprintf(stderr,
                   1342:                "\n  <interface> is "
                   1343: #ifndef ENABLE_IPV6
                   1344:                "either an IPv4 address with mask such as\n"
                   1345:                "  192.168.1.42/255.255.255.0, or "
                   1346: #endif
                   1347:                "an interface name such as eth0.\n");
                   1348:        fprintf(stderr,
                   1349:                "\n  By default, socket will be open as %s\n"
                   1350: #ifndef NO_BACKGROUND_NO_PIDFILE
                   1351:                "  and pid written to file %s\n",
                   1352:                sockpath, pidfilename
                   1353: #else
                   1354:                ,sockpath
                   1355: #endif
                   1356:                );
                   1357:        return 1;
                   1358:    }
                   1359: 
                   1360:    /* open log */
                   1361:    openlog("minissdpd",
                   1362:            LOG_CONS|LOG_PID|(debug_flag?LOG_PERROR:0),
                   1363:            LOG_MINISSDPD);
                   1364:    if(!debug_flag) /* speed things up and ignore LOG_INFO and LOG_DEBUG */
                   1365:        setlogmask(LOG_UPTO(LOG_NOTICE));
                   1366: 
                   1367: #ifndef NO_BACKGROUND_NO_PIDFILE
                   1368:    if(checkforrunning(pidfilename) < 0)
                   1369:    {
                   1370:        syslog(LOG_ERR, "MiniSSDPd is already running. EXITING");
                   1371:        return 1;
                   1372:    }
                   1373: #endif
                   1374: 
                   1375:    upnp_bootid = (unsigned int)time(NULL);
                   1376: 
                   1377:    /* set signal handlers */
                   1378:    memset(&sa, 0, sizeof(struct sigaction));
                   1379:    sa.sa_handler = sigterm;
                   1380:    if(sigaction(SIGTERM, &sa, NULL))
                   1381:    {
                   1382:        syslog(LOG_ERR, "Failed to set SIGTERM handler. EXITING");
                   1383:        ret = 1;
                   1384:        goto quit;
                   1385:    }
                   1386:    if(sigaction(SIGINT, &sa, NULL))
                   1387:    {
                   1388:        syslog(LOG_ERR, "Failed to set SIGINT handler. EXITING");
                   1389:        ret = 1;
                   1390:        goto quit;
                   1391:    }
                   1392:    /* open route/interface config changes socket */
                   1393:    s_ifacewatch = OpenAndConfInterfaceWatchSocket();
                   1394:    /* open UDP socket(s) for receiving SSDP packets */
                   1395:    s_ssdp = OpenAndConfSSDPReceiveSocket(0, ttl);
                   1396:    if(s_ssdp < 0)
                   1397:    {
                   1398:        syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages, exiting");
                   1399:        ret = 1;
                   1400:        goto quit;
                   1401:    }
                   1402: #ifdef ENABLE_IPV6
                   1403:    if(ipv6) {
                   1404:        s_ssdp6 = OpenAndConfSSDPReceiveSocket(1, ttl);
                   1405:        if(s_ssdp6 < 0)
                   1406:        {
                   1407:            syslog(LOG_ERR, "Cannot open socket for receiving SSDP messages (IPv6), exiting");
                   1408:            ret = 1;
                   1409:            goto quit;
                   1410:        }
                   1411:    }
                   1412: #endif /* ENABLE_IPV6 */
                   1413:    /* Open Unix socket to communicate with other programs on
                   1414:     * the same machine */
                   1415:    s_unix = OpenUnixSocket(sockpath);
                   1416:    if(s_unix < 0)
                   1417:    {
                   1418:        syslog(LOG_ERR, "Cannot open unix socket for communicating with clients. Exiting");
                   1419:        ret = 1;
                   1420:        goto quit;
                   1421:    }
                   1422: 
                   1423:    /* drop privileges */
                   1424: #if 0
                   1425:    /* if we drop privileges, how to unlink(/var/run/minissdpd.sock) ? */
                   1426:    if(getuid() == 0) {
                   1427:        struct passwd * user;
                   1428:        struct group * group;
                   1429:        user = getpwnam("nobody");
                   1430:        if(!user) {
                   1431:            syslog(LOG_ERR, "getpwnam(\"%s\") : %m", "nobody");
                   1432:            ret = 1;
                   1433:            goto quit;
                   1434:        }
                   1435:        group = getgrnam("nogroup");
                   1436:        if(!group) {
                   1437:            syslog(LOG_ERR, "getgrnam(\"%s\") : %m", "nogroup");
                   1438:            ret = 1;
                   1439:            goto quit;
                   1440:        }
                   1441:        if(setgid(group->gr_gid) < 0) {
                   1442:            syslog(LOG_ERR, "setgit(%d) : %m", group->gr_gid);
                   1443:            ret = 1;
                   1444:            goto quit;
                   1445:        }
                   1446:        if(setuid(user->pw_uid) < 0) {
                   1447:            syslog(LOG_ERR, "setuid(%d) : %m", user->pw_uid);
                   1448:            ret = 1;
                   1449:            goto quit;
                   1450:        }
                   1451:    }
                   1452: #endif
                   1453: 
                   1454: #ifndef NO_BACKGROUND_NO_PIDFILE
                   1455:    /* daemonize or in any case get pid ! */
                   1456:    if(debug_flag)
                   1457:        pid = getpid();
                   1458:    else {
                   1459: #ifdef USE_DAEMON
                   1460:        if(daemon(0, 0) < 0)
                   1461:            perror("daemon()");
                   1462:        pid = getpid();
                   1463: #else  /* USE_DAEMON */
                   1464:        pid = daemonize();
                   1465: #endif /* USE_DAEMON */
                   1466:    }
                   1467: 
                   1468:    writepidfile(pidfilename, pid);
                   1469: #endif
                   1470: 
                   1471:    /* send M-SEARCH ssdp:all Requests */
                   1472:    if(s_ssdp >= 0) {
                   1473:        for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) {
                   1474: #ifndef HAVE_IP_MREQN
                   1475:            struct in_addr mc_if;
                   1476: 
                   1477:            mc_if.s_addr = lan_addr->addr.s_addr;   /*inet_addr(addr);*/
                   1478: #else
                   1479:            struct ip_mreqn mc_if;
                   1480: 
                   1481:            mc_if.imr_address.s_addr = lan_addr->addr.s_addr;   /*inet_addr(addr);*/
                   1482: #ifdef ENABLE_IPV6
                   1483:            mc_if.imr_ifindex = lan_addr->index;
                   1484: #else  /* ENABLE_IPV6 */
                   1485:            mc_if.imr_ifindex = if_nametoindex(lan_addr->ifname);
                   1486: #endif /* ENABLE_IPV6 */
                   1487: #endif /* HAVE_IP_MREQN */
                   1488:            if(setsockopt(s_ssdp, IPPROTO_IP, IP_MULTICAST_IF, &mc_if, sizeof(mc_if)) < 0) {
                   1489:                syslog(LOG_WARNING, "setsockopt(IP_MULTICAST_IF): %m");
                   1490:            }
                   1491:            ssdpDiscover(s_ssdp, 0, searched_device);
                   1492:            /* XXX if ssdpDiscover() doesn't send the SSDP packet at once,
                   1493:             * we should wait here */
                   1494:        }
                   1495:    }
                   1496:    if(s_ssdp6 >= 0)
                   1497:        ssdpDiscover(s_ssdp6, 1, searched_device);
                   1498: 
                   1499:    /* Main loop */
                   1500:    while(!quitting) {
                   1501:        /* fill readfds fd_set */
                   1502:        FD_ZERO(&readfds);
                   1503:        FD_ZERO(&writefds);
                   1504: 
                   1505:        FD_SET(s_unix, &readfds);
                   1506:        max_fd = s_unix;
                   1507:        if(s_ssdp >= 0) {
                   1508:            FD_SET(s_ssdp, &readfds);
                   1509:            SET_MAX(max_fd, s_ssdp);
                   1510:        }
                   1511: #ifdef ENABLE_IPV6
                   1512:        if(s_ssdp6 >= 0) {
                   1513:            FD_SET(s_ssdp6, &readfds);
                   1514:            SET_MAX(max_fd, s_ssdp6);
                   1515:        }
                   1516: #endif /* ENABLE_IPV6 */
                   1517:        if(s_ifacewatch >= 0) {
                   1518:            FD_SET(s_ifacewatch, &readfds);
                   1519:            SET_MAX(max_fd, s_ifacewatch);
                   1520:        }
                   1521:        for(req = reqlisthead.lh_first; req; req = req->entries.le_next) {
                   1522:            if(req->socket >= 0) {
                   1523:                FD_SET(req->socket, &readfds);
                   1524:                SET_MAX(max_fd, req->socket);
                   1525:            }
                   1526:            if(req->output_buffer_len > 0) {
                   1527:                FD_SET(req->socket, &writefds);
                   1528:                SET_MAX(max_fd, req->socket);
                   1529:            }
                   1530:        }
                   1531:        gettimeofday(&now, NULL);
                   1532:        i = get_sendto_fds(&writefds, &max_fd, &now);
                   1533:        /* select call */
                   1534:        if(select(max_fd + 1, &readfds, &writefds, 0, 0) < 0) {
                   1535:            if(errno != EINTR) {
                   1536:                syslog(LOG_ERR, "select: %m");
                   1537:                break;  /* quit */
                   1538:            }
                   1539:            continue;   /* try again */
                   1540:        }
                   1541:        if(try_sendto(&writefds) < 0) {
                   1542:            syslog(LOG_ERR, "try_sendto: %m");
                   1543:            break;
                   1544:        }
                   1545: #ifdef ENABLE_IPV6
                   1546:        if((s_ssdp6 >= 0) && FD_ISSET(s_ssdp6, &readfds))
                   1547:        {
                   1548:            sendername6_len = sizeof(struct sockaddr_in6);
                   1549:            n = recvfrom(s_ssdp6, buf, sizeof(buf), 0,
                   1550:                         (struct sockaddr *)&sendername6, &sendername6_len);
                   1551:            if(n<0)
                   1552:            {
                   1553:                 /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
                   1554:                  * other errors : log to LOG_ERR */
                   1555:                if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
                   1556:                    syslog(LOG_ERR, "recvfrom: %m");
                   1557:            }
                   1558:            else
                   1559:            {
                   1560:                /* Parse and process the packet received */
                   1561:                /*printf("%.*s", n, buf);*/
                   1562:                i = ParseSSDPPacket(s_ssdp6, buf, n,
                   1563:                                    (struct sockaddr *)&sendername6, searched_device);
                   1564:                syslog(LOG_DEBUG, "** i=%d deltadev=%d **", i, deltadev);
                   1565:                if(i==0 || (i*deltadev < 0))
                   1566:                {
                   1567:                    if(deltadev > 0)
                   1568:                        syslog(LOG_NOTICE, "%d new devices added", deltadev);
                   1569:                    else if(deltadev < 0)
                   1570:                        syslog(LOG_NOTICE, "%d devices removed (good-bye!)", -deltadev);
                   1571:                    deltadev = i;
                   1572:                }
                   1573:                else if((i*deltadev) >= 0)
                   1574:                {
                   1575:                    deltadev += i;
                   1576:                }
                   1577:            }
                   1578:        }
                   1579: #endif /* ENABLE_IPV6 */
                   1580:        if((s_ssdp >= 0) && FD_ISSET(s_ssdp, &readfds))
                   1581:        {
                   1582:            sendername_len = sizeof(struct sockaddr_in);
                   1583:            n = recvfrom(s_ssdp, buf, sizeof(buf), 0,
                   1584:                         (struct sockaddr *)&sendername, &sendername_len);
                   1585:            if(n<0)
                   1586:            {
                   1587:                 /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
                   1588:                  * other errors : log to LOG_ERR */
                   1589:                if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
                   1590:                    syslog(LOG_ERR, "recvfrom: %m");
                   1591:            }
                   1592:            else
                   1593:            {
                   1594:                /* Parse and process the packet received */
                   1595:                /*printf("%.*s", n, buf);*/
                   1596:                i = ParseSSDPPacket(s_ssdp, buf, n,
                   1597:                                    (struct sockaddr *)&sendername, searched_device);
                   1598:                syslog(LOG_DEBUG, "** i=%d deltadev=%d **", i, deltadev);
                   1599:                if(i==0 || (i*deltadev < 0))
                   1600:                {
                   1601:                    if(deltadev > 0)
                   1602:                        syslog(LOG_NOTICE, "%d new devices added", deltadev);
                   1603:                    else if(deltadev < 0)
                   1604:                        syslog(LOG_NOTICE, "%d devices removed (good-bye!)", -deltadev);
                   1605:                    deltadev = i;
                   1606:                }
                   1607:                else if((i*deltadev) >= 0)
                   1608:                {
                   1609:                    deltadev += i;
                   1610:                }
                   1611:            }
                   1612:        }
                   1613:        /* processing unix socket requests */
                   1614:        for(req = reqlisthead.lh_first; req;) {
                   1615:            reqnext = req->entries.le_next;
                   1616:            if((req->socket >= 0) && FD_ISSET(req->socket, &readfds)) {
                   1617:                processRequest(req);
                   1618:            }
                   1619:            if((req->socket >= 0) && FD_ISSET(req->socket, &writefds)) {
                   1620:                write_buffer(req);
                   1621:            }
                   1622:            if(req->socket < 0) {
                   1623:                LIST_REMOVE(req, entries);
                   1624:                free(req->output_buffer);
                   1625:                free(req);
                   1626:            }
                   1627:            req = reqnext;
                   1628:        }
                   1629:        /* processing new requests */
                   1630:        if(FD_ISSET(s_unix, &readfds))
                   1631:        {
                   1632:            struct reqelem * tmp;
                   1633:            int s = accept(s_unix, NULL, NULL);
                   1634:            if(s < 0) {
                   1635:                syslog(LOG_ERR, "accept(s_unix): %m");
                   1636:            } else {
                   1637:                syslog(LOG_INFO, "(s=%d) new request connection", s);
                   1638:                if(!set_non_blocking(s))
                   1639:                    syslog(LOG_WARNING, "Failed to set new socket non blocking : %m");
                   1640:                tmp = malloc(sizeof(struct reqelem));
                   1641:                if(!tmp) {
                   1642:                    syslog(LOG_ERR, "cannot allocate memory for request");
                   1643:                    close(s);
                   1644:                } else {
                   1645:                    memset(tmp, 0, sizeof(struct reqelem));
                   1646:                    tmp->socket = s;
                   1647:                    LIST_INSERT_HEAD(&reqlisthead, tmp, entries);
                   1648:                }
                   1649:            }
                   1650:        }
                   1651:        /* processing route/network interface config changes */
                   1652:        if((s_ifacewatch >= 0) && FD_ISSET(s_ifacewatch, &readfds)) {
                   1653:            ProcessInterfaceWatch(s_ifacewatch, s_ssdp, s_ssdp6);
                   1654:        }
                   1655:    }
                   1656:    syslog(LOG_DEBUG, "quitting...");
                   1657:    finalize_sendto();
                   1658: 
                   1659:    /* closing and cleaning everything */
                   1660: quit:
                   1661:    if(s_ssdp >= 0) {
                   1662:        close(s_ssdp);
                   1663:        s_ssdp = -1;
                   1664:    }
                   1665: #ifdef ENABLE_IPV6
                   1666:    if(s_ssdp6 >= 0) {
                   1667:        close(s_ssdp6);
                   1668:        s_ssdp6 = -1;
                   1669:    }
                   1670: #endif /* ENABLE_IPV6 */
                   1671:    if(s_unix >= 0) {
                   1672:        close(s_unix);
                   1673:        s_unix = -1;
                   1674:        if(unlink(sockpath) < 0)
                   1675:            syslog(LOG_ERR, "unlink(%s): %m", sockpath);
                   1676:    }
                   1677:    if(s_ifacewatch >= 0) {
                   1678:        close(s_ifacewatch);
                   1679:        s_ifacewatch = -1;
                   1680:    }
                   1681:    /* empty LAN interface/address list */
                   1682:    while(lan_addrs.lh_first != NULL) {
                   1683:        lan_addr = lan_addrs.lh_first;
                   1684:        LIST_REMOVE(lan_addrs.lh_first, list);
                   1685:        free(lan_addr);
                   1686:    }
                   1687:    /* empty device list */
                   1688:    while(devlist != NULL) {
                   1689:        struct device * next = devlist->next;
                   1690:        free(devlist);
                   1691:        devlist = next;
                   1692:    }
                   1693:    /* empty service list */
                   1694:    while(servicelisthead.lh_first != NULL) {
                   1695:        struct service * serv = servicelisthead.lh_first;
                   1696:        LIST_REMOVE(servicelisthead.lh_first, entries);
                   1697:        free(serv->st);
                   1698:        free(serv->usn);
                   1699:        free(serv->server);
                   1700:        free(serv->location);
                   1701:        free(serv);
                   1702:    }
                   1703: #ifndef NO_BACKGROUND_NO_PIDFILE
                   1704:    if(unlink(pidfilename) < 0)
                   1705:        syslog(LOG_ERR, "unlink(%s): %m", pidfilename);
                   1706: #endif
                   1707:    closelog();
                   1708:    return ret;
                   1709: }
                   1710: 

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