Annotation of embedaddon/miniupnpd/minissdp.c, revision 1.1.1.3

1.1.1.3 ! misho       1: /* $Id: minissdp.c,v 1.44 2013/02/06 10:50:04 nanard Exp $ */
1.1       misho       2: /* MiniUPnP project
                      3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
1.1.1.3 ! misho       4:  * (c) 2006-2013 Thomas Bernard
1.1       misho       5:  * This software is subject to the conditions detailed
                      6:  * in the LICENCE file provided within the distribution */
                      7: 
                      8: #include <stdio.h>
1.1.1.3 ! misho       9: #include <stdlib.h>
1.1       misho      10: #include <string.h>
                     11: #include <unistd.h>
                     12: #include <sys/socket.h>
                     13: #include <sys/un.h>
                     14: #include <netinet/in.h>
                     15: #include <arpa/inet.h>
1.1.1.3 ! misho      16: #include <errno.h>
1.1       misho      17: #include <syslog.h>
1.1.1.3 ! misho      18: 
1.1       misho      19: #include "config.h"
                     20: #include "upnpdescstrings.h"
                     21: #include "miniupnpdpath.h"
                     22: #include "upnphttp.h"
                     23: #include "upnpglobalvars.h"
                     24: #include "minissdp.h"
1.1.1.2   misho      25: #include "upnputils.h"
1.1.1.3 ! misho      26: #include "getroute.h"
1.1       misho      27: #include "codelength.h"
                     28: 
                     29: /* SSDP ip/port */
                     30: #define SSDP_PORT (1900)
                     31: #define SSDP_MCAST_ADDR ("239.255.255.250")
1.1.1.3 ! misho      32: #define LL_SSDP_MCAST_ADDR "FF02::C"
        !            33: #define SL_SSDP_MCAST_ADDR "FF05::C"
1.1       misho      34: 
1.1.1.2   misho      35: /* AddMulticastMembership()
                     36:  * param s             socket
                     37:  * param ifaddr        ip v4 address
                     38:  */
1.1       misho      39: static int
                     40: AddMulticastMembership(int s, in_addr_t ifaddr)
                     41: {
                     42:        struct ip_mreq imr;     /* Ip multicast membership */
                     43: 
                     44:     /* setting up imr structure */
                     45:     imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
                     46:     /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
                     47:     imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
1.1.1.3 ! misho      48: 
1.1       misho      49:        if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
                     50:        {
                     51:         syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
                     52:                return -1;
                     53:     }
                     54: 
                     55:        return 0;
                     56: }
                     57: 
1.1.1.2   misho      58: /* AddMulticastMembershipIPv6()
                     59:  * param s     socket (IPv6)
                     60:  * To be improved to target specific network interfaces */
                     61: #ifdef ENABLE_IPV6
                     62: static int
                     63: AddMulticastMembershipIPv6(int s)
                     64: {
                     65:        struct ipv6_mreq mr;
                     66:        /*unsigned int ifindex;*/
                     67: 
                     68:        memset(&mr, 0, sizeof(mr));
                     69:        inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
                     70:        /*mr.ipv6mr_interface = ifindex;*/
                     71:        mr.ipv6mr_interface = 0; /* 0 : all interfaces */
                     72: #ifndef IPV6_ADD_MEMBERSHIP
                     73: #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
                     74: #endif
                     75:        if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
                     76:        {
                     77:                syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
                     78:                return -1;
                     79:        }
                     80:        inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
                     81:        if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
                     82:        {
                     83:                syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
                     84:                return -1;
                     85:        }
                     86:        return 0;
                     87: }
                     88: #endif
                     89: 
1.1.1.3 ! misho      90: /* Open and configure the socket listening for
1.1.1.2   misho      91:  * SSDP udp packets sent on 239.255.255.250 port 1900
                     92:  * SSDP v6 udp packets sent on FF02::C, or FF05::C, port 1900 */
