void gena_process_unsubscribe_request( SOCKINFO *info, http_message_t *request) { Upnp_SID sid; service_info *service; struct Handle_Info *handle_info; UpnpDevice_Handle device_handle; memptr temp_hdr; membuffer event_url_path; /* if a CALLBACK or NT header is present, then it is an error */ if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL || httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) { error_respond( info, HTTP_BAD_REQUEST, request ); return; } /* get SID */ if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL || temp_hdr.length > SID_SIZE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); return; } memcpy( sid, temp_hdr.buf, temp_hdr.length ); sid[temp_hdr.length] = '\0'; /* lookup service by eventURL */ membuffer_init( &event_url_path ); if( membuffer_append( &event_url_path, request->uri.pathquery.buff, request->uri.pathquery.size ) != 0 ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); return; } HandleLock(); if( GetDeviceHandleInfoForPath(event_url_path.buf, info->foreign_sockaddr.ss_family, &device_handle, &handle_info, &service) != HND_DEVICE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); membuffer_destroy( &event_url_path ); HandleUnlock(); return; } membuffer_destroy( &event_url_path ); /* validate service */ if( service == NULL || !service->active || GetSubscriptionSID( sid, service ) == NULL ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); HandleUnlock(); return; } RemoveSubscriptionSID(sid, service); error_respond(info, HTTP_OK, request); /* success */ HandleUnlock(); }
int has_xml_content_type(http_message_t *hmsg) { memptr hdr_value; assert(hmsg); /* find 'content-type' header which must have text/xml */ if (httpmsg_find_hdr(hmsg, HDR_CONTENT_TYPE, &hdr_value) && matchstr(hdr_value.buf, hdr_value.length, "%itext%w/%wxml" ) == PARSE_OK) { return TRUE; } return FALSE; }
/*! * \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; }
void gena_process_subscription_renewal_request( SOCKINFO *info, http_message_t *request) { Upnp_SID sid; subscription *sub; int time_out = 1801; service_info *service; struct Handle_Info *handle_info; UpnpDevice_Handle device_handle; memptr temp_hdr; membuffer event_url_path; memptr timeout_hdr; /* if a CALLBACK or NT header is present, then it is an error */ if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL || httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) { error_respond( info, HTTP_BAD_REQUEST, request ); return; } /* get SID */ if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL || temp_hdr.length > SID_SIZE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); return; } memcpy( sid, temp_hdr.buf, temp_hdr.length ); sid[temp_hdr.length] = '\0'; /* lookup service by eventURL */ membuffer_init( &event_url_path ); if( membuffer_append( &event_url_path, request->uri.pathquery.buff, request->uri.pathquery.size ) != 0 ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); return; } HandleLock(); if (GetDeviceHandleInfoForPath(event_url_path.buf, info->foreign_sockaddr.ss_family, &device_handle, &handle_info, &service) != HND_DEVICE ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); membuffer_destroy( &event_url_path ); HandleUnlock(); return; } membuffer_destroy( &event_url_path ); /* get subscription */ if( service == NULL || !service->active || ( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) ) { error_respond( info, HTTP_PRECONDITION_FAILED, request ); HandleUnlock(); return; } UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, "Renew request: Number of subscriptions already: %d\n " "Max Subscriptions allowed:%d\n", service->TotalSubscriptions, handle_info->MaxSubscriptions ); /* too many subscriptions */ if( handle_info->MaxSubscriptions != -1 && service->TotalSubscriptions > handle_info->MaxSubscriptions ) { error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request ); RemoveSubscriptionSID( sub->sid, service ); HandleUnlock(); return; } /* set the timeout */ if( httpmsg_find_hdr( request, HDR_TIMEOUT, &timeout_hdr ) != NULL ) { if( matchstr( timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", &time_out ) == PARSE_OK ) { /*nothing */ } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == 0 ) { time_out = -1; /* inifinite timeout */ } else { time_out = DEFAULT_TIMEOUT; /* default is > 1800 seconds */ } } /* replace infinite timeout with max timeout, if possible */ if( handle_info->MaxSubscriptionTimeOut != -1 ) { if( time_out == -1 || time_out > handle_info->MaxSubscriptionTimeOut ) { time_out = handle_info->MaxSubscriptionTimeOut; } } if( time_out == -1 ) { sub->expireTime = 0; } else { sub->expireTime = time( NULL ) + time_out; } if( respond_ok( info, time_out, sub, request ) != UPNP_E_SUCCESS ) { RemoveSubscriptionSID( sub->sid, service ); } HandleUnlock(); }
void gena_process_subscription_request( SOCKINFO *info, http_message_t *request) { UpnpSubscriptionRequest *request_struct = UpnpSubscriptionRequest_new(); Upnp_SID temp_sid; int return_code = 1; int time_out = 1801; service_info *service; subscription *sub; uuid_upnp uid; struct Handle_Info *handle_info; void *cookie; Upnp_FunPtr callback_fun; UpnpDevice_Handle device_handle; memptr nt_hdr; char *event_url_path = NULL; memptr callback_hdr; memptr timeout_hdr; int rc = 0; UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "Subscription Request Received:\n"); if (httpmsg_find_hdr(request, HDR_NT, &nt_hdr) == NULL) { error_respond(info, HTTP_BAD_REQUEST, request); goto exit_function; } /* check NT header */ /* Windows Millenium Interoperability: */ /* we accept either upnp:event, or upnp:propchange for the NT header */ if (memptr_cmp_nocase(&nt_hdr, "upnp:event") != 0) { error_respond(info, HTTP_PRECONDITION_FAILED, request); goto exit_function; } /* if a SID is present then the we have a bad request "incompatible headers" */ if (httpmsg_find_hdr(request, HDR_SID, NULL) != NULL) { error_respond(info, HTTP_BAD_REQUEST, request); goto exit_function; } /* look up service by eventURL */ event_url_path = str_alloc(request->uri.pathquery.buff, request->uri.pathquery.size); if (event_url_path == NULL) { error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request); goto exit_function; } UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "SubscriptionRequest for event URL path: %s\n", event_url_path); HandleLock(); if (GetDeviceHandleInfoForPath(event_url_path, info->foreign_sockaddr.ss_family, &device_handle, &handle_info, &service) != HND_DEVICE) { free(event_url_path); error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request); HandleUnlock(); goto exit_function; } free(event_url_path); if (service == NULL || !service->active) { error_respond(info, HTTP_NOT_FOUND, request); HandleUnlock(); goto exit_function; } UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "Subscription Request: Number of Subscriptions already %d\n " "Max Subscriptions allowed: %d\n", service->TotalSubscriptions, handle_info->MaxSubscriptions); /* too many subscriptions */ if (handle_info->MaxSubscriptions != -1 && service->TotalSubscriptions >= handle_info->MaxSubscriptions) { error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request); HandleUnlock(); goto exit_function; } /* generate new subscription */ sub = (subscription *)malloc(sizeof (subscription)); if (sub == NULL) { error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request); HandleUnlock(); goto exit_function; } sub->ToSendEventKey = 0; sub->active = 0; sub->next = NULL; sub->DeliveryURLs.size = 0; sub->DeliveryURLs.URLs = NULL; sub->DeliveryURLs.parsedURLs = NULL; if (ListInit(&sub->outgoing, 0, free) != 0) { error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request); HandleUnlock(); goto exit_function; } /* check for valid callbacks */ if (httpmsg_find_hdr( request, HDR_CALLBACK, &callback_hdr) == NULL) { error_respond(info, HTTP_PRECONDITION_FAILED, request); freeSubscriptionList(sub); HandleUnlock(); goto exit_function; } return_code = create_url_list(&callback_hdr, &sub->DeliveryURLs); if (return_code == 0) { error_respond(info, HTTP_PRECONDITION_FAILED, request); freeSubscriptionList(sub); HandleUnlock(); goto exit_function; } if (return_code == UPNP_E_OUTOF_MEMORY) { error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request); freeSubscriptionList(sub); HandleUnlock(); goto exit_function; } /* set the timeout */ if (httpmsg_find_hdr(request, HDR_TIMEOUT, &timeout_hdr) != NULL) { if (matchstr(timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", &time_out) == PARSE_OK) { /* nothing */ } else if(memptr_cmp_nocase(&timeout_hdr, "Second-infinite") == 0) { /* infinite timeout */ time_out = -1; } else { /* default is > 1800 seconds */ time_out = DEFAULT_TIMEOUT; } } /* replace infinite timeout with max timeout, if possible */ if (handle_info->MaxSubscriptionTimeOut != -1) { if (time_out == -1 || time_out > handle_info->MaxSubscriptionTimeOut) { time_out = handle_info->MaxSubscriptionTimeOut; } } if (time_out >= 0) { sub->expireTime = time(NULL) + time_out; } else { /* infinite time */ sub->expireTime = 0; } /* generate SID */ uuid_create(&uid); upnp_uuid_unpack(&uid, temp_sid); rc = snprintf(sub->sid, sizeof(sub->sid), "uuid:%s", temp_sid); /* respond OK */ if (rc < 0 || (unsigned int) rc >= sizeof(sub->sid) || (respond_ok(info, time_out, sub, request) != UPNP_E_SUCCESS)) { freeSubscriptionList(sub); HandleUnlock(); goto exit_function; } /* add to subscription list */ sub->next = service->subscriptionList; service->subscriptionList = sub; service->TotalSubscriptions++; /* finally generate callback for init table dump */ UpnpSubscriptionRequest_strcpy_ServiceId(request_struct, service->serviceId); UpnpSubscriptionRequest_strcpy_UDN(request_struct, service->UDN); UpnpSubscriptionRequest_strcpy_SID(request_struct, sub->sid); /* copy callback */ callback_fun = handle_info->Callback; cookie = handle_info->Cookie; HandleUnlock(); /* make call back with request struct */ /* in the future should find a way of mainting that the handle */ /* is not unregistered in the middle of a callback */ callback_fun(UPNP_EVENT_SUBSCRIPTION_REQUEST, request_struct, cookie); exit_function: UpnpSubscriptionRequest_delete(request_struct); }
/************************************************************************ * 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 ); }
/************************************************************************ * Function : gena_subscribe * * Parameters: * IN char *url: url of service to subscribe * INOUT int* timeout:subscription time desired (in secs) * IN char* renewal_sid:for renewal, this contains a currently h * held subscription SID. For first time * subscription, this must be NULL * OUT char** sid: SID returned by the subscription or renew msg * * Description: * This function subscribes or renew subscription * * Returns: int * return 0 if successful else returns appropriate error ***************************************************************************/ static int gena_subscribe( IN char *url, INOUT int *timeout, IN char *renewal_sid, OUT char **sid ) { int return_code; memptr sid_hdr, timeout_hdr; char timeout_str[25]; membuffer request; uri_type dest_url; http_parser_t response; *sid = NULL; // init // request timeout to string if( ( timeout == NULL ) || ( ( *timeout > 0 ) && ( *timeout < CP_MINIMUM_SUBSCRIPTION_TIME ) ) ) { sprintf( timeout_str, "%d", CP_MINIMUM_SUBSCRIPTION_TIME ); } else if( *timeout >= 0 ) { sprintf( timeout_str, "%d", *timeout ); } else { strcpy( timeout_str, "infinite" ); } // parse url return_code = http_FixStrUrl( url, strlen( url ), &dest_url ); if( return_code != 0 ) { return return_code; } // make request msg membuffer_init( &request ); request.size_inc = 30; if( renewal_sid ) { // renew subscription return_code = http_MakeMessage( &request, 1, 1, "q" "ssc" "ssc" "c", HTTPMETHOD_SUBSCRIBE, &dest_url, "SID: ", renewal_sid, "TIMEOUT: Second-", timeout_str ); } else { // subscribe return_code = http_MakeMessage( &request, 1, 1, "q" "sssdsscc", HTTPMETHOD_SUBSCRIBE, &dest_url, "CALLBACK: <http://", LOCAL_HOST, ":", LOCAL_PORT, "/>\r\n" "NT: upnp:event\r\n" "TIMEOUT: Second-", timeout_str ); } if( return_code != 0 ) { return return_code; } // send request and get reply return_code = http_RequestAndResponse( &dest_url, request.buf, request.length, HTTPMETHOD_SUBSCRIBE, HTTP_DEFAULT_TIMEOUT, &response ); membuffer_destroy( &request ); if( return_code != 0 ) { httpmsg_destroy( &response.msg ); return return_code; } if( response.msg.status_code != HTTP_OK ) { httpmsg_destroy( &response.msg ); return UPNP_E_SUBSCRIBE_UNACCEPTED; } // get SID and TIMEOUT if( httpmsg_find_hdr( &response.msg, HDR_SID, &sid_hdr ) == NULL || sid_hdr.length == 0 || httpmsg_find_hdr( &response.msg, HDR_TIMEOUT, &timeout_hdr ) == NULL || timeout_hdr.length == 0 ) { httpmsg_destroy( &response.msg ); return UPNP_E_BAD_RESPONSE; } // save timeout if( matchstr( timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", timeout ) == PARSE_OK ) { // nothing } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == 0 ) { *timeout = -1; } else { httpmsg_destroy( &response.msg ); return UPNP_E_BAD_RESPONSE; } // save SID *sid = str_alloc( sid_hdr.buf, sid_hdr.length ); if( *sid == NULL ) { httpmsg_destroy( &response.msg ); return UPNP_E_OUTOF_MEMORY; } httpmsg_destroy( &response.msg ); return UPNP_E_SUCCESS; }
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 ); )
/************************************************************************ * 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; }
/*! * \brief Checks the HTTP header of the SOAP request coming from the * control point. * * \return UPNP_E_SUCCESS if successful else returns appropriate error. */ static int check_soap_action_header( /*! [in] HTTP request. */ http_message_t * request, /*! [in] URN. */ const char *urn, /*! [out] Name of the SOAP action. */ char **actionName) { memptr header_name; http_header_t *soap_action_header = NULL; char *ns_compare = NULL; size_t tempSize = 0; int ret_code = UPNP_E_SUCCESS; char *temp_header_value = NULL; char *temp = NULL; char *temp2 = NULL; /* check soap action header */ soap_action_header = httpmsg_find_hdr(request, HDR_SOAPACTION, &header_name); if (!soap_action_header) { ret_code = UPNP_E_INVALID_ACTION; return ret_code; } if (soap_action_header->value.length <= 0) { ret_code = UPNP_E_INVALID_ACTION; return ret_code; } temp_header_value = malloc(soap_action_header->value.length + 1); if (!temp_header_value) { ret_code = UPNP_E_OUTOF_MEMORY; free(temp_header_value); return ret_code; } strncpy(temp_header_value, soap_action_header->value.buf, soap_action_header->value.length); temp_header_value[soap_action_header->value.length] = 0; temp = strchr(temp_header_value, '#'); if (!temp) { free(temp_header_value); ret_code = UPNP_E_INVALID_ACTION; return ret_code; } (*temp) = 0; /* temp make string */ /* check to see if it is Query State Variable or * Service Action */ tempSize = strlen(urn) + 2; ns_compare = malloc(tempSize); if (!ns_compare) { ret_code = UPNP_E_OUTOF_MEMORY; free(temp_header_value); return ret_code; } snprintf(ns_compare, tempSize, "\"%s", urn); /* Don't check version number, to accept a request comming on a v1 * service when publishing a v2 service */ if (strncmp(temp_header_value, ns_compare, strlen(ns_compare) - 2)) ret_code = UPNP_E_INVALID_ACTION; else { ret_code = UPNP_E_SUCCESS; temp++; temp2 = strchr(temp, '\"'); /* remove ending " if present */ if (temp2) (*temp2) = 0; if (*temp) (*actionName) = strdup(temp); if (!*actionName) ret_code = UPNP_E_OUTOF_MEMORY; } free(temp_header_value); free(ns_compare); return ret_code; }
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); }
/*! * \brief Subscribes or renew subscription. * * \return 0 if successful, otherwise returns the appropriate error code. */ static int gena_subscribe( /*! [in] URL of service to subscribe. */ IN const UpnpString *url, /*! [in,out] Subscription time desired (in secs). */ INOUT int *timeout, /*! [in] for renewal, this contains a currently held subscription SID. * For first time subscription, this must be NULL. */ IN const UpnpString *renewal_sid, /*! [out] SID returned by the subscription or renew msg. */ OUT UpnpString *sid) { int return_code; int parse_ret = 0; int local_timeout = CP_MINIMUM_SUBSCRIPTION_TIME; memptr sid_hdr; memptr timeout_hdr; char timeout_str[25]; membuffer request; uri_type dest_url; http_parser_t response; int rc = 0; UpnpString_clear(sid); /* request timeout to string */ if (timeout == NULL) { timeout = &local_timeout; } if (*timeout < 0) { memset(timeout_str, 0, sizeof(timeout_str)); strncpy(timeout_str, "infinite", sizeof(timeout_str) - 1); } else if(*timeout < CP_MINIMUM_SUBSCRIPTION_TIME) { rc = snprintf(timeout_str, sizeof(timeout_str), "%d", CP_MINIMUM_SUBSCRIPTION_TIME); } else { rc = snprintf(timeout_str, sizeof(timeout_str), "%d", *timeout); } if (rc < 0 || (unsigned int) rc >= sizeof(timeout_str)) return UPNP_E_OUTOF_MEMORY; /* parse url */ return_code = http_FixStrUrl( UpnpString_get_String(url), UpnpString_get_Length(url), &dest_url); if (return_code != 0) { return return_code; } /* make request msg */ membuffer_init(&request); request.size_inc = 30; if (renewal_sid) { /* renew subscription */ return_code = http_MakeMessage( &request, 1, 1, "q" "ssc" "sscc", HTTPMETHOD_SUBSCRIBE, &dest_url, "SID: ", UpnpString_get_String(renewal_sid), "TIMEOUT: Second-", timeout_str ); } else { /* subscribe */ if (dest_url.hostport.IPaddress.ss_family == AF_INET6) { struct sockaddr_in6* DestAddr6 = (struct sockaddr_in6*)&dest_url.hostport.IPaddress; return_code = http_MakeMessage( &request, 1, 1, "q" "sssdsc" "sc" "sscc", HTTPMETHOD_SUBSCRIBE, &dest_url, "CALLBACK: <http://[", (IN6_IS_ADDR_LINKLOCAL(&DestAddr6->sin6_addr) || strlen(gIF_IPV6_ULA_GUA) == 0) ? gIF_IPV6 : gIF_IPV6_ULA_GUA, "]:", LOCAL_PORT_V6, "/>", "NT: upnp:event", "TIMEOUT: Second-", timeout_str); } else { return_code = http_MakeMessage( &request, 1, 1, "q" "sssdsc" "sc" "sscc", HTTPMETHOD_SUBSCRIBE, &dest_url, "CALLBACK: <http://", gIF_IPV4, ":", LOCAL_PORT_V4, "/>", "NT: upnp:event", "TIMEOUT: Second-", timeout_str); } } if (return_code != 0) { return return_code; } /* send request and get reply */ return_code = http_RequestAndResponse(&dest_url, request.buf, request.length, HTTPMETHOD_SUBSCRIBE, HTTP_DEFAULT_TIMEOUT, &response); membuffer_destroy(&request); if (return_code != 0) { httpmsg_destroy(&response.msg); return return_code; } if (response.msg.status_code != HTTP_OK) { httpmsg_destroy(&response.msg); return UPNP_E_SUBSCRIBE_UNACCEPTED; } /* get SID and TIMEOUT */ if (httpmsg_find_hdr(&response.msg, HDR_SID, &sid_hdr) == NULL || sid_hdr.length == 0 || httpmsg_find_hdr( &response.msg, HDR_TIMEOUT, &timeout_hdr ) == NULL || timeout_hdr.length == 0 ) { httpmsg_destroy( &response.msg ); return UPNP_E_BAD_RESPONSE; } /* save timeout */ parse_ret = matchstr(timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", timeout); if (parse_ret == PARSE_OK) { /* nothing to do */ } else if (memptr_cmp_nocase(&timeout_hdr, "Second-infinite") == 0) { *timeout = -1; } else { httpmsg_destroy( &response.msg ); return UPNP_E_BAD_RESPONSE; } /* save SID */ UpnpString_set_StringN(sid, sid_hdr.buf, sid_hdr.length); if (UpnpString_get_String(sid) == NULL) { httpmsg_destroy(&response.msg); return UPNP_E_OUTOF_MEMORY; } httpmsg_destroy(&response.msg); return UPNP_E_SUCCESS; }
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 : 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 : check_soap_action_header * * Parameters : * IN http_message_t *request : HTTP request * IN const char *urn : * OUT char **actionName : name of the SOAP action * * Description : This function checks the HTTP header of the SOAP request * coming from the control point * * Return : static int * UPNP_E_SUCCESS if successful else returns appropriate error * * Note : ****************************************************************************/ static int check_soap_action_header( IN http_message_t * request, IN const char *urn, OUT char **actionName ) { memptr header_name; http_header_t *soap_action_header = NULL; char *ns_compare = NULL; int tempSize = 0; int ret_code = UPNP_E_SUCCESS; char *temp_header_value = NULL; char *temp = NULL; char *temp2 = NULL; //check soap action header soap_action_header = httpmsg_find_hdr( request, HDR_SOAPACTION, &header_name ); if( !soap_action_header ) { ret_code = UPNP_E_INVALID_ACTION; return ret_code; } if( soap_action_header->value.length <= 0 ) { ret_code = UPNP_E_INVALID_ACTION; return ret_code; } temp_header_value = ( char * )malloc( soap_action_header->value.length + 1 ); if( !temp_header_value ) { ret_code = UPNP_E_OUTOF_MEMORY; free( temp_header_value ); return ret_code; } strncpy( temp_header_value, soap_action_header->value.buf, soap_action_header->value.length ); temp_header_value[soap_action_header->value.length] = 0; temp = strchr( temp_header_value, '#' ); if( !temp ) { free( temp_header_value ); ret_code = UPNP_E_INVALID_ACTION; return ret_code; } ( *temp ) = 0; //temp make string //check to see if it is Query State Variable or //Service Action tempSize = strlen( urn ) + 2; ns_compare = ( char * )malloc( tempSize ); if( !ns_compare ) { ret_code = UPNP_E_OUTOF_MEMORY; free( temp_header_value ); return ret_code; } snprintf( ns_compare, tempSize, "\"%s", urn ); if( strcmp( temp_header_value, ns_compare ) ) { ret_code = UPNP_E_INVALID_ACTION; } else { ret_code = UPNP_E_SUCCESS; temp++; temp2 = strchr( temp, '\"' ); if( temp2 ) //remove ending " if present { ( *temp2 ) = 0; } if( *temp ) ( *actionName ) = strdup( temp ); if( !*actionName ) { ret_code = UPNP_E_OUTOF_MEMORY; } } free( temp_header_value ); free( ns_compare ); return ret_code; }