forked from qt-creator/qt-creator
mdnsds: implement bonjour fallback on windows
* background task requiring no special privilegies (apart firewall) * started by zeroconf only if bonjour is not installed/not working * avoids console * remove some compilation warnings Change-Id: I50a6aee477ccf681a8e8ab084059190091954006 Reviewed-by: Jarek Kobus <jaroslaw.kobus@nokia.com>
This commit is contained in:
1518
src/tools/mdnssd/CommonServices.h
Normal file
1518
src/tools/mdnssd/CommonServices.h
Normal file
File diff suppressed because it is too large
Load Diff
3075
src/tools/mdnssd/DebugServices.c
Normal file
3075
src/tools/mdnssd/DebugServices.c
Normal file
File diff suppressed because it is too large
Load Diff
11
src/tools/mdnssd/EventLog.mc
Normal file
11
src/tools/mdnssd/EventLog.mc
Normal file
@@ -0,0 +1,11 @@
|
||||
MessageIdTypedef=WORD
|
||||
LanguageNames=(English=0x409:MSG00409)
|
||||
|
||||
MessageId=100
|
||||
SymbolicName=MDNSRESPONDER_LOG
|
||||
Severity=Success
|
||||
Facility=Application
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
484
src/tools/mdnssd/Firewall.cpp
Normal file
484
src/tools/mdnssd/Firewall.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// <rdar://problem/4278931> Doesn't compile correctly with latest Platform SDK
|
||||
|
||||
#if !defined(_WIN32_DCOM)
|
||||
# define _WIN32_DCOM
|
||||
#endif
|
||||
|
||||
|
||||
#include "Firewall.h"
|
||||
#include <windows.h>
|
||||
#include <crtdbg.h>
|
||||
#include <netfw.h>
|
||||
#include <objbase.h>
|
||||
#include <oleauto.h>
|
||||
|
||||
|
||||
static const int kMaxTries = 30;
|
||||
static const int kRetrySleepPeriod = 1 * 1000; // 1 second
|
||||
|
||||
|
||||
static OSStatus
|
||||
mDNSFirewallInitialize(OUT INetFwProfile ** fwProfile)
|
||||
{
|
||||
INetFwMgr * fwMgr = NULL;
|
||||
INetFwPolicy * fwPolicy = NULL;
|
||||
int numRetries = 0;
|
||||
HRESULT err = kNoErr;
|
||||
|
||||
_ASSERT(fwProfile != NULL);
|
||||
|
||||
*fwProfile = NULL;
|
||||
|
||||
// Use COM to get a reference to the firewall settings manager. This
|
||||
// call will fail on anything other than XP SP2
|
||||
|
||||
err = CoCreateInstance( __uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&fwMgr );
|
||||
require(SUCCEEDED(err) && ( fwMgr != NULL ), exit);
|
||||
|
||||
// Use the reference to get the local firewall policy
|
||||
|
||||
err = fwMgr->get_LocalPolicy(&fwPolicy);
|
||||
require(SUCCEEDED(err) && ( fwPolicy != NULL ), exit);
|
||||
|
||||
// Use the reference to get the extant profile. Empirical evidence
|
||||
// suggests that there is the potential for a race condition when a system
|
||||
// service whose startup type is automatic calls this method.
|
||||
// This is true even when the service declares itself to be dependent
|
||||
// on the firewall service. Re-trying the method will succeed within
|
||||
// a few seconds.
|
||||
|
||||
do
|
||||
{
|
||||
err = fwPolicy->get_CurrentProfile(fwProfile);
|
||||
|
||||
if (err)
|
||||
{
|
||||
Sleep(kRetrySleepPeriod);
|
||||
}
|
||||
}
|
||||
while (err && (numRetries++ < kMaxTries));
|
||||
|
||||
require(SUCCEEDED(err), exit);
|
||||
|
||||
err = kNoErr;
|
||||
|
||||
exit:
|
||||
|
||||
// Release temporary COM objects
|
||||
|
||||
if (fwPolicy != NULL)
|
||||
{
|
||||
fwPolicy->Release();
|
||||
}
|
||||
|
||||
if (fwMgr != NULL)
|
||||
{
|
||||
fwMgr->Release();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mDNSFirewallCleanup
|
||||
(
|
||||
IN INetFwProfile * fwProfile
|
||||
)
|
||||
{
|
||||
// Call Release on the COM reference.
|
||||
|
||||
if (fwProfile != NULL)
|
||||
{
|
||||
fwProfile->Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static OSStatus
|
||||
mDNSFirewallAppIsEnabled
|
||||
(
|
||||
IN INetFwProfile * fwProfile,
|
||||
IN const wchar_t * fwProcessImageFileName,
|
||||
OUT BOOL * fwAppEnabled
|
||||
)
|
||||
{
|
||||
BSTR fwBstrProcessImageFileName = NULL;
|
||||
VARIANT_BOOL fwEnabled;
|
||||
INetFwAuthorizedApplication * fwApp = NULL;
|
||||
INetFwAuthorizedApplications* fwApps = NULL;
|
||||
OSStatus err = kNoErr;
|
||||
|
||||
_ASSERT(fwProfile != NULL);
|
||||
_ASSERT(fwProcessImageFileName != NULL);
|
||||
_ASSERT(fwAppEnabled != NULL);
|
||||
|
||||
*fwAppEnabled = FALSE;
|
||||
|
||||
// Get the list of authorized applications
|
||||
|
||||
err = fwProfile->get_AuthorizedApplications(&fwApps);
|
||||
require(SUCCEEDED(err) && ( fwApps != NULL ), exit);
|
||||
|
||||
fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
|
||||
require_action( ( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr);
|
||||
|
||||
// Look for us
|
||||
|
||||
err = fwApps->Item(fwBstrProcessImageFileName, &fwApp);
|
||||
|
||||
if (SUCCEEDED(err) && ( fwApp != NULL ) )
|
||||
{
|
||||
// It's listed, but is it enabled?
|
||||
|
||||
err = fwApp->get_Enabled(&fwEnabled);
|
||||
require(SUCCEEDED(err), exit);
|
||||
|
||||
if (fwEnabled != VARIANT_FALSE)
|
||||
{
|
||||
// Yes, it's enabled
|
||||
|
||||
*fwAppEnabled = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
err = kNoErr;
|
||||
|
||||
exit:
|
||||
|
||||
// Deallocate the BSTR
|
||||
|
||||
if ( fwBstrProcessImageFileName != NULL )
|
||||
{
|
||||
SysFreeString(fwBstrProcessImageFileName);
|
||||
}
|
||||
|
||||
// Release the COM objects
|
||||
|
||||
if (fwApp != NULL)
|
||||
{
|
||||
fwApp->Release();
|
||||
}
|
||||
|
||||
if (fwApps != NULL)
|
||||
{
|
||||
fwApps->Release();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static OSStatus
|
||||
mDNSFirewallAddApp
|
||||
(
|
||||
IN INetFwProfile * fwProfile,
|
||||
IN const wchar_t * fwProcessImageFileName,
|
||||
IN const wchar_t * fwName
|
||||
)
|
||||
{
|
||||
BOOL fwAppEnabled;
|
||||
BSTR fwBstrName = NULL;
|
||||
BSTR fwBstrProcessImageFileName = NULL;
|
||||
INetFwAuthorizedApplication * fwApp = NULL;
|
||||
INetFwAuthorizedApplications* fwApps = NULL;
|
||||
OSStatus err = S_OK;
|
||||
|
||||
_ASSERT(fwProfile != NULL);
|
||||
_ASSERT(fwProcessImageFileName != NULL);
|
||||
_ASSERT(fwName != NULL);
|
||||
|
||||
// First check to see if the application is already authorized.
|
||||
err = mDNSFirewallAppIsEnabled( fwProfile, fwProcessImageFileName, &fwAppEnabled );
|
||||
require_noerr(err, exit);
|
||||
|
||||
// Only add the application if it isn't enabled
|
||||
|
||||
if (!fwAppEnabled)
|
||||
{
|
||||
// Get the list of authorized applications
|
||||
|
||||
err = fwProfile->get_AuthorizedApplications(&fwApps);
|
||||
require(SUCCEEDED(err) && ( fwApps != NULL ), exit);
|
||||
|
||||
// Create an instance of an authorized application.
|
||||
|
||||
err = CoCreateInstance( __uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&fwApp );
|
||||
require(SUCCEEDED(err) && ( fwApp != NULL ), exit);
|
||||
|
||||
fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
|
||||
require_action(( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr);
|
||||
|
||||
// Set the executable file name
|
||||
|
||||
err = fwApp->put_ProcessImageFileName(fwBstrProcessImageFileName);
|
||||
require(SUCCEEDED(err), exit);
|
||||
|
||||
fwBstrName = SysAllocString(fwName);
|
||||
require_action( ( fwBstrName != NULL ) && ( SysStringLen(fwBstrName) > 0 ), exit, err = kNoMemoryErr);
|
||||
|
||||
// Set the friendly name
|
||||
|
||||
err = fwApp->put_Name(fwBstrName);
|
||||
require(SUCCEEDED(err), exit);
|
||||
|
||||
// Now add the application
|
||||
|
||||
err = fwApps->Add(fwApp);
|
||||
require(SUCCEEDED(err), exit);
|
||||
}
|
||||
|
||||
err = kNoErr;
|
||||
|
||||
exit:
|
||||
|
||||
// Deallocate the BSTR objects
|
||||
|
||||
if ( fwBstrName != NULL )
|
||||
{
|
||||
SysFreeString(fwBstrName);
|
||||
}
|
||||
|
||||
if ( fwBstrProcessImageFileName != NULL )
|
||||
{
|
||||
SysFreeString(fwBstrProcessImageFileName);
|
||||
}
|
||||
|
||||
// Release the COM objects
|
||||
|
||||
if (fwApp != NULL)
|
||||
{
|
||||
fwApp->Release();
|
||||
}
|
||||
|
||||
if (fwApps != NULL)
|
||||
{
|
||||
fwApps->Release();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static OSStatus
|
||||
|
||||
mDNSFirewallIsFileAndPrintSharingEnabled
|
||||
|
||||
(
|
||||
|
||||
IN INetFwProfile * fwProfile,
|
||||
|
||||
OUT BOOL * fwServiceEnabled
|
||||
|
||||
)
|
||||
|
||||
{
|
||||
|
||||
VARIANT_BOOL fwEnabled;
|
||||
|
||||
INetFwService* fwService = NULL;
|
||||
|
||||
INetFwServices* fwServices = NULL;
|
||||
|
||||
OSStatus err = S_OK;
|
||||
|
||||
|
||||
|
||||
_ASSERT(fwProfile != NULL);
|
||||
|
||||
_ASSERT(fwServiceEnabled != NULL);
|
||||
|
||||
|
||||
|
||||
*fwServiceEnabled = FALSE;
|
||||
|
||||
|
||||
|
||||
// Retrieve the globally open ports collection.
|
||||
|
||||
err = fwProfile->get_Services(&fwServices);
|
||||
|
||||
require( SUCCEEDED( err ), exit );
|
||||
|
||||
|
||||
|
||||
// Attempt to retrieve the globally open port.
|
||||
|
||||
err = fwServices->Item(NET_FW_SERVICE_FILE_AND_PRINT, &fwService);
|
||||
|
||||
require( SUCCEEDED( err ), exit );
|
||||
|
||||
|
||||
|
||||
// Find out if the globally open port is enabled.
|
||||
|
||||
err = fwService->get_Enabled(&fwEnabled);
|
||||
|
||||
require( SUCCEEDED( err ), exit );
|
||||
|
||||
if (fwEnabled != VARIANT_FALSE)
|
||||
|
||||
{
|
||||
|
||||
*fwServiceEnabled = TRUE;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
exit:
|
||||
|
||||
|
||||
|
||||
// Release the globally open port.
|
||||
|
||||
if (fwService != NULL)
|
||||
|
||||
{
|
||||
|
||||
fwService->Release();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Release the globally open ports collection.
|
||||
|
||||
if (fwServices != NULL)
|
||||
|
||||
{
|
||||
|
||||
fwServices->Release();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
|
||||
OSStatus
|
||||
mDNSAddToFirewall
|
||||
(
|
||||
LPWSTR executable,
|
||||
LPWSTR name
|
||||
)
|
||||
{
|
||||
INetFwProfile * fwProfile = NULL;
|
||||
HRESULT comInit = E_FAIL;
|
||||
OSStatus err = kNoErr;
|
||||
|
||||
// Initialize COM.
|
||||
|
||||
comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
|
||||
|
||||
// Ignore this case. RPC_E_CHANGED_MODE means that COM has already been
|
||||
// initialized with a different mode.
|
||||
|
||||
if (comInit != RPC_E_CHANGED_MODE)
|
||||
{
|
||||
err = comInit;
|
||||
require(SUCCEEDED(err), exit);
|
||||
}
|
||||
|
||||
// Connect to the firewall
|
||||
|
||||
err = mDNSFirewallInitialize(&fwProfile);
|
||||
require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit);
|
||||
|
||||
// Add us to the list of exempt programs
|
||||
|
||||
err = mDNSFirewallAddApp( fwProfile, executable, name );
|
||||
require_noerr(err, exit);
|
||||
|
||||
exit:
|
||||
|
||||
// Disconnect from the firewall
|
||||
|
||||
if ( fwProfile != NULL )
|
||||
{
|
||||
mDNSFirewallCleanup(fwProfile);
|
||||
}
|
||||
|
||||
// De-initialize COM
|
||||
|
||||
if (SUCCEEDED(comInit))
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
BOOL
|
||||
mDNSIsFileAndPrintSharingEnabled( BOOL * retry )
|
||||
{
|
||||
INetFwProfile * fwProfile = NULL;
|
||||
HRESULT comInit = E_FAIL;
|
||||
BOOL enabled = FALSE;
|
||||
OSStatus err = kNoErr;
|
||||
|
||||
// Initialize COM.
|
||||
|
||||
*retry = FALSE;
|
||||
comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
|
||||
|
||||
// Ignore this case. RPC_E_CHANGED_MODE means that COM has already been
|
||||
// initialized with a different mode.
|
||||
|
||||
if (comInit != RPC_E_CHANGED_MODE)
|
||||
{
|
||||
*retry = TRUE;
|
||||
err = comInit;
|
||||
require(SUCCEEDED(err), exit);
|
||||
}
|
||||
|
||||
// Connect to the firewall
|
||||
|
||||
err = mDNSFirewallInitialize(&fwProfile);
|
||||
require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit);
|
||||
|
||||
err = mDNSFirewallIsFileAndPrintSharingEnabled( fwProfile, &enabled );
|
||||
require_noerr( err, exit );
|
||||
|
||||
exit:
|
||||
|
||||
// Disconnect from the firewall
|
||||
|
||||
if ( fwProfile != NULL )
|
||||
{
|
||||
mDNSFirewallCleanup(fwProfile);
|
||||
}
|
||||
|
||||
// De-initialize COM
|
||||
|
||||
if (SUCCEEDED(comInit))
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return enabled;
|
||||
}
|
79
src/tools/mdnssd/Firewall.h
Normal file
79
src/tools/mdnssd/Firewall.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef _Firewall_h
|
||||
|
||||
#define _Firewall_h
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "CommonServices.h"
|
||||
|
||||
#include "DebugServices.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
extern "C"
|
||||
|
||||
{
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
OSStatus
|
||||
|
||||
mDNSAddToFirewall
|
||||
|
||||
(
|
||||
|
||||
LPWSTR executable,
|
||||
|
||||
LPWSTR name
|
||||
|
||||
);
|
||||
|
||||
|
||||
BOOL
|
||||
mDNSIsFileAndPrintSharingEnabled( BOOL * retry );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
906
src/tools/mdnssd/LegacyNATTraversal.c
Normal file
906
src/tools/mdnssd/LegacyNATTraversal.c
Normal file
@@ -0,0 +1,906 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef _LEGACY_NAT_TRAVERSAL_
|
||||
|
||||
#include "stdlib.h" // For strtol()
|
||||
#include "string.h" // For strlcpy(), For strncpy(), strncasecmp()
|
||||
|
||||
#if defined( WIN32 )
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
# define strcasecmp _stricmp
|
||||
# define strncasecmp _strnicmp
|
||||
# define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ;
|
||||
|
||||
static int
|
||||
inet_pton( int family, const char * addr, void * dst )
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
int sslen = sizeof( ss );
|
||||
|
||||
ZeroMemory( &ss, sizeof( ss ) );
|
||||
ss.ss_family = (ADDRESS_FAMILY)family;
|
||||
|
||||
if ( WSAStringToAddressA( (LPSTR)addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 )
|
||||
{
|
||||
if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; }
|
||||
else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; }
|
||||
else return 0;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
#else
|
||||
# include <arpa/inet.h> // For inet_pton()
|
||||
#endif
|
||||
|
||||
#include "mDNSEmbeddedAPI.h"
|
||||
#include "uDNS.h" // For natTraversalHandleAddressReply() etc.
|
||||
|
||||
// used to format SOAP port mapping arguments
|
||||
typedef struct Property_struct
|
||||
{
|
||||
char *name;
|
||||
char *type;
|
||||
char *value;
|
||||
} Property;
|
||||
|
||||
// All of the text parsing in this file is intentionally transparent so that we know exactly
|
||||
// what's being done to the text, with an eye towards preventing security problems.
|
||||
|
||||
// This is an evolving list of useful acronyms to know. Please add to it at will.
|
||||
// ST Service Type
|
||||
// NT Notification Type
|
||||
// USN Unique Service Name
|
||||
// UDN Unique Device Name
|
||||
// UUID Universally Unique Identifier
|
||||
// URN/urn Universal Resource Name
|
||||
|
||||
// Forward declaration because of circular reference:
|
||||
// SendPortMapRequest -> SendSOAPMsgControlAction -> MakeTCPConnection -> tcpConnectionCallback -> handleLNTPortMappingResponse
|
||||
// In the event of a port conflict, handleLNTPortMappingResponse then increments tcpInfo->retries and calls back to SendPortMapRequest to try again
|
||||
mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n);
|
||||
|
||||
#define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (mDNSu16)(n)->tcpInfo.retries)
|
||||
|
||||
// Note that this function assumes src is already NULL terminated
|
||||
mDNSlocal void AllocAndCopy(mDNSu8 **const dst, const mDNSu8 *const src)
|
||||
{
|
||||
if (src == mDNSNULL) return;
|
||||
if ((*dst = mDNSPlatformMemAllocate((mDNSu32)strlen((char*)src) + 1)) == mDNSNULL)
|
||||
{ LogMsg("AllocAndCopy: can't allocate string"); return; }
|
||||
strcpy((char*)*dst, (char*)src);
|
||||
}
|
||||
|
||||
// This function does a simple parse of an HTTP URL that may include a hostname, port, and path
|
||||
// If found in the URL, addressAndPort and path out params will point to newly allocated space (and will leak if they were previously pointing at allocated space)
|
||||
mDNSlocal mStatus ParseHttpUrl(const mDNSu8 *ptr, const mDNSu8 *const end, mDNSu8 **const addressAndPort, mDNSIPPort *const port, mDNSu8 **const path)
|
||||
{
|
||||
// if the data begins with "http://", we assume there is a hostname and possibly a port number
|
||||
if (end - ptr >= 7 && strncasecmp((char*)ptr, "http://", 7) == 0)
|
||||
{
|
||||
int i;
|
||||
const mDNSu8 *stop = end;
|
||||
const mDNSu8 *addrPtr = mDNSNULL;
|
||||
|
||||
ptr += 7; //skip over "http://"
|
||||
if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; }
|
||||
|
||||
// find the end of the host:port
|
||||
addrPtr = ptr;
|
||||
for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break;
|
||||
|
||||
// allocate the buffer (len i+1 so we have space to terminate the string)
|
||||
if ((*addressAndPort = mDNSPlatformMemAllocate(i+1)) == mDNSNULL)
|
||||
{ LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr; }
|
||||
strncpy((char*)*addressAndPort, (char*)ptr, i);
|
||||
(*addressAndPort)[i] = '\0';
|
||||
|
||||
// find the port number in the string, by looking backwards for the ':'
|
||||
stop = ptr; // can't go back farther than the original start
|
||||
ptr = addrPtr; // move ptr to the path part
|
||||
|
||||
for (addrPtr--; addrPtr>stop; addrPtr--)
|
||||
{
|
||||
if (*addrPtr == ':')
|
||||
{
|
||||
addrPtr++; // skip over ':'
|
||||
*port = mDNSOpaque16fromIntVal((mDNSu16)strtol((char*)addrPtr, mDNSNULL, 10)); // store it properly converted
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ptr should now point to the first character we haven't yet processed
|
||||
// everything that remains is the path
|
||||
if (path && ptr < end)
|
||||
{
|
||||
if ((*path = mDNSPlatformMemAllocate((mDNSu32)(end - ptr) + 1)) == mDNSNULL)
|
||||
{ LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr; }
|
||||
strncpy((char*)*path, (char*)ptr, end - ptr);
|
||||
(*path)[end - ptr] = '\0';
|
||||
}
|
||||
|
||||
return mStatus_NoError;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
HTTPCode_NeedMoreData = -1, // No code found in stream
|
||||
HTTPCode_Other = -2, // Valid code other than those below found in stream
|
||||
HTTPCode_Bad = -3,
|
||||
HTTPCode_200 = 200,
|
||||
HTTPCode_404 = 404,
|
||||
HTTPCode_500 = 500,
|
||||
};
|
||||
|
||||
mDNSlocal mDNSs16 ParseHTTPResponseCode(const mDNSu8 **const data, const mDNSu8 *const end)
|
||||
{
|
||||
const mDNSu8 *ptr = *data;
|
||||
const mDNSu8 *code;
|
||||
|
||||
if (end - ptr < 5) return HTTPCode_NeedMoreData;
|
||||
if (strncasecmp((char*)ptr, "HTTP/", 5) != 0) return HTTPCode_Bad;
|
||||
ptr += 5;
|
||||
// should we care about the HTTP protocol version?
|
||||
|
||||
// look for first space, which must come before first LF
|
||||
while (ptr && ptr != end)
|
||||
{
|
||||
if (*ptr == '\n') return HTTPCode_Bad;
|
||||
if (*ptr == ' ') break;
|
||||
ptr++;
|
||||
}
|
||||
if (ptr == end) return HTTPCode_NeedMoreData;
|
||||
ptr++;
|
||||
|
||||
if (end - ptr < 3) return HTTPCode_NeedMoreData;
|
||||
|
||||
code = ptr;
|
||||
ptr += 3;
|
||||
while (ptr && ptr != end)
|
||||
{
|
||||
if (*ptr == '\n') break;
|
||||
ptr++;
|
||||
}
|
||||
if (ptr == end) return HTTPCode_NeedMoreData;
|
||||
*data = ++ptr;
|
||||
|
||||
if (memcmp((char*)code, "200", 3) == 0) return HTTPCode_200;
|
||||
if (memcmp((char*)code, "404", 3) == 0) return HTTPCode_404;
|
||||
if (memcmp((char*)code, "500", 3) == 0) return HTTPCode_500;
|
||||
|
||||
LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code[0], code[1], code[2]);
|
||||
return HTTPCode_Other;
|
||||
}
|
||||
|
||||
// This function parses the xml body of the device description response from the router. Basically, we look to
|
||||
// make sure this is a response referencing a service we care about (WANIPConnection or WANPPPConnection),
|
||||
// look for the "controlURL" header immediately following, and copy the addressing and URL info we need
|
||||
mDNSlocal void handleLNTDeviceDescriptionResponse(tcpLNTInfo *tcpInfo)
|
||||
{
|
||||
mDNS *m = tcpInfo->m;
|
||||
const mDNSu8 *ptr = tcpInfo->Reply;
|
||||
const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread;
|
||||
const mDNSu8 *stop;
|
||||
mDNSs16 http_result;
|
||||
|
||||
if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return; // already have the info we need
|
||||
|
||||
http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
|
||||
if (http_result == HTTPCode_404) LNT_ClearState(m);
|
||||
if (http_result != HTTPCode_200)
|
||||
{
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP code: %d", http_result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Always reset our flag to use WANIPConnection. We'll use WANPPPConnection if we find it and don't find WANIPConnection.
|
||||
m->UPnPWANPPPConnection = mDNSfalse;
|
||||
|
||||
// find either service we care about
|
||||
while (ptr && ptr < end)
|
||||
{
|
||||
if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break;
|
||||
ptr++;
|
||||
}
|
||||
if (ptr == end)
|
||||
{
|
||||
ptr = tcpInfo->Reply;
|
||||
while (ptr && ptr < end)
|
||||
{
|
||||
if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0))
|
||||
{
|
||||
m->UPnPWANPPPConnection = mDNStrue;
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; }
|
||||
|
||||
// find "controlURL", starting from where we left off
|
||||
while (ptr && ptr < end)
|
||||
{
|
||||
if ((*ptr & 0xDF) == 'C' && (strncasecmp((char*)ptr, "controlURL", 10) == 0)) break; // find the first 'c'; is this controlURL? if not, keep looking
|
||||
ptr++;
|
||||
}
|
||||
if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; }
|
||||
ptr += 11; // skip over "controlURL>"
|
||||
if (ptr >= end) { LogInfo("handleLNTDeviceDescriptionResponse: past end of buffer and no body!"); return; } // check ptr again in case we skipped over the end of the buffer
|
||||
|
||||
// find the end of the controlURL element
|
||||
for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } }
|
||||
|
||||
// fill in default port
|
||||
m->UPnPSOAPPort = m->UPnPRouterPort;
|
||||
|
||||
// free string pointers and set to NULL
|
||||
if (m->UPnPSOAPAddressString != mDNSNULL)
|
||||
{
|
||||
mDNSPlatformMemFree(m->UPnPSOAPAddressString);
|
||||
m->UPnPSOAPAddressString = mDNSNULL;
|
||||
}
|
||||
if (m->UPnPSOAPURL != mDNSNULL)
|
||||
{
|
||||
mDNSPlatformMemFree(m->UPnPSOAPURL);
|
||||
m->UPnPSOAPURL = mDNSNULL;
|
||||
}
|
||||
|
||||
if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, &m->UPnPSOAPURL) != mStatus_NoError) return;
|
||||
// the SOAPURL should look something like "/uuid:0013-108c-4b3f0000f3dc"
|
||||
|
||||
if (m->UPnPSOAPAddressString == mDNSNULL)
|
||||
{
|
||||
ptr = tcpInfo->Reply;
|
||||
while (ptr && ptr < end)
|
||||
{
|
||||
if ((*ptr & 0xDF) == 'U' && (strncasecmp((char*)ptr, "URLBase", 7) == 0)) break;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (ptr < end) // found URLBase
|
||||
{
|
||||
LogInfo("handleLNTDeviceDescriptionResponse: found URLBase");
|
||||
ptr += 8; // skip over "URLBase>"
|
||||
// find the end of the URLBase element
|
||||
for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } }
|
||||
if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError)
|
||||
{
|
||||
LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase");
|
||||
}
|
||||
}
|
||||
|
||||
// if all else fails, use the router address string
|
||||
if (m->UPnPSOAPAddressString == mDNSNULL) AllocAndCopy(&m->UPnPSOAPAddressString, m->UPnPRouterAddressString);
|
||||
}
|
||||
if (m->UPnPSOAPAddressString == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL");
|
||||
else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m->UPnPSOAPAddressString);
|
||||
|
||||
if (m->UPnPSOAPURL == mDNSNULL) AllocAndCopy(&m->UPnPSOAPURL, m->UPnPRouterURL);
|
||||
if (m->UPnPSOAPURL == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL");
|
||||
else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m->UPnPSOAPURL);
|
||||
}
|
||||
|
||||
mDNSlocal void handleLNTGetExternalAddressResponse(tcpLNTInfo *tcpInfo)
|
||||
{
|
||||
mDNS *m = tcpInfo->m;
|
||||
mDNSu16 err = NATErr_None;
|
||||
mDNSv4Addr ExtAddr;
|
||||
const mDNSu8 *ptr = tcpInfo->Reply;
|
||||
const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread;
|
||||
mDNSu8 *addrend;
|
||||
static char tagname[20] = { 'N','e','w','E','x','t','e','r','n','a','l','I','P','A','d','d','r','e','s','s' };
|
||||
// Array NOT including a terminating nul
|
||||
|
||||
// LogInfo("handleLNTGetExternalAddressResponse: %s", ptr);
|
||||
|
||||
mDNSs16 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
|
||||
if (http_result == HTTPCode_404) LNT_ClearState(m);
|
||||
if (http_result != HTTPCode_200)
|
||||
{
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP code: %d", http_result);
|
||||
return;
|
||||
}
|
||||
|
||||
while (ptr < end && strncasecmp((char*)ptr, tagname, sizeof(tagname))) ptr++;
|
||||
ptr += sizeof(tagname); // Skip over "NewExternalIPAddress"
|
||||
while (ptr < end && *ptr != '>') ptr++;
|
||||
ptr += 1; // Skip over ">"
|
||||
|
||||
// Find the end of the address and terminate the string so inet_pton() can convert it
|
||||
// (Might be better to copy this to a local string here -- this is overwriting tcpInfo->Reply in-place
|
||||
addrend = (mDNSu8*)ptr;
|
||||
while (addrend < end && (mDNSIsDigit(*addrend) || *addrend == '.')) addrend++;
|
||||
if (addrend >= end) return;
|
||||
*addrend = 0;
|
||||
|
||||
if (inet_pton(AF_INET, (char*)ptr, &ExtAddr) <= 0)
|
||||
{
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "inet_pton", "");
|
||||
LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr);
|
||||
err = NATErr_NetFail;
|
||||
ExtAddr = zerov4Addr;
|
||||
}
|
||||
if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr);
|
||||
|
||||
natTraversalHandleAddressReply(m, err, ExtAddr);
|
||||
}
|
||||
|
||||
mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo)
|
||||
{
|
||||
mDNS *m = tcpInfo->m;
|
||||
mDNSIPPort extport = zeroIPPort;
|
||||
const mDNSu8 *ptr = tcpInfo->Reply;
|
||||
const mDNSu8 *const end = tcpInfo->Reply + tcpInfo->nread;
|
||||
NATTraversalInfo *natInfo;
|
||||
mDNSs16 http_result;
|
||||
|
||||
for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break; }
|
||||
|
||||
if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; }
|
||||
|
||||
http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr
|
||||
if (http_result == HTTPCode_200)
|
||||
{
|
||||
LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)",
|
||||
mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries);
|
||||
|
||||
// Make sure to compute extport *before* we zero tcpInfo->retries
|
||||
extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo));
|
||||
tcpInfo->retries = 0;
|
||||
natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE);
|
||||
}
|
||||
else if (http_result == HTTPCode_500)
|
||||
{
|
||||
while (ptr && ptr != end)
|
||||
{
|
||||
if (((*ptr & 0xDF) == 'C' && end - ptr >= 8 && strncasecmp((char*)ptr, "Conflict", 8) == 0) ||
|
||||
(*ptr == '>' && end - ptr >= 15 && strncasecmp((char*)ptr, ">718</errorCode", 15) == 0))
|
||||
{
|
||||
if (tcpInfo->retries < 100)
|
||||
{
|
||||
tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo);
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo->retries);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort));
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries);
|
||||
natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response");
|
||||
else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code");
|
||||
else if (http_result == HTTPCode_404) LNT_ClearState(m);
|
||||
if (http_result != HTTPCode_200 && http_result != HTTPCode_500)
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result);
|
||||
}
|
||||
|
||||
mDNSlocal void DisposeInfoFromUnmapList(mDNS *m, tcpLNTInfo *tcpInfo)
|
||||
{
|
||||
tcpLNTInfo **ptr = &m->tcpInfoUnmapList;
|
||||
while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next;
|
||||
if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); } // If we found it, cut it from our list and free the memory
|
||||
}
|
||||
|
||||
mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err)
|
||||
{
|
||||
mStatus status = mStatus_NoError;
|
||||
tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context;
|
||||
mDNSBool closed = mDNSfalse;
|
||||
long n = 0;
|
||||
long nsent = 0;
|
||||
|
||||
if (tcpInfo == mDNSNULL) { LogInfo("tcpConnectionCallback: no tcpInfo context"); status = mStatus_Invalid; goto exit; }
|
||||
|
||||
// The handlers below expect to be called with the lock held
|
||||
mDNS_Lock(tcpInfo->m);
|
||||
|
||||
if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; }
|
||||
|
||||
if (ConnectionEstablished) // connection is established - send the message
|
||||
{
|
||||
LogInfo("tcpConnectionCallback: connection established, sending message");
|
||||
nsent = mDNSPlatformWriteTCP(sock, (char*)tcpInfo->Request, tcpInfo->requestLen);
|
||||
if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; }
|
||||
}
|
||||
else
|
||||
{
|
||||
n = mDNSPlatformReadTCP(sock, (char*)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed);
|
||||
LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n);
|
||||
|
||||
if (n < 0) { LogInfo("tcpConnectionCallback - read returned %d", n); status = mStatus_ConnFailed; goto exit; }
|
||||
else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; }
|
||||
|
||||
tcpInfo->nread += n;
|
||||
LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread);
|
||||
if (tcpInfo->nread > LNT_MAXBUFSIZE)
|
||||
{
|
||||
LogInfo("result truncated...");
|
||||
tcpInfo->nread = LNT_MAXBUFSIZE;
|
||||
}
|
||||
|
||||
switch (tcpInfo->op)
|
||||
{
|
||||
case LNTDiscoveryOp: handleLNTDeviceDescriptionResponse (tcpInfo); break;
|
||||
case LNTExternalAddrOp: handleLNTGetExternalAddressResponse(tcpInfo); break;
|
||||
case LNTPortMapOp: handleLNTPortMappingResponse (tcpInfo); break;
|
||||
case LNTPortMapDeleteOp: status = mStatus_ConfigChanged; break;
|
||||
default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
if (err || status)
|
||||
{
|
||||
mDNS *m = tcpInfo->m;
|
||||
switch (tcpInfo->op)
|
||||
{
|
||||
case LNTDiscoveryOp: if (m->UPnPSOAPAddressString == mDNSNULL)
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP Address", "");
|
||||
if (m->UPnPSOAPURL == mDNSNULL)
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP path", "");
|
||||
if (m->UPnPSOAPAddressString && m->UPnPSOAPURL)
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", "");
|
||||
break;
|
||||
case LNTExternalAddrOp: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest",
|
||||
mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success",
|
||||
mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", "");
|
||||
break;
|
||||
case LNTPortMapOp: if (tcpInfo->parentNATInfo)
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success",
|
||||
(tcpInfo->parentNATInfo->Result) ? "failure" : "success", "Result: %d", tcpInfo->parentNATInfo->Result);
|
||||
break;
|
||||
case LNTPortMapDeleteOp: break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
mDNSPlatformTCPCloseConnection(tcpInfo->sock);
|
||||
tcpInfo->sock = mDNSNULL;
|
||||
if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; }
|
||||
if (tcpInfo->Reply ) { mDNSPlatformMemFree(tcpInfo->Reply); tcpInfo->Reply = mDNSNULL; }
|
||||
}
|
||||
|
||||
if (tcpInfo) mDNS_Unlock(tcpInfo->m);
|
||||
|
||||
if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo);
|
||||
}
|
||||
|
||||
mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op)
|
||||
{
|
||||
mStatus err = mStatus_NoError;
|
||||
mDNSIPPort srcport = zeroIPPort;
|
||||
|
||||
if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port))
|
||||
{ LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); }
|
||||
info->m = m;
|
||||
info->Address = *Addr;
|
||||
info->Port = Port;
|
||||
info->op = op;
|
||||
info->nread = 0;
|
||||
info->replyLen = LNT_MAXBUFSIZE;
|
||||
if (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE); // reuse previously allocated buffer
|
||||
else if ((info->Reply = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); }
|
||||
|
||||
if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; }
|
||||
info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport);
|
||||
if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); }
|
||||
LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port));
|
||||
err = mDNSPlatformTCPConnect(info->sock, Addr, Port, mDNSNULL, 0, tcpConnectionCallback, info);
|
||||
|
||||
if (err == mStatus_ConnPending) err = mStatus_NoError;
|
||||
else if (err == mStatus_ConnEstablished)
|
||||
{
|
||||
mDNS_DropLockBeforeCallback();
|
||||
tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError);
|
||||
mDNS_ReclaimLockAfterCallback();
|
||||
err = mStatus_NoError;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
|
||||
LogInfo("LNT MakeTCPConnection: connection failed");
|
||||
mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above
|
||||
info->sock = mDNSNULL;
|
||||
mDNSPlatformMemFree(info->Reply);
|
||||
info->Reply = mDNSNULL;
|
||||
}
|
||||
return(err);
|
||||
}
|
||||
|
||||
mDNSlocal unsigned int AddSOAPArguments(char *const buf, const unsigned int maxlen, const int numArgs, const Property *const a)
|
||||
{
|
||||
static const char f1[] = "<%s>%s</%s>";
|
||||
static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s</%s>";
|
||||
int i, len = 0;
|
||||
*buf = 0;
|
||||
for (i = 0; i < numArgs; i++)
|
||||
{
|
||||
if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name);
|
||||
else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name, a[i].value, a[i].name);
|
||||
}
|
||||
return(len);
|
||||
}
|
||||
|
||||
mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, const char *const Action, const int numArgs, const Property *const Arguments, const LNTOp_t op)
|
||||
{
|
||||
// SOAP message header format -
|
||||
// - control URL
|
||||
// - action (string)
|
||||
// - router's host/port ("host:port")
|
||||
// - content-length
|
||||
static const char header[] =
|
||||
"POST %s HTTP/1.1\r\n"
|
||||
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
||||
"SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n"
|
||||
"User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Pragma: no-cache\r\n"
|
||||
"\r\n"
|
||||
"%s\r\n";
|
||||
|
||||
static const char body1[] =
|
||||
"<?xml version=\"1.0\"?>\r\n"
|
||||
"<SOAP-ENV:Envelope"
|
||||
" xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\""
|
||||
" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||
"<SOAP-ENV:Body>"
|
||||
"<m:%s xmlns:m=\"urn:schemas-upnp-org:service:WAN%sConnection:1\">";
|
||||
|
||||
static const char body2[] =
|
||||
"</m:%s>"
|
||||
"</SOAP-ENV:Body>"
|
||||
"</SOAP-ENV:Envelope>\r\n";
|
||||
|
||||
mStatus err;
|
||||
char *body = (char*)&m->omsg; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty
|
||||
int bodyLen;
|
||||
|
||||
if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here
|
||||
{ LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; }
|
||||
|
||||
// Create body
|
||||
bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action, m->UPnPWANPPPConnection ? "PPP" : "IP");
|
||||
bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments);
|
||||
bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action);
|
||||
|
||||
// Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field
|
||||
if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE);
|
||||
if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; }
|
||||
info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body);
|
||||
|
||||
err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op);
|
||||
if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; }
|
||||
return err;
|
||||
}
|
||||
|
||||
// Build port mapping request with new port (up to max) and send it
|
||||
mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n)
|
||||
{
|
||||
char externalPort[6];
|
||||
char internalPort[6];
|
||||
char localIPAddrString[30];
|
||||
char publicPortString[40];
|
||||
Property propArgs[8];
|
||||
mDNSu16 ReqPortNum = RequestedPortNum(n);
|
||||
NATTraversalInfo *n2 = m->NATTraversals;
|
||||
|
||||
// Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique.
|
||||
// UPnP gateways will report conflicts if different devices request the same external port, but if two
|
||||
// clients on the same device request the same external port the second one just stomps over the first.
|
||||
// One way this can happen is like this:
|
||||
// 1. Client A binds local port 80
|
||||
// 2. Client A requests external port 80 -> internal port 80
|
||||
// 3. UPnP NAT gateway refuses external port 80 (some other client already has it)
|
||||
// 4. Client A tries again, and successfully gets external port 80 -> internal port 81
|
||||
// 5. Client B on same machine tries to bind local port 80, and fails
|
||||
// 6. Client B tries again, and successfully binds local port 81
|
||||
// 7. Client B now requests external port 81 -> internal port 81
|
||||
// 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping
|
||||
|
||||
while (n2)
|
||||
{
|
||||
if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next;
|
||||
else
|
||||
{
|
||||
if (n->tcpInfo.retries < 100)
|
||||
{
|
||||
n->tcpInfo.retries++;
|
||||
ReqPortNum = RequestedPortNum(n); // Pick a new port number
|
||||
n2 = m->NATTraversals; // And re-scan the list looking for conflicts
|
||||
}
|
||||
else
|
||||
{
|
||||
natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0);
|
||||
return mStatus_NoError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create strings to use in the message
|
||||
mDNS_snprintf(externalPort, sizeof(externalPort), "%u", ReqPortNum);
|
||||
mDNS_snprintf(internalPort, sizeof(internalPort), "%u", mDNSVal16(n->IntPort));
|
||||
mDNS_snprintf(publicPortString, sizeof(publicPortString), "iC%u", ReqPortNum);
|
||||
mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u",
|
||||
m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]);
|
||||
|
||||
// build the message
|
||||
mDNSPlatformMemZero(propArgs, sizeof(propArgs));
|
||||
propArgs[0].name = "NewRemoteHost";
|
||||
propArgs[0].type = "string";
|
||||
propArgs[0].value = "";
|
||||
propArgs[1].name = "NewExternalPort";
|
||||
propArgs[1].type = "ui2";
|
||||
propArgs[1].value = externalPort;
|
||||
propArgs[2].name = "NewProtocol";
|
||||
propArgs[2].type = "string";
|
||||
propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
|
||||
propArgs[3].name = "NewInternalPort";
|
||||
propArgs[3].type = "ui2";
|
||||
propArgs[3].value = internalPort;
|
||||
propArgs[4].name = "NewInternalClient";
|
||||
propArgs[4].type = "string";
|
||||
propArgs[4].value = localIPAddrString;
|
||||
propArgs[5].name = "NewEnabled";
|
||||
propArgs[5].type = "boolean";
|
||||
propArgs[5].value = "1";
|
||||
propArgs[6].name = "NewPortMappingDescription";
|
||||
propArgs[6].type = "string";
|
||||
propArgs[6].value = publicPortString;
|
||||
propArgs[7].name = "NewLeaseDuration";
|
||||
propArgs[7].type = "ui4";
|
||||
propArgs[7].value = "0";
|
||||
|
||||
LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum);
|
||||
return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp);
|
||||
}
|
||||
|
||||
mDNSexport mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *const n)
|
||||
{
|
||||
LogInfo("LNT_MapPort");
|
||||
if (n->tcpInfo.sock) return(mStatus_NoError); // If we already have a connection up don't make another request for the same thing
|
||||
n->tcpInfo.parentNATInfo = n;
|
||||
n->tcpInfo.retries = 0;
|
||||
return SendPortMapRequest(m, n);
|
||||
}
|
||||
|
||||
mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *const n)
|
||||
{
|
||||
char externalPort[10];
|
||||
Property propArgs[3];
|
||||
tcpLNTInfo *info;
|
||||
tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList;
|
||||
mStatus err;
|
||||
|
||||
// If no NAT gateway to talk to, no need to do all this work for nothing
|
||||
if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError;
|
||||
|
||||
mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort));
|
||||
|
||||
mDNSPlatformMemZero(propArgs, sizeof(propArgs));
|
||||
propArgs[0].name = "NewRemoteHost";
|
||||
propArgs[0].type = "string";
|
||||
propArgs[0].value = "";
|
||||
propArgs[1].name = "NewExternalPort";
|
||||
propArgs[1].type = "ui2";
|
||||
propArgs[1].value = externalPort;
|
||||
propArgs[2].name = "NewProtocol";
|
||||
propArgs[2].type = "string";
|
||||
propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP";
|
||||
|
||||
n->tcpInfo.parentNATInfo = n;
|
||||
|
||||
// clean up previous port mapping requests and allocations
|
||||
if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection");
|
||||
if (n->tcpInfo.sock ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; }
|
||||
if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request); n->tcpInfo.Request = mDNSNULL; }
|
||||
if (n->tcpInfo.Reply ) { mDNSPlatformMemFree(n->tcpInfo.Reply); n->tcpInfo.Reply = mDNSNULL; }
|
||||
|
||||
// make a copy of the tcpInfo that we can clean up later (the one passed in will be destroyed by the client as soon as this returns)
|
||||
if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL)
|
||||
{ LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); }
|
||||
*info = n->tcpInfo;
|
||||
|
||||
while (*infoPtr) infoPtr = &(*infoPtr)->next; // find the end of the list
|
||||
*infoPtr = info; // append
|
||||
|
||||
err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp);
|
||||
if (err) DisposeInfoFromUnmapList(m, info);
|
||||
return err;
|
||||
}
|
||||
|
||||
mDNSexport mStatus LNT_GetExternalAddress(mDNS *m)
|
||||
{
|
||||
return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp);
|
||||
}
|
||||
|
||||
mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info)
|
||||
{
|
||||
// Device description format -
|
||||
// - device description URL
|
||||
// - host/port
|
||||
static const char szSSDPMsgDescribeDeviceFMT[] =
|
||||
"GET %s HTTP/1.1\r\n"
|
||||
"Accept: text/xml, application/xml\r\n"
|
||||
"User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n"
|
||||
"Host: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n";
|
||||
|
||||
if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need
|
||||
|
||||
if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); }
|
||||
|
||||
// build message
|
||||
if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer
|
||||
else if ((info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); }
|
||||
info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString);
|
||||
LogInfo("Describe Device: [%s]", info->Request);
|
||||
return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp);
|
||||
}
|
||||
|
||||
// This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response
|
||||
// referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and
|
||||
// URL info we need.
|
||||
mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len)
|
||||
{
|
||||
const mDNSu8 *ptr = data;
|
||||
const mDNSu8 *end = data + len;
|
||||
const mDNSu8 *stop = ptr;
|
||||
|
||||
if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need
|
||||
|
||||
// The formatting of the HTTP header is not always the same when it comes to the placement of
|
||||
// the service and location strings, so we just look for each of them from the beginning for every response
|
||||
|
||||
// figure out if this is a message from a service we care about
|
||||
while (ptr && ptr != end)
|
||||
{
|
||||
if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break;
|
||||
ptr++;
|
||||
}
|
||||
if (ptr == end)
|
||||
{
|
||||
ptr = data;
|
||||
while (ptr && ptr != end)
|
||||
{
|
||||
if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) break;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
if (ptr == mDNSNULL || ptr == end) return; // not a message we care about
|
||||
|
||||
// find "Location:", starting from the beginning
|
||||
ptr = data;
|
||||
while (ptr && ptr != end)
|
||||
{
|
||||
if ((*ptr & 0xDF) == 'L' && (strncasecmp((char*)ptr, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking
|
||||
ptr++;
|
||||
}
|
||||
if (ptr == mDNSNULL || ptr == end)
|
||||
{
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", "");
|
||||
return; // not a message we care about
|
||||
}
|
||||
ptr += 9; //Skip over 'Location:'
|
||||
while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces
|
||||
if (ptr >= end) return;
|
||||
|
||||
// find the end of the line
|
||||
for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } }
|
||||
|
||||
// fill in default port
|
||||
m->UPnPRouterPort = mDNSOpaque16fromIntVal(80);
|
||||
|
||||
// free string pointers and set to NULL
|
||||
if (m->UPnPRouterAddressString != mDNSNULL)
|
||||
{
|
||||
mDNSPlatformMemFree(m->UPnPRouterAddressString);
|
||||
m->UPnPRouterAddressString = mDNSNULL;
|
||||
}
|
||||
if (m->UPnPRouterURL != mDNSNULL)
|
||||
{
|
||||
mDNSPlatformMemFree(m->UPnPRouterURL);
|
||||
m->UPnPRouterURL = mDNSNULL;
|
||||
}
|
||||
|
||||
// the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc"
|
||||
if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError)
|
||||
{
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Parse URL", "");
|
||||
return;
|
||||
}
|
||||
|
||||
m->UPnPInterfaceID = InterfaceID;
|
||||
|
||||
if (m->UPnPRouterAddressString == mDNSNULL)
|
||||
{
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router address", "");
|
||||
LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL");
|
||||
}
|
||||
else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString);
|
||||
|
||||
if (m->UPnPRouterURL == mDNSNULL)
|
||||
{
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router path", "");
|
||||
LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL");
|
||||
}
|
||||
else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL);
|
||||
|
||||
LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort));
|
||||
LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID);
|
||||
|
||||
// Don't need the SSDP socket anymore
|
||||
if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
|
||||
|
||||
mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "success", "success", "");
|
||||
// now send message to get the device description
|
||||
GetDeviceDescription(m, &m->tcpDeviceInfo);
|
||||
}
|
||||
|
||||
mDNSexport void LNT_SendDiscoveryMsg(mDNS *m)
|
||||
{
|
||||
static const char msg[] =
|
||||
"M-SEARCH * HTTP/1.1\r\n"
|
||||
"Host:239.255.255.250:1900\r\n"
|
||||
"ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n"
|
||||
"Man:\"ssdp:discover\"\r\n"
|
||||
"MX:3\r\n\r\n";
|
||||
static const mDNSAddr multicastDest = { mDNSAddrType_IPv4, { { { 239, 255, 255, 250 } } } };
|
||||
|
||||
mDNSu8 *buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty
|
||||
unsigned int bufLen;
|
||||
|
||||
if (!mDNSIPPortIsZero(m->UPnPRouterPort))
|
||||
{
|
||||
if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
|
||||
if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
// Always query for WANIPConnection in the first SSDP packet
|
||||
if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse;
|
||||
|
||||
// Create message
|
||||
bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP");
|
||||
|
||||
debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress);
|
||||
|
||||
if (!mDNSIPv4AddressIsZero(m->Router.ip.v4))
|
||||
{
|
||||
if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(m, zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); }
|
||||
mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router, SSDPPort);
|
||||
mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort);
|
||||
}
|
||||
|
||||
m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection;
|
||||
}
|
||||
|
||||
mDNSexport void LNT_ClearState(mDNS *const m)
|
||||
{
|
||||
if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; }
|
||||
if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; }
|
||||
m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; // Reset UPnP ports
|
||||
}
|
||||
|
||||
#endif /* _LEGACY_NAT_TRAVERSAL_ */
|
728
src/tools/mdnssd/Poll.c
Normal file
728
src/tools/mdnssd/Poll.c
Normal file
@@ -0,0 +1,728 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Poll.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#include "GenLinkedList.h"
|
||||
#include "DebugServices.h"
|
||||
|
||||
|
||||
typedef struct PollSource_struct
|
||||
{
|
||||
SOCKET socket;
|
||||
HANDLE handle;
|
||||
void *context;
|
||||
|
||||
union
|
||||
{
|
||||
mDNSPollSocketCallback socket;
|
||||
mDNSPollEventCallback event;
|
||||
} callback;
|
||||
|
||||
struct Worker_struct *worker;
|
||||
struct PollSource_struct *next;
|
||||
|
||||
} PollSource;
|
||||
|
||||
|
||||
typedef struct Worker_struct
|
||||
{
|
||||
HANDLE thread; // NULL for main worker
|
||||
unsigned id; // 0 for main worker
|
||||
|
||||
HANDLE start; // NULL for main worker
|
||||
HANDLE stop; // NULL for main worker
|
||||
BOOL done; // Not used for main worker
|
||||
|
||||
DWORD numSources;
|
||||
PollSource *sources[ MAXIMUM_WAIT_OBJECTS ];
|
||||
HANDLE handles[ MAXIMUM_WAIT_OBJECTS ];
|
||||
DWORD result;
|
||||
struct Worker_struct *next;
|
||||
} Worker;
|
||||
|
||||
|
||||
typedef struct Poll_struct
|
||||
{
|
||||
mDNSBool setup;
|
||||
HANDLE wakeup;
|
||||
GenLinkedList sources;
|
||||
DWORD numSources;
|
||||
Worker main;
|
||||
GenLinkedList workers;
|
||||
HANDLE workerHandles[ MAXIMUM_WAIT_OBJECTS ];
|
||||
DWORD numWorkers;
|
||||
|
||||
} Poll;
|
||||
|
||||
|
||||
/*
|
||||
* Poll Methods
|
||||
*/
|
||||
|
||||
mDNSlocal mStatus PollSetup();
|
||||
mDNSlocal mStatus PollRegisterSource( PollSource *source );
|
||||
mDNSlocal void PollUnregisterSource( PollSource *source );
|
||||
mDNSlocal mStatus PollStartWorkers();
|
||||
mDNSlocal mStatus PollStopWorkers();
|
||||
mDNSlocal void PollRemoveWorker( Worker *worker );
|
||||
|
||||
|
||||
/*
|
||||
* Worker Methods
|
||||
*/
|
||||
|
||||
mDNSlocal mStatus WorkerInit( Worker *worker );
|
||||
mDNSlocal void WorkerFree( Worker *worker );
|
||||
mDNSlocal void WorkerRegisterSource( Worker *worker, PollSource *source );
|
||||
mDNSlocal int WorkerSourceToIndex( Worker *worker, PollSource *source );
|
||||
mDNSlocal void WorkerUnregisterSource( Worker *worker, PollSource *source );
|
||||
mDNSlocal void WorkerDispatch( Worker *worker);
|
||||
mDNSlocal void CALLBACK WorkerWakeupNotification( HANDLE event, void *context );
|
||||
mDNSlocal unsigned WINAPI WorkerMain( LPVOID inParam );
|
||||
|
||||
|
||||
static void
|
||||
ShiftDown( void * arr, size_t arraySize, size_t itemSize, int index )
|
||||
{
|
||||
memmove( ( ( unsigned char* ) arr ) + ( ( index - 1 ) * itemSize ), ( ( unsigned char* ) arr ) + ( index * itemSize ), ( arraySize - index ) * itemSize );
|
||||
}
|
||||
|
||||
|
||||
#define DEBUG_NAME "[mDNSWin32] "
|
||||
#define gMDNSRecord mDNSStorage
|
||||
mDNSlocal Poll gPoll = { mDNSfalse, NULL };
|
||||
|
||||
#define LogErr( err, FUNC ) LogMsg( "%s:%d - %s failed: %d\n", __FUNCTION__, __LINE__, FUNC, err );
|
||||
|
||||
|
||||
mStatus
|
||||
mDNSPollRegisterSocket( SOCKET socket, int networkEvents, mDNSPollSocketCallback callback, void *context )
|
||||
{
|
||||
PollSource *source = NULL;
|
||||
HANDLE event = INVALID_HANDLE_VALUE;
|
||||
mStatus err = mStatus_NoError;
|
||||
|
||||
if ( !gPoll.setup )
|
||||
{
|
||||
err = PollSetup();
|
||||
require_noerr( err, exit );
|
||||
}
|
||||
|
||||
source = malloc( sizeof( PollSource ) );
|
||||
require_action( source, exit, err = mStatus_NoMemoryErr );
|
||||
|
||||
event = WSACreateEvent();
|
||||
require_action( event, exit, err = mStatus_NoMemoryErr );
|
||||
|
||||
err = WSAEventSelect( socket, event, networkEvents );
|
||||
require_noerr( err, exit );
|
||||
|
||||
source->socket = socket;
|
||||
source->handle = event;
|
||||
source->callback.socket = callback;
|
||||
source->context = context;
|
||||
|
||||
err = PollRegisterSource( source );
|
||||
require_noerr( err, exit );
|
||||
|
||||
exit:
|
||||
|
||||
if ( err != mStatus_NoError )
|
||||
{
|
||||
if ( event != INVALID_HANDLE_VALUE )
|
||||
{
|
||||
WSACloseEvent( event );
|
||||
}
|
||||
|
||||
if ( source != NULL )
|
||||
{
|
||||
free( source );
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mDNSPollUnregisterSocket( SOCKET socket )
|
||||
{
|
||||
PollSource *source;
|
||||
|
||||
for ( source = gPoll.sources.Head; source; source = source->next )
|
||||
{
|
||||
if ( source->socket == socket )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( source )
|
||||
{
|
||||
WSACloseEvent( source->handle );
|
||||
PollUnregisterSource( source );
|
||||
free( source );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mStatus
|
||||
mDNSPollRegisterEvent( HANDLE event, mDNSPollEventCallback callback, void *context )
|
||||
{
|
||||
PollSource *source = NULL;
|
||||
mStatus err = mStatus_NoError;
|
||||
|
||||
if ( !gPoll.setup )
|
||||
{
|
||||
err = PollSetup();
|
||||
require_noerr( err, exit );
|
||||
}
|
||||
|
||||
source = malloc( sizeof( PollSource ) );
|
||||
require_action( source, exit, err = mStatus_NoMemoryErr );
|
||||
|
||||
source->socket = INVALID_SOCKET;
|
||||
source->handle = event;
|
||||
source->callback.event = callback;
|
||||
source->context = context;
|
||||
|
||||
err = PollRegisterSource( source );
|
||||
require_noerr( err, exit );
|
||||
|
||||
exit:
|
||||
|
||||
if ( err != mStatus_NoError )
|
||||
{
|
||||
if ( source != NULL )
|
||||
{
|
||||
free( source );
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mDNSPollUnregisterEvent( HANDLE event )
|
||||
{
|
||||
PollSource *source;
|
||||
|
||||
for ( source = gPoll.sources.Head; source; source = source->next )
|
||||
{
|
||||
if ( source->handle == event )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( source )
|
||||
{
|
||||
PollUnregisterSource( source );
|
||||
free( source );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mStatus
|
||||
mDNSPoll( DWORD msec )
|
||||
{
|
||||
mStatus err = mStatus_NoError;
|
||||
|
||||
if ( gPoll.numWorkers > 0 )
|
||||
{
|
||||
err = PollStartWorkers();
|
||||
require_noerr( err, exit );
|
||||
}
|
||||
|
||||
gPoll.main.result = WaitForMultipleObjects( gPoll.main.numSources, gPoll.main.handles, FALSE, msec );
|
||||
err = translate_errno( ( gPoll.main.result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) LogErr( err, "WaitForMultipleObjects()" );
|
||||
require_action( gPoll.main.result != WAIT_FAILED, exit, err = ( mStatus ) GetLastError() );
|
||||
|
||||
if ( gPoll.numWorkers > 0 )
|
||||
{
|
||||
err = PollStopWorkers();
|
||||
require_noerr( err, exit );
|
||||
}
|
||||
|
||||
WorkerDispatch( &gPoll.main );
|
||||
|
||||
exit:
|
||||
|
||||
return ( err );
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal mStatus
|
||||
PollSetup()
|
||||
{
|
||||
mStatus err = mStatus_NoError;
|
||||
|
||||
if ( !gPoll.setup )
|
||||
{
|
||||
memset( &gPoll, 0, sizeof( gPoll ) );
|
||||
|
||||
InitLinkedList( &gPoll.sources, offsetof( PollSource, next ) );
|
||||
InitLinkedList( &gPoll.workers, offsetof( Worker, next ) );
|
||||
|
||||
gPoll.wakeup = CreateEvent( NULL, TRUE, FALSE, NULL );
|
||||
require_action( gPoll.wakeup, exit, err = mStatus_NoMemoryErr );
|
||||
|
||||
err = WorkerInit( &gPoll.main );
|
||||
require_noerr( err, exit );
|
||||
|
||||
gPoll.setup = mDNStrue;
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal mStatus
|
||||
PollRegisterSource( PollSource *source )
|
||||
{
|
||||
Worker *worker = NULL;
|
||||
mStatus err = mStatus_NoError;
|
||||
|
||||
AddToTail( &gPoll.sources, source );
|
||||
gPoll.numSources++;
|
||||
|
||||
// First check our main worker. In most cases, we won't have to worry about threads
|
||||
|
||||
if ( gPoll.main.numSources < MAXIMUM_WAIT_OBJECTS )
|
||||
{
|
||||
WorkerRegisterSource( &gPoll.main, source );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find a thread to use that we've already created
|
||||
|
||||
for ( worker = gPoll.workers.Head; worker; worker = worker->next )
|
||||
{
|
||||
if ( worker->numSources < MAXIMUM_WAIT_OBJECTS )
|
||||
{
|
||||
WorkerRegisterSource( worker, source );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If not, then create a worker and make a thread to run it in
|
||||
|
||||
if ( !worker )
|
||||
{
|
||||
worker = ( Worker* ) malloc( sizeof( Worker ) );
|
||||
require_action( worker, exit, err = mStatus_NoMemoryErr );
|
||||
|
||||
memset( worker, 0, sizeof( Worker ) );
|
||||
|
||||
worker->start = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
require_action( worker->start, exit, err = mStatus_NoMemoryErr );
|
||||
|
||||
worker->stop = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
require_action( worker->stop, exit, err = mStatus_NoMemoryErr );
|
||||
|
||||
err = WorkerInit( worker );
|
||||
require_noerr( err, exit );
|
||||
|
||||
// Create thread with _beginthreadex() instead of CreateThread() to avoid
|
||||
// memory leaks when using static run-time libraries.
|
||||
// See <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp>.
|
||||
|
||||
worker->thread = ( HANDLE ) _beginthreadex_compat( NULL, 0, WorkerMain, worker, 0, &worker->id );
|
||||
err = translate_errno( worker->thread, ( mStatus ) GetLastError(), kUnknownErr );
|
||||
require_noerr( err, exit );
|
||||
|
||||
AddToTail( &gPoll.workers, worker );
|
||||
gPoll.workerHandles[ gPoll.numWorkers++ ] = worker->stop;
|
||||
|
||||
WorkerRegisterSource( worker, source );
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
if ( err && worker )
|
||||
{
|
||||
WorkerFree( worker );
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal void
|
||||
PollUnregisterSource( PollSource *source )
|
||||
{
|
||||
RemoveFromList( &gPoll.sources, source );
|
||||
gPoll.numSources--;
|
||||
|
||||
WorkerUnregisterSource( source->worker, source );
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal mStatus
|
||||
PollStartWorkers()
|
||||
{
|
||||
Worker *worker;
|
||||
mStatus err = mStatus_NoError;
|
||||
BOOL ok;
|
||||
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME "starting workers\n" );
|
||||
|
||||
worker = gPoll.workers.Head;
|
||||
|
||||
while ( worker )
|
||||
{
|
||||
Worker *next = worker->next;
|
||||
|
||||
if ( worker->numSources == 1 )
|
||||
{
|
||||
PollRemoveWorker( worker );
|
||||
}
|
||||
else
|
||||
{
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME "waking up worker\n" );
|
||||
|
||||
ok = SetEvent( worker->start );
|
||||
err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) LogErr( err, "SetEvent()" );
|
||||
|
||||
if ( err )
|
||||
{
|
||||
PollRemoveWorker( worker );
|
||||
}
|
||||
}
|
||||
|
||||
worker = next;
|
||||
}
|
||||
|
||||
err = mStatus_NoError;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal mStatus
|
||||
PollStopWorkers()
|
||||
{
|
||||
DWORD result;
|
||||
Worker *worker;
|
||||
BOOL ok;
|
||||
mStatus err = mStatus_NoError;
|
||||
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME "stopping workers\n" );
|
||||
|
||||
ok = SetEvent( gPoll.wakeup );
|
||||
err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) LogErr( err, "SetEvent()" );
|
||||
|
||||
// Wait For 5 seconds for all the workers to wake up
|
||||
|
||||
result = WaitForMultipleObjects( gPoll.numWorkers, gPoll.workerHandles, TRUE, 5000 );
|
||||
err = translate_errno( ( result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) LogErr( err, "WaitForMultipleObjects()" );
|
||||
|
||||
ok = ResetEvent( gPoll.wakeup );
|
||||
err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) LogErr( err, "ResetEvent()" );
|
||||
|
||||
for ( worker = gPoll.workers.Head; worker; worker = worker->next )
|
||||
{
|
||||
WorkerDispatch( worker );
|
||||
}
|
||||
|
||||
err = mStatus_NoError;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal void
|
||||
PollRemoveWorker( Worker *worker )
|
||||
{
|
||||
DWORD result;
|
||||
mStatus err;
|
||||
BOOL ok;
|
||||
DWORD i;
|
||||
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME "removing worker %d\n", worker->id );
|
||||
|
||||
RemoveFromList( &gPoll.workers, worker );
|
||||
|
||||
// Remove handle from gPoll.workerHandles
|
||||
|
||||
for ( i = 0; i < gPoll.numWorkers; i++ )
|
||||
{
|
||||
if ( gPoll.workerHandles[ i ] == worker->stop )
|
||||
{
|
||||
ShiftDown( gPoll.workerHandles, gPoll.numWorkers, sizeof( gPoll.workerHandles[ 0 ] ), i + 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
worker->done = TRUE;
|
||||
gPoll.numWorkers--;
|
||||
|
||||
// Cause the thread to exit.
|
||||
|
||||
ok = SetEvent( worker->start );
|
||||
err = translate_errno( ok, ( OSStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) LogErr( err, "SetEvent()" );
|
||||
|
||||
result = WaitForSingleObject( worker->thread, 5000 );
|
||||
err = translate_errno( result != WAIT_FAILED, ( OSStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) LogErr( err, "WaitForSingleObject()" );
|
||||
|
||||
if ( ( result == WAIT_FAILED ) || ( result == WAIT_TIMEOUT ) )
|
||||
{
|
||||
ok = TerminateThread( worker->thread, 0 );
|
||||
err = translate_errno( ok, ( OSStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) LogErr( err, "TerminateThread()" );
|
||||
}
|
||||
|
||||
CloseHandle( worker->thread );
|
||||
worker->thread = NULL;
|
||||
|
||||
WorkerFree( worker );
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal void
|
||||
WorkerRegisterSource( Worker *worker, PollSource *source )
|
||||
{
|
||||
source->worker = worker;
|
||||
worker->sources[ worker->numSources ] = source;
|
||||
worker->handles[ worker->numSources ] = source->handle;
|
||||
worker->numSources++;
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal int
|
||||
WorkerSourceToIndex( Worker *worker, PollSource *source )
|
||||
{
|
||||
int index;
|
||||
|
||||
for ( index = 0; index < ( int ) worker->numSources; index++ )
|
||||
{
|
||||
if ( worker->sources[ index ] == source )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( index == ( int ) worker->numSources )
|
||||
{
|
||||
index = -1;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal void
|
||||
WorkerUnregisterSource( Worker *worker, PollSource *source )
|
||||
{
|
||||
int sourceIndex = WorkerSourceToIndex( worker, source );
|
||||
DWORD delta;
|
||||
|
||||
if ( sourceIndex == -1 )
|
||||
{
|
||||
LogMsg( "WorkerUnregisterSource: source not found in list" );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
delta = ( worker->numSources - sourceIndex - 1 );
|
||||
|
||||
// If this source is not at the end of the list, then move memory
|
||||
|
||||
if ( delta > 0 )
|
||||
{
|
||||
ShiftDown( worker->sources, worker->numSources, sizeof( worker->sources[ 0 ] ), sourceIndex + 1 );
|
||||
ShiftDown( worker->handles, worker->numSources, sizeof( worker->handles[ 0 ] ), sourceIndex + 1 );
|
||||
}
|
||||
|
||||
worker->numSources--;
|
||||
|
||||
exit:
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal void CALLBACK
|
||||
WorkerWakeupNotification( HANDLE event, void *context )
|
||||
{
|
||||
DEBUG_UNUSED( event );
|
||||
DEBUG_UNUSED( context );
|
||||
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME "Worker thread wakeup\n" );
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal void
|
||||
WorkerDispatch( Worker *worker )
|
||||
{
|
||||
if ( worker->result == WAIT_FAILED )
|
||||
{
|
||||
/* What should we do here? */
|
||||
}
|
||||
else if ( worker->result == WAIT_TIMEOUT )
|
||||
{
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME "timeout\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD waitItemIndex = ( DWORD )( ( ( int ) worker->result ) - WAIT_OBJECT_0 );
|
||||
PollSource *source = NULL;
|
||||
|
||||
// Sanity check
|
||||
|
||||
if ( waitItemIndex >= worker->numSources )
|
||||
{
|
||||
LogMsg( "WorkerDispatch: waitItemIndex (%d) is >= numSources (%d)", waitItemIndex, worker->numSources );
|
||||
goto exit;
|
||||
}
|
||||
|
||||
source = worker->sources[ waitItemIndex ];
|
||||
|
||||
if ( source->socket != INVALID_SOCKET )
|
||||
{
|
||||
WSANETWORKEVENTS event;
|
||||
|
||||
if ( WSAEnumNetworkEvents( source->socket, source->handle, &event ) == 0 )
|
||||
{
|
||||
source->callback.socket( source->socket, &event, source->context );
|
||||
}
|
||||
else
|
||||
{
|
||||
source->callback.socket( source->socket, NULL, source->context );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
source->callback.event( source->handle, source->context );
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal mStatus
|
||||
WorkerInit( Worker *worker )
|
||||
{
|
||||
PollSource *source = NULL;
|
||||
mStatus err = mStatus_NoError;
|
||||
|
||||
require_action( worker, exit, err = mStatus_BadParamErr );
|
||||
|
||||
source = malloc( sizeof( PollSource ) );
|
||||
require_action( source, exit, err = mStatus_NoMemoryErr );
|
||||
|
||||
source->socket = INVALID_SOCKET;
|
||||
source->handle = gPoll.wakeup;
|
||||
source->callback.event = WorkerWakeupNotification;
|
||||
source->context = NULL;
|
||||
|
||||
WorkerRegisterSource( worker, source );
|
||||
|
||||
exit:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal void
|
||||
WorkerFree( Worker *worker )
|
||||
{
|
||||
if ( worker->start )
|
||||
{
|
||||
CloseHandle( worker->start );
|
||||
worker->start = NULL;
|
||||
}
|
||||
|
||||
if ( worker->stop )
|
||||
{
|
||||
CloseHandle( worker->stop );
|
||||
worker->stop = NULL;
|
||||
}
|
||||
|
||||
free( worker );
|
||||
}
|
||||
|
||||
|
||||
mDNSlocal unsigned WINAPI
|
||||
WorkerMain( LPVOID inParam )
|
||||
{
|
||||
Worker *worker = ( Worker* ) inParam;
|
||||
mStatus err = mStatus_NoError;
|
||||
|
||||
require_action( worker, exit, err = mStatus_BadParamErr );
|
||||
|
||||
dlog( kDebugLevelVerbose, DEBUG_NAME, "entering WorkerMain()\n" );
|
||||
|
||||
while ( TRUE )
|
||||
{
|
||||
DWORD result;
|
||||
BOOL ok;
|
||||
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME, "worker thread %d will wait on main loop\n", worker->id );
|
||||
|
||||
result = WaitForSingleObject( worker->start, INFINITE );
|
||||
err = translate_errno( ( result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) { LogErr( err, "WaitForSingleObject()" ); break; }
|
||||
if ( worker->done ) break;
|
||||
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME "worker thread %d will wait on sockets\n", worker->id );
|
||||
|
||||
worker->result = WaitForMultipleObjects( worker->numSources, worker->handles, FALSE, INFINITE );
|
||||
err = translate_errno( ( worker->result != WAIT_FAILED ), ( mStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) { LogErr( err, "WaitForMultipleObjects()" ); break; }
|
||||
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME "worker thread %d did wait on sockets: %d\n", worker->id, worker->result );
|
||||
|
||||
ok = SetEvent( gPoll.wakeup );
|
||||
err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) { LogErr( err, "SetEvent()" ); break; }
|
||||
|
||||
dlog( kDebugLevelChatty, DEBUG_NAME, "worker thread %d preparing to sleep\n", worker->id );
|
||||
|
||||
ok = SetEvent( worker->stop );
|
||||
err = translate_errno( ok, ( mStatus ) GetLastError(), kUnknownErr );
|
||||
if ( err ) { LogErr( err, "SetEvent()" ); break; }
|
||||
}
|
||||
|
||||
dlog( kDebugLevelVerbose, DEBUG_NAME "exiting WorkerMain()\n" );
|
||||
|
||||
exit:
|
||||
|
||||
return 0;
|
||||
}
|
61
src/tools/mdnssd/Poll.h
Normal file
61
src/tools/mdnssd/Poll.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _Poll_h
|
||||
#define _Poll_h
|
||||
|
||||
#include "CommonServices.h"
|
||||
#include <mswsock.h>
|
||||
#include "mDNSEmbeddedAPI.h"
|
||||
#include "uDNS.h"
|
||||
|
||||
|
||||
#if defined(__cplusplus )
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef void ( CALLBACK *mDNSPollSocketCallback )( SOCKET socket, LPWSANETWORKEVENTS event, void *context );
|
||||
typedef void ( CALLBACK *mDNSPollEventCallback )( HANDLE event, void *context );
|
||||
|
||||
|
||||
extern mStatus
|
||||
mDNSPollRegisterSocket( SOCKET socket, int networkEvents, mDNSPollSocketCallback callback, void *context );
|
||||
|
||||
|
||||
extern void
|
||||
mDNSPollUnregisterSocket( SOCKET socket );
|
||||
|
||||
|
||||
extern mStatus
|
||||
mDNSPollRegisterEvent( HANDLE event, mDNSPollEventCallback callback, void *context );
|
||||
|
||||
|
||||
extern void
|
||||
mDNSPollUnregisterEvent( HANDLE event );
|
||||
|
||||
|
||||
extern mStatus
|
||||
mDNSPoll( DWORD msec );
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
57
src/tools/mdnssd/RegNames.h
Normal file
57
src/tools/mdnssd/RegNames.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Registry Constants
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
#if defined(UNICODE)
|
||||
|
||||
# define kServiceParametersSoftware L"SOFTWARE"
|
||||
# define kServiceParametersAppleComputer L"Apple Computer, Inc."
|
||||
# define kServiceParametersBonjour L"Bonjour"
|
||||
# define kServiceParametersNode L"SOFTWARE\\Apple Inc.\\Bonjour"
|
||||
# define kServiceName L"Bonjour Service"
|
||||
# define kServiceDynDNSBrowseDomains L"BrowseDomains"
|
||||
# define kServiceDynDNSHostNames L"HostNames"
|
||||
# define kServiceDynDNSRegistrationDomains L"RegistrationDomains"
|
||||
# define kServiceDynDNSDomains L"Domains" // value is comma separated list of domains
|
||||
# define kServiceDynDNSEnabled L"Enabled"
|
||||
# define kServiceDynDNSStatus L"Status"
|
||||
# define kServiceManageLLRouting L"ManageLLRouting"
|
||||
# define kServiceCacheEntryCount L"CacheEntryCount"
|
||||
# define kServiceManageFirewall L"ManageFirewall"
|
||||
# define kServiceAdvertisedServices L"Services"
|
||||
|
||||
# else
|
||||
|
||||
# define kServiceParametersSoftware "SOFTWARE"
|
||||
# define kServiceParametersAppleComputer "Apple Computer, Inc."
|
||||
# define kServiceParametersBonjour "Bonjour"
|
||||
# define kServiceParametersNode "SOFTWARE\\Apple Inc.\\Bonjour"
|
||||
# define kServiceName "Bonjour Service"
|
||||
# define kServiceDynDNSBrowseDomains "BrowseDomains"
|
||||
# define kServiceDynDNSHostNames "HostNames"
|
||||
# define kServiceDynDNSRegistrationDomains "RegistrationDomains"
|
||||
# define kServiceDynDNSDomains "Domains" // value is comma separated list of domains
|
||||
# define kServiceDynDNSEnabled "Enabled"
|
||||
# define kServiceDynDNSStatus "Status"
|
||||
# define kServiceManageLLRouting "ManageLLRouting"
|
||||
# define kServiceCacheEntryCount "CacheEntryCount"
|
||||
# define kServiceManageFirewall "ManageFirewall"
|
||||
|
||||
#endif
|
338
src/tools/mdnssd/Secret.c
Normal file
338
src/tools/mdnssd/Secret.c
Normal file
@@ -0,0 +1,338 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Secret.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#include <ntsecapi.h>
|
||||
#include <lm.h>
|
||||
#include "DebugServices.h"
|
||||
|
||||
|
||||
mDNSlocal OSStatus MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input );
|
||||
mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input );
|
||||
|
||||
|
||||
BOOL
|
||||
LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize )
|
||||
{
|
||||
PLSA_UNICODE_STRING domainLSA;
|
||||
PLSA_UNICODE_STRING keyLSA;
|
||||
PLSA_UNICODE_STRING secretLSA;
|
||||
size_t i;
|
||||
size_t dlen;
|
||||
LSA_OBJECT_ATTRIBUTES attrs;
|
||||
LSA_HANDLE handle = NULL;
|
||||
NTSTATUS res;
|
||||
OSStatus err;
|
||||
|
||||
check( inDomain );
|
||||
check( outDomain );
|
||||
check( outKey );
|
||||
check( outSecret );
|
||||
|
||||
// Initialize
|
||||
|
||||
domainLSA = NULL;
|
||||
keyLSA = NULL;
|
||||
secretLSA = NULL;
|
||||
|
||||
// Make sure we have enough space to add trailing dot
|
||||
|
||||
dlen = strlen( inDomain );
|
||||
err = strcpy_s( outDomain, outDomainSize - 2, inDomain );
|
||||
require_noerr( err, exit );
|
||||
|
||||
// If there isn't a trailing dot, add one because the mDNSResponder
|
||||
// presents names with the trailing dot.
|
||||
|
||||
if ( outDomain[ dlen - 1 ] != '.' )
|
||||
{
|
||||
outDomain[ dlen++ ] = '.';
|
||||
outDomain[ dlen ] = '\0';
|
||||
}
|
||||
|
||||
// Canonicalize name by converting to lower case (keychain and some name servers are case sensitive)
|
||||
|
||||
for ( i = 0; i < dlen; i++ )
|
||||
{
|
||||
outDomain[i] = (char) tolower( outDomain[i] ); // canonicalize -> lower case
|
||||
}
|
||||
|
||||
// attrs are reserved, so initialize to zeroes.
|
||||
|
||||
ZeroMemory( &attrs, sizeof( attrs ) );
|
||||
|
||||
// Get a handle to the Policy object on the local system
|
||||
|
||||
res = LsaOpenPolicy( NULL, &attrs, POLICY_GET_PRIVATE_INFORMATION, &handle );
|
||||
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
||||
require_noerr( err, exit );
|
||||
|
||||
// Get the encrypted data
|
||||
|
||||
domainLSA = ( PLSA_UNICODE_STRING ) malloc( sizeof( LSA_UNICODE_STRING ) );
|
||||
require_action( domainLSA != NULL, exit, err = mStatus_NoMemoryErr );
|
||||
err = MakeLsaStringFromUTF8String( domainLSA, outDomain );
|
||||
require_noerr( err, exit );
|
||||
|
||||
// Retrieve the key
|
||||
|
||||
res = LsaRetrievePrivateData( handle, domainLSA, &keyLSA );
|
||||
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
||||
require_noerr_quiet( err, exit );
|
||||
|
||||
// <rdar://problem/4192119> Lsa secrets use a flat naming space. Therefore, we will prepend "$" to the keyname to
|
||||
// make sure it doesn't conflict with a zone name.
|
||||
// Strip off the "$" prefix.
|
||||
|
||||
err = MakeUTF8StringFromLsaString( outKey, outKeySize, keyLSA );
|
||||
require_noerr( err, exit );
|
||||
require_action( outKey[0] == '$', exit, err = kUnknownErr );
|
||||
memcpy( outKey, outKey + 1, strlen( outKey ) );
|
||||
|
||||
// Retrieve the secret
|
||||
|
||||
res = LsaRetrievePrivateData( handle, keyLSA, &secretLSA );
|
||||
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
||||
require_noerr_quiet( err, exit );
|
||||
|
||||
// Convert the secret to UTF8 string
|
||||
|
||||
err = MakeUTF8StringFromLsaString( outSecret, outSecretSize, secretLSA );
|
||||
require_noerr( err, exit );
|
||||
|
||||
exit:
|
||||
|
||||
if ( domainLSA != NULL )
|
||||
{
|
||||
if ( domainLSA->Buffer != NULL )
|
||||
{
|
||||
free( domainLSA->Buffer );
|
||||
}
|
||||
|
||||
free( domainLSA );
|
||||
}
|
||||
|
||||
if ( keyLSA != NULL )
|
||||
{
|
||||
LsaFreeMemory( keyLSA );
|
||||
}
|
||||
|
||||
if ( secretLSA != NULL )
|
||||
{
|
||||
LsaFreeMemory( secretLSA );
|
||||
}
|
||||
|
||||
if ( handle )
|
||||
{
|
||||
LsaClose( handle );
|
||||
handle = NULL;
|
||||
}
|
||||
|
||||
return ( !err ) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
||||
mDNSBool
|
||||
LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret )
|
||||
{
|
||||
size_t inDomainLength;
|
||||
size_t inKeyLength;
|
||||
char domain[ 1024 ];
|
||||
char key[ 1024 ];
|
||||
LSA_OBJECT_ATTRIBUTES attrs;
|
||||
LSA_HANDLE handle = NULL;
|
||||
NTSTATUS res;
|
||||
LSA_UNICODE_STRING lucZoneName;
|
||||
LSA_UNICODE_STRING lucKeyName;
|
||||
LSA_UNICODE_STRING lucSecretName;
|
||||
BOOL ok = TRUE;
|
||||
OSStatus err;
|
||||
|
||||
require_action( inDomain != NULL, exit, ok = FALSE );
|
||||
require_action( inKey != NULL, exit, ok = FALSE );
|
||||
require_action( inSecret != NULL, exit, ok = FALSE );
|
||||
|
||||
// If there isn't a trailing dot, add one because the mDNSResponder
|
||||
// presents names with the trailing dot.
|
||||
|
||||
ZeroMemory( domain, sizeof( domain ) );
|
||||
inDomainLength = strlen( inDomain );
|
||||
require_action( inDomainLength > 0, exit, ok = FALSE );
|
||||
err = strcpy_s( domain, sizeof( domain ) - 2, inDomain );
|
||||
require_action( !err, exit, ok = FALSE );
|
||||
|
||||
if ( domain[ inDomainLength - 1 ] != '.' )
|
||||
{
|
||||
domain[ inDomainLength++ ] = '.';
|
||||
domain[ inDomainLength ] = '\0';
|
||||
}
|
||||
|
||||
// <rdar://problem/4192119>
|
||||
//
|
||||
// Prepend "$" to the key name, so that there will
|
||||
// be no conflict between the zone name and the key
|
||||
// name
|
||||
|
||||
ZeroMemory( key, sizeof( key ) );
|
||||
inKeyLength = strlen( inKey );
|
||||
require_action( inKeyLength > 0 , exit, ok = FALSE );
|
||||
key[ 0 ] = '$';
|
||||
err = strcpy_s( key + 1, sizeof( key ) - 3, inKey );
|
||||
require_action( !err, exit, ok = FALSE );
|
||||
inKeyLength++;
|
||||
|
||||
if ( key[ inKeyLength - 1 ] != '.' )
|
||||
{
|
||||
key[ inKeyLength++ ] = '.';
|
||||
key[ inKeyLength ] = '\0';
|
||||
}
|
||||
|
||||
// attrs are reserved, so initialize to zeroes.
|
||||
|
||||
ZeroMemory( &attrs, sizeof( attrs ) );
|
||||
|
||||
// Get a handle to the Policy object on the local system
|
||||
|
||||
res = LsaOpenPolicy( NULL, &attrs, POLICY_ALL_ACCESS, &handle );
|
||||
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
||||
require_noerr( err, exit );
|
||||
|
||||
// Intializing PLSA_UNICODE_STRING structures
|
||||
|
||||
err = MakeLsaStringFromUTF8String( &lucZoneName, domain );
|
||||
require_noerr( err, exit );
|
||||
|
||||
err = MakeLsaStringFromUTF8String( &lucKeyName, key );
|
||||
require_noerr( err, exit );
|
||||
|
||||
err = MakeLsaStringFromUTF8String( &lucSecretName, inSecret );
|
||||
require_noerr( err, exit );
|
||||
|
||||
// Store the private data.
|
||||
|
||||
res = LsaStorePrivateData( handle, &lucZoneName, &lucKeyName );
|
||||
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
||||
require_noerr( err, exit );
|
||||
|
||||
res = LsaStorePrivateData( handle, &lucKeyName, &lucSecretName );
|
||||
err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
|
||||
require_noerr( err, exit );
|
||||
|
||||
exit:
|
||||
|
||||
if ( handle )
|
||||
{
|
||||
LsaClose( handle );
|
||||
handle = NULL;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================================================================
|
||||
// MakeLsaStringFromUTF8String
|
||||
//===========================================================================================================================
|
||||
|
||||
mDNSlocal OSStatus
|
||||
MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input )
|
||||
{
|
||||
int size;
|
||||
OSStatus err;
|
||||
|
||||
check( input );
|
||||
check( output );
|
||||
|
||||
output->Buffer = NULL;
|
||||
|
||||
size = MultiByteToWideChar( CP_UTF8, 0, input, -1, NULL, 0 );
|
||||
err = translate_errno( size > 0, GetLastError(), kUnknownErr );
|
||||
require_noerr( err, exit );
|
||||
|
||||
output->Length = (USHORT)( size * sizeof( wchar_t ) );
|
||||
output->Buffer = (PWCHAR) malloc( output->Length );
|
||||
require_action( output->Buffer, exit, err = mStatus_NoMemoryErr );
|
||||
size = MultiByteToWideChar( CP_UTF8, 0, input, -1, output->Buffer, size );
|
||||
err = translate_errno( size > 0, GetLastError(), kUnknownErr );
|
||||
require_noerr( err, exit );
|
||||
|
||||
// We're going to subtrace one wchar_t from the size, because we didn't
|
||||
// include it when we encoded the string
|
||||
|
||||
output->MaximumLength = output->Length;
|
||||
output->Length -= sizeof( wchar_t );
|
||||
|
||||
exit:
|
||||
|
||||
if ( err && output->Buffer )
|
||||
{
|
||||
free( output->Buffer );
|
||||
output->Buffer = NULL;
|
||||
}
|
||||
|
||||
return( err );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========================================================================================================================
|
||||
// MakeUTF8StringFromLsaString
|
||||
//===========================================================================================================================
|
||||
|
||||
mDNSlocal OSStatus
|
||||
MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input )
|
||||
{
|
||||
size_t size;
|
||||
OSStatus err = kNoErr;
|
||||
|
||||
// The Length field of this structure holds the number of bytes,
|
||||
// but WideCharToMultiByte expects the number of wchar_t's. So
|
||||
// we divide by sizeof(wchar_t) to get the correct number.
|
||||
|
||||
size = (size_t) WideCharToMultiByte(CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), NULL, 0, NULL, NULL);
|
||||
err = translate_errno( size != 0, GetLastError(), kUnknownErr );
|
||||
require_noerr( err, exit );
|
||||
|
||||
// Ensure that we have enough space (Add one for trailing '\0')
|
||||
|
||||
require_action( ( size + 1 ) <= len, exit, err = mStatus_NoMemoryErr );
|
||||
|
||||
// Convert the string
|
||||
|
||||
size = (size_t) WideCharToMultiByte( CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), output, (int) size, NULL, NULL);
|
||||
err = translate_errno( size != 0, GetLastError(), kUnknownErr );
|
||||
require_noerr( err, exit );
|
||||
|
||||
// have to add the trailing 0 because WideCharToMultiByte doesn't do it,
|
||||
// although it does return the correct size
|
||||
|
||||
output[size] = '\0';
|
||||
|
||||
exit:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
42
src/tools/mdnssd/Secret.h
Normal file
42
src/tools/mdnssd/Secret.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _Secret_h
|
||||
#define _Secret_h
|
||||
|
||||
#include "mDNSEmbeddedAPI.h"
|
||||
|
||||
|
||||
#if defined(__cplusplus )
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
extern mDNSBool
|
||||
LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainLength, char * outKey, unsigned outKeyLength, char * outSecret, unsigned outSecretLength );
|
||||
|
||||
|
||||
extern mDNSBool
|
||||
LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret );
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
2597
src/tools/mdnssd/Service.c
Normal file
2597
src/tools/mdnssd/Service.c
Normal file
File diff suppressed because it is too large
Load Diff
30
src/tools/mdnssd/Service.h
Normal file
30
src/tools/mdnssd/Service.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __MDNS_SERVICE_H__
|
||||
#define __MDNS_SERVICE_H__
|
||||
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
extern int RunDirect( int argc, LPTSTR argv[] );
|
||||
extern int Main( int argc, LPTSTR argv[] );
|
||||
|
||||
|
||||
#endif
|
||||
|
115
src/tools/mdnssd/Service.rc
Normal file
115
src/tools/mdnssd/Service.rc
Normal file
@@ -0,0 +1,115 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
//#include "winres.h"
|
||||
//#include "WinVersRes.h"
|
||||
#include "windows.h"
|
||||
#include "EventLog.rc"
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,0
|
||||
PRODUCTVERSION 1,0,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Nokia Corporation and/or its subsidiary(-ies)\0"
|
||||
VALUE "FileDescription", "Bonjour Service Fallback\0"
|
||||
VALUE "FileVersion", 1,0,0,0
|
||||
VALUE "InternalName", "mdnssd.exe\0"
|
||||
VALUE "LegalCopyright", "Copyright(c)2003-2012, Apple Computer, 2012 Nokia Corporation and/or its subsidiary(-ies).\0"
|
||||
VALUE "OriginalFilename", "mdnssd.exe\0"
|
||||
VALUE "ProductName", "Bonjour Fallback\0"
|
||||
VALUE "ProductVersion", "1.0\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"#include ""WinVersRes.h""\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_SERVICE_DESCRIPTION "Enables hardware devices and software services to automatically configure themselves on the network and advertise their presence, so that users can discover and use those services without any unnecessary manual setup or administration."
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
@@ -1884,7 +1884,7 @@ mDNSlocal mDNSu16 CheckSum(const void *const data, mDNSs32 length, mDNSu32 sum)
|
||||
while (length > 0) { length -= 2; sum += *ptr++; }
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
return(sum != 0xFFFF ? sum : 0);
|
||||
return (mDNSu16)(sum != 0xFFFF ? sum : 0);
|
||||
}
|
||||
|
||||
mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *const dst, const mDNSu8 protocol, const void *const data, const mDNSu32 length)
|
||||
@@ -1892,10 +1892,10 @@ mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *co
|
||||
IPv6PseudoHeader ph;
|
||||
ph.src = *src;
|
||||
ph.dst = *dst;
|
||||
ph.len.b[0] = length >> 24;
|
||||
ph.len.b[1] = length >> 16;
|
||||
ph.len.b[2] = length >> 8;
|
||||
ph.len.b[3] = length;
|
||||
ph.len.b[0] = (0xFF & (length >> 24));
|
||||
ph.len.b[1] = (0xFF & (length >> 16));
|
||||
ph.len.b[2] = (0xFF & (length >> 8));
|
||||
ph.len.b[3] = (0xFF & length);
|
||||
ph.pro.b[0] = 0;
|
||||
ph.pro.b[1] = 0;
|
||||
ph.pro.b[2] = 0;
|
||||
@@ -7706,23 +7706,23 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi
|
||||
}
|
||||
}
|
||||
|
||||
mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout)
|
||||
mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, mDNSu32 timeout)
|
||||
{
|
||||
McastResolver **p = &m->McastResolvers;
|
||||
McastResolver *tmp = mDNSNULL;
|
||||
|
||||
if (!d) d = (const domainname *)"";
|
||||
|
||||
LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout);
|
||||
LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface1, timeout);
|
||||
|
||||
if (m->mDNS_busy != m->mDNS_reentrancy+1)
|
||||
LogMsg("mDNS_AddMcastResolver: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
|
||||
|
||||
while (*p) // Check if we already have this {interface, domain} tuple registered
|
||||
{
|
||||
if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d))
|
||||
if ((*p)->interface1 == interface1 && SameDomainName(&(*p)->domain, d))
|
||||
{
|
||||
if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface);
|
||||
if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface1);
|
||||
(*p)->flags &= ~DNSServer_FlagDelete;
|
||||
tmp = *p;
|
||||
*p = tmp->next;
|
||||
@@ -7740,7 +7740,7 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname
|
||||
if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc");
|
||||
else
|
||||
{
|
||||
(*p)->interface = interface;
|
||||
(*p)->interface1 = interface1;
|
||||
(*p)->flags = DNSServer_FlagNew;
|
||||
(*p)->timeout = timeout;
|
||||
AssignDomainName(&(*p)->domain, d);
|
||||
@@ -7905,13 +7905,13 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
|
||||
//
|
||||
// Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout
|
||||
|
||||
if (curr->scoped && curr->interface == mDNSInterface_Any)
|
||||
if (curr->scoped && curr->interface1 == mDNSInterface_Any)
|
||||
{ debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); continue; }
|
||||
|
||||
currcount = CountLabels(&curr->domain);
|
||||
if ((!DEQuery || !curr->cellIntf) &&
|
||||
((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) ||
|
||||
(curr->interface == question->InterfaceID)))
|
||||
(curr->interface1 == question->InterfaceID)))
|
||||
{
|
||||
bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen);
|
||||
|
||||
@@ -7926,7 +7926,7 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
|
||||
if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; }
|
||||
debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d,"
|
||||
" Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout,
|
||||
curr->interface);
|
||||
curr->interface1);
|
||||
timeout += curr->timeout;
|
||||
if (DEQuery) debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf);
|
||||
bit_set_opaque64(question->validDNSServers, index);
|
||||
@@ -7993,7 +7993,7 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac
|
||||
// is the new way of specifying an InterfaceID option for DNSServer. These will be considered
|
||||
// only when the question has non-zero interfaceID.
|
||||
|
||||
if ((!curr->scoped && !InterfaceID) || (curr->interface == InterfaceID))
|
||||
if ((!curr->scoped && !InterfaceID) || (curr->interface1 == InterfaceID))
|
||||
{
|
||||
|
||||
// If we know that all the names are already equally good matches, then skip calling BetterMatchForName.
|
||||
@@ -9065,7 +9065,7 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
|
||||
query->ServiceInfoQueryContext = Context;
|
||||
|
||||
// info->name = Must already be set up by client
|
||||
// info->interface = Must already be set up by client
|
||||
// info->interface1 = Must already be set up by client
|
||||
info->ip = zeroAddr;
|
||||
info->port = zeroIPPort;
|
||||
info->TXTlen = 0;
|
||||
@@ -9713,9 +9713,9 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se
|
||||
|
||||
// 3. Any DNS servers specific to this interface are now unusable
|
||||
for (s = m->DNSServers; s; s = s->next)
|
||||
if (s->interface == set->InterfaceID)
|
||||
if (s->interface1 == set->InterfaceID)
|
||||
{
|
||||
s->interface = mDNSInterface_Any;
|
||||
s->interface1 = mDNSInterface_Any;
|
||||
s->teststate = DNSServer_Disabled;
|
||||
}
|
||||
}
|
||||
|
@@ -1054,7 +1054,7 @@ enum
|
||||
typedef struct McastResolver
|
||||
{
|
||||
struct McastResolver *next;
|
||||
mDNSInterfaceID interface;
|
||||
mDNSInterfaceID interface1;
|
||||
mDNSu32 flags; // Set when we're planning to delete this from the list
|
||||
domainname domain;
|
||||
mDNSu32 timeout; // timeout value for questions
|
||||
@@ -1063,7 +1063,7 @@ typedef struct McastResolver
|
||||
typedef struct DNSServer
|
||||
{
|
||||
struct DNSServer *next;
|
||||
mDNSInterfaceID interface; // For specialized uses; we can have DNS servers reachable over specific interfaces
|
||||
mDNSInterfaceID interface1; // For specialized uses; we can have DNS servers reachable over specific interfaces
|
||||
mDNSAddr addr;
|
||||
mDNSIPPort port;
|
||||
mDNSOpaque16 testid;
|
||||
@@ -2469,11 +2469,11 @@ extern void RecreateNATMappings(mDNS *const m);
|
||||
extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext);
|
||||
extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn);
|
||||
extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router);
|
||||
extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf);
|
||||
extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf);
|
||||
extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q);
|
||||
extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID);
|
||||
|
||||
extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout);
|
||||
extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, mDNSu32 timeout);
|
||||
|
||||
// We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2
|
||||
#define mDNS_AddSearchDomain_CString(X, I) \
|
||||
|
5018
src/tools/mdnssd/mDNSWin32.c
Normal file
5018
src/tools/mdnssd/mDNSWin32.c
Normal file
File diff suppressed because it is too large
Load Diff
163
src/tools/mdnssd/mDNSWin32.h
Normal file
163
src/tools/mdnssd/mDNSWin32.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __MDNS_WIN32__
|
||||
#define __MDNS_WIN32__
|
||||
|
||||
#include "CommonServices.h"
|
||||
|
||||
#if( !defined( _WIN32_WCE ) )
|
||||
#include <mswsock.h>
|
||||
#endif
|
||||
|
||||
#include "mDNSEmbeddedAPI.h"
|
||||
#include "uDNS.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef void ( *TCPUserCallback )();
|
||||
|
||||
struct TCPSocket_struct
|
||||
{
|
||||
TCPSocketFlags flags; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags
|
||||
SOCKET fd;
|
||||
BOOL connected;
|
||||
TCPUserCallback userCallback;
|
||||
void * userContext;
|
||||
BOOL closed;
|
||||
mDNS * m;
|
||||
};
|
||||
|
||||
|
||||
struct UDPSocket_struct
|
||||
{
|
||||
mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
|
||||
mDNSAddr addr; // This is initialized by our code. If we don't get the
|
||||
// dstAddr from WSARecvMsg we use this value instead.
|
||||
SOCKET fd;
|
||||
LPFN_WSARECVMSG recvMsgPtr;
|
||||
DNSMessage packet;
|
||||
struct mDNSInterfaceData *ifd;
|
||||
UDPSocket *next;
|
||||
mDNS *m;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------
|
||||
/*! @struct mDNSInterfaceData
|
||||
|
||||
@abstract Structure containing interface-specific data.
|
||||
*/
|
||||
|
||||
typedef struct mDNSInterfaceData mDNSInterfaceData;
|
||||
struct mDNSInterfaceData
|
||||
{
|
||||
char name[ 128 ];
|
||||
uint32_t index;
|
||||
uint32_t scopeID;
|
||||
struct UDPSocket_struct sock;
|
||||
NetworkInterfaceInfo interfaceInfo;
|
||||
mDNSBool hostRegistered;
|
||||
mDNSInterfaceData * next;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------
|
||||
/*! @typedef ReportStatusFunc
|
||||
*/
|
||||
typedef void (*ReportStatusFunc)(int inType, const char *inFormat, ...);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------
|
||||
/*! @struct mDNS_PlatformSupport_struct
|
||||
|
||||
@abstract Structure containing platform-specific data.
|
||||
*/
|
||||
|
||||
struct mDNS_PlatformSupport_struct
|
||||
{
|
||||
HANDLE mainThread;
|
||||
HANDLE checkFileSharesTimer;
|
||||
mDNSs32 checkFileSharesTimeout;
|
||||
ReportStatusFunc reportStatusFunc;
|
||||
time_t nextDHCPLeaseExpires;
|
||||
char nbname[ 32 ];
|
||||
char nbdomain[ 32 ];
|
||||
mDNSBool smbFileSharing;
|
||||
mDNSBool smbPrintSharing;
|
||||
ServiceRecordSet smbSRS;
|
||||
AuthRecord smbSubTypes[ 2 ];
|
||||
mDNSBool registeredLoopback4;
|
||||
int interfaceCount;
|
||||
mDNSInterfaceData * interfaceList;
|
||||
mDNSInterfaceData * inactiveInterfaceList;
|
||||
struct UDPSocket_struct unicastSock4;
|
||||
struct UDPSocket_struct unicastSock6;
|
||||
DWORD osMajorVersion;
|
||||
DWORD osMinorVersion;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------------
|
||||
/*! @struct ifaddrs
|
||||
|
||||
@abstract Interface information
|
||||
*/
|
||||
|
||||
struct ifaddrs
|
||||
{
|
||||
struct ifaddrs * ifa_next;
|
||||
char * ifa_name;
|
||||
u_int ifa_flags;
|
||||
struct sockaddr * ifa_addr;
|
||||
struct sockaddr * ifa_netmask;
|
||||
struct sockaddr * ifa_broadaddr;
|
||||
struct sockaddr * ifa_dstaddr;
|
||||
BYTE ifa_physaddr[6];
|
||||
BOOL ifa_dhcpEnabled;
|
||||
time_t ifa_dhcpLeaseExpires;
|
||||
mDNSu8 ifa_womp;
|
||||
void * ifa_data;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
} ifa_extra;
|
||||
};
|
||||
|
||||
|
||||
extern void InterfaceListDidChange( mDNS * const inMDNS );
|
||||
extern void ComputerDescriptionDidChange( mDNS * const inMDNS );
|
||||
extern void TCPIPConfigDidChange( mDNS * const inMDNS );
|
||||
extern void DynDNSConfigDidChange( mDNS * const inMDNS );
|
||||
extern void FileSharingDidChange( mDNS * const inMDNS );
|
||||
extern void FirewallDidChange( mDNS * const inMDNS );
|
||||
extern mStatus TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock );
|
||||
extern mStatus SetupInterfaceList( mDNS * const inMDNS );
|
||||
extern mStatus TearDownInterfaceList( mDNS * const inMDNS );
|
||||
extern BOOL IsWOMPEnabled();
|
||||
extern void DispatchSocketEvents( mDNS * const inMDNS );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __MDNS_WIN32__
|
38
src/tools/mdnssd/main.c
Normal file
38
src/tools/mdnssd/main.c
Normal file
@@ -0,0 +1,38 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Service.h"
|
||||
#include "Shellapi.h"
|
||||
|
||||
//===========================================================================================================================
|
||||
// main
|
||||
//===========================================================================================================================
|
||||
int APIENTRY wWinMain(HINSTANCE hInstance,
|
||||
HINSTANCE hPrevInstance,
|
||||
LPTSTR lpCmdLine,
|
||||
int nCmdShow)
|
||||
{
|
||||
LPWSTR *argv;
|
||||
int argc, res;
|
||||
|
||||
argv = CommandLineToArgvW(lpCmdLine, &argc);
|
||||
if (argv == NULL)
|
||||
argc = 0;
|
||||
res = Main( argc, argv );
|
||||
LocalFree(argv);
|
||||
return res;
|
||||
}
|
@@ -1,19 +1,12 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2011-10-14T10:22:27
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT -= gui
|
||||
QT += core
|
||||
|
||||
TEST = 0
|
||||
include(../../../qtcreator.pri)
|
||||
CONFIG -= console testlib TEST
|
||||
TARGET = mdnssd
|
||||
CONFIG += console
|
||||
CONFIG -= app_bundle
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
include(../../../qtcreator.pri)
|
||||
DESTDIR = $$IDE_BIN_PATH
|
||||
|
||||
DEFINES += PID_FILE=\\\"/var/run/mdnsd.pid\\\" MDNS_UDS_SERVERPATH=\\\"/var/run/mdnsd\\\" MDNS_DEBUGMSGS=0
|
||||
@@ -21,10 +14,6 @@ DEFINES += PID_FILE=\\\"/var/run/mdnsd.pid\\\" MDNS_UDS_SERVERPATH=\\\"/var/run/
|
||||
SOURCES += \
|
||||
uds_daemon.c \
|
||||
uDNS.c \
|
||||
PosixDaemon.c \
|
||||
PlatformCommon.c \
|
||||
mDNSUNP.c \
|
||||
mDNSPosix.c \
|
||||
mDNSDebug.c \
|
||||
mDNS.c \
|
||||
GenLinkedList.c \
|
||||
@@ -35,9 +24,7 @@ SOURCES += \
|
||||
HEADERS += \
|
||||
uds_daemon.h \
|
||||
uDNS.h \
|
||||
PlatformCommon.h \
|
||||
mDNSUNP.h \
|
||||
mDNSPosix.h \
|
||||
mDNSEmbeddedAPI.h \
|
||||
mDNSDebug.h \
|
||||
GenLinkedList.h \
|
||||
@@ -46,14 +33,70 @@ HEADERS += \
|
||||
DebugServices.h \
|
||||
dns_sd.h
|
||||
|
||||
linux-* {
|
||||
SOURCES += mDNSPosix.c \
|
||||
PlatformCommon.c \
|
||||
PosixDaemon.c \
|
||||
mDNSUNP.c
|
||||
|
||||
HEADERS +=\
|
||||
PlatformCommon.h \
|
||||
mDNSPosix.h
|
||||
}
|
||||
|
||||
*-g++ {
|
||||
QMAKE_CFLAGS += -Wno-unused-but-set-variable
|
||||
QMAKE_CXXFLAGS += -Wno-unused-but-set-variable
|
||||
}
|
||||
|
||||
linux-* {
|
||||
DEFINES += _GNU_SOURCE HAVE_IPV6 NOT_HAVE_SA_LEN USES_NETLINK HAVE_LINUX TARGET_OS_LINUX
|
||||
}
|
||||
|
||||
macx {
|
||||
DEFINES += HAVE_IPV6 __MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 __APPLE_USE_RFC_2292
|
||||
}
|
||||
|
||||
win32 {
|
||||
HEADERS += \
|
||||
CommonServices.h \
|
||||
DebugServices.h \
|
||||
Firewall.h \
|
||||
mDNSWin32.h \
|
||||
Poll.h \
|
||||
resource.h \
|
||||
Secret.h \
|
||||
Service.h \
|
||||
RegNames.h
|
||||
|
||||
SOURCES += \
|
||||
DebugServices.c \
|
||||
Firewall.cpp \
|
||||
LegacyNATTraversal.c \
|
||||
main.c \
|
||||
mDNSWin32.c \
|
||||
Poll.c \
|
||||
Secret.c \
|
||||
Service.c
|
||||
|
||||
RC_FILE = Service.rc
|
||||
|
||||
MC_FILES += \
|
||||
EventLog.mc
|
||||
|
||||
OTHER_FILES += \
|
||||
$$MC_FILES \
|
||||
Service.rc
|
||||
|
||||
DEFINES += HAVE_IPV6 _WIN32_WINNT=0x0501 NDEBUG MDNS_DEBUGMSGS=0 TARGET_OS_WIN32 WIN32_LEAN_AND_MEAN USE_TCP_LOOPBACK PLATFORM_NO_STRSEP PLATFORM_NO_EPIPE PLATFORM_NO_RLIMIT UNICODE _UNICODE _CRT_SECURE_NO_DEPRECATE _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 _LEGACY_NAT_TRAVERSAL_ _USE_32BIT_TIME_T
|
||||
LIBS += ws2_32.lib advapi32.lib ole32.lib oleaut32.lib iphlpapi.lib netapi32.lib user32.lib powrprof.lib shell32.lib
|
||||
|
||||
mc.output = ${QMAKE_FILE_BASE}.h
|
||||
mc.commands = mc ${QMAKE_FILE_NAME}
|
||||
mc.input = MC_FILES
|
||||
mc.CONFIG += no_link target_predeps explicit_dependencies
|
||||
QMAKE_EXTRA_COMPILERS += mc
|
||||
}
|
||||
|
||||
target.path=/bin
|
||||
INSTALLS+=target
|
||||
|
17
src/tools/mdnssd/resource.h
Normal file
17
src/tools/mdnssd/resource.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Service.rc
|
||||
//
|
||||
|
||||
#define IDS_SERVICE_DESCRIPTION 100
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
@@ -101,7 +101,7 @@ mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random)
|
||||
#pragma mark - Name Server List Management
|
||||
#endif
|
||||
|
||||
mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf)
|
||||
mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface1, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf)
|
||||
{
|
||||
DNSServer **p = &m->DNSServers;
|
||||
DNSServer *tmp = mDNSNULL;
|
||||
@@ -114,16 +114,16 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons
|
||||
|
||||
if (!d) d = (const domainname *)"";
|
||||
|
||||
LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d", addr, d->c, interface, scoped);
|
||||
LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d", addr, d->c, interface1, scoped);
|
||||
if (m->mDNS_busy != m->mDNS_reentrancy+1)
|
||||
LogMsg("mDNS_AddDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
|
||||
|
||||
while (*p) // Check if we already have this {interface,address,port,domain} tuple registered
|
||||
{
|
||||
if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->teststate != DNSServer_Disabled &&
|
||||
if ((*p)->scoped == scoped && (*p)->interface1 == interface1 && (*p)->teststate != DNSServer_Disabled &&
|
||||
mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d))
|
||||
{
|
||||
if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface);
|
||||
if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface1);
|
||||
(*p)->flags &= ~DNSServer_FlagDelete;
|
||||
tmp = *p;
|
||||
*p = tmp->next;
|
||||
@@ -143,7 +143,7 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons
|
||||
{
|
||||
NumUnicastDNSServers++;
|
||||
(*p)->scoped = scoped;
|
||||
(*p)->interface = interface;
|
||||
(*p)->interface1 = interface1;
|
||||
(*p)->addr = *addr;
|
||||
(*p)->port = port;
|
||||
(*p)->flags = DNSServer_FlagNew;
|
||||
@@ -4215,7 +4215,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
|
||||
q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries);
|
||||
if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
|
||||
if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time
|
||||
else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
|
||||
else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface1, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*- Mode: C; tab-width: 4 -*-
|
||||
*
|
||||
* Copyright (c) 2003-2006 Apple Computer, Inc. All rights reserved.
|
||||
* Copyright (c) 2003-2011 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(WIN32)
|
||||
#include <WinSock2.h>
|
||||
#include <process.h>
|
||||
#define usleep(X) Sleep(((X)+999)/1000)
|
||||
#else
|
||||
@@ -282,8 +283,9 @@ mDNSexport DNameListElem *AutoBrowseDomains; // List created from those local-o
|
||||
|
||||
mDNSlocal void FatalError(char *errmsg)
|
||||
{
|
||||
char* ptr = NULL;
|
||||
LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno));
|
||||
*(long*)0 = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does
|
||||
*ptr = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does
|
||||
abort(); // On platforms where writing to zero doesn't generate an exception, abort instead
|
||||
}
|
||||
|
||||
@@ -2334,7 +2336,7 @@ mDNSlocal mStatus handle_resolve_request(request_state *request)
|
||||
#endif
|
||||
|
||||
// ask the questions
|
||||
LogOperation("%3d: DNSServiceResolve(%##s) START", request->sd, request->u.resolve.qsrv.qname.c);
|
||||
LogOperation("%3d: DNSServiceResolve(%X %d %##s) START", request->sd, flags, interfaceIndex, request->u.resolve.qsrv.qname.c);
|
||||
err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv);
|
||||
if (!err)
|
||||
{
|
||||
@@ -4299,7 +4301,7 @@ mDNSexport void udsserver_info(mDNS *const m)
|
||||
const char *ifname;
|
||||
mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID;
|
||||
if (!InterfaceID && cr->resrec.rDNSServer)
|
||||
InterfaceID = cr->resrec.rDNSServer->interface;
|
||||
InterfaceID = cr->resrec.rDNSServer->interface1;
|
||||
ifname = InterfaceNameForID(m, InterfaceID);
|
||||
CacheUsed++;
|
||||
if (cr->CRActiveQuestion) CacheActive++;
|
||||
@@ -4707,7 +4709,7 @@ struct CompileTimeAssertionChecks_uds_daemon
|
||||
// Check our structures are reasonable sizes. Including overly-large buffers, or embedding
|
||||
// other overly-large structures instead of having a pointer to them, can inadvertently
|
||||
// cause structure sizes (and therefore memory usage) to balloon unreasonably.
|
||||
char sizecheck_request_state [(sizeof(request_state) <= 1784) ? 1 : -1];
|
||||
char sizecheck_request_state [(sizeof(request_state) <= 2000) ? 1 : -1];
|
||||
char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1];
|
||||
char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1];
|
||||
char sizecheck_browser_t [(sizeof(browser_t) <= 1050) ? 1 : -1];
|
||||
|
@@ -5,6 +5,7 @@ SUBDIRS = qtpromaker \
|
||||
|
||||
win32 {
|
||||
SUBDIRS += qtcdebugger
|
||||
SUBDIRS += mdnssd
|
||||
# win64interrupt only make sense for 64bit builds
|
||||
ENV_CPU=$$(CPU)
|
||||
ENV_LIBPATH=$$(LIBPATH)
|
||||
|
Reference in New Issue
Block a user