1.1       misho      93: int
1.1.1.2   misho      94: OpenAndConfSSDPReceiveSocket(int ipv6)
1.1       misho      95: {
                     96:        int s;
1.1.1.2   misho      97:        struct sockaddr_storage sockname;
                     98:        socklen_t sockname_len;
                     99:        struct lan_addr_s * lan_addr;
1.1       misho     100:        int j = 1;
1.1.1.2   misho     101: 
                    102:        if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
1.1       misho     103:        {
1.1.1.3 ! misho     104:                syslog(LOG_ERR, "%s: socket(udp): %m",
        !           105:                       "OpenAndConfSSDPReceiveSocket");
1.1       misho     106:                return -1;
1.1.1.2   misho     107:        }
                    108: 
                    109:        memset(&sockname, 0, sizeof(struct sockaddr_storage));
                    110:        if(ipv6) {
                    111:                struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname;
                    112:                saddr->sin6_family = AF_INET6;
                    113:                saddr->sin6_port = htons(SSDP_PORT);
                    114:                saddr->sin6_addr = in6addr_any;
                    115:                sockname_len = sizeof(struct sockaddr_in6);
                    116:        } else {
                    117:                struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname;
                    118:                saddr->sin_family = AF_INET;
                    119:                saddr->sin_port = htons(SSDP_PORT);
                    120:                /* NOTE : it seems it doesnt work when binding on the specific address */
                    121:                /*saddr->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
                    122:                saddr->sin_addr.s_addr = htonl(INADDR_ANY);
                    123:                /*saddr->sin_addr.s_addr = inet_addr(ifaddr);*/
                    124:                sockname_len = sizeof(struct sockaddr_in);
                    125:        }
1.1       misho     126: 
                    127:        if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
                    128:        {
                    129:                syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
                    130:        }
                    131: 
1.1.1.3 ! misho     132:        if(!set_non_blocking(s))
        !           133:        {
        !           134:                syslog(LOG_WARNING, "%s: set_non_blocking(): %m",
        !           135:                       "OpenAndConfSSDPReceiveSocket");
        !           136:        }
1.1       misho     137: 
1.1.1.2   misho     138:        if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
1.1       misho     139:        {
1.1.1.3 ! misho     140:                syslog(LOG_ERR, "%s: bind(udp%s): %m",
        !           141:                       "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : "");
1.1       misho     142:                close(s);
                    143:                return -1;
1.1.1.2   misho     144:        }
1.1       misho     145: 
1.1.1.2   misho     146: #ifdef ENABLE_IPV6
                    147:        if(ipv6)
                    148:        {
                    149:                AddMulticastMembershipIPv6(s);
                    150:        }
                    151:        else
                    152: #endif
1.1       misho     153:        {
1.1.1.2   misho     154:                for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
1.1       misho     155:                {
1.1.1.2   misho     156:                        if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0)
                    157:                        {
                    158:                                syslog(LOG_WARNING,
1.1.1.3 ! misho     159:                                       "Failed to add multicast membership for interface %s",
        !           160:                                       lan_addr->str ? lan_addr->str : "NULL");
1.1.1.2   misho     161:                        }
1.1       misho     162:                }
                    163:        }
                    164: 
                    165:        return s;
                    166: }
                    167: 
                    168: /* open the UDP socket used to send SSDP notifications to
                    169:  * the multicast group reserved for them */
                    170: static int
                    171: OpenAndConfSSDPNotifySocket(in_addr_t addr)
                    172: {
                    173:        int s;
                    174:        unsigned char loopchar = 0;
                    175:        int bcast = 1;
1.1.1.3 ! misho     176:        unsigned char ttl = 2; /* UDA v1.1 says :
        !           177:                The TTL for the IP packet SHOULD default to 2 and
        !           178:                SHOULD be configurable. */
        !           179:        /* TODO: Make TTL be configurable */
1.1       misho     180:        struct in_addr mc_if;
                    181:        struct sockaddr_in sockname;
1.1.1.3 ! misho     182: 
1.1       misho     183:        if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
                    184:        {
                    185:                syslog(LOG_ERR, "socket(udp_notify): %m");
                    186:                return -1;
                    187:        }
                    188: 
                    189:        mc_if.s_addr = addr;    /*inet_addr(addr);*/
                    190: 
                    191:        if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
                    192:        {
                    193:                syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
                    194:                close(s);
                    195:                return -1;
                    196:        }
                    197: 
                    198:        if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
                    199:        {
                    200:                syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
                    201:                close(s);
                    202:                return -1;
                    203:        }
1.1.1.3 ! misho     204: 
        !           205:        if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
        !           206:        {
        !           207:                syslog(LOG_WARNING, "setsockopt(udp_notify, IP_MULTICAST_TTL,): %m");
        !           208:        }
        !           209: 
1.1       misho     210:        if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
                    211:        {
                    212:                syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
                    213:                close(s);
                    214:                return -1;
                    215:        }
                    216: 
                    217:        memset(&sockname, 0, sizeof(struct sockaddr_in));
                    218:     sockname.sin_family = AF_INET;
                    219:     sockname.sin_addr.s_addr = addr;   /*inet_addr(addr);*/
                    220: 
                    221:     if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
                    222:        {
                    223:                syslog(LOG_ERR, "bind(udp_notify): %m");
                    224:                close(s);
                    225:                return -1;
                    226:     }
                    227: 
                    228:        return s;
                    229: }
                    230: 
