/*++ 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::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 #include // // 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 */ } 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; _pCurrentFsm = NULL; _lpWrapOverlappedSend = NULL; _lpWrapOverlappedRecv = NULL; 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()) { SetLinger(FALSE, 0); Shutdown(SD_BOTH); Close(); } if (_lpWrapOverlappedSend) _lpWrapOverlappedSend->Dereference(); if (_lpWrapOverlappedRecv) _lpWrapOverlappedRecv->Dereference(); 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_WINHTTP_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_WINHTTP_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_WINHTTP_INTERNAL_ERROR; Fsm->SetDone(ERROR_WINHTTP_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 attempts, or until 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; BOOL fSynchronous = FALSE; 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) // fsm.SetNextState(FSM_STATE_CONTINUE); // // 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_WINHTTP_CANNOT_CONNECT; } else { fsm.SetFunctionState(FSM_STATE_2); fsm.SetNextState(FSM_STATE_INIT); fsm.m_dwAddressIndex = -1; 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_WINHTTP_CANNOT_CONNECT; } } else if (error == ERROR_WINHTTP_NAME_NOT_RESOLVED) { fsm.SetNextState(FSM_STATE_CONTINUE); goto quit; // exit out NOW with ERROR_WINHTTP_NAME_NOT_RESOLVED, instead of CANNOT_CONNECT } } } if (error != ERROR_SUCCESS) { // // name resolution failed - done // goto quit; } // // update port for keep-alive info // 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; 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) { error = InternetIndicateStatusAddress(WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, fsm.m_pAddress->RemoteAddr.lpSockaddr, fsm.m_pAddress->RemoteAddr.iSockaddrLength ); if (error != ERROR_SUCCESS) { INET_ASSERT(error = ERROR_WINHTTP_OPERATION_CANCELLED); fsm.SetNextState(FSM_STATE_DONE); goto quit; } } // // 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; } // // Socket successfully created and bound - now, if async, // associate IOCP with this socket. // if (fsm.m_dwFlags & SF_NON_BLOCKING) { DEBUG_ENTER((DBG_API, Dword, "***CreateIoCompletionPort", "(m_Socket)%#x, (hcomp)%#x, (icsocket-compkey)%#x, %#x, (app handle)%#x, (fsm)%#x, (mapped handle obj)%#x", m_Socket, g_hCompletionPort, this, 0, fsm.GetAppHandle(), fsm, fsm.GetMappedHandleObject() )); HANDLE hCompPort = CreateIoCompletionPort( (HANDLE)m_Socket, g_hCompletionPort, (ULONG_PTR)this, 0 ); DEBUG_LEAVE(hCompPort); INET_ASSERT (hCompPort == g_hCompletionPort); if (!hCompPort) { error = GetLastError(); fsm.SetErrorState(error); goto quit; } } // // 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 we are running in blocking mode (ie synchronous case) w/ timeout, unblock for //the connect so we can enforce the timeout. if (!(fsm.m_dwFlags & SF_NON_BLOCKING) && (fsm.GetTimeout() != INFINITE)) { fSynchronous = TRUE; error = SetNonBlockingMode(TRUE); if (error != ERROR_SUCCESS) { fsm.SetErrorState(error); goto quit; } } 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 (fSynchronous && (error == WSAEWOULDBLOCK)) { int n = 1; BOOL bComplete = FALSE; struct fd_set write_fds; struct fd_set except_fds; FD_ZERO(&write_fds); FD_ZERO(&except_fds); SOCKET sock = m_Socket; // connect() & send() DEBUG_PRINT(ASYNC, INFO, ("%s FSM %#x WRITE waiting on socket %#x\n", fsm.MapType(), &fsm, sock )); FD_SET(sock, &write_fds); // all sockets are checked for exception FD_SET(sock, &except_fds); LONG timeout = fsm.GetTimeout(); struct timeval to; struct timeval* pto; if (timeout != INFINITE) { to.tv_sec = timeout / 1000; to.tv_usec = (timeout % 1000) * 1000; pto = &to; } else { pto = NULL; } DEBUG_PRINT(ASYNC, INFO, ("waiting %d mSec (%d.%06d) for select(). %d sockets\n", timeout, pto ? to.tv_sec : -1, pto ? to.tv_usec : -1, n )); n = PERF_Select(n, NULL, &write_fds, &except_fds, pto); DEBUG_PRINT(ASYNC, INFO, ("select() returns %d\n", n )); error = ERROR_WINHTTP_CANNOT_CONNECT; if (n == 0) { INET_ASSERT (pto != NULL); error = ERROR_WINHTTP_TIMEOUT; } else if (n > 0) { if (FD_ISSET(sock, &except_fds)) { DEBUG_PRINT(ASYNC, INFO, ("%s FSM %#x socket %#x exception\n", fsm.MapType(), fsm, sock )); } else if (FD_ISSET(sock, &write_fds)) { DEBUG_PRINT(ASYNC, INFO, ("%s FSM %#x socket %#x completed\n", fsm.MapType(), fsm, sock )); error = ERROR_SUCCESS; } else { INET_ASSERT (FALSE); } } //n >= 0 else { error = MapInternetError(_I_WSAGetLastError()); } //Now set the socket back to blocking mode. // If we run into an error doing that, then fall out of the connect loop // Else we'll fall into the Connect_Continue->Connect_Error codepath, which // will also account for count-outs and timeouts. DWORD dwError; if ((dwError = SetNonBlockingMode(FALSE)) != ERROR_SUCCESS) { error = MapInternetError(dwError); fsm.SetErrorState(error); goto quit; } } //fSynchronous // // 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(WINHTTP_OPTION_CONNECT_TIMEOUT); INTERNET_HANDLE_OBJECT * pObject = fsm.GetMappedHandleObject(); #ifndef WININET_SERVER_CORE //no cache if (pObject != NULL) { if (pObject->IsFromCacheTimeoutSet() && (pObject->GetObjectType() == TypeHttpRequestHandle) && ((HTTP_REQUEST_HANDLE_OBJECT *)pObject)->CanRetrieveFromCache()) { timeout = GetTimeoutValue(WINHTTP_OPTION_FROM_CACHE_TIMEOUT); DWORD connectTime = fsm.m_pServerInfo->GetConnectTime(); if (connectTime == 0) { connectTime = timeout; } timeout += connectTime; } } #endif //no cache 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_WINHTTP_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 // // // Also okay to be here for successful synchronous connect with timeout. // if (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_WINHTTP_OPERATION_CANCELLED; } 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(); 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_WINHTTP_OPERATION_CANCELLED) { error = ERROR_WINHTTP_OPERATION_CANCELLED; } else if (fsm.IsCountedOut() || fsm.IsTimedOut() // entire request timeout || (error == ERROR_WINHTTP_TIMEOUT)) { // just this request t/o DEBUG_PRINT(SOCKETS, INFO, ("counted out or timed out\n" )); // VENKATK_BUG verify this: // CANNOT_CONNECT takes precedence over TIMEOUT // if (fsm.IsTimedOut()) { error = ERROR_WINHTTP_TIMEOUT; } else if (fsm.IsCountedOut()) { error = ERROR_WINHTTP_CANNOT_CONNECT; } } else if (error != ERROR_NOT_ENOUGH_MEMORY) { // // 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_WINHTTP_TIMEOUT) && bInvalidate) || (error == ERROR_WINHTTP_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(WINHTTP_CALLBACK_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); 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(WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NULL, 0); } DWORD error = Close(); if ((error == ERROR_SUCCESS) && (dwFlags & SF_INDICATE)) { // // let the app know the connection is closed // InternetIndicateStatus(WINHTTP_CALLBACK_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)); if (Timeout == INFINITE) { Timeout = 0; } 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_WINHTTP_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_WINHTTP_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_WINHTTP_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 // switch (SocketBufferId) { case ReceiveBuffer: return GlobalSocketReceiveBufferLength; case SendBuffer: return GlobalSocketSendBufferLength; } return (DWORD)-1; } 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_WINHTTP_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_WINHTTP_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_IN address; int namelen = sizeof(address); if (_I_getsockname(GetSocket(), (LPSOCKADDR)&address, &namelen) == 0) { m_SourcePort = (INTERNET_PORT)_I_ntohs(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_WINHTTP_INTERNAL_ERROR; Fsm->SetDone(ERROR_WINHTTP_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 (error != ERROR_SUCCESS) { 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) { error = InternetIndicateStatus(WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0); if (error != ERROR_SUCCESS) { INET_ASSERT(error == ERROR_WINHTTP_OPERATION_CANCELLED); goto quit; } } fsm.StartTimer(); } while (fsm.m_dwBufferLength != 0) { // // the socket may have already been aborted // if (IsAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; break; } if (fsm.m_pServerInfo != NULL) { fsm.m_pServerInfo->SetLastActiveTime(); } DWORD dwSendSize; #define GLOBAL_MAX_SEND_LENGTH_DEFAULT (4*1024*1024) if (fsm.m_dwBufferLength > GLOBAL_MAX_SEND_LENGTH_DEFAULT) { dwSendSize = GLOBAL_MAX_SEND_LENGTH_DEFAULT; } else { dwSendSize = fsm.m_dwBufferLength; } int nSent; if (IsNonBlocking()) { if (fsm.bIOCPInited) { fsm.bIOCPInited = FALSE; if (fsm.bIOCPSuccess) { //send completed successfully DEBUG_PRINT(SOCKETS, INFO, ("WSASend sent %d bytes @ %#x to socket %#x/port %d\n", fsm.dwBytesTransferred, fsm.m_lpBuffer, m_Socket, m_SourcePort )); error = ERROR_SUCCESS; nSent = (int)fsm.dwBytesTransferred; fsm.m_iTotalSent = nSent; fsm.m_lpBuffer = (LPBYTE)fsm.m_lpBuffer + nSent; fsm.m_dwBufferLength -= nSent; continue; } else { // // map any sockets error to WinInet error and terminate this // request // //VENKATKBUG - handle retrieable errors such as WSAWOULDBLOCK DEBUG_PRINT(SOCKETS, ERROR, ("send() returns %d (%s)\n", error, InternetMapError(error) )); error = MapInternetError(error); break; } }// if fsm.bIOCPInited() WSABUF wsabuf; wsabuf.len = dwSendSize; //fsm.m_dwBufferLength; wsabuf.buf = (char FAR *)fsm.m_lpBuffer; int nError; if (!_lpWrapOverlappedSend) { _lpWrapOverlappedSend = New CWrapOverlapped(); if (!_lpWrapOverlappedSend) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } } CWrapOverlapped* lpWrapOverlapped = _lpWrapOverlappedSend; LPWSAOVERLAPPED lpOverlapped = _lpWrapOverlappedSend->GetOverlapped(); memset(lpOverlapped, 0, sizeof(WSAOVERLAPPED)); CFsm* pFsmOld = GetAndSetCurrentFsm(&fsm); INET_ASSERT (pFsmOld == NULL); fsm.bIOCPInited = TRUE; DEBUG_PRINT(SOCKETS, INFO, ("calling WSASend() blocked, socket %#x, port %d\n", m_Socket, m_SourcePort )); fsm.SetAction(FSM_ACTION_SEND); DWORD timeout = GetTimeoutValue(WINHTTP_OPTION_SEND_TIMEOUT); if (timeout != INFINITE) { fsm.SetTimeout(timeout); fsm.SetOnAsyncList(TRUE); error = QueueSocketWorkItem(Fsm, m_Socket); INET_ASSERT (error == ERROR_IO_PENDING); if (error != ERROR_IO_PENDING) { // 2 causes - both irrecoverable // 1. no global pointer to ICAsyncThread // OR 2. no threadinfo or SelectThread. // so bail! GetAndSetCurrentFsm(NULL); fsm.bIOCPInited = FALSE; fsm.SetOnAsyncList(FALSE); fsm.SetErrorState(error); goto quit; } } _lpWrapOverlappedSend->Reference(); // to keep this ICSocket=>the Overlapped struct valid beyong the WSASend() call. _lpWrapOverlappedSend->Reference(); // to make sure it stays alive for the IOCP to get the fsm off it. DEBUG_ENTER((DBG_API, Dword, "***WSASend", "(m_Socket)%#x, (wsabuf.buf)%#x, (wsabuf.len)%#x, (this)%#x, (overlapped)%#x, (fsm)%#x", m_Socket, wsabuf.buf, wsabuf.len, this, lpOverlapped, &fsm )); nError = _I_WSASend(m_Socket, &wsabuf, 1, (LPDWORD)&nSent, 0, lpOverlapped, NULL); DEBUG_LEAVE(nError); lpWrapOverlapped->Dereference(); // release the WSASend reference. DEBUG_PRINT(SOCKETS, ERROR, ("WSASend() returns %d (%s) with nSent=%d bytes\n", nError, InternetMapError(nError), nSent )); if (nError == 0) { #if INET_DEBUG InterlockedIncrement(&g_cWSACompletions); #endif error = ERROR_IO_PENDING; break; } else { INET_ASSERT (nError == SOCKET_ERROR); error = _I_WSAGetLastError(); if (error == WSA_IO_PENDING) { #if INET_DEBUG InterlockedIncrement(&g_cWSACompletions); #endif error = ERROR_IO_PENDING; break; } else { // no IOCompletion here. // VENKATKBUG: remove this assert later, only informational. // DWORD dwError = error; //dummy for debugging // INET_ASSERT (FALSE); if (fsm.HasTimeout()) { if (!RemoveFsmFromAsyncList(&fsm)) { //failure! the select thread already enforced timeout and updated state //informational assert. //VENKATK_BUG-enable later INET_ASSERT (FALSE && "COOL: select enforced timeout"); } } GetAndSetCurrentFsm(NULL); lpWrapOverlapped->Dereference(); // release the IOCP-based reference. // // 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_WINHTTP_OPERATION_CANCELLED; break; } // // map any sockets error to WinInet error and terminate this // request // //VENKATKBUG - handle retrieable errors such as WSAWOULDBLOCK error = MapInternetError(error); break; } }//if! nError == 0 } else// if IsNonBlocking() { nSent = _I_send(m_Socket, (char FAR *)fsm.m_lpBuffer, dwSendSize, //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_WINHTTP_OPERATION_CANCELLED; break; } else { error = _I_WSAGetLastError(); // // map any sockets error to WinHttp error and terminate this // request // DEBUG_PRINT(SOCKETS, ERROR, ("send() returns %d (%s)\n", error, InternetMapError(error) )); error = MapInternetError(error); break; } }// if! nSent!=SOCKET_ERROR }// if! IsNonBlocking() }// while fsm.m_dwBufferLength != 0 quit: if (error != ERROR_IO_PENDING) { fsm.StopTimer(); if (fsm.GetMappedHandleObject() != NULL) { fsm.GetMappedHandleObject()->ResetAbortHandle(); } if (IsAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; } else if (error == ERROR_SUCCESS) { if (fsm.m_dwFlags & SF_INDICATE) { INT iTotalSent = fsm.m_iTotalSent; InternetIndicateStatus(WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &iTotalSent, sizeof(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_WINHTTP_OPERATION_CANCELLED // The operation was cancelled by the caller // // ERROR_WINHTTP_TIMEOUT // The operation timed out // // ERROR_WINHTTP_CONNECTION_ERROR // 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 (IsAborted()) { // error = ERROR_WINHTTP_OPERATION_CANCELLED; // goto quit; // } // // if (lpThreadInfo == NULL) { // // INET_ASSERT(FALSE); // // error = ERROR_WINHTTP_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) { // } // } 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_WINHTTP_OPERATION_CANCELLED; // // } // //} // if (IsAborted()) { // error = ERROR_WINHTTP_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_WINHTTP_INTERNAL_ERROR; Fsm->SetDone(ERROR_WINHTTP_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) { error = InternetIndicateStatus(WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0); if (error != ERROR_SUCCESS) { INET_ASSERT(error == ERROR_WINHTTP_OPERATION_CANCELLED); fsm.SetError(error); } } // // 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 (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; if (IsNonBlocking()) { if (fsm.bIOCPInited) { fsm.bIOCPInited = FALSE; nRead = fsm.dwBytesTransferred; if (fsm.bIOCPSuccess) { 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; } } }// if fsm.m_dwBufferLeft == 0 }// if nRead >= 0 }// if fsm.bIOCPSuccess else { error = fsm.dwIOCPError; // a real error occurred. We need to get out // error = MapInternetError(error); //VENKATKBUG_ remove this assert later - only informational INET_ASSERT (FALSE && "IOCPError"); break; }// if! fsm.bSuccess continue; //for any fall-thrus. } // if fsm.bIOCPInited int nError; WSABUF wsabuf; wsabuf.len = fsm.m_dwBufferLeft; wsabuf.buf = (char FAR *)fsm.m_lpBuffer; DWORD dwFlags = 0; if (!_lpWrapOverlappedRecv) { _lpWrapOverlappedRecv = New CWrapOverlapped(); if (!_lpWrapOverlappedRecv) { error = ERROR_NOT_ENOUGH_MEMORY; fsm.SetErrorState(error); goto error_exit; } } CWrapOverlapped* lpWrapOverlapped = _lpWrapOverlappedRecv; LPWSAOVERLAPPED lpOverlapped = _lpWrapOverlappedRecv->GetOverlapped(); memset(lpOverlapped, 0, sizeof(WSAOVERLAPPED)); CFsm* pFsmOld = GetAndSetCurrentFsm(&fsm); INET_ASSERT (pFsmOld == NULL); fsm.bIOCPInited = TRUE; fsm.SetAction(FSM_ACTION_RECEIVE); DWORD timeout = GetTimeoutValue(WINHTTP_OPTION_RECEIVE_TIMEOUT); if (timeout != INFINITE) { fsm.SetTimeout(timeout); fsm.SetOnAsyncList(TRUE); error = QueueSocketWorkItem(Fsm, m_Socket); INET_ASSERT (error == ERROR_IO_PENDING); if (error != ERROR_IO_PENDING) { // 2 causes - both irrecoverable // 1. no global pointer to ICAsyncThread // OR 2. no threadinfo or SelectThread. // so bail! fsm.bIOCPInited = FALSE; GetAndSetCurrentFsm(NULL); fsm.SetOnAsyncList(FALSE); fsm.SetErrorState(error); goto error_exit; } } _lpWrapOverlappedRecv->Reference(); // to keep this ICSocket=>the Overlapped struct valid beyong the WSARecv() call. _lpWrapOverlappedRecv->Reference(); // to make sure it stays alive for the IOCP to get the fsm off it. DEBUG_PRINT(SOCKETS, INFO, ("calling WSARecv() blocked, socket %#x/port %d\n", m_Socket, m_SourcePort )); DEBUG_ENTER((DBG_API, Dword, "***WSARecv", "(m_Socket)%#x, (wsabuf.buf)%#x, (wsabuf.len)%#x, (this)%#x, (overlapped)%#x, (fsm)%#x", m_Socket, wsabuf.buf, wsabuf.len, this, lpOverlapped, &fsm )); nError = _I_WSARecv(m_Socket, &wsabuf, 1, (LPDWORD)&nRead, &dwFlags, lpOverlapped, NULL); DEBUG_LEAVE(nError); lpWrapOverlapped->Dereference(); // release the first reference. DEBUG_PRINT(SOCKETS, ERROR, ("WSARecv() returns %d (%s) with nRead=%d bytes\n", nError, InternetMapError(nError), nRead )); //VENKATKBUG - omitting hackorama, but may need to put it in just the same. if (nError == 0) { #if INET_DEBUG InterlockedIncrement(&g_cWSACompletions); #endif error = ERROR_IO_PENDING; break; } else // if! nError == 0 { INET_ASSERT (nError == SOCKET_ERROR); error = _I_WSAGetLastError(); if (error == WSA_IO_PENDING) { #if INET_DEBUG InterlockedIncrement(&g_cWSACompletions); #endif error = ERROR_IO_PENDING; break; } else { // no IOCompletion here. // VENKATKBUG_ strictly informational assert - remove later. // DWORD dwError = error; //dummy for debugging // INET_ASSERT (FALSE); if (fsm.HasTimeout()) { if (!RemoveFsmFromAsyncList(&fsm)) { //failure! the select thread already enforced timeout and updated state //informational assert. //VENKATK_BUG-enable later INET_ASSERT (FALSE && "COOL: select enforced timeout"); } } lpWrapOverlapped->Dereference(); // release the IOCP-based reference. GetAndSetCurrentFsm(NULL); //cannot handle SF_NO_WAIT and SF_WAIT error = MapInternetError(error); //VENKATKBUG - retry for certain error types. break; } } // if! nError == 0 } else //if IsNonBlocking() { 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; } } }//if fsm.m_dwBufferLeft == 0 } else //if nRead >= 0 { if (IsAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; break; } else { error = _I_WSAGetLastError(); // // 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 )); error = MapInternetError(error); break; } }//if! nRead >= 0 }// if! IsNonBlocking() } while (error == ERROR_SUCCESS); error_exit: // // get correct error based on settings // if (error == ERROR_IO_PENDING) { goto done; } else if (IsAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; } 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) { DWORD dwBytesRead = fsm.m_dwBytesRead; InternetIndicateStatus(WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &dwBytesRead, sizeof(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_WINHTTP_OPERATION_CANCELLED // The operation was cancelled by the caller // // ERROR_WINHTTP_TIMEOUT // The operation timed out // // ERROR_WINHTTP_CONNECTION_ERROR // 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_WINHTTP_INTERNAL_ERROR; // goto quit; // } // // // // // the socket may have already been aborted // // // // if (IsAborted()) { // error = ERROR_WINHTTP_OPERATION_CANCELLED; // 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) { // } 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_WINHTTP_OPERATION_CANCELLED; // // } // //} // if (IsAborted()) { // error = ERROR_WINHTTP_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_WINHTTP_INTERNAL_ERROR; goto quit; } // // the socket may already be aborted // if (IsAborted()) { error = ERROR_WINHTTP_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) )); } /*} 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_WINHTTP_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_WINHTTP_CONNECTION_ERROR; } 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_WINHTTP_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; 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 // SOCKADDR_IN ourDataAddr; ourDataAddr.sin_family = AF_INET; *((long *)&ourDataAddr.sin_addr) = INADDR_ANY; ourDataAddr.sin_port = 0; 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( PSOCKADDR psaSockName ) { INET_ASSERT(m_Socket != INVALID_SOCKET); INET_ASSERT(psaSockName); int serr; int cbAddrLen; DWORD error; serr = ERROR_SUCCESS; error = ERROR_SUCCESS; // // get the address info . // cbAddrLen = sizeof(SOCKADDR_IN); serr = _I_getsockname(m_Socket, psaSockName, &cbAddrLen ); 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_WINHTTP_INTERNAL_ERROR; 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()) { serr = SocksConnect((LPSOCKADDR_IN)psaRemoteSock, sizeof(SOCKADDR_IN)); } else { serr = _I_connect(m_Socket, psaRemoteSock, sizeof(SOCKADDR_IN)); } 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) )); } 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_WINHTTP_OPERATION_CANCELLED; } } if (IsAborted()) { error = ERROR_WINHTTP_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_WINHTTP_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_WINHTTP_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 { // Fix PREFIX warning (uninitialized error variable); this case should not happen. error = ERROR_WINHTTP_INTERNAL_ERROR; } } else if (n == 0) { // // timeout // error = ERROR_WINHTTP_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 INTERNET_HANDLE_OBJECT* pSessionObject, 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 // DWORD ipAddr = _I_inet_addr(lpszAddress); // // inet_addr() shouldn't fail - we should have called IsNetAddress() already // //INET_ASSERT(ipAddr != INADDR_NONE); if (ipAddr != INADDR_NONE) { LPHOSTENT lpHostent; DWORD ttl; if (pSessionObject && pSessionObject->GetResolverCache()->GetResolverCacheList() && QueryHostentCache(pSessionObject->GetResolverCache()->GetResolverCacheList(), NULL, (LPBYTE)&ipAddr, &lpHostent, &ttl)) { INET_ASSERT(lpHostent != NULL); lpszAddress = lpszMappedName = NewString(lpHostent->h_name); ReleaseHostentCacheEntry(pSessionObject->GetResolverCache()->GetResolverCacheList(), lpHostent); } } DEBUG_PRINT(SOCKETS, INFO, ("mapped name is %q\n", lpszAddress )); DEBUG_LEAVE(lpszAddress); *lplpszMappedName = lpszMappedName; return lpszAddress; }