/*! * \brief Does some quick checking of the ssdp msg. * * \return TRUE if msg is valid, else FALSE. */ static UPNP_INLINE int valid_ssdp_msg( /*! [in] ssdp_thread_data structure. This structure contains SSDP * request message. */ http_message_t * hmsg) { memptr hdr_value; /* check for valid methods - NOTIFY or M-SEARCH */ if (hmsg->method != (http_method_t)HTTPMETHOD_NOTIFY && hmsg->method != (http_method_t)HTTPMETHOD_MSEARCH && hmsg->request_method != (http_method_t)HTTPMETHOD_MSEARCH) { return FALSE; } if (hmsg->request_method != (http_method_t)HTTPMETHOD_MSEARCH) { /* check PATH == "*" */ if (hmsg->uri.type != (enum uriType)RELATIVE || strncmp("*", hmsg->uri.pathquery.buff, hmsg->uri.pathquery.size) != 0) { return FALSE; } /* check HOST header */ if (httpmsg_find_hdr(hmsg, HDR_HOST, &hdr_value) == NULL || (memptr_cmp(&hdr_value, "239.255.255.250:1900") != 0 && memptr_cmp(&hdr_value, "[FF02::C]:1900") != 0 && memptr_cmp(&hdr_value, "[ff02::c]:1900") != 0 && memptr_cmp(&hdr_value, "[FF05::C]:1900") != 0 && memptr_cmp(&hdr_value, "[ff05::c]:1900") != 0)) { UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__, "Invalid HOST header from SSDP message\n"); return FALSE; } } /* passed quick check */ return TRUE; }
/************************************************************************ * Function : map_str_to_int * * Parameters : * IN const char* name ; string containing the name to be matched * IN size_t name_len ; size of the string to be matched * IN str_int_entry* table ; table of entries that need to be * matched. * IN int num_entries ; number of entries in the table that need * to be searched. * IN xboolean case_sensitive ; whether the case should be case * sensitive or not * * Description : Match the given name with names from the entries in the * table. Returns the index of the table when the entry is found. * * Return : int ; * index - On Success * -1 - On failure * * Note : ************************************************************************/ int map_str_to_int( IN const char *name, IN size_t name_len, IN str_int_entry * table, IN int num_entries, IN xboolean case_sensitive ) { int top, mid, bot; int cmp; memptr name_ptr; name_ptr.buf = ( char * )name; name_ptr.length = name_len; top = 0; bot = num_entries - 1; while( top <= bot ) { mid = ( top + bot ) / 2; if( case_sensitive ) { //cmp = strcmp( name, table[mid].name ); cmp = memptr_cmp( &name_ptr, table[mid].name ); } else { //cmp = strcasecmp( name, table[mid].name ); cmp = memptr_cmp_nocase( &name_ptr, table[mid].name ); } if( cmp > 0 ) { top = mid + 1; // look below mid } else if( cmp < 0 ) { bot = mid - 1; // look above mid } else // cmp == 0 { return mid; // match; return table index } } return -1; // header name not found }
/**************************************************************************** * Function : get_request_type * * Parameters : * IN http_message_t* request : HTTP request * OUT memptr* action_name : SOAP action name * * Description : This function retrives the name of the SOAP action * * Return : int * 0 if successful else returns appropriate error. * Note : ****************************************************************************/ static XINLINE int get_request_type( IN http_message_t * request, OUT memptr * action_name ) { memptr value; memptr ns_value, dummy_quote; http_header_t *hdr; char save_char; char *s; membuffer soap_action_name; // find soapaction header // if( request->method == SOAPMETHOD_POST ) { if( httpmsg_find_hdr( request, HDR_SOAPACTION, &value ) == NULL ) { return SREQ_HDR_NOT_FOUND; } } else // M-POST { // get NS value from MAN header hdr = httpmsg_find_hdr( request, HDR_MAN, &value ); if( hdr == NULL ) { return SREQ_HDR_NOT_FOUND; } if( matchstr( value.buf, value.length, "%q%i ; ns = %s", &dummy_quote, &ns_value ) != 0 ) { return SREQ_BAD_HDR_FORMAT; } // create soapaction name header membuffer_init( &soap_action_name ); if( ( membuffer_assign( &soap_action_name, ns_value.buf, ns_value.length ) == UPNP_E_OUTOF_MEMORY ) || ( membuffer_append_str( &soap_action_name, "-SOAPACTION" ) == UPNP_E_OUTOF_MEMORY ) ) { membuffer_destroy( &soap_action_name ); return UPNP_E_OUTOF_MEMORY; } hdr = httpmsg_find_hdr_str( request, soap_action_name.buf ); membuffer_destroy( &soap_action_name ); if( hdr == NULL ) { return SREQ_HDR_NOT_FOUND; } value.buf = hdr->value.buf; value.length = hdr->value.length; } // determine type // save_char = value.buf[value.length]; value.buf[value.length] = '\0'; s = strchr( value.buf, '#' ); if( s == NULL ) { value.buf[value.length] = save_char; return SREQ_BAD_HDR_FORMAT; } s++; // move to value if( matchstr( s, value.length - ( s - value.buf ), "%s", action_name ) != PARSE_OK ) { value.buf[value.length] = save_char; return SREQ_BAD_HDR_FORMAT; } // action name or variable ? if( memptr_cmp( action_name, "QueryStateVariable" ) == 0 ) { // query variable action_name->buf = NULL; action_name->length = 0; } value.buf[value.length] = save_char; // restore return 0; }
/************************************************************************ * Function : gena_process_notification_event * * Parameters: * IN SOCKINFO *info: Socket structure containing the device socket * information * IN http_message_t* event: The http message contains the GENA * notification * * Description: * This function processes NOTIFY events that are sent by devices. * called by genacallback() * * Returns: void * * Note : called by genacallback() ****************************************************************************/ void gena_process_notification_event( IN SOCKINFO * info, IN http_message_t * event ) { struct Upnp_Event event_struct; int eventKey; token sid; client_subscription *subscription; IXML_Document *ChangedVars; struct Handle_Info *handle_info; void *cookie; Upnp_FunPtr callback; UpnpClient_Handle client_handle; memptr sid_hdr; memptr nt_hdr, nts_hdr; memptr seq_hdr; // get SID if( httpmsg_find_hdr( event, HDR_SID, &sid_hdr ) == NULL ) { error_respond( info, HTTP_PRECONDITION_FAILED, event ); return; } sid.buff = sid_hdr.buf; sid.size = sid_hdr.length; // get event key if( httpmsg_find_hdr( event, HDR_SEQ, &seq_hdr ) == NULL || matchstr( seq_hdr.buf, seq_hdr.length, "%d%0", &eventKey ) != PARSE_OK ) { error_respond( info, HTTP_BAD_REQUEST, event ); return; } // get NT and NTS headers if( httpmsg_find_hdr( event, HDR_NT, &nt_hdr ) == NULL || httpmsg_find_hdr( event, HDR_NTS, &nts_hdr ) == NULL ) { error_respond( info, HTTP_BAD_REQUEST, event ); return; } // verify NT and NTS headers if( memptr_cmp( &nt_hdr, "upnp:event" ) != 0 || memptr_cmp( &nts_hdr, "upnp:propchange" ) != 0 ) { error_respond( info, HTTP_PRECONDITION_FAILED, event ); return; } // parse the content (should be XML) if( !has_xml_content_type( event ) || event->msg.length == 0 || ( ixmlParseBufferEx( event->entity.buf, &ChangedVars ) ) != IXML_SUCCESS ) { error_respond( info, HTTP_BAD_REQUEST, event ); return; } HandleLock( ); // get client info if( GetClientHandleInfo( &client_handle, &handle_info ) != HND_CLIENT ) { error_respond( info, HTTP_PRECONDITION_FAILED, event ); HandleUnlock( ); ixmlDocument_free( ChangedVars ); return; } // get subscription based on SID if( ( subscription = GetClientSubActualSID( handle_info->ClientSubList, &sid ) ) == NULL ) { if( eventKey == 0 ) { // wait until we've finished processing a subscription // (if we are in the middle) // this is to avoid mistakenly rejecting the first event if we // receive it before the subscription response HandleUnlock( ); // try and get Subscription Lock // (in case we are in the process of subscribing) SubscribeLock( ); // get HandleLock again HandleLock( ); if( GetClientHandleInfo( &client_handle, &handle_info ) != HND_CLIENT ) { error_respond( info, HTTP_PRECONDITION_FAILED, event ); SubscribeUnlock( ); HandleUnlock( ); ixmlDocument_free( ChangedVars ); return; } if( ( subscription = GetClientSubActualSID( handle_info->ClientSubList, &sid ) ) == NULL ) { error_respond( info, HTTP_PRECONDITION_FAILED, event ); SubscribeUnlock( ); HandleUnlock( ); ixmlDocument_free( ChangedVars ); return; } SubscribeUnlock( ); } else { error_respond( info, HTTP_PRECONDITION_FAILED, event ); HandleUnlock( ); ixmlDocument_free( ChangedVars ); return; } } error_respond( info, HTTP_OK, event ); // success // fill event struct strcpy( event_struct.Sid, subscription->sid ); event_struct.EventKey = eventKey; event_struct.ChangedVariables = ChangedVars; // copy callback callback = handle_info->Callback; cookie = handle_info->Cookie; HandleUnlock( ); // make callback with event struct // In future, should find a way of mainting // that the handle is not unregistered in the middle of a // callback callback( UPNP_EVENT_RECEIVED, &event_struct, cookie ); ixmlDocument_free( ChangedVars ); }
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); }
/************************************************************************ * 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 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 : 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 ); } }
/*! * \brief This function retrives the name of the SOAP action. * * \return 0 if successful else returns appropriate error. */ static UPNP_INLINE int get_request_type( /*! [in] HTTP request. */ http_message_t *request, /*! [out] SOAP action name. */ memptr *action_name) { memptr value; memptr ns_value, dummy_quote; http_header_t *hdr; char save_char; char *s; membuffer soap_action_name; size_t n; /* find soapaction header */ if (request->method == SOAPMETHOD_POST) { if (!httpmsg_find_hdr(request, HDR_SOAPACTION, &value)) return SREQ_HDR_NOT_FOUND; } else { /* M-POST */ /* get NS value from MAN header */ hdr = httpmsg_find_hdr(request, HDR_MAN, &value); if (hdr == NULL) return SREQ_HDR_NOT_FOUND; if (matchstr(value.buf, value.length, "%q%i ; ns = %s", &dummy_quote, &ns_value) != 0) return SREQ_BAD_HDR_FORMAT; /* create soapaction name header */ membuffer_init(&soap_action_name); if (membuffer_assign(&soap_action_name, ns_value.buf, ns_value.length) == UPNP_E_OUTOF_MEMORY || membuffer_append_str(&soap_action_name, "-SOAPACTION") == UPNP_E_OUTOF_MEMORY) { membuffer_destroy(&soap_action_name); return UPNP_E_OUTOF_MEMORY; } hdr = httpmsg_find_hdr_str(request, soap_action_name.buf); membuffer_destroy(&soap_action_name); if (!hdr) return SREQ_HDR_NOT_FOUND; value.buf = hdr->value.buf; value.length = hdr->value.length; } /* determine type */ save_char = value.buf[value.length]; value.buf[value.length] = '\0'; s = strchr(value.buf, '#'); if (s == NULL) { value.buf[value.length] = save_char; return SREQ_BAD_HDR_FORMAT; } /* move to value */ s++; n = value.length - (size_t)(s - value.buf); if (matchstr(s, n, "%s", action_name) != PARSE_OK) { value.buf[value.length] = save_char; return SREQ_BAD_HDR_FORMAT; } /* action name or variable ? */ if (memptr_cmp(action_name, "QueryStateVariable") == 0) { /* query variable */ action_name->buf = NULL; action_name->length = 0; } /* restore */ value.buf[value.length] = save_char; return 0; }
void gena_process_notification_event( SOCKINFO *info, http_message_t *event) { struct Upnp_Event event_struct; IXML_Document *ChangedVars = NULL; int eventKey; token sid; ClientSubscription *subscription = NULL; struct Handle_Info *handle_info; void *cookie; Upnp_FunPtr callback; UpnpClient_Handle client_handle; const UpnpString *tmpSID = NULL; memptr sid_hdr; memptr nt_hdr, nts_hdr; memptr seq_hdr; /* get SID */ if (httpmsg_find_hdr(event, HDR_SID, &sid_hdr) == NULL) { error_respond(info, HTTP_PRECONDITION_FAILED, event); goto exit_function; } sid.buff = sid_hdr.buf; sid.size = sid_hdr.length; /* get event key */ if (httpmsg_find_hdr(event, HDR_SEQ, &seq_hdr) == NULL || matchstr(seq_hdr.buf, seq_hdr.length, "%d%0", &eventKey) != PARSE_OK) { error_respond( info, HTTP_BAD_REQUEST, event ); goto exit_function; } /* get NT and NTS headers */ if (httpmsg_find_hdr(event, HDR_NT, &nt_hdr) == NULL || httpmsg_find_hdr(event, HDR_NTS, &nts_hdr) == NULL) { error_respond( info, HTTP_BAD_REQUEST, event ); goto exit_function; } /* verify NT and NTS headers */ if (memptr_cmp(&nt_hdr, "upnp:event") != 0 || memptr_cmp(&nts_hdr, "upnp:propchange") != 0) { error_respond(info, HTTP_PRECONDITION_FAILED, event); goto exit_function; } /* parse the content (should be XML) */ if (!has_xml_content_type(event) || event->msg.length == 0 || ixmlParseBufferEx(event->entity.buf, &ChangedVars) != IXML_SUCCESS) { error_respond(info, HTTP_BAD_REQUEST, event); goto exit_function; } HandleLock(); /* get client info */ if (GetClientHandleInfo(&client_handle, &handle_info) != HND_CLIENT) { error_respond(info, HTTP_PRECONDITION_FAILED, event); HandleUnlock(); goto exit_function; } /* get subscription based on SID */ subscription = GetClientSubActualSID(handle_info->ClientSubList, &sid); if (subscription == NULL) { if (eventKey == 0) { /* wait until we've finished processing a subscription */ /* (if we are in the middle) */ /* this is to avoid mistakenly rejecting the first event if we */ /* receive it before the subscription response */ HandleUnlock(); /* try and get Subscription Lock */ /* (in case we are in the process of subscribing) */ SubscribeLock(); /* get HandleLock again */ HandleLock(); if (GetClientHandleInfo(&client_handle, &handle_info) != HND_CLIENT) { error_respond(info, HTTP_PRECONDITION_FAILED, event); SubscribeUnlock(); HandleUnlock(); goto exit_function; } subscription = GetClientSubActualSID(handle_info->ClientSubList, &sid); if (subscription == NULL) { error_respond( info, HTTP_PRECONDITION_FAILED, event ); SubscribeUnlock(); HandleUnlock(); goto exit_function; } SubscribeUnlock(); } else { error_respond( info, HTTP_PRECONDITION_FAILED, event ); HandleUnlock(); goto exit_function; } } /* success */ error_respond(info, HTTP_OK, event); /* fill event struct */ tmpSID = UpnpClientSubscription_get_SID(subscription); memset(event_struct.Sid, 0, sizeof(event_struct.Sid)); strncpy(event_struct.Sid, UpnpString_get_String(tmpSID), sizeof(event_struct.Sid) - 1); event_struct.EventKey = eventKey; event_struct.ChangedVariables = ChangedVars; /* copy callback */ callback = handle_info->Callback; cookie = handle_info->Cookie; HandleUnlock(); /* make callback with event struct */ /* In future, should find a way of mainting */ /* that the handle is not unregistered in the middle of a */ /* callback */ callback(UPNP_EVENT_RECEIVED, &event_struct, cookie); exit_function: ixmlDocument_free(ChangedVars); }