1.1.1.3 ! misho     231: #ifdef ENABLE_IPV6
        !           232: /* open the UDP socket used to send SSDP notifications to
        !           233:  * the multicast group reserved for them. IPv6 */
        !           234: static int
        !           235: OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index)
        !           236: {
        !           237:        int s;
        !           238:        unsigned int loop = 0;
        !           239: 
        !           240:        s = socket(PF_INET6, SOCK_DGRAM, 0);
        !           241:        if(s < 0)
        !           242:        {
        !           243:                syslog(LOG_ERR, "socket(udp_notify IPv6): %m");
        !           244:                return -1;
        !           245:        }
        !           246:        if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)) < 0)
        !           247:        {
        !           248:                syslog(LOG_ERR, "setsockopt(udp_notify IPv6, IPV6_MULTICAST_IF, %u): %m", if_index);
        !           249:                close(s);
        !           250:                return -1;
        !           251:        }
        !           252:        if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) < 0)
        !           253:        {
        !           254:                syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_LOOP): %m");
        !           255:                close(s);
        !           256:                return -1;
        !           257:        }
        !           258:        return s;
        !           259: }
        !           260: #endif
        !           261: 
1.1       misho     262: int
                    263: OpenAndConfSSDPNotifySockets(int * sockets)
                    264: /*OpenAndConfSSDPNotifySockets(int * sockets,
                    265:                              struct lan_addr_s * lan_addr, int n_lan_addr)*/
                    266: {
1.1.1.3 ! misho     267:        int i;
1.1.1.2   misho     268:        struct lan_addr_s * lan_addr;
                    269: 
1.1.1.3 ! misho     270:        for(i=0, lan_addr = lan_addrs.lh_first;
        !           271:            lan_addr != NULL;
        !           272:            lan_addr = lan_addr->list.le_next)
1.1       misho     273:        {
1.1.1.2   misho     274:                sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr);
1.1       misho     275:                if(sockets[i] < 0)
1.1.1.3 ! misho     276:                        goto error;
        !           277:                i++;
        !           278: #ifdef ENABLE_IPV6
        !           279:                sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr->index);
        !           280:                if(sockets[i] < 0)
        !           281:                        goto error;
        !           282:                i++;
        !           283: #endif
1.1       misho     284:        }
                    285:        return 0;
1.1.1.3 ! misho     286: error:
        !           287:        while(--i >= 0)
        !           288:        {
        !           289:                close(sockets[i]);
        !           290:                sockets[i] = -1;
        !           291:        }
        !           292:        return -1;
1.1       misho     293: }
                    294: 
                    295: /*
                    296:  * response from a LiveBox (Wanadoo)
                    297: HTTP/1.1 200 OK
                    298: CACHE-CONTROL: max-age=1800
                    299: DATE: Thu, 01 Jan 1970 04:03:23 GMT
                    300: EXT:
                    301: LOCATION: http://192.168.0.1:49152/gatedesc.xml
                    302: SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
                    303: ST: upnp:rootdevice
                    304: USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
                    305: 
                    306:  * response from a Linksys 802.11b :
                    307: HTTP/1.1 200 OK
                    308: Cache-Control:max-age=120
                    309: Location:http://192.168.5.1:5678/rootDesc.xml
                    310: Server:NT/5.0 UPnP/1.0
                    311: ST:upnp:rootdevice
                    312: USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
                    313: EXT:
                    314:  */
                    315: 
                    316: /* not really an SSDP "announce" as it is the response
                    317:  * to a SSDP "M-SEARCH" */
                    318: static void
1.1.1.2   misho     319: SendSSDPAnnounce2(int s, const struct sockaddr * addr,
1.1       misho     320:                   const char * st, int st_len, const char * suffix,
                    321:                   const char * host, unsigned short port)
                    322: {
                    323:        int l, n;
                    324:        char buf[512];
1.1.1.3 ! misho     325:        char addr_str[64];
1.1.1.2   misho     326:        socklen_t addrlen;
1.1.1.3 ! misho     327:        /*
1.1       misho     328:         * follow guideline from document "UPnP Device Architecture 1.0"
                    329:         * uppercase is recommended.
                    330:         * DATE: is recommended
                    331:         * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
1.1.1.3 ! misho     332:         * - check what to put in the 'Cache-Control' header
1.1.1.2   misho     333:         *
                    334:         * have a look at the document "UPnP Device Architecture v1.1 */
1.1       misho     335:        l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
                    336:                "CACHE-CONTROL: max-age=120\r\n"
                    337:                /*"DATE: ...\r\n"*/
                    338:                "ST: %.*s%s\r\n"
                    339:                "USN: %s::%.*s%s\r\n"
                    340:                "EXT:\r\n"
                    341:                "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
                    342:                "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
1.1.1.3 ! misho     343:                "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
1.1.1.2   misho     344:                "01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
                    345:                "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
                    346:                "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
1.1       misho     347:                "\r\n",
                    348:                st_len, st, suffix,
                    349:                uuidvalue, st_len, st, suffix,
1.1.1.2   misho     350:                host, (unsigned int)port,
                    351:                upnp_bootid, upnp_bootid, upnp_configid);
                    352:        addrlen = (addr->sa_family == AF_INET6)
                    353:                  ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
