544 lines
17 KiB
C++
544 lines
17 KiB
C++
/*++
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
emaccept.cpp
|
||
|
||
Abstract:
|
||
|
||
Contains all the event manager routines which
|
||
manage the overlapped accept operations.
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
History:
|
||
|
||
1. created
|
||
Ajay Chitturi (ajaych) 12-Jun-1998
|
||
|
||
--*/
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Include files //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
#include "stdafx.h"
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Constants //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Global Variables //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Externally defined identifiers //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Overlapped accept functions //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
HRESULT
|
||
EventMgrCreateAcceptContext(
|
||
IN OVERLAPPED_PROCESSOR *pOvProcessor,
|
||
IN OUT struct sockaddr_in *pBindAddress,
|
||
OUT PAcceptContext *ppAcceptCtxt
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function creates a socket, binds it to bindAddress and
|
||
issues a listen. It creates the Accept I/O Context and returns
|
||
it to the caller.
|
||
|
||
Arguments:
|
||
|
||
pOvProcessor - pointer to the overlapped processor object.
|
||
Once the accept completes the callback of this object is
|
||
called.
|
||
|
||
pBindAddress - pointer to the address to listen on.
|
||
|
||
ppAcceptCtxt - A new Accept I/O context is allocated initialized
|
||
and returned through this OUT parameter.
|
||
|
||
Return Values:
|
||
|
||
Returns S_OK on success, E_OUTOFMEMORY if the memory allocator fails
|
||
or E_FAIL if any of the Winsock functions fail.
|
||
|
||
--*/
|
||
|
||
{
|
||
SOCKET listenSock;
|
||
int Error;
|
||
HRESULT Result;
|
||
BOOL KeepaliveOption;
|
||
PAcceptContext pAcceptCtxt;
|
||
*ppAcceptCtxt = NULL;
|
||
|
||
// Create an overlapped socket
|
||
listenSock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||
NULL, 0,
|
||
WSA_FLAG_OVERLAPPED);
|
||
|
||
if (listenSock == INVALID_SOCKET)
|
||
{
|
||
Error = WSAGetLastError ();
|
||
DebugF(_T("H323: 0x%x error creating listener socket error: %d pOvProcessor: %p.\n"),
|
||
&pOvProcessor -> GetCallBridge (),
|
||
Error, pOvProcessor);
|
||
return HRESULT_FROM_WIN32 (Error);
|
||
}
|
||
|
||
// Bind the socket to the listen address
|
||
if (bind(listenSock,
|
||
(struct sockaddr *)pBindAddress,
|
||
sizeof(struct sockaddr_in)) == SOCKET_ERROR)
|
||
{
|
||
Error = WSAGetLastError ();
|
||
DebugF (_T("H323: 0x%x bind() failed error: %d.\n"),
|
||
&pOvProcessor -> GetCallBridge (),
|
||
Error);
|
||
closesocket(listenSock);
|
||
listenSock = INVALID_SOCKET;
|
||
return HRESULT_FROM_WIN32 (Error);
|
||
}
|
||
|
||
// Set keepalive on the socket
|
||
KeepaliveOption = TRUE;
|
||
if (SOCKET_ERROR == setsockopt(listenSock, SOL_SOCKET,
|
||
SO_KEEPALIVE, (PCHAR) &KeepaliveOption, sizeof (KeepaliveOption)))
|
||
{
|
||
Error = WSAGetLastError ();
|
||
DebugF (_T("H323: 0x%x failed to set keepalive on listen socket. Error %d.\n"),
|
||
&pOvProcessor -> GetCallBridge (),
|
||
Error);
|
||
closesocket(listenSock);
|
||
listenSock = INVALID_SOCKET;
|
||
return HRESULT_FROM_WIN32 (Error);
|
||
}
|
||
|
||
// Bind the socket handle to the I/O completion port
|
||
if (EventMgrBindIoHandle(listenSock) != S_OK)
|
||
{
|
||
DebugF (_T("H323: 0x%x binding socket:%d to IOCP failed.\n"),
|
||
&pOvProcessor -> GetCallBridge (),
|
||
listenSock);
|
||
closesocket(listenSock);
|
||
listenSock = INVALID_SOCKET;
|
||
return E_FAIL;
|
||
}
|
||
|
||
if (listen(listenSock, MAX_LISTEN_BACKLOG) == SOCKET_ERROR)
|
||
{
|
||
Error = WSAGetLastError ();
|
||
DebugF (_T("H323: 0x%x listen() failed: 0x%x pOvProcessor; %p.\n"),
|
||
&pOvProcessor -> GetCallBridge (),
|
||
Error, pOvProcessor);
|
||
closesocket(listenSock);
|
||
listenSock = INVALID_SOCKET;
|
||
return HRESULT_FROM_WIN32 (Error);
|
||
}
|
||
|
||
// Allocate memory from a private heap for accept contexts
|
||
pAcceptCtxt = (PAcceptContext) HeapAlloc (GetProcessHeap (),
|
||
0, // No Flags
|
||
sizeof(AcceptContext));
|
||
if (!pAcceptCtxt)
|
||
{
|
||
DebugF (_T("H323: 0x%x could not allocate Accept context.\n"),
|
||
&pOvProcessor -> GetCallBridge ());
|
||
closesocket(listenSock);
|
||
listenSock = INVALID_SOCKET;
|
||
|
||
return E_OUTOFMEMORY;
|
||
}
|
||
|
||
memset(pAcceptCtxt, 0, sizeof(AcceptContext));
|
||
pAcceptCtxt->ioCtxt.reqType = EMGR_OV_IO_REQ_ACCEPT;
|
||
pAcceptCtxt->ioCtxt.pOvProcessor = pOvProcessor;
|
||
pAcceptCtxt->listenSock = listenSock;
|
||
pAcceptCtxt->acceptSock = INVALID_SOCKET;
|
||
|
||
*ppAcceptCtxt = pAcceptCtxt;
|
||
return S_OK;
|
||
} // EventMgrCreateAcceptContext
|
||
|
||
|
||
void
|
||
EventMgrFreeAcceptContext (
|
||
PAcceptContext pAcceptCtxt
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function frees the pAcceptCtxt.
|
||
OvProcessor is owned by the Call Bridge Machine and
|
||
so we do not free it.
|
||
|
||
Arguments:
|
||
|
||
pAcceptCtxt - pointer to the AcceptCtxt to be freed.
|
||
|
||
Return Values:
|
||
|
||
This function has not return value.
|
||
|
||
--*/
|
||
|
||
{
|
||
if (pAcceptCtxt->acceptSock != INVALID_SOCKET)
|
||
{
|
||
closesocket(pAcceptCtxt->acceptSock);
|
||
pAcceptCtxt -> acceptSock = INVALID_SOCKET;
|
||
}
|
||
|
||
HeapFree (GetProcessHeap (),
|
||
0, // no flags
|
||
pAcceptCtxt);
|
||
} // EventMgrFreeAcceptContext
|
||
|
||
|
||
HRESULT
|
||
EventMgrIssueAcceptHelperFn(
|
||
PAcceptContext pAcceptCtxt
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function issues an Asynchronous overlapped accept using
|
||
AcceptEx(). The accept socket is created and stored in the
|
||
Accept context before making the call to AcceptEx().
|
||
|
||
In case of an error, the caller needs to free pAcceptCtxt.
|
||
|
||
Arguments:
|
||
|
||
pAcceptCtxt - pointer to the Accept I/O context.
|
||
|
||
Return Values:
|
||
|
||
This function returns S_OK in case of success or E_FAIL
|
||
in case of an error.
|
||
CODEWORK: Need to convert Winsock errors to HRESULT and
|
||
return them instead.
|
||
|
||
--*/
|
||
|
||
{
|
||
SOCKET acceptSock;
|
||
DWORD lastError, bytesRead;
|
||
HRESULT Result;
|
||
BOOL KeepaliveOption;
|
||
|
||
_ASSERTE(pAcceptCtxt);
|
||
|
||
// create an overlapped socket
|
||
acceptSock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||
NULL, 0,
|
||
WSA_FLAG_OVERLAPPED);
|
||
|
||
if (acceptSock == INVALID_SOCKET)
|
||
{
|
||
DebugF (_T("H323: 0x%x error creating accept socket: %d.\n"),
|
||
&pAcceptCtxt -> ioCtxt.pOvProcessor -> GetCallBridge (),
|
||
WSAGetLastError());
|
||
return E_FAIL;
|
||
}
|
||
|
||
pAcceptCtxt->acceptSock = acceptSock;
|
||
|
||
// Bind the socket handle to the I/O completion port
|
||
if (EventMgrBindIoHandle(acceptSock) != S_OK)
|
||
{
|
||
DebugF (_T("H323: 0x%x binding socket:%d to IOCP failed.\n"),
|
||
&pAcceptCtxt -> ioCtxt.pOvProcessor -> GetCallBridge (),
|
||
acceptSock);
|
||
return E_FAIL;
|
||
}
|
||
|
||
memset(&pAcceptCtxt->ioCtxt.ov, 0, sizeof(OVERLAPPED));
|
||
|
||
// Set keepalive on the socket
|
||
KeepaliveOption = TRUE;
|
||
if (SOCKET_ERROR == setsockopt (acceptSock, SOL_SOCKET,
|
||
SO_KEEPALIVE, (PCHAR) &KeepaliveOption, sizeof (KeepaliveOption)))
|
||
{
|
||
DebugF (_T("H323: 0x%x failed to set keepalive on accept socket. Error %d.\n"),
|
||
&pAcceptCtxt -> ioCtxt.pOvProcessor -> GetCallBridge (),
|
||
WSAGetLastError());
|
||
return E_FAIL;
|
||
}
|
||
|
||
pAcceptCtxt->ioCtxt.pOvProcessor->GetCallBridge().AddRef();
|
||
|
||
// Issue overlapped accept
|
||
if (!AcceptEx(pAcceptCtxt->listenSock,
|
||
acceptSock,
|
||
pAcceptCtxt->addrBuf,
|
||
0, // Read nothing from socket
|
||
sizeof(struct sockaddr_in) + 16,
|
||
sizeof(struct sockaddr_in) + 16,
|
||
&bytesRead,
|
||
&pAcceptCtxt->ioCtxt.ov))
|
||
{
|
||
lastError = WSAGetLastError();
|
||
if (lastError != ERROR_IO_PENDING)
|
||
{
|
||
pAcceptCtxt->ioCtxt.pOvProcessor->GetCallBridge().Release();
|
||
|
||
// This means the overlapped AcceptEx() failed
|
||
DebugF(_T("H323: 0x%x AcceptEx() failed error: %d listenSock: %d acceptSock: %d.\n"),
|
||
&pAcceptCtxt -> ioCtxt.pOvProcessor -> GetCallBridge (),
|
||
lastError, pAcceptCtxt->listenSock,
|
||
pAcceptCtxt->acceptSock);
|
||
return E_FAIL;
|
||
}
|
||
}
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
HRESULT
|
||
EventMgrIssueAccept(
|
||
IN DWORD bindIPAddress,
|
||
IN OVERLAPPED_PROCESSOR &rOvProcessor,
|
||
OUT WORD& rBindPort,
|
||
OUT SOCKET& rListenSock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is exported to the Call Bridge machine for
|
||
making an asynchronous accept request for H.245 connections.
|
||
Once the accept is completed, the accept callback function on
|
||
rOvProcessor is called.
|
||
|
||
This function calls bind() with on IP address "bindIPAddress"
|
||
and port 0. Winsock allocates a free port which is got using
|
||
getsockname(). This port is returned using the OUT param
|
||
rBindPort.
|
||
|
||
This function also returns the listen socket using the OUT
|
||
param rListenSock. The Call Bridge machine can use this
|
||
socket to cancel the asynchronous Accept() request.
|
||
Once this function succeeds only the Call bridge frees the
|
||
listen socket.
|
||
|
||
Arguments:
|
||
|
||
bindIPAddress - This is the IP address to listen on.
|
||
This is in host byte order.
|
||
|
||
rOvProcessor - A reference to the Overlapped processor. This
|
||
is stored in the Accept I/O context. Once the accept
|
||
completes the AcceptCallback() on this object is called.
|
||
|
||
rBindPort - The port on which the listen is issued is returned
|
||
through this OUT param. This function calls bind() with on
|
||
IP address "bindIPAddress" and port 0. Winsock allocates a
|
||
free port which is got using getsockname(). This port is
|
||
returned using the OUT param rBindPort.
|
||
The port returned is in host byte order.
|
||
|
||
rListenSock - The listening socket is returned through this OUT
|
||
param. The Call Bridge machine can use this socket to cancel
|
||
the asynchronous Accept() request. Once this function succeeds
|
||
only the Call bridge frees the listen socket.
|
||
|
||
Return Values:
|
||
|
||
This function returns S_OK in case of success or E_FAIL
|
||
in case of a failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAcceptContext pAcceptCtxt;
|
||
struct sockaddr_in bindAddress;
|
||
HRESULT hRes;
|
||
int bindAddressLen = sizeof(struct sockaddr_in);
|
||
|
||
memset(&bindAddress, 0, sizeof(bindAddress));
|
||
|
||
bindAddress.sin_family = AF_INET;
|
||
bindAddress.sin_addr.s_addr = htonl(bindIPAddress);
|
||
bindAddress.sin_port = htons(0);
|
||
|
||
hRes = EventMgrCreateAcceptContext(&rOvProcessor,
|
||
&bindAddress, &pAcceptCtxt);
|
||
if (hRes != S_OK)
|
||
return hRes;
|
||
|
||
if (!pAcceptCtxt)
|
||
return E_FAIL;
|
||
|
||
// Get the port
|
||
if (getsockname(pAcceptCtxt->listenSock,
|
||
(struct sockaddr *)&bindAddress,
|
||
&bindAddressLen))
|
||
{
|
||
closesocket(pAcceptCtxt->listenSock);
|
||
pAcceptCtxt->listenSock = INVALID_SOCKET;
|
||
|
||
EventMgrFreeAcceptContext(pAcceptCtxt);
|
||
return E_FAIL;
|
||
}
|
||
|
||
rBindPort = ntohs(bindAddress.sin_port);
|
||
|
||
hRes = EventMgrIssueAcceptHelperFn(pAcceptCtxt);
|
||
|
||
if (hRes != S_OK)
|
||
{
|
||
closesocket(pAcceptCtxt->listenSock);
|
||
pAcceptCtxt->listenSock = INVALID_SOCKET;
|
||
|
||
EventMgrFreeAcceptContext(pAcceptCtxt);
|
||
return hRes;
|
||
}
|
||
|
||
rListenSock = pAcceptCtxt->listenSock;
|
||
return S_OK;
|
||
} // EventMgrIssueAccept
|
||
|
||
|
||
void
|
||
HandleAcceptCompletion(
|
||
PAcceptContext pAcceptCtxt,
|
||
DWORD status
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called by the event loop when an accept I/O completes.
|
||
The Call Bridge Machine's accept callback function is called for
|
||
H.245 connections.
|
||
|
||
This function always frees pAcceptCtxt if another accept is not issued.
|
||
|
||
Arguments:
|
||
|
||
pAcceptCtxt - The Accept I/O context. This contains the overlapped
|
||
context on which the accept callback is called in the case of
|
||
H.245 connections and the listen and accept sockets.
|
||
|
||
status - This conveys the WIN32 error status.
|
||
|
||
Return Values:
|
||
|
||
This function does not return any error code. In case of an error,
|
||
the call bridge machine is notified about the error in the callback.
|
||
|
||
--*/
|
||
|
||
{
|
||
int locallen = sizeof(struct sockaddr_in);
|
||
int remotelen = sizeof(struct sockaddr_in);
|
||
struct sockaddr_in *pLocalAddr;
|
||
struct sockaddr_in *pRemoteAddr;
|
||
|
||
// If H.245 call the accept callback on overlapped processor
|
||
// and free the accept I/O context.
|
||
// The listening socket is closed by the Call bridge machine.
|
||
if (status == NO_ERROR)
|
||
{
|
||
if (setsockopt(pAcceptCtxt->acceptSock,
|
||
SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
|
||
(char*)(&pAcceptCtxt->listenSock),
|
||
sizeof(SOCKET)) == SOCKET_ERROR)
|
||
{
|
||
DebugF (_T("H323: 0x%x setsockopt SO_UPDATE_ACCEPT_CONTEXT failed acceptSock: %d listenSock: %d err: %d.\n"),
|
||
&pAcceptCtxt -> ioCtxt.pOvProcessor -> GetCallBridge (),
|
||
pAcceptCtxt->acceptSock,
|
||
pAcceptCtxt->listenSock,
|
||
WSAGetLastError());
|
||
|
||
// make callback conveying the error
|
||
|
||
SOCKADDR_IN LocalAddress;
|
||
SOCKADDR_IN RemoteAddress;
|
||
|
||
ZeroMemory (&LocalAddress, sizeof (SOCKADDR_IN));
|
||
ZeroMemory (&RemoteAddress, sizeof (SOCKADDR_IN));
|
||
|
||
pAcceptCtxt->ioCtxt.pOvProcessor->AcceptCallback(
|
||
WSAGetLastError(),
|
||
INVALID_SOCKET,
|
||
&LocalAddress,
|
||
&RemoteAddress);
|
||
|
||
} else {
|
||
|
||
// This function does not return anything
|
||
GetAcceptExSockaddrs(pAcceptCtxt->addrBuf, 0,
|
||
sizeof(struct sockaddr_in) + 16,
|
||
sizeof(struct sockaddr_in) + 16,
|
||
(struct sockaddr**)&pLocalAddr,
|
||
&locallen,
|
||
(struct sockaddr**)&pRemoteAddr,
|
||
&remotelen);
|
||
|
||
// make the callback
|
||
pAcceptCtxt->ioCtxt.pOvProcessor->AcceptCallback(
|
||
S_OK,
|
||
pAcceptCtxt->acceptSock,
|
||
pLocalAddr,
|
||
pRemoteAddr);
|
||
|
||
// ownership of pAcceptCtxt -> acceptSock has been transferred,
|
||
// so we need to make sure the acceptSock won't be used
|
||
pAcceptCtxt -> acceptSock = INVALID_SOCKET;
|
||
}
|
||
|
||
} // if (status == NO_ERROR)
|
||
|
||
else
|
||
{
|
||
DebugF (_T("H245: 0x%x error %d on accept callback.\n"),
|
||
&pAcceptCtxt -> ioCtxt.pOvProcessor -> GetCallBridge (),
|
||
status);
|
||
|
||
|
||
SOCKADDR_IN LocalAddress;
|
||
SOCKADDR_IN RemoteAddress;
|
||
|
||
ZeroMemory (&LocalAddress, sizeof (SOCKADDR_IN));
|
||
ZeroMemory (&RemoteAddress, sizeof (SOCKADDR_IN));
|
||
|
||
// make callback conveying the error
|
||
pAcceptCtxt->ioCtxt.pOvProcessor->AcceptCallback(
|
||
HRESULT_FROM_WIN32_ERROR_CODE(status),
|
||
INVALID_SOCKET,
|
||
&LocalAddress,
|
||
&RemoteAddress);
|
||
|
||
}
|
||
|
||
EventMgrFreeAcceptContext (pAcceptCtxt);
|
||
} // HandleAcceptCompletion(
|