Path: blob/master/thirdparty/miniupnpc/src/minissdpc.c
9904 views
/* $Id: minissdpc.c,v 1.54 2025/03/29 17:59:01 nanard Exp $ */1/* vim: tabstop=4 shiftwidth=4 noexpandtab2* Project : miniupnp3* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/4* Author : Thomas BERNARD5* copyright (c) 2005-2025 Thomas Bernard6* This software is subjet to the conditions detailed in the7* provided LICENCE file. */8#include <stdio.h>9#include <string.h>10#include <stdlib.h>11#include <time.h>12#include <sys/types.h>13#if defined (__NetBSD__)14#include <net/if.h>15#endif16#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)17#ifdef _WIN3218#define WIN32_LEAN_AND_MEAN19#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) */27typedef unsigned short uint16_t;28#endif /* !defined(_MSC_VER) */29#ifndef strncasecmp30#if defined(_MSC_VER) && (_MSC_VER >= 1400)31#define strncasecmp _memicmp32#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */33#define strncasecmp memicmp34#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_APP38#define in6addr_any in6addr_any_init39static const IN6_ADDR in6addr_any_init = {0};40#endif41#endif42#if !defined(_WIN32_WINNT_VISTA)43#define _WIN32_WINNT_VISTA 0x060044#endif45#endif /* _WIN32 */46#if defined(__amigaos__) || defined(__amigaos4__)47#include <sys/socket.h>48#endif /* defined(__amigaos__) || defined(__amigaos4__) */49#if defined(__amigaos__)50#define uint16_t unsigned short51#endif /* defined(__amigaos__) */52/* Hack */53#define UNIX_PATH_LEN 10854struct sockaddr_un {55uint16_t sun_family;56char sun_path[UNIX_PATH_LEN];57};58#else /* defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) */59#include <strings.h>60#include <unistd.h>61#include <sys/socket.h>62#include <sys/param.h>63#include <sys/time.h>64#include <sys/un.h>65#include <netinet/in.h>66#include <arpa/inet.h>67#include <netdb.h>68#include <net/if.h>69#define closesocket close70#endif7172#include "miniupnpc_socketdef.h"7374#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__) && !defined(__HAIKU__)75#define HAS_IP_MREQN76#endif7778#ifndef _WIN3279#include <sys/ioctl.h>80#if defined(__sun) || defined(__HAIKU__)81#include <sys/sockio.h>82#endif83#endif8485#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)86/* Several versions of glibc don't define this structure,87* define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */88struct ip_mreqn89{90struct in_addr imr_multiaddr; /* IP multicast address of group */91struct in_addr imr_address; /* local IP address of interface */92int imr_ifindex; /* Interface index */93};94#endif9596#if defined(__amigaos__) || defined(__amigaos4__)97/* Amiga OS specific stuff */98#define TIMEVAL struct timeval99#endif100101#include "minissdpc.h"102#include "miniupnpc.h"103#include "receivedata.h"104105#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))106107#include "codelength.h"108109struct UPNPDev *110getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error)111{112struct UPNPDev * devlist = NULL;113int s;114int res;115116s = connectToMiniSSDPD(socketpath);117if (s < 0) {118if (error)119*error = s;120return NULL;121}122res = requestDevicesFromMiniSSDPD(s, devtype);123if (res < 0) {124if (error)125*error = res;126} else {127devlist = receiveDevicesFromMiniSSDPD(s, error);128}129disconnectFromMiniSSDPD(s);130return devlist;131}132133/* macros used to read from unix socket */134#define READ_BYTE_BUFFER(c) \135if((int)bufferindex >= n) { \136n = read(s, buffer, sizeof(buffer)); \137if(n<=0) break; \138bufferindex = 0; \139} \140c = buffer[bufferindex++];141142#ifndef MIN143#define MIN(a, b) (((a) < (b)) ? (a) : (b))144#endif /* MIN */145146#define READ_COPY_BUFFER(dst, len) \147for(l = len, p = (unsigned char *)dst; l > 0; ) { \148unsigned int lcopy; \149if((int)bufferindex >= n) { \150n = read(s, buffer, sizeof(buffer)); \151if(n<=0) break; \152bufferindex = 0; \153} \154lcopy = MIN(l, (n - bufferindex)); \155memcpy(p, buffer + bufferindex, lcopy); \156l -= lcopy; \157p += lcopy; \158bufferindex += lcopy; \159}160161#define READ_DISCARD_BUFFER(len) \162for(l = len; l > 0; ) { \163unsigned int lcopy; \164if(bufferindex >= n) { \165n = read(s, buffer, sizeof(buffer)); \166if(n<=0) break; \167bufferindex = 0; \168} \169lcopy = MIN(l, (n - bufferindex)); \170l -= lcopy; \171bufferindex += lcopy; \172}173174int175connectToMiniSSDPD(const char * socketpath)176{177int s;178struct sockaddr_un addr;179#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)180struct timeval timeout;181#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */182183s = socket(AF_UNIX, SOCK_STREAM, 0);184if(s < 0)185{186/*syslog(LOG_ERR, "socket(unix): %m");*/187perror("socket(unix)");188return MINISSDPC_SOCKET_ERROR;189}190#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)191/* setting a 3 seconds timeout */192/* not supported for AF_UNIX sockets under Solaris */193timeout.tv_sec = 3;194timeout.tv_usec = 0;195if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)196{197perror("setsockopt SO_RCVTIMEO unix");198}199timeout.tv_sec = 3;200timeout.tv_usec = 0;201if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)202{203perror("setsockopt SO_SNDTIMEO unix");204}205#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */206if(!socketpath)207socketpath = "/var/run/minissdpd.sock";208memset(&addr, 0, sizeof(addr));209addr.sun_family = AF_UNIX;210strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));211/* TODO : check if we need to handle the EINTR */212if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)213{214/*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/215close(s);216return MINISSDPC_SOCKET_ERROR;217}218return s;219}220221int222disconnectFromMiniSSDPD(int s)223{224if (close(s) < 0)225return MINISSDPC_SOCKET_ERROR;226return MINISSDPC_SUCCESS;227}228229int230requestDevicesFromMiniSSDPD(int s, const char * devtype)231{232unsigned char buffer[256];233unsigned char * p;234unsigned int stsize, l;235236stsize = strlen(devtype);237if(stsize == 8 && 0 == memcmp(devtype, "ssdp:all", 8))238{239buffer[0] = 3; /* request type 3 : everything */240}241else242{243buffer[0] = 1; /* request type 1 : request devices/services by type */244}245p = buffer + 1;246l = stsize; CODELENGTH(l, p);247if(p + stsize > buffer + sizeof(buffer))248{249/* devtype is too long ! */250#ifdef DEBUG251fprintf(stderr, "devtype is too long ! stsize=%u sizeof(buffer)=%u\n",252stsize, (unsigned)sizeof(buffer));253#endif /* DEBUG */254return MINISSDPC_INVALID_INPUT;255}256memcpy(p, devtype, stsize);257p += stsize;258if(write(s, buffer, p - buffer) < 0)259{260/*syslog(LOG_ERR, "write(): %m");*/261perror("minissdpc.c: write()");262return MINISSDPC_SOCKET_ERROR;263}264return MINISSDPC_SUCCESS;265}266267struct UPNPDev *268receiveDevicesFromMiniSSDPD(int s, int * error)269{270struct UPNPDev * tmp;271struct UPNPDev * devlist = NULL;272unsigned char buffer[256];273ssize_t n;274unsigned char * p;275unsigned char * url;276unsigned char * st;277unsigned int bufferindex;278unsigned int i, ndev;279unsigned int urlsize, stsize, usnsize, l;280281n = read(s, buffer, sizeof(buffer));282if(n<=0)283{284perror("minissdpc.c: read()");285if (error)286*error = MINISSDPC_SOCKET_ERROR;287return NULL;288}289ndev = buffer[0];290bufferindex = 1;291for(i = 0; i < ndev; i++)292{293DECODELENGTH_READ(urlsize, READ_BYTE_BUFFER);294if(n<=0) {295if (error)296*error = MINISSDPC_INVALID_SERVER_REPLY;297return devlist;298}299#ifdef DEBUG300printf(" urlsize=%u", urlsize);301#endif /* DEBUG */302url = malloc(urlsize);303if(url == NULL) {304if (error)305*error = MINISSDPC_MEMORY_ERROR;306return devlist;307}308READ_COPY_BUFFER(url, urlsize);309if(n<=0) {310if (error)311*error = MINISSDPC_INVALID_SERVER_REPLY;312goto free_url_and_return;313}314DECODELENGTH_READ(stsize, READ_BYTE_BUFFER);315if(n<=0) {316if (error)317*error = MINISSDPC_INVALID_SERVER_REPLY;318goto free_url_and_return;319}320#ifdef DEBUG321printf(" stsize=%u", stsize);322#endif /* DEBUG */323st = malloc(stsize);324if (st == NULL) {325if (error)326*error = MINISSDPC_MEMORY_ERROR;327goto free_url_and_return;328}329READ_COPY_BUFFER(st, stsize);330if(n<=0) {331if (error)332*error = MINISSDPC_INVALID_SERVER_REPLY;333goto free_url_and_st_and_return;334}335DECODELENGTH_READ(usnsize, READ_BYTE_BUFFER);336if(n<=0) {337if (error)338*error = MINISSDPC_INVALID_SERVER_REPLY;339goto free_url_and_st_and_return;340}341#ifdef DEBUG342printf(" usnsize=%u\n", usnsize);343#endif /* DEBUG */344tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3);345if(tmp == NULL) {346if (error)347*error = MINISSDPC_MEMORY_ERROR;348goto free_url_and_st_and_return;349}350tmp->pNext = devlist;351tmp->descURL = tmp->buffer;352tmp->st = tmp->buffer + 1 + urlsize;353memcpy(tmp->buffer, url, urlsize);354tmp->buffer[urlsize] = '\0';355memcpy(tmp->st, st, stsize);356tmp->buffer[urlsize+1+stsize] = '\0';357free(url);358free(st);359url = NULL;360st = NULL;361tmp->usn = tmp->buffer + 1 + urlsize + 1 + stsize;362READ_COPY_BUFFER(tmp->usn, usnsize);363if(n<=0) {364if (error)365*error = MINISSDPC_INVALID_SERVER_REPLY;366goto free_tmp_and_return;367}368tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';369tmp->scope_id = 0; /* default value. scope_id is not available with MiniSSDPd */370devlist = tmp;371}372if (error)373*error = MINISSDPC_SUCCESS;374return devlist;375376free_url_and_st_and_return:377free(st);378free_url_and_return:379free(url);380return devlist;381382free_tmp_and_return:383free(tmp);384return devlist;385}386387#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */388389/* parseMSEARCHReply()390* the last 4 arguments are filled during the parsing :391* - location/locationsize : "location:" field of the SSDP reply packet392* - st/stsize : "st:" field of the SSDP reply packet.393* - usn/usnsize : "usn:" field of the SSDP reply packet394* The strings are NOT null terminated */395static void396parseMSEARCHReply(const char * reply, int size,397const char * * location, int * locationsize,398const char * * st, int * stsize,399const char * * usn, int * usnsize)400{401int a, b, i;402i = 0;403a = i; /* start of the line */404b = 0; /* end of the "header" (position of the colon) */405while(i<size)406{407switch(reply[i])408{409case ':':410if(b==0)411{412b = i; /* end of the "header" */413/*for(j=a; j<b; j++)414{415putchar(reply[j]);416}417*/418}419break;420case '\x0a':421case '\x0d':422if(b!=0)423{424/*for(j=b+1; j<i; j++)425{426putchar(reply[j]);427}428putchar('\n');*/429/* skip the colon and white spaces */430do { b++; } while(reply[b]==' ');431if(0==strncasecmp(reply+a, "location:", 9))432{433*location = reply+b;434*locationsize = i-b;435}436else if(0==strncasecmp(reply+a, "st:", 3))437{438*st = reply+b;439*stsize = i-b;440}441else if(0==strncasecmp(reply+a, "usn:", 4))442{443*usn = reply+b;444*usnsize = i-b;445}446b = 0;447}448a = i+1;449break;450default:451break;452}453i++;454}455}456457#if defined(CLOCK_MONOTONIC_FAST)458#define UPNP_CLOCKID CLOCK_MONOTONIC_FAST459#elif defined(CLOCK_MONOTONIC)460#define UPNP_CLOCKID CLOCK_MONOTONIC461#endif462463static int upnp_gettimeofday(struct timeval * tv)464{465#if defined(_WIN32)466#if _WIN32_WINNT >= _WIN32_WINNT_VISTA467ULONGLONG ts = GetTickCount64();468#else469DWORD ts = GetTickCount();470#endif471tv->tv_sec = (long)(ts / 1000);472tv->tv_usec = (ts % 1000) * 1000;473return 0; /* success */474#elif defined(CLOCK_MONOTONIC_FAST) || defined(CLOCK_MONOTONIC)475#if defined(__APPLE__)476#if defined(__clang__)477if (__builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {478#else /* !defined(__clang__) */479if (clock_gettime != NULL) {480#endif /* defined(__clang__) */481#endif /* defined(__APPLE__) */482struct timespec ts;483int ret_code = clock_gettime(UPNP_CLOCKID, &ts);484if (ret_code == 0)485{486tv->tv_sec = ts.tv_sec;487tv->tv_usec = ts.tv_nsec / 1000;488}489return ret_code;490#if defined(__APPLE__)491}492else493{494/* fall-back for earlier Apple platforms */495return gettimeofday(tv, NULL);496}497#endif /* defined(__APPLE__) */498#else499return gettimeofday(tv, NULL);500#endif501}502/* port upnp discover : SSDP protocol */503#define SSDP_PORT 1900504#define XSTR(s) STR(s)505#define STR(s) #s506#define UPNP_MCAST_ADDR "239.255.255.250"507/* for IPv6 */508#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */509#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */510511/* direct discovery if minissdpd responses are not sufficient */512/* ssdpDiscoverDevices() :513* return a chained list of all devices found or NULL if514* no devices was found.515* It is up to the caller to free the chained list516* delay is in millisecond (poll).517* UDA v1.1 says :518* The TTL for the IP packet SHOULD default to 2 and519* SHOULD be configurable. */520struct UPNPDev *521ssdpDiscoverDevices(const char * const deviceTypes[],522int delay, const char * multicastif,523int localport,524int ipv6, unsigned char ttl,525int * error,526int searchalltypes)527{528struct UPNPDev * tmp;529struct UPNPDev * devlist = NULL;530unsigned int scope_id = 0;531int opt = 1;532static const char MSearchMsgFmt[] =533"M-SEARCH * HTTP/1.1\r\n"534"HOST: %s:" XSTR(SSDP_PORT) "\r\n"535"ST: %s\r\n"536"MAN: \"ssdp:discover\"\r\n"537"MX: %u\r\n"538"\r\n";539int deviceIndex;540char bufr[1536]; /* reception and emission buffer */541SOCKET sudp;542int n;543struct sockaddr_storage sockudp_r;544unsigned int mx;545int rv;546#ifdef NO_GETADDRINFO547struct sockaddr_storage sockudp_w;548#else549struct addrinfo hints, *servinfo;550#endif551#ifdef _WIN32552unsigned long _ttl = (unsigned long)ttl;553#endif554int linklocal = 0; /* try first with site-local multicast */555int sentok;556557if(error)558*error = MINISSDPC_UNKNOWN_ERROR;559560if(localport==UPNP_LOCAL_PORT_SAME)561localport = SSDP_PORT;562563#ifdef _WIN32564sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);565#else566sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);567#endif568if(ISINVALID(sudp))569{570if(error)571*error = MINISSDPC_SOCKET_ERROR;572PRINT_SOCKET_ERROR("socket");573return NULL;574}575/* reception */576memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));577if(ipv6) {578struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;579p->sin6_family = AF_INET6;580if(localport > 0 && localport < 65536)581p->sin6_port = htons((unsigned short)localport);582p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */583} else {584struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;585p->sin_family = AF_INET;586if(localport > 0 && localport < 65536)587p->sin_port = htons((unsigned short)localport);588p->sin_addr.s_addr = INADDR_ANY;589}590#ifdef _WIN32591/* This code could help us to use the right Network interface for592* SSDP multicast traffic */593/* Get IP associated with the index given in the ip_forward struct594* in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */595if(!ipv6) {596DWORD ifbestidx;597#if _WIN32_WINNT >= _WIN32_WINNT_VISTA598// While we don't need IPv6 support, the IPv4 only function is not available in UWP apps.599SOCKADDR_IN destAddr;600memset(&destAddr, 0, sizeof(destAddr));601destAddr.sin_family = AF_INET;602destAddr.sin_addr.s_addr = inet_addr("223.255.255.255");603destAddr.sin_port = 0;604if (GetBestInterfaceEx((struct sockaddr *)&destAddr, &ifbestidx) == NO_ERROR) {605#else606if (GetBestInterface(inet_addr("223.255.255.255"), &ifbestidx) == NO_ERROR) {607#endif608DWORD dwRetVal = NO_ERROR;609PIP_ADAPTER_ADDRESSES pAddresses = NULL;610ULONG outBufLen = 15360;611int Iterations;612PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;613PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;614615for (Iterations = 0; Iterations < 3; Iterations++) {616pAddresses = (IP_ADAPTER_ADDRESSES *) HeapAlloc(GetProcessHeap(), 0, outBufLen);617if (pAddresses == NULL) {618break;619}620621dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen);622623if (dwRetVal != ERROR_BUFFER_OVERFLOW) {624break;625}626HeapFree(GetProcessHeap(), 0, pAddresses);627pAddresses = NULL;628}629630if (dwRetVal == NO_ERROR) {631pCurrAddresses = pAddresses;632while (pCurrAddresses) {633#ifdef DEBUG634int i;635PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;636PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;637638printf("\tIfIndex (IPv4 interface): %u\n", pCurrAddresses->IfIndex);639printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName);640pUnicast = pCurrAddresses->FirstUnicastAddress;641if (pUnicast != NULL) {642for (i = 0; pUnicast != NULL; i++) {643printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pUnicast->Address.lpSockaddr)->sin_addr) );644pUnicast = pUnicast->Next;645}646printf("\tNumber of Unicast Addresses: %d\n", i);647}648pAnycast = pCurrAddresses->FirstAnycastAddress;649if (pAnycast) {650for (i = 0; pAnycast != NULL; i++) {651printf("\tAnycast Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pAnycast->Address.lpSockaddr)->sin_addr) );652pAnycast = pAnycast->Next;653}654printf("\tNumber of Anycast Addresses: %d\n", i);655}656pMulticast = pCurrAddresses->FirstMulticastAddress;657if (pMulticast) {658for (i = 0; pMulticast != NULL; i++) {659printf("\tMulticast Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pMulticast->Address.lpSockaddr)->sin_addr) );660pMulticast = pMulticast->Next;661}662}663printf("\n");664#endif665pUnicast = pCurrAddresses->FirstUnicastAddress;666if (pCurrAddresses->IfIndex == ifbestidx && pUnicast != NULL) {667SOCKADDR_IN *ipv4 = (SOCKADDR_IN *)(pUnicast->Address.lpSockaddr);668/* Set the address of this interface to be used */669struct in_addr mc_if;670memset(&mc_if, 0, sizeof(mc_if));671mc_if.s_addr = ipv4->sin_addr.s_addr;672if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {673PRINT_SOCKET_ERROR("setsockopt");674}675((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = ipv4->sin_addr.s_addr;676#ifndef DEBUG677break;678#endif679}680pCurrAddresses = pCurrAddresses->Next;681}682}683if (pAddresses != NULL) {684HeapFree(GetProcessHeap(), 0, pAddresses);685pAddresses = NULL;686}687}688}689#endif /* _WIN32 */690691#ifdef _WIN32692if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)693#else694if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)695#endif696{697if(error)698*error = MINISSDPC_SOCKET_ERROR;699PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)");700goto error;701}702703if(ipv6) {704#ifdef _WIN32705DWORD mcastHops = ttl;706if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0)707#else /* _WIN32 */708int mcastHops = ttl;709if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0)710#endif /* _WIN32 */711{712PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)");713}714} else {715#ifdef _WIN32716if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)717#else /* _WIN32 */718if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)719#endif /* _WIN32 */720{721/* not a fatal error */722PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");723}724}725726if(multicastif && multicastif[0] != '\0')727{728if(ipv6) {729#if !defined(_WIN32)730/* according to MSDN, if_nametoindex() is supported since731* MS Windows Vista and MS Windows Server 2008.732* http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */733unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */734if(ifindex == 0)735{736if(error)737*error = MINISSDPC_INVALID_INPUT;738fprintf(stderr, "Invalid multicast interface name %s\n", multicastif);739goto error;740}741if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)742{743PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");744}745#else746#ifdef DEBUG747printf("Setting of multicast interface not supported in IPv6 under Windows.\n");748#endif749#endif750} else {751struct in_addr mc_if;752#if defined(_WIN32)753#if _WIN32_WINNT >= _WIN32_WINNT_VISTA754InetPtonA(AF_INET, multicastif, &mc_if);755#else756mc_if.s_addr = inet_addr(multicastif); /* old Windows SDK do not support InetPtoA() */757#endif758#else759/* was : mc_if.s_addr = inet_addr(multicastif); */ /* ex: 192.168.x.x */760if (inet_pton(AF_INET, multicastif, &mc_if.s_addr) <= 0) {761mc_if.s_addr = INADDR_NONE;762}763#endif764if(mc_if.s_addr != INADDR_NONE)765{766((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;767if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)768{769PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");770}771} else {772/* was not an ip address, try with an interface name */773#ifndef _WIN32774#ifdef HAS_IP_MREQN775struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */776#endif777struct ifreq ifr;778int ifrlen = sizeof(ifr);779strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);780ifr.ifr_name[IFNAMSIZ-1] = '\0';781if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)782{783PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");784goto error;785}786mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;787#ifdef HAS_IP_MREQN788memset(&reqn, 0, sizeof(struct ip_mreqn));789reqn.imr_address.s_addr = mc_if.s_addr;790reqn.imr_ifindex = if_nametoindex(multicastif);791if(reqn.imr_ifindex == 0)792{793if(error)794*error = MINISSDPC_INVALID_INPUT;795fprintf(stderr, "Invalid multicast ip address / interface name %s\n", multicastif);796goto error;797}798if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)799{800PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");801}802#else803if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)804{805PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");806}807#endif808#else /* _WIN32 */809#ifdef DEBUG810printf("Setting of multicast interface not supported with interface name.\n");811#endif812#endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */813}814}815}816817/* Before sending the packed, we first "bind" in order to be able818* to receive the response */819if (bind(sudp, (const struct sockaddr *)&sockudp_r,820ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)821{822if(error)823*error = MINISSDPC_SOCKET_ERROR;824PRINT_SOCKET_ERROR("bind");825closesocket(sudp);826return NULL;827}828829if(error)830*error = MINISSDPC_SUCCESS;831/* Calculating maximum response time in seconds */832mx = ((unsigned int)delay) / 1000u;833if(mx == 0) {834mx = 1;835delay = 1000;836}837/* receiving SSDP response packet */838for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {839sentok = 0;840/* sending the SSDP M-SEARCH packet */841n = snprintf(bufr, sizeof(bufr),842MSearchMsgFmt,843ipv6 ?844(linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")845: UPNP_MCAST_ADDR,846deviceTypes[deviceIndex], mx);847if ((unsigned int)n >= sizeof(bufr)) {848if(error)849*error = MINISSDPC_MEMORY_ERROR;850goto error;851}852#ifdef DEBUG853/*printf("Sending %s", bufr);*/854printf("Sending M-SEARCH request to %s with ST: %s\n",855ipv6 ?856(linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")857: UPNP_MCAST_ADDR,858deviceTypes[deviceIndex]);859#endif860#ifdef NO_GETADDRINFO861/* the following code is not using getaddrinfo */862/* emission */863memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));864if(ipv6) {865struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;866p->sin6_family = AF_INET6;867p->sin6_port = htons(SSDP_PORT);868inet_pton(AF_INET6,869linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,870&(p->sin6_addr));871} else {872struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;873p->sin_family = AF_INET;874p->sin_port = htons(SSDP_PORT);875p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);876}877rv = sendto(sudp, bufr, n, 0, (struct sockaddr *)&sockudp_w,878ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));879if (rv < 0) {880if(error)881*error = MINISSDPC_SOCKET_ERROR;882PRINT_SOCKET_ERROR("sendto");883} else {884sentok = 1;885}886#else /* #ifdef NO_GETADDRINFO */887memset(&hints, 0, sizeof(hints));888hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */889hints.ai_socktype = SOCK_DGRAM;890/*hints.ai_flags = */891if ((rv = getaddrinfo(ipv6892? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)893: UPNP_MCAST_ADDR,894XSTR(SSDP_PORT), &hints, &servinfo)) != 0) {895if(error)896*error = MINISSDPC_SOCKET_ERROR;897#ifdef _WIN32898fprintf(stderr, "getaddrinfo() failed: %d\n", rv);899#else900fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));901#endif902break;903} else {904struct addrinfo *p;905/* as getaddrinfo() returns a linked list, we are iterating it906* even thought it should only return one result here */907for(p = servinfo; p; p = p->ai_next) {908rv = sendto(sudp, bufr, n, 0, p->ai_addr, MSC_CAST_INT p->ai_addrlen);909if (rv < 0) {910#ifdef DEBUG911char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];912if (getnameinfo(p->ai_addr, (socklen_t)p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,913sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {914fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);915}916#endif917PRINT_SOCKET_ERROR("sendto");918continue;919} else {920sentok = 1;921}922}923freeaddrinfo(servinfo);924}925if(!sentok) {926if(error)927*error = MINISSDPC_SOCKET_ERROR;928}929#endif /* #ifdef NO_GETADDRINFO */930/* Waiting for SSDP REPLY packet to M-SEARCH931* if searchalltypes is set, enter the loop only932* when the last deviceType is reached */933if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) {934struct timeval start = {0, 0}, current = {0, 0};935upnp_gettimeofday(&start);936do {937n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);938if (n < 0) {939/* error */940if(error)941*error = MINISSDPC_SOCKET_ERROR;942goto error;943} else if (n == 0) {944/* no data or Time Out */945#ifdef DEBUG946printf("NODATA or TIMEOUT\n");947#endif /* DEBUG */948if (devlist && !searchalltypes) {949/* found some devices, stop now*/950if(error)951*error = MINISSDPC_SUCCESS;952goto error;953}954} else {955const char * descURL=NULL;956int urlsize=0;957const char * st=NULL;958int stsize=0;959const char * usn=NULL;960int usnsize=0;961parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize);962if(st&&descURL) {963#ifdef DEBUG964printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n",965stsize, st, usnsize, (usn?usn:""), urlsize, descURL);966#endif /* DEBUG */967for(tmp=devlist; tmp; tmp = tmp->pNext) {968if(strncmp(tmp->descURL, descURL, urlsize) == 0 &&969tmp->descURL[urlsize] == '\0' &&970strncmp(tmp->st, st, stsize) == 0 &&971tmp->st[stsize] == '\0' &&972(usnsize == 0 || strncmp(tmp->usn, usn, usnsize) == 0) &&973tmp->usn[usnsize] == '\0')974break;975}976/* at the exit of the loop above, tmp is null if977* no duplicate device was found */978if(tmp)979continue;980tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3);981if(!tmp) {982/* memory allocation error */983if(error)984*error = MINISSDPC_MEMORY_ERROR;985goto error;986}987tmp->pNext = devlist;988tmp->descURL = tmp->buffer;989tmp->st = tmp->buffer + 1 + urlsize;990tmp->usn = tmp->st + 1 + stsize;991memcpy(tmp->buffer, descURL, urlsize);992tmp->buffer[urlsize] = '\0';993memcpy(tmp->st, st, stsize);994tmp->buffer[urlsize+1+stsize] = '\0';995if(usn != NULL)996memcpy(tmp->usn, usn, usnsize);997tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';998tmp->scope_id = scope_id;999devlist = tmp;1000}1001if (upnp_gettimeofday(¤t) >= 0) {1002/* exit the loop if delay is reached */1003long interval = (current.tv_sec - start.tv_sec) * 1000;1004interval += (current.tv_usec - start.tv_usec) / 1000;1005if (interval > (long)delay)1006break;1007}1008}1009} while(n > 0);1010}1011if(ipv6) {1012/* switch linklocal flag */1013if(linklocal) {1014linklocal = 0;1015} else {1016/* try again with linklocal multicast */1017linklocal = 1;1018--deviceIndex;1019}1020}1021}1022error:1023closesocket(sudp);1024return devlist;1025}102610271028