Windows2003-3790/inetcore/wininet/common/icsocket.cxx
2020-09-30 16:53:55 +02:00

5942 lines
145 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1995-1997 Microsoft Corporation
Module Name:
icsocket.cxx
Abstract:
Contains sockets functions and ICSocket methods
Contents:
ContainingICSocket
MapNetAddressToName
ICSocket::ICSocket
ICSocket::~ICSocket
ICSocket::Destroy
ICSocket::Reference
ICSocket::Dereference
ICSocket::EnableSocks
ICSocket::Connect
CFsm_SocketConnect::RunSM
ICSocket::Connect_Start
ICSocket::Connect_Continue
ICSocket::Connect_Error
ICSocket::Connect_Finish
ICSocket::SocksConnect
ICSocket::Disconnect
ICSocket::Close
ICSocket::Abort
ICSocket::Shutdown
ICSocket::IsReset
ICSocket::SetTimeout
ICSocket::SetLinger
ICSocket::SetNonBlockingMode
ICSocket::GetBufferLength(SOCKET_BUFFER_ID)
ICSocket::GetBufferLength(SOCKET_BUFFER_ID, LPDWORD)
ICSocket::SetBufferLength
ICSocket::SetSendCoalescing
SetSourcePort
ICSocket::Send
CFsm_SocketSend::RunSM
ICSocket::Send_Start
ICSocket::SendTo
ICSocket::Receive
CFsm_SocketReceive::RunSM
ICSocket::Receive_Start
ICSocket::Receive_Continue
ICSocket::AllocateQueryBuffer
//ICSocket::FreeQueryBuffer
//ICSocket::ReceiveFrom
ICSocket::DataAvailable
//ICSocket::DataAvailable2
ICSocket::WaitForReceive
//ICSocket::GetBytesAvailable
ICSocket::CreateSocket
ICSocket::GetSockName
ICSocket::GetPeerName
ICSocket::Listen
ICSocket::DirectConnect
ICSocket::SelectAccept
Author:
Richard L Firth (rfirth) 08-Apr-1997
Environment:
Win32 user mode
Revision History:
08-Apr-1997 rfirth
Created from ixport.cxx
--*/
#include <wininetp.h>
#include <perfdiag.hxx>
#include "autodial.h"
//
// private prototypes
//
//
// functions
//
#if INET_DEBUG
PRIVATE LPSTR MapFamily(int family) {
switch (family) {
case AF_UNSPEC: return "AF_UNSPEC";
case AF_UNIX: return "AF_UNIX";
case AF_INET: return "AF_INET";
case AF_IMPLINK: return "AF_IMPLINK";
case AF_PUP: return "AF_PUP";
case AF_CHAOS: return "AF_CHAOS";
case AF_IPX: return "AF_IPX";
case AF_OSI: return "AF_OSI";
case AF_ECMA: return "AF_ECMA";
case AF_DATAKIT: return "AF_DATAKIT";
case AF_CCITT: return "AF_CCITT";
case AF_SNA: return "AF_SNA";
case AF_DECnet: return "AF_DECnet";
case AF_DLI: return "AF_DLI";
case AF_LAT: return "AF_LAT";
case AF_HYLINK: return "AF_HYLINK";
case AF_APPLETALK: return "AF_APPLETALK";
case AF_NETBIOS: return "AF_NETBIOS";
#if defined(AF_VOICEVIEW)
case AF_VOICEVIEW: return "AF_VOICEVIEW";
#endif /* AF_VOICEVIEW */
#if defined(AF_FIREFOX)
case AF_FIREFOX: return "AF_FIREFOX";
#endif /* AF_FIREFOX */
#if defined(AF_UNKNOWN1)
case AF_UNKNOWN1: return "AF_UNKNOWN1";
#endif /* AF_UNKNOWN1 */
#if defined(AF_BAN)
case AF_BAN: return "AF_BAN";
#endif /* AF_BAN */
#if defined(AF_INET6)
case AF_INET6: return "AF_INET6";
#endif /* AF_INET6 */
}
return "?";
}
PRIVATE LPSTR MapSock(int sock) {
switch (sock) {
case SOCK_STREAM: return "SOCK_STREAM";
case SOCK_DGRAM: return "SOCK_DGRAM";
case SOCK_RAW: return "SOCK_RAW";
case SOCK_RDM: return "SOCK_RDM";
case SOCK_SEQPACKET: return "SOCK_SEQPACKET";
}
return "?";
}
PRIVATE LPSTR MapProto(int proto) {
switch (proto) {
case IPPROTO_IP: return "IPPROTO_IP";
case IPPROTO_ICMP: return "IPPROTO_ICMP";
case IPPROTO_IGMP: return "IPPROTO_IGMP";
case IPPROTO_GGP: return "IPPROTO_GGP";
case IPPROTO_TCP: return "IPPROTO_TCP";
case IPPROTO_PUP: return "IPPROTO_PUP";
case IPPROTO_UDP: return "IPPROTO_UDP";
case IPPROTO_IDP: return "IPPROTO_IDP";
case IPPROTO_ND: return "IPPROTO_ND";
}
return "?";
}
#endif // INET_DEBUG
ICSocket *
ContainingICSocket(
LPVOID lpAddress
)
/*++
Routine Description:
Returns address of start of ICSocket (i.e. vtable) given address of list
Arguments:
lpAddress - address of m_List part of ICSocket
Return Value:
ICSocket * - address of start of ICSocket object (also ICSecureSocket)
--*/
{
return CONTAINING_RECORD(lpAddress, ICSocket, m_List);
}
//
// ICSocket methods
//
ICSocket::ICSocket(
VOID
)
/*++
Routine Description:
ICSocket constructor
Arguments:
None.
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_OBJECTS,
None,
"ICSocket::ICSocket",
"{%#x}",
this
));
SIGN_ICSOCKET();
m_List.Flink = NULL;
m_List.Blink = NULL;
m_dwTimeout = 0;
m_fTimeoutWraps = 0;
m_Socket = INVALID_SOCKET;
m_dwFlags = 0;
m_bAborted = FALSE;
m_SocksAddress = 0;
m_SocksPort = 0;
m_ReferenceCount = 1;
m_fExemptConnLImit = FALSE;
DEBUG_LEAVE(0);
}
ICSocket::~ICSocket()
/*++
Routine Description:
ICSocket destructor. Virtual function
Arguments:
None.
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_OBJECTS,
None,
"ICSocket::~ICSocket",
"{%#x [sock=%#x, port=%d, ref=%d]}",
this,
GetSocket(),
GetSourcePort(),
ReferenceCount()
));
CHECK_ICSOCKET();
INET_ASSERT(!IsOnList());
INET_ASSERT(m_ReferenceCount == 0);
if (IsOpen()) {
Close();
}
DEBUG_LEAVE(0);
}
VOID
ICSocket::Destroy(
VOID
)
/*++
Routine Description:
description-of-function.
Arguments:
None.
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_OBJECTS,
None,
"ICSocket::Destroy",
"{%#x [%#x/%d]}",
this,
GetSocket(),
GetSourcePort()
));
INET_ASSERT(ReferenceCount() == 1);
m_ReferenceCount = 0;
delete this;
DEBUG_LEAVE(0);
}
VOID
ICSocket::Reference(
VOID
)
/*++
Routine Description:
Just increases the reference count
Arguments:
None.
Return Value:
None.
--*/
{
CHECK_ICSOCKET();
InterlockedIncrement(&m_ReferenceCount);
}
BOOL
ICSocket::Dereference(
VOID
)
/*++
Routine Description:
Reduces the reference count. If it goes to zero, the object is deleted
Arguments:
None.
Return Value:
BOOL
TRUE - object deleted
FALSE - object still alive
--*/
{
CHECK_ICSOCKET();
if (InterlockedDecrement(&m_ReferenceCount) == 0) {
INET_ASSERT(m_ReferenceCount == 0);
delete this;
return TRUE;
}
return FALSE;
}
PRIVATE
DWORD
ICSocket::EnableSocks(
IN LPSTR lpSocksHost,
IN INTERNET_PORT ipSocksPort
)
/*++
Routine Description:
Set SOCKS gateway IP address and port in this socket object
Arguments:
lpSocksHost - IP address or host name of SOCKS host
ipSocksPort - port address of SOCKS host
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INTERNET_NAME_NOT_RESOLVED
failed to resolve SOCKS host name
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::EnableSocks",
"{%#x/%d} %q, %d",
GetSocket(),
GetSourcePort(),
lpSocksHost,
ipSocksPort
));
DWORD error = ERROR_SUCCESS;
m_SocksPort = ipSocksPort;
m_SocksAddress = _I_inet_addr(lpSocksHost);
if (m_SocksAddress == INADDR_NONE) { // 0xffffffff
LPHOSTENT lpHostent = _I_gethostbyname(lpSocksHost);
if (lpHostent != NULL) {
m_SocksAddress = **(LPDWORD*)&lpHostent->h_addr_list[0];
} else {
m_SocksAddress = 0;
error = ERROR_INTERNET_NAME_NOT_RESOLVED;
}
}
DEBUG_PRINT(SOCKETS,
INFO,
("SOCKS address = %d.%d.%d.%d:%d\n",
((BYTE*)&m_SocksAddress)[0] & 0xff,
((BYTE*)&m_SocksAddress)[1] & 0xff,
((BYTE*)&m_SocksAddress)[2] & 0xff,
((BYTE*)&m_SocksAddress)[3] & 0xff,
m_SocksPort
));
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::SocketConnect(
IN LONG Timeout,
IN INT Retries,
IN DWORD dwFlags,
IN CServerInfo *pServerInfo
)
/*++
Routine Description:
Initiate connection with server
Arguments:
Timeout - maximum amount of time (mSec) to wait for connection
Retries - maximum number of attempts to connect
dwFlags - flags controlling request
pServerInfo - Server Info to connect with
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure - ERROR_NOT_ENOUGH_MEMORY
Couldn't create FSM
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::SocketConnect",
"{%#x [%#x]} %d, %d, %#x, %x",
this,
m_Socket,
Timeout,
Retries,
dwFlags,
pServerInfo
));
DWORD error;
CFsm_SocketConnect * pFsm;
pFsm = new CFsm_SocketConnect(Timeout, Retries, dwFlags, this);
if ( pFsm )
{
pFsm->SetServerInfo(pServerInfo);
}
error = DoFsm(pFsm);
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Connect(
IN LONG Timeout,
IN INT Retries,
IN DWORD dwFlags
)
/*++
Routine Description:
Initiate connection with server
Arguments:
Timeout - maximum amount of time (mSec) to wait for connection
Retries - maximum number of attempts to connect
dwFlags - flags controlling request
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure - ERROR_NOT_ENOUGH_MEMORY
Couldn't create FSM
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Connect",
"{%#x [%#x]} %d, %d, %#x",
this,
m_Socket,
Timeout,
Retries,
dwFlags
));
#ifdef TEST_CODE
Timeout *= 20;
Retries *= 20;
#endif
DWORD error;
error = DoFsm(new CFsm_SocketConnect(Timeout, Retries, dwFlags, this));
DEBUG_LEAVE(error);
return error;
}
DWORD
CFsm_SocketConnect::RunSM(
IN CFsm * Fsm
)
/*++
Routine Description:
Runs next CFsm_SocketConnect state
Arguments:
Fsm - FSM controlling operation
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"CFsm_SocketConnect::RunSM",
"%#x",
Fsm
));
ICSocket * pSocket = (ICSocket *)Fsm->GetContext();
CFsm_SocketConnect * stateMachine = (CFsm_SocketConnect *)Fsm;
DWORD error;
switch (Fsm->GetState()) {
case FSM_STATE_INIT:
error = pSocket->Connect_Start(stateMachine);
break;
case FSM_STATE_CONTINUE:
error = pSocket->Connect_Continue(stateMachine);
break;
case FSM_STATE_ERROR:
error = pSocket->Connect_Error(stateMachine);
break;
default:
error = ERROR_INTERNET_INTERNAL_ERROR;
Fsm->SetDone(ERROR_INTERNET_INTERNAL_ERROR);
INET_ASSERT(FALSE);
break;
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Connect_Start(
IN CFsm_SocketConnect * Fsm
)
/*++
Routine Description:
Starts a socket connect operation - creates a socket and connects it to a
server using the address information returned by GetServiceAddress(). There
may be several addresses to try. We return as soon as we successfully
generate a connection, or after we have tried <Retries> attempts, or until
<Timeout> milliseconds have elapsed
Arguments:
Fsm - socket connect FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Connect_Start",
"{%#x [%#x]}, %#x(%d, %d, %#x)",
this,
m_Socket,
Fsm,
Fsm->m_Timeout,
Fsm->m_Retries,
Fsm->m_dwFlags
));
PERF_ENTER(Connect_Start);
CFsm_SocketConnect & fsm = *Fsm;
LPINTERNET_THREAD_INFO lpThreadInfo = fsm.GetThreadInfo();
INET_ASSERT(lpThreadInfo != NULL);
DWORD error = ERROR_SUCCESS;
int serr = SOCKET_ERROR;
INET_ASSERT(IsClosed());
//
// ensure the next state is CONTINUE. It may be INIT because we could have
// been looping through bad addresses (if sufficient timeout & retries)
//
if(Fsm->GetState() == FSM_STATE_INIT)
{
fsm.SetErrorState(error);
}
fsm.SetNextState(FSM_STATE_CONTINUE);
//
// if we are offline then quit now - we can't make any network requests
//
InternetAutodialIfNotLocalHost(NULL, fsm.m_pServerInfo->GetHostName());
if (IsOffline()) {
error = ERROR_INTERNET_OFFLINE;
goto quit;
}
//
// get address to use. If exhausted, re-resolve
//
if (fsm.GetFunctionState() == FSM_STATE_2) {
fsm.SetFunctionState(FSM_STATE_1);
goto resolve_continue;
}
if (!fsm.m_pServerInfo->GetNextAddress(&fsm.m_dwResolutionId,
&fsm.m_dwAddressIndex,
GetPort(),
fsm.m_pAddress
)) {
if (fsm.m_bResolved) {
error = ERROR_INTERNET_CANNOT_CONNECT;
} else {
fsm.SetFunctionState(FSM_STATE_2);
fsm.SetNextState(FSM_STATE_INIT);
error = fsm.m_pServerInfo->ResolveHost(&fsm.m_dwResolutionId,
fsm.m_dwFlags
);
if (error == ERROR_IO_PENDING) {
goto quit;
}
resolve_continue:
fsm.m_bResolved = TRUE;
if (error == ERROR_SUCCESS) {
if (!fsm.m_pServerInfo->GetNextAddress(&fsm.m_dwResolutionId,
&fsm.m_dwAddressIndex,
GetPort(),
fsm.m_pAddress
)) {
error = ERROR_INTERNET_CANNOT_CONNECT;
}
}
else if (error == ERROR_INTERNET_NAME_NOT_RESOLVED)
{
fsm.SetNextState(FSM_STATE_CONTINUE);
goto quit; // exit out NOW with ERROR_INTERNET_NAME_NOT_RESOLVED, instead of CANNOT_CONNECT
}
}
}
if (error != ERROR_SUCCESS) {
//
// name resolution failed - done
//
goto quit;
}
//
// Update port for keep-alive info.
//
// The port number field is in the same location in both a
// sockaddr_in and a sockaddr_in6, so it is safe to cast the
// sockaddr to sockaddr_in here - this works for IPv4 or IPv6.
//
INET_ASSERT(offsetof(SOCKADDR_IN, sin_port) == offsetof(SOCKADDR_IN6, sin6_port));
SetPort(_I_ntohs(((LPSOCKADDR_IN)fsm.m_pAddress->RemoteAddr.lpSockaddr)->sin_port));
//
// BUGBUG - this code was supplying AF_UNSPEC to socket(), which should
// be okay, but because of a bug in the Win95 wsipx driver
// which manifests itself when we call bind(), we must send in
// the address family supplied in the local socket address by
// GetAddressByName()
//
int protocol;
DWORD dwConnFlags;
protocol = fsm.m_pAddress->iProtocol;
#if defined(SITARA)
//
// Only enable Sitara if we're connected via modem
//
//dprintf("connect_start: IsSitara = %B, IsModemConn=%B\n",GlobalEnableSitara, GlobalHasSitaraModemConn);
if (GlobalEnableSitara && GlobalHasSitaraModemConn) {
protocol = (int)GetSitaraProtocol();
}
#endif // SITARA
m_Socket = _I_socket(fsm.m_pAddress->LocalAddr.lpSockaddr->sa_family,
fsm.m_pAddress->iSocketType,
protocol
);
if (m_Socket == INVALID_SOCKET) {
DEBUG_PRINT(SOCKETS,
ERROR,
("failed to create socket\n"
));
goto check_socket_error;
}
DEBUG_PRINT(SOCKETS,
INFO,
("created socket %#x\n",
m_Socket
));
//
// inform the app that we are connecting to the server (but only on the
// first attempt)
//
//if ((fsm.m_dwFlags & SF_INDICATE) && (error == ERROR_SUCCESS)) {
if (fsm.m_dwFlags & SF_INDICATE) {
InternetIndicateStatusAddress(INTERNET_STATUS_CONNECTING_TO_SERVER,
fsm.m_pAddress->RemoteAddr.lpSockaddr,
fsm.m_pAddress->RemoteAddr.iSockaddrLength
);
}
//
// if requested to, put the socket in non-blocking mode
//
if (fsm.m_dwFlags & SF_NON_BLOCKING
&& (GlobalRunningNovellClient32 ? GlobalNonBlockingClient32 : TRUE)) {
error = SetNonBlockingMode(TRUE);
if (error != ERROR_SUCCESS) {
fsm.SetErrorState(error);
goto quit;
}
}
//
// bind the socket to the local address
//
DEBUG_PRINT(SOCKETS,
INFO,
("binding to local address %d.%d.%d.%d, port %d, index %d\n",
((LPBYTE)fsm.m_pAddress->LocalAddr.lpSockaddr)[4] & 0xff,
((LPBYTE)fsm.m_pAddress->LocalAddr.lpSockaddr)[5] & 0xff,
((LPBYTE)fsm.m_pAddress->LocalAddr.lpSockaddr)[6] & 0xff,
((LPBYTE)fsm.m_pAddress->LocalAddr.lpSockaddr)[7] & 0xff,
_I_ntohs(((LPSOCKADDR_IN)fsm.m_pAddress->LocalAddr.lpSockaddr)->sin_port),
fsm.m_dwAddressIndex
));
serr = _I_bind(m_Socket,
fsm.m_pAddress->LocalAddr.lpSockaddr,
fsm.m_pAddress->LocalAddr.iSockaddrLength
);
if (serr == SOCKET_ERROR) {
DEBUG_PRINT(SOCKETS,
ERROR,
("failed to bind socket %#x\n",
m_Socket
));
goto check_socket_error;
}
//
// record source port (useful for matching with net sniff)
//
SetSourcePort();
DEBUG_PRINT(SOCKETS,
INFO,
("socket %#x bound to port %d (%#x)\n",
m_Socket,
m_SourcePort,
m_SourcePort
));
//
// let another thread know the socket to cancel if it wants to kill
// this operation
//
INET_ASSERT(fsm.GetMappedHandleObject() != NULL);
if (fsm.GetMappedHandleObject() != NULL) {
fsm.GetMappedHandleObject()->SetAbortHandle(this);
}
//
// try to connect to the next address
//
DEBUG_PRINT(SOCKETS,
INFO,
("connecting %#x/%d to remote address %d.%d.%d.%d, port %d, index %d\n",
m_Socket,
m_SourcePort,
((LPBYTE)fsm.m_pAddress->RemoteAddr.lpSockaddr)[4] & 0xff,
((LPBYTE)fsm.m_pAddress->RemoteAddr.lpSockaddr)[5] & 0xff,
((LPBYTE)fsm.m_pAddress->RemoteAddr.lpSockaddr)[6] & 0xff,
((LPBYTE)fsm.m_pAddress->RemoteAddr.lpSockaddr)[7] & 0xff,
_I_ntohs(((LPSOCKADDR_IN)fsm.m_pAddress->RemoteAddr.lpSockaddr)->sin_port),
fsm.m_dwAddressIndex
));
fsm.SetNextState(FSM_STATE_CONTINUE);
fsm.StartTimer();
#ifdef TEST_CODE
SetLastError(-1);
serr = -1;
#else
if (IsSocks()) {
serr = SocksConnect((LPSOCKADDR_IN)fsm.m_pAddress->RemoteAddr.lpSockaddr,
fsm.m_pAddress->RemoteAddr.iSockaddrLength
);
} else {
serr = _I_connect(m_Socket,
fsm.m_pAddress->RemoteAddr.lpSockaddr,
fsm.m_pAddress->RemoteAddr.iSockaddrLength
);
}
#endif
//
// here if a socket operation failed, in which case serr will be SOCKET_ERROR
//
check_socket_error:
if (serr == 0) {
//
// successful (probably synchronous) connect completion
//
//
// in the sync case, we just call the continue handler. No need to
// return to the state handler
//
Connect_Continue(Fsm);
goto quit;
}
//
// here if a socket operation failed. We have to read the socket error in
// this thread before doing anything else or we'll lose the error. We handle
// it in Connect_Error()
//
error = _I_WSAGetLastError();
DEBUG_PRINT(SOCKETS,
INFO,
("connect(%#x) returns %d\n",
m_Socket,
error
));
//
// if we are using non-blocking sockets then we need to wait until the
// connect has completed, or an error occurs.
// If we got any status other than WSAEWOULDBLOCK then we have to handle
// the error
//
if (IsNonBlocking() && (error == WSAEWOULDBLOCK)) {
DEBUG_PRINT(SOCKETS,
INFO,
("connect() blocked, socket %#x, port %d\n",
m_Socket,
m_SourcePort
));
fsm.SetAction(FSM_ACTION_CONNECT);
DWORD timeout = GetTimeoutValue(INTERNET_OPTION_CONNECT_TIMEOUT);
INTERNET_HANDLE_OBJECT * pObject = fsm.GetMappedHandleObject();
if (pObject != NULL) {
if (pObject->IsFromCacheTimeoutSet()
&& (pObject->GetObjectType() == TypeHttpRequestHandle)
&& ((HTTP_REQUEST_HANDLE_OBJECT *)pObject)->CanRetrieveFromCache()) {
timeout = GetTimeoutValue(INTERNET_OPTION_FROM_CACHE_TIMEOUT);
DWORD connectTime = fsm.m_pServerInfo->GetConnectTime();
if (connectTime == 0) {
connectTime = timeout;
}
timeout += connectTime;
}
}
fsm.SetTimeout(timeout);
fsm.SetNextState(FSM_STATE_CONTINUE);
//
// after we set the state to waiting, and get ERROR_IO_PENDING from
// QueueSocketWorkItem() then we can no longer touch this FSM until
// it completes asynchronously
//
//
// perf - test the socket. If this completes quickly we don't take a
// context switch
//
//error = WaitForReceive(0);
//if (error == ERROR_INTERNET_TIMEOUT) {
error = QueueSocketWorkItem(Fsm, m_Socket);
//}
if (error == ERROR_SUCCESS) {
//
// in the unlikely event the request completed quickly and
// successfully
//
serr = 0;
goto check_socket_error;
} else if (error == ERROR_IO_PENDING) {
//
// the request is pending. We already set waiting state
//
goto quit;
}
//
// if here then QueueSocketWorkItem() returned some other error
//
} else {
//
// some other socket error occurred. Convert to INTERNET error
//
fsm.SetErrorState(MapInternetError(error));
error = Connect_Continue(Fsm);
}
fsm.SetErrorState(error);
quit:
//
// we are done if not pending AND we will not re-enter this state in order
// to re-do the name resolution/find another address
//
if ((error != ERROR_IO_PENDING) && (fsm.GetNextState() != FSM_STATE_INIT)) {
fsm.SetDone();
PERF_LEAVE(Connect_Start);
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Connect_Continue(
IN CFsm_SocketConnect * Fsm
)
/*++
Routine Description:
Performs common processing after connect completion or failure
Arguments:
Fsm - reference to socket connect finite state machine
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Connect_Continue",
"{%#x [%#x/%d]}, %#x(%d, %d, %#x)",
this,
GetSocket(),
GetSourcePort(),
Fsm,
Fsm->m_Timeout,
Fsm->m_Retries,
Fsm->m_dwFlags
));
PERF_ENTER(Connect_Continue);
CFsm_SocketConnect & fsm = *Fsm;
fsm.StopTimer();
// INET_ASSERT((fsm.GetMappedHandleObject() != NULL)
// ? (fsm.GetMappedHandleObject()->GetAbortHandle() != NULL)
// : TRUE);
DWORD error = fsm.GetError();
//INET_ASSERT(error != SOCKET_ERROR);
DEBUG_PRINT(SOCKETS,
INFO,
("connect() resumed, socket %#x, port %d\n",
m_Socket,
m_SourcePort
));
//
// check for aborted request
//
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
} else if (error == ERROR_INTERNET_INTERNAL_SOCKET_ERROR) {
error = ERROR_INTERNET_CANNOT_CONNECT;
}
if (error == ERROR_SUCCESS) {
DEBUG_PRINT(SOCKETS,
INFO,
("socket %#x/port %d connected; time = %d mSec\n",
m_Socket,
m_SourcePort,
fsm.ReadTimer()
));
error = Connect_Finish(Fsm);
} else {
DEBUG_PRINT(SOCKETS,
ERROR,
("failed to connect socket %#x/port %d: error %s\n",
m_Socket,
m_SourcePort,
InternetMapError(error)
));
fsm.SetError(error);
error = Connect_Error(Fsm);
}
PERF_LEAVE(Connect_Continue);
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Connect_Error(
IN CFsm_SocketConnect * Fsm
)
/*++
Routine Description:
Called to handle a connect error. Either causes the FSM to terminate or
prepares the FSM to try another connection
Arguments:
Fsm - socket connect FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Connect_Error",
"{%#x [%#x/%d]}, %#x(%d, %d, %#x)",
this,
GetSocket(),
GetSourcePort(),
Fsm,
Fsm->m_Timeout,
Fsm->m_Retries,
Fsm->m_dwFlags
));
PERF_ENTER(Connect_Error);
CFsm_SocketConnect & fsm = *Fsm;
fsm.StopTimer();
if (fsm.IsInvalid())
{
fsm.SetDone(ERROR_INTERNET_OPERATION_CANCELLED);
return ERROR_INTERNET_OPERATION_CANCELLED;
}
INTERNET_HANDLE_OBJECT * pObject = fsm.GetMappedHandleObject();
//
// no longer performing socket operation - clear abort handle
//
INET_ASSERT(pObject != NULL);
if (pObject != NULL) {
pObject->ResetAbortHandle();
}
DWORD error = fsm.GetError();
BOOL bRestartable = FALSE;
//INET_ASSERT(error != SOCKET_ERROR);
INET_ASSERT(error != ERROR_SUCCESS);
//
// check for aborted request - this overrides any socket error
//
if (IsAborted() || error == ERROR_INTERNET_OPERATION_CANCELLED) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
} else if (fsm.IsCountedOut()
|| fsm.IsTimedOut() // entire request timeout
|| (error == ERROR_INTERNET_TIMEOUT)) { // just this request t/o
DEBUG_PRINT(SOCKETS,
INFO,
("counted out or timed out\n"
));
//
// CANNOT_CONNECT takes precedence over TIMEOUT
//
if (error == ERROR_INTERNET_INTERNAL_SOCKET_ERROR) {
error = ERROR_INTERNET_CANNOT_CONNECT;
} else if (fsm.IsTimedOut()) {
error = ERROR_INTERNET_TIMEOUT;
} else if (fsm.IsCountedOut()) {
error = ERROR_INTERNET_CANNOT_CONNECT;
}
} else if (error != ERROR_INTERNET_OFFLINE) {
//
// not aborted, timed-out, counted-out, or offline. We can try again
//
bRestartable = TRUE;
}
//
// if the socket is open, close it and try the next connection. Invalidate
// the address we tried
//
if (IsOpen()) {
Close();
}
DWORD mappedError = fsm.GetMappedError();
DEBUG_PRINT(SOCKETS,
INFO,
("mapped error = %d [%s]\n",
mappedError,
InternetMapError(mappedError)
));
//
// don't invalidate address if from-cache-if-net-fail timeout
//
BOOL bInvalidate = TRUE;
if ((pObject != NULL) && pObject->IsFromCacheTimeoutSet()) {
bInvalidate = FALSE;
}
if ((mappedError == WSAENETUNREACH)
|| (mappedError == WSAETIMEDOUT)
|| ((error == ERROR_INTERNET_TIMEOUT) && bInvalidate)
|| (error == ERROR_INTERNET_CANNOT_CONNECT)
#ifdef TEST_CODE
|| (error == (DWORD)-1)
#endif
) {
fsm.m_pServerInfo->InvalidateAddress(fsm.m_dwResolutionId,
fsm.m_dwAddressIndex
);
}
//
// if the operation was cancelled or we lost connectivity then quit
//
if (bRestartable) {
fsm.SetNextState(FSM_STATE_INIT);
} else {
fsm.SetDone(error);
PERF_LEAVE(Connect_Error);
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Connect_Finish(
IN CFsm_SocketConnect * Fsm
)
/*++
Routine Description:
Called when the connection has been successfully established
Arguments:
Fsm - socket connect FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Connect_Finish",
"{%#x [%#x/%d]}, %#x(%d, %d, %#x)",
this,
GetSocket(),
GetSourcePort(),
Fsm,
Fsm->m_Timeout,
Fsm->m_Retries,
Fsm->m_dwFlags
));
PERF_ENTER(Connect_Finish);
CFsm_SocketConnect & fsm = *Fsm;
INET_ASSERT(IsOpen());
//
// store the average connect time to this server in our CServerInfo
//
if (fsm.m_pServerInfo != NULL) {
fsm.m_pServerInfo->UpdateConnectTime(fsm.ReadTimer());
}
if (fsm.m_pOriginServer != NULL) {
fsm.m_pOriginServer->UpdateConnectTime(fsm.ReadTimer());
}
#ifdef TEST_CODE
BOOL optval;
int optlen = sizeof(optval);
int serr = _I_getsockopt(GetSocket(),
IPPROTO_TCP,
TCP_NODELAY,
(char FAR *)&optval,
&optlen
);
if (serr != 0) {
DEBUG_PRINT(SOCKETS,
ERROR,
("getsockopt(TCP_NODELAY) returns %s (%d)\n",
InternetMapError(_I_WSAGetLastError()),
_I_WSAGetLastError()
));
}
#endif
//
// no longer performing socket operation - clear abort handle
//
INET_ASSERT(fsm.GetMappedHandleObject() != NULL);
if (fsm.GetMappedHandleObject() != NULL) {
fsm.GetMappedHandleObject()->ResetAbortHandle();
}
//
// set the send & receive buffer sizes if not -1 (meaning don't change)
//
DWORD bufferLength;
bufferLength = GetBufferLength(ReceiveBuffer);
if (bufferLength != (DWORD)-1) {
SetBufferLength(ReceiveBuffer, bufferLength);
}
bufferLength = GetBufferLength(SendBuffer);
if (bufferLength != (DWORD)-1) {
SetBufferLength(SendBuffer, bufferLength);
}
//
// disable send coalescing
//
SetSendCoalescing(FALSE);
//
// let the app know we connected to the server successfully
//
if (fsm.m_dwFlags & SF_INDICATE) {
InternetIndicateStatusAddress(INTERNET_STATUS_CONNECTED_TO_SERVER,
fsm.m_pAddress->RemoteAddr.lpSockaddr,
fsm.m_pAddress->RemoteAddr.iSockaddrLength
);
}
fsm.SetDone();
PERF_LEAVE(Connect_Finish);
DEBUG_LEAVE(ERROR_SUCCESS);
return ERROR_SUCCESS;
}
int
ICSocket::SocksConnect(
IN LPSOCKADDR_IN pSockaddr,
IN INT nLen
)
/*++
Routine Description:
Connect to remote host via SOCKS proxy. Modified from original. If we are
here then we are going specifically via a known SOCKS proxy. There is now
only one Hosts object, containing a single SOCKD socks proxy address and
user name
N.B. Irrespective of whether we are non-blocking, this function executes
in blocking mode (we expect that we are on an intranet and complete quickly)
Arguments:
pSockaddr - address of remote host (on other side of SOCKS firewall)
nLen - length of *pSockaddr
Return Value:
int
Success - 0
Failure - -1
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Int,
"ICSocket::SocksConnect",
"{%#x} %#x, %d",
GetSocket(),
pSockaddr,
nLen
));
//
// BUGBUG - should check if the socket type is SOCK_STREAM or if we have
// already connected this socket. This code was part of original
// general purpose solution. We don't need it
//
//
// initialize sockaddr for connecting to SOCKS firewall
//
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = _I_htons(m_SocksPort);
sin.sin_addr.s_addr = m_SocksAddress;
memset(&sin.sin_zero, 0, sizeof(sin.sin_zero));
//
// initialize SOCKS request packet
//
struct {
unsigned char VN;
unsigned char CD;
unsigned short DSTPORT;
unsigned long DSTIP;
char UserId[255];
} request;
request.VN = 4;
request.CD = 1;
request.DSTPORT = pSockaddr->sin_port;
request.DSTIP = pSockaddr->sin_addr.s_addr;
DWORD length = sizeof(request.UserId);
GlobalUserName.Get(request.UserId, &length);
length += 8 + 1; // 8 == sizeof fixed portion of request;
// +1 for additional '\0'
//
// put socket into blocking mode
//
BOOL non_blocking = IsNonBlocking();
if (non_blocking) {
SetNonBlockingMode(FALSE);
}
//
// communicate with SOCKS firewall: send SOCKS request & receive response
//
int serr = _I_connect(m_Socket, (LPSOCKADDR)&sin, sizeof(sin));
if (serr != SOCKET_ERROR) {
serr = _I_send(m_Socket, (char *)&request, length, 0);
if (serr == (int)length) {
char response[256];
serr = _I_recv(m_Socket, (char *)response, sizeof(response), 0);
if( serr == 1 ) {
// need to read at least 2 bytes
DEBUG_PRINT(SOCKETS,
ERROR,
("need to read one more byte\n"));
serr = _I_recv(
m_Socket, (char *)(&response[1]), sizeof(response) - 1, 0);
}
if (serr != SOCKET_ERROR) {
if (response[1] != 90) {
serr = SOCKET_ERROR;
}
} else {
DEBUG_PRINT(SOCKETS,
ERROR,
("recv(%#x) returns %d\n",
m_Socket,
_I_WSAGetLastError()
));
}
} else {
DEBUG_PRINT(SOCKETS,
ERROR,
("send(%#x) returns %d\n",
m_Socket,
_I_WSAGetLastError()
));
serr = SOCKET_ERROR;
}
} else {
DEBUG_PRINT(SOCKETS,
ERROR,
("connect(%#x) returns %d\n",
m_Socket,
_I_WSAGetLastError()
));
}
//
// if originally non-blocking, make socket non-blocking again
//
if (non_blocking) {
SetNonBlockingMode(TRUE);
}
//
// if success, mark the socket as being connected through firewall
//
if (serr == SOCKET_ERROR) {
_I_WSASetLastError(WSAECONNREFUSED);
}
DEBUG_LEAVE(serr);
return serr;
}
DWORD
ICSocket::Disconnect(
IN DWORD dwFlags
)
/*++
Routine Description:
Undoes the work of ConnectSocket - i.e. closes a connected socket. We make
callbacks to inform the app that this socket is being closed
Arguments:
dwFlags - controlling operation
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Disconnect",
"{%#x/%d} %#x",
GetSocket(),
GetSourcePort(),
dwFlags
));
//
// let the app know we are closing the connection
//
if (dwFlags & SF_INDICATE) {
InternetIndicateStatus(INTERNET_STATUS_CLOSING_CONNECTION, NULL, 0);
}
DWORD error = Close();
if ((error == ERROR_SUCCESS) && (dwFlags & SF_INDICATE)) {
//
// let the app know the connection is closed
//
InternetIndicateStatus(INTERNET_STATUS_CONNECTION_CLOSED, NULL, 0);
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Close(
VOID
)
/*++
Routine Description:
Closes a connected socket. Assumes that any linger or shutdown etc.
requirements have already been applied to the socket
Arguments:
none.
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Close",
"{%#x/%d}",
GetSocket(),
GetSourcePort()
));
DWORD error = ERROR_SUCCESS;
if (IsOpen()) {
//dprintf("**** closing %#x\n", m_Socket);
int serr;
__try {
serr = _I_closesocket(m_Socket);
} __except(EXCEPTION_EXECUTE_HANDLER) {
serr = 0;
}
ENDEXCEPT
error = (serr == SOCKET_ERROR)
? MapInternetError(_I_WSAGetLastError())
: ERROR_SUCCESS;
}
//
// the socket is now closed
//
m_Socket = INVALID_SOCKET;
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Abort(
VOID
)
/*++
Routine Description:
Aborts a socket by simply closing it
Arguments:
None.
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Abort",
"{%#x/%d}",
GetSocket(),
GetSourcePort()
));
DWORD error = Close();
if (error == ERROR_SUCCESS) {
SetAborted();
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Shutdown(
IN DWORD dwControl
)
/*++
Routine Description:
Stops any more send/receives from the socket
Arguments:
dwControl - 0 to stop receives, 1 to stop sends, 2 to stop both
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Shutdown",
"{%#x/%d}",
GetSocket(),
GetSourcePort()
));
DWORD error = ERROR_SUCCESS;
if (IsOpen()) {
int serr = _I_shutdown(m_Socket, dwControl);
if (serr == SOCKET_ERROR) {
//
// map any sockets error to WinInet error
//
error = MapInternetError(_I_WSAGetLastError());
}
}
DEBUG_LEAVE(error);
return error;
}
BOOL
ICSocket::IsReset(
VOID
)
/*++
Routine Description:
Determines if the socket has been closed. We peek the socket for 1 byte. If
the socket is in blocking mode, we temporarily switch to non-blocking to
perform the test - we don't want to block, nor remove any data from the
socket
Arguments:
None.
Return Value:
BOOL
TRUE - socket reset (closed by server)
FALSE - socket alive
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Bool,
"ICSocket::IsReset",
"{%#x [%#x/%d]}",
this,
GetSocket(),
GetSourcePort()
));
CHECK_ICSOCKET();
BOOL bReset = FALSE;
BOOL bSetBlocking = FALSE;
if (IsOpen()) {
if (!IsNonBlocking()) {
SetNonBlockingMode(TRUE);
bSetBlocking = TRUE;
}
char ch;
#ifndef unix
int n = _I_recv(m_Socket, &ch, 1, MSG_PEEK);
if (n < 0) {
DWORD error = _I_WSAGetLastError();
if (error != WSAEWOULDBLOCK) {
DEBUG_PRINT(SOCKETS,
INFO,
("recv() returns %s (%d)\n",
InternetMapError(error),
error
));
n = 0;
}
}
if (n == 0) {
#else
DWORD dwAvail = 0;
int n = _I_ioctlsocket(m_Socket,FIONREAD,&dwAvail);
if (n != 0) {
#endif /* unix */
DEBUG_PRINT(SOCKETS,
INFO,
("socket %#x/port %d is reset\n",
m_Socket,
m_SourcePort
));
bReset = TRUE;
}
if (bSetBlocking) {
SetNonBlockingMode(FALSE);
}
} else {
bReset = TRUE;
}
DEBUG_LEAVE(bReset);
return bReset;
}
DWORD
ICSocket::SetTimeout(
IN DWORD Type,
IN int Timeout
)
/*++
Routine Description:
Sets a timeout value for a connected socket
Arguments:
Type - type of timeout to set - send, or receive
Timeout - timeout value to set
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::SetTimeout",
"{%#x/%d} %s (%d), %d",
GetSocket(),
GetSourcePort(),
(Type == SEND_TIMEOUT) ? "SEND_TIMEOUT"
: (Type == RECEIVE_TIMEOUT) ? "RECEIVE_TIMEOUT"
: "?",
Type,
Timeout
));
INET_ASSERT((Type == SEND_TIMEOUT) || (Type == RECEIVE_TIMEOUT));
int serr = _I_setsockopt(m_Socket,
SOL_SOCKET,
(Type == SEND_TIMEOUT)
? SO_SNDTIMEO
: SO_RCVTIMEO,
(const char FAR *)&Timeout,
sizeof(Timeout)
);
DWORD error = ERROR_SUCCESS;
if (serr == SOCKET_ERROR) {
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
} else {
error = MapInternetError(_I_WSAGetLastError());
}
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::SetLinger(
IN BOOL Linger,
IN int Timeout
)
/*++
Routine Description:
Sets the linger option for a connected socket
Arguments:
Linger - FALSE if the caller wants immediate shutdown of the socket
when closed, or TRUE if we are to wait around until
queued data has been sent
Timeout - timeout value to use if Linger is TRUE
Return Value:
DWORD
Success - ERROR_SUCESS
Failure - WSA error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::SetLinger",
"{%#x/%d} %B, %d",
GetSocket(),
GetSourcePort(),
Linger,
Timeout
));
DWORD error = ERROR_SUCCESS;
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
} else if (IsOpen()) {
LINGER linger;
INET_ASSERT(Timeout <= USHRT_MAX);
linger.l_onoff = (u_short)(Linger ? 1 : 0);
linger.l_linger = (u_short)Timeout;
//
// in some shutdown situations, we are hitting exception in winsock
// on win95 (!). Handle exception
//
__try {
if (_I_setsockopt(m_Socket,
SOL_SOCKET,
SO_LINGER,
(const char FAR *)&linger,
sizeof(linger)
) == SOCKET_ERROR) {
error = MapInternetError(_I_WSAGetLastError());
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
//
// do nothing except catch exception in retail
//
DEBUG_PRINT(SOCKETS,
ERROR,
("exception closing socket %#x/%d\n",
GetSocket(),
GetSourcePort()
));
INET_ASSERT(IsOpen());
}
ENDEXCEPT
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::SetNonBlockingMode(
IN BOOL bNonBlocking
)
/*++
Routine Description:
Sets socket non-blocking/blocking mode
Arguments:
bNonBlocking - TRUE if non-blocking, FALSE if blocking
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error mapped to INTERNET error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::SetNonBlockingMode",
"{%#x/%d} %B",
GetSocket(),
GetSourcePort(),
bNonBlocking
));
u_long on = (bNonBlocking) ? 1 : 0;
DWORD error = ERROR_SUCCESS;
if (_I_ioctlsocket(m_Socket, FIONBIO, &on) == 0) {
if (on) {
m_dwFlags |= SF_NON_BLOCKING;
} else {
m_dwFlags &= ~SF_NON_BLOCKING;
}
} else {
DEBUG_PRINT(SOCKETS,
ERROR,
("failed to put socket %#x/port %d into %sblocking mode\n",
m_Socket,
m_SourcePort,
on ? "non-" : ""
));
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
} else {
error = MapInternetError(_I_WSAGetLastError());
}
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::GetBufferLength(
IN SOCKET_BUFFER_ID SocketBufferId
)
/*++
Routine Description:
Returns the send or receive buffer length for this socket object
Arguments:
SocketBufferId - which buffer length to return
Return Value:
DWORD
--*/
{
//
// BUGBUG - RLF 04/29/96
//
// This function should access first the current object, then the parent
// object, then the globals for this data
//
HINTERNET hInternet;
DWORD dwBufferLength = (DWORD)-1;
DWORD error;
switch (SocketBufferId)
{
case ReceiveBuffer:
dwBufferLength = GlobalSocketReceiveBufferLength;
break;
case SendBuffer:
hInternet = InternetGetMappedObjectHandle();
if (hInternet != NULL)
{
//
// ensure the handle is valid
//
error = ((HANDLE_OBJECT*)hInternet)->IsValid(TypeHttpRequestHandle);
if (error == ERROR_SUCCESS)
{
dwBufferLength = ((HTTP_REQUEST_HANDLE_OBJECT*)hInternet)->GetSocketSendBufferLength();
}
}
if (dwBufferLength == (DWORD)-1)
{
dwBufferLength = GlobalSocketSendBufferLength;
}
break;
default:
dwBufferLength = (DWORD)-1;
}
return dwBufferLength;
}
DWORD
ICSocket::GetBufferLength(
IN SOCKET_BUFFER_ID SocketBufferId,
OUT LPDWORD lpdwBufferLength
)
/*++
Routine Description:
Gets the socket send or receive buffer length (if supported)
Arguments:
SocketBufferId - which buffer to set
lpdwBufferLength - where to write length
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Int,
"ICSocket::GetBufferLength",
"{%#x/%d} %s, %#x",
GetSocket(),
GetSourcePort(),
(SocketBufferId == ReceiveBuffer)
? "ReceiveBuffer"
: ((SocketBufferId == SendBuffer)
? "SendBuffer"
: "?")
));
DWORD size = sizeof(*lpdwBufferLength);
int serr = _I_getsockopt(m_Socket,
SOL_SOCKET,
SocketBufferId,
(char FAR *)lpdwBufferLength,
(int FAR *)&size
);
DWORD error;
if (serr != SOCKET_ERROR) {
error = ERROR_SUCCESS;
} else {
error = MapInternetError(_I_WSAGetLastError());
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::SetBufferLength(
IN SOCKET_BUFFER_ID SocketBufferId,
IN DWORD dwBufferLength
)
/*++
Routine Description:
Sets the socket send or receive buffer length
Arguments:
SocketBufferId - which buffer to set
dwBufferLength - length to set it to
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error mapped to INTERNET error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::SetBufferLength",
"{%#x/%d} %s, %d",
GetSocket(),
GetSourcePort(),
(SocketBufferId == ReceiveBuffer)
? "ReceiveBuffer"
: (SocketBufferId == SendBuffer)
? "SendBuffer"
: "?",
dwBufferLength
));
INET_ASSERT((int)dwBufferLength >= 0);
DWORD size = sizeof(dwBufferLength);
int serr = _I_setsockopt(m_Socket,
SOL_SOCKET,
SocketBufferId,
(const char FAR *)&dwBufferLength,
(int)size
);
DWORD error = ERROR_SUCCESS;
if (serr == SOCKET_ERROR) {
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
} else {
error = MapInternetError(_I_WSAGetLastError());
}
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::SetSendCoalescing(
IN BOOL bOnOff
)
/*++
Routine Description:
Enables or disables Nagle algorithm
Arguments:
bOnOff - FALSE to disable, TRUE to enable
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::SetSendCoalescing",
"{%#x/%d} %B",
GetSocket(),
GetSourcePort(),
bOnOff
));
int optval = bOnOff ? 0 : 1;
int serr = _I_setsockopt(m_Socket,
IPPROTO_TCP,
TCP_NODELAY,
(const char FAR *)&optval,
sizeof(optval)
);
DWORD error = ERROR_SUCCESS;
if (serr == SOCKET_ERROR) {
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
} else {
error = MapInternetError(_I_WSAGetLastError());
}
}
DEBUG_LEAVE(error);
return error;
}
VOID
ICSocket::SetSourcePort(
VOID
)
/*++
Routine Description:
Record the port we are connected to locally. Useful for debugging & matching
up socket with net sniff
Arguments:
None.
Return Value:
None.
--*/
{
sockaddr_storage address; // Large enough for any address family.
int namelen = sizeof(address);
if (_I_getsockname(GetSocket(), (LPSOCKADDR)&address, &namelen) == 0) {
//
// The port number field is in the same location in both a
// sockaddr_in and a sockaddr_in6, so it is safe to cast the
// sockaddr to sockaddr_in here - this works for IPv4 or IPv6.
//
INET_ASSERT(offsetof(SOCKADDR_IN, sin_port) == offsetof(SOCKADDR_IN6, sin6_port));
m_SourcePort = (INTERNET_PORT)_I_ntohs(((LPSOCKADDR_IN)&address)->sin_port);
} else {
m_SourcePort = 0;
}
}
DWORD
ICSocket::Send(
IN LPVOID lpBuffer,
IN DWORD dwBufferLength,
IN DWORD dwFlags
)
/*++
Routine Description:
Sends data over connected socket
Arguments:
lpBuffer - pointer to buffer containing data to send
dwBufferLength - length of lpBuffer in bytes
dwFlags - flags controlling send:
SF_INDICATE - make status callbacks to the app when
we are starting to send data and when
we finish
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure - ERROR_NOT_ENOUGH_MEMORY
Couldn't create FSM
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Send",
"{%#x [%#x/%d]} %#x, %d, %#x",
this,
GetSocket(),
GetSourcePort(),
lpBuffer,
dwBufferLength,
dwFlags
));
INET_ASSERT(lpBuffer != NULL);
INET_ASSERT((int)dwBufferLength > 0);
DWORD error = DoFsm(new CFsm_SocketSend(lpBuffer,
dwBufferLength,
dwFlags,
this
));
DEBUG_LEAVE(error);
return error;
}
DWORD
CFsm_SocketSend::RunSM(
IN CFsm * Fsm
)
/*++
Routine Description:
Runs next CFsm_SocketSend state
Arguments:
Fsm - socket send FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"CFsm_SocketSend::RunSM",
"%#x",
Fsm
));
ICSocket * pSocket = (ICSocket *)Fsm->GetContext();
CFsm_SocketSend * stateMachine = (CFsm_SocketSend *)Fsm;
DWORD error;
switch (Fsm->GetState()) {
case FSM_STATE_INIT:
case FSM_STATE_CONTINUE:
case FSM_STATE_ERROR:
error = pSocket->Send_Start(stateMachine);
break;
default:
error = ERROR_INTERNET_INTERNAL_ERROR;
Fsm->SetDone(ERROR_INTERNET_INTERNAL_ERROR);
INET_ASSERT(FALSE);
break;
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Send_Start(
IN CFsm_SocketSend * Fsm
)
/*++
Routine Description:
Continues send request - sends the data
Arguments:
Fsm - socket send FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Send_Start",
"{%#x [%#x/%d]} %#x(%#x, %d, %#x)",
this,
GetSocket(),
GetSourcePort(),
Fsm,
Fsm->m_lpBuffer,
Fsm->m_dwBufferLength,
Fsm->m_dwFlags
));
CFsm_SocketSend & fsm = *Fsm;
DWORD error = fsm.GetError();
FSM_STATE state = fsm.GetState();
INTERNET_HANDLE_OBJECT * pObject = fsm.GetMappedHandleObject();
if (fsm.IsInvalid())
{
error = ERROR_INTERNET_OPERATION_CANCELLED;
goto quit;
}
if (error != ERROR_SUCCESS) {
if (error == ERROR_INTERNET_INTERNAL_SOCKET_ERROR) {
error = ERROR_INTERNET_CONNECTION_RESET;
}
goto quit;
}
if (state == FSM_STATE_INIT) {
if (!(m_dwFlags & (SF_ENCRYPT | SF_DECRYPT))) {
DEBUG_DUMP_API(SOCKETS,
"sending data:\n",
fsm.m_lpBuffer,
fsm.m_dwBufferLength
);
}
if (pObject != NULL) {
pObject->SetAbortHandle(this);
}
if (fsm.m_dwFlags & SF_INDICATE) {
InternetIndicateStatus(INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
}
fsm.StartTimer();
}
while (fsm.m_dwBufferLength != 0) {
//
// if we are offline then quit now - we can't make any network
// requests
//
if (IsOffline()) {
error = ERROR_INTERNET_OFFLINE;
break;
}
//
// the socket may have already been aborted
//
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
break;
}
if (fsm.m_pServerInfo != NULL) {
fsm.m_pServerInfo->SetLastActiveTime();
}
int nSent = _I_send(m_Socket,
(char FAR *)fsm.m_lpBuffer,
fsm.m_dwBufferLength,
0
);
if (nSent != SOCKET_ERROR) {
DEBUG_PRINT(SOCKETS,
INFO,
("sent %d bytes @ %#x to socket %#x/port %d\n",
nSent,
fsm.m_lpBuffer,
m_Socket,
m_SourcePort
));
fsm.m_iTotalSent += nSent;
fsm.m_lpBuffer = (LPBYTE)fsm.m_lpBuffer + nSent;
fsm.m_dwBufferLength -= nSent;
} else {
//
// check first to see if the error was due to the socket being
// closed as a result of the request being cancelled
//
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
break;
} else {
error = _I_WSAGetLastError();
}
if (IsNonBlocking() && (error == WSAEWOULDBLOCK)) {
DEBUG_PRINT(SOCKETS,
INFO,
("send() blocked, socket %#x, port %d\n",
m_Socket,
m_SourcePort
));
//dprintf("!!! send() blocked - socket %#x\n", m_Socket);
fsm.SetAction(FSM_ACTION_SEND);
DWORD timeout = GetTimeoutValue(INTERNET_OPTION_SEND_TIMEOUT);
if (pObject != NULL) {
if (pObject->IsFromCacheTimeoutSet()
&& (pObject->GetObjectType() == TypeHttpRequestHandle)
&& ((HTTP_REQUEST_HANDLE_OBJECT *)pObject)->CanRetrieveFromCache()) {
timeout = GetTimeoutValue(INTERNET_OPTION_FROM_CACHE_TIMEOUT);
DWORD RTT = fsm.m_pOriginServer->GetRTT();
if (RTT == 0) {
RTT = timeout;
}
timeout += RTT;
}
}
fsm.SetTimeout(timeout);
error = QueueSocketWorkItem(Fsm, m_Socket);
if (error != ERROR_SUCCESS) {
break;
}
} else {
//
// map any sockets error to WinInet error and terminate this
// request
//
DEBUG_PRINT(SOCKETS,
ERROR,
("send() returns %d (%s)\n",
error,
InternetMapError(error)
));
error = MapInternetError(error);
break;
}
}
}
quit:
if (error != ERROR_IO_PENDING) {
fsm.StopTimer();
if (fsm.GetMappedHandleObject() != NULL) {
fsm.GetMappedHandleObject()->ResetAbortHandle();
}
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
} else if (error == ERROR_SUCCESS) {
if (fsm.m_dwFlags & SF_INDICATE) {
InternetIndicateStatus(INTERNET_STATUS_REQUEST_SENT,
&fsm.m_iTotalSent,
sizeof(fsm.m_iTotalSent)
);
}
if (fsm.m_pServerInfo != NULL) {
//fsm.m_pServerInfo->UpdateSendTime(fsm.ReadTimer());
}
}
fsm.SetDone(error);
}
DEBUG_LEAVE(error);
return error;
}
//
//DWORD
//ICSocket::SendTo(
// IN LPSOCKADDR lpDestination,
// IN DWORD dwDestinationLength,
// IN LPVOID lpBuffer,
// IN DWORD dwBufferLength,
// OUT LPDWORD lpdwBytesSent,
// IN DWORD dwWinsockFlags,
// IN DWORD dwFlags
// )
//
///*++
//
//Routine Description:
//
// Wrapper for sendto()
//
//Arguments:
//
// lpDestination - pointer to remote address to send to
//
// dwDestinationLength - length of *lpDestination
//
// lpBuffer - pointer to buffer containing data to send
//
// dwBufferLength - number of bytes to send from lpBuffer
//
// lpdwBytesSent - number of bytes sent to destination
//
// dwWinsockFlags - flags to pass through to sendto()
//
// dwFlags - ICSocket flags
//
//Return Value:
//
// DWORD
// Success - ERROR_SUCCESS
//
// Failure - ERROR_INTERNET_OPERATION_CANCELLED
// The operation was cancelled by the caller
//
// ERROR_INTERNET_TIMEOUT
// The operation timed out
//
// ERROR_INTERNET_CONNECTION_RESET
// An error occurred. We approximate to connection reset
//
// WSA error
// Some other sockets error occurred
//
//--*/
//
//{
// DEBUG_ENTER((DBG_SOCKETS,
// Dword,
// "ICSocket::SendTo",
// "{%#x} %#x, %d, %#x, %d, %#x, %#x, %#x",
// m_Socket,
// lpDestination,
// dwDestinationLength,
// lpBuffer,
// dwBufferLength,
// lpdwBytesSent,
// dwWinsockFlags,
// dwFlags
// ));
//
// INET_ASSERT(IsSocketValid());
// INET_ASSERT(lpdwBytesSent != NULL);
//
// int totalSent = 0;
// DWORD error = ERROR_SUCCESS;
// INTERNET_HANDLE_OBJECT * pObject = NULL;
// LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
// BOOL fNonBlocking;
//
// //
// // if we are offline then quit now - we can't make any network requests
// //
//
// if (IsOffline()) {
// error = ERROR_INTERNET_OFFLINE;
// goto quit;
// }
//
// if (IsAborted()) {
// error = ERROR_INTERNET_OPERATION_CANCELLED;
// goto quit;
// }
//
// if (lpThreadInfo == NULL) {
//
// INET_ASSERT(FALSE);
//
// error = ERROR_INTERNET_INTERNAL_ERROR;
// goto quit;
// }
//
// fNonBlocking = lpThreadInfo->IsAsyncWorkerThread;
//
// //
// // set the cancel socket in the object
// //
//
// pObject = (INTERNET_HANDLE_OBJECT * )lpThreadInfo->hObjectMapped;
// if (pObject != NULL) {
// pObject->SetAbortHandle(this);
// }
//
// //
// // if we are in async (== non-blocking) mode, let the async request
// // scheduler know what operation we will be waiting on
// //
//
// if (fNonBlocking) {
//
// INET_ASSERT(lpThreadInfo->lpArb != NULL);
//
// SET_ARB_SOCKET_OPERATION(lpThreadInfo->lpArb, m_Socket, SEND);
// }
//
// if (dwFlags & SF_INDICATE) {
//
// //
// // let the app know we are starting to send data
// //
//
// InternetIndicateStatus(INTERNET_STATUS_SENDING_REQUEST,
// NULL,
// 0
// );
// }
//
// DEBUG_DUMP(SOCKETS,
// "sending data:\n",
// lpBuffer,
// dwBufferLength
// );
//
// int nSent;
//
// //
// // loop until all data sent
// //
//
// do {
//
// nSent = _I_sendto(m_Socket,
// (char FAR *)lpBuffer + totalSent,
// dwBufferLength,
// dwWinsockFlags,
// lpDestination,
// dwDestinationLength
// );
// if (nSent != SOCKET_ERROR) {
//
// DEBUG_PRINT(SOCKETS,
// INFO,
// ("sent %d bytes @ %#x on socket %#x\n",
// nSent,
// (LPBYTE)lpBuffer + totalSent,
// m_Socket
// ));
//
// INET_ASSERT(nSent > 0);
//
// totalSent += nSent;
// dwBufferLength -= nSent;
// } else {
// error = _I_WSAGetLastError();
// if ((error == WSAEWOULDBLOCK) && fNonBlocking) {
//
// INET_ASSERT(_dwFlags & SF_NON_BLOCKING);
//
// DEBUG_PRINT(SOCKETS,
// INFO,
// ("sendto(%#x) would block\n",
// m_Socket
// ));
//
// lpThreadInfo->lpArb->Header.dwResultCode = ERROR_SUCCESS;
//
// SwitchToAsyncScheduler(m_Socket);
//
// error = lpThreadInfo->lpArb->Header.dwResultCode;
//
// DEBUG_PRINT(SOCKETS,
// INFO,
// ("sendto(%#x) resumed, returns %s\n",
// m_Socket,
// InternetMapError(error)
// ));
//
// if (error != ERROR_SUCCESS) {
// if (error == ERROR_INTERNET_INTERNAL_SOCKET_ERROR) {
// error = ERROR_INTERNET_CONNECTION_RESET;
// }
// }
// } else {
//
// //
// // some other error
// //
//
// error = MapInternetError(error);
// }
// }
//
// INET_ASSERT((int)dwBufferLength >= 0);
//
// } while ((dwBufferLength != 0) && (error == ERROR_SUCCESS));
//
// if ((dwFlags & SF_INDICATE) && (error == ERROR_SUCCESS)) {
//
// //
// // let the app know we have finished sending
// //
//
// InternetIndicateStatus(INTERNET_STATUS_REQUEST_SENT,
// &totalSent,
// sizeof(totalSent)
// );
// }
//
// //
// // if we are in async (== non-blocking) mode, let the async request
// // scheduler know that we no longer require this socket
// //
//
// if (fNonBlocking) {
//
// INET_ASSERT(lpThreadInfo->lpArb != NULL);
//
// SET_ARB_SOCKET_OPERATION(lpThreadInfo->lpArb, INVALID_SOCKET, SEND);
// }
//
//quit:
//
// *lpdwBytesSent = totalSent;
//
// //
// // no longer performing operation on this socket
// //
//
// if (pObject != NULL) {
// pObject->ResetAbortHandle();
//
// //
// // if the operation has been cancelled, then this error overrides any
// // other
// //
//
// //if (pObject->IsInvalidated()) {
// // error = pObject->GetError();
// // if (error == ERROR_SUCCESS) {
// // error = ERROR_INTERNET_OPERATION_CANCELLED;
// // }
// //}
// if (IsAborted()) {
// error = ERROR_INTERNET_OPERATION_CANCELLED;
// }
// }
//
// INET_ASSERT((pObject != NULL) ? (pObject->GetAbortHandle() == NULL) : TRUE);
//
// DEBUG_LEAVE(error);
//
// return error;
//}
DWORD
ICSocket::Receive(
IN OUT LPVOID * lplpBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPDWORD lpdwBufferRemaining,
IN OUT LPDWORD lpdwBytesReceived,
IN DWORD dwExtraSpace,
IN DWORD dwFlags,
OUT LPBOOL lpbEof
)
/*++
Routine Description:
Receives data from connected socket. Depending on flags settings, we will
perform a single receive, loop until we have filled the buffer and/or loop
until we have received all the data.
This function returns user data, so if the stream we are receiving from is
encrypted, we must decrypt the data before returning. This may require
receiving more data than the user expects because we have to decrypt at
message boundaries
This function is intended to be called in a loop. The buffer pointer and
buffer sizes are intended to be updated by each successive call to this
function, and should therefore have the same values the next time this
function is called
Arguments:
lplpBuffer - pointer to pointer to users buffer. If supplied, the
buffer should be LMEM_FIXED
lpdwBufferLength - size of buffer
lpdwBufferRemaining - number of bytes left in the buffer
lpdwBytesReceived - number of bytes received
dwExtraSpace - number of additional bytes caller wants at end of
buffer (only useful if resizing AND only applied at
end of receive)
dwFlags - flags controlling receive:
SF_EXPAND - lpBuffer can be expanded to fit
data
SF_COMPRESS - if set, we will shrink the buffer
to compress out any unused space
SF_RECEIVE_ALL - if set, this function will loop
until all data received, or the
supplied buffer is filled
SF_INDICATE - if set, we will make status
callbacks to the app when we are
starting to receive data, and when
we finish
SF_WAIT - (used with SF_NON_BLOCKING). Even
though the socket is non-blocking,
the caller wants us to not
relinquish control under the
request has been satisfied
lpbEof - TRUE if we got end-of-connection indication
(recv() returns 0)
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure - ERROR_NOT_ENOUGH_MEMORY
Couldn't allocate/grow buffer
ERROR_INSUFFICIENT_BUFFER
The initial buffer was insufficient (i.e. caller supplied
buffer pointer was NULL, or we ran out of buffer space and
are not allowed to resize it)
WSA error
Sockets error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Receive",
"%#x [%#x], %#x [%d], %#x [%d], %#x [%d], %d, %#x, %#x [%B]",
lplpBuffer,
*lplpBuffer,
lpdwBufferLength,
*lpdwBufferLength,
lpdwBufferRemaining,
*lpdwBufferRemaining,
lpdwBytesReceived,
*lpdwBytesReceived,
dwExtraSpace,
dwFlags,
lpbEof,
*lpbEof
));
INET_ASSERT((int)*lpdwBufferLength >= 0);
INET_ASSERT((int)*lpdwBufferRemaining >= 0);
INET_ASSERT((int)*lpdwBytesReceived >= 0);
#define SF_MUTEX_FLAGS (SF_RECEIVE_ALL | SF_NO_WAIT)
INET_ASSERT((dwFlags & SF_MUTEX_FLAGS) != SF_MUTEX_FLAGS);
DWORD error = DoFsm(new CFsm_SocketReceive(lplpBuffer,
lpdwBufferLength,
lpdwBufferRemaining,
lpdwBytesReceived,
dwExtraSpace,
dwFlags,
lpbEof,
this
));
DEBUG_LEAVE(error);
return error;
}
DWORD
CFsm_SocketReceive::RunSM(
IN CFsm * Fsm
)
/*++
Routine Description:
Runs next CFsm_SocketReceive state
Arguments:
Fsm - socket receive FSM
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"CFsm_SocketReceive::RunSM",
"%#x",
Fsm
));
ICSocket * pSocket = (ICSocket *)Fsm->GetContext();
CFsm_SocketReceive * stateMachine = (CFsm_SocketReceive *)Fsm;
DWORD error;
switch (Fsm->GetState()) {
case FSM_STATE_INIT:
error = pSocket->Receive_Start(stateMachine);
break;
case FSM_STATE_CONTINUE:
case FSM_STATE_ERROR:
error = pSocket->Receive_Continue(stateMachine);
break;
default:
error = ERROR_INTERNET_INTERNAL_ERROR;
Fsm->SetDone(ERROR_INTERNET_INTERNAL_ERROR);
INET_ASSERT(FALSE);
break;
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Receive_Start(
IN CFsm_SocketReceive * Fsm
)
/*++
Routine Description:
Initiates a receive request - grows the buffer if required and kicks off the
first receive operation
Arguments:
Fsm - reference to FSM controlling operation
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Receive_Start",
"{%#x [%#x/%d]} %#x(%#x [%#x], %#x [%d], %#x [%d], %#x [%d], %d, %#x, %#x [%B])",
this,
GetSocket(),
GetSourcePort(),
Fsm,
Fsm->m_lplpBuffer,
*Fsm->m_lplpBuffer,
Fsm->m_lpdwBufferLength,
*Fsm->m_lpdwBufferLength,
Fsm->m_lpdwBufferRemaining,
*Fsm->m_lpdwBufferRemaining,
Fsm->m_lpdwBytesReceived,
*Fsm->m_lpdwBytesReceived,
Fsm->m_dwExtraSpace,
Fsm->m_dwFlags,
Fsm->m_lpbEof,
*Fsm->m_lpbEof
));
CFsm_SocketReceive & fsm = *Fsm;
DWORD error = ERROR_SUCCESS;
//
// if we weren't given a buffer, but the caller told us its okay to resize
// then we allocate the initial buffer
//
if ((fsm.m_dwBufferLength == 0) || (fsm.m_dwBufferLeft == 0)) {
INET_ASSERT((fsm.m_dwBufferLength == 0) ? (fsm.m_dwBufferLeft == 0) : TRUE);
if (fsm.m_dwFlags & SF_EXPAND) {
//
// allocate a fixed memory buffer
//
//
// BUGBUG - the initial buffer size should come from the handle
// object
//
fsm.m_dwBufferLeft = DEFAULT_RECEIVE_BUFFER_INCREMENT;
if (fsm.m_dwBufferLength == 0) {
fsm.m_bAllocated = TRUE;
}
fsm.m_dwBufferLength += fsm.m_dwBufferLeft;
DEBUG_PRINT(SOCKETS,
INFO,
("resizing %#x to %d\n",
fsm.m_hBuffer,
fsm.m_dwBufferLength
));
fsm.m_hBuffer = ResizeBuffer(fsm.m_hBuffer, fsm.m_dwBufferLength, FALSE);
if (fsm.m_hBuffer == (HLOCAL)NULL) {
error = GetLastError();
INET_ASSERT(error != ERROR_SUCCESS);
fsm.m_bAllocated = FALSE;
}
} else {
//
// the caller didn't say its okay to resize
//
error = ERROR_INSUFFICIENT_BUFFER;
}
} else if (fsm.m_hBuffer == (HLOCAL)NULL) {
error = ERROR_INSUFFICIENT_BUFFER;
}
if (error == ERROR_SUCCESS) {
if (fsm.GetMappedHandleObject() != NULL) {
fsm.GetMappedHandleObject()->SetAbortHandle(this);
}
//
// keep the app informed (if requested to do so)
//
if (fsm.m_dwFlags & SF_INDICATE) {
InternetIndicateStatus(INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
}
//
// kick off the receive request. If we complete synchronously (with
// an error or successfully), then call the finish handler here
//
error = Receive_Continue(Fsm);
} else {
fsm.SetDone();
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::Receive_Continue(
IN CFsm_SocketReceive * Fsm
)
/*++
Routine Description:
Receives data from connected socket. Depending on flags settings, we will
perform a single receive, loop until we have filled the buffer and/or loop
until we have received all the data.
Arguments:
Fsm - reference to FSM controlling operation
Return Value:
DWORD
Success - ERROR_SUCCESS
ERROR_IO_PENDING
Operation will complete asynchronously
Failure -
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::Receive_Continue",
"{%#x [%#x/%d]} %#x(%#x [%#x], %#x [%d], %#x [%d], %#x [%d], %d, %#x, %#x [%B])",
this,
GetSocket(),
GetSourcePort(),
Fsm,
Fsm->m_lplpBuffer,
*Fsm->m_lplpBuffer,
Fsm->m_lpdwBufferLength,
*Fsm->m_lpdwBufferLength,
Fsm->m_lpdwBufferRemaining,
*Fsm->m_lpdwBufferRemaining,
Fsm->m_lpdwBytesReceived,
*Fsm->m_lpdwBytesReceived,
Fsm->m_dwExtraSpace,
Fsm->m_dwFlags,
Fsm->m_lpbEof,
*Fsm->m_lpbEof
));
CFsm_SocketReceive & fsm = *Fsm;
DWORD error = fsm.GetError();
INTERNET_HANDLE_OBJECT * pObject = fsm.GetMappedHandleObject();
if (fsm.IsInvalid() && fsm.GetState() == FSM_STATE_ERROR)
{
error = ERROR_INTERNET_OPERATION_CANCELLED;
goto error_exit;
}
if (error != ERROR_SUCCESS) {
goto error_exit;
}
fsm.m_lpBuffer = (LPBYTE)fsm.m_hBuffer + fsm.m_dwBytesReceived;
//
// receive some data
//
do {
if (fsm.m_pServerInfo != NULL) {
fsm.m_pServerInfo->SetLastActiveTime();
}
INET_ASSERT((int)fsm.m_dwBufferLeft > 0);
int nRead = _I_recv(m_Socket,
(char FAR *)fsm.m_lpBuffer,
(int)fsm.m_dwBufferLeft,
0
);
//
// hackorama # 95, subparagraph 13
//
// RLF 07/15/96
//
// On Win95 (wouldn't you know it?) in low-memory conditions, we can get
// into a situation where one or more pages of our receive buffer is
// filled with zeroes.
//
// The reason this happens is that the winsock VxD creates an alias to
// our buffer, locks the buffer & writes into it, then marks the alias
// dirty, but not the original buffer. If the buffer is paged out then
// back in, one or more pages are zeroed because the O/S didn't know
// they had been written to; it decides to initialize the pages with
// zeroes.
//
// We try to circumvent this by immediately probing each page (we read
// a byte then write it back).
//
// This doesn't fix the problem, just makes the window a lot smaller.
// However, apart from writing a device driver or modifying the VxD,
// there's not much else we can do
//
ProbeWriteBuffer(fsm.m_lpBuffer, fsm.m_dwBufferLeft);
if (nRead == 0) {
//
// done
//
DEBUG_PRINT(SOCKETS,
INFO,
("EOF connection %#x/port %d\n",
m_Socket,
m_SourcePort
));
fsm.m_bEof = TRUE;
break;
} else if (nRead > 0) {
DEBUG_PRINT(SOCKETS,
INFO,
("received %d bytes from socket %#x/port %d\n",
nRead,
m_Socket,
m_SourcePort
));
fsm.m_dwBytesReceived += nRead;
fsm.m_dwBytesRead += nRead;
fsm.m_lpBuffer += nRead;
fsm.m_dwBufferLeft -= nRead;
//
// if SF_RECEIVE_ALL is not set then the caller just wants us to
// perform a single receive. We're done
//
if (!(fsm.m_dwFlags & SF_RECEIVE_ALL) ) {
break;
}
//
// if we've filled the current buffer, then either we're done, or
// the caller wants us to receive the entire response, in which
// case we attempt to grow the buffer and receive the next part
// of the message. Note that we may have already received the
// entire response if it just happened to be the same size as our
// buffer
//
// BUGBUG [arthurbi] we're broken for SSL/PCT case !!!
// We need to handle expanding the buffer.
//
if (fsm.m_dwBufferLeft == 0) {
//
// BUGBUG - RLF - why are we testing for SF_DECRYPT here?
//
if (!(fsm.m_dwFlags & SF_EXPAND) || (m_dwFlags & SF_DECRYPT)) {
break;
} else {
//
// BUGBUG - the buffer increment should come from the handle
// object
//
fsm.m_dwBufferLeft = DEFAULT_RECEIVE_BUFFER_INCREMENT;
fsm.m_dwBufferLength += DEFAULT_RECEIVE_BUFFER_INCREMENT;
DEBUG_PRINT(SOCKETS,
INFO,
("resizing %#x to %d\n",
fsm.m_hBuffer,
fsm.m_dwBufferLength
));
fsm.m_hBuffer = ResizeBuffer(fsm.m_hBuffer,
fsm.m_dwBufferLength,
FALSE
);
if (fsm.m_hBuffer != NULL) {
fsm.m_lpBuffer = (LPBYTE)fsm.m_hBuffer + fsm.m_dwBytesReceived;
} else {
error = GetLastError();
INET_ASSERT(error != ERROR_SUCCESS);
fsm.m_dwBytesReceived = 0;
fsm.m_dwBufferLength = 0;
fsm.m_dwBufferLeft = 0;
}
}
}
} else {
error = _I_WSAGetLastError();
if ((error != WSAEWOULDBLOCK) || (fsm.m_dwFlags & SF_NO_WAIT)) {
//
// a real error occurred. We need to get out
//
DEBUG_PRINT(SOCKETS,
ERROR,
("recv() on socket %#x/port %d returns %d\n",
m_Socket,
m_SourcePort,
error
));
if (!(fsm.m_dwFlags & SF_NO_WAIT)) {
error = MapInternetError(error);
}
break;
}
//
// socket would block. If SF_NON_BLOCKING is set then the caller is
// expecting that we complete asynchronously. If SF_WAIT is set then
// the caller wants to force synchronous behaviour, so we wait here
// until the socket unblocks. If neither is set then the caller just
// wants us to return what we have
//
if (IsNonBlocking()) {
DEBUG_PRINT(SOCKETS,
INFO,
("recv() blocked, socket %#x/port %d\n",
m_Socket,
m_SourcePort
));
fsm.SetAction(FSM_ACTION_RECEIVE);
DWORD timeout = GetTimeoutValue(INTERNET_OPTION_RECEIVE_TIMEOUT);
if (pObject != NULL) {
if (pObject->IsFromCacheTimeoutSet()
&& (pObject->GetObjectType() == TypeHttpRequestHandle)
&& ((HTTP_REQUEST_HANDLE_OBJECT *)pObject)->CanRetrieveFromCache()) {
timeout = GetTimeoutValue(INTERNET_OPTION_FROM_CACHE_TIMEOUT);
DWORD RTT = fsm.m_pOriginServer->GetRTT();
if (RTT == 0) {
RTT = timeout;
}
timeout += RTT;
}
}
fsm.SetTimeout(timeout);
error = QueueSocketWorkItem(Fsm, m_Socket);
if (error != ERROR_SUCCESS) {
break;
}
DEBUG_PRINT(SOCKETS,
INFO,
("recv() resumed, socket %#x/port %d, returns %s\n",
m_Socket,
m_SourcePort,
InternetMapError(error)
));
} else if (fsm.m_dwFlags & SF_WAIT) {
DEBUG_PRINT(SOCKETS,
INFO,
("waiting for socket %#x/port %d to become unblocked\n",
m_Socket,
m_SourcePort
));
//error = WaitForReceive(INFINITE);
} else {
//
// return what we have (non-blocking, non-waiting read). But we
// *should* have read *something*
//
if (fsm.m_dwBytesRead == 0) {
DEBUG_PRINT(SOCKETS,
ERROR,
("bogus - 0 bytes read from non-blocking recv(). Waiting\n"
));
//
// AOL problem:
//
//error = WaitForReceive(INFINITE);
} else {
error = ERROR_SUCCESS;
break;
}
}
}
} while (error == ERROR_SUCCESS);
error_exit:
//
// get correct error based on settings
//
if (error == ERROR_IO_PENDING) {
goto done;
} else if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
} else if (IsOffline()) {
error = ERROR_INTERNET_OFFLINE;
} else if (error == ERROR_INTERNET_INTERNAL_SOCKET_ERROR) {
error = ERROR_INTERNET_CONNECTION_RESET;
}
if (pObject != NULL) {
pObject->ResetAbortHandle();
}
if (error == ERROR_SUCCESS) {
//
// inform the app that we finished, and tell it how much we received
// this time
//
if (fsm.m_dwFlags & SF_INDICATE) {
InternetIndicateStatus(INTERNET_STATUS_RESPONSE_RECEIVED,
&fsm.m_dwBytesRead,
sizeof(fsm.m_dwBytesRead)
);
}
//
// if we received the entire response and the caller specified
// SF_COMPRESS then we shrink the buffer to fit. We may end up growing
// the buffer to contain dwExtraSpace if it is not zero and we just
// happened to fill the current buffer
//
if (fsm.m_bEof && (fsm.m_dwFlags & SF_COMPRESS)) {
fsm.m_dwBufferLeft = fsm.m_dwExtraSpace;
//
// include any extra that the caller required
//
fsm.m_dwBufferLength = fsm.m_dwBytesReceived + fsm.m_dwExtraSpace;
DEBUG_PRINT(SOCKETS,
INFO,
("shrinking buffer %#x to %d (%#x) bytes (includes %d extra)\n",
fsm.m_hBuffer,
fsm.m_dwBufferLength,
fsm.m_dwBufferLength,
fsm.m_dwExtraSpace
));
fsm.m_hBuffer = ResizeBuffer(fsm.m_hBuffer,
fsm.m_dwBufferLength,
FALSE
);
INET_ASSERT((fsm.m_hBuffer == NULL)
? ((fsm.m_dwBytesReceived + fsm.m_dwExtraSpace) == 0)
: TRUE
);
}
DEBUG_PRINT(SOCKETS,
INFO,
("read %d bytes @ %#x from socket %#x/port %d\n",
fsm.m_dwBytesRead,
(LPBYTE)fsm.m_hBuffer + *fsm.m_lpdwBytesReceived,
m_Socket,
m_SourcePort
));
DEBUG_DUMP_API(SOCKETS,
"received data:\n",
(LPBYTE)fsm.m_hBuffer + *fsm.m_lpdwBytesReceived,
fsm.m_dwBytesRead
);
} else if (fsm.m_bAllocated && (fsm.m_hBuffer != NULL)) {
//
// if we failed but allocated a buffer then we need to free it (we were
// leaking this buffer if the request was cancelled)
//
fsm.m_hBuffer = FREE_MEMORY(fsm.m_hBuffer);
INET_ASSERT(fsm.m_hBuffer == NULL);
fsm.m_dwBufferLength = 0;
fsm.m_dwBufferLeft = 0;
fsm.m_dwBytesReceived = 0;
fsm.m_bEof = TRUE;
}
DEBUG_PRINT(SOCKETS,
INFO,
("returning: lpBuffer=%#x, bufferLength=%d, bufferLeft=%d, bytesReceived=%d\n",
fsm.m_hBuffer,
fsm.m_dwBufferLength,
fsm.m_dwBufferLeft,
fsm.m_dwBytesReceived
));
//
// update output parameters
//
*fsm.m_lplpBuffer = (LPVOID)fsm.m_hBuffer;
*fsm.m_lpdwBufferLength = fsm.m_dwBufferLength;
*fsm.m_lpdwBufferRemaining = fsm.m_dwBufferLeft;
*fsm.m_lpdwBytesReceived = fsm.m_dwBytesReceived;
*fsm.m_lpbEof = fsm.m_bEof;
if (error != ERROR_IO_PENDING) {
fsm.SetDone(error);
}
done:
DEBUG_LEAVE(error);
return error;
}
//
//DWORD
//ICSocket::ReceiveFrom(
// IN LPVOID lpBuffer,
// IN DWORD dwBufferLength,
// OUT LPDWORD lpdwBytesReceived,
// OUT LPSOCKADDR lpDestination OPTIONAL,
// IN OUT LPDWORD lpdwDestinationLength OPTIONAL,
// IN DWORD dwTimeout,
// IN DWORD dwWinsockFlags,
// IN DWORD dwFlags
// )
//
///*++
//
//Routine Description:
//
// Wrapper for recvfrom()
//
//Arguments:
//
// lpBuffer - pointer to buffer where data returned
//
// dwBufferLength - size of lpBuffer in bytes
//
// lpdwBytesReceived - pointer to returned number of bytes received
//
// lpDestination - pointer to returned destination address
//
// lpdwDestinationLength - IN: size of lpDestination buffer
// OUT: length of returned destination address info
//
// dwTimeout - number of milliseconds to wait for response
//
// dwWinsockFlags - flags to pass through to recvfrom()
//
// dwFlags - ICSocket flags
//
//Return Value:
//
// DWORD
// Success - ERROR_SUCCESS
//
// Failure - ERROR_INTERNET_OPERATION_CANCELLED
// The operation was cancelled by the caller
//
// ERROR_INTERNET_TIMEOUT
// The operation timed out
//
// ERROR_INTERNET_CONNECTION_RESET
// An error occurred. We approximate to connection reset
//
// WSA error
// Some other sockets error occurred
//
//--*/
//
//{
// DEBUG_ENTER((DBG_SOCKETS,
// Dword,
// "ICSocket::ReceiveFrom",
// "{%#x} %#x, %d, %#x, %#x, %#x [%d], %d, %#x, %#x",
// m_Socket,
// lpBuffer,
// dwBufferLength,
// lpdwBytesReceived,
// lpDestination,
// lpdwDestinationLength,
// lpdwDestinationLength ? *lpdwDestinationLength : 0,
// dwTimeout,
// dwWinsockFlags,
// dwFlags
// ));
//
// //INET_ASSERT(IsSocketValid());
// INET_ASSERT(lpdwBytesReceived != NULL);
//
// //
// // most ICSocket flags not allowed for this operation
// //
//
// INET_ASSERT(!(dwFlags
// & (SF_ENCRYPT
// | SF_DECRYPT
// | SF_EXPAND
// | SF_COMPRESS
// | SF_SENDING_DATA
// | SF_SCH_REDO
// )
// )
// );
//
// DWORD error = ERROR_SUCCESS;
// LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
// BOOL bStopOfflineTimer = FALSE;
// BOOL fNonBlocking;
// DWORD bytesReceived;
// INTERNET_HANDLE_OBJECT * pObject = NULL;
//
// if (lpThreadInfo == NULL) {
//
// INET_ASSERT(FALSE);
//
// error = ERROR_INTERNET_INTERNAL_ERROR;
// goto quit;
// }
//
// //
// // the socket may have already been aborted
// //
//
// if (IsAborted()) {
// error = ERROR_INTERNET_OPERATION_CANCELLED;
// goto quit;
// }
//
// //
// // if we are offline then quit now - we can't make any network requests
// //
//
// if (IsOffline()) {
// error = ERROR_INTERNET_OFFLINE;
// goto quit;
// }
//
// //
// // let another thread know the socket to cancel if it wants to kill this
// // operation
// //
//
// pObject = (INTERNET_HANDLE_OBJECT * )lpThreadInfo->hObjectMapped;
// if (pObject != NULL) {
// pObject->SetAbortHandle(this);
// }
//
// //
// // keep the app informed (if requested to do so)
// //
//
// if (dwFlags & SF_INDICATE) {
// InternetIndicateStatus(INTERNET_STATUS_RECEIVING_RESPONSE,
// NULL,
// 0
// );
// }
//
// //
// // if we are in async (== non-blocking) mode, let the async request
// // scheduler know what operation we will be waiting on
// //
//
// fNonBlocking = lpThreadInfo->IsAsyncWorkerThread;
// if (fNonBlocking) {
//
// INET_ASSERT(lpThreadInfo->lpArb != NULL);
//
// SET_ARB_SOCKET_OPERATION_TIMEOUT(lpThreadInfo->lpArb,
// m_Socket,
// RECEIVE,
// dwTimeout
// );
//
// DWORD timerError = StartOfflineTimerForArb(lpThreadInfo->lpArb);
//
// INET_ASSERT(timerError == ERROR_SUCCESS);
//
// bStopOfflineTimer = (timerError == ERROR_SUCCESS) ? TRUE : FALSE;
// }
//
// int nBytes;
//
// bytesReceived = 0;
//
// do {
//
// nBytes = _I_recvfrom(m_Socket,
// (char FAR *)lpBuffer + bytesReceived,
// dwBufferLength,
// dwWinsockFlags,
// lpDestination,
// (int FAR *)lpdwDestinationLength
// );
// if (nBytes != SOCKET_ERROR) {
//
// DEBUG_PRINT(SOCKETS,
// INFO,
// ("received %d bytes from socket %#x\n",
// nBytes,
// m_Socket
// ));
//
// INET_ASSERT(nBytes > 0);
//
// bytesReceived += nBytes;
// dwBufferLength -= nBytes;
//
// //
// // for recvfrom(), we quit as soon as we get some data
// //
//
// error = ERROR_SUCCESS;
// break;
// } else {
// error = _I_WSAGetLastError();
// if ((error == WSAEWOULDBLOCK) && fNonBlocking) {
//
// INET_ASSERT(_dwFlags & SF_NON_BLOCKING);
//
// //
// // if this function is called expedited (we expect the request
// // to complete quickly) then we test to see if it already
// // completed before switching to the async scheduler
// //
//
// BOOL switchFiber = TRUE;
//
// if (dwFlags & SF_EXPEDITED) {
// error = WaitForReceive(1);
//
// //
// // if the socket is already readable then we don't switch
// // fibers (only to virtually immediately come back here,
// // incurring a couple of thread switches
// //
//
// if (error == ERROR_SUCCESS) {
// switchFiber = FALSE;
//
// //
// // use this error to go round loop once again
// //
//
// error = WSAEWOULDBLOCK;
// }
// }
// if (switchFiber) {
//
// DEBUG_PRINT(SOCKETS,
// INFO,
// ("recvfrom(%#x) blocked\n",
// m_Socket
// ));
//
// lpThreadInfo->lpArb->Header.dwResultCode = ERROR_SUCCESS;
//
// SwitchToAsyncScheduler(m_Socket);
//
// error = lpThreadInfo->lpArb->Header.dwResultCode;
//
// DEBUG_PRINT(SOCKETS,
// INFO,
// ("recvfrom(%#x) resumed, returns %s\n",
// m_Socket,
// InternetMapError(error)
// ));
//
// if (error != ERROR_SUCCESS) {
// if (error == ERROR_INTERNET_INTERNAL_SOCKET_ERROR) {
// error = ERROR_INTERNET_CONNECTION_RESET;
// }
// } else {
//
// //
// // use this error to force another loop now we believe
// // we have the data
// //
//
// error = WSAEWOULDBLOCK;
// }
// }
// } else {
//
// //
// // real error
// //
//
// error = MapInternetError(error);
// }
// }
// } while (error == WSAEWOULDBLOCK);
//
// if (error == ERROR_SUCCESS) {
//
// DEBUG_DUMP(SOCKETS,
// "received data:\n",
// lpBuffer,
// bytesReceived
// );
//
// }
//
// if (fNonBlocking) {
//
// INET_ASSERT(lpThreadInfo->lpArb != NULL);
//
// SET_ARB_SOCKET_OPERATION(lpThreadInfo->lpArb, INVALID_SOCKET, RECEIVE);
//
// if (bStopOfflineTimer) {
// StopOfflineTimerForArb(lpThreadInfo->lpArb);
// }
// }
//
// //
// // inform the app that we finished, and tell it how much we received this
// // time
// //
//
// if ((dwFlags & SF_INDICATE) && (error == ERROR_SUCCESS)) {
// InternetIndicateStatus(INTERNET_STATUS_RESPONSE_RECEIVED,
// &bytesReceived,
// sizeof(bytesReceived)
// );
// }
//
// *lpdwBytesReceived = bytesReceived;
//
// if (pObject != NULL) {
// pObject->ResetAbortHandle();
//
// //
// // if the operation has been cancelled, then this error overrides any
// // other
// //
//
// //if (pObject->IsInvalidated()) {
// // error = pObject->GetError();
// // if (error == ERROR_SUCCESS) {
// // error = ERROR_INTERNET_OPERATION_CANCELLED;
// // }
// //}
// if (IsAborted()) {
// error = ERROR_INTERNET_OPERATION_CANCELLED;
// }
// }
//
//quit:
//
// INET_ASSERT((pObject != NULL) ? (pObject->GetAbortHandle() == NULL) : TRUE);
//
// DEBUG_LEAVE(error);
//
// return error;
//}
//
DWORD
ICSocket::DataAvailable(
OUT LPDWORD lpdwBytesAvailable
)
/*++
Routine Description:
Determines the amount of data available to be read on the socket
Arguments:
lpdwBytesAvailable - pointer to returned data available
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::DataAvailable",
"%#x",
lpdwBytesAvailable
));
//
// sanity check parameters
//
INET_ASSERT(m_Socket != INVALID_SOCKET);
INET_ASSERT(lpdwBytesAvailable != NULL);
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
DWORD error;
if (lpThreadInfo == NULL) {
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
//
// if we are offline then quit now - we can't make any network requests
//
if (IsOffline()) {
error = ERROR_INTERNET_OFFLINE;
goto quit;
}
//
// the socket may already be aborted
//
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
goto quit;
}
//
// if we're in async mode, we have to perform a zero-length receive in order
// to get the information from the socket
//
int nRead;
//
// we actually have to peek a non-zero number of bytes because on Win95,
// attempting to perform a receive of 0 bytes (to put the socket in blocked
// read mode) results in zero bytes being returned, and the socket never
// blocks
//
nRead = _I_recv(m_Socket, NULL, 0, 0);
//
// N.B. buf[] will only ever be used if there is data to peek right now
//
char buf[1];
PERF_LOG(PE_PEEK_RECEIVE_START,
m_Socket,
lpThreadInfo->ThreadId,
lpThreadInfo->hObject
);
nRead = _I_recv(m_Socket, buf, sizeof(buf), MSG_PEEK);
if (nRead == SOCKET_ERROR) {
error = _I_WSAGetLastError();
if ((error == WSAEWOULDBLOCK) && (m_dwFlags & SF_NON_BLOCKING)) {
DEBUG_PRINT(SOCKETS,
INFO,
("peek(1) blocked, socket %#x\n",
m_Socket
));
PERF_LOG(PE_PEEK_RECEIVE_END,
m_Socket,
lpThreadInfo->ThreadId,
lpThreadInfo->hObject
);
DEBUG_PRINT(SOCKETS,
INFO,
("peek(1) resumed, socket %#x, returns %s\n",
m_Socket,
InternetMapError(error)
));
if (error != ERROR_SUCCESS) {
if (error == ERROR_INTERNET_INTERNAL_SOCKET_ERROR) {
error = ERROR_INTERNET_CONNECTION_RESET;
}
}
}
/*} else if ((nRead == 0) && !(m_dwFlags & SF_NON_BLOCKING)) {
PERF_LOG(PE_PEEK_RECEIVE_END,
m_Socket,
lpThreadInfo->ThreadId,
lpThreadInfo->hObject
);
//
// nothing to peek right now. If the socket is in blocking mode then
// we wait here until there is something to receive
//
error = WaitForReceive(INFINITE);*/
} else {
PERF_LOG(PE_PEEK_RECEIVE_END,
m_Socket,
lpThreadInfo->ThreadId,
lpThreadInfo->hObject
);
//
// nRead == 0 but non-blocking, or nRead > 0
//
DEBUG_PRINT(SOCKETS,
INFO,
("peek(1) returns %d\n",
nRead
));
error = ERROR_SUCCESS;
}
if (error == ERROR_SUCCESS) {
//
// now we can get the amount from the socket
//
error = (DWORD)_I_ioctlsocket(m_Socket,
FIONREAD,
(u_long FAR *)lpdwBytesAvailable
);
//
// N.B. assumes ioctlsocket() returns 0 on success == ERROR_SUCCESS
//
if (error == SOCKET_ERROR) {
error = _I_WSAGetLastError();
} else {
DEBUG_PRINT(SOCKETS,
INFO,
("ioctlsocket(FIONREAD) returns %d\n",
*lpdwBytesAvailable
));
}
}
//
// map any sockets error to WinInet error
//
if (error != ERROR_SUCCESS) {
error = MapInternetError(error);
}
quit:
DEBUG_LEAVE(error);
return error;
}
//
//DWORD
//ICSocket::DataAvailable2(
// OUT LPVOID lpBuffer,
// IN DWORD dwBufferLength,
// OUT LPDWORD lpdwBytesAvailable
// )
//
///*++
//
//Routine Description:
//
// Determines the amount of data available to be read on the socket
//
//Arguments:
//
// lplpBuffer - pointer to pointer to buffer where data read
//
// dwBufferLength - size of the buffer
//
// lpdwBytesAvailable - pointer to returned data available
//
//
//Return Value:
//
// DWORD
// Success - ERROR_SUCCESS
//
// Failure - WSA error
//
//--*/
//
//{
// DEBUG_ENTER((DBG_SOCKETS,
// Dword,
// "ICSocket::DataAvailable2",
// "%#x, %d, %#x",
// lpBuffer,
// dwBufferLength,
// lpdwBytesAvailable
// ));
//
// //
// // sanity check parameters
// //
//
// INET_ASSERT(lpdwBytesAvailable != NULL);
//
// //
// // we're about to receive data from the socket. The amount of data currently
// // on hand must be 0
// //
//
// INET_ASSERT(*lpdwBytesAvailable == 0);
// INET_ASSERT(lpBuffer != NULL);
//
// DWORD error;
//
// //
// // new scheme: actually read the data from sockets into our buffer. This is
// // the only way on Win95 to determine the correct number of bytes available.
// // We only perform a single receive
// //
//
// DWORD bufferLeft = dwBufferLength;
// BOOL eof;
//
// error = Receive(&lpBuffer,
// &dwBufferLength,
// &bufferLeft, // don't care about this
// lpdwBytesAvailable,
// 0,
// 0,
// &eof // don't care about this either
// );
//
// DEBUG_LEAVE(error);
//
// return error;
//}
DWORD
ICSocket::WaitForReceive(
IN DWORD Timeout
)
/*++
Routine Description:
Waits until a receive socket becomes unblocked (readable)
Arguments:
Timeout - milliseconds to wait, or INFINITE
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error
sockets error
ERROR_INTERNET_TIMEOUT
Receive timed out
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::WaitForReceive",
"{%#x} %d",
m_Socket,
Timeout
));
struct fd_set read_fds;
struct fd_set except_fds;
FD_ZERO(&read_fds);
FD_ZERO(&except_fds);
FD_SET(m_Socket, &read_fds);
FD_SET(m_Socket, &except_fds);
int n;
DEBUG_PRINT(SOCKETS,
INFO,
("waiting on socket %#x\n",
m_Socket
));
TIMEVAL timeout;
LPTIMEVAL lpTimeout;
if (Timeout != INFINITE) {
timeout.tv_sec = Timeout / 1000;
timeout.tv_usec = (Timeout % 1000) * 1000;
lpTimeout = &timeout;
} else {
lpTimeout = NULL;
}
n = _I_select(0, &read_fds, NULL, &except_fds, lpTimeout);
DWORD error;
if (n == SOCKET_ERROR) {
//
// real error?
//
error = _I_WSAGetLastError();
DEBUG_PRINT(SOCKETS,
ERROR,
("select() returns %d\n",
error
));
INET_ASSERT(FALSE);
error = MapInternetError(error);
} else if (n != 0) {
if (FD_ISSET(m_Socket, &except_fds)) {
DEBUG_PRINT(SOCKETS,
ERROR,
("socket %#x exception\n",
m_Socket
));
error = ERROR_INTERNET_CONNECTION_RESET;
} else {
//
// it *must* be unblocked (i.e. readable)
//
INET_ASSERT(FD_ISSET(m_Socket, &read_fds));
DEBUG_PRINT(SOCKETS,
INFO,
("socket %#x unblocked\n",
m_Socket
));
error = ERROR_SUCCESS;
}
} else {
DEBUG_PRINT(SOCKETS,
ERROR,
("timed out\n"
));
error = ERROR_INTERNET_TIMEOUT;
}
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::AllocateQueryBuffer(
OUT LPVOID * lplpBuffer,
OUT LPDWORD lpdwBufferLength
)
/*++
Routine Description:
Allocates a query buffer for the socket
Arguments:
lplpBuffer - returned pointer to allocated query buffer
lpdwBufferLength - returned length of allocated query buffer
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_NOT_ENOUGH_MEMORY
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::AllocateQueryBuffer",
"{%#x/%d} %#x, %#x",
GetSocket(),
GetSourcePort(),
lplpBuffer,
lpdwBufferLength
));
DWORD error;
DWORD bufferLength;
DWORD size = sizeof(bufferLength);
int serr = _I_getsockopt(m_Socket,
SOL_SOCKET,
SO_RCVBUF,
(char FAR *)&bufferLength,
(int FAR *)&size
);
if (serr != SOCKET_ERROR) {
bufferLength = min(bufferLength, DEFAULT_SOCKET_QUERY_BUFFER_LENGTH);
if (bufferLength == 0) {
bufferLength = DEFAULT_SOCKET_QUERY_BUFFER_LENGTH;
}
*lplpBuffer = (LPVOID)ALLOCATE_MEMORY(LMEM_FIXED, bufferLength);
if (*lplpBuffer != NULL) {
*lpdwBufferLength = bufferLength;
error = ERROR_SUCCESS;
} else {
error = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
error = MapInternetError(_I_WSAGetLastError());
}
DEBUG_LEAVE(error);
return error;
}
//
//VOID
//ICSocket::FreeQueryBuffer(
// IN LPVOID lpBuffer
// )
//
///*++
//
//Routine Description:
//
// description-of-function.
//
//Arguments:
//
// lpBuffer -
//
//Return Value:
//
// None.
//
//--*/
//
//{
// lpBuffer = (LPVOID)FREE_MEMORY((HLOCAL)lpBuffer);
//
// INET_ASSERT(lpBuffer == NULL);
//}
//
//DWORD
//ICSocket::GetBytesAvailable(
// OUT LPDWORD lpdwBytesAvailable
// )
//
///*++
//
//Routine Description:
//
// Determines amount of data available to be read from socket
//
//Arguments:
//
// lpdwBytesAvailable - pointer to returned available length
//
//Return Value:
//
// DWORD
// Success - ERROR_SUCCESS
//
// Failure -
//
//--*/
//
//{
// DEBUG_ENTER((DBG_SOCKETS,
// Dword,
// "ICSocket::GetBytesAvailable",
// "{%#x} %#x",
// m_Socket,
// lpdwBytesAvailable
// ));
//
// //INET_ASSERT(m_Socket != INVALID_SOCKET);
// INET_ASSERT(lpdwBytesAvailable != NULL);
//
// //
// // get the amount from the socket. If the socket has been reset or shutdown
// // by the server then we expect to get an error, else 0 (== ERROR_SUCCESS)
// //
//
// DWORD error = (DWORD)_I_ioctlsocket(m_Socket,
// FIONREAD,
// (u_long FAR *)lpdwBytesAvailable
// );
// if (error == SOCKET_ERROR) {
// error = _I_WSAGetLastError();
// } else {
//
// DEBUG_PRINT(SOCKETS,
// INFO,
// ("ioctlsocket(FIONREAD) returns %d\n",
// *lpdwBytesAvailable
// ));
//
// }
//
// DEBUG_LEAVE(error);
//
// return error;
//}
//
DWORD
ICSocket::CreateSocket(
IN DWORD dwFlags,
IN int nAddressFamily,
IN int nType,
IN int nProtocol
)
/*++
Routine Description:
Opens a socket handle for this ICSocket object
Arguments:
dwFlags - flags to use for new socket
nAddressFamily - parameter to socket()
nType - parameter to socket()
nProtocol - parameter to socket()
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error mapped to INTERNET error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::CreateSocket",
"%#x, %s (%d), %s (%d), %s (%d)",
dwFlags,
MapFamily(nAddressFamily),
nAddressFamily,
MapSock(nType),
nType,
MapProto(nProtocol),
nProtocol
));
INET_ASSERT(m_Socket == INVALID_SOCKET);
int serr;
DWORD error;
DWORD dwConnFlags;
#if defined(SITARA)
//
// Only enable Sitara if we're connected via modem
//
//dprintf("create socket: IsSitara = %B, IsModemConn=%B\n",GlobalEnableSitara, GlobalHasSitaraModemConn);
if (GlobalEnableSitara && GlobalHasSitaraModemConn) {
nProtocol = (int)GetSitaraProtocol();
}
#endif
m_Socket = _I_socket(nAddressFamily, nType, nProtocol);
if (m_Socket == INVALID_SOCKET) {
DEBUG_PRINT(SOCKETS,
ERROR,
("failed to create socket\n"
));
goto socket_error;
}
DEBUG_PRINT(SOCKETS,
INFO,
("created socket %#x\n",
m_Socket
));
if (dwFlags & SF_NON_BLOCKING) {
INET_ASSERT(FALSE);
error = SetNonBlockingMode(TRUE);
if (error == ERROR_SUCCESS) {
//
// ICSocket is non-blocking socket object
//
m_dwFlags |= SF_NON_BLOCKING;
} else {
goto close_socket;
}
}
//
// bind our data socket to an endpoint, so that we know an address to
// tell the FTP server
//
struct sockaddr_storage ourDataAddr;
ourDataAddr.ss_family = (short)nAddressFamily;
if (nAddressFamily == AF_INET6) {
struct in_addr6 in6addr_any = IN6ADDR_ANY_INIT;
((LPSOCKADDR_IN6)&ourDataAddr)->sin6_port = 0;
((LPSOCKADDR_IN6)&ourDataAddr)->sin6_flowinfo = 0;
((LPSOCKADDR_IN6)&ourDataAddr)->sin6_addr = in6addr_any;
((LPSOCKADDR_IN6)&ourDataAddr)->sin6_scope_id = 0;
} else {
((LPSOCKADDR_IN)&ourDataAddr)->sin_port = 0;
((LPSOCKADDR_IN)&ourDataAddr)->sin_addr.s_addr = INADDR_ANY;
}
serr = _I_bind(m_Socket,
(PSOCKADDR)&ourDataAddr,
sizeof(ourDataAddr)
);
if (serr == SOCKET_ERROR) {
goto socket_error;
}
error = ERROR_SUCCESS;
quit:
DEBUG_LEAVE(error);
return error;
socket_error:
error = MapInternetError(_I_WSAGetLastError());
close_socket:
Close();
m_dwFlags &= ~SF_NON_BLOCKING;
goto quit;
}
DWORD
ICSocket::GetSockName(
IN PSOCKADDR psaSockName,
IN int SockNameSize
)
/*++
Routine Description:
Returns the socket address for this socket.
Arguments:
psaSocketName - pointer to sockaddr buffer to fill in.
SockNameSize - size of sockaddr buffer;
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error
--*/
{
INET_ASSERT(m_Socket != INVALID_SOCKET);
INET_ASSERT(psaSockName);
INET_ASSERT(SockNameSize >= sizeof(SOCKADDR));
int serr;
DWORD error;
serr = ERROR_SUCCESS;
error = ERROR_SUCCESS;
//
// get the address info .
//
serr = _I_getsockname(m_Socket,
psaSockName,
&SockNameSize
);
if ( serr == SOCKET_ERROR )
{
error = _I_WSAGetLastError();
}
return error;
}
DWORD
ICSocket::GetPeerName(
IN PSOCKADDR psaSockName,
IN int SockNameSize
)
/*++
Routine Description:
Returns the socket address for this socket's connected peer.
Arguments:
psaSocketName - pointer to sockaddr buffer to fill in.
SockNameSize - size of sockaddr buffer;
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error
--*/
{
INET_ASSERT(m_Socket != INVALID_SOCKET);
INET_ASSERT(psaSockName);
INET_ASSERT(SockNameSize >= sizeof(SOCKADDR));
int serr;
DWORD error;
serr = ERROR_SUCCESS;
error = ERROR_SUCCESS;
//
// get the address info .
//
serr = _I_getpeername(m_Socket,
psaSockName,
&SockNameSize
);
if ( serr == SOCKET_ERROR )
{
error = _I_WSAGetLastError();
}
return error;
}
DWORD
ICSocket::Listen(
VOID
)
{
INET_ASSERT(m_Socket != INVALID_SOCKET);
DWORD error = ERROR_SUCCESS;
//
// Listen on the socket.
//
if (_I_listen(m_Socket, 1) == SOCKET_ERROR) {
error = _I_WSAGetLastError();
}
return error;
}
DWORD
ICSocket::DirectConnect(
PSOCKADDR psaRemoteSock
)
/*++
Routine Description:
Connects a ICSocket to the remote address
Arguments:
psaRemoteSock - pointer to remote socket address (TCP/IP!)
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error mapped to INTERNET error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::DirectConnectSocket",
"{%#x} %#x",
m_Socket,
psaRemoteSock
));
INET_ASSERT(m_Socket != INVALID_SOCKET);
DWORD error;
BOOL bStopOfflineTimer = FALSE;
//
// we need the thread info for async processing
//
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
if (lpThreadInfo == NULL) {
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
//
// if we are offline then quit now - we can't make any network requests
//
if (IsOffline()) {
error = ERROR_INTERNET_OFFLINE;
goto quit;
}
BOOL isAsync;
isAsync = lpThreadInfo->IsAsyncWorkerThread;
//
// BUGBUG - this is essentially common to ConnectSocket()
//
//
// let another thread know the socket to cancel if it wants to kill
// this operation
//
INTERNET_HANDLE_OBJECT * pObject;
pObject = (INTERNET_HANDLE_OBJECT * )lpThreadInfo->hObjectMapped;
if (pObject != NULL) {
pObject->SetAbortHandle(this);
}
#if defined(UNIX) && defined(ux10)
DEBUG_PRINT(SOCKETS,
INFO,
("connecting to remote address %d.%d.%d.%d, port %d\n",
((LPBYTE)&((LPSOCKADDR_IN)psaRemoteSock)->sin_addr)[0],
((LPBYTE)&((LPSOCKADDR_IN)psaRemoteSock)->sin_addr)[1],
((LPBYTE)&((LPSOCKADDR_IN)psaRemoteSock)->sin_addr)[2],
((LPBYTE)&((LPSOCKADDR_IN)psaRemoteSock)->sin_addr)[3],
_I_ntohs(((LPSOCKADDR_IN)psaRemoteSock)->sin_port)
));
#else
DEBUG_PRINT(SOCKETS,
INFO,
("connecting to remote address %d.%d.%d.%d, port %d\n",
((LPSOCKADDR_IN)psaRemoteSock)->sin_addr.S_un.S_un_b.s_b1,
((LPSOCKADDR_IN)psaRemoteSock)->sin_addr.S_un.S_un_b.s_b2,
((LPSOCKADDR_IN)psaRemoteSock)->sin_addr.S_un.S_un_b.s_b3,
((LPSOCKADDR_IN)psaRemoteSock)->sin_addr.S_un.S_un_b.s_b4,
_I_ntohs(((LPSOCKADDR_IN)psaRemoteSock)->sin_port)
));
#endif
DWORD connectTime;
connectTime = GetTickCountWrap();
int serr;
PERF_LOG(PE_CONNECT_START,
m_Socket,
lpThreadInfo->ThreadId,
lpThreadInfo->hObject
);
if (IsSocks()) {
// BUGBUG: We only support IPv4 Socks.
serr = SocksConnect((LPSOCKADDR_IN)psaRemoteSock, sizeof(SOCKADDR_IN));
} else {
int RemoteSockSize;
if (psaRemoteSock->sa_family == AF_INET6) {
RemoteSockSize = sizeof(SOCKADDR_IN6);
} else {
RemoteSockSize = sizeof(SOCKADDR_IN);
}
serr = _I_connect(m_Socket, psaRemoteSock, RemoteSockSize);
}
if (serr != 0) {
error = _I_WSAGetLastError();
//
// if we are using non-blocking sockets then we need to wait until
// the connect has completed, or an error occurs
//
if (isAsync) {
if (error == WSAEWOULDBLOCK) {
DEBUG_PRINT(SOCKETS,
INFO,
("connect() blocked, socket %#x\n",
m_Socket
));
PERF_LOG(PE_CONNECT_END,
m_Socket,
lpThreadInfo->ThreadId,
lpThreadInfo->hObject
);
connectTime = (GetTickCountWrap() - connectTime);
DEBUG_PRINT(SOCKETS,
INFO,
("connect() resumed, socket %#x, returns %s\n",
m_Socket,
InternetMapError(error)
));
if (error != ERROR_SUCCESS) {
if (error == ERROR_INTERNET_INTERNAL_SOCKET_ERROR) {
error = ERROR_INTERNET_CANNOT_CONNECT;
}
}
} else {
DEBUG_PRINT(SOCKETS,
ERROR,
("failed to connect non-blocking socket %#x, error %d\n",
m_Socket,
error
));
}
} else {
DEBUG_PRINT(SOCKETS,
ERROR,
("failed to connect blocking socket %#x, error %d\n",
m_Socket,
error
));
}
} else {
PERF_LOG(PE_CONNECT_END,
m_Socket,
lpThreadInfo->ThreadId,
lpThreadInfo->hObject
);
connectTime = (GetTickCountWrap() - connectTime);
DEBUG_PRINT(SOCKETS,
INFO,
("socket %#x connected, time = %d mSec\n",
m_Socket,
connectTime
));
error = ERROR_SUCCESS;
}
if (error != ERROR_SUCCESS) {
error = MapInternetError(error);
}
if (pObject != NULL) {
pObject->ResetAbortHandle();
//
// if the operation has been cancelled, then this error overrides any
// other
//
if (pObject->IsInvalidated()) {
error = pObject->GetError();
if (error == ERROR_SUCCESS) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
}
}
if (IsAborted()) {
error = ERROR_INTERNET_OPERATION_CANCELLED;
}
}
quit:
INET_ASSERT((pObject != NULL) ? (pObject->GetAbortHandle() == NULL) : TRUE);
DEBUG_LEAVE(error);
return error;
}
DWORD
ICSocket::SelectAccept(
IN ICSocket & acceptSocket,
IN DWORD dwTimeout
)
/*++
Routine Description:
Wait until listening socket has connection to accept. We use the socket
handle in this ICSocket object to accept a connection & create a socket
handle in another ICSocket object (in acceptSocket)
Arguments:
acceptSocket - socket object to wait on
dwTimeout - number of milliseconds to wait
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - WSA error mapped to INTERNET error
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Dword,
"ICSocket::SelectAccept",
"%#x, %d",
&acceptSocket,
dwTimeout
));
DWORD error;
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
if (lpThreadInfo == NULL) {
INET_ASSERT(FALSE);
error = ERROR_INTERNET_INTERNAL_ERROR;
goto quit;
}
fd_set read_fds;
fd_set except_fds;
FD_ZERO(&read_fds);
FD_ZERO(&except_fds);
FD_SET(m_Socket, &read_fds);
FD_SET(m_Socket, &except_fds);
TIMEVAL timeout;
timeout.tv_sec = dwTimeout / 1000;
timeout.tv_usec = dwTimeout % 1000;
int n;
n = _I_select(0, &read_fds, NULL, &except_fds, &timeout);
if (n == 1) {
if (FD_ISSET(m_Socket, &read_fds)) {
error = ERROR_SUCCESS;
} else if (FD_ISSET(m_Socket, &except_fds)) {
error = ERROR_INTERNET_CANNOT_CONNECT;
DEBUG_PRINT(FTP,
ERROR,
("select(): listening socket %#x in error (%d)\n",
m_Socket,
error
));
INET_ASSERT(acceptSocket.m_Socket == INVALID_SOCKET);
}
} else if (n == 0) {
//
// timeout
//
error = ERROR_INTERNET_TIMEOUT;
DEBUG_PRINT(FTP,
WARNING,
("select() timed out (%d.%d)\n",
timeout.tv_sec,
timeout.tv_usec
));
INET_ASSERT(acceptSocket.m_Socket == INVALID_SOCKET);
} else {
//
// socket error
//
DEBUG_PRINT(FTP,
ERROR,
("select() returns %d\n",
_I_WSAGetLastError()
));
INET_ASSERT(acceptSocket.m_Socket == INVALID_SOCKET);
goto socket_error;
}
//
// if we have a success indication then accept the connection; it may still
// fail
//
if (error == ERROR_SUCCESS) {
acceptSocket.m_Socket = _I_accept(m_Socket, NULL, NULL);
if (acceptSocket.m_Socket != INVALID_SOCKET) {
//
// copy non-blocking indication to new socket
//
INET_ASSERT(!(m_dwFlags & SF_NON_BLOCKING));
//acceptSocket.m_dwFlags |= (m_dwFlags & SF_NON_BLOCKING);
} else {
DEBUG_PRINT(FTP,
ERROR,
("accept() returns %d\n",
error
));
goto socket_error;
}
}
quit:
DEBUG_LEAVE(error);
return error;
socket_error:
error = MapInternetError(_I_WSAGetLastError());
goto quit;
}
LPSTR
MapNetAddressToName(
IN LPSTR lpszAddress,
OUT LPSTR * lplpszMappedName
)
/*++
Routine Description:
Given a network address, tries to map it to the corresponding host name. We
consult the name resolution cache to determine this
Arguments:
lpszAddress - pointer to network address to map
lplpszMappedName - pointer to pointer to mapped name. Caller must free
Return Value:
LPSTR
Success - pointer to mapped name
Failure - NULL
--*/
{
DEBUG_ENTER((DBG_SOCKETS,
Pointer,
"MapNetAddressToName",
"%q, %#x",
lpszAddress,
lplpszMappedName
));
INET_ASSERT(lpszAddress != NULL);
INET_ASSERT(lplpszMappedName != NULL);
LPSTR lpszMappedName = NULL;
//
// now try to find the address in the cache. If it's not in the cache then
// we don't resolve it, simply return the address
//
//
// BUGBUG - if required, we need to resolve the name, but we need to know
// whether the address can be resolved on the intranet
//
//
// If getaddrinfo with the AI_NUMERICHOST flag succeeds then we were
// given a string representation of an IPv6 or IPv4 address.
//
ADDRINFO Hints;
LPADDRINFO lpAddrInfo;
memset(&Hints, 0, sizeof(struct addrinfo));
Hints.ai_flags = AI_NUMERICHOST; // Only check for address literals.
Hints.ai_family = PF_UNSPEC; // Accept any protocol family.
if (_I_getaddrinfo(lpszAddress, NULL, &Hints, &lpAddrInfo) == 0) {
//
// An IP address literal (either v4 or v6) was passed in.
// See if we have a name cached that maps to it.
//
DWORD ttl;
LPADDRINFO CachedAddrInfo;
LPRESOLVER_CACHE_ENTRY lpResolverCacheEntry;
if ((lpResolverCacheEntry=QueryResolverCache(NULL, lpAddrInfo->ai_addr, &CachedAddrInfo, &ttl))) {
INET_ASSERT(CachedAddrInfo != NULL);
if (CachedAddrInfo->ai_canonname != NULL)
lpszAddress = lpszMappedName = NewString(CachedAddrInfo->ai_canonname);
ReleaseResolverCacheEntry(lpResolverCacheEntry);
}
_I_freeaddrinfo(lpAddrInfo);
}
DEBUG_PRINT(SOCKETS,
INFO,
("mapped name is %q\n",
lpszAddress
));
DEBUG_LEAVE(lpszAddress);
*lplpszMappedName = lpszMappedName;
return lpszAddress;
}
DWORD
AUTO_PROXY_HELPER_APIS::ResolveHostName(
IN LPSTR lpszHostName,
IN OUT LPSTR lpszIPAddress,
IN OUT LPDWORD lpdwIPAddressSize
)
/*++
Routine Description:
Resolves a HostName to an IP address literal by using Winsock DNS.
Arguments:
lpszHostName - the host name that should be used.
lpszIPAddress - the output IP address as a string.
lpdwIPAddressSize - the size of the outputed IP address string.
Return Value:
DWORD
Win32 error code.
--*/
{
//
// Figure out if we're being asked to resolve a name or an address literal.
// If getaddrinfo() with the AI_NUMERICHOST flag succeeds then we were
// given a string respresentation of an IPv6 or IPv4 address. Otherwise
// we expect getaddrinfo to return EAI_NONAME.
//
DWORD dwIPAddressSize;
LPRESOLVER_CACHE_ENTRY lpResolverCacheEntry = NULL;
BOOL bResolved = FALSE;
ADDRINFO Hints;
LPADDRINFO lpAddrInfo;
DWORD error;
memset(&Hints, 0, sizeof(struct addrinfo));
Hints.ai_flags = AI_NUMERICHOST; // Only check for address literals.
Hints.ai_family = PF_UNSPEC; // Accept any protocol family.
Hints.ai_socktype = SOCK_STREAM; // Constrain results to stream socket.
Hints.ai_protocol = IPPROTO_TCP; // Constrain results to TCP.
error = _I_getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo);
if (error != EAI_NONAME) {
if (error != 0) {
if (error == EAI_MEMORY)
error = ERROR_NOT_ENOUGH_MEMORY;
else
error = ERROR_INTERNET_NAME_NOT_RESOLVED;
goto quit;
}
//
// An IP address (either v4 or v6) was passed in.
// This is precisely what we want, so if we have the room,
// just copy it back out.
//
_I_freeaddrinfo(lpAddrInfo);
dwIPAddressSize = lstrlen(lpszHostName);
if ( *lpdwIPAddressSize < dwIPAddressSize ||
lpszIPAddress == NULL )
{
*lpdwIPAddressSize = dwIPAddressSize+1;
error = ERROR_INSUFFICIENT_BUFFER;
goto quit;
}
lstrcpy(lpszIPAddress, lpszHostName);
goto quit;
}
//
// now try to find the name in the cache. If it's not in the
// cache then resolve it
//
DWORD ttl;
if (!(lpResolverCacheEntry=QueryResolverCache(lpszHostName, NULL, &lpAddrInfo, &ttl)))
{
DEBUG_PRINT(SOCKETS,
INFO,
("resolving %q\n",
lpszHostName
));
PERF_LOG(PE_NAMERES_START, 0);
Hints.ai_flags = 0; // No special treatment this time.
error = _I_getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo);
PERF_LOG(PE_NAMERES_END, 0);
DEBUG_PRINT(SOCKETS,
INFO,
("%q %sresolved\n",
lpszHostName,
(error == 0) ? "" : "NOT "
));
//
// if we successfully resolved the name then remember to add the
// information to the cache when we're done with it
//
if (error == 0) {
bResolved = TRUE;
} else {
if (error == EAI_MEMORY)
error = ERROR_NOT_ENOUGH_MEMORY;
else
error = ERROR_INTERNET_NAME_NOT_RESOLVED;
goto quit;
}
}
INET_ASSERT(lpAddrInfo != NULL);
//
// We have an addrinfo struct for lpszHostName.
// Convert its IP address into a string.
//
//
// BUGBUG: Until our caller can deal with IPv6 addresses, we'll only
// return IPv4 addresses here, regardless of what may be in the cache.
// Step through chain until we find an IPv4 address.
//
LPADDRINFO IPv4Only;
IPv4Only = lpAddrInfo;
while (IPv4Only->ai_family != AF_INET) {
IPv4Only = IPv4Only->ai_next;
if (IPv4Only == NULL) {
error = ERROR_INTERNET_NAME_NOT_RESOLVED;
goto quit;
}
}
error = _I_getnameinfo(IPv4Only->ai_addr, IPv4Only->ai_addrlen,
lpszIPAddress, *lpdwIPAddressSize, NULL, 0,
NI_NUMERICHOST);
if (error != 0) {
error = ERROR_INTERNET_NAME_NOT_RESOLVED;
}
quit:
if (lpResolverCacheEntry) {
INET_ASSERT(!bResolved);
INET_ASSERT(lpAddrInfo != NULL);
ReleaseResolverCacheEntry(lpResolverCacheEntry);
} else if (bResolved) {
INET_ASSERT(lpAddrInfo != NULL);
AddResolverCacheEntry(lpszHostName, lpAddrInfo, LIVE_DEFAULT);
}
return error;
}
BOOL
AUTO_PROXY_HELPER_APIS::IsResolvable(
IN LPSTR lpszHost
)
/*++
Routine Description:
Determines wheter a HostName can be resolved. Performs a Winsock DNS query,
and if it succeeds returns TRUE.
Arguments:
lpszHost - the host name that should be used.
Return Value:
BOOL
TRUE - the host is resolved.
FALSE - could not resolve.
--*/
{
char szDummy[] = "1111:2222:3333:4444:5555:6666:255.255.255.255%4294967295";
DWORD dwDummySize = sizeof(szDummy);
DWORD error;
error = ResolveHostName(
lpszHost,
szDummy,
&dwDummySize
);
if (error == 0)
{
return TRUE;
}
else
{
INET_ASSERT(error != ERROR_SUCCESS );
return FALSE;
}
}
DWORD
AUTO_PROXY_HELPER_APIS::GetIPAddress(
IN OUT LPSTR lpszIPAddress,
IN OUT LPDWORD lpdwIPAddressSize
)
/*++
Routine Description:
Acquires the IP address string of this client machine WININET is running on.
Arguments:
lpszIPAddress - the IP address of the machine, returned.
lpdwIPAddressSize - size of the IP address string.
Return Value:
DWORD
Win32 Error.
--*/
{
CHAR szHostBuffer[255];
int serr;
serr = _I_gethostname(
szHostBuffer,
ARRAY_ELEMENTS(szHostBuffer)-1
);
if ( serr != ERROR_SUCCESS)
{
return ERROR_INTERNET_INTERNAL_ERROR;
}
return ResolveHostName(
szHostBuffer,
lpszIPAddress,
lpdwIPAddressSize
);
}
BOOL
AUTO_PROXY_HELPER_APIS::IsInNet(
IN LPSTR lpszIPAddress,
IN LPSTR lpszDest,
IN LPSTR lpszMask
)
/*++
Routine Description:
Determines whether a given IP address is in a given dest/mask IP address.
Arguments:
lpszIPAddress - the host name that should be used.
lpszDest - the IP address dest to check against.
lpszMask - the IP mask string
Return Value:
BOOL
TRUE - the IP address is in the given dest/mask
FALSE - the IP address is NOT in the given dest/mask
--*/
{
DWORD dwDest, dwIpAddr, dwMask;
INET_ASSERT(lpszIPAddress);
INET_ASSERT(lpszDest);
INET_ASSERT(lpszMask);
dwIpAddr = _I_inet_addr(lpszIPAddress);
dwDest = _I_inet_addr(lpszDest);
dwMask = _I_inet_addr(lpszMask);
if ( dwDest == INADDR_NONE ||
dwIpAddr == INADDR_NONE )
{
INET_ASSERT(FALSE);
return FALSE;
}
if ( (dwIpAddr & dwMask) != dwDest)
{
return FALSE;
}
//
// Pass, its Matches.
//
return TRUE;
}