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; char SERVER[200]; 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; /* get server info */ get_sdk_info(SERVER, sizeof(SERVER)); /* 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; dbgStr = ixmlNode_getNodeName(tmpNode); 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, 0); 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 == 0, UDNstr, SInfo->DescURL, Exp, SInfo->DeviceAf); } else { /* AdFlag == -1 */ DeviceShutdown(devType, i == 0, UDNstr, SERVER, SInfo->DescURL, Exp, SInfo->DeviceAf); } } else { switch (SearchType) { case SSDP_ALL: { DeviceReply(DestAddr, devType, i == 0, UDNstr, SInfo->DescURL, defaultExp); } break; case SSDP_ROOTDEVICE: if (i == 0) { SendReply(DestAddr, devType, 1, UDNstr, SInfo->DescURL, defaultExp, 0); } 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); break; } } } case SSDP_DEVICETYPE: { if (!strncasecmp(DeviceType, devType, strlen(DeviceType) - 2)) { if (atoi(strrchr(DeviceType, ':') + 1) == atoi(&devType[strlen(devType) - 1])) { /* the requested version is lower than the device version * must reply with the lower version number */ 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); } 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); } else { /* AdFlag == -1 */ ServiceShutdown(UDNstr, servType, SInfo->DescURL, Exp, SInfo->DeviceAf); } } } else { switch (SearchType) { case SSDP_ALL: { ServiceReply(DestAddr, servType, UDNstr, SInfo->DescURL, defaultExp); } break; case SSDP_SERVICE: if (ServiceType) { if (!strncasecmp(ServiceType, servType, strlen(ServiceType) - 2)) { if (atoi(strrchr(ServiceType, ':') + 1) == atoi(&servType[strlen(servType) - 1])) { /* the requested version is lower than the service version * must reply with the lower version number */ 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); } 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; }
/*! * \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(__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. */ ret = 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; }
/*! * \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)); ret += setsockopt(*ssdpSock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&onOff, sizeof(onOff)); if (ret < 0) { 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(__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; }
/************************************************************************ * Function : ssdp_handle_device_request * * Parameters: * IN http_message_t* hmsg: SSDP search request from the control point * IN struct sockaddr_in* dest_addr: The address info of control point * * Description: * This function handles the search request. It do the sanity checks of * the request and then schedules a thread to send a random time reply ( * random within maximum time given by the control point to reply). * * Returns: void * * 1 if successful else appropriate error ***************************************************************************/ void ssdp_handle_device_request( IN http_message_t * hmsg, IN struct sockaddr_in *dest_addr ) { #define MX_FUDGE_FACTOR 10 int handle; struct Handle_Info *dev_info = NULL; memptr hdr_value; int mx; char save_char; SsdpEvent event; int ret_code; SsdpSearchReply *threadArg = NULL; ThreadPoolJob job; int replyTime; int maxAge; // check man hdr if( httpmsg_find_hdr( hmsg, HDR_MAN, &hdr_value ) == NULL || memptr_cmp( &hdr_value, "\"ssdp:discover\"" ) != 0 ) { return; // bad or missing hdr } // MX header if( httpmsg_find_hdr( hmsg, HDR_MX, &hdr_value ) == NULL || ( mx = raw_to_int( &hdr_value, 10 ) ) < 0 ) { return; } // ST header if( httpmsg_find_hdr( hmsg, HDR_ST, &hdr_value ) == NULL ) { return; } save_char = hdr_value.buf[hdr_value.length]; hdr_value.buf[hdr_value.length] = '\0'; ret_code = ssdp_request_type( hdr_value.buf, &event ); hdr_value.buf[hdr_value.length] = save_char; // restore if( ret_code == -1 ) { return; // bad ST header } HandleLock( ); // device info if( GetDeviceHandleInfo( &handle, &dev_info ) != HND_DEVICE ) { HandleUnlock( ); return; // no info found } maxAge = dev_info->MaxAge; HandleUnlock( ); DBGONLY( UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, "ssdp_handle_device_request with Cmd %d SEARCH\n", event.Cmd ); UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, "MAX-AGE = %d\n", maxAge ); UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, "MX = %d\n", event.Mx ); UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, "DeviceType = %s\n", event.DeviceType ); UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, "DeviceUuid = %s\n", event.UDN ); UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__, "ServiceType = %s\n", event.ServiceType ); )
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); }
int genaSubscribe( UpnpClient_Handle client_handle, const UpnpString *PublisherURL, int *TimeOut, UpnpString *out_sid) { int return_code = GENA_SUCCESS; ClientSubscription *newSubscription = UpnpClientSubscription_new(); uuid_upnp uid; Upnp_SID temp_sid; Upnp_SID temp_sid2; UpnpString *ActualSID = UpnpString_new(); UpnpString *EventURL = UpnpString_new(); struct Handle_Info *handle_info; int rc = 0; memset(temp_sid, 0, sizeof(temp_sid)); memset(temp_sid2, 0, sizeof(temp_sid2)); UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA SUBSCRIBE BEGIN"); UpnpString_clear(out_sid); HandleReadLock(); /* validate handle */ if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { return_code = GENA_E_BAD_HANDLE; SubscribeLock(); goto error_handler; } HandleUnlock(); /* subscribe */ SubscribeLock(); return_code = gena_subscribe(PublisherURL, TimeOut, NULL, ActualSID); HandleLock(); if (return_code != UPNP_E_SUCCESS) { UpnpPrintf( UPNP_CRITICAL, GENA, __FILE__, __LINE__, "SUBSCRIBE FAILED in transfer error code: %d returned\n", return_code ); goto error_handler; } if(GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { return_code = GENA_E_BAD_HANDLE; goto error_handler; } /* generate client SID */ uuid_create(&uid ); uuid_unpack(&uid, temp_sid); rc = snprintf(temp_sid2, sizeof(temp_sid2), "uuid:%s", temp_sid); if (rc < 0 || (unsigned int) rc >= sizeof(temp_sid2)) { return_code = UPNP_E_OUTOF_MEMORY; goto error_handler; } UpnpString_set_String(out_sid, temp_sid2); /* create event url */ UpnpString_assign(EventURL, PublisherURL); /* fill subscription */ if (newSubscription == NULL) { return_code = UPNP_E_OUTOF_MEMORY; goto error_handler; } UpnpClientSubscription_set_RenewEventId(newSubscription, -1); UpnpClientSubscription_set_SID(newSubscription, out_sid); UpnpClientSubscription_set_ActualSID(newSubscription, ActualSID); UpnpClientSubscription_set_EventURL(newSubscription, EventURL); UpnpClientSubscription_set_Next(newSubscription, handle_info->ClientSubList); handle_info->ClientSubList = newSubscription; /* schedule expiration event */ return_code = ScheduleGenaAutoRenew(client_handle, *TimeOut, newSubscription); error_handler: UpnpString_delete(ActualSID); UpnpString_delete(EventURL); if (return_code != UPNP_E_SUCCESS) UpnpClientSubscription_delete(newSubscription); HandleUnlock(); SubscribeUnlock(); return return_code; }
int genaRenewSubscription( UpnpClient_Handle client_handle, const UpnpString *in_sid, int *TimeOut) { int return_code = GENA_SUCCESS; ClientSubscription *sub = NULL; ClientSubscription *sub_copy = UpnpClientSubscription_new(); struct Handle_Info *handle_info; UpnpString *ActualSID = UpnpString_new(); ThreadPoolJob tempJob; HandleLock(); /* validate handle and sid */ if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { HandleUnlock(); return_code = GENA_E_BAD_HANDLE; goto exit_function; } sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid); if (sub == NULL) { HandleUnlock(); return_code = GENA_E_BAD_SID; goto exit_function; } /* remove old events */ if (TimerThreadRemove( &gTimerThread, UpnpClientSubscription_get_RenewEventId(sub), &tempJob) == 0 ) { free_upnp_timeout((upnp_timeout *)tempJob.arg); } UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "REMOVED AUTO RENEW EVENT"); UpnpClientSubscription_set_RenewEventId(sub, -1); UpnpClientSubscription_assign(sub_copy, sub); HandleUnlock(); return_code = gena_subscribe( UpnpClientSubscription_get_EventURL(sub_copy), TimeOut, UpnpClientSubscription_get_ActualSID(sub_copy), ActualSID); HandleLock(); if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) { HandleUnlock(); return_code = GENA_E_BAD_HANDLE; goto exit_function; } /* we just called GetHandleInfo, so we don't check for return value */ /*GetHandleInfo(client_handle, &handle_info); */ if (return_code != UPNP_E_SUCCESS) { /* network failure (remove client sub) */ RemoveClientSubClientSID(&handle_info->ClientSubList, in_sid); free_client_subscription(sub_copy); HandleUnlock(); goto exit_function; } /* get subscription */ sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid); if (sub == NULL) { free_client_subscription(sub_copy); HandleUnlock(); return_code = GENA_E_BAD_SID; goto exit_function; } /* store actual sid */ UpnpClientSubscription_set_ActualSID(sub, ActualSID); /* start renew subscription timer */ return_code = ScheduleGenaAutoRenew(client_handle, *TimeOut, sub); if (return_code != GENA_SUCCESS) { RemoveClientSubClientSID( &handle_info->ClientSubList, UpnpClientSubscription_get_SID(sub)); } free_client_subscription(sub_copy); HandleUnlock(); exit_function: UpnpString_delete(ActualSID); UpnpClientSubscription_delete(sub_copy); return return_code; }
/**************************************************************************** * Function : handle_query_variable * * Parameters : * IN SOCKINFO *info : Socket info * IN http_message_t* request : HTTP request * IN IXML_Document *xml_doc : Document containing the variable request * SOAP message * * Description : This action handles the SOAP requests to querry the * state variables. This functionality has been deprecated in * the UPnP V1.0 architecture * * Return : void * * Note : ****************************************************************************/ static UPNP_INLINE void handle_query_variable( IN SOCKINFO * info, IN http_message_t * request, IN IXML_Document * xml_doc ) { Upnp_FunPtr soap_event_callback; void *cookie; char var_name[LINE_SIZE]; struct Upnp_State_Var_Request variable; const char *err_str; int err_code; // get var name if( get_var_name( xml_doc, var_name ) != 0 ) { send_error_response( info, SOAP_INVALID_VAR, Soap_Invalid_Var, request ); return; } // get info for event if( get_device_info( request, 1, xml_doc, variable.DevUDN, variable.ServiceID, &soap_event_callback, &cookie ) != 0 ) { send_error_response( info, SOAP_INVALID_VAR, Soap_Invalid_Var, request ); return; } linecopy( variable.ErrStr, "" ); variable.ErrCode = UPNP_E_SUCCESS; namecopy( variable.StateVarName, var_name ); variable.CurrentVal = NULL; variable.CtrlPtIPAddr = info->foreign_ip_addr; // send event soap_event_callback( UPNP_CONTROL_GET_VAR_REQUEST, &variable, cookie ); UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, "Return from callback for var request\n" ); // validate, and handle result if( variable.CurrentVal == NULL ) { err_code = SOAP_ACTION_FAILED; err_str = Soap_Action_Failed; send_error_response( info, SOAP_INVALID_VAR, Soap_Invalid_Var, request ); return; } if( variable.ErrCode != UPNP_E_SUCCESS ) { if( strlen( variable.ErrStr ) > 0 ) { err_code = SOAP_INVALID_VAR; err_str = Soap_Invalid_Var; } else { err_code = variable.ErrCode; err_str = variable.ErrStr; } send_error_response( info, err_code, err_str, request ); return; } // send response send_var_query_response( info, variable.CurrentVal, request ); ixmlFreeDOMString( variable.CurrentVal ); }
/**************************************************************************** * Function : handle_invoke_action * * Parameters : * IN SOCKINFO *info : Socket info * IN http_message_t* request : HTTP Request * IN memptr action_name : Name of the SOAP Action * IN IXML_Document *xml_doc : document containing the SOAP action * request * * Description : This functions handle the SOAP action request. It checks * the integrity of the SOAP action request and gives the call back to * the device application. * * Return : void * * Note : ****************************************************************************/ static void handle_invoke_action( IN SOCKINFO * info, IN http_message_t * request, IN memptr action_name, IN IXML_Document * xml_doc ) { char save_char; IXML_Document *resp_node = NULL; struct Upnp_Action_Request action; Upnp_FunPtr soap_event_callback; void *cookie = NULL; int err_code; const char *err_str; action.ActionResult = NULL; // null-terminate save_char = action_name.buf[action_name.length]; action_name.buf[action_name.length] = '\0'; // set default error err_code = SOAP_INVALID_ACTION; err_str = Soap_Invalid_Action; // get action node if( get_action_node( xml_doc, action_name.buf, &resp_node ) == -1 ) { goto error_handler; } // get device info for action event err_code = get_device_info( request, 0, xml_doc, action.DevUDN, action.ServiceID, &soap_event_callback, &cookie ); if( err_code != UPNP_E_SUCCESS ) { goto error_handler; } namecopy( action.ActionName, action_name.buf ); linecopy( action.ErrStr, "" ); action.ActionRequest = resp_node; action.ActionResult = NULL; action.ErrCode = UPNP_E_SUCCESS; action.CtrlPtIPAddr = info->foreign_ip_addr; UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, "Calling Callback\n" ); soap_event_callback( UPNP_CONTROL_ACTION_REQUEST, &action, cookie ); if( action.ErrCode != UPNP_E_SUCCESS ) { if( strlen( action.ErrStr ) <= 0 ) { err_code = SOAP_ACTION_FAILED; err_str = Soap_Action_Failed; } else { err_code = action.ErrCode; err_str = action.ErrStr; } goto error_handler; } // validate, and handle action error if( action.ActionResult == NULL ) { err_code = SOAP_ACTION_FAILED; err_str = Soap_Action_Failed; goto error_handler; } // send response send_action_response( info, action.ActionResult, request ); err_code = 0; // error handling and cleanup error_handler: ixmlDocument_free( action.ActionResult ); ixmlDocument_free( resp_node ); action_name.buf[action_name.length] = save_char; // restore if( err_code != 0 ) { send_error_response( info, err_code, err_str, request ); } }
/**************************************************************************** * Function : send_action_response * * Parameters : * IN SOCKINFO *info : socket info * IN IXML_Document *action_resp : The response document * IN http_message_t* request : action request document * * Description : This function sends the SOAP response * * Return : void * * Note : ****************************************************************************/ static UPNP_INLINE void send_action_response( IN SOCKINFO * info, IN IXML_Document * action_resp, IN http_message_t * request ) { char *xml_response = NULL; membuffer headers; int major, minor; int err_code; off_t content_length; int ret_code; int timeout_secs = SOAP_TIMEOUT; static char *start_body = // "<?xml version=\"1.0\"?>" required?? "<s:Envelope xmlns:s=\"http://schemas.xmlsoap." "org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap." "org/soap/encoding/\"><s:Body>\n"; static char *end_body = "</s:Body> </s:Envelope>"; // init http_CalcResponseVersion( request->major_version, request->minor_version, &major, &minor ); membuffer_init( &headers ); err_code = UPNP_E_OUTOF_MEMORY; // one error only // get xml xml_response = ixmlPrintNode( ( IXML_Node * ) action_resp ); if( xml_response == NULL ) { goto error_handler; } content_length = strlen( start_body ) + strlen( xml_response ) + strlen( end_body ); // make headers if (http_MakeMessage( &headers, major, minor, "RNsDsSXcc", HTTP_OK, // status code content_length, ContentTypeHeader, "EXT:\r\n", X_USER_AGENT) != 0 ) { goto error_handler; } // send whole msg ret_code = http_SendMessage( info, &timeout_secs, "bbbb", headers.buf, headers.length, start_body, strlen( start_body ), xml_response, strlen( xml_response ), end_body, strlen( end_body ) ); if( ret_code != 0 ) { UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, "Failed to send response: err code = %d\n", ret_code ); } err_code = 0; error_handler: ixmlFreeDOMString( xml_response ); membuffer_destroy( &headers ); if( err_code != 0 ) { // only one type of error to worry about - out of mem send_error_response( info, SOAP_ACTION_FAILED, "Out of memory", request ); } }
/**************************************************************************** * Function : get_action_node * * Parameters : * IN IXML_Document *TempDoc : The root DOM node. * IN char *NodeName : IXML_Node name to be searched. * OUT IXML_Document ** RespNode : Response/Output node. * * Description : This function separates the action node from * the root DOM node. * * Return : static UPNP_INLINE int * 0 if successful, or -1 if fails. * * Note : ****************************************************************************/ static UPNP_INLINE int get_action_node( IN IXML_Document * TempDoc, IN char *NodeName, OUT IXML_Document ** RespNode ) { IXML_Node *EnvpNode = NULL; IXML_Node *BodyNode = NULL; IXML_Node *ActNode = NULL; DOMString ActNodeName = NULL; const DOMString nodeName; int ret_code = -1; // error, by default IXML_NodeList *nl = NULL; UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, "get_action_node(): node name =%s\n ", NodeName ); *RespNode = NULL; // Got the Envelope node here EnvpNode = ixmlNode_getFirstChild( ( IXML_Node * ) TempDoc ); if( EnvpNode == NULL ) { goto error_handler; } nl = ixmlElement_getElementsByTagNameNS( ( IXML_Element * ) EnvpNode, "*", "Body" ); if( nl == NULL ) { goto error_handler; } BodyNode = ixmlNodeList_item( nl, 0 ); if( BodyNode == NULL ) { goto error_handler; } // Got action node here ActNode = ixmlNode_getFirstChild( BodyNode ); if( ActNode == NULL ) { goto error_handler; } //Test whether this is the action node nodeName = ixmlNode_getNodeName( ActNode ); if( nodeName == NULL ) { goto error_handler; } if( strstr( nodeName, NodeName ) == NULL ) { goto error_handler; } else { ActNodeName = ixmlPrintNode( ActNode ); if( ActNodeName == NULL ) { goto error_handler; } ret_code = ixmlParseBufferEx( ActNodeName, RespNode ); if( ret_code != IXML_SUCCESS ) { ixmlFreeDOMString( ActNodeName ); ret_code = -1; goto error_handler; } } ret_code = 0; // success error_handler: ixmlFreeDOMString( ActNodeName ); if( nl ) ixmlNodeList_free( nl ); return ret_code; }
/************************************************************************ * Function : getServiceList * * Parameters : * IXML_Node *node ; XML node information * service_info **end ; service added is returned to the output * parameter * char * URLBase ; provides Base URL to resolve relative URL * * Description : Returns pointer to service info after getting the * sub-elements of the service info. * * Return : service_info * - pointer to the service info node ; * * Note : ************************************************************************/ service_info *getServiceList( IXML_Node *node, service_info **end, char *URLBase) { IXML_Node *serviceList = NULL; IXML_Node *current_service = NULL; IXML_Node *UDN = NULL; IXML_Node *serviceType = NULL; IXML_Node *serviceId = NULL; IXML_Node *SCPDURL = NULL; IXML_Node *controlURL = NULL; IXML_Node *eventURL = NULL; DOMString tempDOMString = NULL; service_info *head = NULL; service_info *current = NULL; service_info *previous = NULL; IXML_NodeList *serviceNodeList = NULL; long unsigned int NumOfServices = 0lu; long unsigned int i = 0lu; int fail = 0; if (getSubElement("UDN", node, &UDN) && getSubElement("serviceList", node, &serviceList)) { serviceNodeList = ixmlElement_getElementsByTagName( (IXML_Element *)serviceList, "service"); if (serviceNodeList != NULL) { NumOfServices = ixmlNodeList_length(serviceNodeList); for (i = 0lu; i < NumOfServices; i++) { current_service = ixmlNodeList_item(serviceNodeList, i); fail = 0; if (current) { current->next = malloc(sizeof(service_info)); previous = current; current = current->next; } else { head = malloc(sizeof(service_info)); current = head; } if (!current) { freeServiceList(head); ixmlNodeList_free(serviceNodeList); return NULL; } current->next = NULL; current->controlURL = NULL; current->eventURL = NULL; current->serviceType = NULL; current->serviceId = NULL; current->SCPDURL = NULL; current->active = 1; current->subscriptionList = NULL; current->TotalSubscriptions = 0; if (!(current->UDN = getElementValue(UDN))) fail = 1; if (!getSubElement("serviceType", current_service, &serviceType) || !(current->serviceType = getElementValue(serviceType))) fail = 1; if (!getSubElement("serviceId", current_service, &serviceId) || !(current->serviceId = getElementValue(serviceId))) fail = 1; if (!getSubElement("SCPDURL", current_service, &SCPDURL) || !(tempDOMString = getElementValue(SCPDURL)) || !(current->SCPDURL = resolve_rel_url(URLBase, tempDOMString))) fail = 1; ixmlFreeDOMString(tempDOMString); tempDOMString = NULL; if (!(getSubElement("controlURL", current_service, &controlURL)) || !(tempDOMString = getElementValue(controlURL)) || !(current->controlURL = resolve_rel_url(URLBase, tempDOMString))) { UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "BAD OR MISSING CONTROL URL"); UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "CONTROL URL SET TO NULL IN SERVICE INFO"); current->controlURL = NULL; fail = 0; } ixmlFreeDOMString(tempDOMString); tempDOMString = NULL; if (!getSubElement("eventSubURL", current_service, &eventURL) || !(tempDOMString = getElementValue(eventURL)) || !(current->eventURL = resolve_rel_url(URLBase, tempDOMString))) { UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "BAD OR MISSING EVENT URL"); UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "EVENT URL SET TO NULL IN SERVICE INFO"); current->eventURL = NULL; fail = 0; } ixmlFreeDOMString(tempDOMString); tempDOMString = NULL; if (fail) { freeServiceList(current); if (previous) previous->next = NULL; else head = NULL; current = previous; } } ixmlNodeList_free(serviceNodeList); } (*end) = current; return head; } else { (*end) = NULL; return NULL; } }
/**************************************************************************** * Function : handle_invoke_action * * Parameters : * IN SOCKINFO *info : Socket info * IN http_message_t* request : HTTP Request * IN memptr action_name : Name of the SOAP Action * IN IXML_Document *xml_doc : document containing the SOAP action * request * * Description : This functions handle the SOAP action request. It checks * the integrity of the SOAP action request and gives the call back to * the device application. * * Return : void * * Note : ****************************************************************************/ static void handle_invoke_action( IN SOCKINFO * info, IN http_message_t * request, IN memptr action_name, IN IXML_Document * xml_doc ) { char save_char; IXML_Document *resp_node = NULL; struct Upnp_Action_Request action; Upnp_FunPtr soap_event_callback; void *cookie = NULL; int err_code; const char *err_str; action.ActionResult = NULL; // null-terminate save_char = action_name.buf[action_name.length]; action_name.buf[action_name.length] = '\0'; // set default error err_code = SOAP_INVALID_ACTION; err_str = Soap_Invalid_Action; // get action node if( get_action_node( xml_doc, action_name.buf, &resp_node ) == -1 ) { goto error_handler; } // get device info for action event err_code = get_device_info( request, 0, xml_doc, action.DevUDN, action.ServiceID, &soap_event_callback, &cookie ); if( err_code != UPNP_E_SUCCESS ) { goto error_handler; } /* for user agent probe support! by HouXB, 22Oct10 */ ListNode *node; http_header_t *header; node = ListHead(&request->headers); while( node != NULL ) { header = ( http_header_t * ) node->item; if (!strncmp(USER_AGENT, header->name.buf, strlen(USER_AGENT))) { int aLen = (int)header->value.length; sprintf(action.UserAgent, "%.*s", aLen, header->value.buf ); if(aLen <NAME_SIZE) { action.UserAgent[aLen] = '\0'; } else { action.UserAgent[NAME_SIZE-1] = '\0'; } /* printf("\n####\nuser Agent found!\nallow len is:%d\nlen is: %d\nagent is:%s\n####\n", NAME_SIZE, aLen, action.UserAgent); */ break; } node = ListNext( &request->headers , node ); } /*end for user agent probe support! by HouXB, 22Oct10 */ namecopy( action.ActionName, action_name.buf ); linecopy( action.ErrStr, "" ); action.ActionRequest = resp_node; action.ActionResult = NULL; action.ErrCode = UPNP_E_SUCCESS; action.CtrlPtIPAddr = info->foreign_ip_addr; UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, "Calling Callback\n" ); soap_event_callback( UPNP_CONTROL_ACTION_REQUEST, &action, cookie ); if( action.ErrCode != UPNP_E_SUCCESS ) { if( strlen( action.ErrStr ) <= 0 ) { err_code = SOAP_ACTION_FAILED; err_str = Soap_Action_Failed; } else { err_code = action.ErrCode; err_str = action.ErrStr; } goto error_handler; } // validate, and handle action error if( action.ActionResult == NULL ) { err_code = SOAP_ACTION_FAILED; err_str = Soap_Action_Failed; goto error_handler; } // send response send_action_response( info, action.ActionResult, request ); err_code = 0; // error handling and cleanup error_handler: ixmlDocument_free( action.ActionResult ); ixmlDocument_free( resp_node ); action_name.buf[action_name.length] = save_char; // restore if( err_code != 0 ) { send_error_response( info, err_code, err_str, request ); } }
/**************************************************************************** * Function : SoapSendActionEx * * Parameters : * IN char* action_url : device contrl URL * IN char *service_type : device service type IN IXML_Document *Header: Soap header * IN IXML_Document *action_node : SOAP action node ( SOAP body) * OUT IXML_Document **response_node : SOAP response node * * Description : This function is called by UPnP API to send the SOAP * action request and waits till it gets the response from the device * pass the response to the API layer. This action is similar to the * the SoapSendAction with only difference that it allows users to * pass the SOAP header along the SOAP body ( soap action request) * * Return : int * returns UPNP_E_SUCCESS if successful else returns appropriate error * Note : ****************************************************************************/ int SoapSendActionEx( IN char *action_url, IN char *service_type, IN IXML_Document * header, IN IXML_Document * action_node, OUT IXML_Document ** response_node ) { char *xml_header_str = NULL; char *action_str = NULL; memptr name; membuffer request; membuffer responsename; int err_code; int ret_code; http_parser_t response; uri_type url; int upnp_error_code; char *upnp_error_str; int got_response = FALSE; const char *xml_start = "<s:Envelope " "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"; const char *xml_header_start = "<s:Header>\r\n"; const char *xml_header_end = "</s:Header>\r\n"; const char *xml_body_start = "<s:Body>"; const char *xml_end = "</s:Body>\r\n" "</s:Envelope>\r\n"; size_t xml_start_len; size_t xml_header_start_len; size_t xml_header_str_len; size_t xml_header_end_len; size_t xml_body_start_len; size_t action_str_len; size_t xml_end_len; off_t content_length; *response_node = NULL; /* init */ err_code = UPNP_E_OUTOF_MEMORY; /* default error */ UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, "Inside SoapSendActionEx():" ); /* init */ membuffer_init( &request ); membuffer_init( &responsename ); /* header string */ xml_header_str = ixmlPrintNode( ( IXML_Node * ) header ); if( xml_header_str == NULL ) { goto error_handler; } /* print action */ action_str = ixmlPrintNode( ( IXML_Node * ) action_node ); if( action_str == NULL ) { goto error_handler; } /* get action name */ if( get_action_name( action_str, &name ) != 0 ) { err_code = UPNP_E_INVALID_ACTION; goto error_handler; } /* parse url */ if( http_FixStrUrl( action_url, strlen( action_url ), &url ) != 0 ) { err_code = UPNP_E_INVALID_URL; goto error_handler; } UpnpPrintf( UPNP_INFO, SOAP, __FILE__, __LINE__, "path=%.*s, hostport=%.*s\n", (int)url.pathquery.size, url.pathquery.buff, (int)url.hostport.text.size, url.hostport.text.buff ); xml_start_len = strlen( xml_start ); xml_body_start_len = strlen( xml_body_start ); xml_end_len = strlen( xml_end ); action_str_len = strlen( action_str ); xml_header_start_len = strlen( xml_header_start ); xml_header_end_len = strlen( xml_header_end ); xml_header_str_len = strlen( xml_header_str ); /* make request msg */ request.size_inc = 50; content_length = (off_t)(xml_start_len + xml_header_start_len + xml_header_str_len + xml_header_end_len + xml_body_start_len + action_str_len + xml_end_len); if (http_MakeMessage( &request, 1, 1, "q" "N" "s" "sssbsc" "Uc" "b" "b" "b" "b" "b" "b" "b", SOAPMETHOD_POST, &url, content_length, ContentTypeHeader, "SOAPACTION: \"", service_type, "#", name.buf, name.length, "\"", xml_start, xml_start_len, xml_header_start, xml_header_start_len, xml_header_str, xml_header_str_len, xml_header_end, xml_header_end_len, xml_body_start, xml_body_start_len, action_str, action_str_len, xml_end, xml_end_len ) != 0 ) { goto error_handler; } ret_code = soap_request_and_response( &request, &url, &response ); got_response = TRUE; if( ret_code != UPNP_E_SUCCESS ) { err_code = ret_code; goto error_handler; } if( membuffer_append( &responsename, name.buf, name.length ) != 0 || membuffer_append_str( &responsename, "Response" ) != 0 ) { goto error_handler; } /* get action node from the response */ ret_code = get_response_value( &response.msg, SOAP_ACTION_RESP, responsename.buf, &upnp_error_code, ( IXML_Node ** ) response_node, &upnp_error_str ); if( ret_code == SOAP_ACTION_RESP ) { err_code = UPNP_E_SUCCESS; } else if( ret_code == SOAP_ACTION_RESP_ERROR ) { err_code = upnp_error_code; } else { err_code = ret_code; } error_handler: ixmlFreeDOMString( action_str ); ixmlFreeDOMString( xml_header_str ); membuffer_destroy( &request ); membuffer_destroy( &responsename ); if( got_response ) { httpmsg_destroy( &response.msg ); } return err_code; }