Files
libupnp/upnp/src/ssdp/ssdp_server.c
2013-10-28 14:01:11 -02:00

1269 lines
38 KiB
C

/**************************************************************************
*
* Copyright (c) 2000-2003 Intel Corporation
* All rights reserved.
* Copyright (C) 2011-2012 France Telecom All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************/
/*!
* \addtogroup SSDPlib
*
* @{
*
* \file
*/
#ifndef WIN32
#include <sys/param.h>
#else
#define snprintf _snprintf
#endif /* WIN32 */
#include "config.h"
#if EXCLUDE_SSDP == 0
#include "ssdplib.h"
#include "httpparser.h"
#include "httpreadwrite.h"
#include "membuffer.h"
#include "miniserver.h"
#include "sock.h"
#include "ThreadPool.h"
#include "upnpapi.h"
#include <stdio.h>
#define MAX_TIME_TOREAD 45
#ifdef INCLUDE_CLIENT_APIS
SOCKET gSsdpReqSocket4 = INVALID_SOCKET;
#ifdef UPNP_ENABLE_IPV6
SOCKET gSsdpReqSocket6 = INVALID_SOCKET;
#endif /* UPNP_ENABLE_IPV6 */
#endif /* INCLUDE_CLIENT_APIS */
void RequestHandler();
enum Listener {
Idle,
Stopping,
Running
};
struct SSDPSockArray {
/*! socket for incoming advertisments and search requests */
SOCKET ssdpSock;
#ifdef INCLUDE_CLIENT_APIS
/*! socket for sending search requests and receiving search replies */
int ssdpReqSock;
#endif /* INCLUDE_CLIENT_APIS */
};
#ifdef INCLUDE_DEVICE_APIS
static const char SERVICELIST_STR[] = "serviceList";
int AdvertiseAndReply(int AdFlag, UpnpDevice_Handle Hnd,
enum SsdpSearchType SearchType,
struct sockaddr *DestAddr, char *DeviceType,
char *DeviceUDN, char *ServiceType, int Exp)
{
int retVal = UPNP_E_SUCCESS;
long unsigned int i;
long unsigned int j;
int defaultExp = DEFAULT_MAXAGE;
struct Handle_Info *SInfo = NULL;
char UDNstr[100];
char devType[100];
char servType[100];
IXML_NodeList *nodeList = NULL;
IXML_NodeList *tmpNodeList = NULL;
IXML_Node *tmpNode = NULL;
IXML_Node *tmpNode2 = NULL;
IXML_Node *textNode = NULL;
const DOMString tmpStr;
const DOMString dbgStr;
int NumCopy = 0;
memset(UDNstr, 0, sizeof(UDNstr));
memset(devType, 0, sizeof(devType));
memset(servType, 0, sizeof(servType));
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Inside AdvertiseAndReply with AdFlag = %d\n", AdFlag);
/* Use a read lock */
HandleReadLock();
if (GetHandleInfo(Hnd, &SInfo) != HND_DEVICE) {
retVal = UPNP_E_INVALID_HANDLE;
goto end_function;
}
defaultExp = SInfo->MaxAge;
/* parse the device list and send advertisements/replies */
while (NumCopy == 0 || (AdFlag && NumCopy < NUM_SSDP_COPY)) {
if (NumCopy != 0)
imillisleep(SSDP_PAUSE);
NumCopy++;
for (i = 0lu;; i++) {
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Entering new device list with i = %lu\n\n",
i);
tmpNode = ixmlNodeList_item(SInfo->DeviceList, i);
if (!tmpNode) {
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Exiting new device list with i = %lu\n\n",
i);
break;
}
dbgStr = ixmlNode_getNodeName(tmpNode);
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"Extracting device type once for %s\n",
dbgStr);
ixmlNodeList_free(nodeList);
nodeList = ixmlElement_getElementsByTagName((IXML_Element *) tmpNode, "deviceType");
if (!nodeList)
continue;
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Extracting UDN for %s\n", dbgStr);
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Extracting device type\n");
tmpNode2 = ixmlNodeList_item(nodeList, 0lu);
if (!tmpNode2)
continue;
textNode = ixmlNode_getFirstChild(tmpNode2);
if (!textNode)
continue;
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Extracting device type \n");
tmpStr = ixmlNode_getNodeValue(textNode);
if (!tmpStr)
continue;
strncpy(devType, tmpStr, sizeof(devType) - 1);
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Extracting device type = %s\n", devType);
if (!tmpNode) {
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"TempNode is NULL\n");
}
dbgStr = ixmlNode_getNodeName(tmpNode);
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Extracting UDN for %s\n", dbgStr);
ixmlNodeList_free(nodeList);
nodeList = ixmlElement_getElementsByTagName((IXML_Element *) tmpNode, "UDN");
if (!nodeList) {
UpnpPrintf(UPNP_CRITICAL, API, __FILE__,
__LINE__, "UDN not found!\n");
continue;
}
tmpNode2 = ixmlNodeList_item(nodeList, 0lu);
if (!tmpNode2) {
UpnpPrintf(UPNP_CRITICAL, API, __FILE__,
__LINE__, "UDN not found!\n");
continue;
}
textNode = ixmlNode_getFirstChild(tmpNode2);
if (!textNode) {
UpnpPrintf(UPNP_CRITICAL, API, __FILE__,
__LINE__, "UDN not found!\n");
continue;
}
tmpStr = ixmlNode_getNodeValue(textNode);
if (!tmpStr) {
UpnpPrintf(UPNP_CRITICAL, API, __FILE__,
__LINE__, "UDN not found!\n");
continue;
}
strncpy(UDNstr, tmpStr, sizeof(UDNstr) - 1);
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"Sending UDNStr = %s \n", UDNstr);
if (AdFlag) {
/* send the device advertisement */
if (AdFlag == 1) {
DeviceAdvertisement(devType, i == 0lu,
UDNstr,
SInfo->DescURL, Exp,
SInfo->DeviceAf,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
} else {
/* AdFlag == -1 */
DeviceShutdown(devType, i == 0lu, UDNstr,
SInfo->DescURL,
Exp, SInfo->DeviceAf,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
}
} else {
switch (SearchType) {
case SSDP_ALL:
DeviceReply(DestAddr, devType, i == 0lu,
UDNstr, SInfo->DescURL,
defaultExp, SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
break;
case SSDP_ROOTDEVICE:
if (i == 0lu) {
SendReply(DestAddr, devType, 1,
UDNstr,
SInfo->DescURL,
defaultExp, 0,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
}
break;
case SSDP_DEVICEUDN: {
if (DeviceUDN && strlen(DeviceUDN) != (size_t)0) {
if (strcasecmp(DeviceUDN, UDNstr)) {
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"DeviceUDN=%s and search UDN=%s DID NOT match\n",
UDNstr, DeviceUDN);
break;
} else {
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"DeviceUDN=%s and search UDN=%s MATCH\n",
UDNstr, DeviceUDN);
SendReply(DestAddr, devType, 0, UDNstr, SInfo->DescURL, defaultExp, 0,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
break;
}
}
}
case SSDP_DEVICETYPE: {
if (!strncasecmp(DeviceType, devType, strlen(DeviceType) - (size_t)2)) {
if (atoi(strrchr(DeviceType, ':') + 1)
< atoi(&devType[strlen(devType) - (size_t)1])) {
/* the requested version is lower than the device version
* must reply with the lower version number and the lower
* description URL */
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"DeviceType=%s and search devType=%s MATCH\n",
devType, DeviceType);
SendReply(DestAddr, DeviceType, 0, UDNstr, SInfo->LowerDescURL,
defaultExp, 1,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
} else if (atoi(strrchr(DeviceType, ':') + 1)
== atoi(&devType[strlen(devType) - (size_t)1])) {
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"DeviceType=%s and search devType=%s MATCH\n",
devType, DeviceType);
SendReply(DestAddr, DeviceType, 0, UDNstr, SInfo->DescURL,
defaultExp, 1,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
} else {
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"DeviceType=%s and search devType=%s DID NOT MATCH\n",
devType, DeviceType);
}
} else {
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"DeviceType=%s and search devType=%s DID NOT MATCH\n",
devType, DeviceType);
}
break;
}
default:
break;
}
}
/* send service advertisements for services corresponding
* to the same device */
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"Sending service Advertisement\n");
/* Correct service traversal such that each device's serviceList
* is directly traversed as a child of its parent device. This
* ensures that the service's alive message uses the UDN of
* the parent device. */
tmpNode = ixmlNode_getFirstChild(tmpNode);
while (tmpNode) {
dbgStr = ixmlNode_getNodeName(tmpNode);
if (!strncmp
(dbgStr, SERVICELIST_STR,
sizeof SERVICELIST_STR)) {
break;
}
tmpNode = ixmlNode_getNextSibling(tmpNode);
}
ixmlNodeList_free(nodeList);
if (!tmpNode) {
nodeList = NULL;
continue;
}
nodeList = ixmlElement_getElementsByTagName((IXML_Element *) tmpNode, "service");
if (!nodeList) {
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"Service not found 3\n");
continue;
}
for (j = 0lu;; j++) {
tmpNode = ixmlNodeList_item(nodeList, j);
if (!tmpNode) {
break;
}
ixmlNodeList_free(tmpNodeList);
tmpNodeList = ixmlElement_getElementsByTagName((IXML_Element *) tmpNode, "serviceType");
if (!tmpNodeList) {
UpnpPrintf(UPNP_CRITICAL, API, __FILE__,
__LINE__,
"ServiceType not found \n");
continue;
}
tmpNode2 = ixmlNodeList_item(tmpNodeList, 0lu);
if (!tmpNode2)
continue;
textNode = ixmlNode_getFirstChild(tmpNode2);
if (!textNode)
continue;
/* servType is of format Servicetype:ServiceVersion */
tmpStr = ixmlNode_getNodeValue(textNode);
if (!tmpStr)
continue;
strncpy(servType, tmpStr, sizeof(servType) - 1);
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"ServiceType = %s\n", servType);
if (AdFlag) {
if (AdFlag == 1) {
ServiceAdvertisement(UDNstr,
servType, SInfo->DescURL,
Exp, SInfo->DeviceAf,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
} else {
/* AdFlag == -1 */
ServiceShutdown(UDNstr,
servType, SInfo->DescURL,
Exp, SInfo->DeviceAf,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
}
} else {
switch (SearchType) {
case SSDP_ALL:
ServiceReply(DestAddr, servType,
UDNstr,
SInfo->DescURL,
defaultExp,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
break;
case SSDP_SERVICE:
if (ServiceType) {
if (!strncasecmp(ServiceType, servType, strlen(ServiceType) - (size_t)2)) {
if (atoi(strrchr(ServiceType, ':') + 1) <
atoi(&servType[strlen(servType) - (size_t)1])) {
/* the requested version is lower than the service version
* must reply with the lower version number and the lower
* description URL */
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"ServiceType=%s and search servType=%s MATCH\n",
ServiceType, servType);
SendReply(DestAddr, ServiceType, 0, UDNstr, SInfo->LowerDescURL,
defaultExp, 1,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
} else if (atoi(strrchr (ServiceType, ':') + 1) ==
atoi(&servType[strlen(servType) - (size_t)1])) {
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"ServiceType=%s and search servType=%s MATCH\n",
ServiceType, servType);
SendReply(DestAddr, ServiceType, 0, UDNstr, SInfo->DescURL,
defaultExp, 1,
SInfo->PowerState,
SInfo->SleepPeriod,
SInfo->RegistrationState);
} else {
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"ServiceType=%s and search servType=%s DID NOT MATCH\n",
ServiceType, servType);
}
} else {
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"ServiceType=%s and search servType=%s DID NOT MATCH\n",
ServiceType, servType);
}
}
break;
default:
break;
}
}
}
ixmlNodeList_free(tmpNodeList);
tmpNodeList = NULL;
ixmlNodeList_free(nodeList);
nodeList = NULL;
}
}
end_function:
ixmlNodeList_free(tmpNodeList);
ixmlNodeList_free(nodeList);
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Exiting AdvertiseAndReply.\n");
HandleUnlock();
return retVal;
}
#endif /* INCLUDE_DEVICE_APIS */
int unique_service_name(char *cmd, SsdpEvent *Evt)
{
char TempBuf[COMMAND_LEN];
char *TempPtr = NULL;
char *Ptr = NULL;
char *ptr1 = NULL;
char *ptr2 = NULL;
char *ptr3 = NULL;
int CommandFound = 0;
size_t n = (size_t)0;
if (strstr(cmd, "uuid:schemas") != NULL) {
ptr1 = strstr(cmd, ":device");
if (ptr1 != NULL)
ptr2 = strstr(ptr1 + 1, ":");
else
return -1;
if (ptr2 != NULL)
ptr3 = strstr(ptr2 + 1, ":");
else
return -1;
if (ptr3 != NULL) {
if (strlen("uuid:") + strlen(ptr3 + 1) >= sizeof Evt->UDN)
return -1;
snprintf(Evt->UDN, sizeof Evt->UDN, "uuid:%s", ptr3 + 1);
}
else
return -1;
ptr1 = strstr(cmd, ":");
if (ptr1 != NULL) {
n = (size_t)ptr3 - (size_t)ptr1;
n = n >= sizeof TempBuf ? sizeof TempBuf - 1 : n;
strncpy(TempBuf, ptr1, n);
TempBuf[n] = '\0';
if (strlen("urn") + strlen(TempBuf) >= sizeof(Evt->DeviceType))
return -1;
snprintf(Evt->DeviceType, sizeof(Evt->DeviceType),
"urn%s", TempBuf);
} else
return -1;
return 0;
}
if ((TempPtr = strstr(cmd, "uuid")) != NULL) {
if ((Ptr = strstr(cmd, "::")) != NULL) {
n = (size_t)Ptr - (size_t)TempPtr;
n = n >= sizeof Evt->UDN ? sizeof Evt->UDN - 1 : n;
strncpy(Evt->UDN, TempPtr, n);
Evt->UDN[n] = '\0';
} else {
memset(Evt->UDN, 0, sizeof(Evt->UDN));
strncpy(Evt->UDN, TempPtr, sizeof Evt->UDN - 1);
}
CommandFound = 1;
}
if (strstr(cmd, "urn:") != NULL && strstr(cmd, ":service:") != NULL) {
if ((TempPtr = strstr(cmd, "urn")) != NULL) {
memset(Evt->ServiceType, 0, sizeof Evt->ServiceType);
strncpy(Evt->ServiceType, TempPtr,
sizeof Evt->ServiceType - 1);
CommandFound = 1;
}
}
if (strstr(cmd, "urn:") != NULL && strstr(cmd, ":device:") != NULL) {
if ((TempPtr = strstr(cmd, "urn")) != NULL) {
memset(Evt->DeviceType, 0, sizeof Evt->DeviceType);
strncpy(Evt->DeviceType, TempPtr,
sizeof Evt->DeviceType - 1);
CommandFound = 1;
}
}
if ((TempPtr = strstr(cmd, "::upnp:rootdevice")) != NULL) {
/* Everything before "::upnp::rootdevice" is the UDN. */
if (TempPtr != cmd) {
n = (size_t)TempPtr - (size_t)cmd;
n = n >= sizeof Evt->UDN ? sizeof Evt->UDN - 1 : n;
strncpy(Evt->UDN, cmd, n);
Evt->UDN[n] = 0;
CommandFound = 1;
}
}
if (CommandFound == 0)
return -1;
return 0;
}
enum SsdpSearchType ssdp_request_type1(char *cmd)
{
if (strstr(cmd, ":all"))
return SSDP_ALL;
if (strstr(cmd, ":rootdevice"))
return SSDP_ROOTDEVICE;
if (strstr(cmd, "uuid:"))
return SSDP_DEVICEUDN;
if (strstr(cmd, "urn:") && strstr(cmd, ":device:"))
return SSDP_DEVICETYPE;
if (strstr(cmd, "urn:") && strstr(cmd, ":service:"))
return SSDP_SERVICE;
return SSDP_SERROR;
}
int ssdp_request_type(char *cmd, SsdpEvent *Evt)
{
/* clear event */
memset(Evt, 0, sizeof(SsdpEvent));
unique_service_name(cmd, Evt);
Evt->ErrCode = NO_ERROR_FOUND;
if ((Evt->RequestType = ssdp_request_type1(cmd)) == SSDP_SERROR) {
Evt->ErrCode = E_HTTP_SYNTEX;
return -1;
}
return 0;
}
/*!
* \brief Frees the ssdp request.
*/
static void free_ssdp_event_handler_data(
/*! [in] ssdp_thread_data structure. This structure contains SSDP
* request message. */
void *the_data)
{
ssdp_thread_data *data = (ssdp_thread_data *) the_data;
if (data != NULL) {
http_message_t *hmsg = &data->parser.msg;
/* free data */
httpmsg_destroy(hmsg);
free(data);
}
}
/*!
* \brief Does some quick checking of the ssdp msg.
*
* \return TRUE if msg is valid, else FALSE.
*/
static UPNP_INLINE int valid_ssdp_msg(
/*! [in] ssdp_thread_data structure. This structure contains SSDP
* request message. */
http_message_t * hmsg)
{
memptr hdr_value;
/* check for valid methods - NOTIFY or M-SEARCH */
if (hmsg->method != (http_method_t)HTTPMETHOD_NOTIFY &&
hmsg->method != (http_method_t)HTTPMETHOD_MSEARCH &&
hmsg->request_method != (http_method_t)HTTPMETHOD_MSEARCH) {
return FALSE;
}
if (hmsg->request_method != (http_method_t)HTTPMETHOD_MSEARCH) {
/* check PATH == "*" */
if (hmsg->uri.type != (enum uriType)RELATIVE ||
strncmp("*", hmsg->uri.pathquery.buff,
hmsg->uri.pathquery.size) != 0) {
return FALSE;
}
/* check HOST header */
if (httpmsg_find_hdr(hmsg, HDR_HOST, &hdr_value) == NULL ||
(memptr_cmp(&hdr_value, "239.255.255.250:1900") != 0 &&
memptr_cmp(&hdr_value, "[FF02::C]:1900") != 0 &&
memptr_cmp(&hdr_value, "[ff02::c]:1900") != 0 &&
memptr_cmp(&hdr_value, "[FF05::C]:1900") != 0 &&
memptr_cmp(&hdr_value, "[ff05::c]:1900") != 0)) {
UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
"Invalid HOST header from SSDP message\n");
return FALSE;
}
}
/* passed quick check */
return TRUE;
}
/*!
* \brief Parses the message and dispatches it to a handler which handles the
* ssdp request msg.
*
* \return 0 if successful, -1 if error.
*/
static UPNP_INLINE int start_event_handler(
/*! [in] ssdp_thread_data structure. This structure contains SSDP
* request message. */
void *Data)
{
http_parser_t *parser = NULL;
parse_status_t status;
ssdp_thread_data *data = (ssdp_thread_data *) Data;
parser = &data->parser;
status = parser_parse(parser);
if (status == (parse_status_t)PARSE_FAILURE) {
if (parser->msg.method != (http_method_t)HTTPMETHOD_NOTIFY ||
!parser->valid_ssdp_notify_hack) {
UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
"SSDP recvd bad msg code = %d\n", status);
/* ignore bad msg, or not enuf mem */
goto error_handler;
}
/* valid notify msg */
} else if (status != (parse_status_t)PARSE_SUCCESS) {
UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
"SSDP recvd bad msg code = %d\n", status);
goto error_handler;
}
/* check msg */
if (valid_ssdp_msg(&parser->msg) != TRUE) {
goto error_handler;
}
/* done; thread will free 'data' */
return 0;
error_handler:
free_ssdp_event_handler_data(data);
return -1;
}
/*!
* \brief This function is a thread that handles SSDP requests.
*/
static void ssdp_event_handler_thread(
/*! [] ssdp_thread_data structure. This structure contains SSDP
* request message. */
void *the_data)
{
ssdp_thread_data *data = (ssdp_thread_data *) the_data;
http_message_t *hmsg = &data->parser.msg;
if (start_event_handler(the_data) != 0)
return;
/* send msg to device or ctrlpt */
if (hmsg->method == (http_method_t)HTTPMETHOD_NOTIFY ||
hmsg->request_method == (http_method_t)HTTPMETHOD_MSEARCH) {
#ifdef INCLUDE_CLIENT_APIS
ssdp_handle_ctrlpt_msg(hmsg,
&data->dest_addr,
FALSE, NULL);
#endif /* INCLUDE_CLIENT_APIS */
} else {
ssdp_handle_device_request(hmsg,
&data->dest_addr);
}
/* free data */
free_ssdp_event_handler_data(data);
}
void readFromSSDPSocket(SOCKET socket)
{
char *requestBuf = NULL;
char staticBuf[BUFSIZE];
struct sockaddr_storage __ss;
ThreadPoolJob job;
ssdp_thread_data *data = NULL;
socklen_t socklen = sizeof(__ss);
ssize_t byteReceived = 0;
char ntop_buf[INET6_ADDRSTRLEN];
memset(&job, 0, sizeof(job));
requestBuf = staticBuf;
/* in case memory can't be allocated, still drain the socket using a
* static buffer. */
data = malloc(sizeof(ssdp_thread_data));
if (data) {
/* initialize parser */
#ifdef INCLUDE_CLIENT_APIS
if (socket == gSsdpReqSocket4
#ifdef UPNP_ENABLE_IPV6
|| socket == gSsdpReqSocket6
#endif /* UPNP_ENABLE_IPV6 */
)
parser_response_init(&data->parser, HTTPMETHOD_MSEARCH);
else
parser_request_init(&data->parser);
#else /* INCLUDE_CLIENT_APIS */
parser_request_init(&data->parser);
#endif /* INCLUDE_CLIENT_APIS */
/* set size of parser buffer */
if (membuffer_set_size(&data->parser.msg.msg, BUFSIZE) == 0)
/* use this as the buffer for recv */
requestBuf = data->parser.msg.msg.buf;
else {
free(data);
data = NULL;
}
}
byteReceived = recvfrom(socket, requestBuf, BUFSIZE - (size_t)1, 0,
(struct sockaddr *)&__ss, &socklen);
if (byteReceived > 0) {
requestBuf[byteReceived] = '\0';
switch (__ss.ss_family) {
case AF_INET:
inet_ntop(AF_INET,
&((struct sockaddr_in *)&__ss)->sin_addr,
ntop_buf, sizeof(ntop_buf));
break;
#ifdef UPNP_ENABLE_IPV6
case AF_INET6:
inet_ntop(AF_INET6,
&((struct sockaddr_in6 *)&__ss)->sin6_addr,
ntop_buf, sizeof(ntop_buf));
break;
#endif /* UPNP_ENABLE_IPV6 */
default:
memset(ntop_buf, 0, sizeof(ntop_buf));
strncpy(ntop_buf, "<Invalid address family>",
sizeof(ntop_buf) - 1);
}
UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
"Start of received response ----------------------------------------------------\n"
"%s\n"
"End of received response ------------------------------------------------------\n"
"From host %s\n", requestBuf, ntop_buf);
/* add thread pool job to handle request */
if (data != NULL) {
data->parser.msg.msg.length += (size_t) byteReceived;
/* null-terminate */
data->parser.msg.msg.buf[byteReceived] = 0;
memcpy(&data->dest_addr, &__ss, sizeof(__ss));
TPJobInit(&job, (start_routine)
ssdp_event_handler_thread, data);
TPJobSetFreeFunction(&job,
free_ssdp_event_handler_data);
TPJobSetPriority(&job, MED_PRIORITY);
if (ThreadPoolAdd(&gRecvThreadPool, &job, NULL) != 0)
free_ssdp_event_handler_data(data);
}
} else
free_ssdp_event_handler_data(data);
}
/*!
* \brief
*/
static int create_ssdp_sock_v4(
/*! [] SSDP IPv4 socket to be created. */
SOCKET *ssdpSock)
{
char errorBuffer[ERROR_BUFFER_LEN];
int onOff;
u_char ttl = (u_char)4;
struct ip_mreq ssdpMcastAddr;
struct sockaddr_storage __ss;
struct sockaddr_in *ssdpAddr4 = (struct sockaddr_in *)&__ss;
int ret = 0;
struct in_addr addr;
*ssdpSock = socket(AF_INET, SOCK_DGRAM, 0);
if (*ssdpSock == INVALID_SOCKET) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in socket(): %s\n", errorBuffer);
return UPNP_E_OUTOF_SOCKET;
}
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEADDR,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() SO_REUSEADDR: %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
#if (defined(BSD) && !defined(__GNU__)) || defined(__OSX__) || defined(__APPLE__)
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() SO_REUSEPORT: %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
#endif /* BSD, __OSX__, __APPLE__ */
memset(&__ss, 0, sizeof(__ss));
ssdpAddr4->sin_family = (sa_family_t)AF_INET;
ssdpAddr4->sin_addr.s_addr = htonl(INADDR_ANY);
ssdpAddr4->sin_port = htons(SSDP_PORT);
ret = bind(*ssdpSock, (struct sockaddr *)ssdpAddr4, sizeof(*ssdpAddr4));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in bind(), addr=0x%08X, port=%d: %s\n",
INADDR_ANY, SSDP_PORT, errorBuffer);
ret = UPNP_E_SOCKET_BIND;
goto error_handler;
}
memset((void *)&ssdpMcastAddr, 0, sizeof(struct ip_mreq));
ssdpMcastAddr.imr_interface.s_addr = inet_addr(gIF_IPV4);
ssdpMcastAddr.imr_multiaddr.s_addr = inet_addr(SSDP_IP);
ret = setsockopt(*ssdpSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&ssdpMcastAddr, sizeof(struct ip_mreq));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() IP_ADD_MEMBERSHIP (join multicast group): %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
/* Set multicast interface. */
memset((void *)&addr, 0, sizeof(struct in_addr));
addr.s_addr = inet_addr(gIF_IPV4);
ret = setsockopt(*ssdpSock, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&addr, sizeof addr);
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
"Error in setsockopt() IP_MULTICAST_IF (set multicast interface): %s\n",
errorBuffer);
/* This is probably not a critical error, so let's continue. */
}
/* result is not checked becuase it will fail in WinMe and Win9x. */
setsockopt(*ssdpSock, IPPROTO_IP,
IP_MULTICAST_TTL, &ttl, sizeof(ttl));
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_BROADCAST,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() SO_BROADCAST (set broadcast): %s\n",
errorBuffer);
ret = UPNP_E_NETWORK_ERROR;
goto error_handler;
}
ret = UPNP_E_SUCCESS;
error_handler:
if (ret != UPNP_E_SUCCESS) {
if (shutdown(*ssdpSock, SD_BOTH) == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
"Error in shutdown: %s\n", errorBuffer);
}
UpnpCloseSocket(*ssdpSock);
}
return ret;
}
#ifdef INCLUDE_CLIENT_APIS
/*!
* \brief Creates the SSDP IPv4 socket to be used by the control point.
*
* \return UPNP_E_SUCCESS on successful socket creation.
*/
static int create_ssdp_sock_reqv4(
/*! [out] SSDP IPv4 request socket to be created. */
SOCKET *ssdpReqSock)
{
char errorBuffer[ERROR_BUFFER_LEN];
u_char ttl = 4;
*ssdpReqSock = socket(AF_INET, SOCK_DGRAM, 0);
if (*ssdpReqSock == INVALID_SOCKET) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in socket(): %s\n", errorBuffer);
return UPNP_E_OUTOF_SOCKET;
}
setsockopt(*ssdpReqSock, IPPROTO_IP, IP_MULTICAST_TTL,
&ttl, sizeof(ttl));
/* just do it, regardless if fails or not. */
sock_make_no_blocking(*ssdpReqSock);
return UPNP_E_SUCCESS;
}
#ifdef UPNP_ENABLE_IPV6
/*!
* \brief This function ...
*/
static int create_ssdp_sock_v6(
/* [] SSDP IPv6 socket to be created. */
SOCKET *ssdpSock)
{
char errorBuffer[ERROR_BUFFER_LEN];
struct ipv6_mreq ssdpMcastAddr;
struct sockaddr_storage __ss;
struct sockaddr_in6 *ssdpAddr6 = (struct sockaddr_in6 *)&__ss;
int onOff;
int ret = 0;
*ssdpSock = socket(AF_INET6, SOCK_DGRAM, 0);
if (*ssdpSock == INVALID_SOCKET) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in socket(): %s\n", errorBuffer);
return UPNP_E_OUTOF_SOCKET;
}
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEADDR,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() SO_REUSEADDR: %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
#if (defined(BSD) && !defined(__GNU__)) || defined(__OSX__) || defined(__APPLE__)
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() SO_REUSEPORT: %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
#endif /* BSD, __OSX__, __APPLE__ */
onOff = 1;
ret = setsockopt(*ssdpSock, IPPROTO_IPV6, IPV6_V6ONLY,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() IPV6_V6ONLY: %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
memset(&__ss, 0, sizeof(__ss));
ssdpAddr6->sin6_family = (sa_family_t)AF_INET6;
ssdpAddr6->sin6_addr = in6addr_any;
ssdpAddr6->sin6_scope_id = gIF_INDEX;
ssdpAddr6->sin6_port = htons(SSDP_PORT);
ret = bind(*ssdpSock, (struct sockaddr *)ssdpAddr6, sizeof(*ssdpAddr6));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in bind(), addr=0x%032lX, port=%d: %s\n",
0lu, SSDP_PORT, errorBuffer);
ret = UPNP_E_SOCKET_BIND;
goto error_handler;
}
memset((void *)&ssdpMcastAddr, 0, sizeof(ssdpMcastAddr));
ssdpMcastAddr.ipv6mr_interface = gIF_INDEX;
inet_pton(AF_INET6, SSDP_IPV6_LINKLOCAL,
&ssdpMcastAddr.ipv6mr_multiaddr);
ret = setsockopt(*ssdpSock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
(char *)&ssdpMcastAddr, sizeof(ssdpMcastAddr));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() IPV6_JOIN_GROUP (join multicast group): %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_BROADCAST,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() SO_BROADCAST (set broadcast): %s\n",
errorBuffer);
ret = UPNP_E_NETWORK_ERROR;
goto error_handler;
}
ret = UPNP_E_SUCCESS;
error_handler:
if (ret != UPNP_E_SUCCESS) {
if (shutdown(*ssdpSock, SD_BOTH) == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
"Error in shutdown: %s\n", errorBuffer);
}
UpnpCloseSocket(*ssdpSock);
}
return ret;
}
#endif /* IPv6 */
#ifdef UPNP_ENABLE_IPV6
/*!
* \brief This function ...
*/
static int create_ssdp_sock_v6_ula_gua(
/*! [] SSDP IPv6 socket to be created. */
SOCKET * ssdpSock)
{
char errorBuffer[ERROR_BUFFER_LEN];
struct ipv6_mreq ssdpMcastAddr;
struct sockaddr_storage __ss;
struct sockaddr_in6 *ssdpAddr6 = (struct sockaddr_in6 *)&__ss;
int onOff;
int ret = 0;
*ssdpSock = socket(AF_INET6, SOCK_DGRAM, 0);
if (*ssdpSock == INVALID_SOCKET) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in socket(): %s\n", errorBuffer);
return UPNP_E_OUTOF_SOCKET;
}
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEADDR,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() SO_REUSEADDR: %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
#if (defined(BSD) && !defined(__GNU__)) || defined(__OSX__) || defined(__APPLE__)
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() SO_REUSEPORT: %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
#endif /* BSD, __OSX__, __APPLE__ */
onOff = 1;
ret = setsockopt(*ssdpSock, IPPROTO_IPV6, IPV6_V6ONLY,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() IPV6_V6ONLY: %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
memset(&__ss, 0, sizeof(__ss));
ssdpAddr6->sin6_family = (sa_family_t)AF_INET6;
ssdpAddr6->sin6_addr = in6addr_any;
ssdpAddr6->sin6_scope_id = gIF_INDEX;
ssdpAddr6->sin6_port = htons(SSDP_PORT);
ret = bind(*ssdpSock, (struct sockaddr *)ssdpAddr6, sizeof(*ssdpAddr6));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in bind(), addr=0x%032lX, port=%d: %s\n",
0lu, SSDP_PORT, errorBuffer);
ret = UPNP_E_SOCKET_BIND;
goto error_handler;
}
memset((void *)&ssdpMcastAddr, 0, sizeof(ssdpMcastAddr));
ssdpMcastAddr.ipv6mr_interface = gIF_INDEX;
/* SITE LOCAL */
inet_pton(AF_INET6, SSDP_IPV6_SITELOCAL,
&ssdpMcastAddr.ipv6mr_multiaddr);
ret = setsockopt(*ssdpSock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
(char *)&ssdpMcastAddr, sizeof(ssdpMcastAddr));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() IPV6_JOIN_GROUP (join multicast group): %s\n",
errorBuffer);
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_BROADCAST,
(char *)&onOff, sizeof(onOff));
if (ret == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in setsockopt() SO_BROADCAST (set broadcast): %s\n",
errorBuffer);
ret = UPNP_E_NETWORK_ERROR;
goto error_handler;
}
ret = UPNP_E_SUCCESS;
error_handler:
if (ret != UPNP_E_SUCCESS) {
if (shutdown(*ssdpSock, SD_BOTH) == -1) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
"Error in shutdown: %s\n", errorBuffer);
}
UpnpCloseSocket(*ssdpSock);
}
return ret;
}
#endif /* IPv6 */
/*!
* \brief Creates the SSDP IPv6 socket to be used by the control point.
*/
#ifdef UPNP_ENABLE_IPV6
static int create_ssdp_sock_reqv6(
/* [out] SSDP IPv6 request socket to be created. */
SOCKET *ssdpReqSock)
{
char errorBuffer[ERROR_BUFFER_LEN];
char hops = 1;
*ssdpReqSock = socket(AF_INET6, SOCK_DGRAM, 0);
if (*ssdpReqSock == INVALID_SOCKET) {
strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
UpnpPrintf(UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
"Error in socket(): %s\n", errorBuffer);
return UPNP_E_OUTOF_SOCKET;
}
/* MUST use scoping of IPv6 addresses to control the propagation os SSDP
* messages instead of relying on the Hop Limit (Equivalent to the TTL
* limit in IPv4). */
setsockopt(*ssdpReqSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&hops, sizeof(hops));
/* just do it, regardless if fails or not. */
sock_make_no_blocking(*ssdpReqSock);
return UPNP_E_SUCCESS;
}
#endif /* IPv6 */
#endif /* INCLUDE_CLIENT_APIS */
int get_ssdp_sockets(MiniServerSockArray * out)
{
int retVal;
#ifdef INCLUDE_CLIENT_APIS
out->ssdpReqSock4 = INVALID_SOCKET;
out->ssdpReqSock6 = INVALID_SOCKET;
/* Create the IPv4 socket for SSDP REQUESTS */
if (strlen(gIF_IPV4) > (size_t)0) {
retVal = create_ssdp_sock_reqv4(&out->ssdpReqSock4);
if (retVal != UPNP_E_SUCCESS)
return retVal;
/* For use by ssdp control point. */
gSsdpReqSocket4 = out->ssdpReqSock4;
} else
out->ssdpReqSock4 = INVALID_SOCKET;
/* Create the IPv6 socket for SSDP REQUESTS */
#ifdef UPNP_ENABLE_IPV6
if (strlen(gIF_IPV6) > (size_t)0) {
retVal = create_ssdp_sock_reqv6(&out->ssdpReqSock6);
if (retVal != UPNP_E_SUCCESS) {
shutdown(out->ssdpReqSock4, SD_BOTH);
UpnpCloseSocket(out->ssdpReqSock4);
return retVal;
}
/* For use by ssdp control point. */
gSsdpReqSocket6 = out->ssdpReqSock6;
} else
out->ssdpReqSock6 = INVALID_SOCKET;
#endif /* IPv6 */
#endif /* INCLUDE_CLIENT_APIS */
/* Create the IPv4 socket for SSDP */
if (strlen(gIF_IPV4) > (size_t)0) {
retVal = create_ssdp_sock_v4(&out->ssdpSock4);
if (retVal != UPNP_E_SUCCESS) {
#ifdef INCLUDE_CLIENT_APIS
shutdown(out->ssdpReqSock4, SD_BOTH);
UpnpCloseSocket(out->ssdpReqSock4);
shutdown(out->ssdpReqSock6, SD_BOTH);
UpnpCloseSocket(out->ssdpReqSock6);
#endif /* INCLUDE_CLIENT_APIS */
return retVal;
}
} else
out->ssdpSock4 = INVALID_SOCKET;
/* Create the IPv6 socket for SSDP */
#ifdef UPNP_ENABLE_IPV6
if (strlen(gIF_IPV6) > (size_t)0) {
retVal = create_ssdp_sock_v6(&out->ssdpSock6);
if (retVal != UPNP_E_SUCCESS) {
shutdown(out->ssdpSock4, SD_BOTH);
UpnpCloseSocket(out->ssdpSock4);
#ifdef INCLUDE_CLIENT_APIS
shutdown(out->ssdpReqSock4, SD_BOTH);
UpnpCloseSocket(out->ssdpReqSock4);
shutdown(out->ssdpReqSock6, SD_BOTH);
UpnpCloseSocket(out->ssdpReqSock6);
#endif /* INCLUDE_CLIENT_APIS */
return retVal;
}
} else
out->ssdpSock6 = INVALID_SOCKET;
if (strlen(gIF_IPV6_ULA_GUA) > (size_t)0) {
retVal = create_ssdp_sock_v6_ula_gua(&out->ssdpSock6UlaGua);
if (retVal != UPNP_E_SUCCESS) {
shutdown(out->ssdpSock4, SD_BOTH);
UpnpCloseSocket(out->ssdpSock4);
shutdown(out->ssdpSock6, SD_BOTH);
UpnpCloseSocket(out->ssdpSock6);
#ifdef INCLUDE_CLIENT_APIS
shutdown(out->ssdpReqSock4, SD_BOTH);
UpnpCloseSocket(out->ssdpReqSock4);
shutdown(out->ssdpReqSock6, SD_BOTH);
UpnpCloseSocket(out->ssdpReqSock6);
#endif /* INCLUDE_CLIENT_APIS */
return retVal;
}
} else
out->ssdpSock6UlaGua = INVALID_SOCKET;
#endif /* UPNP_ENABLE_IPV6 */
return UPNP_E_SUCCESS;
}
#endif /* EXCLUDE_SSDP */
/* @} SSDPlib */