1.1       misho     354:        n = sendto(s, buf, l, 0,
1.1.1.2   misho     355:                   addr, addrlen);
1.1.1.3 ! misho     356:        sockaddr_to_string(addr, addr_str, sizeof(addr_str));
        !           357:        syslog(LOG_INFO, "SSDP Announce %d bytes to %s ST: %.*s",n,
        !           358:                        addr_str,
1.1       misho     359:                l, buf);
                    360:        if(n < 0)
                    361:        {
1.1.1.3 ! misho     362:                /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
1.1       misho     363:                syslog(LOG_ERR, "sendto(udp): %m");
                    364:        }
                    365: }
                    366: 
1.1.1.3 ! misho     367: #ifndef IGD_V2
        !           368: #define IGD_VER 1
        !           369: #define WANIPC_VER 1
        !           370: #else
        !           371: #define IGD_VER 2
        !           372: #define WANIPC_VER 2
        !           373: #endif
        !           374: 
        !           375: static struct {
        !           376:        const char * s;
        !           377:        const int version;
        !           378: } const known_service_types[] =
        !           379: {
        !           380:        {"upnp:rootdevice", 0},
        !           381:        {"urn:schemas-upnp-org:device:InternetGatewayDevice:", IGD_VER},
        !           382:        {"urn:schemas-upnp-org:device:WANConnectionDevice:", 1},
        !           383:        {"urn:schemas-upnp-org:device:WANDevice:", 1},
        !           384:        {"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1},
        !           385:        {"urn:schemas-upnp-org:service:WANIPConnection:", WANIPC_VER},
        !           386:        {"urn:schemas-upnp-org:service:WANPPPConnection:", 1},
1.1.1.2   misho     387: #ifdef ENABLE_L3F_SERVICE
1.1.1.3 ! misho     388:        {"urn:schemas-upnp-org:service:Layer3Forwarding:", 1},
1.1.1.2   misho     389: #endif
                    390: #ifdef ENABLE_6FC_SERVICE
1.1.1.3 ! misho     391:        {"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1},
1.1.1.2   misho     392: #endif
1.1.1.3 ! misho     393:        {0, 0}
1.1       misho     394: };
                    395: 
                    396: static void
                    397: SendSSDPNotifies(int s, const char * host, unsigned short port,
1.1.1.3 ! misho     398:                  unsigned int lifetime, int ipv6)
1.1       misho     399: {
1.1.1.3 ! misho     400: #ifdef ENABLE_IPV6
        !           401:        struct sockaddr_storage sockname;
        !           402: #else
1.1       misho     403:        struct sockaddr_in sockname;
1.1.1.3 ! misho     404: #endif
1.1       misho     405:        int l, n, i=0;
                    406:        char bufr[512];
1.1.1.3 ! misho     407:        char ver_str[4];
1.1       misho     408: 
1.1.1.3 ! misho     409:        memset(&sockname, 0, sizeof(sockname));
        !           410: #ifdef ENABLE_IPV6
        !           411:        if(ipv6)
        !           412:        {
        !           413:                struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockname;
        !           414:                p->sin6_family = AF_INET6;
        !           415:                p->sin6_port = htons(SSDP_PORT);
        !           416:                inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(p->sin6_addr));
        !           417:        }
        !           418:        else
        !           419: #endif
        !           420:        {
        !           421:                struct sockaddr_in *p = (struct sockaddr_in *)&sockname;
        !           422:                p->sin_family = AF_INET;
        !           423:                p->sin_port = htons(SSDP_PORT);
        !           424:                p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
        !           425:        }
1.1       misho     426: 
1.1.1.3 ! misho     427:        while(known_service_types[i].s)
1.1       misho     428:        {
1.1.1.3 ! misho     429:                if(i==0)
        !           430:                        ver_str[0] = '\0';
        !           431:                else
        !           432:                        snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
        !           433:                l = snprintf(bufr, sizeof(bufr),
1.1.1.2   misho     434:                        "NOTIFY * HTTP/1.1\r\n"
                    435:                        "HOST: %s:%d\r\n"
                    436:                        "CACHE-CONTROL: max-age=%u\r\n"
                    437:                        "lOCATION: http://%s:%d" ROOTDESC_PATH"\r\n"
                    438:                        "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
                    439:                        "NT: %s%s\r\n"
                    440:                        "USN: %s::%s%s\r\n"
                    441:                        "NTS: ssdp:alive\r\n"
1.1.1.3 ! misho     442:                        "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
1.1.1.2   misho     443:                        "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
                    444:                        "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
                    445:                        "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
                    446:                        "\r\n",
1.1.1.3 ! misho     447:                        ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
        !           448:                        SSDP_PORT,
1.1.1.2   misho     449:                        lifetime,
                    450:                        host, port,
1.1.1.3 ! misho     451:                        known_service_types[i].s, ver_str,
        !           452:                        uuidvalue, known_service_types[i].s, ver_str,
1.1.1.2   misho     453:                        upnp_bootid, upnp_bootid, upnp_configid );
1.1.1.3 ! misho     454:                if(l<0)
        !           455:                {
        !           456:                        syslog(LOG_ERR, "SendSSDPNotifies() snprintf error");
        !           457:                        continue;
        !           458:                }
        !           459:                if((unsigned int)l >= sizeof(bufr))
1.1       misho     460:                {
                    461:                        syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
                    462:                        l = sizeof(bufr);
                    463:                }
                    464:                n = sendto(s, bufr, l, 0,
1.1.1.3 ! misho     465:                        (struct sockaddr *)&sockname,
        !           466: #ifdef ENABLE_IPV6
        !           467:                        ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
        !           468: #else
        !           469:                        sizeof(struct sockaddr_in)
        !           470: #endif
        !           471:                        );
1.1       misho     472:                if(n < 0)
                    473:                {
1.1.1.3 ! misho     474:                        /* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
        !           475:                        syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
        !           476:                               host ? host : "NULL");
1.1       misho     477:                }
                    478:                i++;
                    479:        }
                    480: }
                    481: 
                    482: void
                    483: SendSSDPNotifies2(int * sockets,
                    484:                   unsigned short port,
                    485:                   unsigned int lifetime)
                    486: {
                    487:        int i;
1.1.1.2   misho     488:        struct lan_addr_s * lan_addr;
1.1.1.3 ! misho     489:        for(i=0, lan_addr = lan_addrs.lh_first;
        !           490:            lan_addr != NULL;
        !           491:            lan_addr = lan_addr->list.le_next)
1.1       misho     492:        {
1.1.1.3 ! misho     493:                SendSSDPNotifies(sockets[i], lan_addr->str, port,
        !           494:                                 lifetime, 0);
        !           495:                i++;
        !           496: #ifdef ENABLE_IPV6
        !           497:                SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, port,
        !           498:                                 lifetime, 1);
        !           499:                i++;
        !           500: #endif
1.1       misho     501:        }
                    502: }
                    503: 
                    504: /* ProcessSSDPRequest()
                    505:  * process SSDP M-SEARCH requests and responds to them */
                    506: void
                    507: ProcessSSDPRequest(int s, unsigned short port)
                    508: {
                    509:        int n;
                    510:        char bufr[1500];
                    511:        socklen_t len_r;
1.1.1.2   misho     512: #ifdef ENABLE_IPV6
                    513:        struct sockaddr_storage sendername;
                    514:        len_r = sizeof(struct sockaddr_storage);
                    515: #else
1.1       misho     516:        struct sockaddr_in sendername;
                    517:        len_r = sizeof(struct sockaddr_in);
1.1.1.2   misho     518: #endif
1.1       misho     519: 
                    520:        n = recvfrom(s, bufr, sizeof(bufr), 0,
                    521:                     (struct sockaddr *)&sendername, &len_r);
                    522:        if(n < 0)
                    523:        {
1.1.1.3 ! misho     524:                /* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
        !           525:                 * other errors : log to LOG_ERR */
        !           526:                if(errno != EAGAIN &&
        !           527:                   errno != EWOULDBLOCK &&
        !           528:                   errno != EINTR)
        !           529:                {
        !           530:                        syslog(LOG_ERR, "recvfrom(udp): %m");
        !           531:                }
1.1       misho     532:                return;
                    533:        }
1.1.1.2   misho     534:        ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port);
1.1       misho     535: 
                    536: }
                    537: 
1.1.1.2   misho     538: void
                    539: ProcessSSDPData(int s, const char *bufr, int n,
                    540:                 const struct sockaddr * sender, unsigned short port) {
1.1       misho     541:        int i, l;
1.1.1.2   misho     542:        struct lan_addr_s * lan_addr = NULL;
                    543:        const char * st = NULL;
1.1       misho     544:        int st_len = 0;
1.1.1.3 ! misho     545:        int st_ver = 0;
1.1.1.2   misho     546:        char sender_str[64];
1.1.1.3 ! misho     547:        char ver_str[4];
1.1.1.2   misho     548:        const char * announced_host = NULL;
1.1.1.3 ! misho     549: #ifdef ENABLE_IPV6
        !           550: #ifdef UPNP_STRICT
        !           551:        char announced_host_buf[64];
        !           552: #endif
        !           553: #endif
1.1       misho     554: 
1.1.1.2   misho     555:        /* get the string representation of the sender address */
                    556:        sockaddr_to_string(sender, sender_str, sizeof(sender_str));
1.1.1.3 ! misho     557:        lan_addr = get_lan_for_peer(sender);
        !           558:        if(lan_addr == NULL)
        !           559:        {
        !           560:                syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring",
        !           561:                       sender_str);
        !           562:                return;
        !           563:        }
1.1       misho     564: 
                    565:        if(memcmp(bufr, "NOTIFY", 6) == 0)
                    566:        {
                    567:                /* ignore NOTIFY packets. We could log the sender and device type */
                    568:                return;
                    569:        }
                    570:        else if(memcmp(bufr, "M-SEARCH", 8) == 0)
                    571:        {
                    572:                i = 0;
                    573:                while(i < n)
                    574:                {
                    575:                        while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
                    576:                                i++;
                    577:                        i += 2;
                    578:                        if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
                    579:                        {
                    580:                                st = bufr+i+3;
                    581:                                st_len = 0;
                    582:                                while((*st == ' ' || *st == '\t') && (st < bufr + n))
                    583:                                        st++;
                    584:                                while(st[st_len]!='\r' && st[st_len]!='\n'
                    585:                                     && (st + st_len < bufr + n))
                    586:                                        st_len++;
1.1.1.3 ! misho     587:                                l = st_len;
        !           588:                                while(l > 0 && st[l-1] != ':')
        !           589:                                        l--;
        !           590:                                st_ver = atoi(st+l);
        !           591:                                syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver);
1.1       misho     592:                                /*j = 0;*/
                    593:                                /*while(bufr[i+j]!='\r') j++;*/
                    594:                                /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
                    595:                        }
                    596:                }
1.1.1.2   misho     597:                /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
                    598:                   sender_str );*/
1.1       misho     599:                if(st && (st_len > 0))
                    600:                {
                    601:                        /* TODO : doesnt answer at once but wait for a random time */
1.1.1.2   misho     602:                        syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s",
                    603:                               sender_str, st_len, st);
1.1       misho     604:                        /* find in which sub network the client is */
1.1.1.2   misho     605:                        if(sender->sa_family == AF_INET)
1.1       misho     606:                        {
1.1.1.2   misho     607:                                if (lan_addr == NULL)
                    608:                                {
                    609:                                        syslog(LOG_ERR, "Can't find in which sub network the client is");
                    610:                                        return;
1.1       misho     611:                                }
1.1.1.2   misho     612:                                announced_host = lan_addr->str;
                    613:                        }
                    614: #ifdef ENABLE_IPV6
                    615:                        else
                    616:                        {
                    617:                                /* IPv6 address with brackets */
1.1.1.3 ! misho     618: #ifdef UPNP_STRICT
        !           619:                                int index;
        !           620:                                struct in6_addr addr6;
        !           621:                                size_t addr6_len = sizeof(addr6);
        !           622:                                /* retrieve the IPv6 address which
        !           623:                                 * will be used locally to reach sender */
        !           624:                                memset(&addr6, 0, sizeof(addr6));
        !           625:                                if(get_src_for_route_to (sender, &addr6, &addr6_len, &index) < 0) {
        !           626:                                        syslog(LOG_WARNING, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets);
        !           627:                                        announced_host = ipv6_addr_for_http_with_brackets;
        !           628:                                } else {
        !           629:                                        if(inet_ntop(AF_INET6, &addr6,
        !           630:                                                     announced_host_buf+1,
        !           631:                                                     sizeof(announced_host_buf) - 2)) {
        !           632:                                                announced_host_buf[0] = '[';
        !           633:                                                i = strlen(announced_host_buf);
        !           634:                                                if(i < (int)sizeof(announced_host_buf) - 1) {
        !           635:                                                        announced_host_buf[i] = ']';
        !           636:                                                        announced_host_buf[i+1] = '\0';
        !           637:                                                } else {
        !           638:                                                        syslog(LOG_NOTICE, "cannot suffix %s with ']'",
        !           639:                                                               announced_host_buf);
        !           640:                                                }
        !           641:                                                announced_host = announced_host_buf;
        !           642:                                        } else {
        !           643:                                                syslog(LOG_NOTICE, "inet_ntop() failed %m");
        !           644:                                                announced_host = ipv6_addr_for_http_with_brackets;
        !           645:                                        }
        !           646:                                }
        !           647: #else
1.1.1.2   misho     648:                                announced_host = ipv6_addr_for_http_with_brackets;
1.1.1.3 ! misho     649: #endif
1.1       misho     650:                        }
1.1.1.2   misho     651: #endif
1.1       misho     652:                        /* Responds to request with a device as ST header */
1.1.1.3 ! misho     653:                        for(i = 0; known_service_types[i].s; i++)
1.1       misho     654:                        {
1.1.1.3 ! misho     655:                                l = (int)strlen(known_service_types[i].s);
        !           656:                                if(l<=st_len && (0 == memcmp(st, known_service_types[i].s, l))
        !           657: #ifdef UPNP_STRICT
        !           658:                                   && (st_ver <= known_service_types[i].version)
        !           659:                /* only answer for service version lower or equal of supported one */
        !           660: #endif
        !           661:                                   )
1.1       misho     662:                                {
                    663:                                        syslog(LOG_INFO, "Single search found");
1.1.1.2   misho     664:                                        SendSSDPAnnounce2(s, sender,
1.1       misho     665:                                                          st, st_len, "",
1.1.1.2   misho     666:                                                          announced_host, port);
1.1       misho     667:                                        break;
                    668:                                }
                    669:                        }
                    670:                        /* Responds to request with ST: ssdp:all */
                    671:                        /* strlen("ssdp:all") == 8 */
                    672:                        if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
                    673:                        {
                    674:                                syslog(LOG_INFO, "ssdp:all found");
1.1.1.3 ! misho     675:                                for(i=0; known_service_types[i].s; i++)
1.1       misho     676:                                {
1.1.1.3 ! misho     677:                                        if(i==0)
        !           678:                                                ver_str[0] = '\0';
        !           679:                                        else
        !           680:                                                snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
        !           681:                                        l = (int)strlen(known_service_types[i].s);
1.1.1.2   misho     682:                                        SendSSDPAnnounce2(s, sender,
1.1.1.3 ! misho     683:                                                          known_service_types[i].s, l, ver_str,
1.1.1.2   misho     684:                                                          announced_host, port);
1.1       misho     685:                                }
1.1.1.3 ! misho     686:                                /* also answer for uuid */
        !           687:                                SendSSDPAnnounce2(s, sender, uuidvalue, strlen(uuidvalue), "",
        !           688:                                                  announced_host, port);
1.1       misho     689:                        }
                    690:                        /* responds to request by UUID value */
                    691:                        l = (int)strlen(uuidvalue);
                    692:                        if(l==st_len && (0 == memcmp(st, uuidvalue, l)))
                    693:                        {
                    694:                                syslog(LOG_INFO, "ssdp:uuid found");
1.1.1.2   misho     695:                                SendSSDPAnnounce2(s, sender, st, st_len, "",
                    696:                                                  announced_host, port);
1.1       misho     697:                        }
                    698:                }
                    699:                else
                    700:                {
1.1.1.2   misho     701:                        syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
1.1       misho     702:                }
                    703:        }
                    704:        else
                    705:        {
1.1.1.2   misho     706:                syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
1.1       misho     707:        }
                    708: }
                    709: 
