|
|
#include "precomp.h"
#ifdef IP_OBEX
//#include <atlconv.h>
#include "dynamlnk.h"
#include <malloc.h>
#include <ssdp.h>
#define IMPL
// dynamic DLL stuff for SSDP
typedef enum _SsdpApiIndex { SSDP_REGISTER_SERVICE = 0, SSDP_DEREGISTER_SERVICE, SSDP_STARTUP, SSDP_FIND_SERVICES, SSDP_GET_FIRST_SERVICE, SSDP_GET_NEXT_SERVICE, SSDP_FIND_SERVICES_CLOSE, SSDP_CLEANUP, SSDP_REGISTER_NOTIFICATION, SSDP_DEREGISTER_NOTIFICATION, SSDP_CLEANUP_CACHE, };
// not subject to localization
static LPCSTR g_apchFunctionNames[] = { "RegisterService", "DeregisterService", "SsdpStartup", "FindServices", "GetFirstService", "GetNextService", "FindServicesClose", "SsdpCleanup", "RegisterNotification", "DeregisterNotification", "CleanupCache", NULL };
//
// NOTE: i have copied the following structs and defines
// so that we are not dependant on the SSDP headers, most of
// which we weren't using.
//
// from ssdp.h
//
typedef struct _SSDP_MESSAGE { /* [string] */ LPSTR szType; /* [string] */ LPSTR szLocHeader; /* [string] */ LPSTR szAltHeaders; /* [string] */ LPSTR szUSN; /* [string] */ LPSTR szSid; DWORD iSeq; UINT iLifeTime; /* [string] */ LPSTR szContent; } SSDP_MESSAGE;
typedef struct _SSDP_MESSAGE *PSSDP_MESSAGE;
typedef enum _NOTIFY_TYPE { NOTIFY_ALIVE, NOTIFY_PROP_CHANGE } NOTIFY_TYPE;
typedef enum _SSDP_CALLBACK_TYPE { SSDP_FOUND = 0, SSDP_ALIVE = 1, SSDP_BYEBYE = 2, SSDP_DONE = 3, SSDP_EVENT = 4, SSDP_DEAD = 5, } SSDP_CALLBACK_TYPE, *PSSDP_CALLBACK_TYPE;
typedef void (WINAPI *SERVICE_CALLBACK_FUNC)(SSDP_CALLBACK_TYPE CallbackType, CONST SSDP_MESSAGE *pSsdpService, void *pContext); //
// from ssdperror.h
//
#define SSDP_ERROR_BASE 18000
#define ERROR_NO_MORE_SERVICES SSDP_ERROR_BASE+1
//
// end copy header
//
typedef HANDLE (*REGISTERSERVICE) (PSSDP_MESSAGE, DWORD); typedef BOOL (*DEREGISTERSERVICE) (HANDLE, BOOL); typedef BOOL (*SSDPSTARTUP) (); typedef HANDLE (*FINDSERVICES) (char *, void *, BOOL); typedef BOOL (*GETFIRSTSERVICE) (HANDLE, PSSDP_MESSAGE *); typedef BOOL (*GETNEXTSERVICE) (HANDLE, PSSDP_MESSAGE *); typedef BOOL (*FINDSERVICESCLOSE) (HANDLE); typedef void (*SSDPCLEANUP) (); typedef HANDLE (*REGISTERNOTIFICAION) (NOTIFY_TYPE, char *, char *, SERVICE_CALLBACK_FUNC,void *); typedef BOOL (*DEREGISTERNOTIFICAION) (HANDLE); typedef BOOL (*CLEANUPCACHE) ();
DynamicDLL g_SsdpDLL( TEXT("SSDPAPI.DLL"), g_apchFunctionNames );
#define USES_CONVERSION int _convert = 0; _convert; UINT _acp = CP_ACP; _acp; LPCWSTR _lpw = NULL; _lpw; LPCSTR _lpa = NULL; _lpa
inline LPSTR WINAPI AtlW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars, UINT acp) { // verify that no illegal character present
// since lpa was allocated based on the size of lpw
// don't worry about the number of chars
lpa[0] = '\0'; WideCharToMultiByte(acp, 0, lpw, -1, lpa, nChars, NULL, NULL); return lpa; }
#define W2A(lpw) (\
((_lpw = lpw) == NULL) ? NULL : (\ _convert = (lstrlenW(_lpw)+1)*2,\ AtlW2AHelper((LPSTR) alloca(_convert), _lpw, _convert, _acp)))
#define Trace0 DbgPrint
#define Trace1 DbgPrint
#define Trace2 DbgPrint
#define Trace3 DbgPrint
#define Trace4 DbgPrint
DWORD DeviceChangeWorker( PVOID Context );
#define T2A W2A
const WCHAR *c_szObex= TEXT("OBEX");
BOOL RegisterWithSsdp( const in_addr *IpAddress, SOCKET *listenSocket, HANDLE *SsdpHandle, DWORD dwPort )
{ SOCKET sock = INVALID_SOCKET; sockaddr_in saListen = {0}; int nRet = 0; TCHAR szHostNameW[MAX_PATH]; SSDP_MESSAGE message = {0}; DWORD dwFlags = 0, dwSize; HRESULT hr; char szPort[MAX_PATH]; char * pszAddr;
USES_CONVERSION;
//
// establish listen socket
//
sock = socket( AF_INET, SOCK_STREAM, 0 );
if ( sock == INVALID_SOCKET ) { Trace2( "RegisterWithSsdp - socket() for port 0x%lx failed! 0x%lx\n", dwPort); goto Error; }
//
// now get ready to bind it to the address
//
saListen.sin_addr=*IpAddress; saListen.sin_family = AF_INET; saListen.sin_port = (short) dwPort;
nRet = bind( sock, (const struct sockaddr *)&saListen, sizeof(saListen) );
if( nRet == SOCKET_ERROR ) {
Trace2( "RegisterWithSsdp - bind on port 0x%lx failed! 0x%lx\n", dwPort, WSAGetLastError()); goto Error; }
// set socket into listening mode
nRet = listen( sock, 2 ); if ( nRet == SOCKET_ERROR ) {
Trace2( "RegisterWithSsdp - listen on port 0x%lx failed! 0x%lx\n", dwPort, WSAGetLastError()); goto Error; }
// register with SSDP so other people can find us
_itoa(dwPort, szPort, 10); // gethostname(szHostName, sizeof(szHostName));
dwSize = sizeof(szHostNameW); GetComputerNameEx(ComputerNameDnsFullyQualified, szHostNameW, &dwSize);
pszAddr = inet_ntoa(*IpAddress);
// now fill in the struct
message.szType = T2A(c_szObex); message.szLocHeader = T2A(szHostNameW); message.szAltHeaders = szPort; message.szUSN = pszAddr; message.szSid = ""; message.iLifeTime = 1 * 60; dwFlags = 0;//SSDP_SERVICE_PERSISTENT;
// call the SSDP api
if ( g_SsdpDLL.LoadFunctionPointers() ) { // init SSDP
//
(SSDPSTARTUP) g_SsdpDLL[SSDP_STARTUP]();
*SsdpHandle = ((REGISTERSERVICE) g_SsdpDLL[SSDP_REGISTER_SERVICE])(&message, dwFlags);
if (SsdpHandle == INVALID_HANDLE_VALUE) {
Trace2( "SSDP RegisterService on port 0x%lx failed! 0x%lx\n", dwPort, GetLastError()); goto Error;
} else {
Trace3("RegisterWithSsdp - host name %ws, addr %s, port %d\n", szHostNameW, pszAddr, dwPort); } }
*listenSocket = sock;
return TRUE;
Error: closesocket(sock); sock = INVALID_SOCKET;
return FALSE;
}
VOID UnregisterWithSsdp( HANDLE SsdpHandle )
{ // call the SSDP api
if ( g_SsdpDLL.LoadFunctionPointers() ) {
((DEREGISTERSERVICE) g_SsdpDLL[SSDP_DEREGISTER_SERVICE])(SsdpHandle, 0); //
// shutdown ssdp
//
(SSDPCLEANUP) g_SsdpDLL[SSDP_CLEANUP](); } return;
}
typedef struct _SSDP_CONTROL {
HANDLE hNotify; HWND hWnd; UINT Msg;
LONG ReferenceCount;
BOOL Closing;
SOCKET Socket;
WSAOVERLAPPED WsOverlapped;
TCHAR HostName[256];
CRITICAL_SECTION Lock;
} SSDP_CONTROL, *PSSDP_CONTROL;
VOID RemoveReferenceOnSsdpControl( PSSDP_CONTROL Control );
void SsdpCallbackHandler( SSDP_CALLBACK_TYPE ct, CONST SSDP_MESSAGE *pSsdpMessage, HANDLE Context )
{
PSSDP_CONTROL Control=(PSSDP_CONTROL)Context;
DbgPrint("SsdpCallbackHandler: %d\n",ct);
switch (ct) {
case SSDP_DONE:
case SSDP_ALIVE: case SSDP_FOUND: case SSDP_EVENT:
DbgPrint("SsdpCallabck: Name=%s, Address=%s\n",pSsdpMessage->szLocHeader,pSsdpMessage->szUSN);
break;
default:
break; }
PostMessage( Control->hWnd, Control->Msg, ct, NULL );
return;
}
LONG RefreshSsdp( VOID )
{ USES_CONVERSION;
g_SsdpDLL.LoadFunctionPointers();
// init SSDP
//
(SSDPSTARTUP) g_SsdpDLL[SSDP_STARTUP]();
(CLEANUPCACHE) g_SsdpDLL[SSDP_CLEANUP_CACHE]();
HANDLE hSearch;
hSearch = ((FINDSERVICES) g_SsdpDLL[SSDP_FIND_SERVICES])(T2A(c_szObex), NULL, TRUE);
if (hSearch != INVALID_HANDLE_VALUE) {
((FINDSERVICESCLOSE) g_SsdpDLL[SSDP_FIND_SERVICES_CLOSE])(hSearch); }
// shutdown ssdp
(SSDPCLEANUP) g_SsdpDLL[SSDP_CLEANUP]();
return ERROR_SUCCESS;
}
VOID SsdpAddressListChangeHandler( DWORD Error, DWORD BytesTransfered, LPWSAOVERLAPPED WsOverlapped, DWORD Flags )
{
PSSDP_CONTROL Control=(PSSDP_CONTROL)WsOverlapped->hEvent;
RefreshSsdp();
PostMessage( Control->hWnd, Control->Msg, -1, NULL );
DeviceChangeWorker(Control);
}
DWORD DeviceChangeWorker( PVOID Context )
{ PSSDP_CONTROL Control=(PSSDP_CONTROL)Context; int err; DWORD BytesReturned;
if (!Control->Closing) {
ZeroMemory(&Control->WsOverlapped,sizeof(Control->WsOverlapped));
Control->WsOverlapped.hEvent=Control;
err=WSAIoctl( Control->Socket, SIO_ADDRESS_LIST_CHANGE, NULL, 0, NULL, 0, &BytesReturned, &Control->WsOverlapped, SsdpAddressListChangeHandler );
if (err == SOCKET_ERROR) {
if (WSAGetLastError() != WSA_IO_PENDING) { //
// the call failed and return value was not pending
//
RemoveReferenceOnSsdpControl(Control); } }
} else {
RemoveReferenceOnSsdpControl(Control);
}
return 0; }
HANDLE CreateSsdpDiscoveryObject( LPSTR Service, HWND hWnd, UINT Msg )
{
PSSDP_CONTROL Control; ULONG dwSize; BOOL bResult;
USES_CONVERSION;
Control = new SSDP_CONTROL;
if (Control == NULL) {
return NULL;
}
InitializeCriticalSection(&Control->Lock);
dwSize=sizeof(Control->HostName)/sizeof(TCHAR); GetComputerNameEx(ComputerNameDnsFullyQualified, Control->HostName, &dwSize);
Control->Closing =FALSE; Control->Socket = INVALID_SOCKET; Control->ReferenceCount=1; Control->hWnd=hWnd; Control->Msg=Msg;
g_SsdpDLL.LoadFunctionPointers();
// init SSDP
//
(SSDPSTARTUP) g_SsdpDLL[SSDP_STARTUP](); #if 1
// Tell SSDP to start notifying us of devices
//
(CLEANUPCACHE) g_SsdpDLL[SSDP_CLEANUP_CACHE](); #endif
// register for notification as devices come and go
//
Control->hNotify = ((REGISTERNOTIFICAION) g_SsdpDLL[SSDP_REGISTER_NOTIFICATION])( NOTIFY_ALIVE, T2A(c_szObex), NULL, SsdpCallbackHandler, Control );
if ((Control->hNotify == INVALID_HANDLE_VALUE) || (Control->hNotify == NULL)) {
Trace1("CIpTransport::InitiateDiscovery - RegisterNotification failed! %d", GetLastError());
goto CleanUp; } #if 1
HANDLE hSearch;
hSearch = ((FINDSERVICES) g_SsdpDLL[SSDP_FIND_SERVICES])(T2A(c_szObex), NULL, TRUE);
if (hSearch != INVALID_HANDLE_VALUE) {
((FINDSERVICESCLOSE) g_SsdpDLL[SSDP_FIND_SERVICES_CLOSE])(hSearch); } #endif
// RefreshSsdp();
Control->Socket=socket(AF_INET,SOCK_STREAM,0);
if (Control->Socket == INVALID_SOCKET) {
goto CleanUp; }
//
// add one refcount for the workitem
//
InterlockedIncrement(&Control->ReferenceCount);
bResult=QueueUserWorkItem( DeviceChangeWorker, Control, WT_EXECUTEINIOTHREAD );
if (bResult) {
return Control;
} else {
RemoveReferenceOnSsdpControl(Control); }
CleanUp:
RemoveReferenceOnSsdpControl(Control);
return NULL;
}
VOID RemoveReferenceOnSsdpControl( PSSDP_CONTROL Control )
{
LONG CurrentCount;
CurrentCount=InterlockedDecrement(&Control->ReferenceCount);
if (CurrentCount == 0) { //
// cleanup notifications with SSDP
//
if (Control->hNotify != INVALID_HANDLE_VALUE) {
((DEREGISTERNOTIFICAION) g_SsdpDLL[SSDP_DEREGISTER_NOTIFICATION])(Control->hNotify); }
(SSDPCLEANUP) g_SsdpDLL[SSDP_CLEANUP]();
DeleteCriticalSection(&Control->Lock);
delete Control;
}
return;
}
VOID CloseSsdpDiscoveryObject( HANDLE Context )
{
PSSDP_CONTROL Control=(PSSDP_CONTROL)Context;
Control->Closing=TRUE;
closesocket(Control->Socket);
RemoveReferenceOnSsdpControl(Control);
}
LONG GetSsdpDevices( HANDLE Context, POBEX_DEVICE_LIST DeviceList, ULONG *ListLength )
{
PSSDP_CONTROL Control=(PSSDP_CONTROL)Context;
HANDLE hSearch = NULL; LPSTR pszTypeURI; ULONG BytesAvailible=*ListLength; ULONG BytesUsed=0; POBEX_DEVICE CurrentDevice=&DeviceList->DeviceList[0];
USES_CONVERSION;
ZeroMemory(DeviceList,BytesAvailible);
if (BytesAvailible < FIELD_OFFSET(OBEX_DEVICE_LIST,DeviceList)) {
return ERROR_INSUFFICIENT_BUFFER; }
BytesUsed+=FIELD_OFFSET(OBEX_DEVICE_LIST,DeviceList);
DeviceList->DeviceCount=0;
pszTypeURI = T2A(c_szObex);
hSearch = ((FINDSERVICES) g_SsdpDLL[SSDP_FIND_SERVICES])(pszTypeURI, NULL, FALSE);
if (hSearch != INVALID_HANDLE_VALUE) {
SSDP_MESSAGE * pSsdpMessage = NULL;
BOOL fContinue = ((GETFIRSTSERVICE) g_SsdpDLL[SSDP_GET_FIRST_SERVICE])(hSearch, &pSsdpMessage);
ASSERT(DeviceList->DeviceCount == 0);
while (fContinue) {
ULONG Address = inet_addr(pSsdpMessage->szUSN); int Port = atoi(pSsdpMessage->szAltHeaders);
if (BytesAvailible >= BytesUsed + sizeof(OBEX_DEVICE)) { //
// we have enough romm in the buffer for this one
//
MultiByteToWideChar( CP_ACP, 0, pSsdpMessage->szLocHeader, -1, CurrentDevice->DeviceName, sizeof(CurrentDevice->DeviceName)/sizeof(WCHAR) );
DbgPrint("irmon: count=%d, remote=%ws, host=%ws\n",DeviceList->DeviceCount,CurrentDevice->DeviceName,Control->HostName);
if (lstrcmpi(CurrentDevice->DeviceName,Control->HostName) != 0) { //
// not this machine
//
CurrentDevice->DeviceType=TYPE_IP;
CurrentDevice->DeviceName[sizeof(CurrentDevice->DeviceName)/sizeof(WCHAR)]=L'\0';
CurrentDevice->DeviceSpecific.s.Ip.IpAddress=Address;
CurrentDevice->DeviceSpecific.s.Ip.Port=(USHORT)Port;
CurrentDevice++;
DeviceList->DeviceCount++; }
}
BytesUsed+=sizeof(OBEX_DEVICE);
fContinue = ((GETNEXTSERVICE) g_SsdpDLL[SSDP_GET_NEXT_SERVICE])(hSearch, &pSsdpMessage); }
((FINDSERVICESCLOSE) g_SsdpDLL[SSDP_FIND_SERVICES_CLOSE])(hSearch);
if (BytesUsed > BytesAvailible) {
*ListLength=BytesUsed; return ERROR_INSUFFICIENT_BUFFER; }
DbgPrint("irmon: %d ip device found\n",DeviceList->DeviceCount);
return ERROR_SUCCESS;
} else { //
// Check to see if the handle is invalid merely because no devices
// are present
//
DWORD dwError = GetLastError();
if (dwError == ERROR_NO_MORE_SERVICES) {
Trace0("FindServices failed because no devices were present. This is OK"); dwError=ERROR_SUCCESS;
} else {
Trace1("FindServices call failed in FindByDeviceType %lx", dwError); } return dwError; }
}
#endif //IP_OBEX
|