Annotation of embedaddon/miniupnpd/miniupnpc/src/minissdpc.c, revision 1.1.1.1

1.1       misho       1: /* $Id: minissdpc.c,v 1.49 2021/05/13 11:00:36 nanard Exp $ */
                      2: /* vim: tabstop=4 shiftwidth=4 noexpandtab
                      3:  * Project : miniupnp
                      4:  * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
                      5:  * Author : Thomas BERNARD
                      6:  * copyright (c) 2005-2021 Thomas Bernard
                      7:  * This software is subjet to the conditions detailed in the
                      8:  * provided LICENCE file. */
                      9: #include <stdio.h>
                     10: #include <string.h>
                     11: #include <stdlib.h>
                     12: #include <time.h>
                     13: #include <sys/types.h>
                     14: #if defined (__NetBSD__)
                     15: #include <net/if.h>
                     16: #endif
                     17: #if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
                     18: #ifdef _WIN32
                     19: #include <winsock2.h>
                     20: #include <ws2tcpip.h>
                     21: #include <io.h>
                     22: #include <iphlpapi.h>
                     23: #include "win32_snprintf.h"
                     24: #if !defined(_MSC_VER)
                     25: #include <stdint.h>
                     26: #else /* !defined(_MSC_VER) */
                     27: typedef unsigned short uint16_t;
                     28: #endif /* !defined(_MSC_VER) */
                     29: #ifndef strncasecmp
                     30: #if defined(_MSC_VER) && (_MSC_VER >= 1400)
                     31: #define strncasecmp _memicmp
                     32: #else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
                     33: #define strncasecmp memicmp
                     34: #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
                     35: #endif /* #ifndef strncasecmp */
                     36: #if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PARTITION)
                     37: #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
                     38: #define in6addr_any in6addr_any_init
                     39: static const IN6_ADDR in6addr_any_init = {0};
                     40: #endif
                     41: #endif
                     42: #endif /* _WIN32 */
                     43: #if defined(__amigaos__) || defined(__amigaos4__)
                     44: #include <sys/socket.h>
                     45: #endif /* defined(__amigaos__) || defined(__amigaos4__) */
                     46: #if defined(__amigaos__)
                     47: #define uint16_t unsigned short
                     48: #endif /* defined(__amigaos__) */
                     49: /* Hack */
                     50: #define UNIX_PATH_LEN   108
                     51: struct sockaddr_un {
                     52:   uint16_t sun_family;
                     53:   char     sun_path[UNIX_PATH_LEN];
                     54: };
                     55: #else /* defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) */
                     56: #include <strings.h>
                     57: #include <unistd.h>
                     58: #include <sys/socket.h>
                     59: #include <sys/param.h>
                     60: #include <sys/time.h>
                     61: #include <sys/un.h>
                     62: #include <netinet/in.h>
                     63: #include <arpa/inet.h>
                     64: #include <netdb.h>
                     65: #include <net/if.h>
                     66: #define closesocket close
                     67: #endif
                     68: 
                     69: #include "miniupnpc_socketdef.h"
                     70: 
                     71: #if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__) && !defined(__HAIKU__)
                     72: #define HAS_IP_MREQN
                     73: #endif
                     74: 
                     75: #ifndef _WIN32
                     76: #include <sys/ioctl.h>
                     77: #if defined(__sun) || defined(__HAIKU__)
                     78: #include <sys/sockio.h>
                     79: #endif
                     80: #endif
                     81: 
                     82: #if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
                     83: /* Several versions of glibc don't define this structure,
                     84:  * define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
                     85: struct ip_mreqn
                     86: {
                     87:    struct in_addr  imr_multiaddr;      /* IP multicast address of group */
                     88:    struct in_addr  imr_address;        /* local IP address of interface */
                     89:    int     imr_ifindex;        /* Interface index */
                     90: };
                     91: #endif
                     92: 
                     93: #if defined(__amigaos__) || defined(__amigaos4__)
                     94: /* Amiga OS specific stuff */
                     95: #define TIMEVAL struct timeval
                     96: #endif
                     97: 
                     98: #include "minissdpc.h"
                     99: #include "miniupnpc.h"
                    100: #include "receivedata.h"
                    101: 
                    102: #if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
                    103: 
                    104: #include "codelength.h"
                    105: 
                    106: struct UPNPDev *
                    107: getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error)
                    108: {
                    109:    struct UPNPDev * devlist = NULL;
                    110:    int s;
                    111:    int res;
                    112: 
                    113:    s = connectToMiniSSDPD(socketpath);
                    114:    if (s < 0) {
                    115:        if (error)
                    116:            *error = s;
                    117:        return NULL;
                    118:    }
                    119:    res = requestDevicesFromMiniSSDPD(s, devtype);
                    120:    if (res < 0) {
                    121:        if (error)
                    122:            *error = res;
                    123:    } else {
                    124:        devlist = receiveDevicesFromMiniSSDPD(s, error);
                    125:    }
                    126:    disconnectFromMiniSSDPD(s);
                    127:    return devlist;
                    128: }
                    129: 
                    130: /* macros used to read from unix socket */
                    131: #define READ_BYTE_BUFFER(c) \
                    132:    if((int)bufferindex >= n) { \
                    133:        n = read(s, buffer, sizeof(buffer)); \
                    134:        if(n<=0) break; \
                    135:        bufferindex = 0; \
                    136:    } \
                    137:    c = buffer[bufferindex++];
                    138: 
                    139: #ifndef MIN
                    140: #define MIN(a, b) (((a) < (b)) ? (a) : (b))
                    141: #endif /* MIN */
                    142: 
                    143: #define READ_COPY_BUFFER(dst, len) \
                    144:    for(l = len, p = (unsigned char *)dst; l > 0; ) { \
                    145:        unsigned int lcopy; \
                    146:        if((int)bufferindex >= n) { \
                    147:            n = read(s, buffer, sizeof(buffer)); \
                    148:            if(n<=0) break; \
                    149:            bufferindex = 0; \
                    150:        } \
                    151:        lcopy = MIN(l, (n - bufferindex)); \
                    152:        memcpy(p, buffer + bufferindex, lcopy); \
                    153:        l -= lcopy; \
                    154:        p += lcopy; \
                    155:        bufferindex += lcopy; \
                    156:    }
                    157: 
                    158: #define READ_DISCARD_BUFFER(len) \
                    159:    for(l = len; l > 0; ) { \
                    160:        unsigned int lcopy; \
                    161:        if(bufferindex >= n) { \
                    162:            n = read(s, buffer, sizeof(buffer)); \
                    163:            if(n<=0) break; \
                    164:            bufferindex = 0; \
                    165:        } \
                    166:        lcopy = MIN(l, (n - bufferindex)); \
                    167:        l -= lcopy; \
                    168:        bufferindex += lcopy; \
                    169:    }
                    170: 
                    171: int
                    172: connectToMiniSSDPD(const char * socketpath)
                    173: {
                    174:    int s;
                    175:    struct sockaddr_un addr;
                    176: #if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
                    177:    struct timeval timeout;
                    178: #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
                    179: 
                    180:    s = socket(AF_UNIX, SOCK_STREAM, 0);
                    181:    if(s < 0)
                    182:    {
                    183:        /*syslog(LOG_ERR, "socket(unix): %m");*/
                    184:        perror("socket(unix)");
                    185:        return MINISSDPC_SOCKET_ERROR;
                    186:    }
                    187: #if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
                    188:    /* setting a 3 seconds timeout */
                    189:    /* not supported for AF_UNIX sockets under Solaris */
                    190:    timeout.tv_sec = 3;
                    191:    timeout.tv_usec = 0;
                    192:    if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
                    193:    {
                    194:        perror("setsockopt SO_RCVTIMEO unix");
                    195:    }
                    196:    timeout.tv_sec = 3;
                    197:    timeout.tv_usec = 0;
                    198:    if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
                    199:    {
                    200:        perror("setsockopt SO_SNDTIMEO unix");
                    201:    }
                    202: #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
                    203:    if(!socketpath)
                    204:        socketpath = "/var/run/minissdpd.sock";
                    205:    memset(&addr, 0, sizeof(addr));
                    206:    addr.sun_family = AF_UNIX;
                    207:    strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
                    208:    /* TODO : check if we need to handle the EINTR */
                    209:    if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
                    210:    {
                    211:        /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
                    212:        close(s);
                    213:        return MINISSDPC_SOCKET_ERROR;
                    214:    }
                    215:    return s;
                    216: }
                    217: 
                    218: int
                    219: disconnectFromMiniSSDPD(int s)
                    220: {
                    221:    if (close(s) < 0)
                    222:        return MINISSDPC_SOCKET_ERROR;
                    223:    return MINISSDPC_SUCCESS;
                    224: }
                    225: 
                    226: int
                    227: requestDevicesFromMiniSSDPD(int s, const char * devtype)
                    228: {
                    229:    unsigned char buffer[256];
                    230:    unsigned char * p;
                    231:    unsigned int stsize, l;
                    232: 
                    233:    stsize = strlen(devtype);
                    234:    if(stsize == 8 && 0 == memcmp(devtype, "ssdp:all", 8))
                    235:    {
                    236:        buffer[0] = 3;  /* request type 3 : everything */
                    237:    }
                    238:    else
                    239:    {
                    240:        buffer[0] = 1; /* request type 1 : request devices/services by type */
                    241:    }
                    242:    p = buffer + 1;
                    243:    l = stsize; CODELENGTH(l, p);
                    244:    if(p + stsize > buffer + sizeof(buffer))
                    245:    {
                    246:        /* devtype is too long ! */
                    247: #ifdef DEBUG
                    248:        fprintf(stderr, "devtype is too long ! stsize=%u sizeof(buffer)=%u\n",
                    249:                stsize, (unsigned)sizeof(buffer));
                    250: #endif /* DEBUG */
                    251:        return MINISSDPC_INVALID_INPUT;
                    252:    }
                    253:    memcpy(p, devtype, stsize);
                    254:    p += stsize;
                    255:    if(write(s, buffer, p - buffer) < 0)
                    256:    {
                    257:        /*syslog(LOG_ERR, "write(): %m");*/
                    258:        perror("minissdpc.c: write()");
                    259:        return MINISSDPC_SOCKET_ERROR;
                    260:    }
                    261:    return MINISSDPC_SUCCESS;
                    262: }
                    263: 
                    264: struct UPNPDev *
                    265: receiveDevicesFromMiniSSDPD(int s, int * error)
                    266: {
                    267:    struct UPNPDev * tmp;
                    268:    struct UPNPDev * devlist = NULL;
                    269:    unsigned char buffer[256];
                    270:    ssize_t n;
                    271:    unsigned char * p;
                    272:    unsigned char * url;
                    273:    unsigned char * st;
                    274:    unsigned int bufferindex;
                    275:    unsigned int i, ndev;
                    276:    unsigned int urlsize, stsize, usnsize, l;
                    277: 
                    278:    n = read(s, buffer, sizeof(buffer));
                    279:    if(n<=0)
                    280:    {
                    281:        perror("minissdpc.c: read()");
                    282:        if (error)
                    283:            *error = MINISSDPC_SOCKET_ERROR;
                    284:        return NULL;
                    285:    }
                    286:    ndev = buffer[0];
                    287:    bufferindex = 1;
                    288:    for(i = 0; i < ndev; i++)
                    289:    {
                    290:        DECODELENGTH_READ(urlsize, READ_BYTE_BUFFER);
                    291:        if(n<=0) {
                    292:            if (error)
                    293:                *error = MINISSDPC_INVALID_SERVER_REPLY;
                    294:            return devlist;
                    295:        }
                    296: #ifdef DEBUG
                    297:        printf("  urlsize=%u", urlsize);
                    298: #endif /* DEBUG */
                    299:        url = malloc(urlsize);
                    300:        if(url == NULL) {
                    301:            if (error)
                    302:                *error = MINISSDPC_MEMORY_ERROR;
                    303:            return devlist;
                    304:        }
                    305:        READ_COPY_BUFFER(url, urlsize);
                    306:        if(n<=0) {
                    307:            if (error)
                    308:                *error = MINISSDPC_INVALID_SERVER_REPLY;
                    309:            goto free_url_and_return;
                    310:        }
                    311:        DECODELENGTH_READ(stsize, READ_BYTE_BUFFER);
                    312:        if(n<=0) {
                    313:            if (error)
                    314:                *error = MINISSDPC_INVALID_SERVER_REPLY;
                    315:            goto free_url_and_return;
                    316:        }
                    317: #ifdef DEBUG
                    318:        printf("   stsize=%u", stsize);
                    319: #endif /* DEBUG */
                    320:        st = malloc(stsize);
                    321:        if (st == NULL) {
                    322:            if (error)
                    323:                *error = MINISSDPC_MEMORY_ERROR;
                    324:            goto free_url_and_return;
                    325:        }
                    326:        READ_COPY_BUFFER(st, stsize);
                    327:        if(n<=0) {
                    328:            if (error)
                    329:                *error = MINISSDPC_INVALID_SERVER_REPLY;
                    330:            goto free_url_and_st_and_return;
                    331:        }
                    332:        DECODELENGTH_READ(usnsize, READ_BYTE_BUFFER);
                    333:        if(n<=0) {
                    334:            if (error)
                    335:                *error = MINISSDPC_INVALID_SERVER_REPLY;
                    336:            goto free_url_and_st_and_return;
                    337:        }
                    338: #ifdef DEBUG
                    339:        printf("   usnsize=%u\n", usnsize);
                    340: #endif /* DEBUG */
                    341:        tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
                    342:        if(tmp == NULL) {
                    343:            if (error)
                    344:                *error = MINISSDPC_MEMORY_ERROR;
                    345:            goto free_url_and_st_and_return;
                    346:        }
                    347:        tmp->pNext = devlist;
                    348:        tmp->descURL = tmp->buffer;
                    349:        tmp->st = tmp->buffer + 1 + urlsize;
                    350:        memcpy(tmp->buffer, url, urlsize);
                    351:        tmp->buffer[urlsize] = '\0';
                    352:        memcpy(tmp->st, st, stsize);
                    353:        tmp->buffer[urlsize+1+stsize] = '\0';
                    354:        free(url);
                    355:        free(st);
                    356:        url = NULL;
                    357:        st = NULL;
                    358:        tmp->usn = tmp->buffer + 1 + urlsize + 1 + stsize;
                    359:        READ_COPY_BUFFER(tmp->usn, usnsize);
                    360:        if(n<=0) {
                    361:            if (error)
                    362:                *error = MINISSDPC_INVALID_SERVER_REPLY;
                    363:            goto free_tmp_and_return;
                    364:        }
                    365:        tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
                    366:        tmp->scope_id = 0;  /* default value. scope_id is not available with MiniSSDPd */
                    367:        devlist = tmp;
                    368:    }
                    369:    if (error)
                    370:        *error = MINISSDPC_SUCCESS;
                    371:    return devlist;
                    372: 
                    373: free_url_and_st_and_return:
                    374:    free(st);
                    375: free_url_and_return:
                    376:    free(url);
                    377:    return devlist;
                    378: 
                    379: free_tmp_and_return:
                    380:    free(tmp);
                    381:    return devlist;
                    382: }
                    383: 
                    384: #endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
                    385: 
                    386: /* parseMSEARCHReply()
                    387:  * the last 4 arguments are filled during the parsing :
                    388:  *    - location/locationsize : "location:" field of the SSDP reply packet
                    389:  *    - st/stsize : "st:" field of the SSDP reply packet.
                    390:  *    - usn/usnsize : "usn:" filed of the SSDP reply packet
                    391:  * The strings are NOT null terminated */
                    392: static void
                    393: parseMSEARCHReply(const char * reply, int size,
                    394:                   const char * * location, int * locationsize,
                    395:                  const char * * st, int * stsize,
                    396:                  const char * * usn, int * usnsize)
                    397: {
                    398:    int a, b, i;
                    399:    i = 0;
                    400:    a = i;  /* start of the line */
                    401:    b = 0;  /* end of the "header" (position of the colon) */
                    402:    while(i<size)
                    403:    {
                    404:        switch(reply[i])
                    405:        {
                    406:        case ':':
                    407:                if(b==0)
                    408:                {
                    409:                    b = i; /* end of the "header" */
                    410:                    /*for(j=a; j<b; j++)
                    411:                    {
                    412:                        putchar(reply[j]);
                    413:                    }
                    414:                    */
                    415:                }
                    416:                break;
                    417:        case '\x0a':
                    418:        case '\x0d':
                    419:                if(b!=0)
                    420:                {
                    421:                    /*for(j=b+1; j<i; j++)
                    422:                    {
                    423:                        putchar(reply[j]);
                    424:                    }
                    425:                    putchar('\n');*/
                    426:                    /* skip the colon and white spaces */
                    427:                    do { b++; } while(reply[b]==' ');
                    428:                    if(0==strncasecmp(reply+a, "location:", 9))
                    429:                    {
                    430:                        *location = reply+b;
                    431:                        *locationsize = i-b;
                    432:                    }
                    433:                    else if(0==strncasecmp(reply+a, "st:", 3))
                    434:                    {
                    435:                        *st = reply+b;
                    436:                        *stsize = i-b;
                    437:                    }
                    438:                    else if(0==strncasecmp(reply+a, "usn:", 4))
                    439:                    {
                    440:                        *usn = reply+b;
                    441:                        *usnsize = i-b;
                    442:                    }
                    443:                    b = 0;
                    444:                }
                    445:                a = i+1;
                    446:                break;
                    447:        default:
                    448:                break;
                    449:        }
                    450:        i++;
                    451:    }
                    452: }
                    453: 
                    454: #if defined(CLOCK_MONOTONIC_FAST)
                    455: #define UPNP_CLOCKID CLOCK_MONOTONIC_FAST
                    456: #elif defined(CLOCK_MONOTONIC)
                    457: #define UPNP_CLOCKID CLOCK_MONOTONIC
                    458: #endif
                    459: 
                    460: static int upnp_gettimeofday(struct timeval * tv)
                    461: {
                    462: #if defined(_WIN32)
                    463: #if _WIN32_WINNT >= 0x0600 // _WIN32_WINNT_VISTA
                    464:    ULONGLONG ts = GetTickCount64();
                    465: #else
                    466:    DWORD ts = GetTickCount();
                    467: #endif
                    468:    tv->tv_sec = (long)(ts / 1000);
                    469:    tv->tv_usec = (ts % 1000) * 1000;
                    470:    return 0; /* success */
                    471: #elif defined(CLOCK_MONOTONIC_FAST) || defined(CLOCK_MONOTONIC)
                    472: #if defined(__APPLE__)
                    473: #if defined(__clang__)
                    474:    if (__builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
                    475: #else /* !defined(__clang__) */
                    476:    if (clock_gettime != NULL) {
                    477: #endif /* defined(__clang__) */
                    478: #endif /* defined(__APPLE__) */
                    479:        struct timespec ts;
                    480:        int ret_code = clock_gettime(UPNP_CLOCKID, &ts);
                    481:        if (ret_code == 0)
                    482:        {
                    483:            tv->tv_sec = ts.tv_sec;
                    484:            tv->tv_usec = ts.tv_nsec / 1000;
                    485:        }
                    486:        return ret_code;
                    487: #if defined(__APPLE__)
                    488:    }
                    489:    else
                    490:    {
                    491:        /* fall-back for earlier Apple platforms */
                    492:        return gettimeofday(tv, NULL);
                    493:    }
                    494: #endif /* defined(__APPLE__) */
                    495: #else
                    496:    return gettimeofday(tv, NULL);
                    497: #endif
                    498: }
                    499: /* port upnp discover : SSDP protocol */
                    500: #define SSDP_PORT 1900
                    501: #define XSTR(s) STR(s)
                    502: #define STR(s) #s
                    503: #define UPNP_MCAST_ADDR "239.255.255.250"
                    504: /* for IPv6 */
                    505: #define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
                    506: #define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
                    507: 
                    508: /* direct discovery if minissdpd responses are not sufficient */
                    509: /* ssdpDiscoverDevices() :
                    510:  * return a chained list of all devices found or NULL if
                    511:  * no devices was found.
                    512:  * It is up to the caller to free the chained list
                    513:  * delay is in millisecond (poll).
                    514:  * UDA v1.1 says :
                    515:  *   The TTL for the IP packet SHOULD default to 2 and
                    516:  *   SHOULD be configurable. */
                    517: struct UPNPDev *
                    518: ssdpDiscoverDevices(const char * const deviceTypes[],
                    519:                     int delay, const char * multicastif,
                    520:                     int localport,
                    521:                     int ipv6, unsigned char ttl,
                    522:                     int * error,
                    523:                     int searchalltypes)
                    524: {
                    525:    struct UPNPDev * tmp;
                    526:    struct UPNPDev * devlist = NULL;
                    527:    unsigned int scope_id = 0;
                    528:    int opt = 1;
                    529:    static const char MSearchMsgFmt[] =
                    530:    "M-SEARCH * HTTP/1.1\r\n"
                    531:    "HOST: %s:" XSTR(SSDP_PORT) "\r\n"
                    532:    "ST: %s\r\n"
                    533:    "MAN: \"ssdp:discover\"\r\n"
                    534:    "MX: %u\r\n"
                    535:    "\r\n";
                    536:    int deviceIndex;
                    537:    char bufr[1536];    /* reception and emission buffer */
                    538:    SOCKET sudp;
                    539:    int n;
                    540:    struct sockaddr_storage sockudp_r;
                    541:    unsigned int mx;
                    542: #ifdef NO_GETADDRINFO
                    543:    struct sockaddr_storage sockudp_w;
                    544: #else
                    545:    int rv;
                    546:    struct addrinfo hints, *servinfo;
                    547: #endif
                    548: #ifdef _WIN32
                    549:    unsigned long _ttl = (unsigned long)ttl;
                    550: #endif
                    551:    int linklocal = 1;
                    552:    int sentok;
                    553: 
                    554:    if(error)
                    555:        *error = MINISSDPC_UNKNOWN_ERROR;
                    556: 
                    557:    if(localport==UPNP_LOCAL_PORT_SAME)
                    558:        localport = SSDP_PORT;
                    559: 
                    560: #ifdef _WIN32
                    561:    sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
                    562: #else
                    563:    sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
                    564: #endif
                    565:    if(ISINVALID(sudp))
                    566:    {
                    567:        if(error)
                    568:            *error = MINISSDPC_SOCKET_ERROR;
                    569:        PRINT_SOCKET_ERROR("socket");
                    570:        return NULL;
                    571:    }
                    572:    /* reception */
                    573:    memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
                    574:    if(ipv6) {
                    575:        struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
                    576:        p->sin6_family = AF_INET6;
                    577:        if(localport > 0 && localport < 65536)
                    578:            p->sin6_port = htons((unsigned short)localport);
                    579:        p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
                    580:    } else {
                    581:        struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
                    582:        p->sin_family = AF_INET;
                    583:        if(localport > 0 && localport < 65536)
                    584:            p->sin_port = htons((unsigned short)localport);
                    585:        p->sin_addr.s_addr = INADDR_ANY;
                    586:    }
                    587: #ifdef _WIN32
                    588: /* This code could help us to use the right Network interface for
                    589:  * SSDP multicast traffic */
                    590: /* Get IP associated with the index given in the ip_forward struct
                    591:  * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
                    592:    if(!ipv6) {
                    593:        DWORD ifbestidx;
                    594: #if _WIN32_WINNT >= 0x0600 // _WIN32_WINNT_VISTA
                    595:        // While we don't need IPv6 support, the IPv4 only funciton is not available in UWP apps.
                    596:        SOCKADDR_IN destAddr;
                    597:        memset(&destAddr, 0, sizeof(destAddr));
                    598:        destAddr.sin_family = AF_INET;
                    599:        destAddr.sin_addr.s_addr = inet_addr("223.255.255.255");
                    600:        destAddr.sin_port = 0;
                    601:        if (GetBestInterfaceEx((struct sockaddr *)&destAddr, &ifbestidx) == NO_ERROR) {
                    602: #else
                    603:        if (GetBestInterface(inet_addr("223.255.255.255"), &ifbestidx) == NO_ERROR) {
                    604: #endif
                    605:            DWORD dwRetVal = NO_ERROR;
                    606:            PIP_ADAPTER_ADDRESSES pAddresses = NULL;
                    607:            ULONG outBufLen = 15360;
                    608:            int Iterations;
                    609:            PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
                    610:            PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
                    611: 
                    612:            for (Iterations = 0; Iterations < 3; Iterations++) {
                    613:                pAddresses = (IP_ADAPTER_ADDRESSES *) HeapAlloc(GetProcessHeap(), 0, outBufLen);
                    614:                if (pAddresses == NULL) {
                    615:                    break;
                    616:                }
                    617: 
                    618:                dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen);
                    619: 
                    620:                if (dwRetVal != ERROR_BUFFER_OVERFLOW) {
                    621:                    break;
                    622:                }
                    623:                HeapFree(GetProcessHeap(), 0, pAddresses);
                    624:                pAddresses = NULL;
                    625:            }
                    626: 
                    627:            if (dwRetVal == NO_ERROR) {
                    628:                pCurrAddresses = pAddresses;
                    629:                while (pCurrAddresses) {
                    630: #ifdef DEBUG
                    631:                    int i;
                    632:                    PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
                    633:                    PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
                    634: 
                    635:                    printf("\tIfIndex (IPv4 interface): %u\n", pCurrAddresses->IfIndex);
                    636:                    printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName);
                    637:                    pUnicast = pCurrAddresses->FirstUnicastAddress;
                    638:                    if (pUnicast != NULL) {
                    639:                        for (i = 0; pUnicast != NULL; i++) {
                    640:                            printf("\tIP Address[%d]:     \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pUnicast->Address.lpSockaddr)->sin_addr) );
                    641:                            pUnicast = pUnicast->Next;
                    642:                        }
                    643:                        printf("\tNumber of Unicast Addresses: %d\n", i);
                    644:                    }
                    645:                    pAnycast = pCurrAddresses->FirstAnycastAddress;
                    646:                    if (pAnycast) {
                    647:                        for (i = 0; pAnycast != NULL; i++) {
                    648:                            printf("\tAnycast Address[%d]:     \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pAnycast->Address.lpSockaddr)->sin_addr) );
                    649:                            pAnycast = pAnycast->Next;
                    650:                        }
                    651:                        printf("\tNumber of Anycast Addresses: %d\n", i);
                    652:                    }
                    653:                    pMulticast = pCurrAddresses->FirstMulticastAddress;
                    654:                    if (pMulticast) {
                    655:                        for (i = 0; pMulticast != NULL; i++) {
                    656:                            printf("\tMulticast Address[%d]:     \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pMulticast->Address.lpSockaddr)->sin_addr) );
                    657:               pMulticast = pMulticast->Next;
                    658:                        }
                    659:                    }
                    660:                    printf("\n");
                    661: #endif
                    662:                    pUnicast = pCurrAddresses->FirstUnicastAddress;
                    663:                    if (pCurrAddresses->IfIndex == ifbestidx && pUnicast != NULL) {
                    664:                        SOCKADDR_IN *ipv4 = (SOCKADDR_IN *)(pUnicast->Address.lpSockaddr);
                    665:                        /* Set the address of this interface to be used */
                    666:                        struct in_addr mc_if;
                    667:                        memset(&mc_if, 0, sizeof(mc_if));
                    668:                        mc_if.s_addr = ipv4->sin_addr.s_addr;
                    669:                        if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
                    670:                            PRINT_SOCKET_ERROR("setsockopt");
                    671:                        }
                    672:                        ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = ipv4->sin_addr.s_addr;
                    673: #ifndef DEBUG
                    674:                        break;
                    675: #endif
                    676:                    }
                    677:                    pCurrAddresses = pCurrAddresses->Next;
                    678:                }
                    679:            }
                    680:            if (pAddresses != NULL) {
                    681:                HeapFree(GetProcessHeap(), 0, pAddresses);
                    682:                pAddresses = NULL;
                    683:            }
                    684:        }
                    685:    }
                    686: #endif /* _WIN32 */
                    687: 
                    688: #ifdef _WIN32
                    689:    if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
                    690: #else
                    691:    if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
                    692: #endif
                    693:    {
                    694:        if(error)
                    695:            *error = MINISSDPC_SOCKET_ERROR;
                    696:        PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)");
                    697:        goto error;
                    698:    }
                    699: 
                    700:    if(ipv6) {
                    701: #ifdef _WIN32
                    702:        DWORD mcastHops = ttl;
                    703:        if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0)
                    704: #else  /* _WIN32 */
                    705:        int mcastHops = ttl;
                    706:        if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0)
                    707: #endif /* _WIN32 */
                    708:        {
                    709:            PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)");
                    710:        }
                    711:    } else {
                    712: #ifdef _WIN32
                    713:        if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
                    714: #else  /* _WIN32 */
                    715:        if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
                    716: #endif /* _WIN32 */
                    717:        {
                    718:            /* not a fatal error */
                    719:            PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
                    720:        }
                    721:    }
                    722: 
                    723:    if(multicastif && multicastif[0] != '\0')
                    724:    {
                    725:        if(ipv6) {
                    726: #if !defined(_WIN32)
                    727:            /* according to MSDN, if_nametoindex() is supported since
                    728:             * MS Windows Vista and MS Windows Server 2008.
                    729:             * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
                    730:            unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
                    731:            if(ifindex == 0)
                    732:            {
                    733:                if(error)
                    734:                    *error = MINISSDPC_INVALID_INPUT;
                    735:                fprintf(stderr, "Invalid multicast interface name %s\n", multicastif);
                    736:                goto error;
                    737:            }
                    738:            if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
                    739:            {
                    740:                PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
                    741:            }
                    742: #else
                    743: #ifdef DEBUG
                    744:            printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
                    745: #endif
                    746: #endif
                    747:        } else {
                    748:            struct in_addr mc_if;
                    749: #if defined(_WIN32)
                    750: #if _WIN32_WINNT >= 0x0600 // _WIN32_WINNT_VISTA
                    751:            InetPtonA(AF_INET, multicastif, &mc_if);
                    752: #else
                    753:            mc_if.s_addr = inet_addr(multicastif); /* old Windows SDK do not support InetPtoA() */
                    754: #endif
                    755: #else
                    756:            /* was : mc_if.s_addr = inet_addr(multicastif); */ /* ex: 192.168.x.x */
                    757:            if (inet_pton(AF_INET, multicastif, &mc_if.s_addr) <= 0) {
                    758:                mc_if.s_addr = INADDR_NONE;
                    759:            }
                    760: #endif
                    761:            if(mc_if.s_addr != INADDR_NONE)
                    762:            {
                    763:                ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
                    764:                if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
                    765:                {
                    766:                    PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
                    767:                }
                    768:            } else {
                    769:                /* was not an ip address, try with an interface name */
                    770: #ifndef _WIN32
                    771: #ifdef HAS_IP_MREQN
                    772:                struct ip_mreqn reqn;   /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
                    773: #endif
                    774:                struct ifreq ifr;
                    775:                int ifrlen = sizeof(ifr);
                    776:                strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
                    777:                ifr.ifr_name[IFNAMSIZ-1] = '\0';
                    778:                if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
                    779:                {
                    780:                    PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
                    781:                    goto error;
                    782:                }
                    783:                mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
                    784: #ifdef HAS_IP_MREQN
                    785:                memset(&reqn, 0, sizeof(struct ip_mreqn));
                    786:                reqn.imr_address.s_addr = mc_if.s_addr;
                    787:                reqn.imr_ifindex = if_nametoindex(multicastif);
                    788:                if(reqn.imr_ifindex == 0)
                    789:                {
                    790:                    if(error)
                    791:                        *error = MINISSDPC_INVALID_INPUT;
                    792:                    fprintf(stderr, "Invalid multicast ip address / interface name %s\n", multicastif);
                    793:                    goto error;
                    794:                }
                    795:                if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
                    796:                {
                    797:                    PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
                    798:                }
                    799: #else
                    800:                if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
                    801:                {
                    802:                    PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
                    803:                }
                    804: #endif
                    805: #else /* _WIN32 */
                    806: #ifdef DEBUG
                    807:                printf("Setting of multicast interface not supported with interface name.\n");
                    808: #endif
                    809: #endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */
                    810:            }
                    811:        }
                    812:    }
                    813: 
                    814:    /* Before sending the packed, we first "bind" in order to be able
                    815:     * to receive the response */
                    816:    if (bind(sudp, (const struct sockaddr *)&sockudp_r,
                    817:             ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
                    818:    {
                    819:        if(error)
                    820:            *error = MINISSDPC_SOCKET_ERROR;
                    821:        PRINT_SOCKET_ERROR("bind");
                    822:        closesocket(sudp);
                    823:        return NULL;
                    824:    }
                    825: 
                    826:    if(error)
                    827:        *error = MINISSDPC_SUCCESS;
                    828:    /* Calculating maximum response time in seconds */
                    829:    mx = ((unsigned int)delay) / 1000u;
                    830:    if(mx == 0) {
                    831:        mx = 1;
                    832:        delay = 1000;
                    833:    }
                    834:    /* receiving SSDP response packet */
                    835:    for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
                    836:        sentok = 0;
                    837:        /* sending the SSDP M-SEARCH packet */
                    838:        n = snprintf(bufr, sizeof(bufr),
                    839:                     MSearchMsgFmt,
                    840:                     ipv6 ?
                    841:                     (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" :  "[" UPNP_MCAST_SL_ADDR "]")
                    842:                     : UPNP_MCAST_ADDR,
                    843:                     deviceTypes[deviceIndex], mx);
                    844:        if ((unsigned int)n >= sizeof(bufr)) {
                    845:            if(error)
                    846:                *error = MINISSDPC_MEMORY_ERROR;
                    847:            goto error;
                    848:        }
                    849: #ifdef DEBUG
                    850:        /*printf("Sending %s", bufr);*/
                    851:        printf("Sending M-SEARCH request to %s with ST: %s\n",
                    852:               ipv6 ?
                    853:               (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" :  "[" UPNP_MCAST_SL_ADDR "]")
                    854:               : UPNP_MCAST_ADDR,
                    855:               deviceTypes[deviceIndex]);
                    856: #endif
                    857: #ifdef NO_GETADDRINFO
                    858:        /* the following code is not using getaddrinfo */
                    859:        /* emission */
                    860:        memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
                    861:        if(ipv6) {
                    862:            struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
                    863:            p->sin6_family = AF_INET6;
                    864:            p->sin6_port = htons(SSDP_PORT);
                    865:            inet_pton(AF_INET6,
                    866:                      linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
                    867:                      &(p->sin6_addr));
                    868:        } else {
                    869:            struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
                    870:            p->sin_family = AF_INET;
                    871:            p->sin_port = htons(SSDP_PORT);
                    872:            p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
                    873:        }
                    874:        n = sendto(sudp, bufr, n, 0, &sockudp_w,
                    875:                   ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
                    876:        if (n < 0) {
                    877:            if(error)
                    878:                *error = MINISSDPC_SOCKET_ERROR;
                    879:            PRINT_SOCKET_ERROR("sendto");
                    880:        } else {
                    881:            sentok = 1;
                    882:        }
                    883: #else /* #ifdef NO_GETADDRINFO */
                    884:        memset(&hints, 0, sizeof(hints));
                    885:        hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
                    886:        hints.ai_socktype = SOCK_DGRAM;
                    887:        /*hints.ai_flags = */
                    888:        if ((rv = getaddrinfo(ipv6
                    889:                              ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
                    890:                              : UPNP_MCAST_ADDR,
                    891:                              XSTR(SSDP_PORT), &hints, &servinfo)) != 0) {
                    892:            if(error)
                    893:                *error = MINISSDPC_SOCKET_ERROR;
                    894: #ifdef _WIN32
                    895:            fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
                    896: #else
                    897:            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
                    898: #endif
                    899:            break;
                    900:        } else {
                    901:            struct addrinfo *p;
                    902:            for(p = servinfo; p; p = p->ai_next) {
                    903:                n = sendto(sudp, bufr, n, 0, p->ai_addr, MSC_CAST_INT p->ai_addrlen);
                    904:                if (n < 0) {
                    905: #ifdef DEBUG
                    906:                    char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
                    907:                    if (getnameinfo(p->ai_addr, (socklen_t)p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
                    908:                                    sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
                    909:                        fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
                    910:                    }
                    911: #endif
                    912:                    PRINT_SOCKET_ERROR("sendto");
                    913:                    continue;
                    914:                } else {
                    915:                    sentok = 1;
                    916:                }
                    917:            }
                    918:            freeaddrinfo(servinfo);
                    919:        }
                    920:        if(!sentok) {
                    921:            if(error)
                    922:                *error = MINISSDPC_SOCKET_ERROR;
                    923:        }
                    924: #endif /* #ifdef NO_GETADDRINFO */
                    925:        /* Waiting for SSDP REPLY packet to M-SEARCH
                    926:         * if searchalltypes is set, enter the loop only
                    927:         * when the last deviceType is reached */
                    928:        if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) {
                    929:            struct timeval start = {0, 0}, current = {0, 0};
                    930:            upnp_gettimeofday(&start);
                    931:            do {
                    932:                n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
                    933:                if (n < 0) {
                    934:                    /* error */
                    935:                    if(error)
                    936:                        *error = MINISSDPC_SOCKET_ERROR;
                    937:                    goto error;
                    938:                } else if (n == 0) {
                    939:                    /* no data or Time Out */
                    940: #ifdef DEBUG
                    941:                    printf("NODATA or TIMEOUT\n");
                    942: #endif /* DEBUG */
                    943:                    if (devlist && !searchalltypes) {
                    944:                        /* found some devices, stop now*/
                    945:                        if(error)
                    946:                            *error = MINISSDPC_SUCCESS;
                    947:                        goto error;
                    948:                    }
                    949:                } else {
                    950:                    const char * descURL=NULL;
                    951:                    int urlsize=0;
                    952:                    const char * st=NULL;
                    953:                    int stsize=0;
                    954:                    const char * usn=NULL;
                    955:                    int usnsize=0;
                    956:                    parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize);
                    957:                    if(st&&descURL) {
                    958: #ifdef DEBUG
                    959:                        printf("M-SEARCH Reply:\n  ST: %.*s\n  USN: %.*s\n  Location: %.*s\n",
                    960:                               stsize, st, usnsize, (usn?usn:""), urlsize, descURL);
                    961: #endif /* DEBUG */
                    962:                        for(tmp=devlist; tmp; tmp = tmp->pNext) {
                    963:                            if(strncmp(tmp->descURL, descURL, urlsize) == 0 &&
                    964:                               tmp->descURL[urlsize] == '\0' &&
                    965:                               strncmp(tmp->st, st, stsize) == 0 &&
                    966:                               tmp->st[stsize] == '\0' &&
                    967:                               (usnsize == 0 || strncmp(tmp->usn, usn, usnsize) == 0) &&
                    968:                               tmp->usn[usnsize] == '\0')
                    969:                                break;
                    970:                        }
                    971:                        /* at the exit of the loop above, tmp is null if
                    972:                         * no duplicate device was found */
                    973:                        if(tmp)
                    974:                            continue;
                    975:                        tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3);
                    976:                        if(!tmp) {
                    977:                            /* memory allocation error */
                    978:                            if(error)
                    979:                                *error = MINISSDPC_MEMORY_ERROR;
                    980:                            goto error;
                    981:                        }
                    982:                        tmp->pNext = devlist;
                    983:                        tmp->descURL = tmp->buffer;
                    984:                        tmp->st = tmp->buffer + 1 + urlsize;
                    985:                        tmp->usn = tmp->st + 1 + stsize;
                    986:                        memcpy(tmp->buffer, descURL, urlsize);
                    987:                        tmp->buffer[urlsize] = '\0';
                    988:                        memcpy(tmp->st, st, stsize);
                    989:                        tmp->buffer[urlsize+1+stsize] = '\0';
                    990:                        if(usn != NULL)
                    991:                            memcpy(tmp->usn, usn, usnsize);
                    992:                        tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
                    993:                        tmp->scope_id = scope_id;
                    994:                        devlist = tmp;
                    995:                    }
                    996:                    if (upnp_gettimeofday(&current) >= 0) {
                    997:                        /* exit the loop if delay is reached */
                    998:                        long interval = (current.tv_sec - start.tv_sec) * 1000;
                    999:                        interval += (current.tv_usec - start.tv_usec) / 1000;
                   1000:                        if (interval > (long)delay)
                   1001:                            break;
                   1002:                    }
                   1003:                }
                   1004:            } while(n > 0);
                   1005:        }
                   1006:        if(ipv6) {
                   1007:            /* switch linklocal flag */
                   1008:            if(linklocal) {
                   1009:                linklocal = 0;
                   1010:                --deviceIndex;
                   1011:            } else {
                   1012:                linklocal = 1;
                   1013:            }
                   1014:        }
                   1015:    }
                   1016: error:
                   1017:    closesocket(sudp);
                   1018:    return devlist;
                   1019: }

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