/** * This function schedules the expiration when this pinhole is created or updated * * @param pinhole The pinhole to expire * @return the event_id created */ int phv6_scheduleExpiration(struct pinholev6 *pinhole) { ThreadPoolJob job; struct phv6_expirationEvent *event; event = (struct phv6_expirationEvent *)malloc( sizeof(struct phv6_expirationEvent)); if(event == NULL) { return 0; } event->pinhole = pinhole; TPJobInit( &job, ( start_routine ) phv6_expiration, event ); TPJobSetFreeFunction( &job, ( free_routine ) phv6_freeEvent ); if( TimerThreadSchedule(&gExpirationTimerThread, pinhole->lease_time, REL_SEC, &job, SHORT_TERM, &(event->event_id)) != UPNP_E_SUCCESS ) { free( event ); return 0; } pinhole->event_id = event->event_id; return event->event_id; }
/*! * \brief Initilize the thread pool to handle a request, sets priority for the * job and adds the job to the thread pool. */ static UPNP_INLINE void schedule_request_job( /*! [in] Socket Descriptor on which connection is accepted. */ SOCKET connfd, /*! [in] Clients Address information. */ struct sockaddr *clientAddr) { struct mserv_request_t *request; ThreadPoolJob job; memset(&job, 0, sizeof(job)); request = (struct mserv_request_t *)malloc( sizeof (struct mserv_request_t)); if (request == NULL) { UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__, "mserv %d: out of memory\n", connfd); sock_close(connfd); return; } request->connfd = connfd; memcpy(&request->foreign_sockaddr, clientAddr, sizeof(request->foreign_sockaddr)); TPJobInit(&job, (start_routine)handle_request, (void *)request); TPJobSetFreeFunction(&job, free_handle_request_arg); TPJobSetPriority(&job, MED_PRIORITY); if (ThreadPoolAdd(&gMiniServerThreadPool, &job, NULL) != 0) { UpnpPrintf(UPNP_INFO, MSERV, __FILE__, __LINE__, "mserv %d: cannot schedule request\n", connfd); free(request); sock_close(connfd); return; } }
/************************************************************************ * Function : ScheduleGenaAutoRenew * * Parameters: * IN int client_handle: Handle that also contains the subscription list * IN int TimeOut: The time out value of the subscription * IN client_subscription * sub: Subscription being renewed * * Description: * This function schedules a job to renew the subscription just before * time out. * * Returns: int * return GENA_E_SUCCESS if successful else returns appropriate error ***************************************************************************/ static int ScheduleGenaAutoRenew( IN int client_handle, IN int TimeOut, IN client_subscription * sub ) { struct Upnp_Event_Subscribe *RenewEventStruct = NULL; upnp_timeout *RenewEvent = NULL; int return_code = GENA_SUCCESS; ThreadPoolJob job; if( TimeOut == UPNP_INFINITE ) { return GENA_SUCCESS; } RenewEventStruct = ( struct Upnp_Event_Subscribe * )malloc( sizeof ( struct Upnp_Event_Subscribe ) ); if( RenewEventStruct == NULL ) { return UPNP_E_OUTOF_MEMORY; } RenewEvent = ( upnp_timeout * ) malloc( sizeof( upnp_timeout ) ); if( RenewEvent == NULL ) { free( RenewEventStruct ); return UPNP_E_OUTOF_MEMORY; } //schedule expire event strcpy( RenewEventStruct->Sid, sub->sid ); RenewEventStruct->ErrCode = UPNP_E_SUCCESS; strncpy( RenewEventStruct->PublisherUrl, sub->EventURL, UPNP_NAME_SIZE - 1 ); RenewEventStruct->TimeOut = TimeOut; //RenewEvent->EventType=UPNP_EVENT_SUBSCRIPTION_EXPIRE; RenewEvent->handle = client_handle; RenewEvent->Event = RenewEventStruct; TPJobInit( &job, ( start_routine ) GenaAutoRenewSubscription, RenewEvent ); TPJobSetFreeFunction( &job, ( free_routine ) free_upnp_timeout ); TPJobSetPriority( &job, MED_PRIORITY ); //Schedule the job if( ( return_code = TimerThreadSchedule( &gTimerThread, TimeOut - AUTO_RENEW_TIME, REL_SEC, &job, SHORT_TERM, &( RenewEvent-> eventId ) ) ) != UPNP_E_SUCCESS ) { free( RenewEvent ); free( RenewEventStruct ); return return_code; } sub->RenewEventId = RenewEvent->eventId; return GENA_SUCCESS; }
/************************************************************************ * Function: TimerThreadInit * * Description: * Initializes and starts timer thread. * * Parameters: * timer - valid timer thread pointer. * tp - valid thread pool to use. Must be * started. Must be valid for lifetime * of timer. Timer must be shutdown * BEFORE thread pool. * Return: * 0 on success, nonzero on failure * Returns error from ThreadPoolAddPersistent if failure. ************************************************************************/ int TimerThreadInit( TimerThread * timer, ThreadPool * tp ) { int rc = 0; ThreadPoolJob timerThreadWorker; assert( timer != NULL ); assert( tp != NULL ); if( ( timer == NULL ) || ( tp == NULL ) ) { return EINVAL; } rc += ithread_mutex_init( &timer->mutex, NULL ); assert( rc == 0 ); rc += ithread_mutex_lock( &timer->mutex ); assert( rc == 0 ); rc += ithread_cond_init( &timer->condition, NULL ); assert( rc == 0 ); rc += FreeListInit( &timer->freeEvents, sizeof( TimerEvent ), 100 ); assert( rc == 0 ); timer->shutdown = 0; timer->tp = tp; timer->lastEventId = 0; rc += ListInit( &timer->eventQ, NULL, NULL ); assert( rc == 0 ); if( rc != 0 ) { rc = EAGAIN; } else { TPJobInit( &timerThreadWorker, TimerThreadWorker, timer ); TPJobSetPriority( &timerThreadWorker, HIGH_PRIORITY ); rc = ThreadPoolAddPersistent( tp, &timerThreadWorker, NULL ); } ithread_mutex_unlock( &timer->mutex ); if( rc != 0 ) { ithread_cond_destroy( &timer->condition ); ithread_mutex_destroy( &timer->mutex ); FreeListDestroy( &timer->freeEvents ); ListDestroy( &timer->eventQ, 0 ); } return rc; }
/************************************************************************ * Function: schedule_request_job * * Parameters: * IN int connfd - Socket Descriptor on which connection is accepted * IN struct sockaddr_in* clientAddr - Clients Address information * * Description: * Initilize the thread pool to handle a request. * Sets priority for the job and adds the job to the thread pool * * Return: void ************************************************************************/ static DLNA_INLINE void schedule_request_job( IN int connfd, IN struct sockaddr_in *clientAddr ) { struct mserv_request_t *request; ThreadPoolJob job; request = ( struct mserv_request_t * ) malloc( sizeof( struct mserv_request_t ) ); if( request == NULL ) { dlnaPrintf( DLNA_INFO, MSERV, __FILE__, __LINE__, "mserv %d: out of memory\n", connfd ); shutdown( request->connfd, SD_BOTH ); dlnaCloseSocket( connfd ); return; } request->connfd = connfd; request->foreign_ip_addr = clientAddr->sin_addr; request->foreign_ip_port = ntohs( clientAddr->sin_port ); TPJobInit( &job, ( start_routine ) handle_request, ( void * )request ); TPJobSetFreeFunction( &job, free_handle_request_arg ); TPJobSetPriority( &job, MED_PRIORITY ); if( ThreadPoolAdd( &gMiniServerThreadPool, &job, NULL ) != 0 ) { dlnaPrintf( DLNA_INFO, MSERV, __FILE__, __LINE__, "mserv %d: cannot schedule request\n", connfd ); free( request ); shutdown( connfd, SD_BOTH ); dlnaCloseSocket( connfd ); return; } }
int genaNotifyAll( UpnpDevice_Handle device_handle, char *UDN, char *servId, char **VarNames, char **VarValues, int var_count) { int ret = GENA_SUCCESS; int line = 0; int *reference_count = NULL; char *UDN_copy = NULL; char *servId_copy = NULL; DOMString propertySet = NULL; char *headers = NULL; notify_thread_struct *thread_struct = NULL; subscription *finger = NULL; service_info *service = NULL; struct Handle_Info *handle_info; ThreadPoolJob job; memset(&job, 0, sizeof(job)); UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA BEGIN NOTIFY ALL"); reference_count = (int *)malloc(sizeof (int)); if (reference_count == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } *reference_count = 0; UDN_copy = strdup(UDN); if (UDN_copy == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } servId_copy = strdup(servId); if( servId_copy == NULL ) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } ret = GeneratePropertySet(VarNames, VarValues, var_count, &propertySet); if (ret != XML_SUCCESS) { line = __LINE__; goto ExitFunction; } UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENERATED PROPERTY SET IN EXT NOTIFY: %s", propertySet); headers = AllocGenaHeaders(propertySet); if (headers == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } HandleLock(); if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) { line = __LINE__; ret = GENA_E_BAD_HANDLE; } else { service = FindServiceId(&handle_info->ServiceTable, servId, UDN); if (service != NULL) { finger = GetFirstSubscription(service); while (finger) { thread_struct = (notify_thread_struct *)malloc(sizeof (notify_thread_struct)); if (thread_struct == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; break; } (*reference_count)++; thread_struct->reference_count = reference_count; thread_struct->UDN = UDN_copy; thread_struct->servId = servId_copy; thread_struct->headers = headers; thread_struct->propertySet = propertySet; memset(thread_struct->sid, 0, sizeof(thread_struct->sid)); strncpy(thread_struct->sid, finger->sid, sizeof(thread_struct->sid) - 1); thread_struct->eventKey = finger->eventKey++; thread_struct->device_handle = device_handle; /* if overflow, wrap to 1 */ if (finger->eventKey < 0) { finger->eventKey = 1; } TPJobInit(&job, (start_routine)genaNotifyThread, thread_struct); TPJobSetFreeFunction(&job, (free_routine)free_notify_struct); TPJobSetPriority(&job, MED_PRIORITY); ret = ThreadPoolAdd(&gSendThreadPool, &job, NULL); if (ret != 0) { line = __LINE__; if (ret == EOUTOFMEM) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; } break; } finger = GetNextSubscription(service, finger); } } else { line = __LINE__; ret = GENA_E_BAD_SERVICE; } } ExitFunction: if (ret != GENA_SUCCESS || *reference_count == 0) { free(headers); ixmlFreeDOMString(propertySet); free(servId_copy); free(UDN_copy); free(reference_count); } HandleUnlock(); UpnpPrintf(UPNP_INFO, GENA, __FILE__, line, "GENA END NOTIFY ALL, ret = %d", ret); return ret; }
int genaInitNotifyExt( UpnpDevice_Handle device_handle, char *UDN, char *servId, IXML_Document *PropSet, const Upnp_SID sid) { int ret = GENA_SUCCESS; int line = 0; int *reference_count = NULL; char *UDN_copy = NULL; char *servId_copy = NULL; DOMString propertySet = NULL; char *headers = NULL; notify_thread_struct *thread_struct = NULL; subscription *sub = NULL; service_info *service = NULL; struct Handle_Info *handle_info; ThreadPoolJob job; memset(&job, 0, sizeof(job)); UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA BEGIN INITIAL NOTIFY EXT"); reference_count = (int *)malloc(sizeof (int)); if (reference_count == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } *reference_count = 0; UDN_copy = strdup(UDN); if (UDN_copy == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } servId_copy = strdup(servId); if( servId_copy == NULL ) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } HandleLock(); if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) { line = __LINE__; ret = GENA_E_BAD_HANDLE; goto ExitFunction; } service = FindServiceId(&handle_info->ServiceTable, servId, UDN); if (service == NULL) { line = __LINE__; ret = GENA_E_BAD_SERVICE; goto ExitFunction; } UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "FOUND SERVICE IN INIT NOTFY EXT: UDN %s, ServID: %s", UDN, servId); sub = GetSubscriptionSID(sid, service); if (sub == NULL || sub->active) { line = __LINE__; ret = GENA_E_BAD_SID; goto ExitFunction; } UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, "FOUND SUBSCRIPTION IN INIT NOTIFY EXT: SID %s", sid); sub->active = 1; if (PropSet == 0) { line = __LINE__; ret = GENA_SUCCESS; goto ExitFunction; } propertySet = ixmlPrintNode((IXML_Node *)PropSet); if (propertySet == NULL) { line = __LINE__; ret = UPNP_E_INVALID_PARAM; goto ExitFunction; } UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENERATED PROPERTY SET IN INIT EXT NOTIFY: %s", propertySet); headers = AllocGenaHeaders(propertySet); if (headers == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } /* schedule thread for initial notification */ thread_struct = (notify_thread_struct *)malloc(sizeof (notify_thread_struct)); if (thread_struct == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; } else { *reference_count = 1; thread_struct->servId = servId_copy; thread_struct->UDN = UDN_copy; thread_struct->headers = headers; thread_struct->propertySet = propertySet; memset(thread_struct->sid, 0, sizeof(thread_struct->sid)); strncpy(thread_struct->sid, sid, sizeof(thread_struct->sid) - 1); thread_struct->eventKey = sub->eventKey++; thread_struct->reference_count = reference_count; thread_struct->device_handle = device_handle; TPJobInit(&job, (start_routine)genaNotifyThread, thread_struct); TPJobSetFreeFunction(&job, (free_routine)free_notify_struct); TPJobSetPriority(&job, MED_PRIORITY); ret = ThreadPoolAdd(&gSendThreadPool, &job, NULL); if (ret != 0) { if (ret == EOUTOFMEM) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; } } else { line = __LINE__; ret = GENA_SUCCESS; } } ExitFunction: if (ret != GENA_SUCCESS || PropSet == 0) { free(thread_struct); free(headers); ixmlFreeDOMString(propertySet); free(servId_copy); free(UDN_copy); free(reference_count); } HandleUnlock(); UpnpPrintf(UPNP_INFO, GENA, __FILE__, line, "GENA END INITIAL NOTIFY EXT, ret = %d", ret); return ret; }
/*! * \brief Thread job to Notify a control point. * * It validates the subscription and copies the subscription. Also make sure * that events are sent in order. * * \note calls the genaNotify to do the actual work. */ static void genaNotifyThread( /*! [in] notify thread structure containing all the headers and property set info. */ void *input) { subscription *sub; service_info *service; subscription sub_copy; notify_thread_struct *in = (notify_thread_struct *) input; int return_code; struct Handle_Info *handle_info; ThreadPoolJob job; memset(&job, 0, sizeof(job)); /* This should be a HandleLock and not a HandleReadLock otherwise if there * is a lot of notifications, then multiple threads will acquire a read * lock and the thread which sends the notification will be blocked forever * on the HandleLock at the end of this function. */ /*HandleReadLock(); */ HandleLock(); /* validate context */ if (GetHandleInfo(in->device_handle, &handle_info) != HND_DEVICE) { free_notify_struct(in); HandleUnlock(); return; } if (!(service = FindServiceId(&handle_info->ServiceTable, in->servId, in->UDN)) || !service->active || !(sub = GetSubscriptionSID(in->sid, service)) || copy_subscription(sub, &sub_copy) != HTTP_SUCCESS) { free_notify_struct(in); HandleUnlock(); return; } #ifdef UPNP_ENABLE_NOTIFICATION_REORDERING /*If the event is out of order push it back to the job queue */ if (in->eventKey != sub->ToSendEventKey) { TPJobInit(&job, (start_routine) genaNotifyThread, input); TPJobSetFreeFunction(&job, (free_function) free_notify_struct); TPJobSetPriority(&job, MED_PRIORITY); /* Sleep a little before creating another thread otherwise if there is * a lot of notifications to send, the device will take 100% of the CPU * to create threads and push them back to the job queue. */ imillisleep(1); ThreadPoolAdd(&gSendThreadPool, &job, NULL); freeSubscription(&sub_copy); HandleUnlock(); return; } #endif HandleUnlock(); /* send the notify */ return_code = genaNotify(in->headers, in->propertySet, &sub_copy); freeSubscription(&sub_copy); HandleLock(); if (GetHandleInfo(in->device_handle, &handle_info) != HND_DEVICE) { free_notify_struct(in); HandleUnlock(); return; } /* validate context */ if (!(service = FindServiceId(&handle_info->ServiceTable, in->servId, in->UDN)) || !service->active || !(sub = GetSubscriptionSID(in->sid, service))) { free_notify_struct(in); HandleUnlock(); return; } sub->ToSendEventKey++; if (sub->ToSendEventKey < 0) /* wrap to 1 for overflow */ sub->ToSendEventKey = 1; if (return_code == GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB) RemoveSubscriptionSID(in->sid, service); free_notify_struct(in); HandleUnlock(); }
void ssdp_handle_ctrlpt_msg(http_message_t *hmsg, struct sockaddr_storage *dest_addr, int timeout, void *cookie) { int handle; struct Handle_Info *ctrlpt_info = NULL; memptr hdr_value; /* byebye or alive */ int is_byebye; UpnpDiscovery *param = UpnpDiscovery_new(); int expires; int ret; SsdpEvent event; int nt_found; int usn_found; int st_found; char save_char; Upnp_EventType event_type; Upnp_FunPtr ctrlpt_callback; void *ctrlpt_cookie; ListNode *node = NULL; SsdpSearchArg *searchArg = NULL; int matched = 0; SSDPResultData *threadData = NULL; ThreadPoolJob job; memset(&job, 0, sizeof(job)); /* we are assuming that there can be only one client supported at a time */ HandleReadLock(); if (GetClientHandleInfo(&handle, &ctrlpt_info) != HND_CLIENT) { HandleUnlock(); goto end_ssdp_handle_ctrlpt_msg; } /* copy */ ctrlpt_callback = ctrlpt_info->Callback; ctrlpt_cookie = ctrlpt_info->Cookie; HandleUnlock(); /* search timeout */ if (timeout) { ctrlpt_callback(UPNP_DISCOVERY_SEARCH_TIMEOUT, NULL, cookie); goto end_ssdp_handle_ctrlpt_msg; } UpnpDiscovery_set_ErrCode(param, UPNP_E_SUCCESS); /* MAX-AGE, assume error */ expires = -1; UpnpDiscovery_set_Expires(param, expires); if (httpmsg_find_hdr(hmsg, HDR_CACHE_CONTROL, &hdr_value) != NULL) { ret = matchstr(hdr_value.buf, hdr_value.length, "%imax-age = %d%0", &expires); UpnpDiscovery_set_Expires(param, expires); if (ret != PARSE_OK) goto end_ssdp_handle_ctrlpt_msg; } /* DATE */ if (httpmsg_find_hdr(hmsg, HDR_DATE, &hdr_value) != NULL) { UpnpDiscovery_strcpy_Date(param, hdr_value.buf); } /* dest addr */ UpnpDiscovery_set_DestAddr(param, dest_addr); /* EXT */ if (httpmsg_find_hdr(hmsg, HDR_EXT, &hdr_value) != NULL) { UpnpDiscovery_strncpy_Ext(param, hdr_value.buf, hdr_value.length); } /* LOCATION */ if (httpmsg_find_hdr(hmsg, HDR_LOCATION, &hdr_value) != NULL) { UpnpDiscovery_strncpy_Location(param, hdr_value.buf, hdr_value.length); } /* SERVER / USER-AGENT */ if (httpmsg_find_hdr(hmsg, HDR_SERVER, &hdr_value) != NULL || httpmsg_find_hdr(hmsg, HDR_USER_AGENT, &hdr_value) != NULL) { UpnpDiscovery_strncpy_Os(param, hdr_value.buf, hdr_value.length); } /* clear everything */ event.UDN[0] = '\0'; event.DeviceType[0] = '\0'; event.ServiceType[0] = '\0'; nt_found = FALSE; if (httpmsg_find_hdr(hmsg, HDR_NT, &hdr_value) != NULL) { save_char = hdr_value.buf[hdr_value.length]; hdr_value.buf[hdr_value.length] = '\0'; nt_found = (ssdp_request_type(hdr_value.buf, &event) == 0); hdr_value.buf[hdr_value.length] = save_char; } usn_found = FALSE; if (httpmsg_find_hdr(hmsg, HDR_USN, &hdr_value) != NULL) { save_char = hdr_value.buf[hdr_value.length]; hdr_value.buf[hdr_value.length] = '\0'; usn_found = (unique_service_name(hdr_value.buf, &event) == 0); hdr_value.buf[hdr_value.length] = save_char; } if (nt_found || usn_found) { UpnpDiscovery_strcpy_DeviceID(param, event.UDN); UpnpDiscovery_strcpy_DeviceType(param, event.DeviceType); UpnpDiscovery_strcpy_ServiceType(param, event.ServiceType); } /* ADVERT. OR BYEBYE */ if (hmsg->is_request) { /* use NTS hdr to determine advert., or byebye */ if (httpmsg_find_hdr(hmsg, HDR_NTS, &hdr_value) == NULL) { /* error; NTS header not found */ goto end_ssdp_handle_ctrlpt_msg; } if (memptr_cmp(&hdr_value, "ssdp:alive") == 0) { is_byebye = FALSE; } else if (memptr_cmp(&hdr_value, "ssdp:byebye") == 0) { is_byebye = TRUE; } else { /* bad value */ goto end_ssdp_handle_ctrlpt_msg; } if (is_byebye) { /* check device byebye */ if (!nt_found || !usn_found) { /* bad byebye */ goto end_ssdp_handle_ctrlpt_msg; } event_type = UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE; } else { /* check advertisement. * Expires is valid if positive. This is for testing * only. Expires should be greater than 1800 (30 mins) */ if (!nt_found || !usn_found || UpnpString_get_Length(UpnpDiscovery_get_Location(param)) == 0 || UpnpDiscovery_get_Expires(param) <= 0) { /* bad advertisement */ goto end_ssdp_handle_ctrlpt_msg; } event_type = UPNP_DISCOVERY_ADVERTISEMENT_ALIVE; } /* call callback */ ctrlpt_callback(event_type, param, ctrlpt_cookie); } else { /* reply (to a SEARCH) */ /* only checking to see if there is a valid ST header */ st_found = FALSE; if (httpmsg_find_hdr(hmsg, HDR_ST, &hdr_value) != NULL) { save_char = hdr_value.buf[hdr_value.length]; hdr_value.buf[hdr_value.length] = '\0'; st_found = ssdp_request_type(hdr_value.buf, &event) == 0; hdr_value.buf[hdr_value.length] = save_char; } if (hmsg->status_code != HTTP_OK || UpnpDiscovery_get_Expires(param) <= 0 || UpnpString_get_Length(UpnpDiscovery_get_Location(param)) == 0 || !usn_found || !st_found) { /* bad reply */ goto end_ssdp_handle_ctrlpt_msg; } /* check each current search */ HandleLock(); if (GetClientHandleInfo(&handle, &ctrlpt_info) != HND_CLIENT) { HandleUnlock(); goto end_ssdp_handle_ctrlpt_msg; } node = ListHead(&ctrlpt_info->SsdpSearchList); /* temporary add null termination */ /*save_char = hdr_value.buf[ hdr_value.length ]; */ /*hdr_value.buf[ hdr_value.length ] = '\0'; */ while (node != NULL) { searchArg = node->item; /* check for match of ST header and search target */ switch (searchArg->requestType) { case SSDP_ALL: matched = 1; break; case SSDP_ROOTDEVICE: matched = (event.RequestType == SSDP_ROOTDEVICE); break; case SSDP_DEVICEUDN: matched = !strncmp(searchArg->searchTarget, hdr_value.buf, hdr_value.length); break; case SSDP_DEVICETYPE:{ size_t m = min(hdr_value.length, strlen (searchArg->searchTarget)); matched = !strncmp(searchArg->searchTarget, hdr_value.buf, m); break; } case SSDP_SERVICE:{ size_t m = min(hdr_value.length, strlen (searchArg->searchTarget)); matched = !strncmp(searchArg->searchTarget, hdr_value.buf, m); break; } default: matched = 0; break; } if (matched) { /* schedule call back */ threadData = SSDPResultData_new(); if (threadData != NULL) { SSDPResultData_set_Param(threadData, param); SSDPResultData_set_Cookie(threadData, searchArg-> cookie); SSDPResultData_set_CtrlptCallback (threadData, ctrlpt_callback); TPJobInit(&job, (start_routine) send_search_result, threadData); TPJobSetPriority(&job, MED_PRIORITY); TPJobSetFreeFunction(&job, (free_routine) SSDPResultData_delete); ThreadPoolAdd(&gRecvThreadPool, &job, NULL); } } node = ListNext(&ctrlpt_info->SsdpSearchList, node); } HandleUnlock(); /*ctrlpt_callback( UPNP_DISCOVERY_SEARCH_RESULT, param, cookie ); */ } end_ssdp_handle_ctrlpt_msg: UpnpDiscovery_delete(param); }
int SearchByTarget(int Mx, char *St, void *Cookie) { char errorBuffer[ERROR_BUFFER_LEN]; int *id = NULL; int ret = 0; char ReqBufv4[BUFSIZE]; #ifdef UPNP_ENABLE_IPV6 char ReqBufv6[BUFSIZE]; char ReqBufv6UlaGua[BUFSIZE]; #endif struct sockaddr_storage __ss_v4; #ifdef UPNP_ENABLE_IPV6 struct sockaddr_storage __ss_v6; #endif struct sockaddr_in *destAddr4 = (struct sockaddr_in *)&__ss_v4; #ifdef UPNP_ENABLE_IPV6 struct sockaddr_in6 *destAddr6 = (struct sockaddr_in6 *)&__ss_v6; #endif fd_set wrSet; SsdpSearchArg *newArg = NULL; int timeTillRead = 0; int handle; struct Handle_Info *ctrlpt_info = NULL; enum SsdpSearchType requestType; unsigned long addrv4 = inet_addr(gIF_IPV4); SOCKET max_fd = 0; int retVal; /*ThreadData *ThData; */ ThreadPoolJob job; memset(&job, 0, sizeof(job)); requestType = ssdp_request_type1(St); if (requestType == SSDP_SERROR) return UPNP_E_INVALID_PARAM; UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__, "Inside SearchByTarget\n"); timeTillRead = Mx; if (timeTillRead < MIN_SEARCH_TIME) timeTillRead = MIN_SEARCH_TIME; else if (timeTillRead > MAX_SEARCH_TIME) timeTillRead = MAX_SEARCH_TIME; retVal = CreateClientRequestPacket(ReqBufv4, sizeof(ReqBufv4), timeTillRead, St, AF_INET); if (retVal != UPNP_E_SUCCESS) return retVal; #ifdef UPNP_ENABLE_IPV6 retVal = CreateClientRequestPacket(ReqBufv6, sizeof(ReqBufv6), timeTillRead, St, AF_INET6); if (retVal != UPNP_E_SUCCESS) return retVal; retVal = CreateClientRequestPacketUlaGua(ReqBufv6UlaGua, sizeof(ReqBufv6UlaGua), timeTillRead, St, AF_INET6); if (retVal != UPNP_E_SUCCESS) return retVal; #endif memset(&__ss_v4, 0, sizeof(__ss_v4)); destAddr4->sin_family = (sa_family_t)AF_INET; inet_pton(AF_INET, SSDP_IP, &destAddr4->sin_addr); destAddr4->sin_port = htons(SSDP_PORT); #ifdef UPNP_ENABLE_IPV6 memset(&__ss_v6, 0, sizeof(__ss_v6)); destAddr6->sin6_family = (sa_family_t)AF_INET6; inet_pton(AF_INET6, SSDP_IPV6_SITELOCAL, &destAddr6->sin6_addr); destAddr6->sin6_port = htons(SSDP_PORT); destAddr6->sin6_scope_id = gIF_INDEX; #endif /* add search criteria to list */ HandleLock(); if (GetClientHandleInfo(&handle, &ctrlpt_info) != HND_CLIENT) { HandleUnlock(); return UPNP_E_INTERNAL_ERROR; } newArg = (SsdpSearchArg *) malloc(sizeof(SsdpSearchArg)); newArg->searchTarget = strdup(St); newArg->cookie = Cookie; newArg->requestType = requestType; id = (int *)malloc(sizeof(int)); TPJobInit(&job, (start_routine) searchExpired, id); TPJobSetPriority(&job, MED_PRIORITY); TPJobSetFreeFunction(&job, (free_routine) free); /* Schedule a timeout event to remove search Arg */ TimerThreadSchedule(&gTimerThread, timeTillRead, REL_SEC, &job, SHORT_TERM, id); newArg->timeoutEventId = *id; ListAddTail(&ctrlpt_info->SsdpSearchList, newArg); HandleUnlock(); /* End of lock */ FD_ZERO(&wrSet); if (gSsdpReqSocket4 != INVALID_SOCKET) { setsockopt(gSsdpReqSocket4, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addrv4, sizeof(addrv4)); FD_SET(gSsdpReqSocket4, &wrSet); max_fd = max(max_fd, gSsdpReqSocket4); } #ifdef UPNP_ENABLE_IPV6 if (gSsdpReqSocket6 != INVALID_SOCKET) { setsockopt(gSsdpReqSocket6, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *)&gIF_INDEX, sizeof(gIF_INDEX)); FD_SET(gSsdpReqSocket6, &wrSet); max_fd = max(max_fd, gSsdpReqSocket6); } #endif ret = select(max_fd + 1, NULL, &wrSet, NULL, NULL); if (ret == -1) { strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN); UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__, "SSDP_LIB: Error in select(): %s\n", errorBuffer); shutdown(gSsdpReqSocket4, SD_BOTH); UpnpCloseSocket(gSsdpReqSocket4); #ifdef UPNP_ENABLE_IPV6 shutdown(gSsdpReqSocket6, SD_BOTH); UpnpCloseSocket(gSsdpReqSocket6); #endif return UPNP_E_INTERNAL_ERROR; } #ifdef UPNP_ENABLE_IPV6 if (gSsdpReqSocket6 != INVALID_SOCKET && FD_ISSET(gSsdpReqSocket6, &wrSet)) { int NumCopy = 0; while (NumCopy < NUM_SSDP_COPY) { UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__, ">>> SSDP SEND M-SEARCH >>>\n%s\n", ReqBufv6UlaGua); sendto(gSsdpReqSocket6, ReqBufv6UlaGua, strlen(ReqBufv6UlaGua), 0, (struct sockaddr *)&__ss_v6, sizeof(struct sockaddr_in6)); NumCopy++; imillisleep(SSDP_PAUSE); } NumCopy = 0; inet_pton(AF_INET6, SSDP_IPV6_LINKLOCAL, &destAddr6->sin6_addr); while (NumCopy < NUM_SSDP_COPY) { UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__, ">>> SSDP SEND M-SEARCH >>>\n%s\n", ReqBufv6); sendto(gSsdpReqSocket6, ReqBufv6, strlen(ReqBufv6), 0, (struct sockaddr *)&__ss_v6, sizeof(struct sockaddr_in6)); NumCopy++; imillisleep(SSDP_PAUSE); } } #endif /* IPv6 */ if (gSsdpReqSocket4 != INVALID_SOCKET && FD_ISSET(gSsdpReqSocket4, &wrSet)) { int NumCopy = 0; while (NumCopy < NUM_SSDP_COPY) { UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__, ">>> SSDP SEND M-SEARCH >>>\n%s\n", ReqBufv4); sendto(gSsdpReqSocket4, ReqBufv4, strlen(ReqBufv4), 0, (struct sockaddr *)&__ss_v4, sizeof(struct sockaddr_in)); NumCopy++; imillisleep(SSDP_PAUSE); } } return 1; }
void ssdp_handle_device_request(http_message_t *hmsg, struct sockaddr_storage *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; memset(&job, 0, sizeof(job)); /* check man hdr. */ if (httpmsg_find_hdr(hmsg, HDR_MAN, &hdr_value) == NULL || memptr_cmp(&hdr_value, "\"ssdp:discover\"") != 0) /* bad or missing hdr. */ return; /* 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); /* restore. */ hdr_value.buf[hdr_value.length] = save_char; if (ret_code == -1) /* bad ST header. */ return; HandleLock(); /* device info. */ switch (GetDeviceHandleInfo((int)dest_addr->ss_family, &handle, &dev_info)) { case HND_DEVICE: break; default: HandleUnlock(); /* no info found. */ return; } maxAge = dev_info->MaxAge; HandleUnlock(); 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); threadArg = (SsdpSearchReply *)malloc(sizeof(SsdpSearchReply)); if (threadArg == NULL) return; threadArg->handle = handle; memcpy(&threadArg->dest_addr, dest_addr, sizeof(threadArg->dest_addr)); threadArg->event = event; threadArg->MaxAge = maxAge; TPJobInit(&job, advertiseAndReplyThread, threadArg); TPJobSetFreeFunction(&job, (free_routine) free); /* Subtract a percentage from the mx to allow for network and processing * delays (i.e. if search is for 30 seconds, respond * within 0 - 27 seconds). */ if (mx >= 2) mx -= MAXVAL(1, mx / MX_FUDGE_FACTOR); if (mx < 1) mx = 1; replyTime = rand() % mx; TimerThreadSchedule(&gTimerThread, replyTime, REL_SEC, &job, SHORT_TERM, NULL); }
/************************************************************************ * Function: StartMiniServer * * Parameters : * unsigned short listen_port - Port on which the server listens for * incoming connections * * Description: * Initialize the sockets functionality for the * Miniserver. Initialize a thread pool job to run the MiniServer * and the job to the thread pool. If listen port is 0, port is * dynamically picked * * Use timer mechanism to start the MiniServer, failure to meet the * allowed delay aborts the attempt to launch the MiniServer. * * Return: int * Actual port socket is bound to - On Success * A negative number DLNA_E_XXX - On Error ************************************************************************/ int StartMiniServer( unsigned short listen_port ) { int success; int count; int max_count = 10000; MiniServerSockArray *miniSocket; ThreadPoolJob job; if( gMServState != MSERV_IDLE ) { return DLNA_E_INTERNAL_ERROR; // miniserver running } miniSocket = ( MiniServerSockArray * ) malloc( sizeof( MiniServerSockArray ) ); if( miniSocket == NULL ) return DLNA_E_OUTOF_MEMORY; if( ( success = get_miniserver_sockets( miniSocket, listen_port ) ) != DLNA_E_SUCCESS ) { free( miniSocket ); return success; } if( ( success = get_ssdp_sockets( miniSocket ) ) != DLNA_E_SUCCESS ) { shutdown( miniSocket->miniServerSock, SD_BOTH ); dlnaCloseSocket( miniSocket->miniServerSock ); shutdown( miniSocket->miniServerStopSock, SD_BOTH ); dlnaCloseSocket( miniSocket->miniServerStopSock ); free( miniSocket ); return success; } TPJobInit( &job, ( start_routine ) RunMiniServer, ( void * )miniSocket ); TPJobSetPriority( &job, MED_PRIORITY ); TPJobSetFreeFunction( &job, ( free_routine ) free ); success = ThreadPoolAddPersistent( &gMiniServerThreadPool, &job, NULL ); if( success < 0 ) { shutdown( miniSocket->miniServerSock, SD_BOTH ); dlnaCloseSocket( miniSocket->miniServerSock ); shutdown( miniSocket->miniServerStopSock, SD_BOTH ); dlnaCloseSocket( miniSocket->miniServerStopSock ); shutdown( miniSocket->ssdpSock, SD_BOTH ); dlnaCloseSocket( miniSocket->ssdpSock ); #ifdef INCLUDE_CLIENT_APIS shutdown( miniSocket->ssdpReqSock, SD_BOTH ); dlnaCloseSocket( miniSocket->ssdpReqSock ); #endif return DLNA_E_OUTOF_MEMORY; } // wait for miniserver to start count = 0; while( gMServState != MSERV_RUNNING && count < max_count ) { usleep( 50 * 1000 ); // 0.05s count++; } // taking too long to start that thread if( count >= max_count ) { shutdown( miniSocket->miniServerSock, SD_BOTH ); dlnaCloseSocket( miniSocket->miniServerSock ); shutdown( miniSocket->miniServerStopSock, SD_BOTH ); dlnaCloseSocket( miniSocket->miniServerStopSock ); shutdown( miniSocket->ssdpSock, SD_BOTH ); dlnaCloseSocket( miniSocket->ssdpSock ); #ifdef INCLUDE_CLIENT_APIS shutdown( miniSocket->ssdpReqSock, SD_BOTH ); dlnaCloseSocket( miniSocket->ssdpReqSock ); #endif return DLNA_E_INTERNAL_ERROR; } return miniSocket->miniServerPort; }
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) && IsBoundAddress(((struct sockaddr_in *)&__ss)->sin_addr.s_addr)) { 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); }
/************************************************************************ * Function : ssdp_handle_ctrlpt_msg * * Parameters: * IN http_message_t* hmsg: SSDP message from the device * IN struct sockaddr_in* dest_addr: Address of the device * IN xboolean timeout: timeout kept by the control point while sending * search message * IN void* cookie: Cookie stored by the control point application. * This cookie will be returned to the control point * in the callback * * Description: * This function handles the ssdp messages from the devices. These * messages includes the search replies, advertisement of device coming * alive and bye byes. * * Returns: void * ***************************************************************************/ void ssdp_handle_ctrlpt_msg( IN http_message_t * hmsg, IN struct sockaddr_in *dest_addr, IN xboolean timeout, // only in search reply IN void *cookie ) // only in search reply { int handle; struct Handle_Info *ctrlpt_info = NULL; memptr hdr_value; xboolean is_byebye; // byebye or alive struct Upnp_Discovery param; SsdpEvent event; xboolean nt_found, usn_found, st_found; char save_char; Upnp_EventType event_type; Upnp_FunPtr ctrlpt_callback; void *ctrlpt_cookie; ListNode *node = NULL; SsdpSearchArg *searchArg = NULL; int matched = 0; ResultData *threadData; ThreadPoolJob job; // we are assuming that there can be only one client supported at a time HandleLock( ); if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { HandleUnlock( ); return; } // copy ctrlpt_callback = ctrlpt_info->Callback; ctrlpt_cookie = ctrlpt_info->Cookie; HandleUnlock( ); // search timeout if( timeout ) { ctrlpt_callback( UPNP_DISCOVERY_SEARCH_TIMEOUT, NULL, cookie ); return; } param.ErrCode = UPNP_E_SUCCESS; // MAX-AGE param.Expires = -1; // assume error if( httpmsg_find_hdr( hmsg, HDR_CACHE_CONTROL, &hdr_value ) != NULL ) { matchstr( hdr_value.buf, hdr_value.length, "%imax-age = %d%0", ¶m.Expires ); } // DATE param.Date[0] = '\0'; if( httpmsg_find_hdr( hmsg, HDR_DATE, &hdr_value ) != NULL ) { linecopylen( param.Date, hdr_value.buf, hdr_value.length ); } // dest addr param.DestAddr = dest_addr; // EXT param.Ext[0] = '\0'; if( httpmsg_find_hdr( hmsg, HDR_EXT, &hdr_value ) != NULL ) { linecopylen( param.Ext, hdr_value.buf, hdr_value.length ); } // LOCATION param.Location[0] = '\0'; if( httpmsg_find_hdr( hmsg, HDR_LOCATION, &hdr_value ) != NULL ) { linecopylen( param.Location, hdr_value.buf, hdr_value.length ); } // SERVER / USER-AGENT param.Os[0] = '\0'; if( httpmsg_find_hdr( hmsg, HDR_SERVER, &hdr_value ) != NULL || httpmsg_find_hdr( hmsg, HDR_USER_AGENT, &hdr_value ) != NULL ) { linecopylen( param.Os, hdr_value.buf, hdr_value.length ); } // clear everything param.DeviceId[0] = '\0'; param.DeviceType[0] = '\0'; param.ServiceType[0] = '\0'; param.ServiceVer[0] = '\0'; // not used; version is in ServiceType event.UDN[0] = '\0'; event.DeviceType[0] = '\0'; event.ServiceType[0] = '\0'; nt_found = FALSE; if( httpmsg_find_hdr( hmsg, HDR_NT, &hdr_value ) != NULL ) { save_char = hdr_value.buf[hdr_value.length]; hdr_value.buf[hdr_value.length] = '\0'; nt_found = ( ssdp_request_type( hdr_value.buf, &event ) == 0 ); hdr_value.buf[hdr_value.length] = save_char; } usn_found = FALSE; if( httpmsg_find_hdr( hmsg, HDR_USN, &hdr_value ) != NULL ) { save_char = hdr_value.buf[hdr_value.length]; hdr_value.buf[hdr_value.length] = '\0'; usn_found = ( unique_service_name( hdr_value.buf, &event ) == 0 ); hdr_value.buf[hdr_value.length] = save_char; } if( nt_found || usn_found ) { strcpy( param.DeviceId, event.UDN ); strcpy( param.DeviceType, event.DeviceType ); strcpy( param.ServiceType, event.ServiceType ); } // ADVERT. OR BYEBYE if( hmsg->is_request ) { // use NTS hdr to determine advert., or byebye // if( httpmsg_find_hdr( hmsg, HDR_NTS, &hdr_value ) == NULL ) { return; // error; NTS header not found } if( memptr_cmp( &hdr_value, "ssdp:alive" ) == 0 ) { is_byebye = FALSE; } else if( memptr_cmp( &hdr_value, "ssdp:byebye" ) == 0 ) { is_byebye = TRUE; } else { return; // bad value } if( is_byebye ) { // check device byebye if( !nt_found || !usn_found ) { return; // bad byebye } event_type = UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE; } else { // check advertisement // .Expires is valid if positive. This is for testing // only. Expires should be greater than 1800 (30 mins) if( !nt_found || !usn_found || strlen( param.Location ) == 0 || param.Expires <= 0 ) { return; // bad advertisement } event_type = UPNP_DISCOVERY_ADVERTISEMENT_ALIVE; } // call callback ctrlpt_callback( event_type, ¶m, ctrlpt_cookie ); } else // reply (to a SEARCH) { // only checking to see if there is a valid ST header st_found = FALSE; if( httpmsg_find_hdr( hmsg, HDR_ST, &hdr_value ) != NULL ) { save_char = hdr_value.buf[hdr_value.length]; hdr_value.buf[hdr_value.length] = '\0'; st_found = ssdp_request_type( hdr_value.buf, &event ) == 0; hdr_value.buf[hdr_value.length] = save_char; } if( hmsg->status_code != HTTP_OK || param.Expires <= 0 || strlen( param.Location ) == 0 || !usn_found || !st_found ) { return; // bad reply } //check each current search HandleLock( ); if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { HandleUnlock( ); return; } node = ListHead( &ctrlpt_info->SsdpSearchList ); //temporary add null termination //save_char = hdr_value.buf[ hdr_value.length ]; //hdr_value.buf[ hdr_value.length ] = '\0'; while( node != NULL ) { searchArg = node->item; matched = 0; //check for match of ST header and search target switch ( searchArg->requestType ) { case SSDP_ALL: { matched = 1; break; } case SSDP_ROOTDEVICE: { matched = ( event.RequestType == SSDP_ROOTDEVICE ); break; } case SSDP_DEVICEUDN: { matched = !( strncmp( searchArg->searchTarget, hdr_value.buf, hdr_value.length ) ); break; } case SSDP_DEVICETYPE: { int m = min( hdr_value.length, strlen( searchArg->searchTarget ) ); matched = !( strncmp( searchArg->searchTarget, hdr_value.buf, m ) ); break; } case SSDP_SERVICE: { int m = min( hdr_value.length, strlen( searchArg->searchTarget ) ); matched = !( strncmp( searchArg->searchTarget, hdr_value.buf, m ) ); break; } default: { matched = 0; break; } } if( matched ) { //schedule call back threadData = ( ResultData * ) malloc( sizeof( ResultData ) ); if( threadData != NULL ) { threadData->param = param; threadData->cookie = searchArg->cookie; threadData->ctrlpt_callback = ctrlpt_callback; TPJobInit( &job, ( start_routine ) send_search_result, threadData ); TPJobSetPriority( &job, MED_PRIORITY ); TPJobSetFreeFunction( &job, ( free_routine ) free ); ThreadPoolAdd( &gRecvThreadPool, &job, NULL ); } } node = ListNext( &ctrlpt_info->SsdpSearchList, node ); } HandleUnlock( ); //ctrlpt_callback( UPNP_DISCOVERY_SEARCH_RESULT, ¶m, cookie ); } }
/* We take ownership of propertySet and will free it */ static int genaInitNotifyCommon( UpnpDevice_Handle device_handle, char *UDN, char *servId, DOMString propertySet, const Upnp_SID sid) { int ret = GENA_SUCCESS; int line = 0; int *reference_count = NULL; char *UDN_copy = NULL; char *servId_copy = NULL; char *headers = NULL; notify_thread_struct *thread_struct = NULL; subscription *sub = NULL; service_info *service = NULL; struct Handle_Info *handle_info; ThreadPoolJob *job = NULL; UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA BEGIN INITIAL NOTIFY COMMON"); job = (ThreadPoolJob *)malloc(sizeof(ThreadPoolJob)); if (job == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } memset(job, 0, sizeof(ThreadPoolJob)); reference_count = (int *)malloc(sizeof (int)); if (reference_count == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } *reference_count = 0; UDN_copy = strdup(UDN); if (UDN_copy == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } servId_copy = strdup(servId); if (servId_copy == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } HandleLock(); if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) { line = __LINE__; ret = GENA_E_BAD_HANDLE; goto ExitFunction; } service = FindServiceId(&handle_info->ServiceTable, servId, UDN); if (service == NULL) { line = __LINE__; ret = GENA_E_BAD_SERVICE; goto ExitFunction; } UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "FOUND SERVICE IN INIT NOTFY: UDN %s, ServID: %s", UDN, servId); sub = GetSubscriptionSID(sid, service); if (sub == NULL || sub->active) { line = __LINE__; ret = GENA_E_BAD_SID; goto ExitFunction; } UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, "FOUND SUBSCRIPTION IN INIT NOTIFY: SID %s", sid); sub->active = 1; headers = AllocGenaHeaders(propertySet); if (headers == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } /* schedule thread for initial notification */ thread_struct = (notify_thread_struct *)malloc(sizeof (notify_thread_struct)); if (thread_struct == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; } else { *reference_count = 1; thread_struct->servId = servId_copy; thread_struct->UDN = UDN_copy; thread_struct->headers = headers; thread_struct->propertySet = propertySet; memset(thread_struct->sid, 0, sizeof(thread_struct->sid)); strncpy(thread_struct->sid, sid, sizeof(thread_struct->sid) - 1); thread_struct->ctime = time(0); thread_struct->reference_count = reference_count; thread_struct->device_handle = device_handle; TPJobInit(job, (start_routine)genaNotifyThread, thread_struct); TPJobSetFreeFunction(job, (free_routine)free_notify_struct); TPJobSetPriority(job, MED_PRIORITY); ret = ThreadPoolAdd(&gSendThreadPool, job, NULL); if (ret != 0) { if (ret == EOUTOFMEM) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; } } else { ListNode *node = ListAddTail(&sub->outgoing, job); if (node != NULL) { ((ThreadPoolJob *)node->item)->jobId = STALE_JOBID; line = __LINE__; ret = GENA_SUCCESS; } else { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; } } } ExitFunction: if (ret != GENA_SUCCESS) { free(job); free(thread_struct); free(headers); ixmlFreeDOMString(propertySet); free(servId_copy); free(UDN_copy); free(reference_count); } HandleUnlock(); UpnpPrintf(UPNP_INFO, GENA, __FILE__, line, "GENA END INITIAL NOTIFY COMMON, ret = %d", ret); return ret; }
int StartMiniServer( /*! [in,out] Port on which the server listens for incoming IPv4 * connections. */ uint16_t *listen_port4, /*! [in,out] Port on which the server listens for incoming IPv6 * connections. */ uint16_t *listen_port6) { int ret_code; int count; int max_count = 10000; MiniServerSockArray *miniSocket; ThreadPoolJob job; memset(&job, 0, sizeof(job)); switch (gMServState) { case MSERV_IDLE: break; default: /* miniserver running. */ return UPNP_E_INTERNAL_ERROR; } miniSocket = (MiniServerSockArray *)malloc( sizeof (MiniServerSockArray)); if (!miniSocket) { return UPNP_E_OUTOF_MEMORY; } InitMiniServerSockArray(miniSocket); #ifdef INTERNAL_WEB_SERVER /* V4 and V6 http listeners. */ ret_code = get_miniserver_sockets( miniSocket, *listen_port4, *listen_port6); if (ret_code != UPNP_E_SUCCESS) { free(miniSocket); return ret_code; } #endif /* Stop socket (To end miniserver processing). */ ret_code = get_miniserver_stopsock(miniSocket); if (ret_code != UPNP_E_SUCCESS) { sock_close(miniSocket->miniServerSock4); sock_close(miniSocket->miniServerSock6); free(miniSocket); return ret_code; } /* SSDP socket for discovery/advertising. */ ret_code = get_ssdp_sockets(miniSocket); if (ret_code != UPNP_E_SUCCESS) { sock_close(miniSocket->miniServerSock4); sock_close(miniSocket->miniServerSock6); sock_close(miniSocket->miniServerStopSock); free(miniSocket); return ret_code; } TPJobInit(&job, (start_routine)RunMiniServer, (void *)miniSocket); TPJobSetPriority(&job, MED_PRIORITY); TPJobSetFreeFunction(&job, (free_routine)free); ret_code = ThreadPoolAddPersistent(&gMiniServerThreadPool, &job, NULL); if (ret_code < 0) { sock_close(miniSocket->miniServerSock4); sock_close(miniSocket->miniServerSock6); sock_close(miniSocket->miniServerStopSock); sock_close(miniSocket->ssdpSock4); sock_close(miniSocket->ssdpSock6); sock_close(miniSocket->ssdpSock6UlaGua); #ifdef INCLUDE_CLIENT_APIS sock_close(miniSocket->ssdpReqSock4); sock_close(miniSocket->ssdpReqSock6); #endif /* INCLUDE_CLIENT_APIS */ return UPNP_E_OUTOF_MEMORY; } /* Wait for miniserver to start. */ count = 0; while (gMServState != (MiniServerState)MSERV_RUNNING && count < max_count) { /* 0.05s */ usleep(50u * 1000u); count++; } if (count >= max_count) { /* Took it too long to start that thread. */ sock_close(miniSocket->miniServerSock4); sock_close(miniSocket->miniServerSock6); sock_close(miniSocket->miniServerStopSock); sock_close(miniSocket->ssdpSock4); sock_close(miniSocket->ssdpSock6); sock_close(miniSocket->ssdpSock6UlaGua); #ifdef INCLUDE_CLIENT_APIS sock_close(miniSocket->ssdpReqSock4); sock_close(miniSocket->ssdpReqSock6); #endif /* INCLUDE_CLIENT_APIS */ return UPNP_E_INTERNAL_ERROR; } #ifdef INTERNAL_WEB_SERVER *listen_port4 = miniSocket->miniServerPort4; *listen_port6 = miniSocket->miniServerPort6; #endif return UPNP_E_SUCCESS; }
/* We take ownership of propertySet and will free it */ static int genaNotifyAllCommon( UpnpDevice_Handle device_handle, char *UDN, char *servId, DOMString propertySet) { int ret = GENA_SUCCESS; int line = 0; int *reference_count = NULL; char *UDN_copy = NULL; char *servId_copy = NULL; char *headers = NULL; notify_thread_struct *thread_struct = NULL; subscription *finger = NULL; service_info *service = NULL; struct Handle_Info *handle_info; UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA BEGIN NOTIFY ALL COMMON"); /* Keep this allocation first */ reference_count = (int *)malloc(sizeof (int)); if (reference_count == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } *reference_count = 0; UDN_copy = strdup(UDN); if (UDN_copy == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } servId_copy = strdup(servId); if( servId_copy == NULL ) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } headers = AllocGenaHeaders(propertySet); if (headers == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; goto ExitFunction; } HandleLock(); if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) { line = __LINE__; ret = GENA_E_BAD_HANDLE; } else { service = FindServiceId(&handle_info->ServiceTable, servId, UDN); if (service != NULL) { finger = GetFirstSubscription(service); while (finger) { ThreadPoolJob *job = NULL; ListNode *node; thread_struct = (notify_thread_struct *)malloc(sizeof (notify_thread_struct)); if (thread_struct == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; break; } (*reference_count)++; thread_struct->reference_count = reference_count; thread_struct->UDN = UDN_copy; thread_struct->servId = servId_copy; thread_struct->headers = headers; thread_struct->propertySet = propertySet; memset(thread_struct->sid, 0, sizeof(thread_struct->sid)); strncpy(thread_struct->sid, finger->sid, sizeof(thread_struct->sid) - 1); thread_struct->ctime = time(0); thread_struct->device_handle = device_handle; maybeDiscardEvents(&finger->outgoing); job = (ThreadPoolJob *)malloc(sizeof(ThreadPoolJob)); if (job == NULL) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; break; } memset(job, 0, sizeof(ThreadPoolJob)); TPJobInit(job, (start_routine)genaNotifyThread, thread_struct); TPJobSetFreeFunction(job, (free_routine)free_notify_struct); TPJobSetPriority(job, MED_PRIORITY); node = ListAddTail(&finger->outgoing, job); /* If there is only one element on the list (which we just added), need to kickstart the threadpool */ if (ListSize(&finger->outgoing) == 1) { ret = ThreadPoolAdd(&gSendThreadPool, job, NULL); if (ret != 0) { line = __LINE__; if (ret == EOUTOFMEM) { line = __LINE__; ret = UPNP_E_OUTOF_MEMORY; } break; } if (node) { ((ThreadPoolJob *)(node->item))->jobId = STALE_JOBID; } } finger = GetNextSubscription(service, finger); } } else { line = __LINE__; ret = GENA_E_BAD_SERVICE; } } ExitFunction: /* The only case where we want to free memory here is if the struct was never queued. Else, let the normal cleanup take place. reference_count is allocated first so it's ok to do nothing if it's 0 */ if (reference_count && *reference_count == 0) { free(headers); ixmlFreeDOMString(propertySet); free(servId_copy); free(UDN_copy); free(reference_count); } HandleUnlock(); UpnpPrintf(UPNP_INFO, GENA, __FILE__, line, "GENA END NOTIFY ALL COMMON, ret = %d", ret); return ret; }
/*! * \brief Schedules a job to renew the subscription just before time out. * * \return GENA_E_SUCCESS if successful, otherwise returns the appropriate * error code. */ static int ScheduleGenaAutoRenew( /*! [in] Handle that also contains the subscription list. */ IN int client_handle, /*! [in] The time out value of the subscription. */ IN int TimeOut, /*! [in] Subscription being renewed. */ IN ClientSubscription *sub) { struct Upnp_Event_Subscribe *RenewEventStruct = NULL; upnp_timeout *RenewEvent = NULL; int return_code = GENA_SUCCESS; ThreadPoolJob job; const UpnpString *tmpSID = UpnpClientSubscription_get_SID(sub); const UpnpString *tmpEventURL = UpnpClientSubscription_get_EventURL(sub); memset(&job, 0, sizeof(job)); if (TimeOut == UPNP_INFINITE) { return_code = GENA_SUCCESS; goto end_function; } RenewEventStruct = (struct Upnp_Event_Subscribe *)malloc(sizeof (struct Upnp_Event_Subscribe)); if (RenewEventStruct == NULL) { return_code = UPNP_E_OUTOF_MEMORY; goto end_function; } memset(RenewEventStruct, 0, sizeof(struct Upnp_Event_Subscribe)); RenewEvent = (upnp_timeout *) malloc(sizeof(upnp_timeout)); if (RenewEvent == NULL) { free(RenewEventStruct); return_code = UPNP_E_OUTOF_MEMORY; goto end_function; } memset(RenewEvent, 0, sizeof(upnp_timeout)); /* schedule expire event */ RenewEventStruct->ErrCode = UPNP_E_SUCCESS; RenewEventStruct->TimeOut = TimeOut; strncpy(RenewEventStruct->Sid, UpnpString_get_String(tmpSID), sizeof(RenewEventStruct->Sid) - 1); strncpy(RenewEventStruct->PublisherUrl, UpnpString_get_String(tmpEventURL), NAME_SIZE - 1); /* RenewEvent->EventType=UPNP_EVENT_SUBSCRIPTION_EXPIRE; */ RenewEvent->handle = client_handle; RenewEvent->Event = RenewEventStruct; TPJobInit(&job, (start_routine) GenaAutoRenewSubscription, RenewEvent); TPJobSetFreeFunction(&job, (free_routine)free_upnp_timeout); TPJobSetPriority(&job, MED_PRIORITY); /* Schedule the job */ return_code = TimerThreadSchedule( &gTimerThread, TimeOut - AUTO_RENEW_TIME, REL_SEC, &job, SHORT_TERM, &(RenewEvent->eventId)); if (return_code != UPNP_E_SUCCESS) { free(RenewEvent); free(RenewEventStruct); goto end_function; } UpnpClientSubscription_set_RenewEventId(sub, RenewEvent->eventId); return_code = GENA_SUCCESS; end_function: return return_code; }