📄 ssdp_device.c
字号:
///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2003 Intel Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither name of Intel Corporation nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////
#ifdef INCLUDE_DEVICE_APIS
#if EXCLUDE_SSDP == 0
#include <assert.h>
#include <stdio.h>
#include "config.h"
#include "ssdplib.h"
#include "upnpapi.h"
#include "ThreadPool.h"
#include "httpparser.h"
#include "httpreadwrite.h"
#include "statcodes.h"
#include "unixutil.h"
#ifdef _WIN32
#include <winsock2.h>
#include <Ws2tcpip.h>
#endif
#define MSGTYPE_SHUTDOWN 0
#define MSGTYPE_ADVERTISEMENT 1
#define MSGTYPE_REPLY 2
/************************************************************************
* Function : advertiseAndReplyThread
*
* Parameters:
* IN void *data: Structure containing the search request
*
* Description:
* This function is a wrapper function to reply the search request
* coming from the control point.
*
* Returns: void *
* always return NULL
***************************************************************************/
void *
advertiseAndReplyThread( IN void *data )
{
SsdpSearchReply *arg = ( SsdpSearchReply * ) data;
AdvertiseAndReply( 0, arg->handle,
arg->event.RequestType,
&arg->dest_addr,
arg->event.DeviceType,
arg->event.UDN,
arg->event.ServiceType, arg->MaxAge );
free( arg );
return NULL;
}
/************************************************************************
* 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 ); )
threadArg =
( SsdpSearchReply * ) malloc( sizeof( SsdpSearchReply ) );
if( threadArg == NULL ) {
return;
}
threadArg->handle = handle;
threadArg->dest_addr = ( *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 withing 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 );
}
#ifdef _WIN32
static
void _genaGetIP(char * addr, char * ipAddr, int * portAddr)
{
int curChar;
char * charPtr;
char buf[80];
curChar = 0;
while (addr[curChar] != ':') {
curChar++;
}
strncpy(ipAddr, addr, curChar);
ipAddr[curChar] = 0;
charPtr = addr + curChar + 1;
strcpy(buf, charPtr);
*portAddr = atoi(buf);
}
#endif
/************************************************************************
* Function : NewRequestHandler
*
* Parameters:
* IN struct sockaddr_in * DestAddr: Ip address, to send the reply.
* IN int NumPacket: Number of packet to be sent.
* IN char **RqPacket:Number of packet to be sent.
*
* Description:
* This function works as a request handler which passes the HTTP
* request string to multicast channel then
*
* Returns: void *
* 1 if successful else appropriate error
***************************************************************************/
static int
NewRequestHandler( IN struct sockaddr_in *DestAddr,
IN int NumPacket,
IN char **RqPacket )
{
/*#ifdef _WIN32
struct ip_mreq genaMcastAddr;
int option = 1;
//char ipAddr[80];
//int portAddr;
#endif*/
int socklen = sizeof( struct sockaddr_in );
SOCKET ReplySock;
int NumCopy, Index;
unsigned long replyAddr = inet_addr( LOCAL_HOST );
int ttl = 4; //a/c to UPNP Spec
ReplySock = socket( AF_INET, SOCK_DGRAM, 0 );
if( ReplySock == UPNP_INVALID_SOCKET ) {
DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
"SSDP_LIB: New Request Handler:"
"Error in socket operation !!!\n" ) );
return UPNP_E_OUTOF_SOCKET;
}
/*#ifdef _WIN32
memset( ( void * )&genaMcastAddr, 0, sizeof( struct ip_mreq ) );
genaMcastAddr.imr_interface.s_addr = htonl( INADDR_ANY );
genaMcastAddr.imr_multiaddr.s_addr = inet_addr( LOCAL_HOST );
if( setsockopt( ReplySock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
( char * )&genaMcastAddr,
sizeof( struct ip_mreq ) ) != 0 ) {
shutdown( ReplySock, SD_BOTH );
UpnpCloseSocket( ReplySock );
return UPNP_E_SOCKET_ERROR;
}
// result is not checked becuase it will fail in WinMe and Win9x.
setsockopt( ReplySock, IPPROTO_IP,
IP_MULTICAST_TTL, &ttl, sizeof( ttl ) );
if( setsockopt( ReplySock, SOL_SOCKET, SO_BROADCAST,
( const char * )&option, sizeof( option ) ) != 0 ) {
shutdown( ReplySock, SD_BOTH );
UpnpCloseSocket( ReplySock );
return UPNP_E_NETWORK_ERROR;
}
#else*/
setsockopt( ReplySock, IPPROTO_IP, IP_MULTICAST_IF,
( char * )&replyAddr, sizeof( replyAddr ) );
setsockopt( ReplySock, IPPROTO_IP, IP_MULTICAST_TTL,
( char * )&ttl, sizeof( int ) );
//#endif
for( Index = 0; Index < NumPacket; Index++ ) {
int rc;
NumCopy = 0;
while( NumCopy < NUM_COPY ) {
DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
">>> SSDP SEND >>>\n%s\n",
*( RqPacket + Index ) );
)
rc = sendto( ReplySock, *( RqPacket + Index ),
strlen( *( RqPacket + Index ) ),
0, ( struct sockaddr * )DestAddr, socklen );
imillisleep( SSDP_PAUSE );
++NumCopy;
}
}
shutdown( ReplySock, SD_BOTH );
UpnpCloseSocket( ReplySock );
return UPNP_E_SUCCESS;
}
/************************************************************************
* Function : CreateServiceRequestPacket
*
* Parameters:
* IN int msg_type : type of the message ( Search Reply, Advertisement
* or Shutdown )
* IN char * nt : ssdp type
* IN char * usn : unique service name ( go in the HTTP Header)
* IN char * location :Location URL.
* IN int duration :Service duration in sec.
* OUT char** packet :Output buffer filled with HTTP statement.
*
* Description:
* This function creates a HTTP request packet. Depending
* on the input parameter it either creates a service advertisement
* request or service shutdown request etc.
*
* Returns: void
*
***************************************************************************/
void
CreateServicePacket( IN int msg_type,
IN char *nt,
IN char *usn,
IN char *location,
IN int duration,
OUT char **packet )
{
int ret_code;
char *nts;
membuffer buf;
//Notf=0 means service shutdown,
//Notf=1 means service advertisement, Notf =2 means reply
membuffer_init( &buf );
buf.size_inc = 30;
*packet = NULL;
if( msg_type == MSGTYPE_REPLY ) {
ret_code = http_MakeMessage( &buf, 1, 1,
"R" "sdc" "D" "s" "ssc" "S" "ssc"
"ssc" "c", HTTP_OK,
"CACHE-CONTROL: max-age=", duration,
"EXT:\r\n", "LOCATION: ", location,
"ST: ", nt, "USN: ", usn );
if( ret_code != 0 ) {
return;
}
} else if( msg_type == MSGTYPE_ADVERTISEMENT ||
msg_type == MSGTYPE_SHUTDOWN ) {
if( msg_type == MSGTYPE_ADVERTISEMENT ) {
nts = "ssdp:alive";
} else // shutdown
{
nts = "ssdp:byebye";
}
// NOTE: The CACHE-CONTROL and LOCATION headers are not present in
// a shutdown msg, but are present here for MS WinMe interop.
ret_code = http_MakeMessage( &buf, 1, 1,
"Q" "sssdc" "sdc" "ssc" "ssc" "ssc"
"S" "ssc" "c", HTTPMETHOD_NOTIFY, "*",
1, "HOST: ", SSDP_IP, ":", SSDP_PORT,
"CACHE-CONTROL: max-age=", duration,
"LOCATION: ", location, "NT: ", nt,
"NTS: ", nts, "USN: ", usn );
if( ret_code != 0 ) {
return;
}
} else {
assert( 0 ); // unknown msg
}
*packet = membuffer_detach( &buf ); // return msg
membuffer_destroy( &buf );
return;
}
/************************************************************************
* Function : DeviceAdvertisement
*
* Parameters:
* IN char * DevType : type of the device
* IN int RootDev: flag to indicate if the device is root device
* IN char * nt : ssdp type
* IN char * usn : unique service name
* IN char * location :Location URL.
* IN int duration :Service duration in sec.
*
* Description:
* This function creates the device advertisement request based on
* the input parameter, and send it to the multicast channel.
*
* Returns: int
* UPNP_E_SUCCESS if successful else appropriate error
***************************************************************************/
int
DeviceAdvertisement( IN char *DevType,
int RootDev,
char *Udn,
IN char *Location,
IN int Duration )
{
struct sockaddr_in DestAddr;
//char Mil_Nt[LINE_SIZE]
char Mil_Usn[LINE_SIZE];
char *msgs[3];
int ret_code;
DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
"In function SendDeviceAdvertisemenrt\n" );
)
DestAddr.sin_family = AF_INET;
DestAddr.sin_addr.s_addr = inet_addr( SSDP_IP );
DestAddr.sin_port = htons( SSDP_PORT );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -