1/////////////////////////////////////////////////////////////////////////// 2// 3// Copyright (c) 2000-2003 Intel Corporation 4// All rights reserved. 5// 6// Redistribution and use in source and binary forms, with or without 7// modification, are permitted provided that the following conditions are met: 8// 9// * Redistributions of source code must retain the above copyright notice, 10// this list of conditions and the following disclaimer. 11// * Redistributions in binary form must reproduce the above copyright notice, 12// this list of conditions and the following disclaimer in the documentation 13// and/or other materials provided with the distribution. 14// * Neither name of Intel Corporation nor the names of its contributors 15// may be used to endorse or promote products derived from this software 16// without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 22// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 26// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29// 30/////////////////////////////////////////////////////////////////////////// 31 32#include "config.h" 33#include "util.h" 34#ifdef INCLUDE_CLIENT_APIS 35#if EXCLUDE_SSDP == 0 36 37#include "ssdplib.h" 38#include "upnpapi.h" 39#include <stdio.h> 40#include "ThreadPool.h" 41 42#include "httpparser.h" 43#include "httpreadwrite.h" 44#include "statcodes.h" 45 46#include "unixutil.h" 47 48/************************************************************************ 49* Function : send_search_result 50* 51* Parameters: 52* IN void *data: Search reply from the device 53* 54* Description: 55* This function sends a callback to the control point application with 56* a SEARCH result 57* 58* Returns: void 59* 60***************************************************************************/ 61void 62send_search_result( IN void *data ) 63{ 64 ResultData *temp = ( ResultData * ) data; 65 66 if(temp==NULL) 67 return; 68 temp->ctrlpt_callback( UPNP_DISCOVERY_SEARCH_RESULT, 69 &temp->param, temp->cookie ); 70 free( temp ); 71} 72 73/************************************************************************ 74* Function : ssdp_handle_ctrlpt_msg 75* 76* Parameters: 77* IN http_message_t* hmsg: SSDP message from the device 78* IN struct sockaddr_in* dest_addr: Address of the device 79* IN xboolean timeout: timeout kept by the control point while sending 80* search message 81* IN void* cookie: Cookie stored by the control point application. 82* This cookie will be returned to the control point 83* in the callback 84* 85* Description: 86* This function handles the ssdp messages from the devices. These 87* messages includes the search replies, advertisement of device coming 88* alive and bye byes. 89* 90* Returns: void 91* 92***************************************************************************/ 93void 94ssdp_handle_ctrlpt_msg( IN http_message_t * hmsg, 95 IN struct sockaddr_in *dest_addr, 96 IN xboolean timeout, // only in search reply 97 98 IN void *cookie ) // only in search reply 99{ 100 int handle; 101 struct Handle_Info *ctrlpt_info = NULL; 102 memptr hdr_value; 103 xboolean is_byebye; // byebye or alive 104 struct Upnp_Discovery param; 105 SsdpEvent event; 106 xboolean nt_found, 107 usn_found, 108 st_found; 109 char save_char; 110 Upnp_EventType event_type; 111 Upnp_FunPtr ctrlpt_callback; 112 void *ctrlpt_cookie; 113 ListNode *node = NULL; 114 SsdpSearchArg *searchArg = NULL; 115 int matched = 0; 116 ResultData *threadData; 117 ThreadPoolJob job; 118 119 // we are assuming that there can be only one client supported at a time 120 121 HandleLock( ); 122 123 if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { 124 HandleUnlock( ); 125 return; 126 } 127 // copy 128 ctrlpt_callback = ctrlpt_info->Callback; 129 ctrlpt_cookie = ctrlpt_info->Cookie; 130 HandleUnlock( ); 131 132 // search timeout 133 if( timeout ) { 134 ctrlpt_callback( UPNP_DISCOVERY_SEARCH_TIMEOUT, NULL, cookie ); 135 return; 136 } 137 138 param.ErrCode = UPNP_E_SUCCESS; 139 140 // MAX-AGE 141 param.Expires = -1; // assume error 142 if( httpmsg_find_hdr( hmsg, HDR_CACHE_CONTROL, &hdr_value ) != NULL ) { 143 matchstr( hdr_value.buf, hdr_value.length, 144 "%imax-age = %d%0", ¶m.Expires ); 145 } 146 147 // DATE 148 param.Date[0] = '\0'; 149 if( httpmsg_find_hdr( hmsg, HDR_DATE, &hdr_value ) != NULL ) { 150 linecopylen( param.Date, hdr_value.buf, hdr_value.length ); 151 } 152 153 // dest addr 154 param.DestAddr = dest_addr; 155 156 // EXT 157 param.Ext[0] = '\0'; 158 if( httpmsg_find_hdr( hmsg, HDR_EXT, &hdr_value ) != NULL ) { 159 linecopylen( param.Ext, hdr_value.buf, hdr_value.length ); 160 } 161 // LOCATION 162 param.Location[0] = '\0'; 163 if( httpmsg_find_hdr( hmsg, HDR_LOCATION, &hdr_value ) != NULL ) { 164 linecopylen( param.Location, hdr_value.buf, hdr_value.length ); 165 } 166 // SERVER / USER-AGENT 167 param.Os[0] = '\0'; 168 if( httpmsg_find_hdr( hmsg, HDR_SERVER, &hdr_value ) != NULL || 169 httpmsg_find_hdr( hmsg, HDR_USER_AGENT, &hdr_value ) != NULL ) { 170 linecopylen( param.Os, hdr_value.buf, hdr_value.length ); 171 } 172 // clear everything 173 param.DeviceId[0] = '\0'; 174 param.DeviceType[0] = '\0'; 175 param.ServiceType[0] = '\0'; 176 177 param.ServiceVer[0] = '\0'; // not used; version is in ServiceType 178 179 event.UDN[0] = '\0'; 180 event.DeviceType[0] = '\0'; 181 event.ServiceType[0] = '\0'; 182 183 nt_found = FALSE; 184 185 if( httpmsg_find_hdr( hmsg, HDR_NT, &hdr_value ) != NULL ) { 186 save_char = hdr_value.buf[hdr_value.length]; 187 hdr_value.buf[hdr_value.length] = '\0'; 188 189 nt_found = ( ssdp_request_type( hdr_value.buf, &event ) == 0 ); 190 191 hdr_value.buf[hdr_value.length] = save_char; 192 } 193 194 usn_found = FALSE; 195 if( httpmsg_find_hdr( hmsg, HDR_USN, &hdr_value ) != NULL ) { 196 save_char = hdr_value.buf[hdr_value.length]; 197 hdr_value.buf[hdr_value.length] = '\0'; 198 199 usn_found = ( unique_service_name( hdr_value.buf, &event ) == 0 ); 200 201 hdr_value.buf[hdr_value.length] = save_char; 202 } 203 204 if( nt_found || usn_found ) { 205 strcpy( param.DeviceId, event.UDN ); 206 strcpy( param.DeviceType, event.DeviceType ); 207 strcpy( param.ServiceType, event.ServiceType ); 208 } 209 210 // ADVERT. OR BYEBYE 211 if( hmsg->is_request ) { 212 // use NTS hdr to determine advert., or byebye 213 // 214 if( httpmsg_find_hdr( hmsg, HDR_NTS, &hdr_value ) == NULL ) { 215 return; // error; NTS header not found 216 } 217 if( memptr_cmp( &hdr_value, "ssdp:alive" ) == 0 ) { 218 is_byebye = FALSE; 219 } else if( memptr_cmp( &hdr_value, "ssdp:byebye" ) == 0 ) { 220 is_byebye = TRUE; 221 } else { 222 return; // bad value 223 } 224 225 if( is_byebye ) { 226 // check device byebye 227 if( !nt_found || !usn_found ) { 228 return; // bad byebye 229 } 230 event_type = UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE; 231 } else { 232 // check advertisement 233 // .Expires is valid if positive. This is for testing 234 // only. Expires should be greater than 1800 (30 mins) 235 if( !nt_found || 236 !usn_found || 237 strlen( param.Location ) == 0 || param.Expires <= 0 ) { 238 return; // bad advertisement 239 } 240 241 event_type = UPNP_DISCOVERY_ADVERTISEMENT_ALIVE; 242 } 243 244 // call callback 245 ctrlpt_callback( event_type, ¶m, ctrlpt_cookie ); 246 247 } else // reply (to a SEARCH) 248 { 249 // only checking to see if there is a valid ST header 250 st_found = FALSE; 251 if( httpmsg_find_hdr( hmsg, HDR_ST, &hdr_value ) != NULL ) { 252 save_char = hdr_value.buf[hdr_value.length]; 253 hdr_value.buf[hdr_value.length] = '\0'; 254 st_found = ssdp_request_type( hdr_value.buf, &event ) == 0; 255 hdr_value.buf[hdr_value.length] = save_char; 256 } 257 if( hmsg->status_code != HTTP_OK || 258 param.Expires <= 0 || 259 strlen( param.Location ) == 0 || !usn_found || !st_found ) { 260 return; // bad reply 261 } 262 //check each current search 263 HandleLock( ); 264 if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { 265 HandleUnlock( ); 266 return; 267 } 268 node = ListHead( &ctrlpt_info->SsdpSearchList ); 269 270 //temporary add null termination 271 //save_char = hdr_value.buf[ hdr_value.length ]; 272 //hdr_value.buf[ hdr_value.length ] = '\0'; 273 274 while( node != NULL ) { 275 searchArg = node->item; 276 matched = 0; 277 //check for match of ST header and search target 278 switch ( searchArg->requestType ) { 279 case SSDP_ALL: 280 { 281 matched = 1; 282 break; 283 } 284 case SSDP_ROOTDEVICE: 285 { 286 matched = ( event.RequestType == SSDP_ROOTDEVICE ); 287 break; 288 } 289 case SSDP_DEVICEUDN: 290 { 291 matched = !( strncmp( searchArg->searchTarget, 292 hdr_value.buf, 293 hdr_value.length ) ); 294 break; 295 } 296 case SSDP_DEVICETYPE: 297 { 298 int m = min( hdr_value.length, 299 strlen( searchArg->searchTarget ) ); 300 301 matched = !( strncmp( searchArg->searchTarget, 302 hdr_value.buf, m ) ); 303 break; 304 } 305 case SSDP_SERVICE: 306 { 307 int m = min( hdr_value.length, 308 strlen( searchArg->searchTarget ) ); 309 310 matched = !( strncmp( searchArg->searchTarget, 311 hdr_value.buf, m ) ); 312 break; 313 } 314 default: 315 { 316 matched = 0; 317 break; 318 } 319 } 320 321 if( matched ) { 322 //schedule call back 323 threadData = 324 ( ResultData * ) malloc( sizeof( ResultData ) ); 325 if( threadData != NULL ) { 326 threadData->param = param; 327 threadData->cookie = searchArg->cookie; 328 threadData->ctrlpt_callback = ctrlpt_callback; 329 TPJobInit( &job, ( start_routine ) send_search_result, 330 threadData ); 331 TPJobSetPriority( &job, MED_PRIORITY ); 332 TPJobSetFreeFunction( &job, ( free_routine ) free ); 333 ThreadPoolAdd( &gRecvThreadPool, &job, NULL ); 334 } 335 } 336 node = ListNext( &ctrlpt_info->SsdpSearchList, node ); 337 } 338 339 HandleUnlock( ); 340 //ctrlpt_callback( UPNP_DISCOVERY_SEARCH_RESULT, ¶m, cookie ); 341 } 342} 343 344/************************************************************************ 345* Function : process_reply 346* 347* Parameters: 348* IN char* request_buf: the response came from the device 349* IN int buf_len: The length of the response buffer 350* IN struct sockaddr_in* dest_addr: The address of the device 351* IN void *cookie : cookie passed by the control point application 352* at the time of sending search message 353* 354* Description: 355* This function processes reply recevied from a search 356* 357* Returns: void 358* 359***************************************************************************/ 360static XINLINE void 361process_reply( IN char *request_buf, 362 IN int buf_len, 363 IN struct sockaddr_in *dest_addr, 364 IN void *cookie ) 365{ 366 http_parser_t parser; 367 368 parser_response_init( &parser, HTTPMETHOD_MSEARCH ); 369 370 // parse 371 if( parser_append( &parser, request_buf, buf_len ) != PARSE_SUCCESS ) { 372 httpmsg_destroy( &parser.msg ); 373 return; 374 } 375 // handle reply 376 ssdp_handle_ctrlpt_msg( &parser.msg, dest_addr, FALSE, cookie ); 377 378 // done 379 httpmsg_destroy( &parser.msg ); 380} 381 382/************************************************************************ 383* Function : CreateClientRequestPacket 384* 385* Parameters: 386* IN char * RqstBuf:Output string in HTTP format. 387* IN char *SearchTarget:Search Target 388* IN int Mx dest_addr: Number of seconds to wait to 389* collect all the responses 390* 391* Description: 392* This function creates a HTTP search request packet 393* depending on the input parameter. 394* 395* Returns: void 396* 397***************************************************************************/ 398static void 399CreateClientRequestPacket( IN char *RqstBuf, 400 IN int Mx, 401 IN char *SearchTarget ) 402{ 403 char TempBuf[COMMAND_LEN]; 404 int Port; 405 406 strcpy( RqstBuf, "M-SEARCH * HTTP/1.1\r\n" ); 407 408 Port = SSDP_PORT; 409 strcpy( TempBuf, "HOST: " ); //Added space NK. 410 strcat( TempBuf, SSDP_IP ); 411 sprintf( TempBuf, "%s:%d\r\n", TempBuf, Port ); 412 strcat( RqstBuf, TempBuf ); 413 414 strcat( RqstBuf, "MAN: \"ssdp:discover\"\r\n" ); 415 416 if( Mx > 0 ) { 417 sprintf( TempBuf, "MX: %d\r\n", Mx ); 418 strcat( RqstBuf, TempBuf ); 419 } 420 421 if( SearchTarget != NULL ) { 422 sprintf( TempBuf, "ST: %s\r\n", SearchTarget ); 423 strcat( RqstBuf, TempBuf ); 424 } 425 strcat( RqstBuf, "\r\n" ); 426 427} 428 429/************************************************************************ 430* Function : searchExpired 431* 432* Parameters: 433* IN void * arg: 434* 435* Description: 436* This function 437* 438* Returns: void 439* 440***************************************************************************/ 441void 442searchExpired( void *arg ) 443{ 444 445 int *id = ( int * )arg; 446 int handle = -1; 447 struct Handle_Info *ctrlpt_info = NULL; 448 449 //remove search Target from list and call client back 450 ListNode *node = NULL; 451 SsdpSearchArg *item; 452 Upnp_FunPtr ctrlpt_callback; 453 void *cookie = NULL; 454 int found = 0; 455 456 HandleLock( ); 457 458 //remove search target from search list 459 460 if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { 461 if(id) 462 free( id ); 463 HandleUnlock( ); 464 return; 465 } 466 467 ctrlpt_callback = ctrlpt_info->Callback; 468 469 node = ListHead( &ctrlpt_info->SsdpSearchList ); 470 471 while( node != NULL ) { 472 item = ( SsdpSearchArg * ) node->item; 473 if( item->timeoutEventId == ( *id ) ) { 474 free( item->searchTarget ); 475 cookie = item->cookie; 476 found = 1; 477 item->searchTarget = NULL; 478 free( item ); 479 ListDelNode( &ctrlpt_info->SsdpSearchList, node, 0 ); 480 break; 481 } 482 node = ListNext( &ctrlpt_info->SsdpSearchList, node ); 483 } 484 HandleUnlock( ); 485 486 if( found ) { 487 ctrlpt_callback( UPNP_DISCOVERY_SEARCH_TIMEOUT, NULL, cookie ); 488 } 489 if(id) 490 free( id ); 491} 492 493/************************************************************************ 494* Function : SearchByTarget 495* 496* Parameters: 497* IN int Mx:Number of seconds to wait, to collect all the 498* responses. 499* char *St: Search target. 500* void *Cookie: cookie provided by control point application. This 501* cokie will be returned to application in the 502* callback. 503* 504* Description: 505* This function creates and send the search request for a specific URL. 506* 507* Returns: int 508* 1 if successful else appropriate error 509***************************************************************************/ 510int 511SearchByTarget( IN int Mx, 512 IN char *St, 513 IN void *Cookie ) 514{ 515 int socklen = sizeof( struct sockaddr_in ); 516 int *id = NULL; 517 char *ReqBuf; 518 struct sockaddr_in destAddr; 519 fd_set wrSet; 520 SsdpSearchArg *newArg = NULL; 521 int timeTillRead = 0; 522 int handle; 523 struct Handle_Info *ctrlpt_info = NULL; 524 enum SsdpSearchType requestType; 525 unsigned long addr = inet_addr( LOCAL_HOST ); 526 527 //ThreadData *ThData; 528 ThreadPoolJob job; 529 530 requestType = ssdp_request_type1( St ); 531 if( requestType == SSDP_SERROR ) { 532 return UPNP_E_INVALID_PARAM; 533 } 534 535 ReqBuf = ( char * )malloc( BUFSIZE ); 536 if( ReqBuf == NULL ) 537 return UPNP_E_OUTOF_MEMORY; 538 539 DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, 540 ">>> SSDP SEND >>>\n%s\n", ReqBuf ); 541 ) 542 543 timeTillRead = Mx; 544 545 if( timeTillRead < MIN_SEARCH_TIME ) { 546 timeTillRead = MIN_SEARCH_TIME; 547 } else if( timeTillRead > MAX_SEARCH_TIME ) { 548 timeTillRead = MAX_SEARCH_TIME; 549 } 550 551 CreateClientRequestPacket( ReqBuf, timeTillRead, St ); 552 memset( ( char * )&destAddr, 0, sizeof( struct sockaddr_in ) ); 553 554 destAddr.sin_family = AF_INET; 555 destAddr.sin_addr.s_addr = inet_addr( SSDP_IP ); 556 destAddr.sin_port = htons( SSDP_PORT ); 557 558 FD_ZERO( &wrSet ); 559 FD_SET( gSsdpReqSocket, &wrSet ); 560 561 //add search criteria to list 562 HandleLock( ); 563 if( GetClientHandleInfo( &handle, &ctrlpt_info ) != HND_CLIENT ) { 564 HandleUnlock( ); 565 free( ReqBuf ); 566 return UPNP_E_INTERNAL_ERROR; 567 } 568 569 newArg = ( SsdpSearchArg * ) malloc( sizeof( SsdpSearchArg ) ); 570 if(newArg==NULL) { 571 HandleUnlock( ); 572 free( ReqBuf ); 573 return UPNP_E_INTERNAL_ERROR; 574 } 575 576 newArg->searchTarget = strdup( St ); 577 newArg->cookie = Cookie; 578 newArg->requestType = requestType; 579 580 id = ( int * )malloc( sizeof( int ) ); 581 if(id==NULL) { 582 HandleUnlock( ); 583 free( ReqBuf ); 584 free(newArg); 585 return UPNP_E_INTERNAL_ERROR; 586 } 587 TPJobInit( &job, ( start_routine ) searchExpired, id ); 588 TPJobSetPriority( &job, MED_PRIORITY ); 589 TPJobSetFreeFunction( &job, ( free_routine ) free ); 590 591 //Schdule a timeout event to remove search Arg 592 TimerThreadSchedule( &gTimerThread, timeTillRead, 593 REL_SEC, &job, SHORT_TERM, id ); 594 newArg->timeoutEventId = ( *id ); 595 596 ListAddTail( &ctrlpt_info->SsdpSearchList, newArg ); 597 HandleUnlock( ); 598 599 setsockopt( gSsdpReqSocket, IPPROTO_IP, IP_MULTICAST_IF, 600 ( char * )&addr, sizeof( addr ) ); 601 602 if( select( gSsdpReqSocket + 1, NULL, &wrSet, NULL, NULL ) 603 == UPNP_SOCKETERROR ) { 604 DBGONLY( if( errno == EBADF ) { 605 UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, 606 "SSDP_LIB :RequestHandler:An invalid file descriptor" 607 " was givenin one of the sets. \n" );} 608 else 609 if( errno == EINTR ) { 610 UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, 611 "SSDP_LIB :RequestHandler: A non blocked " 612 "signal was caught. \n" );} 613 else 614 if( errno == EINVAL ) { 615 UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, 616 "SSDP_LIB :RequestHandler: n is negative. \n" );} 617 else 618 if( errno == ENOMEM ) { 619 UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__, 620 "SSDP_LIB : RequestHandler:select was unable to " 621 "allocate memory for internal tables.\n" );} 622 ) 623shutdown( gSsdpReqSocket, SD_BOTH ); 624 UpnpCloseSocket( gSsdpReqSocket ); 625 free( ReqBuf ); 626 return UPNP_E_INTERNAL_ERROR; 627 } else if( FD_ISSET( gSsdpReqSocket, &wrSet ) ) { 628 int NumCopy = 0; 629 630 while( NumCopy < NUM_SSDP_COPY ) { 631 sendto( gSsdpReqSocket, ReqBuf, strlen( ReqBuf ), 0, 632 ( struct sockaddr * )&destAddr, socklen ); 633 NumCopy++; 634 imillisleep( SSDP_PAUSE ); 635 } 636 } 637 638 free( ReqBuf ); 639 return 1; 640} 641 642#endif // EXCLUDE_SSDP 643#endif // INCLUDE_CLIENT_APIS 644