1.1.1.3 ! misho     710: /* This will broadcast ssdp:byebye notifications to inform
1.1       misho     711:  * the network that UPnP is going down. */
                    712: int
                    713: SendSSDPGoodbye(int * sockets, int n_sockets)
                    714: {
                    715:        struct sockaddr_in sockname;
1.1.1.3 ! misho     716: #ifdef ENABLE_IPV6
        !           717:        struct sockaddr_in6 sockname6;
        !           718: #endif
1.1       misho     719:        int n, l;
                    720:        int i, j;
                    721:        char bufr[512];
1.1.1.3 ! misho     722:        char ver_str[4];
        !           723:        int ret = 0;
        !           724:        int ipv6 = 0;
1.1       misho     725: 
                    726:     memset(&sockname, 0, sizeof(struct sockaddr_in));
                    727:     sockname.sin_family = AF_INET;
                    728:     sockname.sin_port = htons(SSDP_PORT);
                    729:     sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
1.1.1.3 ! misho     730: #ifdef ENABLE_IPV6
        !           731:        memset(&sockname6, 0, sizeof(struct sockaddr_in6));
        !           732:        sockname6.sin6_family = AF_INET6;
        !           733:        sockname6.sin6_port = htons(SSDP_PORT);
        !           734:        inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(sockname6.sin6_addr));
        !           735: #endif
