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", &param.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, &param, 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, &param, 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