pupnp (libupnp) snapshot from SourceForge: git clone git://pupnp.git.sourceforge...
[igd2-for-linux:pandonghui1211s-igd2-for-linux.git] / pupnp_branch-1.6.x / upnp / src / ssdp / ssdp_server.c
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 #if EXCLUDE_SSDP == 0
34
35 #include "membuffer.h"
36 #include "ssdplib.h"
37 #include <stdio.h>
38 #include "ThreadPool.h"
39 #include "miniserver.h"
40
41 #include "upnpapi.h"
42 #include "httpparser.h"
43 #include "httpreadwrite.h"
44
45 #ifdef WIN32
46         #include <string.h>
47         #include <winsock2.h>
48         #include <ws2tcpip.h>
49         #include "unixutil.h"
50 #endif
51
52 #define MAX_TIME_TOREAD  45
53
54 CLIENTONLY( SOCKET gSsdpReqSocket = 0; )
55
56 void RequestHandler();
57 Event ErrotEvt;
58
59 enum Listener { Idle, Stopping, Running };
60
61 unsigned short ssdpStopPort;
62
63 struct SSDPSockArray {
64         // socket for incoming advertisments and search requests
65         int ssdpSock;
66         // socket for sending search requests and receiving search replies
67         CLIENTONLY( int ssdpReqSock; )
68 };
69
70 #ifdef INCLUDE_DEVICE_APIS
71 #if EXCLUDE_SSDP == 0
72
73 /************************************************************************
74  * Function : AdvertiseAndReply
75  *
76  * Parameters:
77  *      IN int AdFlag:
78  *              -1 = Send shutdown,
79  *               0 = send reply, 
80  *               1 = Send Advertisement
81  *      IN UpnpDevice_Handle Hnd: Device handle
82  *      IN enum SsdpSearchType SearchType:Search type for sending replies
83  *      IN struct sockaddr_in *DestAddr:Destination address
84  *   IN char *DeviceType:Device type
85  *      IN char *DeviceUDN:Device UDN
86  *   IN char *ServiceType:Service type
87  *      IN int Exp:Advertisement age
88  *
89  * Description:
90  *      This function sends SSDP advertisements, replies and shutdown messages.
91  *
92  * Returns: int
93  *      UPNP_E_SUCCESS if successful else appropriate error
94  ***************************************************************************/
95 int AdvertiseAndReply( IN int AdFlag,
96                        IN UpnpDevice_Handle Hnd,
97                        IN enum SsdpSearchType SearchType,
98                        IN struct sockaddr_in *DestAddr,
99                        IN char *DeviceType,
100                        IN char *DeviceUDN,
101                        IN char *ServiceType,
102                        int Exp )
103 {
104     int i,
105       j;
106     int defaultExp = DEFAULT_MAXAGE;
107     struct Handle_Info *SInfo = NULL;
108     char UDNstr[100],
109       devType[100],
110       servType[100];
111     IXML_NodeList *nodeList = NULL;
112     IXML_NodeList *tmpNodeList = NULL;
113     IXML_Node *tmpNode = NULL;
114     IXML_Node *tmpNode2 = NULL;
115     IXML_Node *textNode = NULL;
116     const DOMString tmpStr;
117     char SERVER[200];
118
119     const DOMString dbgStr;
120     UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
121         "Inside AdvertiseAndReply with AdFlag = %d\n",
122         AdFlag );
123
124     // Use a read lock
125     HandleReadLock();
126     if( GetHandleInfo( Hnd, &SInfo ) != HND_DEVICE ) {
127         HandleUnlock();
128         return UPNP_E_INVALID_HANDLE;
129     }
130     defaultExp = SInfo->MaxAge;
131
132     //get server info
133
134     get_sdk_info( SERVER );
135
136     // parse the device list and send advertisements/replies 
137     for( i = 0;; i++ ) {
138
139         UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
140             "Entering new device list with i = %d\n\n", i );
141
142         tmpNode = ixmlNodeList_item( SInfo->DeviceList, i );
143         if( tmpNode == NULL ) {
144             UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
145                 "Exiting new device list with i = %d\n\n", i );
146             break;
147         }
148
149         dbgStr = ixmlNode_getNodeName( tmpNode );
150         UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
151             "Extracting device type once for %s\n", dbgStr );
152         // extract device type 
153         ixmlNodeList_free( nodeList );
154         nodeList = NULL;
155         nodeList = ixmlElement_getElementsByTagName(
156             ( IXML_Element * ) tmpNode, "deviceType" );
157         if( nodeList == NULL ) {
158             continue;
159         }
160
161         dbgStr = ixmlNode_getNodeName( tmpNode );
162         UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
163             "Extracting UDN for %s\n", dbgStr );
164         UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
165             "Extracting device type\n" );
166
167         tmpNode2 = ixmlNodeList_item( nodeList, 0 );
168         if( tmpNode2 == NULL ) {
169             continue;
170         }
171         textNode = ixmlNode_getFirstChild( tmpNode2 );
172         if( textNode == NULL ) {
173             continue;
174         }
175
176         UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
177             "Extracting device type \n" );
178
179         tmpStr = ixmlNode_getNodeValue( textNode );
180         if( tmpStr == NULL ) {
181             continue;
182         }
183
184         strcpy( devType, tmpStr );
185         if( devType == NULL ) {
186             continue;
187         }
188
189         UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
190             "Extracting device type = %s\n", devType );
191         if( tmpNode == NULL ) {
192             UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
193                 "TempNode is NULL\n" );
194         }
195         dbgStr = ixmlNode_getNodeName( tmpNode );
196         UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
197             "Extracting UDN for %s\n", dbgStr );
198         // extract UDN 
199         ixmlNodeList_free( nodeList );
200         nodeList = NULL;
201         nodeList = ixmlElement_getElementsByTagName( ( IXML_Element * )
202                                                      tmpNode, "UDN" );
203         if( nodeList == NULL ) {
204             UpnpPrintf( UPNP_CRITICAL, API, __FILE__,
205                 __LINE__, "UDN not found!!!\n" );
206                 continue;
207         }
208         tmpNode2 = ixmlNodeList_item( nodeList, 0 );
209         if( tmpNode2 == NULL ) {
210             UpnpPrintf( UPNP_CRITICAL, API, __FILE__,
211                 __LINE__, "UDN not found!!!\n" );
212             continue;
213         }
214         textNode = ixmlNode_getFirstChild( tmpNode2 );
215         if( textNode == NULL ) {
216             UpnpPrintf( UPNP_CRITICAL, API, __FILE__,
217                 __LINE__, "UDN not found!!!\n" );
218             continue;
219         }
220         tmpStr = ixmlNode_getNodeValue( textNode );
221         if( tmpStr == NULL ) {
222             UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__,
223                 "UDN not found!!!!\n" );
224                 continue;
225         }
226         strcpy( UDNstr, tmpStr );
227         if( UDNstr == NULL ) {
228             continue;
229         }
230
231         UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
232             "Sending UDNStr = %s \n", UDNstr );
233         if( AdFlag ) {
234             // send the device advertisement 
235             if( AdFlag == 1 ) {
236                 DeviceAdvertisement( devType, i == 0,
237                                      UDNstr, SInfo->DescURL, Exp );
238             } else {             // AdFlag == -1
239                 DeviceShutdown( devType, i == 0, UDNstr,
240                                 SERVER, SInfo->DescURL, Exp );
241             }
242         } else {
243             switch ( SearchType ) {
244
245                 case SSDP_ALL:
246                     DeviceReply( DestAddr,
247                                  devType, i == 0,
248                                  UDNstr, SInfo->DescURL, defaultExp );
249                     break;
250
251                 case SSDP_ROOTDEVICE:
252                     if( i == 0 ) {
253                         SendReply( DestAddr, devType, 1,
254                                    UDNstr, SInfo->DescURL, defaultExp, 0 );
255                     }
256                     break;
257                 case SSDP_DEVICEUDN:
258                     {
259                         if( DeviceUDN != NULL && strlen( DeviceUDN ) != 0 ) {
260                             if( strcasecmp( DeviceUDN, UDNstr ) ) {
261                                 UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
262                                     "DeviceUDN=%s and search "
263                                     "UDN=%s did not match\n",
264                                     UDNstr, DeviceUDN );
265                                     break;
266                             } else {
267                                 UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
268                                     "DeviceUDN=%s and search "
269                                     "UDN=%s MATCH\n", UDNstr,
270                                     DeviceUDN );
271                                     SendReply( DestAddr, devType, 0,
272                                                UDNstr, SInfo->DescURL,
273                                                defaultExp, 0 );
274                                 break;
275                             }
276                         }
277                     }
278                 case SSDP_DEVICETYPE:
279                     {
280                         if( !strncasecmp
281                             ( DeviceType, devType,
282                               strlen( DeviceType ) ) ) {
283                             UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
284                                 "DeviceType=%s and search devType=%s MATCH\n",
285                                 devType, DeviceType );
286                             SendReply( DestAddr, devType, 0, UDNstr,
287                                        SInfo->DescURL, defaultExp, 1 );
288                         } else {
289                             UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
290                                 "DeviceType=%s and search devType=%s"
291                                 " DID NOT MATCH\n",
292                                 devType, DeviceType );
293                         }
294                         break;
295                     }
296                 default:
297                     break;
298             }
299         }
300         // send service advertisements for services corresponding 
301         // to the same device 
302         UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
303             "Sending service Advertisement\n" );
304
305         tmpNode = ixmlNodeList_item( SInfo->ServiceList, i );
306         if( tmpNode == NULL ) {
307             continue;
308         }
309         ixmlNodeList_free( nodeList );
310         nodeList = NULL;
311         nodeList = ixmlElement_getElementsByTagName( ( IXML_Element * )
312                                                      tmpNode, "service" );
313         if( nodeList == NULL ) {
314             UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
315                 "Service not found 3\n" );
316             continue;
317         }
318         for( j = 0;; j++ ) {
319             tmpNode = ixmlNodeList_item( nodeList, j );
320             if( tmpNode == NULL ) {
321                 break;
322             }
323
324             ixmlNodeList_free( tmpNodeList );
325             tmpNodeList = NULL;
326             tmpNodeList = ixmlElement_getElementsByTagName(
327                 ( IXML_Element *)tmpNode, "serviceType" );
328             if( tmpNodeList == NULL ) {
329                 UpnpPrintf( UPNP_CRITICAL, API, __FILE__, __LINE__,
330                     "ServiceType not found \n" );
331                 continue;
332             }
333             tmpNode2 = ixmlNodeList_item( tmpNodeList, 0 );
334             if( tmpNode2 == NULL ) {
335                 continue;
336             }
337             textNode = ixmlNode_getFirstChild( tmpNode2 );
338             if( textNode == NULL ) {
339                 continue;
340             }
341             // servType is of format Servicetype:ServiceVersion
342             tmpStr = ixmlNode_getNodeValue( textNode );
343             if( tmpStr == NULL ) {
344                 continue;
345             }
346             strcpy( servType, tmpStr );
347             if( servType == NULL ) {
348                 continue;
349             }
350
351             UpnpPrintf( UPNP_INFO, API, __FILE__, __LINE__,
352                 "ServiceType = %s\n", servType );
353             if( AdFlag ) {
354                 if( AdFlag == 1 ) {
355                     ServiceAdvertisement( UDNstr, servType,
356                                           SInfo->DescURL, Exp );
357                 } else {         // AdFlag == -1
358                     ServiceShutdown( UDNstr, servType,
359                                      SInfo->DescURL, Exp );
360                 }
361             } else {
362                 switch ( SearchType ) {
363                     case SSDP_ALL:
364                         ServiceReply( DestAddr, servType,
365                                       UDNstr, SInfo->DescURL,
366                                       defaultExp );
367                         break;
368                     case SSDP_SERVICE:
369                         if( ServiceType != NULL ) {
370                             if( !strncasecmp( ServiceType,
371                                               servType,
372                                               strlen( ServiceType ) ) ) {
373                                 ServiceReply( DestAddr, servType,
374                                               UDNstr, SInfo->DescURL,
375                                               defaultExp );
376                             }
377                         }
378                         break;
379                     default:
380                         break;
381                 }               // switch(SearchType)               
382
383             }
384         }
385         ixmlNodeList_free( tmpNodeList );
386         tmpNodeList = NULL;
387         ixmlNodeList_free( nodeList );
388         nodeList = NULL;
389     }
390     UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
391         "Exiting AdvertiseAndReply : \n" );
392
393     HandleUnlock();
394
395     return UPNP_E_SUCCESS;
396
397 }  /****************** End of AdvertiseAndReply *********************/
398
399 #endif /* EXCLUDE_SSDP == 0 */
400 #endif /* INCLUDE_DEVICE_APIS */
401
402 /************************************************************************
403  * Function : Make_Socket_NoBlocking
404  *
405  * Parameters:
406  *      IN int sock: socket
407  *
408  * Description:
409  *      This function makes socket non-blocking.
410  *
411  * Returns: int
412  *      0 if successful else -1 
413  ***************************************************************************/
414 int
415 Make_Socket_NoBlocking( int sock )
416 {
417 #ifdef WIN32
418      u_long val=1;
419      return ioctlsocket(sock, FIONBIO, &val);
420 #else
421     int val;
422
423     val = fcntl( sock, F_GETFL, 0 );
424     if( fcntl( sock, F_SETFL, val | O_NONBLOCK ) == -1 ) {
425         return -1;
426     }
427 #endif
428     return 0;
429 }
430
431 /************************************************************************
432  * Function : unique_service_name
433  *
434  * Parameters:
435  *      IN char *cmd: Service Name string
436  *      OUT SsdpEvent *Evt: The SSDP event structure partially filled
437  *              by all the function.
438  *
439  * Description:
440  *      This function fills the fields of the event structure like DeviceType,
441  *      Device UDN and Service Type
442  *
443  * Returns: int
444  *      0 if successful else -1
445  ***************************************************************************/
446 int unique_service_name(IN char *cmd, IN SsdpEvent *Evt)
447 {
448     char TempBuf[COMMAND_LEN];
449     char *TempPtr = NULL;
450     char *Ptr = NULL;
451     char *ptr1 = NULL;
452     char *ptr2 = NULL;
453     char *ptr3 = NULL;
454     int CommandFound = 0;
455     int length = 0;
456
457     if( ( TempPtr = strstr( cmd, "uuid:schemas" ) ) != NULL ) {
458         ptr1 = strstr( cmd, ":device" );
459         if( ptr1 != NULL ) {
460             ptr2 = strstr( ptr1 + 1, ":" );
461         } else {
462             return -1;
463         }
464
465         if( ptr2 != NULL ) {
466             ptr3 = strstr( ptr2 + 1, ":" );
467         } else {
468             return -1;
469         }
470
471         if( ptr3 != NULL ) {
472             sprintf( Evt->UDN, "uuid:%s", ptr3 + 1 );
473         } else {
474             return -1;
475         }
476
477         ptr1 = strstr( cmd, ":" );
478         if( ptr1 != NULL ) {
479             strncpy( TempBuf, ptr1, ptr3 - ptr1 );
480             TempBuf[ptr3 - ptr1] = '\0';
481             sprintf( Evt->DeviceType, "urn%s", TempBuf );
482         } else {
483             return -1;
484         }
485         return 0;
486     }
487
488     if( ( TempPtr = strstr( cmd, "uuid" ) ) != NULL ) {
489         if( ( Ptr = strstr( cmd, "::" ) ) != NULL ) {
490             strncpy( Evt->UDN, TempPtr, Ptr - TempPtr );
491             Evt->UDN[Ptr - TempPtr] = '\0';
492         } else {
493             strcpy( Evt->UDN, TempPtr );
494         }
495         CommandFound = 1;
496     }
497
498     if( strstr( cmd, "urn:" ) != NULL
499         && strstr( cmd, ":service:" ) != NULL ) {
500         if( ( TempPtr = strstr( cmd, "urn" ) ) != NULL ) {
501             strcpy( Evt->ServiceType, TempPtr );
502             CommandFound = 1;
503         }
504     }
505
506     if( strstr( cmd, "urn:" ) != NULL
507         && strstr( cmd, ":device:" ) != NULL ) {
508         if( ( TempPtr = strstr( cmd, "urn" ) ) != NULL ) {
509             strcpy( Evt->DeviceType, TempPtr );
510             CommandFound = 1;
511         }
512     }
513
514     if( ( TempPtr = strstr( cmd, "::upnp:rootdevice" ) ) != NULL ) {
515         /* Everything before "::upnp::rootdevice" is the UDN. */
516         if( TempPtr != cmd ) {
517             length = TempPtr - cmd;
518             strncpy(Evt->UDN, cmd, length);
519             Evt->UDN[length] = 0;
520             CommandFound = 1;
521         }
522     }
523    
524     if( CommandFound == 0 ) {
525         return -1;
526     }
527
528     return 0;
529 }
530
531 /************************************************************************
532  * Function : ssdp_request_type1
533  *
534  * Parameters:
535  *      IN char *cmd: command came in the ssdp request
536  *
537  * Description:
538  *      This function figures out the type of the SSDP search in the
539  *      in the request.
540  *
541  * Returns: enum SsdpSearchType
542  *      return appropriate search type else returns SSDP_ERROR
543  ***************************************************************************/
544 enum SsdpSearchType
545 ssdp_request_type1( IN char *cmd )
546 {
547     if( strstr( cmd, ":all" ) != NULL ) {
548         return SSDP_ALL;
549     }
550     if( strstr( cmd, ":rootdevice" ) != NULL ) {
551         return SSDP_ROOTDEVICE;
552     }
553     if( strstr( cmd, "uuid:" ) != NULL ) {
554         return SSDP_DEVICEUDN;
555     }
556     if( ( strstr( cmd, "urn:" ) != NULL )
557         && ( strstr( cmd, ":device:" ) != NULL ) ) {
558         return SSDP_DEVICETYPE;
559     }
560     if( ( strstr( cmd, "urn:" ) != NULL )
561         && ( strstr( cmd, ":service:" ) != NULL ) ) {
562         return SSDP_SERVICE;
563     }
564     return SSDP_SERROR;
565 }
566
567 /************************************************************************
568  * Function : ssdp_request_type
569  *
570  * Parameters:
571  *      IN char *cmd: command came in the ssdp request
572  *      OUT SsdpEvent *Evt: The event structure partially filled by
573  *               this function.
574  *
575  * Description:
576  *      This function starts filling the SSDP event structure based upon the 
577  *      request received.
578  *
579  * Returns: int
580  *      0 on success; -1 on error
581  ***************************************************************************/
582 int
583 ssdp_request_type( IN char *cmd,
584                    OUT SsdpEvent * Evt )
585 {
586     // clear event
587     memset( Evt, 0, sizeof( SsdpEvent ) );
588     unique_service_name( cmd, Evt );
589     Evt->ErrCode = NO_ERROR_FOUND;
590
591     if( ( Evt->RequestType = ssdp_request_type1( cmd ) ) == SSDP_SERROR ) {
592         Evt->ErrCode = E_HTTP_SYNTEX;
593         return -1;
594     }
595     return 0;
596 }
597
598 /************************************************************************
599  * Function : free_ssdp_event_handler_data
600  *
601  * Parameters:
602  *      IN void *the_data: ssdp_thread_data structure. This structure contains
603  *                      SSDP request message.
604  *
605  * Description:
606  *      This function frees the ssdp request
607  *
608  * Returns: VOID
609  *
610  ***************************************************************************/
611 static void
612 free_ssdp_event_handler_data( void *the_data )
613 {
614     ssdp_thread_data *data = ( ssdp_thread_data * ) the_data;
615
616     if( data != NULL ) {
617         http_message_t *hmsg = &data->parser.msg;
618
619         // free data
620         httpmsg_destroy( hmsg );
621         free( data );
622     }
623 }
624
625 /************************************************************************
626  * Function : valid_ssdp_msg
627  *
628  * Parameters:
629  *      IN void *the_data: ssdp_thread_data structure. This structure contains
630  *                      SSDP request message.
631  *
632  * Description:
633  *      This function do some quick checking of the ssdp msg
634  *
635  * Returns: xboolean
636  *      returns TRUE if msg is valid else FALSE
637  ***************************************************************************/
638 static UPNP_INLINE xboolean
639 valid_ssdp_msg( IN http_message_t * hmsg )
640 {
641     memptr hdr_value;
642
643     // check for valid methods - NOTIFY or M-SEARCH
644     if( hmsg->method != HTTPMETHOD_NOTIFY &&
645         hmsg->method != HTTPMETHOD_MSEARCH
646         && hmsg->request_method != HTTPMETHOD_MSEARCH ) {
647         return FALSE;
648     }
649     if( hmsg->request_method != HTTPMETHOD_MSEARCH ) {
650         // check PATH == *
651         if( hmsg->uri.type != RELATIVE ||
652             strncmp( "*", hmsg->uri.pathquery.buff,
653                      hmsg->uri.pathquery.size ) != 0 ) {
654             return FALSE;
655         }
656         // check HOST header
657         if( ( httpmsg_find_hdr( hmsg, HDR_HOST, &hdr_value ) == NULL ) ||
658             ( memptr_cmp( &hdr_value, "239.255.255.250:1900" ) != 0 )
659              ) {
660             return FALSE;
661         }
662     }
663     return TRUE;                // passed quick check
664 }
665
666 /************************************************************************
667  * Function : start_event_handler
668  *
669  * Parameters:
670  *      IN void *the_data: ssdp_thread_data structure. This structure contains
671  *                      SSDP request message.
672  *
673  * Description:
674  *      This function parses the message and dispatches it to a handler
675  *      which handles the ssdp request msg
676  *
677  * Returns: int
678  *      0 if successful -1 if error
679  ***************************************************************************/
680 static UPNP_INLINE int
681 start_event_handler( void *Data )
682 {
683
684     http_parser_t *parser = NULL;
685     parse_status_t status;
686     ssdp_thread_data *data = ( ssdp_thread_data * ) Data;
687
688     parser = &data->parser;
689
690     status = parser_parse( parser );
691     if( status == PARSE_FAILURE ) {
692         if( parser->msg.method != HTTPMETHOD_NOTIFY ||
693             !parser->valid_ssdp_notify_hack ) {
694             UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
695                 "SSDP recvd bad msg code = %d\n",
696                 status );
697             // ignore bad msg, or not enuf mem
698             goto error_handler;
699         }
700         // valid notify msg
701     } else if( status != PARSE_SUCCESS ) {
702         UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
703             "SSDP recvd bad msg code = %d\n", status );
704
705         goto error_handler;
706     }
707     // check msg
708     if( !valid_ssdp_msg( &parser->msg ) ) {
709         goto error_handler;
710     }
711     return 0;                   //////// done; thread will free 'data'
712
713   error_handler:
714     free_ssdp_event_handler_data( data );
715     return -1;
716 }
717
718 /************************************************************************
719  * Function : ssdp_event_handler_thread
720  *
721  * Parameters:
722  *      IN void *the_data: ssdp_thread_data structure. This structure contains
723  *                      SSDP request message.
724  *
725  * Description:
726  *      This function is a thread that handles SSDP requests.
727  *
728  * Returns: void
729  *
730  ***************************************************************************/
731 static void
732 ssdp_event_handler_thread( void *the_data )
733 {
734     ssdp_thread_data *data = ( ssdp_thread_data * ) the_data;
735     http_message_t *hmsg = &data->parser.msg;
736
737     if( start_event_handler( the_data ) != 0 ) {
738         return;
739     }
740     // send msg to device or ctrlpt
741     if( ( hmsg->method == HTTPMETHOD_NOTIFY ) ||
742         ( hmsg->request_method == HTTPMETHOD_MSEARCH ) ) {
743         CLIENTONLY( ssdp_handle_ctrlpt_msg( hmsg, &data->dest_addr, FALSE, NULL );)
744     } else {
745         ssdp_handle_device_request( hmsg, &data->dest_addr );
746     }
747
748     // free data
749     free_ssdp_event_handler_data( data );
750 }
751
752 /************************************************************************
753  * Function : readFromSSDPSocket
754  *
755  * Parameters:
756  *      IN SOCKET socket: SSDP socket
757  *
758  * Description:
759  *      This function reads the data from the ssdp socket.
760  *
761  * Returns: void
762  *
763  ***************************************************************************/
764 void
765 readFromSSDPSocket( SOCKET socket )
766 {
767     char *requestBuf = NULL;
768     char staticBuf[BUFSIZE];
769     struct sockaddr_in clientAddr;
770     ThreadPoolJob job;
771     ssdp_thread_data *data = NULL;
772     socklen_t socklen = 0;
773     int byteReceived = 0;
774
775     requestBuf = staticBuf;
776
777     //in case memory
778     //can't be allocated, still drain the 
779     //socket using a static buffer
780
781     socklen = sizeof( struct sockaddr_in );
782
783     data = ( ssdp_thread_data * )
784         malloc( sizeof( ssdp_thread_data ) );
785
786     if( data != NULL ) {
787         //initialize parser
788
789 #ifdef INCLUDE_CLIENT_APIS
790         if( socket == gSsdpReqSocket ) {
791             parser_response_init( &data->parser, HTTPMETHOD_MSEARCH );
792         } else {
793             parser_request_init( &data->parser );
794         }
795 #else
796         parser_request_init( &data->parser );
797 #endif
798
799         //set size of parser buffer
800
801         if( membuffer_set_size( &data->parser.msg.msg, BUFSIZE ) == 0 ) {
802             //use this as the buffer for recv
803             requestBuf = data->parser.msg.msg.buf;
804
805         } else {
806             free( data );
807             data = NULL;
808         }
809     }
810     byteReceived = recvfrom( socket, requestBuf,
811                              BUFSIZE - 1, 0,
812                              ( struct sockaddr * )&clientAddr, &socklen );
813
814     if( byteReceived > 0 ) {
815         requestBuf[byteReceived] = '\0';
816         UpnpPrintf( UPNP_INFO, SSDP,
817             __FILE__, __LINE__,
818             "Start of received response ----------------------------------------------------\n"
819             "%s\n"
820             "End of received response ------------------------------------------------------\n"
821             "From host %s\n",
822             requestBuf,
823             inet_ntoa( clientAddr.sin_addr ) );
824         UpnpPrintf( UPNP_PACKET, SSDP, __FILE__, __LINE__,
825             "Start of received multicast packet --------------------------------------------\n"
826             "%s\n"
827             "End of received multicast packet ----------------------------------------------\n",
828             requestBuf );
829         //add thread pool job to handle request
830         if( data != NULL ) {
831             data->parser.msg.msg.length += byteReceived;
832             // null-terminate
833             data->parser.msg.msg.buf[byteReceived] = 0;
834             data->dest_addr = clientAddr;
835             TPJobInit( &job, ( start_routine )
836                        ssdp_event_handler_thread, data );
837             TPJobSetFreeFunction( &job, free_ssdp_event_handler_data );
838             TPJobSetPriority( &job, MED_PRIORITY );
839
840             if( ThreadPoolAdd( &gRecvThreadPool, &job, NULL ) != 0 ) {
841                 free_ssdp_event_handler_data( data );
842             }
843         }
844     } else {
845         free_ssdp_event_handler_data( data );
846     }
847 }
848
849 /************************************************************************
850  * Function : get_ssdp_sockets                                                          
851  *
852  * Parameters:
853  *      OUT MiniServerSockArray *out: Arrays of SSDP sockets
854  *
855  * Description:
856  *      This function creates the ssdp sockets. It set their option to listen
857  *      for multicast traffic.
858  *
859  * Returns: int
860  *      return UPNP_E_SUCCESS if successful else returns appropriate error
861  ***************************************************************************/
862 int
863 get_ssdp_sockets( MiniServerSockArray * out )
864 {
865     char errorBuffer[ERROR_BUFFER_LEN];
866     int onOff = 1;
867     u_char ttl = 4;
868     struct ip_mreq ssdpMcastAddr;
869     struct sockaddr_in ssdpAddr;
870     int option = 1;
871     int ret = 0;
872     struct in_addr addr;
873     SOCKET ssdpSock;
874 #if INCLUDE_CLIENT_APIS
875     SOCKET ssdpReqSock;
876
877     ssdpReqSock = socket( AF_INET, SOCK_DGRAM, 0 );
878     if ( ssdpReqSock == -1 ) {
879         strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
880         UpnpPrintf( UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
881             "Error in socket(): %s\n", errorBuffer );
882
883             return UPNP_E_OUTOF_SOCKET;
884     }
885     ret = setsockopt( ssdpReqSock, IPPROTO_IP, IP_MULTICAST_TTL,
886         &ttl, sizeof (ttl) );
887     // just do it, regardless if fails or not.
888     Make_Socket_NoBlocking( ssdpReqSock );
889     gSsdpReqSocket = ssdpReqSock;
890 #endif /* INCLUDE_CLIENT_APIS */
891
892     ssdpSock = socket( AF_INET, SOCK_DGRAM, 0 );
893     if ( ssdpSock == -1 ) {
894         strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
895         UpnpPrintf( UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
896             "Error in socket(): %s\n", errorBuffer );
897         CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ); )
898         CLIENTONLY( UpnpCloseSocket( ssdpReqSock ); )
899
900         return UPNP_E_OUTOF_SOCKET;
901     }
902
903     onOff = 1;
904     ret = setsockopt( ssdpSock, SOL_SOCKET, SO_REUSEADDR,
905         (char *)&onOff, sizeof(onOff) );
906     if ( ret == -1) {
907         strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
908         UpnpPrintf( UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
909             "Error in setsockopt() SO_REUSEADDR: %s\n", errorBuffer );
910         CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ); )
911         CLIENTONLY( UpnpCloseSocket( ssdpReqSock ); )
912         shutdown( ssdpSock, SD_BOTH );
913         UpnpCloseSocket( ssdpSock );
914
915         return UPNP_E_SOCKET_ERROR;
916     }
917     
918 #if defined(__FreeBSD__) || defined(__OSX__) || defined(__APPLE__)
919     ret = setsockopt( ssdpSock, SOL_SOCKET, SO_REUSEPORT,
920         (char *)&onOff, sizeof (onOff) );
921     if ( ret == -1 ) {
922         strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
923         UpnpPrintf( UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
924             "Error in setsockopt() SO_REUSEPORT: %s\n", errorBuffer );
925         CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ); )
926         CLIENTONLY( UpnpCloseSocket( ssdpReqSock ); )
927         shutdown( ssdpSock, SD_BOTH );
928         UpnpCloseSocket( ssdpSock );
929
930         return UPNP_E_SOCKET_ERROR;
931     }
932 #endif /* __FreeBSD__ */
933
934     memset( (void *)&ssdpAddr, 0, sizeof( struct sockaddr_in ) );
935     ssdpAddr.sin_family = AF_INET;
936     //  ssdpAddr.sin_addr.s_addr = inet_addr(LOCAL_HOST);
937     ssdpAddr.sin_addr.s_addr = htonl( INADDR_ANY );
938     ssdpAddr.sin_port = htons( SSDP_PORT );
939     ret = bind( ssdpSock, (struct sockaddr *)&ssdpAddr, sizeof (ssdpAddr) );
940     if ( ret == -1 ) {
941         strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
942         UpnpPrintf( UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
943             "Error in bind(), addr=0x%08X, port=%d: %s\n",
944             INADDR_ANY, SSDP_PORT, errorBuffer );
945             shutdown( ssdpSock, SD_BOTH );
946         UpnpCloseSocket( ssdpSock );
947         CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ); )
948         CLIENTONLY( UpnpCloseSocket( ssdpReqSock ); )
949
950         return UPNP_E_SOCKET_BIND;
951     }
952
953     memset( (void *)&ssdpMcastAddr, 0, sizeof (struct ip_mreq) );
954     ssdpMcastAddr.imr_interface.s_addr = inet_addr( LOCAL_HOST );
955     ssdpMcastAddr.imr_multiaddr.s_addr = inet_addr( SSDP_IP );
956     ret = setsockopt( ssdpSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
957         (char *)&ssdpMcastAddr, sizeof (struct ip_mreq) );
958     if ( ret == -1 ) {
959         strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
960         UpnpPrintf( UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
961             "Error in setsockopt() IP_ADD_MEMBERSHIP (join multicast group): %s\n",
962             errorBuffer );
963         shutdown( ssdpSock, SD_BOTH );
964         CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ); )
965         UpnpCloseSocket( ssdpSock );
966         CLIENTONLY( UpnpCloseSocket( ssdpReqSock ); )
967
968         return UPNP_E_SOCKET_ERROR;
969     }
970
971     /* Set multicast interface. */
972     memset( (void *)&addr, 0, sizeof (struct in_addr) );
973     addr.s_addr = inet_addr(LOCAL_HOST);
974     ret = setsockopt(ssdpSock, IPPROTO_IP, IP_MULTICAST_IF,
975         (char *)&addr, sizeof addr);
976     if ( ret == -1 ) {
977         strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
978         UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
979             "Error in setsockopt() IP_MULTICAST_IF (set multicast interface): %s\n",
980             errorBuffer );
981         /* This is probably not a critical error, so let's continue. */
982     }
983
984     /* result is not checked becuase it will fail in WinMe and Win9x. */
985     ret = setsockopt( ssdpSock, IPPROTO_IP,
986         IP_MULTICAST_TTL, &ttl, sizeof (ttl) );
987
988     ret = setsockopt( ssdpSock, SOL_SOCKET, SO_BROADCAST,
989         (char *)&option, sizeof (option) );
990     if( ret == -1) {
991         strerror_r(errno, errorBuffer, ERROR_BUFFER_LEN);
992         UpnpPrintf( UPNP_CRITICAL, SSDP, __FILE__, __LINE__,
993             "Error in setsockopt() SO_BROADCAST (set broadcast): %s\n",
994             errorBuffer );
995         shutdown( ssdpSock, SD_BOTH );
996         CLIENTONLY( shutdown( ssdpReqSock, SD_BOTH ); )
997         UpnpCloseSocket( ssdpSock );
998         CLIENTONLY( UpnpCloseSocket( ssdpReqSock ); )
999
1000         return UPNP_E_NETWORK_ERROR;
1001     }
1002
1003     CLIENTONLY( out->ssdpReqSock = ssdpReqSock; )
1004     out->ssdpSock = ssdpSock;
1005
1006     return UPNP_E_SUCCESS;
1007 }
1008
1009 #endif /* EXCLUDE_SSDP */
1010