1.1       misho     736: 
                    737:        for(j=0; j<n_sockets; j++)
                    738:        {
1.1.1.3 ! misho     739: #ifdef ENABLE_IPV6
        !           740:                ipv6 = j & 1;
        !           741: #endif
        !           742:            for(i=0; known_service_types[i].s; i++)
1.1       misho     743:            {
1.1.1.3 ! misho     744:                        if(i==0)
        !           745:                                ver_str[0] = '\0';
        !           746:                        else
        !           747:                                snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
1.1       misho     748:                l = snprintf(bufr, sizeof(bufr),
1.1.1.2   misho     749:                  "NOTIFY * HTTP/1.1\r\n"
                    750:                  "HOST: %s:%d\r\n"
                    751:                  "NT: %s%s\r\n"
                    752:                  "USN: %s::%s%s\r\n"
                    753:                  "NTS: ssdp:byebye\r\n"
1.1.1.3 ! misho     754:                                 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
1.1.1.2   misho     755:                                 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
                    756:                                 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
                    757:                                 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
                    758:                  "\r\n",
1.1.1.3 ! misho     759:                  ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
        !           760:                             SSDP_PORT,
        !           761:                                 known_service_types[i].s, ver_str,
        !           762:                  uuidvalue, known_service_types[i].s, ver_str,
1.1.1.2   misho     763:                  upnp_bootid, upnp_bootid, upnp_configid);
1.1       misho     764:                n = sendto(sockets[j], bufr, l, 0,
1.1.1.3 ! misho     765: #ifdef ENABLE_IPV6
        !           766:                           ipv6 ? (struct sockaddr *)&sockname6 : (struct sockaddr *)&sockname,
        !           767:                           ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
        !           768: #else
        !           769:                           (struct sockaddr *)&sockname,
        !           770:                           sizeof(struct sockaddr_in)
        !           771: #endif
        !           772:                         );
1.1       misho     773:                        if(n < 0)
                    774:                        {
                    775:                                syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m",
                    776:                                       sockets[j]);
1.1.1.3 ! misho     777:                                ret = -1;
1.1       misho     778:                        }
                    779:        }
                    780:        }
