#include "stdafx.h" SYNC_COUNTER Q931SyncCounter; ASYNC_ACCEPT Q931AsyncAccept; SOCKADDR_IN Q931ListenSocketAddress; HANDLE Q931LoopbackRedirectHandle; static HRESULT Q931AsyncAcceptFunctionInternal ( IN PVOID Context, IN SOCKET Socket, IN SOCKADDR_IN * LocalAddress, IN SOCKADDR_IN * RemoteAddress ); void Q931AsyncAcceptFunction ( IN PVOID Context, IN SOCKET Socket, IN SOCKADDR_IN * LocalAddress, IN SOCKADDR_IN * RemoteAddress ) { HRESULT Result; Result = Q931AsyncAcceptFunctionInternal ( Context, Socket, LocalAddress, RemoteAddress ); if (S_OK != Result) { if (INVALID_SOCKET != Socket) { closesocket (Socket); Socket = INVALID_SOCKET; } } } static HRESULT Q931AsyncAcceptFunctionInternal ( IN PVOID Context, IN SOCKET Socket, IN SOCKADDR_IN * LocalAddress, IN SOCKADDR_IN * RemoteAddress ) { CALL_BRIDGE * CallBridge; HRESULT Result; NAT_KEY_SESSION_MAPPING_EX_INFORMATION RedirectInformation; ULONG RedirectInformationLength; ULONG Error; DWORD BestInterfaceAddress; DebugF (_T("Q931: ----------------------------------------------------------------------\n")); #if DBG ExposeTimingWindow (); #endif // a new Q.931 connection has been accepted from the network. // first, we determine the original addresses of the transport connection. // if the connection was redirected to our socket (due to NAT), // then the query of the NAT redirect table will yield the original transport addresses. // if an errant client has connected to our service, well, we really didn't // intend for that to happen, so we just immediately close the socket. assert (NatHandle); RedirectInformationLength = sizeof (RedirectInformation); Result = NatLookupAndQueryInformationSessionMapping ( NatHandle, IPPROTO_TCP, LocalAddress -> sin_addr.s_addr, LocalAddress -> sin_port, RemoteAddress -> sin_addr.s_addr, RemoteAddress -> sin_port, &RedirectInformation, &RedirectInformationLength, NatKeySessionMappingExInformation); if (STATUS_SUCCESS != STATUS_SUCCESS) { DebugError (Result, _T("Q931: New connection was accepted, but it is not in the NAT redirect table -- connection will be rejected.\n")); return Result; } Error = GetBestInterfaceAddress (ntohl (RedirectInformation.DestinationAddress), &BestInterfaceAddress); if (ERROR_SUCCESS != Error) { if (WSAEHOSTUNREACH == Error) { Error = RasAutoDialSharedConnection (); if (ERROR_SUCCESS != Error) { DebugF (_T("Q931: RasAutoDialSharedConnection failed. Error=%d\n"), Error); } } else { DebugError (Error, _T("LDAP: Failed to get interface address for the destination.\n")); return HRESULT_FROM_WIN32 (Error); } } // based on the source address of the socket, we decide whether the connection // came from an internal or external client. this will govern later decisions // on how the call is routed. #if DBG BOOL IsPrivateOrLocalSource; BOOL IsPublicDestination; Result = ::IsPrivateAddress (ntohl (RedirectInformation.SourceAddress), &IsPrivateOrLocalSource); if (S_OK != Result) { return Result; } IsPrivateOrLocalSource = IsPrivateOrLocalSource || ::NhIsLocalAddress (RedirectInformation.SourceAddress); Result = ::IsPublicAddress (ntohl (RedirectInformation.DestinationAddress), &IsPublicDestination); if (S_OK != Result) { return Result; } if (::NhIsLocalAddress (RedirectInformation.SourceAddress) && ::NhIsLocalAddress (RedirectInformation.DestinationAddress)) { Debug (_T("Q931: New LOCAL connection.\n")); } else { if (IsPrivateOrLocalSource && IsPublicDestination) { Debug (_T("Q931: New OUTBOUND connection.\n")); } else { Debug (_T("Q931: New INBOUND connection.\n")); } } #endif // DBG DebugF (_T("Q931: Connection redirected: (%08X:%04X -> %08X:%04X) => (%08X:%04X -> %08X:%04X).\n"), ntohl (RedirectInformation.SourceAddress), ntohs (RedirectInformation.SourcePort), ntohl (RedirectInformation.DestinationAddress), ntohs (RedirectInformation.DestinationPort), ntohl (RedirectInformation.NewSourceAddress), ntohs (RedirectInformation.NewSourcePort), ntohl (RedirectInformation.NewDestinationAddress), ntohs (RedirectInformation.NewDestinationPort)); CallBridge = new CALL_BRIDGE (&RedirectInformation); if (!CallBridge) { DebugF (_T("Q931: Failed to allocate CALL_BRIDGE.\n")); return E_OUTOFMEMORY; } CallBridge -> AddRef (); // Add the call-bridge to the list. Doing so makes an additional reference // to the object, which is retained until the object is destroyed by calling // RemoveCallBridge. if (CallBridgeList.InsertCallBridge (CallBridge) == S_OK) { // should we check the local address or the caller's address ? // The problem is that if someone tries to connect to the // external IP address from inside, they will still probably succeed Result = CallBridge -> Initialize ( Socket, LocalAddress, RemoteAddress, &RedirectInformation ); if (Result != S_OK) { CallBridge -> TerminateExternal (); DebugF (_T("Q931: 0x%x accepted new client, but failed to initialize.\n"), CallBridge); // Probably there was something wrong with just this // Init failure. Continue to accept more Q.931 connections. } } CallBridge -> Release (); return Result; } HRESULT Q931CreateBindSocket ( void ) { HRESULT Result; SOCKADDR_IN SocketAddress; SocketAddress.sin_family = AF_INET; SocketAddress.sin_addr.s_addr = htonl (INADDR_LOOPBACK); SocketAddress.sin_port = htons (0); // request dynamic port Result = Q931AsyncAccept.StartIo ( &SocketAddress, Q931AsyncAcceptFunction, NULL ); if (Result != S_OK) { DebugError (Result, _T("Q931: Failed to create and bind socket.\n")); return Result; } DebugF (_T("Q931: Asynchronous Accept started.\n")); Result = Q931AsyncAccept.GetListenSocketAddress (&Q931ListenSocketAddress); if (Result != S_OK) { DebugError (Result, _T("Q931: Failed to get listen socket address.\n")); return Result; } return S_OK; } void Q931CloseSocket ( void ) { ZeroMemory ((PVOID)&Q931ListenSocketAddress, sizeof (SOCKADDR_IN)); Q931AsyncAccept.StopWait (); } HRESULT Q931StartLoopbackRedirect ( void ) { NTSTATUS Status; Status = NatCreateDynamicAdapterRestrictedPortRedirect ( NatRedirectFlagLoopback | NatRedirectFlagSendOnly, IPPROTO_TCP, htons (Q931_TSAP_IP_TCP), Q931ListenSocketAddress.sin_addr.s_addr, Q931ListenSocketAddress.sin_port, ::NhMapAddressToAdapter (htonl (INADDR_LOOPBACK)), MAX_LISTEN_BACKLOG, &Q931LoopbackRedirectHandle); if (Status != STATUS_SUCCESS) { Q931LoopbackRedirectHandle = NULL; DebugError (Status, _T("Q931: Failed to create local dynamic redirect.\n")); return (HRESULT)Status; } DebugF (_T("Q931: Connections traversing loopback interface to port %04X will be redirected to %08X:%04X.\n"), Q931_TSAP_IP_TCP, SOCKADDR_IN_PRINTF (&Q931ListenSocketAddress)); return (HRESULT) Status; } void Q931StopLoopbackRedirect ( void ) { if (Q931LoopbackRedirectHandle) { NatCancelDynamicRedirect (Q931LoopbackRedirectHandle); Q931LoopbackRedirectHandle = NULL; } }