1.1.1.3 ! misho     781:        return ret;
1.1       misho     782: }
                    783: 
                    784: /* SubmitServicesToMiniSSDPD() :
                    785:  * register services offered by MiniUPnPd to a running instance of
                    786:  * MiniSSDPd */
                    787: int
                    788: SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
                    789:        struct sockaddr_un addr;
                    790:        int s;
                    791:        unsigned char buffer[2048];
                    792:        char strbuf[256];
                    793:        unsigned char * p;
1.1.1.3 ! misho     794:        int i, l, n;
        !           795:        char ver_str[4];
1.1       misho     796: 
                    797:        s = socket(AF_UNIX, SOCK_STREAM, 0);
                    798:        if(s < 0) {
                    799:                syslog(LOG_ERR, "socket(unix): %m");
                    800:                return -1;
                    801:        }
                    802:        addr.sun_family = AF_UNIX;
                    803:        strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
                    804:        if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
                    805:                syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
1.1.1.3 ! misho     806:                close(s);
1.1       misho     807:                return -1;
                    808:        }
1.1.1.3 ! misho     809:        for(i = 0; known_service_types[i].s; i++) {
        !           810:                buffer[0] = 4;  /* request type 4 : submit service */
        !           811:                /* 4 strings following : ST (service type), USN, Server, Location */
1.1       misho     812:                p = buffer + 1;
1.1.1.3 ! misho     813:                l = (int)strlen(known_service_types[i].s);
1.1       misho     814:                if(i > 0)
                    815:                        l++;
                    816:                CODELENGTH(l, p);
1.1.1.3 ! misho     817:                memcpy(p, known_service_types[i].s, l);
1.1       misho     818:                if(i > 0)
                    819:                        p[l-1] = '1';
                    820:                p += l;
1.1.1.3 ! misho     821:                if(i==0)
        !           822:                        ver_str[0] = '\0';
        !           823:                else
        !           824:                        snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
        !           825:                l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
        !           826:                             uuidvalue, known_service_types[i].s, ver_str);
1.1       misho     827:                CODELENGTH(l, p);
                    828:                memcpy(p, strbuf, l);
                    829:                p += l;
                    830:                l = (int)strlen(MINIUPNPD_SERVER_STRING);
                    831:                CODELENGTH(l, p);
                    832:                memcpy(p, MINIUPNPD_SERVER_STRING, l);
                    833:                p += l;
                    834:                l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
                    835:                             host, (unsigned int)port);
                    836:                CODELENGTH(l, p);
                    837:                memcpy(p, strbuf, l);
                    838:                p += l;
1.1.1.3 ! misho     839:                /* now write the encoded data */
        !           840:                n = p - buffer; /* bytes to send */
        !           841:                p = buffer;     /* start */
        !           842:                while(n > 0) {
        !           843:                        l = write(s, p, n);
        !           844:                        if (l < 0) {
        !           845:                                syslog(LOG_ERR, "write(): %m");
        !           846:                                close(s);
        !           847:                                return -1;
        !           848:                        } else if (l == 0) {
        !           849:                                syslog(LOG_ERR, "write() returned 0");
        !           850:                                close(s);
        !           851:                                return -1;
        !           852:                        }
        !           853:                        p += l;
        !           854:                        n -= l;
1.1       misho     855:                }
                    856:        }
                    857:        close(s);
                    858:        return 0;
                    859: }
                    860: 

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