text: /*++
  
  Copyright (C) Microsoft Corporation, 1996 - 1999
  
  Module Name:
  
      wstrans.cxx
  
  Abstract:
  
      Winsock connection transport interface.
  
  Author:
  
      Mario Goertzel    [MarioGo]
  
  
  Revision History:
  
      MarioGo     3/18/1996    Bits 'n pieces
      MarioGo     12/1997      Async and client parts
      KamenM      Aug 2/2000   IPv6 Support added - rewrote parts of it
  
  --*/
  
  #include <precomp.hxx>
  #include <CharConv.hxx>
  
  //
  // Globals
  //
  
  BOOL fWinsockLoaded = FALSE;
  
  const WS_TRANS_INFO WsTransportTable[] =
      // indexed by protocol ID
  {
  
      {
      0
      },
  
      // TCP
      {
      AF_INET,
      SOCK_STREAM,
      IPPROTO_TCP,
      sizeof(SOCKADDR_IN),
      FALSE,
      TRUE,
      TRUE, TRUE, FALSE, FALSE
      },
  
  #ifdef SPX_ON
      // SPX
      {
      AF_IPX,
      SOCK_STREAM,
      NSPROTO_SPXII,
      sizeof(SOCKADDR_IPX),
      FALSE,
      FALSE,
      FALSE, FALSE, FALSE, FALSE
      },
  #else
      {
      0,
      0,
      0,
      0,
      0, 0, 0
      },
  
  #endif
  
      // NMP - not winsock.
      {
      0,
      0,
      0,
      0,
      0, 0, 0
      },
  
  #ifdef NETBIOS_ON
      // NBF
      {
      AF_NETBIOS,
      SOCK_SEQPACKET,
      -1, // Protocol is -1*(LANA),
      sizeof(SOCKADDR_NB),
      TRUE,
      FALSE,
      FALSE, FALSE, FALSE, FALSE
      },
  
      // NBT
      {
      AF_NETBIOS,
      0,
      -1, // Protocol is -1*(LANA)
      sizeof(SOCKADDR_NB),
      TRUE,
      FALSE,
      FALSE, FALSE, FALSE, FALSE
      },
  
      // NBI
      {
      AF_NETBIOS,
      SOCK_SEQPACKET,
      -1, // Protocol is -1*(LANA)
      sizeof(SOCKADDR_NB),
      TRUE,
      FALSE,
      FALSE, FALSE, FALSE, FALSE
      },
  #else
      // NBF
      {
      0,
      0,
      0,
      0,
      0, 0, 0
      },
  
      // NBT
      {
      0,
      0,
      0,
      0,
      0, 0, 0
      },
  
      // NBI
      {
      0,
      0,
      0,
      0,
      0, 0, 0
      },
  #endif
  
  #ifdef APPLETALK_ON
      // DSP
      {
      AF_APPLETALK,
      SOCK_RDM,
      ATPROTO_ADSP,
      sizeof(SOCKADDR_AT),
      FALSE,
      FALSE,
      FALSE, FALSE, FALSE, FALSE
      },
  #else
      // DSP
      {
      0,
      0,
      0,
      0,
      0, 0, 0
      },
  #endif
  
      // SPP
      {
      0,
      0,
      0,
      0,
      0, 0, 0
      },
  
      // HTTP
      {
      AF_INET,
      SOCK_STREAM,
      IPPROTO_TCP,
      sizeof(SOCKADDR_IN),
      FALSE,
      TRUE,
      TRUE, TRUE, FALSE, FALSE
      },
  
      // UDP
      {
      AF_INET,
      SOCK_DGRAM,
      IPPROTO_UDP,
      sizeof(SOCKADDR_IN),
      FALSE,
      FALSE,
      FALSE, FALSE, FALSE, FALSE
      },
  
  #ifdef IPX_ON
      // IPX
      {
      AF_IPX,
      SOCK_DGRAM,
      NSPROTO_IPX,
      sizeof(SOCKADDR_IPX),
      FALSE,
      FALSE,
      FALSE, FALSE, FALSE, FALSE
      },
  #else
      // IPX
      {
      0,
      0,
      0,
      0,
      0,
      0,
      0, 0, 0, 0
      },
  #endif
  
      // CDP (Cluster datagram protocol)
      {
      AF_CLUSTER,
      SOCK_DGRAM,
      CLUSPROTO_CDP,
      sizeof(SOCKADDR_CLUSTER),
      FALSE,
      FALSE,
      FALSE, FALSE, FALSE, FALSE
      },
  
      // MSMQ - not winsock.
      {
      0,
      0,
      0,
      0,
      0, 0, 0
      },
  
      // TCP over IPv6
      {
      AF_INET6,
      SOCK_STREAM,
      IPPROTO_TCP,
      sizeof(SOCKADDR_IN6),
      FALSE,
      TRUE,
      TRUE, TRUE, FALSE, FALSE
      },
  
      // HTTPv2
      {
      AF_INET,
      SOCK_STREAM,
      IPPROTO_TCP,
      sizeof(SOCKADDR_IN),
      FALSE,      // fNetbios
      FALSE,      // fCheckShutdowns
      TRUE,       // fSetNoDelay
      TRUE,       // fSetKeepAlive
      FALSE,      // fSetRecvBuffer
      FALSE       // fSetSendBuffer
      },
  
  };
  
  const DWORD cWsTransportTable = sizeof(WsTransportTable)/sizeof(WS_TRANS_INFO);
  
  const DWORD cTcpTimeoutDefault = 120 * 60 * 1000; // Normal TCP/IP timeout, 120 minutes
  
  const UUID WS_ADDRESS::ExtensionFunctionsUuids[] = {WSAID_ACCEPTEX, WSAID_GETACCEPTEXSOCKADDRS};
  const int WS_ADDRESS::AcceptExFunctionId = 0;
  const int WS_ADDRESS::GetAcceptExSockAddressFunctionId = 1;
  
  #define FIRST_EXTENSION_FUNCTION_CODE  (WS_ADDRESS::AcceptExFunctionId)
  #define LAST_EXTENSION_FUNCTION_CODE  (WS_ADDRESS::GetAcceptExSockAddressFunctionId)
  
  inline BOOL IsNetbiosProtocol(PROTOCOL_ID id)
  {
  #ifdef NETBIOS_ON
      return ((id == NBT) || (id == NBF) || (id == NBI));
  #else
      return FALSE;
  #endif
  }
  
  void 
  TCPResolverHint::GetResolverHint (
      OUT BOOL *fIPv4Hint,
      OUT WS_SOCKADDR *sa
      )
  /*++
  
  Routine Description:
  
      Retrieves the resolver hint from the runtime supplied hint
      (the this object).
  
  Arguments:
  
      fIPv4Hint - on output true if the store hint was about IPv4
      sa - on output, the IP address is retrieved from the hint
          and stored in sa.
  
  Return Value:
  
  --*/
  {
      SOCKADDR_IN6 *IPv6Address = (SOCKADDR_IN6 *)sa;
  
      *fIPv4Hint = fIPv4HintValid;
      if (fIPv4HintValid)
          {
          ((SOCKADDR_IN *)sa)->sin_addr.s_addr = u.IPv4Hint;
          }
      else
          {
          IPv6Address->sin6_flowinfo = 0;
          *((u_long *)(IPv6Address->sin6_addr.s6_addr)    ) = *((u_long *)(u.IPv6Hint.u.Word)    );
          *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 1) = *((u_long *)(u.IPv6Hint.u.Word) + 1);
          *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 2) = *((u_long *)(u.IPv6Hint.u.Word) + 2);
          *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 3) = *((u_long *)(u.IPv6Hint.u.Word) + 3);
          IPv6Address->sin6_scope_id = 0;
          }
  }
  
  void
  TCPResolverHint::SetResolverHint (
      IN BOOL fIPv4Hint,
      IN WS_SOCKADDR *sa
      )
  /*++
  
  Routine Description:
  
      Sets the resolver hint in the runtime supplied hint
      (the this object).
  
  Arguments:
  
      fIPv4Hint - true if the stored hint is about IPv4
      sa - the IP address is retrieved from sa
          and is stored in the hint.
  
  Return Value:
  
  --*/
  {
      SOCKADDR_IN6 *IPv6Address = (SOCKADDR_IN6 *)sa;
  
      fIPv4HintValid = fIPv4Hint;
      if (fIPv4HintValid)
          {
          u.IPv4Hint = ((SOCKADDR_IN *)sa)->sin_addr.s_addr;
          }
      else
          {
          *((u_long *)(u.IPv6Hint.u.Word)    ) = *((u_long *)(IPv6Address->sin6_addr.s6_addr)    );
          *((u_long *)(u.IPv6Hint.u.Word) + 1) = *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 1);
          *((u_long *)(u.IPv6Hint.u.Word) + 2) = *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 2);
          *((u_long *)(u.IPv6Hint.u.Word) + 3) = *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 3);
          }
  }
  
  BOOL WS_ADDRESS::GetExtensionFunctionPointers(SOCKET sock)
  {
      int i;
  
      for (i = FIRST_EXTENSION_FUNCTION_CODE; i <= LAST_EXTENSION_FUNCTION_CODE; i ++)
          {
          if (GetExtensionFunctionPointerForFunction(sock, i) == FALSE)
              return FALSE;
          }
      return TRUE;
  }
  
  BOOL WS_ADDRESS::GetExtensionFunctionPointerForFunction(SOCKET sock, int nFunctionCode)
  {
      DWORD dwBytesReturned;
      int retval;
  
      ASSERT(nFunctionCode >= FIRST_EXTENSION_FUNCTION_CODE);
      ASSERT(nFunctionCode <= LAST_EXTENSION_FUNCTION_CODE);
      ASSERT(sizeof(ExtensionFunctionPointers)/sizeof(ExtensionFunctionPointers[0]) == (LAST_EXTENSION_FUNCTION_CODE - FIRST_EXTENSION_FUNCTION_CODE + 1));
  
      retval = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, (void *) &ExtensionFunctionsUuids[nFunctionCode],
          sizeof(UUID), (void *) &ExtensionFunctionPointers[nFunctionCode], sizeof(void *), &dwBytesReturned,
          NULL, NULL);
  
      if (retval == SOCKET_ERROR)
          return FALSE;
  
      ASSERT(dwBytesReturned == sizeof(void *));
      return TRUE;
  }
  
  
  //
  // General winsock interfaces
  //
  
  RPC_STATUS WS_CONNECTION::Abort(void)
  /*++
  
  Routine Description:
  
      Closes a connection, will be called only before WS_Close() and
      maybe called by several threads at once.  It must also handle
      the case where another thread is about to start IO on the connection.
  
  Arguments:
  
      Connection - pointer to a server connection object to abort.
  
  Return Value:
  
      RPC_S_OK
  
  --*/
  
  {
      if (InterlockedIncrement(&fAborted) != 1)
          {
          // Another thread beat us to it. Normal during
          // a call to WS_Close.
          return(RPC_S_OK);
          }
  
      I_RpcLogEvent(SU_TRANS_CONN, EV_ABORT, this, ULongToPtr(GetLastError()), 0, 1, 2);
  
      // Wait for any threads which are starting IO to do so.
      while(IsIoStarting())
          Sleep(1);
  
      RTL_SOFT_ASSERT(fAborted != 0 && IsIoStarting() == 0);
  
      if (type & SERVER)
          {
          ASSERT(pAddress != NULL);
          }
  
      if (Conn.Socket)
          {
          closesocket(Conn.Socket);
          Conn.Socket = 0;
          }
  
      return(RPC_S_OK);
  }
  
  
  VOID
  WS_DeactivateAddress (
      IN WS_ADDRESS *pAddress
      )
  /*++
  Function Name:WS_DeactivateAddress
  
  Parameters:
  
  Note:
  
      Doesn't deal with multiple transport addresses/runtime address
      case in netbios or TCP/IP bound to a subset of NICs case. These
      cases currently don't PnP.
  
  Description:
  
  Returns:
  
  --*/
  {
      switch (pAddress->id)
          {
          case TCP:
          case TCP_IPv6:
          case HTTP:
  #ifdef SPX_ON
          case SPX:
  #endif
  #ifdef APPLETALK_ON
          case DSP:
  #endif
              break;
  
          default:
              //
              // Don't deactivate the other guys
              //
  #ifdef NETBIOS_ON
              ASSERT((pAddress->id == NMP)
                      || (pAddress->id == NBF)
                      || (pAddress->id == NBT)
                      || (pAddress->id == NBI)
                      || (pAddress->id == CDP)
                      );
  #else
              ASSERT((pAddress->id == NMP)
                      || (pAddress->id == CDP)
                      );
  #endif
              return;
          }
  
      if (InterlockedIncrement(&pAddress->fAborted) != 1)
          {
          return;
          }
  
      if (pAddress->ListenSocket)
          {
          closesocket(pAddress->ListenSocket);
          pAddress->ListenSocket = 0;
          }
  
      if (pAddress->ConnectionSocket)
          {
          closesocket(pAddress->ConnectionSocket);
          pAddress->ConnectionSocket = 0;
          }
  }
  
  RPC_STATUS
  WS_ServerListenCommon (
      IN WS_ADDRESS *pAddress,
      IN BOOL fResetAddressListEntries = TRUE, OPTIONAL
      IN BOOL fAddToProtocolList = TRUE OPTIONAL
      );
  
  USHORT
  WS_GetPortForTCPAddressOnAddressRestart (
      IN WS_ADDRESS *pAddress
      )
  /*++
  Function Name: WS_GetPortForTCPAddressOnAddressRestart
  
  Parameters:
      pAddress - the address for which we need to get the port
  
  Description:
      When an address is restarted and it happens to be a TCP
      address, we need to call this function to get the port to
      be used. This is necessary so that if we are in a dual
      transport configuration with an active address we can get
      the port from the other address in order to maintain
      consistency
  
  Returns:
      the port number or 0 (means no known ports or not a dual 
      transport configuration)
  
  --*/
  {
      WS_ADDRESS *NextAddress;
      USHORT PortNumber = 0;
  
      ASSERT((pAddress->id == TCP) || (pAddress->id == TCP_IPv6));
      ASSERT(pAddress->fDynamicEndpoint);
  
      if (pAddress->pFirstAddress != NULL)
          {
          NextAddress = (WS_ADDRESS *)pAddress->pFirstAddress;
          }
      else
          NextAddress = pAddress;
  
  
      while (NextAddress != NULL)
          {
          ASSERT(NextAddress->fDynamicEndpoint);
          ASSERT((NextAddress->id == TCP) || (NextAddress->id == TCP_IPv6));
          if (!NextAddress->fAborted)
              {
              PortNumber = RpcpGetIpPort(&NextAddress->ListenAddr);
              ASSERT(PortNumber != 0);
              break;
              }
          NextAddress = (WS_ADDRESS *)NextAddress->pNextAddress;
          }
  
      return PortNumber;
  }
  
  
  RPC_STATUS
  WS_ReactivateAddress (
      IN WS_ADDRESS *pAddress,
      IN BOOL fResetAddressListEntries OPTIONAL
      )
  /*++
  Function Name:WS_ReactivateAddress
  
  Parameters:
  
  Description:
  
  Returns:
  
  --*/
  {
      RPC_STATUS Status;
      WS_SOCKADDR *sockaddr = &(pAddress->ListenAddr);
  
      //
      // If the endpoint is dynamic, clear out the endpoint
      //
      switch (pAddress->id)
          {
          case TCP:
          case TCP_IPv6:
              if (pAddress->fDynamicEndpoint)
                  {
                  RpcpSetIpPort(sockaddr, WS_GetPortForTCPAddressOnAddressRestart(pAddress));
                  }
              break;
  
          case HTTP:
              if (pAddress->fDynamicEndpoint)
                  {
                  RpcpSetIpPort(sockaddr, 0);
                  }
              break;
  
  #ifdef SPX_ON
          case SPX:
              if (pAddress->fDynamicEndpoint)
                  {
                  sockaddr->ipxaddr.sa_socket = 0;
                  }
              break;
  #endif
  
  #ifdef APPLETALK_ON
          case DSP:
              // Don't need to null out the endpoint
              break;
  #endif
  
          default:
              VALIDATE(pAddress->id)
                  {
                  NMP,
  #ifdef NETBIOS_ON
                  NBF,
                  NBT,
                  NBI,
  #endif
                  CDP
                  } END_VALIDATE;
              //
              // Don't reactivate the other guys
              //
              return RPC_S_OK;
          }
  
      // Pass in fAddToProtocolList = FALSE since the object is already in the protocol list.
      Status = WS_ServerListenCommon (pAddress, fResetAddressListEntries, FALSE);
      if (Status == RPC_S_OK)
          {
          pAddress->fAborted = 0;
          pAddress->GetExtensionFunctionPointers(pAddress->ListenSocket);
          }
      else if (Status == RPC_P_ADDRESS_FAMILY_INVALID)
          {
          Status = RPC_S_PROTSEQ_NOT_SUPPORTED;
          }
  
      return Status;
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  WS_Close(
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      IN BOOL fDontFlush
      )
  /*++
  
  Routine Description:
  
      Called once when the connection object is to be really deleted.
      At this point there will be no async IO pending on the connection.
  
  Arguments:
  
      ThisConnection - The connection object to close
  
  Return Value:
  
      RPC_S_OK
  
  --*/
  
  {
      SOCKET s;
      WS_CONNECTION *p = (WS_CONNECTION *)ThisConnection;
  
      p->WS_CONNECTION::Abort();
  
      if (p->iLastRead)
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         RPCTRANS "Closing connection %p with left over data (%d) %p \n",
                         p,
                         p->iLastRead,
                         p->pReadBuffer));
          }
  
      // Wait for the pending receive, if any, to actually complete.
  
      if ((p->type & TYPE_MASK) == CLIENT)
          {
          // these operations don't apply to all netbios flavors - they
          // apply to protocols which interop with OSF servers only
          // (because of the check for shutdown)
          if (!IsNetbiosProtocol(p->id))
              {
              PWS_CCONNECTION pcc = (PWS_CCONNECTION)ThisConnection;
              if (pcc->fReceivePending)
                  {
                  UTIL_WaitForSyncIO(&pcc->Read.ol,
                                     FALSE,
                                     INFINITE);
  
                  }
              }
          }
  
      // Now free the read buffer
  
      TransConnectionFreePacket(ThisConnection, p->pReadBuffer);
      p->pReadBuffer = 0;
  
      TransportProtocol::RemoveObjectFromProtocolList((BASE_ASYNC_OBJECT *) ThisConnection);
  
      return(RPC_S_OK);
  }
  
  
  BOOL
  WS_ProtectListeningSocket(
      IN SOCKET sock,
      IN BOOL newValue
      )
  /*++
  
  Routine Description:
  
      Sets the SO_EXCLUSIVEADDRUSE socket option on the sock parameter.  This
      prevents another process from using same port # and stealing our
      connections.
  
      Note: This option can only be set by administrators.  This routine has no
            affect when called by non-administrator.
  
  Arguments:
  
      sock - The socket to protect
      newValue - TRUE if the socket is to be protected, FALSE if it is to be unprotected
  
  Return Value:
  
      0 if it succeeds, 1 if it fails
  
  --*/
  {
      int fSocketExclusive = newValue;
      int rval;
  
      rval = setsockopt(sock,
                        SOL_SOCKET,
                        SO_EXCLUSIVEADDRUSE,
                        (const char *)&fSocketExclusive,
                        sizeof(fSocketExclusive));
  
      if (rval != 0)
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         "Unable to protect listening socket %d\n",
                         GetLastError()));
          }
  
      return rval;
  }
  
  
  RPC_STATUS
  WS_CheckForShutdowns(
      PWS_CCONNECTION p
      )
  /*++
  
  Routine Description:
  
      When a connection is idle OSF DCE 1.1+ machines will try to reduce
      resource usage by shutting down the connection.  This is the place
      in the DCE RPC protocol where the servers send data to the client
      asychronously to the client sending a request.
      When a server decides to shutdown a connection it send three
      PDUs of the rpc_shutdown type.  Then, if there are no open context
      handles on the connection, it will gracefully close the connection.
      Note: If there are context handles open on the connection then
      the connection is not acutally closed, but the shutdown PDUs are
      still sent.
  
  Algorithm:
  
      allocate a buffer large enough for a shutdown PDU.
  
      loop (i = 1 to 4 step 1)
          submit an async receive on the connection.
          If the receive doesn't complete immediately, return.
  
          type = PDU->type.
  
          If type != shutdown, save the PDU in the connection and return.
  
          goto Loop:
  
      If we get here it means we got too many shutdown PDUs. ASSERT and
      close the connection.
  
  Arguments:
  
      p - The connection to check for shutdowns on.
  
  Return Value:
  
      RPC_S_OK
  
      RPC_S_OUT_OF_MEMORY
      RPC_P_RECEIVE_FAILED
      RPC_P_SEND_FAILED
  
  --*/
  
  {
      RPC_STATUS status;
      BUFFER buffer;
      UINT length;
      HANDLE hEvent;
  
      if (p->iLastRead == 0)
          {
          // The server may have sent us a shutdown packet while the client
          // was idle.  We'll submit a recv now and check the result without
          // waiting.
          //
          // OSF servers will send 3 shutdown packets and then close the
          // connection.  We try to submit four receives, this way if the
          // connection has already been closed we can fail here.
  
          // Allocate a packet
          p->pReadBuffer = TransConnectionAllocatePacket(p, p->iPostSize);
  
          if (NULL == p->pReadBuffer)
              {
              p->WS_CONNECTION::Abort();
              return(RPC_S_OUT_OF_MEMORY);
              }
  
          p->maxReadBuffer = p->iPostSize;
          }
      else
          {
          ASSERT(p->pReadBuffer);
          }
  
      InitReadEvent(p);
  
      for (int i = 0; i < 4; i++)
          {
          status = CO_SubmitSyncRead(p, &buffer, &length);
  
          if (status == RPC_S_OK)
              {
              TransDbgPrint((DPFLTR_RPCPROXY_ID,
                             DPFLTR_WARNING_LEVEL,
                             RPCTRANS "Shutdown check completed!\n"));
  
              PCONN_RPC_HEADER phdr = (PCONN_RPC_HEADER)buffer;
  
              switch (MessageType(phdr))
                  {
                  case rpc_shutdown:
                      TransDbgPrint((DPFLTR_RPCPROXY_ID,
                                     DPFLTR_WARNING_LEVEL,
                                     RPCTRANS "Received a shutdown\n"));
  
                      p->fShutdownReceived = TRUE;
  
                      // Reset the buffers and try again.
                      if (p->pReadBuffer == 0)
                          {
                          // we don't know how big the buffer actually is. It may be
                          // gPostSize, but it also may be only length (if the
                          // previous receive was coalesced). Play it safe and choose
                          // what we know - length
                          p->pReadBuffer = buffer;
                          p->maxReadBuffer = length;
                          }
                      // else
                      // it is possible that by now all shutdowns
                      // are coalesced in memory - in this case
                      // pReadbuffer and maxReadBuffer are already
                      // set and there is nothing for us to do here -
                      // just loop around and get them
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          RPC_P_SEND_FAILED, 
                          EEInfoDLWSCheckForShutdowns10,
                          i);
                      break;
  
                  case rpc_fault:
                      // Io pending - don't free the buffer.
                      p->fReceivePending = TRUE;
                      if (p->pReadBuffer == 0)
                          {
                          // see comment in buffer resetting logic in rpc_shutdown 
                          // for explanation of this.
                          p->pReadBuffer = buffer;
                          p->maxReadBuffer = length;
                          }
  
                      if (((CONN_RPC_FAULT *) buffer)->status == NCA_STATUS_PROTO_ERROR)
                          {
                          //
                          // This can happen if the server is NT 4.0 and it received a cancel
                          // after a call completed.
                          //
                          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                                         DPFLTR_WARNING_LEVEL,
                                         RPCTRANS "Received an out of sequence packet: %p %p\n",
                                         p,
                                         phdr));
  
                          RpcpErrorAddRecord(EEInfoGCWinsock,
                              RPC_P_SEND_FAILED, 
                              EEInfoDLWSCheckForShutdowns20,
                              (ULONG)((CONN_RPC_FAULT *) buffer)->status,
                              (ULONG)i);
                          goto Cleanup;
                          }
  
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          RPC_P_RECEIVE_COMPLETE, 
                          EEInfoDLWSCheckForShutdowns30,
                          (ULONG)((CONN_RPC_FAULT *) buffer)->status,
                          (ULONG)i);
  
                      return RPC_P_RECEIVE_COMPLETE;
  
                  default:
                      // Got something else - this is probably a protocol error.
                      TransDbgPrint((DPFLTR_RPCPROXY_ID,
                                     DPFLTR_WARNING_LEVEL,
                                     RPCTRANS "Received an out of sequence packet: %p %p\n",
                                     p,
                                     phdr));
  
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          RPC_P_SEND_FAILED, 
                          EEInfoDLWSCheckForShutdowns40,
                          (ULONG)MessageType(phdr),
                          (ULONG)i);
  
                      CORRUPTION_ASSERT(0);
                      goto Cleanup;
                  }
              }
          else
              {
              if (status == RPC_P_IO_PENDING)
                  {
                  // Io pending - don't free the buffer.
                  p->fReceivePending = TRUE;
                  return(RPC_S_OK);
                  }
  
              RpcpErrorAddRecord(EEInfoGCWinsock,
                  status, 
                  EEInfoDLWSCheckForShutdowns50,
                  i);
              return(status);
              }
          }
  
  Cleanup:
      p->WS_CONNECTION::Abort();
      return(RPC_P_SEND_FAILED);
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  WS_SyncSend(
      IN RPC_TRANSPORT_CONNECTION Connection,
      IN UINT BufferLength,
      IN BUFFER Buffer,
      IN BOOL fDisableShutdownCheck,
      IN BOOL fDisableCancelCheck,
      ULONG Timeout
      )
  /*++
  
  Routine Description:
  
      Sends a message on the connection.  This method must appear
      to be synchronous from the callers perspective.
  
      Note: This routine must check for OSF DCE shutdown PDUs
      on TCP/IP to avoid interop problems.
  
  Arguments:
  
      Connection - The connection of send to.
      BufferLength - The size of the buffer.
      Buffer - The data to sent.
      fDisableShutdownCheck - Normally FALSE, when true this disables
          the transport check for async shutdown PDUs.  This is needed
          when sending the third leg.
  
  
  Return Value:
  
      RPC_P_SEND_FAILED - Connection will be closed if this is returned.
  
      RPC_S_OK - Data sent
  
  --*/
  
  {
      DWORD bytes;
      RPC_STATUS status;
  
      PWS_CCONNECTION p = (PWS_CCONNECTION)Connection;
      ASSERT(!IsNetbiosProtocol(p->id));
  
      // Note: this can be called on SERVER connections, too.
      // All references to CLIENT-ONLY members must be guarded with
      // (p->type & CLIENT).
  
      //
      // OSF 1.1(+) server's send, asynchronously to the client sending a
      // request, shutdown PDUs on "idle" connections.  Here we check for
      // shutdown PDUs in order to allow the client to retry the call on
      // another connection if this one has closed.
      //
  
      if (   ((p->type & TYPE_MASK) == CLIENT)
          && (FALSE == p->fCallStarted)
          && WsTransportTable[p->id].fCheckShutdowns
          && (fDisableShutdownCheck == FALSE)
          && (GetTickCount() - p->dwLastCallTime) > MILLISECONDS_BEFORE_PEEK)
          {
          p->fShutdownReceived = FALSE;
  
          status = WS_CheckForShutdowns(p);
  
          if (status != RPC_S_OK)
              {
              VALIDATE(status)
                  {
                  RPC_P_RECEIVE_FAILED,
                  RPC_P_CONNECTION_SHUTDOWN,
                  RPC_P_SEND_FAILED,
                  RPC_S_OUT_OF_MEMORY,
                  RPC_P_RECEIVE_COMPLETE
                  } END_VALIDATE;
  
              if (status == RPC_P_RECEIVE_COMPLETE)
                  {
                  return status;
                  }
  
              RpcpErrorAddRecord(EEInfoGCWinsock,
                  RPC_P_SEND_FAILED, 
                  EEInfoDLWSSyncSend10);
              return(RPC_P_SEND_FAILED);
              }
  
          // There is no need to to this again until SyncRecv is called.
          p->fCallStarted = TRUE;
          }
  
      HANDLE hEvent = I_RpcTransGetThreadEvent();
  
      p->StartingWriteIO();
  
      if (p->fAborted)
          {
          p->WriteIOFinished();
          return(RPC_P_SEND_FAILED);
          }
  
      // Setting the low bit of the event indicates that the write
      // completion should NOT be sent to the i/o completion port.
      OVERLAPPED olWrite;
      olWrite.Internal = 0;
      olWrite.InternalHigh = 0;
      olWrite.Offset = 0;
      olWrite.OffsetHigh = 0;
      olWrite.hEvent = (HANDLE) ((ULONG_PTR)hEvent | 0x1);
  
  #ifdef _INTERNAL_RPC_BUILD_
      if (gpfnFilter)
          {
          (*gpfnFilter) (Buffer, BufferLength, 0);
          }
  #endif
  
      status = p->Send(p->Conn.Handle,
                              Buffer,
                              BufferLength,
                              &bytes,
                              &olWrite
                              );
  
      p->WriteIOFinished();
  
      if (status == RPC_S_OK)
          {
          ASSERT(bytes == BufferLength);
          return(RPC_S_OK);
          }
  
      if (status == ERROR_IO_PENDING)
          {
          // if fDisableCancelCheck, make the thread wait non-alertably,
          // otherwise, make it wait alertably.
          status = UTIL_GetOverlappedResultEx(Connection,
                                              &olWrite,
                                              &bytes,
                                              !fDisableCancelCheck,
                                              Timeout);
  
          if (status == RPC_S_OK)
              {
              ASSERT(bytes == BufferLength);
  
              return(RPC_S_OK);
              }
          }
  
      ASSERT(status != RPC_S_OK);
  
      RpcpErrorAddRecord(EEInfoGCWinsock,
          status, 
          EEInfoDLWSSyncSend20);
  
      p->WS_CONNECTION::Abort();
  
      if ((status == RPC_S_CALL_CANCELLED) || (status == RPC_P_TIMEOUT))
          {
          // Wait for the write to finish.  Since we closed the
          // connection this won't take very long.
          UTIL_WaitForSyncIO(&olWrite,
                             FALSE,
                             INFINITE);
          }
      else
          {
          RpcpErrorAddRecord(EEInfoGCRuntime,
              RPC_P_SEND_FAILED, 
              EEInfoDLWSSyncSend30);
  
          status = RPC_P_SEND_FAILED;
          }
  
      return(status);
  }
  
  VOID
  WS_P_SetKeepAliveTimeout(
      IN SOCKET Socket,
      IN BOOL OnOff,
      IN UINT KATime,
      IN UINT KAInterval = 5000 OPTIONAL)
  /*++
  
  Arguments:
  
      Socket - The socket to set the keepalive timeout on
      OnOff - TRUE to turn if on. FALSE to turn it off
      KATime - The time between the last keep alive
          response and the next probe.
      KAInterval - The timeout in milliseconds for the
          subsequent keepalive packets.  The time between
          two consequitive keep alive probes if the first one
          did not receive a response.
  
  --*/
  {
      int r;
      tcp_keepalive tcpka;
      DWORD t;
  
      // make sure we indeed get TRUE of FALSE - we don't know how Winsock will
      // take it otherwise
      ASSERT((OnOff == TRUE) || (OnOff == FALSE));
  
      tcpka.onoff             = OnOff;
      tcpka.keepalivetime     = KATime; // Milliseconds, time to send first KA
      tcpka.keepaliveinterval = KAInterval;    // Milliseconds, this is the TCP/IP default.
  
      r = WSAIoctl(Socket,
                   SIO_KEEPALIVE_VALS,
                   (PVOID)&tcpka,
                   sizeof(tcpka),
                   (PVOID)&tcpka,
                   sizeof(tcpka),
                   &t,
                   0,
                   0);
  
      if (r != 0)
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         RPCTRANS "setsockopt KEEPALIVE_VALS failed %d\n",
                         GetLastError()));
          }
  }
  
  RPC_STATUS
  RPC_ENTRY 
  WS_TurnOnOffKeepAlives (
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      IN BOOL TurnOn,
      IN BOOL bProtectIO,
      IN KEEPALIVE_TIMEOUT_UNITS Units,
      IN OUT KEEPALIVE_TIMEOUT KATime,
      IN ULONG KAInterval OPTIONAL
      )
  /*++
  
  Routine Description:
  
      Turns on keep alives for Winsock transports supporting keepalives.
  
  Arguments:
  
      ThisConnection - The connection to turn keep alives on on.
      TurnOn - if non-zero, keep alives are turned on. If zero, keep alives
          are turned off.
      KATime - how much to wait before sending first keep alive.  The time between the last keep alive
          response and the next probe.
      KAInterval - the time between two consequitive keep alive probes if the first one did not
          receive a response.  5000 milliseconds is the default.
  
  Return Value:
  
      RPC_S_OK or RPC_S_* / Win32 errors on failure
  
  Note:
  
      If we use it on the server, we must protect
          the handle by calling StartingOtherIO
  
  --*/
  {
      PWS_CCONNECTION p = (PWS_CCONNECTION)ThisConnection;
      const WS_TRANS_INFO *pInfo = &WsTransportTable[p->id];
      RPC_STATUS RpcStatus = RPC_S_OK;
  
      // convert the timeout from runtime scale to transport scale
      if (Units == tuRuntime)
          {
          ASSERT(KATime.RuntimeUnits != RPC_C_BINDING_INFINITE_TIMEOUT);
          KATime.Milliseconds = ConvertRuntimeTimeoutToWSTimeout(KATime.RuntimeUnits);
          }
  
      // When the server is turning on keepalives it must protect
      // the operation by calling StartingOtherIO
      if (bProtectIO)
          {
          p->StartingOtherIO();
          }
  
      //
      // It is possible that this code is executed before TCP_Open and WS_Open
      // have been called or that unrecoverable failure has been hit.
      // In this case p->id will be invalid or fAborted will be set.
      //
      if (p->id == INVALID_PROTOCOL_ID || p->fAborted)
          {
          RpcStatus = RPC_P_CONNECTION_CLOSED;
          goto Cleanup;
          }
  
      ASSERT(pInfo->fSetKeepAlive);
  
      WS_P_SetKeepAliveTimeout(
          p->Conn.Socket, 
          TurnOn ? TRUE : FALSE, 
          KATime.Milliseconds,
          KAInterval);
  
  Cleanup:
      if (bProtectIO)
          {
          p->OtherIOFinished();
          }
      return RpcStatus;
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  WS_SyncRecv(
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      OUT BUFFER *pBuffer,
      OUT PUINT pBufferLength,
      IN DWORD dwTimeout
      )
  /*++
  
  Routine Description:
  
      Receive the next PDU to arrive at the connection.
  
  Arguments:
  
      ThisConnection - The connection to wait on.
      fCancelable - If TRUE, the wait will also include the threads cancel
          event and will timeout after the cancel event is signaled.
      pBuffer - If successful, points to a buffer containing the next PDU.
      BufferSize -  If successful, contains the length of the message.
  
  Return Value:
  
      RPC_S_OK
      RPC_P_RECEIVE_FAILED - Connection aborted.
      RPC_P_CONNECTION_SHUTDOWN - Graceful disconnect from server, connection aborted.
      RPC_S_CALL_CANCELLED - Timeout after cancel event, connection aborted.
  
  Note:
  
      Used only on the client. If we use it on the server, we must protect
          the handle with StartingOtherIO.
  
  --*/
  {
      RPC_STATUS status;
      DWORD bytes;
      HANDLE hEvent;
      BOOL fReceivePending;
      BOOL fSetKeepAliveVals = FALSE;
      PWS_CCONNECTION p = (PWS_CCONNECTION)ThisConnection;
      DWORD dwActualTimeout;
      BOOL fWaitOnConnectionTimeout;
  
      ASSERT((p->type & TYPE_MASK) == CLIENT);
      ASSERT(!IsNetbiosProtocol(p->id));
  
      // There maybe a receive already pending from the shutdown check
      // in WS_SyncSend.  If so, we want to skip the first submit.
  
      fReceivePending = p->fReceivePending;
      p->fReceivePending = FALSE;
      p->fCallStarted = FALSE;
  
      // if there's a per operation timeout, use the lesser of the operation
      // and connection timeout
      if (dwTimeout != INFINITE)
          {
          if (dwTimeout <= p->Timeout)
              {
              dwActualTimeout = dwTimeout;
              fWaitOnConnectionTimeout = FALSE;
              }
          else
              {
              dwActualTimeout = p->Timeout;
              fWaitOnConnectionTimeout = TRUE;
              }
          }
      else
          {
          // wait on the connection timeout
          dwActualTimeout = p->Timeout;
          fWaitOnConnectionTimeout = TRUE;
          }
  
      ASSERT(   (fReceivePending == FALSE)
             || (p->Read.ol.hEvent == (HANDLE) ((ULONG_PTR)I_RpcTransGetThreadEvent() | 0x1)) );
  
      //
      // Keep looping until we have a complete message.
      //
      // Note that SubmitSyncRecv may complete with a whole message read.
      //
      do
          {
  
          if (!fReceivePending)
              {
              // Allocate a receive buffer if needed.
  
              if (p->pReadBuffer == NULL)
                  {
                  ASSERT(p->iLastRead == 0);
  
                  p->pReadBuffer = TransConnectionAllocatePacket(p,
                                                                 p->iPostSize);
                  if (p->pReadBuffer == NULL)
                      {
                      p->WS_CONNECTION::Abort();
                      return(RPC_P_RECEIVE_FAILED);
                      }
  
                  p->maxReadBuffer = p->iPostSize;
                  }
  
              InitReadEvent(p);
  
              status = CO_SubmitSyncRead(p, pBuffer, pBufferLength);
  
              if (status != RPC_P_IO_PENDING)
                  {
                  break;
                  }
              }
          else
              {
              fReceivePending = FALSE;
              }
  
          do
              {
  
              //
              // Wait for the pending receive on the connection to complete
              //
              status = UTIL_GetOverlappedResultEx(ThisConnection,
                                                  &p->Read.ol,
                                                  &bytes,
                                                  TRUE, // Alertable
                                                  dwActualTimeout);
  
  
              if (   status != RPC_S_OK
                  || bytes == 0 )
                  {
  
                  // if we timed out ...
                  if (status == RPC_P_TIMEOUT)
                      {
                      ASSERT(dwActualTimeout != INFINITE);
  
                      // if we waited on the per connection timeout ...
                      if (fWaitOnConnectionTimeout)
                          {
                          ASSERT(p->Timeout != INFINITE);
                          if (dwTimeout == INFINITE)
                              {
                              // enable keep alives and wait forever
                              dwActualTimeout = INFINITE;
                              }
                          else
                              {
                              ASSERT(p->Timeout < dwTimeout);
  
                              // enable keep alives and wait the difference
                              dwActualTimeout = dwTimeout - p->Timeout;
                              fWaitOnConnectionTimeout = FALSE;
                              }
                          // Enable aggressive keepalives on the socket if transport
                          // supports it
                          if (WsTransportTable[p->id].fSetKeepAlive)
                              {
                              WS_P_SetKeepAliveTimeout(p->Conn.Socket, 
                                  TRUE,       // OnOff
                                  p->Timeout);
                              fSetKeepAliveVals = TRUE;
                              }
                          continue;
                          }
                      // else we have chosen the per operation timeout and
                      // have timed out on that - time to bail out
                      }
  
                  // Normal error path
  
                  RpcpErrorAddRecord(EEInfoGCWinsock,
                      status, 
                      EEInfoDLWSSyncRecv10,
                      (status == RPC_S_OK ? bytes : 0));
  
                  p->WS_CONNECTION::Abort();
  
                  if ((status == RPC_S_CALL_CANCELLED) || (status == RPC_P_TIMEOUT))
                      {
                      UTIL_WaitForSyncIO(&p->Read.ol,
                                         FALSE,
                                         INFINITE);
                      if ((status == RPC_P_TIMEOUT) && fWaitOnConnectionTimeout)
                          {
                          status = RPC_P_RECEIVE_FAILED;
                          RpcpErrorAddRecord(EEInfoGCWinsock,
                              status, 
                              EEInfoDLWSSyncRecv20);
                          }
                      }
                  else
                      {
                      status = RPC_P_RECEIVE_FAILED;
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          status, 
                          EEInfoDLWSSyncRecv30);
                      }
  
                  return(status);
                  }
              }
          while (status == RPC_P_TIMEOUT);
  
          status = p->ProcessRead(bytes, pBuffer, pBufferLength);
  
          }
      while (status == RPC_P_PARTIAL_RECEIVE);
  
      p->dwLastCallTime = GetTickCount();
  
      if (fSetKeepAliveVals)
          {
          // Call complete okay, clear keep alives
          WS_P_SetKeepAliveTimeout(p->Conn.Socket, 
              FALSE,      // OnOff
              0);
          }
  
      return(status);
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  WS_SyncRecv_Avrf(
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      OUT BUFFER *pBuffer,
      OUT PUINT pBufferLength,
      IN DWORD dwTimeout
      )
  /*++
  
  Routine Description:
  
      Wrapper for WS_SyncRecv implementing corruption injection
      under the RPC verifier.
  
      SyncRecv member of the transport interface may only be called
      by the cliet, hence we inject the corruption for a client receive.
  
  Arguments:
  
  Return Value:
  
  --*/
  {
      RPC_STATUS Status;
  
      Status = WS_SyncRecv(
          ThisConnection,
          pBuffer,
          pBufferLength,
          dwTimeout);
  
      if (!Status)
          {
          if (gfRPCVerifierEnabled)
              {
              CorruptionInject(ClientReceive,
                               pBufferLength,
                               (void **)pBuffer);
              }
          }
  
      return Status;
  }
  
  
  void
  RPC_ENTRY
  WS_ServerAbortListen(
      IN RPC_TRANSPORT_ADDRESS Address
      )
  /*++
  
  Routine Description:
  
      This routine will be called if an error occurs in setting up the
      address between the time that SetupWithEndpoint or SetupUnknownEndpoint
      successfully completed and before the next call into this loadable
      transport module.  We need to do any cleanup from Setup*.
  
  Arguments:
  
      pAddress - The address which is being aborted.
  
  Return Value:
  
      None
  
  --*/
  {
      PWS_ADDRESS pList = (PWS_ADDRESS)Address;
      PWS_ADDRESS pLast = 0;
      INT i, retval;
  
      delete pList->pAddressVector;
      delete pList->Endpoint;
  
      TransportProtocol::RemoveObjectFromProtocolList(pList);
  
      while(pList)
          {
          if (pList->ListenSocket)
              {
              closesocket(pList->ListenSocket);
              }
  
          if (pList->ConnectionSocket)
              {
              closesocket(pList->ConnectionSocket);
              }
  
  
          pLast = pList;
          pList = (PWS_ADDRESS) pList->pNextAddress;
  
          if (pLast != (PWS_ADDRESS)Address)
              {
              TransportProtocol::RemoveObjectFromProtocolList(pLast);
              delete pLast;
              }
          }
  
      return;
  }
  
  
  VOID
  WS_SubmitAccept(
      BASE_ADDRESS *Address
      )
  /*++
  
  Routine Description:
  
      Used to submit an accept on a listen socket. Called once when the
      listen socket is created and again after each client connects.
  
      The listen socket must already be added to the completion port.
  
  Arguments:
  
      Address - The address to accept on.
              ->ConnectionSocket - socket to accept on, or zero in
                  which case a new socket is allocated and put here.
  
  Return Value:
  
      None
  
  --*/
  {
      PWS_ADDRESS pAddress = (PWS_ADDRESS)Address;
      const WS_TRANS_INFO *pInfo = &WsTransportTable[pAddress->id];
      SOCKET sAccept;
      RPC_STATUS status;
  
  #ifdef MAJOR_TRANS_DEBUG
      DbgPrint("WS_SubmitAccept: Address=0x%x ListenAddr=%x\n", pAddress, (DWORD)pAddress->ListenAddr.inetaddr.sin_addr.S_un.S_addr);
  #endif
  
      if (pAddress->ConnectionSocket != 0)
          {
          closesocket(pAddress->ConnectionSocket);
          pAddress->ConnectionSocket = 0;
          }
  
      pAddress->ConnectionSocket =
          WSASocketT(pInfo->AddressFamily,
                     pInfo->SocketType,
                     pInfo->Protocol * GetProtocolMultiplier(pAddress),
                     0,
                     0,
                     WSA_FLAG_OVERLAPPED);
  
  #ifdef MAJOR_TRANS_DEBUG
      DbgPrint("WS_SubmitAccept: WSASocketT: pAddress->ConnectionSocket=%x\n", pAddress->ConnectionSocket);
  #endif
  
      if (pAddress->ConnectionSocket == SOCKET_ERROR)
          {
          pAddress->ConnectionSocket = 0;
          COMMON_AddressManager(Address);
          return;
          }
  
      //
      // make the handle non-inheritable so it goes away when we close it.
      // join the socket to the completion port
      //
      if (FALSE    == SetHandleInformation(   (HANDLE) pAddress->ConnectionSocket, HANDLE_FLAG_INHERIT, 0) ||
          RPC_S_OK != COMMON_PrepareNewHandle((HANDLE) pAddress->ConnectionSocket))
          {
          closesocket(pAddress->ConnectionSocket);
          pAddress->ConnectionSocket = 0;
          COMMON_AddressManager(Address);
          return;
          }
  
      ASSERT(pAddress->ConnectionSocket != INVALID_SOCKET);
  
      DWORD bytes = 0;
  
      BOOL b = pAddress->pAcceptExFunction(pAddress->ListenSocket,
                        pAddress->ConnectionSocket,
                        &pAddress->AcceptBuffer,
                        0,
                        0,
                        sizeof(WS_SOCKADDR) + 16,
                        &bytes,
                        &pAddress->Listen.ol
                        );
  
  #ifdef MAJOR_TRANS_DEBUG
      DbgPrint("WS_SubmitAccept: pAddress->pAcceptExFunction: b=%x GetLastError()=%x\n", b, GetLastError());
  #endif
  
      if (!b && (GetLastError() != ERROR_IO_PENDING))
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         "AcceptEx failed %p, %d %d\n",
                         pAddress,
                         pAddress->ConnectionSocket,
                         GetLastError()));
  
          closesocket(pAddress->ConnectionSocket);
          pAddress->ConnectionSocket = 0;
          COMMON_AddressManager(Address);
          }
  
      return;
  }
  
  
  void
  WS_SetSockOptForConnection (
      IN const WS_TRANS_INFO *pInfo,
      IN SOCKET sock
      )
  /*++
  
  Routine Description:
  
      Sets the socket options for the given socket for a server
      side connection socket.
  
  Arguments:
  
      pInfo - the transport information for the Winsock transport
      sock - the socket on which to set options
  
  Return Value:
  
      None. Setting the options is a best effort. Failures are ignored.
  
  --*/
  {
      int retval = 0;
  
      if (pInfo->fSetNoDelay)
          {
          INT NoDelay = TRUE;
          retval = setsockopt(sock,
                              pInfo->Protocol,
                              TCP_NODELAY,
                              (PCHAR)&NoDelay, sizeof(NoDelay)
                              );
          }
  
      if (   pInfo->fSetKeepAlive
          && retval == 0)
          {
          INT KeepAlive = TRUE;
          retval = setsockopt(sock,
                              pInfo->Protocol,
                              SO_KEEPALIVE,
                              (PCHAR)&KeepAlive, sizeof(KeepAlive)
                              );
          }
  
      if (retval != 0)
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         RPCTRANS "setsockopt failed %d, %d\n",
                         retval,
                         GetLastError()));
          }
  
  }
  
  
  RPC_STATUS
  WS_NewConnection(
      IN PADDRESS Address,
      OUT PCONNECTION *ppConnection
      )
  /*++
  
  Routine Description:
  
      Called when an AcceptEx completes on an I/O completion thread.
  
  Arguments:
  
      Address - The address used as context in a previous AcceptEx.
      ppConnection - A place to store the new pConnection.  Used
          when a connection been created and then a failure occurs.
  
  Return Value:
  
      RPC_S_OK
      RPC_S_OUT_OF_RESOURCES
      RPC_S_OUT_OF_MEMORY
  
  --*/
  {
      RPC_STATUS status;
      BOOL b;
      WS_ADDRESS *pAddress = (WS_ADDRESS *)Address;
      const WS_TRANS_INFO *pInfo = &WsTransportTable[pAddress->id];
      WS_CONNECTION *pConnection;
      UINT fReceiveDirect;
      SOCKET sock = pAddress->ConnectionSocket;
      int retval = 0;
      WS_SOCKADDR *saAddrs;
      WS_SOCKADDR saClient;
      INT adwAddrLen;
      BOOL fSANConnection;
      int LocalAddressLength = 0;
      PSOCKADDR DummyAddr = NULL;
  
      ASSERT(sock);
  
      pAddress->ConnectionSocket = 0;
  
      // First, parse the client address out of the accept
      // since the next accept will reuse the same buffer.
  
      pAddress->pGetAcceptExSockaddressFunction(&pAddress->AcceptBuffer,
                           0,
                           0,
                           sizeof(WS_SOCKADDR) + 16,
                           &DummyAddr,
                           &LocalAddressLength,
                           (struct sockaddr **)&saAddrs,
                           &adwAddrLen);
  
      ASSERT(adwAddrLen <= sizeof(WS_SOCKADDR));
  
      // Save the client address before submitting the next accept.
      saClient = *saAddrs;
  
      // Submit the next accept.
      WS_SubmitAccept(pAddress);
  
      // Now, try process the new connection..
      WS_SetSockOptForConnection(pInfo, sock);
  
      /*
      fSANConnection = IsUserModeSocket(sock, &status);
      if (status != RPC_S_OK)
          {
          closesocket(sock);
          return status;
          }
          */
      fSANConnection = TRUE;
  
      //
      // Notes:
      //
      // a. For security reasons, we require the RPC HTTP Servers to send back
      //    an identification message.
      //
      // b. This should "really" be done in WS_SubmitAccept(). This is done here
      //    for convenience. This is OK if HttpSendIdentifyRespomse() rarely
      //    fails.
      //
      if ((pAddress->id == HTTP) &&
          (status = HttpSendIdentifyResponse(sock)))
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         "HttpSendIdentifyResponse failed %p, %d - %d\n",
                         pAddress,
                         sock,
                         status
                         ));
          closesocket(sock);
          return(RPC_S_OUT_OF_RESOURCES);
          }
  
      BASE_ADDRESS *pRealAddress = pAddress->pFirstAddress;
  
      pConnection = (WS_CONNECTION *)
                    I_RpcTransServerNewConnection(pRealAddress);
  
      *ppConnection = pConnection;
  
      if (!pConnection)
          {
          // Abort the connection.
  
          INT DontLinger = TRUE;
          // REVIEW: check a protocol flag to do this?
          retval = setsockopt(sock, SOL_SOCKET, SO_DONTLINGER,
                              (PCHAR)&DontLinger, sizeof(DontLinger));
          // setsockopt can fail with something like WSAENOBUFS in low
          // memory conditions.  We can't handle this in any special way.
          closesocket(sock);
          return(RPC_S_OUT_OF_MEMORY);
          }
  
      // Got a good connection, initialize it..
  
      // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      // This function cannot fail after this point.  There is no
      // way to notify the runtime that the connection has been closed.
      // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  
  #ifdef NETBIOS_ON
      if ((pAddress->id == NBF)
          || (pAddress->id == NBT)
          || (pAddress->id == NBI))
          {
          if (fSANConnection)
              pConnection = new (pConnection) NB_SAN_CONNECTION;
          else
              pConnection = new (pConnection) NB_CONNECTION;
          }
      else 
  #endif
      if (pAddress->id == HTTP)
          {
          pConnection = new (pConnection) WS_HTTP2_INITIAL_CONNECTION;
          }
      else
          {
          if (fSANConnection)
              pConnection = new (pConnection) WS_SAN_CONNECTION;
          else
              pConnection = new (pConnection) WS_CONNECTION;
          }
  
      pConnection->type = SERVER | CONNECTION;
      pConnection->id = pAddress->id;
      pConnection->Conn.Socket = sock;
      pConnection->fAborted = 0;
      pConnection->pReadBuffer = 0;
      pConnection->maxReadBuffer = 0;
      pConnection->iLastRead = 0;
      pConnection->iPostSize = gPostSize;
      pConnection->saClientAddress = saClient;
      RpcpMemorySet(&pConnection->Read.ol, 0, sizeof(pConnection->Read.ol));
      pConnection->Read.pAsyncObject = pConnection;
      pConnection->InitIoCounter();
      pConnection->pAddress = pAddress;
  
      TransportProtocol::AddObjectToProtocolList((BASE_ASYNC_OBJECT *) *ppConnection);
  
      return(RPC_S_OK);
  }
  
  
  BOOL
  IsUserModeSocket(
      IN SOCKET s,
      OUT RPC_STATUS *pStatus)
  /*++
  
  Routine Description:
  
      Given a socket, it tells whether this is a true kernel socket or not. This test is based per VadimE's input
      that:
          "Just call getsockopt (SOL_SOCKET, SO_PROTOCOL_INFOW) and check XP1_IFS_HANDLES in
          dwServiceFlags1 of WSAPROTOCOL_INFOW. If flag is not set, the handle is not "TRUE"
          IFS handle and file system calls on them carry performance penalty.
  
          Make sure you call after connection is established or information returned may be
          inaccurate."
  
  Arguments:
  
      s - The socket to be tested
      pStatus - RPC_S_OK if everything is fine. RPC_S_OUT_OF_MEMORY if the test could not be performed. Note
          that in the latter case the return value is undefined and should be disregarded.
  
  Return Value:
  
      TRUE - the socket is a true kernel socket
      FALSE  - the socket is not a kernel socket
  
  --*/
  {
      WSAPROTOCOL_INFO protocolInfo;
      int paramSize = sizeof(protocolInfo);
      int retval;
  
      // check whether this is a kernel connection. We do this by checking whether this socket is a true
      // IFS_HANDLE. If yes, then this is not a kernel socket. If not, then it is a kernel socket
      retval = getsockopt(s, SOL_SOCKET, SO_PROTOCOL_INFOW, (char *) &protocolInfo, &paramSize);
      if (retval == SOCKET_ERROR)
          {
          *pStatus = RPC_S_OUT_OF_MEMORY;
          return FALSE;
          }
  
      *pStatus = RPC_S_OK;
  
      if (protocolInfo.dwServiceFlags1 & XP1_IFS_HANDLES)
          return FALSE;
      else
          return TRUE;
  }
  
  
  RPC_STATUS
  WS_ServerListenCommon (
      IN WS_ADDRESS *pAddress,
      IN BOOL fResetAddressListEntries, OPTIONAL
      IN BOOL fAddToProtocolList OPTIONAL
      )
  /*++
  
  Routine Description:
  
      This routine does common server address setup.
  
  Arguments:
  
      pAddress - A pointer to the loadable transport interface address.
          Will contain the newly allocated listen socket when finished.
  
      pListenAddr - Initalized socket address to bind to. On output
          it will contain results of the bind.
  
      PendingQueueSize - Value specified in use protseq, used
          to set the pending queue size for listens.
  
  ReturnValue:
  
      RPC_S_OK
  
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
      RPC_S__CANT_CREATE_ENDPOINT
  
  --*/
  {
      SOCKET               sock;
      int                  retval, length;
      RPC_STATUS           status;
      const WS_TRANS_INFO *pInfo = &WsTransportTable[pAddress->id];
      WS_SOCKADDR *pListenAddr = &(pAddress->ListenAddr);
      DWORD EndpointFlags = pAddress->EndpointFlags;
      BOOL fRetVal;
      int i;
      DWORD LastError;
  
      pAddress->type = ADDRESS;
      pAddress->InAddressList = NotInList;
      pAddress->fAborted = 0;
      pAddress->pNext = 0;
      pAddress->ListenSocket = 0;
      pAddress->ConnectionSocket = 0;
  
      // We may be called on an address that is enlinked in a list
      // and does not want these entries reset.
      if (fResetAddressListEntries)
          {
          pAddress->pNextAddress = 0;
          pAddress->pFirstAddress = pAddress;
          }
  
      memset(&pAddress->Listen, 0, sizeof(BASE_OVERLAPPED));
      pAddress->Listen.pAsyncObject = pAddress;
      for (i = FIRST_EXTENSION_FUNCTION_CODE; i < LAST_EXTENSION_FUNCTION_CODE; i ++)
          {
          pAddress->ExtensionFunctionPointers[i] = NULL;
          }
  
      if (fAddToProtocolList)
          {
          RpcpInitializeListHead(&pAddress->ObjectList);
          }
  
      // First order of business: get a valid socket
      //
      sock = WSASocketT(pInfo->AddressFamily,
                        pInfo->SocketType,
                        pInfo->Protocol,
                        0,
                        0,
                        WSA_FLAG_OVERLAPPED);
  
      if (sock == INVALID_SOCKET)
          {
          LastError = GetLastError();
  
          RpcpErrorAddRecord(EEInfoGCWinsock,
              LastError, 
              EEInfoDLWSServerListenCommon10,
              pInfo->AddressFamily,
              pInfo->SocketType,
              pInfo->Protocol);
  
          switch(LastError)
              {
              case WSAENETDOWN:
              case WSAEINVAL:
              case WSAEPROTOTYPE:
              case WSAENOPROTOOPT:
              case WSAEPROTONOSUPPORT:
              case WSAESOCKTNOSUPPORT:
              case WSAEPFNOSUPPORT:
              case WSAEADDRNOTAVAIL:
                  status = RPC_S_PROTSEQ_NOT_SUPPORTED;
                  break;
  
              case WSAEAFNOSUPPORT:
                  status = RPC_P_ADDRESS_FAMILY_INVALID;
                  break;
  
              case WSAENOBUFS:
              case WSAEMFILE:
              case WSA_NOT_ENOUGH_MEMORY:
                  status = RPC_S_OUT_OF_MEMORY;
                  break;
  
              default:
                  ASSERT(0);
  
                  // !break
  
              case WSAEPROVIDERFAILEDINIT:
                  status = RPC_S_OUT_OF_RESOURCES;
                  break;
              }
  
          RpcpErrorAddRecord(EEInfoGCRuntime,
              status, 
              EEInfoDLWSServerListenCommon30);
  
          return(status);
          }
  
      //
      // Make the handle non-inheritable so it goes away when we close it.
      //
      if (FALSE == SetHandleInformation( (HANDLE) sock, HANDLE_FLAG_INHERIT, 0))
          {
          closesocket(sock);
          return RPC_S_OUT_OF_RESOURCES;
          }
  
      fRetVal = pAddress->GetExtensionFunctionPointers(sock);
  
      if (!fRetVal)
          {
          switch (GetLastError())
              {
              case WSAEFAULT:
              case WSAEINVAL:
                  status = RPC_S_INTERNAL_ERROR;
                  break;
  
              case WSAEOPNOTSUPP:
                  status = RPC_S_PROTSEQ_NOT_SUPPORTED;
                  break;
  
              default:
                  status = RPC_S_OUT_OF_RESOURCES;
              }
          closesocket(sock);
          return(status);
          }
  
      //
      // Try to bind to the given port number...
      //
  
      pListenAddr->generic.sa_family = pInfo->AddressFamily;
  
      // N.B. - we should think how the port allocation will look for TCP/IPv6
      status = WS_Bind(sock, pListenAddr, (pAddress->id == TCP) || (pAddress->id == HTTP), EndpointFlags);
  
      if (status != RPC_S_OK)
          {
          closesocket(sock);
          return(status);
          }
  
      if(listen(sock, pAddress->QueueSize) == SOCKET_ERROR)
          {
          RpcpErrorAddRecord(EEInfoGCWinsock,
              RPC_S_OUT_OF_RESOURCES,
              EEInfoDLWSServerListenCommon20,
              GetLastError(),
              (ULONGLONG)sock,
              (ULONG)pAddress->QueueSize);
          closesocket(sock);
          return(RPC_S_OUT_OF_RESOURCES);
          }
  
      status = COMMON_PrepareNewHandle((HANDLE)sock);
      if (status != RPC_S_OK)
          {
          closesocket(sock);
          return(status);
          }
  
      pAddress->ListenSocket = sock;
  
      if (fAddToProtocolList)
          {
          TransportProtocol::AddObjectToProtocolList((BASE_ASYNC_OBJECT *) pAddress);
          }
  
      TransportProtocol::FunctionalProtocolDetected(pAddress->id);
  
      return(RPC_S_OK);
  }
  
  NETWORK_ADDRESS_VECTOR *
  WS_GetNetworkAddressVector (
      IN RPC_TRANSPORT_ADDRESS ThisAddress
      )
  {
      PWS_ADDRESS pAddress = (PWS_ADDRESS)ThisAddress;
      ASSERT(pAddress->pAddressVector);
  #ifdef MAJOR_TRANS_DEBUG
      DbgPrint("WS_GetNetworkAddressVector: pAddress=0x%x pAddressVector=0x%x\n", pAddress, pAddress->pAddressVector);
  #endif
      return pAddress->pAddressVector;
  }
  
  
  RPC_STATUS
  WS_Initialize_Internal (
      IN PWS_CCONNECTION pConnection
      )
  {
      pConnection->Initialize();
  
      pConnection->fCallStarted      = FALSE;
      pConnection->fShutdownReceived = FALSE;
      pConnection->fReceivePending   = FALSE;
      pConnection->dwLastCallTime    = GetTickCount();
      pConnection->pAddress = NULL;
      RpcpInitializeListHead(&pConnection->ObjectList);
  
      return RPC_S_OK;
  }
  
  
  RPC_STATUS
  WS_Initialize (
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      IN RPC_CHAR *NetworkAddress,
      IN RPC_CHAR *NetworkOptions,
      IN BOOL fAsync
      )
  {
      PWS_CCONNECTION p = (PWS_CCONNECTION) ThisConnection;
  
      p->id = INVALID_PROTOCOL_ID;
      return WS_Initialize_Internal(p);
  }
  
  const UUID ConnectExExtensionFunctionUuid = WSAID_CONNECTEX;
  
  
  RPC_STATUS
  WS_Open(
      IN PWS_CCONNECTION p,
      IN WS_SOCKADDR *psa,
      IN UINT ConnTimeout,
      IN UINT SendBufferSize,
      IN UINT RecvBufferSize,
      IN ULONG CallTimeout,
      IN BOOL fHTTP2Open
      )
  /*++
  
  Routine Description:
  
      Common part of opening a winsock connection to a server.
  
  Arguments:
  
      p - The partially initialized client connection object. If fHTTP2,
          this is a connection object, not client connection object.
      psa - sockaddr with protocol specific part already containing
          this address and port of the server.
      ConnTimeout - Valid for TCP/IP, see SyncRecv error handling.
      {Send,Recv}BufferSize - Used to set the transport buffer
          sizes on some protocols. Currently ignored.
      CallTimeout - call timeout in milliseconds
      fHTTP2Open - the open is an HTTP2 open
  
  Return Value:
  
      RPC_S_OK
  
      RPC_S_OUT_OF_RESOURCES
      RPC_S_OUT_OF_MEMORY
  
      RPC_S_SERVER_UNAVAILABLE - failed
      ERROR_RETRY - failed, but another address might work.
  
  --*/
  {
      // Initialize common part of the connection object
  
      const WS_TRANS_INFO *pInfo = &WsTransportTable[p->id];
      SOCKET sock;
      RPC_STATUS           status;
      BOOL fSANConnection;
      DWORD LastError;
      RPC_CLIENT_PROCESS_IDENTIFIER ServerAddress;
      HANDLE hEvent;
      OVERLAPPED ol;
      DWORD dwBytesReturned;
      LPFN_CONNECTEX ConnectEx;
      union
          {
          SOCKADDR_IN sockaddr;
          SOCKADDR_IN6 sockaddr6;
          };
      int NameLen;
  
      DWORD Transfer;
      DWORD Flags;
  
      // Set if we had already called GetLastErrorand added EEInfo.
      // Adding EEInfo may overwrite the LastError and getting it second
      // time will return 0 and cause an assert.
      BOOL fGetLastErrorCalled = FALSE;
  
      if (!fHTTP2Open)
          {
          WS_Initialize_Internal(p);
          }
  
      //
      // Open a socket
      //
  
      sock = WSASocketT(pInfo->AddressFamily,
                        pInfo->SocketType,
                        pInfo->Protocol,
                        0,
                        0,
                        WSA_FLAG_OVERLAPPED);
  
      if (sock == INVALID_SOCKET)
          {
          LastError = GetLastError();
  
          RpcpErrorAddRecord(EEInfoGCWinsock,
              LastError, 
              EEInfoDLWSOpen10,
              pInfo->AddressFamily,
              pInfo->SocketType,
              pInfo->Protocol);
  
          switch(LastError)
              {
              case WSAEAFNOSUPPORT:
              case WSAEPROTONOSUPPORT:
              case WSAEPROTOTYPE:
              case WSAENETDOWN:
              case WSAESOCKTNOSUPPORT:
              case WSAEINVAL:     // when registry is not yet setup.
                  status = RPC_S_PROTSEQ_NOT_SUPPORTED;
                  break;
  
              case ERROR_NOT_ENOUGH_QUOTA:
              case WSAENOBUFS:
              case WSAEMFILE:
              case WSA_NOT_ENOUGH_MEMORY:
              // This failure is possible in low memory conditions
              // or due to fault injection during registry read or
              // notification creation.
              case WSASYSCALLFAILURE:
                  status = RPC_S_OUT_OF_MEMORY;
                  break;
  
              case ERROR_ACCESS_DENIED:
                  status = RPC_S_ACCESS_DENIED;
                  break;
  
              default:
                  ASSERT(0);
                  // no break - fall through
  
              case WSAEPROVIDERFAILEDINIT:
                  status = RPC_S_OUT_OF_RESOURCES;
                  break;
              }
  
          RpcpErrorAddRecord(EEInfoGCRuntime,
              status, 
              EEInfoDLWSOpen30);
  
          return (status);
          }
  
      //
      // make the handle non-inheritable so it goes away when we close it.
      //
      if (FALSE == SetHandleInformation( (HANDLE) sock, HANDLE_FLAG_INHERIT, 0))
          {
          closesocket(sock);
          return RPC_S_OUT_OF_RESOURCES;
          }
  
      p->Conn.Socket = sock;
  
      //
      // Set socket options
      //
      // REVIEW: Set loopback socket option? Ask winsock folks.
  
      DWORD option;
      int retval = 0;
  
      if (pInfo->fSetNoDelay)
          {
          option = TRUE;
          retval = setsockopt( sock, pInfo->Protocol, TCP_NODELAY,
                               (PCHAR)&option, sizeof(option) );
          }
  
      if (pInfo->fSetKeepAlive && retval == 0)
          {
          option = TRUE;
          retval = setsockopt( sock, pInfo->Protocol, SO_KEEPALIVE,
                               (PCHAR)&option, sizeof(option) );
          }
  
      if (   pInfo->fSetSendBuffer
          && SendBufferSize
          && retval == 0)
          {
          ASSERT(SendBufferSize <= 0xFFFF);
          retval = setsockopt( sock, SOL_SOCKET, SO_SNDBUF,
                               (PCHAR)&SendBufferSize, sizeof(UINT) );
          }
  
      if (   pInfo->fSetRecvBuffer
          && RecvBufferSize
          && retval == 0 )
          {
          ASSERT(RecvBufferSize <= 0xFFFF);
          retval = setsockopt( sock, SOL_SOCKET, SO_RCVBUF,
                               (PCHAR)&RecvBufferSize, sizeof(UINT) );
          }
  
  
      if (retval)
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         RPCTRANS "setsockopt failed %d\n",
                         GetLastError()));
  
          p->WS_CONNECTION::Abort();
  
          return(RPC_S_OUT_OF_RESOURCES);
          }
  
      if (!fHTTP2Open)
          {
          //
          // Set timeout
          //
  
          if (   WsTransportTable[p->id].fSetKeepAlive
              && ConnTimeout != RPC_C_BINDING_INFINITE_TIMEOUT)
              {
              ASSERT(   ((long)ConnTimeout >= RPC_C_BINDING_MIN_TIMEOUT)
                     && (ConnTimeout <= RPC_C_BINDING_MAX_TIMEOUT));
  
              // convert the timeout from runtime scale to transport scale
              p->Timeout = ConvertRuntimeTimeoutToWSTimeout(ConnTimeout);
              }
          else
              {
              p->Timeout = INFINITE;
              }
          }
  
      //
      // HTTP specific connect() done in HTTP_Open().
      //
      if (p->id == HTTP)
          {
          //
          // For HTTP, add the new socket to the io completion port
          // now. For TCP, we'll add it later.
          //
          status = COMMON_PrepareNewHandle((HANDLE)sock);
          if (status != RPC_S_OK)
              {
              closesocket(sock);
              return status;
              }
  
          return (RPC_S_OK);
          }
  
  
      //
      // Connect the socket to the server
      //
  
      psa->generic.sa_family = pInfo->AddressFamily;
  
      if ((CallTimeout == INFINITE) || (CallTimeout == 0) 
  #ifdef SPX_ON
          || (p->id == SPX)
  #endif
          )
          {
          retval = connect(sock, &psa->generic, pInfo->SockAddrSize);
          }
      else
          {
          // we have a specified call timeout. Use ConnectEx instead
  
          // first, bind the socket. Unlike connect, ConnectEx doesn't
          // accept unbound sockets
          if (p->id != TCP_IPv6)
              {
              sockaddr.sin_addr.S_un.S_addr = INADDR_ANY;
              sockaddr.sin_family = AF_INET;
              sockaddr.sin_port = 0;
              NameLen = sizeof(sockaddr);
              }
          else
              {
              IN6ADDR_SETANY(&sockaddr6);
              sockaddr6.sin6_scope_id = 0;
              NameLen = sizeof(sockaddr6);
              }
  
          retval = bind(sock,
              (SOCKADDR *)&sockaddr,
              NameLen);
  
          if (retval == SOCKET_ERROR)
              {
              status = GetLastError();
              RpcpErrorAddRecord(EEInfoGCWinsock,
                  status,
                  EEInfoDLWSOpen60);
  
              fGetLastErrorCalled = TRUE;
  
              goto Handle_WS_OpenError;
              }
  
          // second retrieve address of ConnectEx
          retval = WSAIoctl(sock, 
              SIO_GET_EXTENSION_FUNCTION_POINTER, 
              (void *) &ConnectExExtensionFunctionUuid,
              sizeof(UUID), 
              (void *) &ConnectEx,
              sizeof(void *), 
              &dwBytesReturned,
              NULL,   // lpOverlapped
              NULL    // lpCompletionRoutine
              );
  
          if (retval == SOCKET_ERROR)
              {
              // ConnectEx is not available.  We need to default to using connect.
              retval = connect(sock, &psa->generic, pInfo->SockAddrSize);
              }
          else
              {
              // Use ConnectEx.
  
              ASSERT(dwBytesReturned == sizeof(void *));
  
              hEvent = I_RpcTransGetThreadEvent();
              ASSERT(hEvent);
  
              ol.Internal = 0;
              ol.InternalHigh = 0;
              ol.Offset = 0;
              ol.OffsetHigh = 0;
              // There may be a window between winsock's raising the event to signal IO completion
              // and checking if there is a completion port associated with the socket.  We
              // need to make sure that the IO completion packet will not be posted to a port if
              // we associate it with the socket after the event is raised but before the packet is posted.
              ol.hEvent = (HANDLE) ((ULONG_PTR)hEvent | 0x1);
  
              retval = ConnectEx(sock, 
                  &psa->generic, 
                  pInfo->SockAddrSize,
                  NULL,   // lpSendBuffer
                  0,      // dwSendDataLength
                  NULL,   // lpdwBytesSent
                  &ol);
  
              // N.B. ConnectEx returns the opposite of connect - TRUE for
              // success and FALSE for failure. Since the error handling is
              // common, we must make the return value consistent. We do this
              // by reverting the return value for ConnectEx
              retval = !retval;
  
              if (retval != 0)
                  {
                  LastError = GetLastError();
                  if ((LastError != ERROR_IO_PENDING) && (LastError != WSA_IO_PENDING))
                      {
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          LastError, 
                          EEInfoDLWSOpen80);
  
                      status = LastError;
                      fGetLastErrorCalled = TRUE;
  
                      goto Handle_WS_OpenError;                    
                      }
  
                  // wait for the result or for timeout
                  LastError = WaitForSingleObject(hEvent, CallTimeout);
                  if (LastError == WAIT_TIMEOUT)
                      {
                      // we have hit a timeout. Kill the socket and bail out
                      status = RPC_S_CALL_CANCELLED;
                      RpcpErrorAddRecord(EEInfoGCRuntime,
                          status, 
                          EEInfoDLWSOpen50,
                          CallTimeout);
  
                      p->WS_CONNECTION::Abort();
  
                      // wait for our IO to complete. Should be quick after
                      // we closed the socket
                      LastError = WaitForSingleObject(hEvent, INFINITE);
                      ASSERT(LastError == WAIT_OBJECT_0);
                      ASSERT(HasOverlappedIoCompleted(&ol));
  
                      return status;
                      }
                  else
                      {
                      ASSERT(LastError == WAIT_OBJECT_0);
                      ASSERT(HasOverlappedIoCompleted(&ol));
  
                      // Retrieve the overlapped result.  No need to wait since the IO has
                      // already completed.
                      if (!WSAGetOverlappedResult(sock, &ol, &Transfer, FALSE, &Flags))
                          {
                          // set retval to the WSA error code.
                          retval = WSAGetLastError();
  
                          RpcpErrorAddRecord(EEInfoGCWinsock,
                              retval, 
                              EEInfoDLWSOpen90);
  
                          status = retval;
                          fGetLastErrorCalled = TRUE;
                          }
                      else
                          {
                          retval = setsockopt(sock,
                              SOL_SOCKET,
                              SO_UPDATE_CONNECT_CONTEXT,
                              NULL,
                              0
                              );
                          }
                      }
                  }
              else
                  {
                  retval = setsockopt(sock,
                      SOL_SOCKET,
                      SO_UPDATE_CONNECT_CONTEXT,
                      NULL,
                      0
                      );
                  }
              }
          }
  
      if (retval == 0)
          {
          //
          // After we're done with connect/ConnectEx, add the socket
          // to the completion port.
          //
          status = COMMON_PrepareNewHandle((HANDLE)sock);
          if (status != RPC_S_OK)
              {
              goto Handle_WS_OpenError;
              }
  
          fSANConnection = IsUserModeSocket(sock, &status);
          if (status == RPC_S_OK)
              {
              if (fSANConnection && !fHTTP2Open)
                  {
                  // reinitialize vtbl
                  p = new (p) WS_SAN_CLIENT_CONNECTION;
                  }
              TransportProtocol::AddObjectToProtocolList((BASE_ASYNC_OBJECT *) p);
              return(RPC_S_OK);
              }
          }
  
  Handle_WS_OpenError:
  
      if (!fGetLastErrorCalled)
          {
          status = GetLastError();
          }
  
      ServerAddress.ZeroOut();
      if (p->id == TCP)
          {
          ServerAddress.SetIPv4ClientIdentifier(psa->inetaddr.sin_addr.S_un.S_addr, FALSE);
          }
      else if (p->id == TCP_IPv6)
          {
          ServerAddress.SetIPv6ClientIdentifier(&psa->ipaddr, sizeof(psa->ipaddr), FALSE);
          }
  
      RpcpErrorAddRecord(EEInfoGCWinsock,
          status, 
          EEInfoDLWSOpen20,
          (ULONG)ntohs(RpcpGetIpPort(psa)),
          ServerAddress.GetDebugULongLong1(),
          ServerAddress.GetDebugULongLong2());
  
      switch(status)
          {
          case WSAENETUNREACH:
          case STATUS_BAD_NETWORK_PATH:
          case STATUS_NETWORK_UNREACHABLE:
          case STATUS_PROTOCOL_UNREACHABLE:
          case WSAEHOSTUNREACH:
          case STATUS_HOST_UNREACHABLE:
          case WSAETIMEDOUT:
          case STATUS_LINK_TIMEOUT:
          case STATUS_IO_TIMEOUT:
          case STATUS_TIMEOUT:
          case WSAEADDRNOTAVAIL:
          case STATUS_INVALID_ADDRESS:
          case STATUS_INVALID_ADDRESS_COMPONENT:
              status = ERROR_RETRY;
              break;
  
          case WSAENOBUFS:
          case STATUS_INSUFFICIENT_RESOURCES:
          case STATUS_PAGEFILE_QUOTA:
          case STATUS_COMMITMENT_LIMIT:
          case STATUS_WORKING_SET_QUOTA:
          case STATUS_NO_MEMORY:
          case STATUS_QUOTA_EXCEEDED:
          case STATUS_TOO_MANY_PAGING_FILES:
          case STATUS_REMOTE_RESOURCES:
          case ERROR_NOT_ENOUGH_MEMORY:
              status = RPC_S_OUT_OF_MEMORY;
              break;
  
          case WSAECONNREFUSED:
          case STATUS_REMOTE_NOT_LISTENING:
          case STATUS_CONNECTION_REFUSED:
              // for conn refused, we have different logic for IPv4 and IPv6
              // since IPv6 is installed optionally at the time of this writing
              // and is not PnP compliant in the way we need, it will not be
              // picked up by the server by default. Yet the DNS entries will
              // be updated. So if we have an IPv6 address, the server may
              // not have picke dup IPv6 and we need to retry for IPv6 only.
              if (p->id == TCP_IPv6)
                  {
                  status = ERROR_RETRY;
                  }
              else
                  {
                  status = RPC_S_SERVER_UNAVAILABLE;
                  }
              break;
  
          default:
              VALIDATE(status)
                  {
                  WSAENETDOWN,
                  STATUS_INVALID_NETWORK_RESPONSE,
                  STATUS_NETWORK_BUSY,
                  STATUS_NO_SUCH_DEVICE,
                  STATUS_NO_SUCH_FILE,
                  STATUS_OBJECT_PATH_NOT_FOUND,
                  STATUS_OBJECT_NAME_NOT_FOUND,
                  STATUS_UNEXPECTED_NETWORK_ERROR,
                  WSAECONNABORTED,
                  STATUS_LOCAL_DISCONNECT,
                  STATUS_TRANSACTION_ABORTED,
                  STATUS_CONNECTION_ABORTED,
                  WSAEADDRINUSE,
                  ERROR_CONNECTION_REFUSED,
                  WSAECONNRESET
                  } END_VALIDATE;
  
              status = RPC_S_SERVER_UNAVAILABLE;
              break;
          }
  
      RpcpErrorAddRecord(EEInfoGCWinsock,
          status, 
          EEInfoDLWSOpen40);
  
      p->WS_CONNECTION::Abort();
  
      return(status);
  }
  
  
  /////////////////////////////////////////////////////////////////////
  //
  // TCP/IP specific stuff
  //
  
  RPC_STATUS
  IP_ADDRESS_RESOLVER::NextAddress(
      OUT SOCKADDR_STORAGE *pAddress
      )
  /*++
  
  Routine Description:
  
      Returns the next IP address associated with the Name
      parameter to the constructor.
  
      During the first call if check for loopback and for dotted numeric IP
      address formats.  If these fail then it begins a complex lookup
      (WSALookupServiceBegin) and returns the first available address.
  
      During successive calls in which a complex lookup was started
      it returns sucessive addressed returned by WSALookupServiceNext().
  
  Arguments:
  
      pAddress - If successful, the pAddress->sin_addr.s_addr member is set
          to an IP address to try. For all cosClients, if IPvToUse is ipvtuIPAny,
          pAddress->sin_family is set to the actual address family for the
          returned address. This allows client to find out what address was
          returned to them.
  
  Return Value:
  
      RPC_S_OK - pAddress contains a new IP address
  
      RPC_S_SERVER_UNAVAILABLE - Unable to find any more addresses
      RPC_S_OUT_OF_MEMORY
  
  --*/
  {
      int err;
      RPC_STATUS status;
      SOCKADDR_IN6 *IPv6Address = (SOCKADDR_IN6 *)pAddress;
      SOCKADDR_STORAGE addr;
      int ai_flags;
      int i;
      USES_CONVERSION;
      CStackAnsi AnsiName;
      BOOL fValidIPv4;
      BOOL fValidIPv6;
      ADDRINFO *ThisAddrInfo;
  
      if (!AddrInfo)
          {
          if (!Name)
              {
              if (cos == cosServer)
                  {
                  if (IPvToUse == ipvtuIPv6)
                      {
                      IPv6Address->sin6_flowinfo = 0;
                      *((u_long *)(IPv6Address->sin6_addr.s6_addr)    ) = 0;
                      *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 1) = 0;
                      *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 2) = 0;
                      *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 3) = 0;
                      IPv6Address->sin6_scope_id = 0;
                      }
                  else
                      {
                      ASSERT(IPvToUse == ipvtuIPv4);
                      ((SOCKADDR_IN *)pAddress)->sin_addr.s_addr = INADDR_ANY;
                      }
                  }
              else
                  {
                  if (LoopbacksReturned > 2)
                      {
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          RPC_S_SERVER_UNAVAILABLE, 
                          EEInfoDLNextAddress40);
                      return RPC_S_SERVER_UNAVAILABLE;
                      }
  
                  if ((IPvToUse == ipvtuIPv6)
                      || ((IPvToUse == ipvtuIPAny) 
                           && 
                           (LoopbacksReturned == 1)
                         )
                     )
                      {
                      IPv6Address->sin6_family = AF_INET6;
                      IPv6Address->sin6_flowinfo = 0;
                      *((u_long *)(IPv6Address->sin6_addr.s6_addr)    ) = 0;
                      *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 1) = 0;
                      *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 2) = 0;
                      *((u_long *)(IPv6Address->sin6_addr.s6_addr) + 3) = 1;
                      IPv6Address->sin6_scope_id = 0;
                      }
                  else if ((IPvToUse == ipvtuIPv4)
                           || ((IPvToUse == ipvtuIPAny) 
                               && 
                               (LoopbacksReturned == 0)
                             )
                          )
                      {
                      // Loopback - assign result of htonl(INADDR_LOOPBACK)
                      // Little-endian dependence.
                      ((SOCKADDR_IN *)pAddress)->sin_addr.s_addr = 0x0100007F;
                      ((SOCKADDR_IN *)pAddress)->sin_family = AF_INET;
                      }
                  else
                      {
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          RPC_S_SERVER_UNAVAILABLE, 
                          EEInfoDLNextAddress40);
                      return RPC_S_SERVER_UNAVAILABLE;
                      }
  
                  LoopbacksReturned ++;
                  }
  
              return RPC_S_OK;
              }
  
          if (cos == cosServer)
              ai_flags = AI_PASSIVE;
          else
              ai_flags = 0;
  
          switch (IPvToUse)
              {
              case ipvtuIPAny:
                  // make a hint for any protocol
                  Hint.ai_flags = ai_flags | AI_NUMERICHOST;
                  Hint.ai_family = PF_UNSPEC;
                  break;
  
              case ipvtuIPv4:
                  // make a hint for any v4 protocol
                  Hint.ai_flags = ai_flags | AI_NUMERICHOST;
                  Hint.ai_family = AF_INET;
                  break;
  
              case ipvtuIPv6:
                  // make a hint for TCPv6
                  Hint.ai_flags = ai_flags | AI_NUMERICHOST;
                  Hint.ai_family = AF_INET6;
                  break;
  
              default:
                  ASSERT((IPvToUse == ipvtuIPAny)
                      || (IPvToUse == ipvtuIPv4)
                      || (IPvToUse == ipvtuIPv6));
              }
  
          ATTEMPT_STACK_W2A(AnsiName, Name);
  
          err = getaddrinfo(AnsiName, 
              NULL, 
              &Hint,
              &AddrInfo);
  
          if (err)
              {
              ASSERT((err != EAI_BADFLAGS)
                  && (err != EAI_SOCKTYPE));
  
              // take down the numeric hosts flag - we'll try
              // to resolve it as a DNS name
              Hint.ai_flags &= ~AI_NUMERICHOST;
  
              err = getaddrinfo(AnsiName,
                  NULL,
                  &Hint,
                  &AddrInfo);
  
              if (err)
                  {
                  RpcpErrorAddRecord(EEInfoGCWinsock,
                      err, 
                      EEInfoDLNextAddress10,
                      Name);
                  if (err == EAI_MEMORY)
                      {
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          RPC_S_OUT_OF_MEMORY, 
                          EEInfoDLNextAddress20);
                      return RPC_S_OUT_OF_MEMORY;
                      }
                  else
                      {
                      VALIDATE(err)
                          {
                          EAI_AGAIN,
                          EAI_FAMILY,
                          EAI_FAIL,
                          EAI_NODATA,
                          EAI_NONAME,
                          EAI_SERVICE
                          } END_VALIDATE;
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          RPC_S_SERVER_UNAVAILABLE, 
                          EEInfoDLNextAddress30);
                      return RPC_S_SERVER_UNAVAILABLE;
                      }
                  }
  
              }
  
          // successfully resolved this address
          // just stick it in and we'll let the code below handle it
          CurrentAddrInfo = AddrInfo;
          }
  
      ASSERT(AddrInfo != NULL);
  
      // get the next value from the cache
      while (CurrentAddrInfo)
          {
          ThisAddrInfo = CurrentAddrInfo;
          CurrentAddrInfo = CurrentAddrInfo->ai_next;
  
          fValidIPv4 = FALSE;
          fValidIPv6 = FALSE;
  
          if (ThisAddrInfo->ai_family == AF_INET)
              {
              fValidIPv4 = TRUE;
              }
  
          if (ThisAddrInfo->ai_family == AF_INET6)
              {
              fValidIPv6 = TRUE;
              }
  
          if ((IPvToUse == ipvtuIPv4) && !fValidIPv4)
              continue;
  
          if ((IPvToUse == ipvtuIPv6) && !fValidIPv6)
              continue;
  
          if ((IPvToUse == ipvtuIPAny) && !fValidIPv4 && !fValidIPv6)
              continue;
  
          if (ThisAddrInfo->ai_family == AF_INET)
              {
              ASSERT((IPvToUse == ipvtuIPv4)
                  || (IPvToUse == ipvtuIPAny));
              RpcpCopyIPv4Address((SOCKADDR_IN *)ThisAddrInfo->ai_addr, (SOCKADDR_IN *)pAddress);
              ((SOCKADDR_IN *)pAddress)->sin_family = AF_INET;
              }
          else
              {
              ASSERT((IPvToUse == ipvtuIPv6)
                  || (IPvToUse == ipvtuIPAny));
              RpcpCopyIPv6Address((SOCKADDR_IN6 *)ThisAddrInfo->ai_addr, IPv6Address);
              IPv6Address->sin6_family = AF_INET6;
              IPv6Address->sin6_scope_id = ((SOCKADDR_IN6 *)ThisAddrInfo->ai_addr)->sin6_scope_id;
              IPv6Address->sin6_flowinfo = 0;
              }
  
          return RPC_S_OK;
          }
  
      RpcpErrorAddRecord(EEInfoGCWinsock,
          RPC_S_SERVER_UNAVAILABLE, 
          EEInfoDLNextAddress40);
  
      return RPC_S_SERVER_UNAVAILABLE;
  }
  
  IP_ADDRESS_RESOLVER::~IP_ADDRESS_RESOLVER()
  {
      if (AddrInfo)
          freeaddrinfo(AddrInfo);
  }
  
  RPC_STATUS
  IP_BuildAddressVector(
      OUT NETWORK_ADDRESS_VECTOR **ppAddressVector,
      IN ULONG NICFlags,
      IN RPC_CHAR *NetworkAddress OPTIONAL,
      IN WS_ADDRESS *Address OPTIONAL
      )
  /*++
  
  Routine Description:
  
      Builds a vector of IP addresses supported by this machine.
  
  Arguments:
  
      ppAddressVector - A place to store the vector.  If a non-NULL
          pointer is passed in, the memory will be freed before returning
          a new vector.  Queries should always specify *ppAddressVector == NULL.
      NICFlags - the flags as specified in the RPC_POLICY of the
          RpcServerUse*Protseq* APIs
      NetworkAddess - the network address we were asked to listen
          on. May be NULL.
      Address - in the case of firewall, the addresses we chose to
          listen on.
  
  Return Value:
  
      RPC_S_OK
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
  
  --*/
  {
      //
      // Figure out all of our IP addresses
      //
      NETWORK_ADDRESS_VECTOR *pVector;
      unsigned i, iActive;
      RPC_CHAR *NextAddress;
      int NumberOfAddresses;
      WS_ADDRESS *CurrentAddress;
  
      // If we were called with an old address vector, delete it.
      if (*ppAddressVector)
          {
          delete (*ppAddressVector);
          }
  
      if ((pFirewallTable == 0 || NICFlags == RPC_C_BIND_TO_ALL_NICS) && !NetworkAddress)
          {
          ULONG Ignored;
  
          // Get Dns hostname
          pVector = (NETWORK_ADDRESS_VECTOR *)AllocateAndGetComputerName(cnaNew,
              ComputerNameDnsHostname,
              FIELD_OFFSET(NETWORK_ADDRESS_VECTOR, NetworkAddresses[1]),
              FIELD_OFFSET(NETWORK_ADDRESS_VECTOR, NetworkAddresses[1]),
              &Ignored);
          if (pVector == NULL)
              {
              RpcpErrorAddRecord(EEInfoGCWinsock,
                  RPC_S_OUT_OF_MEMORY, 
                  EEInfoDLIPBuildAddressVector10,
                  GetLastError());
              return RPC_S_OUT_OF_MEMORY;
              }
  
          pVector->Count = 1;
          pVector->NetworkAddresses[0] = (RPC_CHAR*)&pVector->NetworkAddresses[1];
          }
      else if (NetworkAddress)
          {
          // the length of the network address including the terminating NULL
          // (in characters)
          int NetworkAddressLength;
  #if DBG
              {
              // if we have a network address, it must be in IP address notation
              // make an ASSERT to verify that nobody ever passes an dns name here
              ADDRINFO Hint;
              ADDRINFO *AddrInfo;
              USES_CONVERSION;
              CStackAnsi AnsiName;
              int err;
  
              RpcpMemorySet(&Hint, 0, sizeof(Hint));
              Hint.ai_flags = AI_NUMERICHOST;
              ATTEMPT_STACK_W2A(AnsiName, NetworkAddress);
  
              err = getaddrinfo(AnsiName,
                  NULL,
                  &Hint,
                  &AddrInfo);
  
              // this is a numeric address. It should never fail
              ASSERT (!err);
              }
  #endif
  
          NetworkAddressLength = RpcpStringLength(NetworkAddress) + 1;
  
          pVector = (NETWORK_ADDRESS_VECTOR *)
                I_RpcAllocate(  sizeof(NETWORK_ADDRESS_VECTOR)
                              + sizeof(RPC_CHAR *)
                              + (sizeof(RPC_CHAR) * NetworkAddressLength));
          if (pVector == NULL)
              {
              return (RPC_S_OUT_OF_MEMORY);
              }
  
          pVector->Count = 1;
  
          NextAddress = (RPC_CHAR *)&pVector->NetworkAddresses[1];
  
          pVector->NetworkAddresses[0] = NextAddress;
  
          RpcpMemoryCopy(NextAddress, NetworkAddress, NetworkAddressLength * 2);
          }
      else
          {
          NumberOfAddresses = 0;
          CurrentAddress = Address;
          while (CurrentAddress != NULL)
              {
              NumberOfAddresses ++;
              CurrentAddress = (WS_ADDRESS *)CurrentAddress->pNextAddress;
              }
  
          pVector = (NETWORK_ADDRESS_VECTOR *)
                I_RpcAllocate(  sizeof(NETWORK_ADDRESS_VECTOR)
                              + (sizeof(RPC_CHAR *)
                              + max(IPv6_MAXIMUM_RAW_NAME, IP_MAXIMUM_RAW_NAME) * sizeof(RPC_CHAR))
                                * ((WS_ADDRESS *)Address->pFirstAddress)->NumActiveAddresses);
          if (pVector == NULL)
              {
              return (RPC_S_OUT_OF_MEMORY);
              }
  
          // Only populate the vector with addresses that have been initialized.
          // It is possible that the list contains some address object that have
          // not yet had their DHCP address assigned - they should be skipped.
          pVector->Count = ((WS_ADDRESS *)Address->pFirstAddress)->NumActiveAddresses;
  
          NextAddress = (RPC_CHAR*)&pVector->NetworkAddresses[NumberOfAddresses];
          IN_ADDR addr;
          SOCKADDR_IN6 *Ipv6Address;
          unsigned j;
  
          CurrentAddress = Address;
          
          iActive = 0;
          for (i = 0; i < NumberOfAddresses; i++)
              {
              if (CurrentAddress->fAddressInitialized)
                  {
                  pVector->NetworkAddresses[iActive] = NextAddress;
                  iActive++;
  
                  if (CurrentAddress->id != TCP_IPv6)
                      {
                      addr.s_addr = ((SOCKADDR_IN *)(&CurrentAddress->ListenAddr.inetaddr))->sin_addr.s_addr;
  
                      swprintf((RPC_SCHAR *)NextAddress, RPC_CONST_SSTRING("%d.%d.%d.%d"),
                               addr.s_net, addr.s_host, addr.s_lh, addr.s_impno);
                      }
                  else
                      {
                      Ipv6Address = (SOCKADDR_IN6 *)(&CurrentAddress->ListenAddr.inetaddr);
                  
  
                      swprintf((RPC_SCHAR *)NextAddress, RPC_CONST_SSTRING("%X:%X:%X:%X:%X:%X:%X:%X"),
                               Ipv6Address->sin6_addr.u.Word[0],
                               Ipv6Address->sin6_addr.u.Word[1],
                               Ipv6Address->sin6_addr.u.Word[2],
                               Ipv6Address->sin6_addr.u.Word[3],
                               Ipv6Address->sin6_addr.u.Word[4],
                               Ipv6Address->sin6_addr.u.Word[5],
                               Ipv6Address->sin6_addr.u.Word[6],
                               Ipv6Address->sin6_addr.u.Word[7]
                               );
                      }
  
                  NextAddress += max(IPv6_MAXIMUM_RAW_NAME, IP_MAXIMUM_RAW_NAME);
                  }
  
              CurrentAddress = (WS_ADDRESS *)CurrentAddress->pNextAddress;
              }
          }
  
  
      *ppAddressVector = pVector;
  
      return(RPC_S_OK);
  }
  
  
  RPC_STATUS
  WS_Bind(
      IN SOCKET sock,
      IN OUT WS_SOCKADDR *pListenAddr,
      IN BOOL IpProtocol,
      IN DWORD EndpointFlags
      )
  /*++
  
  Routine Description:
  
      Binds the socket to a port.  Takes into account the endpoint flags
      and the value of the port in the WS_SOCKADDR.  There is IP specific
      code for firewalls which is keyed off of the EndpointFlags.
  
  Arguments:
  
      sock - The socket to bind
      pListenAddr - On input the sin_port member is checked. For fixed endpoints
          this is set and is the only address bound to.  On output it contains
          the info on the fully bound socket.
      IpProtocol - Whether this is an IP protocol.  TRUE for TCP/IP and HTTP.
      EndpointFlags - see RpcTrans.hxx.  If non-zero and we're allocating a
          dynamic port then we must call the runtime.
  
  Return Value:
  
      RPC_S_OK
  
      RPC_S_DUPLICATE_ENDPOINT : when a fixed endpoint is already in use.
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
      RPC_S_CANT_CREATE_ENDPOINT
  
  --*/
  {
      RPC_STATUS status;
      int Retries = 0;
      BOOL fFirewallPorts = FALSE;
      BOOL fSetSockOptFailed;
      DWORD LastError;
      USHORT Port;
  
      if (IpProtocol && (RpcpGetIpPort(pListenAddr) == 0))
          {
          Retries = 8;
          }
  
      // Protect the socket to prevent another server from using our port.
      if (WS_ProtectListeningSocket(sock, TRUE) != 0)
          {
          return RPC_S_CANT_CREATE_ENDPOINT;
          }
  
      do
          {
          if (Retries)
              {
              status = I_RpcServerAllocateIpPort(EndpointFlags, &Port);
  
              if (status != RPC_S_OK)
                  {
                  RpcpErrorAddRecord(EEInfoGCRuntime,
                      status, 
                      EEInfoDLWSBind10,
                      EndpointFlags);
                  break;
                  }
  
              RpcpSetIpPort(pListenAddr, Port);
  
              // Check if any firewall ports are defined. If they are remember
              // that so we can map the error correctly.
  
              if (!fFirewallPorts && (RpcpGetIpPort(pListenAddr) == 0))
                  {
                  Retries = 0;
                  }
              else
                  {
                  Retries--;
                  fFirewallPorts = TRUE;
                  }
  
              RpcpSetIpPort(pListenAddr, htons(RpcpGetIpPort(pListenAddr)));
              }
          else
              {
              status = RPC_S_OK;
              }
  
  WS_Bind_Rebind:
          if ( bind(sock,
                    &pListenAddr->generic,
                    sizeof(WS_SOCKADDR)) )
              {
  
              LastError = GetLastError();
              switch(LastError)
                  {
                  case WSAEACCES:
                      // This error can only be returned when a bind is done for a socket
                      // with SO_REUSEADDR following a bind with SO_EXCLUSIVEADDRUSE.  Since
                      // we never bind with SO_REUSEADDR we should not see this error unless
                      // the port is being allocated dynamically and happens to use
                      // an already listening port.
                      ASSERT(Retries > 0);
                      status = RPC_S_DUPLICATE_ENDPOINT;
                      break;
  
                  case WSAEADDRINUSE:
                      {
                      status = RPC_S_DUPLICATE_ENDPOINT;
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          status, 
                          EEInfoDLWSBind45,
                          (ULONG)ntohs(RpcpGetIpPort(pListenAddr)));
                      break;
                      }
  
                  case WSAENOBUFS:
                      {
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          LastError, 
                          EEInfoDLWSBind40);
                      status = RPC_S_OUT_OF_MEMORY;
                      break;
                      }
  
                  default:
                      {
                      RpcpErrorAddRecord(EEInfoGCWinsock,
                          LastError, 
                          EEInfoDLWSBind50);
                      status = RPC_S_CANT_CREATE_ENDPOINT;
                      break;
                      }
                  }
              }
          }
      while ( (status == RPC_S_DUPLICATE_ENDPOINT) && Retries);
  
      if (status != RPC_S_OK)
          {
          if (fFirewallPorts && status == RPC_S_DUPLICATE_ENDPOINT)
              {
              status = RPC_S_OUT_OF_RESOURCES;
              }
  
          RpcpErrorAddRecord(EEInfoGCWinsock,
              status, 
              EEInfoDLWSBind30);
          return(status);
          }
  
      int length = sizeof(WS_SOCKADDR);
      if (getsockname(sock, &pListenAddr->generic, &length))
          {
          return(RPC_S_OUT_OF_RESOURCES);
          }
  
      return(RPC_S_OK);
  }
  
  RPC_STATUS
  CDP_BuildAddressVector(
      OUT NETWORK_ADDRESS_VECTOR **ppAddressVector
      )
  /*++
  
  Routine Description:
  
      Look up the Cluster node number of this node and build
      the appropriate vector
  
  Arguments:
  
      ppAddressVector - A place to store the vector.
  
  Return Value:
  
      RPC_S_OK
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
  
  --*/
  {
      HKEY ParamsKey;
      NETWORK_ADDRESS_VECTOR * pVector;
      const RPC_CHAR *ClussvcParams =
                  RPC_CONST_STRING("System\\CurrentControlSet\\Services\\ClusSvc\\Parameters");
      RPC_CHAR *ClusRegNodeId = RPC_STRING_LITERAL("NodeId");
      DWORD KeyType;
      RPC_CHAR NodeIdString[ CDP_MAXIMUM_RAW_NAME ];
      DWORD StringLength = CDP_MAXIMUM_RAW_NAME * sizeof( RPC_CHAR );
      DWORD status;
  
      //
      // open the Clussvc parameters key and extrace the NodeId
      //
  
      status = RegOpenKey( HKEY_LOCAL_MACHINE,
                           (const RPC_SCHAR *)ClussvcParams,
                           &ParamsKey );
  
      if ( status != ERROR_SUCCESS )
          {
          return RPC_S_INVALID_NET_ADDR;
          }
  
      status = RegQueryValueEx(ParamsKey,
                               (const RPC_SCHAR *)ClusRegNodeId,
                               NULL,
                               &KeyType,
                               (LPBYTE)NodeIdString,
                               &StringLength);
  
      RegCloseKey( ParamsKey );
  
      if ( status != ERROR_SUCCESS ||
           KeyType != REG_SZ ||
                  (( StringLength / sizeof( RPC_CHAR )) > CDP_MAXIMUM_RAW_NAME ))
          {
          return RPC_S_INVALID_NET_ADDR;
          }
  
      pVector = (NETWORK_ADDRESS_VECTOR *)
          I_RpcAllocate( sizeof(NETWORK_ADDRESS_VECTOR ) +
                         sizeof(RPC_CHAR *) +
                         StringLength );
  
      if (pVector == NULL)
          {
          return(RPC_S_OUT_OF_MEMORY);
          }
  
      pVector->Count = 1;
      pVector->NetworkAddresses[0] = (RPC_CHAR *)&pVector->NetworkAddresses[1];
  
      RpcpStringCopy( pVector->NetworkAddresses[0], NodeIdString );
  
      *ppAddressVector = pVector;
  
      return RPC_S_OK;
  }
  
  typedef struct tagIPVersionSettings
  {
      IPVersionToUse IPVersion;
      BOOL fUseIPv6;
  } IPVersionSettings;
  
  const static IPVersionSettings ListenIPVersionSettings[2] = {{ipvtuIPv4, FALSE}, {ipvtuIPv6, TRUE}};
  
  typedef enum tagIPVersionSettingsIndexes
  {
      ipvsiIPv4SettingsIndex = 0,
      ipvsiIPv6SettingsIndex
  } IPVersionSettingsIndexes;
  
  typedef struct tagListenAddressListElement
  {
      INT ProtocolId;
      IPVersionSettingsIndexes IPVersionSettingsIndex;
      BOOL fSuccessfullyInitialized;
      RPC_STATUS ErrorCode;
      union
          {
          SOCKADDR_IN6 inet6Addr;
          SOCKADDR_IN inetAddr;
          } u;
  } ListenAddressListElement;
  
  typedef struct tagTCPServerListenParams
  {
      INT ProtocolId;
      IPVersionSettingsIndexes IPVersionSettingsIndex;
  } TCPServerListenParams;
  
  const static TCPServerListenParams HTTPListenParams[1] = {HTTP, ipvsiIPv4SettingsIndex};
  const static TCPServerListenParams TCPListenParams[2] = 
      {{TCP, ipvsiIPv4SettingsIndex}, 
      {TCP_IPv6, ipvsiIPv6SettingsIndex}};
  const int MAX_TCP_SERVER_LISTEN_LOOP_ITERATIONS = 2;
  
  RPC_STATUS
  TCP_ServerListenEx(
      IN RPC_TRANSPORT_ADDRESS ThisAddress,
      IN RPC_CHAR *NetworkAddress,
      IN OUT RPC_CHAR * *pEndpoint,
      IN UINT PendingQueueSize,
      IN PSECURITY_DESCRIPTOR SecurityDescriptor,
      IN ULONG EndpointFlags,
      IN ULONG NICFlags,
      IN BOOL fHttp
      )
  /*++
  
  Routine Description:
  
      This routine allocates a port to receive new client connections.
      If successful a call to COMMON_ServerCompleteListen() will actually allow
      new connection callbacks to the RPC runtime to occur. If the runtime
      is unable to complete then it must abort the address by calling
      WS_ServerAbortListen().
  
  Arguments:
  
      ThisAddress - A pointer to the loadable transport interface address.
      NetworkAddress - the address to listen on. This can be specified for
          IP only, and it *cannot* be a DNS name. If it is, this function
          will work incorrectly for multihomed/multi IP machines.
      pEndpoint - Optionally, the endpoint (port) to listen on. Set to
           to listened port for dynamically allocated endpoints.
      PendingQueueSize - Count to call listen() with.
      EndpointFlags - Flags that control dynamic port allocation
      NICFlags - Flags that control network (IP) address binding
      SecurityDescriptor - Meaningless for TCP
  
  Return Value:
  
      RPC_S_OK
  
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
      RPC_S_CANT_CREATE_ENDPOINT
      RPC_S_DUPLICATE_ENDPOINT
  
  --*/
  {
      PWS_ADDRESS pAddress = (PWS_ADDRESS)ThisAddress;
      RPC_STATUS status;
      WS_SOCKADDR *sockaddr = &(pAddress->ListenAddr);
      unsigned i;
      PWS_ADDRESS pList, pOld;
      int NeededAddressListSize;
      ListenAddressListElement *ListenAddressList;
      ListenAddressListElement *CurrentListElement;
      int AddressListSize;
      SOCKADDR_IN IPv4Address;
      SOCKADDR_IN6 *IPv6Address;
      int LoopIterations;
      TCPServerListenParams *ParamsToUse;
      IPVersionSettingsIndexes IPVersionSettingsIndex;
      BOOL fAtLeastOneAddressInitialized;
      USHORT PortNumber;      // in network byte order!
      BOOL fPortNumberInitialized;
      BOOL fDynamicEndpoint;
      BOOL fFatalErrorEncountered;
      RPC_STATUS FatalErrorCode;
      WS_ADDRESS *LastSuccessfullyInitializedAddress;
      WS_ADDRESS *NextAddress;
      BOOL fDualTransportConfiguration = FALSE;
      BOOL fLoopbackAddressProcessed;
      FIREWALL_INFO *pCopyOfFirewallTable = NULL;
  
      RequestGlobalMutex();
      // Get a copy of pFirewallTable while in the critical section
      // to protect against a race with the PnP table update.
      // We should only access the local copy and remember to delete it.
      pCopyOfFirewallTable = GetFirewallTableCopy();
  
      if (pFirewallTable != NULL &&
          pCopyOfFirewallTable == NULL)
          {
          ClearGlobalMutex();
          return RPC_S_OUT_OF_MEMORY;
          }
      ClearGlobalMutex();
  
      // sizing pass - allocate sufficient memory
      if (pCopyOfFirewallTable == 0 || NICFlags == RPC_C_BIND_TO_ALL_NICS)
          {
          if (fHttp)
              AddressListSize = 1;
          else
              {
              AddressListSize = 2;
              fDualTransportConfiguration = TRUE;
              }
          }
      else
          {
          AddressListSize = pCopyOfFirewallTable->NumAddresses + 1;
          }
  
      NeededAddressListSize = AddressListSize * sizeof(ListenAddressListElement);
  
      ListenAddressList = new ListenAddressListElement[NeededAddressListSize];
      if (ListenAddressList == NULL)
          {
          delete pCopyOfFirewallTable;
          return RPC_S_OUT_OF_MEMORY;
          }
  
      fAtLeastOneAddressInitialized = FALSE;
      RpcpMemorySet(ListenAddressList, 
          0,
          NeededAddressListSize);
  
      // processing pass. Set all the required addresses in the array
      if (pCopyOfFirewallTable == 0 || NICFlags == RPC_C_BIND_TO_ALL_NICS)
          {
          if (fHttp)
              {
              ASSERT(AddressListSize == 1);
              ParamsToUse = (TCPServerListenParams *)HTTPListenParams;
              LoopIterations = 1;
              }
          else
              {
              ParamsToUse = (TCPServerListenParams *)TCPListenParams;
              LoopIterations = 2;
              }
  
          for (i = 0; i < LoopIterations; i ++)
              {
              CurrentListElement = &ListenAddressList[i];
              CurrentListElement->ProtocolId = ParamsToUse[i].ProtocolId;
              IPVersionSettingsIndex = ParamsToUse[i].IPVersionSettingsIndex;
              CurrentListElement->IPVersionSettingsIndex = IPVersionSettingsIndex;
  
              IP_ADDRESS_RESOLVER resolver(NetworkAddress, 
                  cosServer,
                  ListenIPVersionSettings[IPVersionSettingsIndex].IPVersion       // IP version to use
                  );
  
              // resolve the address. Since this cannot be a DNS name, we will resolve
              // to one address at most. We choose the ipv6 address, because it has space
              // for both. The actual parameter that determines the type of name resolution
              // to be done is IP version to use passed to the constructor
              status = resolver.NextAddress((SOCKADDR_STORAGE *)&CurrentListElement->u.inet6Addr);
              if (status == RPC_S_OK)
                  {
                  fAtLeastOneAddressInitialized = TRUE;
                  CurrentListElement->fSuccessfullyInitialized = TRUE;
                  }
              }
          }
      else
          {
          fAtLeastOneAddressInitialized = TRUE;
          fLoopbackAddressProcessed = FALSE;
  
          for (i = 0; i < AddressListSize; i++)
              {
              CurrentListElement = &ListenAddressList[i];
  
              if (fHttp)
                  {
                  CurrentListElement->ProtocolId = HTTP;
                  }
              else 
                  {
                  CurrentListElement->ProtocolId = TCP;                
                  }
  
              CurrentListElement->IPVersionSettingsIndex = ipvsiIPv4SettingsIndex;
              CurrentListElement->fSuccessfullyInitialized = TRUE;
  
              if (i == pCopyOfFirewallTable->NumAddresses)
                  {
                  CurrentListElement->u.inetAddr.sin_addr.s_addr = 0x0100007F;
                  }
              else
                  {
                  if (pCopyOfFirewallTable->Entries[i].Address == 0x0100007F)
                      fLoopbackAddressProcessed = TRUE;
                  CurrentListElement->u.inetAddr.sin_addr.s_addr = pCopyOfFirewallTable->Entries[i].Address;
                  }
              }
  
          // if the loopback address was in the firewall configuration, 'forget' about
          // the last entry we added for the loopback address. Otherwise, we'll have
          // it twice in the list, and this will cause errors
          if (fLoopbackAddressProcessed)
              {
              AddressListSize --;
              // since we added one, and fLoopbackAddressProcessed is set only if
              // we find something in the list, then AddressListSize must still be
              // greater than 0.
              ASSERT(AddressListSize > 0);
              }
          }
  
      if (fAtLeastOneAddressInitialized)
          {
          fAtLeastOneAddressInitialized = FALSE;
          }
      else
          {
          // the only place where we can fail so far is name resolution. If this
          // fails, return the status
          delete [] ListenAddressList;
          delete pCopyOfFirewallTable;
          return status;
          }
  
      // Figure out what port to bind to.
  
      if (*pEndpoint)
          {
          status = EndpointToPortNumber(*pEndpoint, PortNumber);
          if (status != RPC_S_OK)
              {
              delete [] ListenAddressList;
              delete pCopyOfFirewallTable;
              return(status);
              }
  
          PortNumber = htons(PortNumber);
          fDynamicEndpoint = 0;
          fPortNumberInitialized = TRUE;
          }
      else
          {
          PortNumber = 0;
          fDynamicEndpoint = TRUE;
          fPortNumberInitialized = FALSE;
          }
  
      // zoom in through the array address, and listen on all successfully initialized
      // protocols
      pList = pAddress;
  
      pAddress->NumActiveAddresses = 0;
  
      fFatalErrorEncountered = FALSE;
  
      for (i = 0; i < AddressListSize; i ++)
          {
          CurrentListElement = &ListenAddressList[i];
          if (!CurrentListElement->fSuccessfullyInitialized)
              continue;
  
          if (pList == 0)
              {
              pList = new WS_ADDRESS;
              if (pList == 0)
                  {
                  fFatalErrorEncountered = TRUE;
  
                  FatalErrorCode = RPC_S_OUT_OF_MEMORY;
                  break;
                  }
  
              pOld->pNextAddress = pList;
              }
  
          sockaddr = &(pList->ListenAddr);
  
          RpcpMemorySet(sockaddr, 0, sizeof(*sockaddr));
  
          // the port we have set is already in network byte order - 
          // no need to change it
          RpcpSetIpPort(sockaddr, PortNumber);
  
          pList->fDynamicEndpoint = fDynamicEndpoint;
  
          if (ListenIPVersionSettings[CurrentListElement->IPVersionSettingsIndex].fUseIPv6)
              {
              ((SOCKADDR_IN6 *)sockaddr)->sin6_flowinfo = 0;
              RpcpCopyIPv6Address(&CurrentListElement->u.inet6Addr, (SOCKADDR_IN6 *)sockaddr);
              }
          else
              {
              RpcpCopyIPv4Address(&CurrentListElement->u.inetAddr, (SOCKADDR_IN *)sockaddr);
              }
  
          pList->id = CurrentListElement->ProtocolId;
          pList->NewConnection = WS_NewConnection;
          pList->SubmitListen = WS_SubmitAccept;
          SetProtocolMultiplier(pList, 1);
          pList->pAddressVector = 0;
          pList->Endpoint = 0;
          pList->QueueSize = PendingQueueSize;
          pList->EndpointFlags = EndpointFlags;
          pList->fAddressInitialized = TRUE;
  
          pList->pFirstAddress = pAddress;
          pList->pNextAddress = NULL;
  
          sockaddr->generic.sa_family = WsTransportTable[CurrentListElement->ProtocolId].AddressFamily;
  
          // If we are running with the firewall address table
          // and the address has not yet been initialized or enabled, delay listening on it.
          if (pCopyOfFirewallTable != NULL &&
              (pCopyOfFirewallTable->Entries[i].fInitialized == FALSE
               || pCopyOfFirewallTable->Entries[i].fEnabled == FALSE
              )
             )
              {
              ASSERT(pCopyOfFirewallTable->NumAddresses > i);
  
              pList->InAddressList = Inactive;
  
              // We need to register the address to receive
              // PnP notifications.  This is necessary to allow
              // the initialization and activation of the address later on
              // when the corresponding interface has been initialized.
              TransportProtocol::AddObjectToProtocolList(pList);
  
              pList->type = ADDRESS;
  
              // Record that the address has not yet been initialized and
              // keep track of the interface index so that we can later use
              // the pFirewallTable to initialize this address.
              pList->fAddressInitialized = FALSE;
              }
          // Either selective bindings are not used, or the address has been initialized.
          else
              {
              // we must know whether we got WSAEAFNOSUPPORT when we opened the socket
              // if yes, we must record this in CurrentElement, and we must not blow
              // the address. If we got something else, we should abort even in dual
              // transport config. The addresses on the firewall are a separate thing -
              // we ignore the ones we can't listen on.
  
              pAddress->NumActiveAddresses++;
  
              // Actually listen.
              // Pass in fResetAddressListEntries = FALSE since the address list
              // fields have already been initialized.
              status = WS_ServerListenCommon(pList, FALSE);
  
              if (status != RPC_S_OK)
                  {
                  if ((status == RPC_S_DUPLICATE_ENDPOINT) 
                      || (fDualTransportConfiguration && (status != RPC_P_ADDRESS_FAMILY_INVALID)))
                      {
                      // if either somebody else is listening on our port for this address,
                      // or this is a dual transport configuratuon, and we fail to listen
                      // on one of the transports for reasons other that it not being
                      // installed, bail out
                      fFatalErrorEncountered = TRUE;
                      FatalErrorCode = status;
                      break;
                      }
                  else if (fDualTransportConfiguration && (status == RPC_P_ADDRESS_FAMILY_INVALID))
                      {
                      pList->InAddressList = Inactive;
  
                      // we still need to register the address with PnP
                      // make sure it's not already there
                      ASSERT(RpcpIsListEmpty(&pList->ObjectList));
                      TransportProtocol::AddObjectToProtocolList(pList);
                      }
  
                  CurrentListElement->ErrorCode = status;
  
                  CurrentListElement->fSuccessfullyInitialized = FALSE;
                  }
              else
                  {
                  if (!fPortNumberInitialized)
                      {
                      PortNumber = RpcpGetIpPort(&(pList->ListenAddr));
                      fPortNumberInitialized = TRUE;
                      }
                  fAtLeastOneAddressInitialized = TRUE;
                  }
              }
  
          pOld = pList;
          pList = 0;
          }
  
      // If at least one address has initialized then that address should have picked
      // the dynamic endpoint to listen on:
      // fAtLeastOneAddressInitialized -> fPortNumberInitialized
      ASSERT(!fAtLeastOneAddressInitialized || fPortNumberInitialized);
  
      // compact the list by removing addresses we couldn't successfully listen on
      pList = pAddress;
      for (i = 0; i < AddressListSize; i ++)
          {
          // we may have an early break if a memory allocation failed
          if (pList == NULL)
              break;
  
          // if this address has not initialized successfully, and this is not
          // the first address, and either none of the elements initialized
          // successfully or this is not a dual transport configuration with
          // error RPC_P_ADDRESS_FAMILY_INVALID and this is not a
          // delay-initialized address under dynamic binding that has not yet been
          // assigned an IP address, delete the element.
          if (!ListenAddressList[i].fSuccessfullyInitialized && (i > 0)
              && 
              (!fAtLeastOneAddressInitialized 
                  || (!fDualTransportConfiguration 
                      || (ListenAddressList[i].ErrorCode != RPC_P_ADDRESS_FAMILY_INVALID)
                     )
              )
              &&
              !(pCopyOfFirewallTable != NULL && pCopyOfFirewallTable->Entries[i].fInitialized == FALSE)
             )
              {
              ASSERT(pOld != pList);
              pOld->pNextAddress = pList->pNextAddress;
              NextAddress = (WS_ADDRESS *)pList->pNextAddress;
              TransportProtocol::RemoveObjectFromProtocolList(pList);
              delete pList;
              }
          else
              {
              pOld = pList;
              NextAddress = (WS_ADDRESS *)pList->pNextAddress;
              }
  
          pList = NextAddress;
          }
  
      if (!fAtLeastOneAddressInitialized)
          {
          TransportProtocol::RemoveObjectFromProtocolList(pAddress);
          delete [] ListenAddressList;
          delete pCopyOfFirewallTable;
          if (status == RPC_P_ADDRESS_FAMILY_INVALID)
              status = RPC_S_PROTSEQ_NOT_SUPPORTED;
          return status;
          }
  
      // the attempt to listen on dual protocol configurations may have left EEInfo
      // record in the TEB. If we're here, we have succeeded, and we can delete them
      RpcpPurgeEEInfo();
  
      if (!ListenAddressList[0].fSuccessfullyInitialized)
          {
          // here, pOld must be the last successfully initialized element
          // or the last element we haven't given up on (i.e. potentially
          // active through PnP)
          // It cannot be the first element, or we would have bailed out
          // by now. We cannot have only one element either
          ASSERT(pOld->pNextAddress == NULL);
          ASSERT(pAddress->pNextAddress != NULL);
  
          // here at least one has succeeded and all non-first failed
          // elements are deleted, though a fatal error may have been
          // encountered. We need to deal with the first element
          // because we don't want to expose elements with failed
          // initialization outside this routine.
          // 
          // We will leave the first element alone if it is a delay-initialized address
          // that has not yet been assigned a transport address.
          if (!(fDualTransportConfiguration && (ListenAddressList[0].ErrorCode == RPC_P_ADDRESS_FAMILY_INVALID))
              &&
              !(pCopyOfFirewallTable != NULL && pCopyOfFirewallTable->Entries[0].fInitialized == FALSE))
              {
              // remove the element we will copy to the first element
              TransportProtocol::RemoveObjectFromProtocolList(pOld);
  
              NextAddress = (WS_ADDRESS *)pAddress->pNextAddress;
              RpcpMemoryCopy(pAddress, pOld, sizeof(WS_ADDRESS));
              pAddress->pNextAddress = NextAddress;
  
              // find the element we just copied over the first, and free it
              LastSuccessfullyInitializedAddress = pOld;
              pList = pAddress;
              while (pList->pNextAddress != LastSuccessfullyInitializedAddress)
                  {
                  pList = (WS_ADDRESS *)pList->pNextAddress;
                  }
  
              delete pList->pNextAddress;
              pList->pNextAddress = NULL;
  
              // add the first element back on the list.
              TransportProtocol::AddObjectToProtocolList(pAddress);
              }
          }
  
      delete [] ListenAddressList;
      delete pCopyOfFirewallTable;
  
      // by now all elements in the list have listened successfully
      // or are in transport PnP state. However
      // if we encountered a fatal error, we need to abort any way
      if (fFatalErrorEncountered)
          {
          WS_ServerAbortListen(pAddress);
  
          // fatal error - bail out
          return FatalErrorCode;
          }
  
      // Listened okay
  
      // Figure out our network addresses
      ASSERT(pAddress->pAddressVector == NULL);
      status = IP_BuildAddressVector(
                                     &pAddress->pAddressVector,
                                     NICFlags,
                                     NetworkAddress,
                                     pAddress);
  
      if (status != RPC_S_OK)
          {
          WS_ServerAbortListen(pAddress);
          return(status);
          }
  
      // Return the dynamic port, if needed.
  
      if (!*pEndpoint)
          {
          *pEndpoint = new RPC_CHAR[6]; // 65535 max
          if (!*pEndpoint)
              {
              WS_ServerAbortListen(ThisAddress);
              return(RPC_S_OUT_OF_MEMORY);
              }
  
          PortNumber = ntohs(PortNumber);
  
          PortNumberToEndpoint(PortNumber, *pEndpoint);
          }
  
      // Save away the endpoint in the address, if needed, here.
      // (PnP?)
  
      return(status);
  }
  
  
  
  
  RPC_STATUS
  TCP_ServerListen(
      IN RPC_TRANSPORT_ADDRESS ThisAddress,
      IN RPC_CHAR *NetworkAddress,
      IN OUT RPC_CHAR * *pEndpoint,
      IN UINT PendingQueueSize,
      IN PSECURITY_DESCRIPTOR SecurityDescriptor,
      IN ULONG EndpointFlags,
      IN ULONG NICFlags
      )
  {
  
      return (TCP_ServerListenEx(
                  ThisAddress,
                  NetworkAddress,
                  pEndpoint,
                  PendingQueueSize,
                  SecurityDescriptor,
                  EndpointFlags,
                  NICFlags,
                  FALSE // Not HTTP
                  ));
  }
  
  RPC_STATUS
  WS_ConvertClientAddress (
      IN const SOCKADDR *ClientAddress,
      IN ULONG ClientAddressType,
      OUT RPC_CHAR **pNetworkAddress
      )
  /*++
  
  Routine Description:
  
      Converts a given IP address to a RPC network address
  
  Arguments:
  
      ClientAddress - the client IP address. Can be SOCKADDR_IN6
          for IPv6.
  
      ClientAddressType - TCP or TCP_IPv6
  
      NetworkAddress - Will contain string on success.
  
  Return Value:
  
      RPC_S_OK or other RPC_S_* errors for error
  
  --*/
  {
      USES_CONVERSION;
      CNlUnicode nlUnicode;
      int Result;
      int SocketLength;
      int HostLength;
      char *HostName;
      char *ScopeIdSeparator;
  
      ASSERT((ClientAddressType == TCP) || (ClientAddressType == TCP_IPv6));
  
      if (ClientAddressType == TCP)
          SocketLength = sizeof(SOCKADDR_IN);
      else
          SocketLength = sizeof(SOCKADDR_IN6);
  
      // allocate space for the numeric name plus the terminating NULL
      HostLength = max(IP_MAXIMUM_RAW_NAME, IPv6_MAXIMUM_RAW_NAME) + 1;
      HostName = (char *)alloca(HostLength);
  
      Result = getnameinfo(ClientAddress,
          SocketLength,
          HostName,
          HostLength,
          NULL,
          0,
          NI_NUMERICHOST);
  
      ASSERT(Result == 0);
  
      ScopeIdSeparator = strchr(HostName, '%');
      if (ScopeIdSeparator)
          {
          // if there is a scope separator, whack everything after
          // the scope separator (i.e. we don't care about the scope).
          *ScopeIdSeparator = 0;
          }
  
      ATTEMPT_NL_A2W(nlUnicode, HostName);
  
      *pNetworkAddress = (WCHAR *)nlUnicode;
      return(RPC_S_OK);
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  TCP_QueryClientAddress (
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      OUT RPC_CHAR **pNetworkAddress
      )
  /*++
  
  Routine Description:
  
      Returns the IP address of the client on a connection as a string.
      The clients address is saved when the client connects, so all
      we need to do is format the address.
  
  Arguments:
  
      ThisConnection - The server connection of interest.
      NetworkAddress - Will contain string on success.
  
  Return Value:
  
      RPC_S_OK or other RPC_S_* errors for error
  
  --*/
  {
      PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
  
      return WS_ConvertClientAddress((const SOCKADDR *)&p->saClientAddress.ipaddr,
          p->id,
          pNetworkAddress
          );
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  TCP_QueryLocalAddress (
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      IN OUT void *Buffer,
      IN OUT unsigned long *BufferSize,
      OUT unsigned long *AddressFormat
      )
  /*++
  
  Routine Description:
  
      Returns the local IP address of a connection.
  
  Arguments:
  
      ThisConnection - The server connection of interest.
  
      Buffer - The buffer that will receive the output address
  
      BufferSize - the size of the supplied Buffer on input. On output the
          number of bytes written to the buffer. If the buffer is too small
          to receive all the output data, ERROR_MORE_DATA is returned,
          nothing is written to the buffer, and BufferSize is set to
          the size of the buffer needed to return all the data.
  
      AddressFormat - a constant indicating the format of the returned address.
          Currently supported are RPC_P_ADDR_FORMAT_TCP_IPV4 and
          RPC_P_ADDR_FORMAT_TCP_IPV6. Undefined on failure.
  
  Return Value:
  
      RPC_S_OK or other RPC_S_* errors for error
  
  --*/
  {
      PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
      int MinimumBufferLength;
      int Result;
      const WS_TRANS_INFO *pInfo = &WsTransportTable[p->id];
  
      ASSERT(p->type & SERVER);
  
      if ((p->id == TCP) || (p->id == HTTP))
          {
          MinimumBufferLength = sizeof(SOCKADDR_IN);
          *AddressFormat = RPC_P_ADDR_FORMAT_TCP_IPV4;
          }
      else
          {
          ASSERT(p->id == TCP_IPv6);
          MinimumBufferLength = sizeof(SOCKADDR_STORAGE);
          *AddressFormat = RPC_P_ADDR_FORMAT_TCP_IPV6;
          }
  
      if (*BufferSize < MinimumBufferLength)
          {
          *BufferSize = MinimumBufferLength;
          return ERROR_MORE_DATA;
          }
  
      ASSERT(p->pAddress);
  
      p->StartingOtherIO();
  
      if (p->fAborted)
          {
          p->OtherIOFinished();
          return(RPC_S_NO_CONTEXT_AVAILABLE);
          }
  
      Result = setsockopt(p->Conn.Socket,
          SOL_SOCKET,
          SO_UPDATE_ACCEPT_CONTEXT,
          (char *)&p->pAddress->ListenSocket,
          sizeof(p->pAddress->ListenSocket) );
  
      if (Result != SOCKET_ERROR)
          {
          Result = getsockname(p->Conn.Socket,
              (sockaddr *)Buffer, 
              (int *) BufferSize);
  
          // SO_UPDATE_ACCEPT_CONTEXT has the nasty habit of deleting
          // all of our socker options. Restore them
          WS_SetSockOptForConnection(pInfo, p->Conn.Socket);
  
          p->OtherIOFinished();
  
          if (Result == SOCKET_ERROR)
              {
              RpcpErrorAddRecord(EEInfoGCWinsock,
                  RPC_S_OUT_OF_MEMORY,
                  EEInfoDLTCP_QueryLocalAddress10,
                  (ULONGLONG) p->Conn.Socket,
                  GetLastError());
              return RPC_S_OUT_OF_MEMORY;
              }
          }
      else
          {
          p->OtherIOFinished();
  
          RpcpErrorAddRecord(EEInfoGCWinsock,
              RPC_S_OUT_OF_MEMORY,
              EEInfoDLTCP_QueryLocalAddress20,
              (ULONGLONG) p->Conn.Socket,
              GetLastError());
          return RPC_S_OUT_OF_MEMORY;
          }
  
      return(RPC_S_OK);
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  TCP_QueryClientId(
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      OUT RPC_CLIENT_PROCESS_IDENTIFIER *ClientProcess
      )
  /*++
  
  Routine Description:
  
      For secure protocols (which TCP/IP is not) this is suppose to
      give an ID which will be shared by all clients from the same
      process.  This prevents one user from grabbing another users
      association group and using their context handles.
  
      Since TCP/IP is not secure we return the IP address of the
      client machine.  This limits the attacks to other processes
      running on the client machine which is better than nothing.
  
  Arguments:
  
      ThisConnection - Server connection in question.
      ClientProcess - Transport identification of the "client".
  
  Return Value:
  
      RPC_S_OUT
  
  --*/
  {
      PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
  
      ASSERT(p->type & SERVER);
  
      // Currently, we don't have an efficient way of determining which clients
      // are local, and which remote. Since some clients grant more permissions
      // to local clients, we want to be on the safe side, and return all TCP
      // clients as remote.
      ClientProcess->ZeroOut();
      if (p->id != TCP_IPv6)
          {
          ClientProcess->SetIPv4ClientIdentifier(p->saClientAddress.inetaddr.sin_addr.s_addr,
              FALSE   // fLocal
              );
          }
      else
          {
          ClientProcess->SetIPv6ClientIdentifier(&p->saClientAddress.ipaddr, 
              sizeof(p->saClientAddress.ipaddr),
              FALSE   // fLocal
              );
          }
  
      return(RPC_S_OK);
  }
  
  RPC_STATUS
  RPC_ENTRY
  TCP_QueryClientIpAddress (
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      IN OUT RPC_CLIENT_IP_ADDRESS *ClientIpAddress
      )
  /*++
  
  Routine Description:
  
      Returns the IP address of the client on a connection as a SOCKADDR.
      The clients address is saved when the client connects, so all
      we need to do is copy it.
  
  Arguments:
  
      ThisConnection - The server connection of interest.
  
      ClientIpAddress - the buffer to store the address to.
  
  Return Value:
  
      RPC_S_OK or other RPC_S_* errors for error
  
  --*/
  {
      ULONG BufferLength;
      PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
  
      if (p->id != TCP_IPv6)
          {
          BufferLength = sizeof(SOCKADDR_IN);
          }
      else
          {
          BufferLength = sizeof(SOCKADDR_IN6);
          }
  
      ASSERT(BufferLength <= sizeof(ClientIpAddress->Data));
  
      ClientIpAddress->DataSize = BufferLength;
      // we know both IPv4 and IPv6 start at the same offset. Just copy the respective
      // size starting at the offset.
      RpcpMemoryCopy (ClientIpAddress->Data, &p->saClientAddress.ipaddr, BufferLength);
  
      return RPC_S_OK;
  }
  
  RPC_STATUS
  TCPOrHTTP_Open(
      IN WS_CONNECTION *Connection,
      IN RPC_CHAR * NetworkAddress,
      IN USHORT Endpoint,
      IN UINT ConnTimeout,
      IN UINT SendBufferSize,
      IN UINT RecvBufferSize,
      IN OUT TCPResolverHint *Hint,
      IN BOOL  fHintInitialized,
      IN ULONG CallTimeout,
      IN BOOL fHTTP2Open,
      IN I_RpcProxyIsValidMachineFn IsValidMachineFn OPTIONAL
      )
  /*++
  
  Routine Description:
  
      Opens a connection to a server.
  
  Arguments:
  
      ThisConnection - A place to store the connection
      NetworkAddress - The name of the server, either a dot address or DNS name
      Endpoint - the port number in host byte order representation
      ConnTimeout - See RpcMgmtSetComTimeout
              0 - Min
              5 - Default
              9 - Max
              10 - Infinite
      SendBufferSize -
      RecvBufferSize - (Both optional) Specifies the size of the send/recv
          transport buffers.
      ResolverHint - Resolver hint
      fHintInitialized - If TRUE, the ResolveHint contains the IP address
          of the server. If FALSE, do standard name resolution.
      CallTimeout - the call timeout in milliseconds
      fHTTP2Open - non-zero if this is an HTTP2 Open
      IsValidMachineFn - a callback function that is used to validate machine/port
          for access from this process. Used by HTTP only.
  
  Return Value:
  
      RPC_S_OK
  
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
      RPC_S_SERVER_UNAVAILABLE
      RPC_S_INVALID_ENDPOINT_FORMAT
      RPC_S_INVALID_NET_ADDR
  
  --*/
  {
      RPC_STATUS status;
      RPC_STATUS status2;
      WS_SOCKADDR sa;
      PVOID rnrContext;
      BOOL fIPv4Hint;
      USES_CONVERSION;
      CStackAnsi AnsiName;
      BOOL NetworkAddressConverted;
      char *DotName;
      char *DotNamePtr;
      char DotNameBuffer[max(IP_MAXIMUM_RAW_NAME, IPv6_MAXIMUM_RAW_NAME) + 1]; 
  
      ASSERT(NetworkAddress);
  
      // All this function needs to is initialize transport specific
      // parts of the connection and sockaddr.  This includes resolving
      // the network address into a `raw' address.
  
      RpcpSetIpPort(&sa, htons(Endpoint));
  
      if (fHTTP2Open)
          {
          Connection->id = HTTPv2;
          NetworkAddressConverted = FALSE;
          }
      else
          Connection->id = TCP;
  
      // Two cases, previously saved address or first open
  
      if (fHintInitialized)
          {
          Hint->GetResolverHint(&fIPv4Hint, &sa);
  
          if (!fIPv4Hint)
              Connection->id = TCP_IPv6;
  
          ASSERT(IsValidMachineFn == FALSE);
  
          status = WS_Open((PWS_CCONNECTION)Connection, 
              &sa, 
              ConnTimeout, 
              SendBufferSize, 
              RecvBufferSize, 
              CallTimeout,
              fHTTP2Open
              );
          if (status == ERROR_RETRY)
              {
              status = RPC_S_SERVER_UNAVAILABLE;
              }
  
          return(status);
          }
  
      // Prepare to resolve the network address
  
      IP_ADDRESS_RESOLVER resolver(NetworkAddress, 
          cosClient,
          ipvtuIPAny       // IP version to use
          );
  
      // Loop until success, fatal failure or we run out of addresses.
  
      do
          {
          status = resolver.NextAddress(&sa.ipaddr);
  
          // check the list of valid machines first. If this is not a valid
          // machine, we don't want to return out information on whether the name
          // resolves or not
          if (IsValidMachineFn)
              {
              ASSERT(fHTTP2Open != FALSE);
              if (NetworkAddressConverted == FALSE)
                  {
                  ATTEMPT_STACK_W2A(AnsiName, NetworkAddress);
                  NetworkAddressConverted = TRUE;
                  }
  
              if (status == RPC_S_OK)
                  {
                  DotName = inet_ntoa(sa.inetaddr.sin_addr);
                  strcpy(DotNameBuffer, DotName);
                  DotNamePtr = DotNameBuffer;
                  }
              else
                  {
                  DotNamePtr = NULL;
                  }
  
              status2 = IsValidMachineFn(AnsiName,
                  DotNamePtr,
                  Endpoint
                  );
  
              if (status2 != RPC_S_OK)
                  {
                  // if we're out of names, no point in continuing
                  if (status != RPC_S_OK)
                      {
                      // move the status from the allowed check to the return value
                      status = status2;
                      break;
                      }
  
                  // if the server is resolved, but not allowed for access, continue with next
                  status = ERROR_RETRY;
                  continue;
                  }
              else if (status != RPC_S_OK)
                  {
                  // the server is allowed, but can't be resolved - break where we will return an error
                  break;
                  }
              }
  
          if (status != RPC_S_OK)
              {
              break;
              }
  
          if (sa.ipaddr.ss_family == AF_INET6)
              Connection->id = TCP_IPv6;
          else if (Connection->id == TCP_IPv6)
              {
              // if we were at IPv6 and we didn't select IPv6 this time, it must
              // be IPv4
              ASSERT(sa.ipaddr.ss_family == AF_INET);
              Connection->id = TCP;
              }
  
          // Call common open function
          status = WS_Open((PWS_CCONNECTION)Connection, 
              &sa, 
              ConnTimeout, 
              SendBufferSize, 
              RecvBufferSize, 
              CallTimeout,
              fHTTP2Open
              );
          }
      while (status == ERROR_RETRY);
  
      if (status == RPC_S_OK)
          {
          Hint->SetResolverHint((Connection->id == TCP) || (Connection->id == HTTPv2), &sa);
          }
  
      return(status);
  }
  
  
  
  
  RPC_STATUS
  RPC_ENTRY
  TCP_Open(
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      IN RPC_CHAR * ProtocolSequence,
      IN RPC_CHAR * NetworkAddress,
      IN RPC_CHAR * Endpoint,
      IN RPC_CHAR * NetworkOptions,
      IN UINT ConnTimeout,
      IN UINT SendBufferSize,
      IN UINT RecvBufferSize,
      IN OUT PVOID ResolverHint,
      IN BOOL  fHintInitialized,
      IN ULONG CallTimeout,
      IN ULONG AdditionalTransportCredentialsType, OPTIONAL
      IN void *AdditionalCredentials OPTIONAL
      )
  /*++
  
  Routine Description:
  
      Opens a connection to a server.
  
  Arguments:
  
      ThisConnection - A place to store the connection
      ProtocolSeqeunce - "ncacn_ip_tcp". Ignored in this function
      NetworkAddress - The name of the server, either a dot address or DNS name
      NetworkOptions - Ignored
      ConnTimeout - See RpcMgmtSetComTimeout
              0 - Min
              5 - Default
              9 - Max
              10 - Infinite
      SendBufferSize -
      RecvBufferSize - (Both optional) Specifies the size of the send/recv
          transport buffers.
      ResolverHint - IP address of server, if valid.
      fHintInitialized - If TRUE, the ResolveHint contains the IP address
          of the server. If FALSE, do standard name resolution.
      CallTimeout - the call timeout in milliseconds
  
      AdditionalTransportCredentialsType - the type of additional credentials that we were
          given. Not used for TCP.
  
      AdditionalCredentials - additional credentials that we were given. 
          Not used for TCP.
  
  Return Value:
  
      RPC_S_OK
  
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
      RPC_S_SERVER_UNAVAILABLE
      RPC_S_INVALID_ENDPOINT_FORMAT
      RPC_S_INVALID_NET_ADDR
  
  --*/
  {
      RPC_STATUS status;
      PWS_CCONNECTION p = (PWS_CCONNECTION)ThisConnection;
      USHORT port;
  
      ASSERT(NetworkAddress);
  
      if ((AdditionalTransportCredentialsType != 0) || (AdditionalCredentials != NULL))
          return RPC_S_CANNOT_SUPPORT;
  
      // use explicit placement to initialize the vtable. We need this to
      // be able to use the virtual functions
      p = new (p) WS_CLIENT_CONNECTION;
  
      // All this function needs to is initialize transport specific
      // parts of the connection and sockaddr.  This includes resolving
      // the network address into a `raw' address.
  
      // Figure out the destination port
      status = EndpointToPortNumber(Endpoint, port);
      if (status != RPC_S_OK)
          {
          return(status);
          }
  
      return TCPOrHTTP_Open (p,
          NetworkAddress,
          port,
          ConnTimeout,
          SendBufferSize,
          RecvBufferSize,
          (TCPResolverHint *)ResolverHint,
          fHintInitialized,
          CallTimeout,
          FALSE,   // fHTTP2Open
          NULL     // IsValidMachineFn
          );
  }
  
  #ifdef SPX_ON
  /////////////////////////////////////////////////////////////////////
  //
  // SPX specific functions
  //
  
  RPC_STATUS
  SPX_ServerListen(
      IN RPC_TRANSPORT_ADDRESS ThisAddress,
      IN RPC_CHAR *NetworkAddress,
      IN OUT RPC_CHAR * *pEndpoint,
      IN UINT PendingQueueSize,
      IN PSECURITY_DESCRIPTOR SecurityDescriptor,
      IN ULONG EndpointFlags,
      IN ULONG NICFlags
      )
  /*++
  
  Routine Description:
  
      This routine allocates a new pipe to receive new client connections.
      If successful a call to COMMON_ServerCompleteListen() will actually allow
      new connection callbacks to the RPC runtime to occur. If the runtime
      is unable to complete then it must abort the address by calling
      WS_ServerAbortListen().
  
  Arguments:
  
      pAddress - A pointer to the loadable transport interface address.
      pEndpoint - Optionally, the endpoint (port) to listen on. Set to
           to listened port for dynamically allocated endpoints.
      PendingQueueSize - Count to call listen() with.
      EndpointFlags - Meaningless for SPX
      NICFlags - Meaningless for SPX
      SecurityDescriptor - Meaningless for SPX
  
  Return Value:
  
      RPC_S_OK
  
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
      RPC_S_CANT_CREATE_ENDPOINT
      RPC_S_DUPLICATE_ENDPOINT
  
  --*/
  {
      PWS_ADDRESS pAddress = (PWS_ADDRESS)ThisAddress;
      RPC_STATUS status;
      WS_SOCKADDR *sockaddr = &(pAddress->ListenAddr);
      INT i;
      USHORT port;
  
      pAddress->id = SPX;
      pAddress->NewConnection = WS_NewConnection;
      pAddress->SubmitListen = WS_SubmitAccept;
      SetProtocolMultiplier(pAddress, 1);
      pAddress->pAddressVector = 0;
      pAddress->Endpoint = 0;
      pAddress->QueueSize = PendingQueueSize;
      pAddress->EndpointFlags = EndpointFlags;
  
      sockaddr->generic.sa_family = WsTransportTable[SPX].AddressFamily;
  
      // Figure out what port to bind to.
  
      if (*pEndpoint)
          {
          status = EndpointToPortNumber(*pEndpoint, port);
          if (status != RPC_S_OK)
              {
              return(status);
              }
  
          sockaddr->ipxaddr.sa_socket = htons(port);
          pAddress->fDynamicEndpoint = 0;
          }
      else
          {
          pAddress->fDynamicEndpoint = 1;
          sockaddr->ipxaddr.sa_socket = 0;
          }
  
      // No need to bind to a specific address for SPX
      memset(sockaddr->ipxaddr.sa_netnum,  0, sizeof(sockaddr->ipxaddr.sa_netnum) );
      memset(sockaddr->ipxaddr.sa_nodenum, 0, sizeof(sockaddr->ipxaddr.sa_nodenum));
  
      // Actually listen
  
      status = WS_ServerListenCommon(pAddress);
  
  
      if (status == RPC_S_OK)
          {
          // Listened okay, update local IPX address.
          //
          // Since there is only one addess no lock is required.
          //
          memcpy(IpxAddr.sa_netnum, sockaddr->ipxaddr.sa_netnum, 4);
          memcpy(IpxAddr.sa_nodenum, sockaddr->ipxaddr.sa_nodenum, 6);
          fIpxAddrValid = TRUE;
  
          // Figure out our network addresses
          status = IPX_BuildAddressVector(&pAddress->pAddressVector);
  
          if (status != RPC_S_OK)
              {
              WS_ServerAbortListen(pAddress);
              return(status);
              }
  
          // Return the dynamic port, if needed.
  
          if (!*pEndpoint)
              {
              *pEndpoint = new RPC_CHAR[6]; // 65535 max
              if (!*pEndpoint)
                  {
                  WS_ServerAbortListen(ThisAddress);
                  return(RPC_S_OUT_OF_MEMORY);
                  }
  
              port = ntohs(sockaddr->ipxaddr.sa_socket);
  
              PortNumberToEndpoint(port, *pEndpoint);
              }
  
          TransportProtocol::FunctionalProtocolDetected(pAddress->id);
  
          // Save away the endpoint in the address, if needed, here.
          // (PnP?)
  
          }
      else if (status == RPC_P_ADDRESS_FAMILY_INVALID)
          {
          status = RPC_S_PROTSEQ_NOT_SUPPORTED;
          }
  
      return(status);
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  SPX_QueryClientAddress(
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      OUT RPC_CHAR ** pNetworkAddress
      )
  /*++
  
  Routine Description:
  
      Returns the raw IPX of the client to a connection as a string.  The
      clients address is saved when the client connects, so all we need to do is
      format the address.
  
  Arguments:
  
      ThisConnection - The connection of interest.
      pNetworkAddress - Will contain string on success.
  
  Return Value:
  
      RPC_S_OK
      RPC_S_OUT_OF_MEMORY
  
  --*/
  {
      WS_CONNECTION *p= (WS_CONNECTION *)ThisConnection;
      ASSERT(p->type & SERVER);
  
      RPC_CHAR *Address = new RPC_CHAR[IPX_MAXIMUM_RAW_NAME];
  
      if (!Address)
          {
          return(RPC_S_OUT_OF_MEMORY);
          }
  
      IPX_AddressToName(&p->saClientAddress.ipxaddr, Address);
  
      *pNetworkAddress = Address;
  
      return(RPC_S_OK);
  }
  
  RPC_STATUS
  RPC_ENTRY
  SPX_QueryClientId(
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      OUT RPC_CLIENT_PROCESS_IDENTIFIER *ClientProcess
      )
  /*++
  
  Routine Description:
  
      For secure protocols (which SPX is not) this is suppose to give an ID
      which will be shared by all connections from the same process or security
      identity.  This prevents one user from grabbing another users association
      group and using their context handles.
  
      Since SPX is not secure we return the IPX node number of the client.
      This limits the attacks to other processes running on the client machine
      which is better than nothing.
  
  Arguments:
  
      ThisConnection - Server connection in question.
      ClientProcess - Transport identification of the "client".
  
  Return Value:
  
      RPC_S_OUT
  
  --*/
  {
      PWS_CONNECTION p = (PWS_CONNECTION)ThisConnection;
  
      ASSERT(p->type & SERVER);
  
      // The runtime assumes that any connections with a ClientProcess->FirstPart is 
      // zero means the client is local.
      // Currently, we don't have an efficient way of determining which clients
      // are local, and which remote. Since some clients grant more permissions
      // to local clients, we want to be on the safe side, and return all SPX
      // clients as remote.
  
      ClientProcess->ZeroOut();
      ClientProcess->SetIPXClientIdentifier(p->saClientAddress.ipxaddr.sa_nodenum,
          sizeof(p->saClientAddress.ipxaddr.sa_nodenum), FALSE);
  
      return(RPC_S_OK);
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  SPX_Open(
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      IN RPC_CHAR * ProtocolSequence,
      IN RPC_CHAR * NetworkAddress,
      IN RPC_CHAR * Endpoint,
      IN RPC_CHAR * NetworkOptions,
      IN UINT ConnTimeout,
      IN UINT SendBufferSize,
      IN UINT RecvBufferSize,
      IN void *ResolverHint,
      IN BOOL fHintInitialized,
      IN ULONG CallTimeout,
      IN ULONG AdditionalTransportCredentialsType, OPTIONAL
      IN void *AdditionalCredentials OPTIONAL
      )
  /*++
  
  Routine Description:
  
      Opens a connection to a server.
  
  Arguments:
  
      ThisConnection - A place to store the connection
      ProtocolSeqeunce - "ncacn_spx"
      NetworkAddress - The name of the server, either a raw address or pretty name
      NetworkOptions - Ignored
      ConnTimeout - See RpcMgmtSetComTimeout
              0 - Min
              5 - Default
              9 - Max
              10 - Infinite
      SendBufferSize -
      RecvBufferSize - (Both optional) Specifies the size of the send/recv
          transport buffers.
      CallTimeout - call timeout in milliseconds
  
      AdditionalTransportCredentialsType - the type of additional credentials that we were
          given. Not used for SPX.
  
      AdditionalCredentials - additional credentials that we were given. 
          Not used for SPX.
  
  Return Value:
  
      RPC_S_OK
  
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
      RPC_S_SERVER_UNAVAILABLE
      RPC_S_INVALID_ENDPOINT_FORMAT
      RPC_S_INVALID_NET_ADDR
  
  --*/
  {
      RPC_STATUS status;
      PWS_CCONNECTION p = (PWS_CCONNECTION)ThisConnection;
      SOCKET sock;
      WS_SOCKADDR sa;
      CHAR AnsiPort[IPX_MAXIMUM_ENDPOINT];
      PCHAR AnsiNetworkAddress;
      BOOL fUseCache = TRUE;
      BOOL fFoundInCache = FALSE;
  
      if ((AdditionalTransportCredentialsType != 0) || (AdditionalCredentials != NULL))
          return RPC_S_CANNOT_SUPPORT;
  
      // use explicit placement to initialize the vtable. We need this to
      // be able to use the virtual functions
      p = new (p) WS_CLIENT_CONNECTION;
  
      // All this function needs to is initialize transport specific
      // parts of the connection and sockaddr.  This includes resolving
      // the network address into a `raw' address.
  
      p->id = SPX;
  
      // Figure out the destination port
      USHORT port;
      status = EndpointToPortNumber(Endpoint, port);
      if (status != RPC_S_OK)
          {
          return(status);
          }
  
      for (;;)
          {
          // Resolve network address
  
          sa.ipxaddr.sa_family = AF_IPX;
          sa.ipxaddr.sa_socket = 0;
  
          status = IPX_NameToAddress(NetworkAddress, fUseCache, &sa.ipxaddr);
  
          if (status == RPC_P_FOUND_IN_CACHE)
              {
              ASSERT(fUseCache);
              fFoundInCache = TRUE;
              status = RPC_S_OK;
              }
  
          if (status != RPC_S_OK)
              {
              if (status == RPC_P_MATCHED_CACHE)
                  {
                  status = RPC_S_SERVER_UNAVAILABLE;
                  }
              return(status);
              }
  
          sa.ipxaddr.sa_socket = htons(port);
  
          // Call common open function
  
          status = WS_Open(p, 
              &sa, 
              ConnTimeout, 
              SendBufferSize, 
              RecvBufferSize, 
              CallTimeout,
              FALSE       // fHTTP2Open
              );
  
          if (status == ERROR_RETRY)
              {
              status = RPC_S_SERVER_UNAVAILABLE;
              }
  
          if (   status == RPC_S_SERVER_UNAVAILABLE
              && fFoundInCache
              && fUseCache )
              {
              fUseCache = FALSE;
              continue;
              }
  
          break;
          }
  
      return(status);
  }
  #endif
  
  #ifdef APPLETALK_ON
  
  /////////////////////////////////////////////////////////////////////
  //
  // Appletalk data stream protocol (DSP) specific functions
  //
  
  RPC_STATUS DSP_GetAppleTalkName(
      OUT CHAR *Buffer
      )
  /*++
  
  Routine Description:
  
      Returns the server's name for appletalk workstations.  This value
      defaults to GetComputerName() but can be changed.  Mail from JameelH:
  
      By default it is the netbios name of the server. It can be overwritten.
      The new name is at:
      HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MacFile\Parameters\ServerName.
  
      By default this value is not present.
  
  Arguments:
  
      Buffer - Supplies a buffer (at least 33 bytes) for the name.
  
  Return Value:
  
      RPC_S_OK - The operation completed successfully.
  
      RPC_S_OUT_OF_RESOURCES - Unable to get the name for some reasons.
  --*/
  {
      RPC_STATUS Status;
      HKEY hKey;
      DWORD Size = 33;
      DWORD Type;
  
      Status =
      RegOpenKeyEx(
          HKEY_LOCAL_MACHINE,
                  RPC_CONST_SSTRING("System\\CurrentControlSet\\Services\\MacFile\\Parameters"),
          0,
          KEY_READ,
          &hKey);
  
      if (   Status != ERROR_SUCCESS
          && Status != ERROR_FILE_NOT_FOUND )
          {
          ASSERT(0);
          return(RPC_S_OUT_OF_RESOURCES);
          }
  
      if (Status == ERROR_SUCCESS)
          {
  
          Status =
          RegQueryValueExA(
              hKey,
              "ServerName",
              0,
              &Type,
              (PBYTE)Buffer,
              &Size);
          }
  
      (void) RegCloseKey(hKey);
          
      if (   Status != ERROR_SUCCESS
          && Status != ERROR_FILE_NOT_FOUND )
          {
          ASSERT(0);
          return(RPC_S_OUT_OF_RESOURCES);
          }
  
      if (Status == ERROR_SUCCESS)
          {
          // Found a name in the registry.
  
          ASSERT(   Type == REG_SZ
                 && Size <= 32
                 && strlen(Buffer) == (Size + 1));
  
          return(RPC_S_OK);
          }
  
      // Not in the registry, must be using the computer name.
  
      Size = 33;
  
      if ( GetComputerNameA(
              Buffer,
              &Size ) == FALSE )
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         "GetComputerNameA failed! %d\n",
                         GetLastError()));
  
          ASSERT(0);
          return(RPC_S_OUT_OF_RESOURCES);
          }
  
      return(RPC_S_OK);
  }
  
  
  const PCHAR DSP_OBJECTTYPE_PREFIX = "DceDspRpc ";
  
  RPC_STATUS
  DSP_ServerListen(
      IN RPC_TRANSPORT_ADDRESS ThisAddress,
      IN RPC_CHAR *NetworkAddress,
      IN OUT RPC_CHAR * *pEndpoint,
      IN UINT PendingQueueSize,
      IN PSECURITY_DESCRIPTOR SecurityDescriptor,
      IN ULONG EndpointFlags,
      IN ULONG NICFlags
      )
  /*++
  
  Routine Description:
  
      This routine allocates a port to receive new client connections.
      If successful a call to COMMON_ServerCompleteListen() will actually allow
      new connection callbacks to the RPC runtime to occur. If the runtime
      is unable to complete then it must abort the address by calling
      WS_ServerAbortListen().
  
  Arguments:
  
      pAddress - A pointer to the loadable transport interface address.
      pEndpoint - Optionally, the endpoint (port) to listen on. Set to
           to listened port for dynamically allocated endpoints.
      PendingQueueSize - Count to call listen() with.
      EndpointFlags - Meaningless for DSP
      NICFlags - Meaningless for DSP
      SecurityDescriptor - Meaningless for DSP
  
  Return Value:
  
      RPC_S_OK
  
      RPC_S_OUT_OF_MEMORY
      RPC_S_OUT_OF_RESOURCES
      RPC_S_CANT_CREATE_ENDPOINT
      RPC_S_DUPLICATE_ENDPOINT
  
  --*/
  {
      PWS_ADDRESS pAddress = (PWS_ADDRESS)ThisAddress;
      RPC_STATUS status;
      WS_SOCKADDR *sockaddr = &(pAddress->ListenAddr);
      USHORT port;
      CHAR AnsiEndpoint[NBP_MAXIMUM_ENDPOINT];
  
      pAddress->id = DSP;
      pAddress->NewConnection = WS_NewConnection;
      pAddress->SubmitListen = WS_SubmitAccept;
      SetProtocolMultiplier(pAddress, 1);
      pAddress->pAddressVector = 0;
      pAddress->Endpoint = 0;
      pAddress->QueueSize = PendingQueueSize;
      pAddress->EndpointFlags = EndpointFlags;
  
      //
      // For DSP the endpoint is a character string.  It is not actually used
      // to allocate the socket.  We let DSP allocate a port and then register
      // the port along with our address and endpoint with NBP.
      //
  
      if (*pEndpoint)
          {
          // Runtime gave us an endpoint, convert it to ansi
          int nLength;
                  *AnsiEndpoint = 0;
          nLength = RpcpStringLength(*pEndpoint);
          if ((nLength <= 0) || (nLength > 22))
              {
              return(RPC_S_INVALID_ENDPOINT_FORMAT);
              }
          PlatformToAnsi(*pEndpoint, AnsiEndpoint);
          pAddress->fDynamicEndpoint = 0;
          }
      else
          {
          static LONG EndpointCount = 0;
          //
          // Create a dynamic endpoint using Use process ID + 16-bit counter.
          //
          *pEndpoint = new RPC_CHAR[7 + 8 + 4 + 1 + 1];
          if (!*pEndpoint)
              {
              return(RPC_S_OUT_OF_MEMORY);
              }
          CHAR buffer[9];
  
          strcpy(AnsiEndpoint, "DynEpt ");
          _ltoa(GetCurrentProcessId(), buffer, 16);
          lstrcatA(AnsiEndpoint, buffer);
          buffer[0] = '.';
          LONG t = InterlockedIncrement(&EndpointCount);
          _ltoa( t & 0xFFFF, buffer + 1, 16);
          lstrcatA(AnsiEndpoint, buffer);
  
          SimpleAnsiToPlatform(AnsiEndpoint, *pEndpoint);
          pAddress->fDynamicEndpoint = 1;
          }
  
      ASSERT(strlen(AnsiEndpoint) > 0 && strlen(AnsiEndpoint) < NBP_MAXIMUM_ENDPOINT);
  
      memset(&sockaddr->ataddr, 0, sizeof(WS_SOCKADDR));
      sockaddr->generic.sa_family = WsTransportTable[DSP].AddressFamily;
  
      // For DSP we always bind to a transport choosen port.  The endpoint
      // only exists in the NBP router as a mapping to our address and port.
  
      // Create listen socket
  
      status = WS_ServerListenCommon(pAddress);
  
  
      if (status != RPC_S_OK)
          {
          if (status == RPC_P_ADDRESS_FAMILY_INVALID)
              status = RPC_S_PROTSEQ_NOT_SUPPORTED;
          return(status);
          }
  
      // Now, try to register our name and zone.
      //
      // The final format of the name to register is:
      // <ComputerName>@<Zone>@DceDspRpc <Endpoint>
      //
      // The <ComputerName>@<Zone> string is treated as this machines
      // name as far as the runtime cares.  The <Endpoint> is used as
      // the endpoint.
      //
  
      CHAR ComputerName[NBP_MAXIMUM_NAME];
  
      status = DSP_GetAppleTalkName(ComputerName);
  
      if (status != RPC_S_OK)
          {
          WS_ServerAbortListen(ThisAddress);
          return(status);
          }
  
  // The following code segment is commented out to be consistent with
  // what we did in NT4. In NT4, we had a bug where if the AppleTalk name
  // registered is just the computer name without the zone name appended.
  // In NT5, we achieve the same by commenting out this code which appends
  // the zone name.
  //
  
  /*
      INT cZone;
  
      PSZ pszT = ComputerName;
      while(*pszT)
          pszT++;
      *pszT++ = '@';
      cZone = 33;
  
      // So far we have ComputerName@ and a pointer to just after the @.
  
      if (getsockopt(pAddress->ListenSocket,
                     SOL_APPLETALK,
                     SO_LOOKUP_MYZONE,
                     pszT,
                     &cZone) != 0)
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         RPCTRANS "Failed to lookup zone: %d\n",
                         GetLastError()));
  
          // Don't fail, this could be a small network, just register '*' for
          // the broadcast/local zone.
          *pszT++ = '*';
          *pszT++ = '0';
          }
  */
  
      //
      // We need to register our computer name and endpoint next.
      //
  
      WSH_REGISTER_NAME   AtalkNameToRegister;
      int                 length;
  
      ASSERT(MAX_COMPUTERNAME_LENGTH < MAX_ENTITY + 1);
  
      length = strlen(AnsiEndpoint);
      memcpy(AtalkNameToRegister.TypeName, DSP_OBJECTTYPE_PREFIX, 10);
      memcpy(AtalkNameToRegister.TypeName + 10, AnsiEndpoint, length);
      AtalkNameToRegister.TypeNameLen = length + 10;
  
      length = strlen(ComputerName);
      memcpy(AtalkNameToRegister.ObjectName, ComputerName, length);
      AtalkNameToRegister.ObjectNameLen = (char) length;
  
      AtalkNameToRegister.ZoneName[0] = '*';
      AtalkNameToRegister.ZoneNameLen = 1;
  
      // Could do a lookup and connect to see if our name is already registered..
  
      if (setsockopt(
                pAddress->ListenSocket,
                SOL_APPLETALK,
                SO_REGISTER_NAME,
                (char *)&AtalkNameToRegister,
                sizeof(AtalkNameToRegister)
                ) != 0 )
          {
          TransDbgPrint((DPFLTR_RPCPROXY_ID,
                         DPFLTR_WARNING_LEVEL,
                         RPCTRANS "Failed to register name: %d\n",
                         GetLastError()));
  
          WS_ServerAbortListen(ThisAddress);
          return(RPC_S_CANT_CREATE_ENDPOINT);
          }
  
      // All done, build out parameters
  
      ASSERT(length == (int) strlen(ComputerName));
  
      NETWORK_ADDRESS_VECTOR *pVector;
  
      pVector =  new(  sizeof(RPC_CHAR *)
                     + (length + 2) * sizeof(RPC_CHAR))
                     NETWORK_ADDRESS_VECTOR;
  
      if (NULL == pVector)
          {
          WS_ServerAbortListen(ThisAddress);
          return(RPC_S_OUT_OF_MEMORY);
          }
  
      pVector->Count = 1;
      pVector->NetworkAddresses[0] = (RPC_CHAR*)&pVector->NetworkAddresses[1];
  
          AnsiToPlatform(ComputerName, pVector->NetworkAddresses[0]);
  
      pAddress->pAddressVector = pVector;
  
      return(RPC_S_OK);
  }
  
  
  RPC_STATUS
  RPC_ENTRY
  DSP_Open(
      IN RPC_TRANSPORT_CONNECTION ThisConnection,
      IN RPC_CHAR * ProtocolSequence,
      IN RPC_CHAR * NetworkAddress,
      IN RPC_CHAR * Endpoint,
      IN RPC_CHAR * NetworkOptions,
      IN UINT ConnTimeout,
      IN UINT SendBufferSize,
      IN UINT RecvBufferSize,
      IN void *ResolverHint,
      IN BOOL fHintInitialized,
      IN ULONG CallTimeout,
      IN ULONG AdditionalTransportCredentialsType, OPTIONAL
      IN void *AdditionalCredentials OPTIONAL
      )
  /*++
  
  Routine Description:
  
      Not supported on Windows NT.
  
  --*/
  {
      return(RPC_S_PROTSEQ_NOT_SUPPORTED);
  }
  #endif
  
  
  RPC_STATUS RPC_ENTRY WS_Abort(IN RPC_TRANSPORT_CONNECTION Connection)
  {
      return ((WS_CONNECTION *)Connection)->WS_CONNECTION::Abort();
  }
  
  /////////////////////////////////////////////////////////////////////
  //
  // Transport information definitions
  //
  
  const RPC_CONNECTION_TRANSPORT
  TCP_TransportInterface =
      {
      RPC_TRANSPORT_INTERFACE_VERSION,
      TCP_TOWER_ID,
      IP_ADDRESS_ID,
      RPC_STRING_LITERAL("ncacn_ip_tcp"),
      "135",
      COMMON_ProcessCalls,
  
      COMMON_StartPnpNotifications,
      COMMON_ListenForPNPNotifications,
  
      COMMON_TowerConstruct,
      COMMON_TowerExplode,
      COMMON_PostRuntimeEvent,
      FALSE,
      WS_GetNetworkAddressVector,
      sizeof(WS_ADDRESS),
      max(sizeof(WS_CLIENT_CONNECTION), sizeof(WS_SAN_CLIENT_CONNECTION)),
      max(sizeof(WS_CONNECTION), sizeof(WS_SAN_CONNECTION)),
      sizeof(CO_SEND_CONTEXT),
      sizeof(TCPResolverHint),
      TCP_MAX_SEND,
      WS_Initialize,
      0,
      TCP_Open,
      0, // No SendRecv on winsock
      WS_SyncRecv,
      WS_Abort,
      WS_Close,
      CO_Send,
      CO_Recv,
      WS_SyncSend,
      WS_TurnOnOffKeepAlives,
      TCP_ServerListen,
      WS_ServerAbortListen,
      COMMON_ServerCompleteListen,
      TCP_QueryClientAddress,
      TCP_QueryLocalAddress,
      TCP_QueryClientId,
      TCP_QueryClientIpAddress,
      0,  // Impersonate
      0,  // Revert
      0,  // FreeResolverHint
      0,  // CopyResolverHint
      0,  // CompareResolverHint
      0   // SetLastBufferToFree
      };
  
  // When the RPC verifier is enabled and we are corrupting client receives,
  // we will use a modified transport interface given below.  It will have the sync
  // receive members overwritten.
  RPC_CONNECTION_TRANSPORT *pTCP_TransportInterface_Avrf = NULL;
  
  #ifdef SPX_ON
  const RPC_CONNECTION_TRANSPORT
  SPX_TransportInterface =
      {
      RPC_TRANSPORT_INTERFACE_VERSION,
      SPX_TOWER_ID,
      IPX_ADDRESS_ID,
      RPC_STRING_LITERAL("ncacn_spx"),
      "34280",
      COMMON_ProcessCalls,
  
      COMMON_StartPnpNotifications,
      COMMON_ListenForPNPNotifications,
  
      COMMON_TowerConstruct,
      COMMON_TowerExplode,
      COMMON_PostRuntimeEvent,
      FALSE,
      WS_GetNetworkAddressVector,
      sizeof(WS_ADDRESS),
      max(sizeof(WS_CLIENT_CONNECTION), sizeof(WS_SAN_CLIENT_CONNECTION)),
      max(sizeof(WS_CONNECTION), sizeof(WS_SAN_CONNECTION)),
      sizeof(CO_SEND_CONTEXT),
      0,
      SPX_MAX_SEND,
      WS_Initialize,
      0,
      SPX_Open,
      0, // No SendRecv on winsock
      WS_SyncRecv,
      WS_Abort,
      WS_Close,
      CO_Send,
      CO_Recv,
      WS_SyncSend,
      0,  // turn on/off keep alives
      SPX_ServerListen,
      WS_ServerAbortListen,
      COMMON_ServerCompleteListen,
      SPX_QueryClientAddress,
      0, // query local address
      SPX_QueryClientId,
      0,  // query client ip address
      0,  // Impersonate
      0,  // Revert
      0,  // FreeResolverHint
      0,  // CopyResolverHint
      0,  // CompareResolverHint
      0   // SetLastBufferToFree
      };
  #endif
  
  #ifdef APPLETALK_ON
  const RPC_CONNECTION_TRANSPORT
  DSP_TransportInterface =
      {
      RPC_TRANSPORT_INTERFACE_VERSION,
      DSP_TOWER_ID,
      NBP_ADDRESS_ID,
      RPC_STRING_LITERAL("ncacn_at_dsp"),
      "Endpoint Mapper",
      COMMON_ProcessCalls,
      COMMON_StartPnpNotifications,
      COMMON_ListenForPNPNotifications,
      COMMON_TowerConstruct,
      COMMON_TowerExplode,
      COMMON_PostRuntimeEvent,
      FALSE,
      WS_GetNetworkAddressVector,
      sizeof(WS_ADDRESS),
      sizeof(WS_CLIENT_CONNECTION),
      sizeof(WS_CONNECTION),
      sizeof(CO_SEND_CONTEXT),
      0,
      DSP_MAX_SEND,
      WS_Initialize,
      0,
      DSP_Open,  // Not really supported.
      0, // No SendRecv on winsock
      WS_SyncRecv,
      WS_Abort,
      WS_Close,
      CO_Send,
      CO_Recv,
      WS_SyncSend,
      0,  // turn on/off keep alives
      DSP_ServerListen,
      WS_ServerAbortListen,
      COMMON_ServerCompleteListen,
      0,  // DSP_QueryClientAddress,
      0,  // DSP_QueryClientId,
      0,  // query client ip address
      0,  // Impersonate
      0,  // Revert
      0,  // FreeResolverHint
      0,  // CopyResolverHint
      0,  // CompareResolverHint
      0   // SetLastBufferToFree
      };
  #endif
  
  const RPC_CONNECTION_TRANSPORT *
  WS_TransportLoad (
      IN PROTOCOL_ID index
      )
  {
      RPC_STATUS status;
  
      if (fWinsockLoaded == FALSE)
          {
          if (RPC_WSAStartup() == FALSE)
              {
              return 0;
              }
          fWinsockLoaded = TRUE;
          }
  
      switch(index)
          {
          case TCP:
              AddressChangeFn = I_RpcServerInqAddressChangeFn();
  
              if (AddressChangeFn)
                  {
                  TransDbgPrint((DPFLTR_RPCPROXY_ID,
                                 DPFLTR_WARNING_LEVEL,
                                 RPCTRANS "TCP address change function has been loaded"));
                  }
  
              if (DoFirewallInit())
                  {
                  // Overwrite the SyncSendRecv and SyncRecv members of the transport interfaces if the
                  // RPC verifier is enabled.
                  if (gfRpcVerifierCorruptionInjectClientReceives)
                      {
                      // Check if we have previously initialized the Avrf transport interface.
                      if (pTCP_TransportInterface_Avrf == NULL)
                          {
                          // Allocate a transport interface structure to override the default.
                          pTCP_TransportInterface_Avrf = new (RPC_CONNECTION_TRANSPORT);
                          if (pTCP_TransportInterface_Avrf == NULL)
                              {
                              return NULL;
                              }
                          // Initialize the Avrf transport interface with the default values.
                          RpcpMemoryCopy(pTCP_TransportInterface_Avrf,
                                         &TCP_TransportInterface,
                                         sizeof(RPC_CONNECTION_TRANSPORT));
                          // Override the interface function for sync receive.
                          ASSERT(pTCP_TransportInterface_Avrf->SyncRecv == WS_SyncRecv);
                          pTCP_TransportInterface_Avrf->SyncRecv = WS_SyncRecv_Avrf;
                          }
                      return(pTCP_TransportInterface_Avrf);
                      }
  
                  return(&TCP_TransportInterface);
                  }
              break;
  
  #ifdef SPX_ON
          case SPX:
              AddressChangeFn = I_RpcServerInqAddressChangeFn();
  
              if (AddressChangeFn)
                  {
                  TransDbgPrint((DPFLTR_RPCPROXY_ID,
                                 DPFLTR_WARNING_LEVEL,
                                 RPCTRANS "SPX address change function has been loaded"));
                  }
  
              if (InitializeIpxNameCache() != RPC_S_OK)
                  {
                  return(0);
                  }
              return(&SPX_TransportInterface);
  #endif
  
  #ifdef APPLETALK_ON
          case DSP:
              return(&DSP_TransportInterface);
  #endif
  
          case HTTP:
      	    if (DoFirewallInit())
                  {
                  return(&HTTP_TransportInterface);
      	        }
  	        break;
  
          default:
              TransDbgPrint((DPFLTR_RPCPROXY_ID,
                             DPFLTR_WARNING_LEVEL,
                             RPCTRANS "WS_TransportLoad called with index: %d\n",
                             index));
  
              ASSERT(0);
          }
  
      return(0);
  }
  
  RPC_STATUS SAN_CONNECTION::SANReceive(HANDLE hFile, LPVOID lpBuffer,
                                    DWORD nNumberOfBytesToRead,
                                    LPDWORD lpNumberOfBytesRead,
                                    LPOVERLAPPED lpOverlapped)
  {
      WSABUF wsaBuf;
      int nResult;
  
      wsaBuf.len = nNumberOfBytesToRead;
      wsaBuf.buf = (char *)lpBuffer;
      m_dwFlags = 0;
  
      nResult = WSARecv((SOCKET)hFile, &wsaBuf, 1, lpNumberOfBytesRead, &m_dwFlags,
              lpOverlapped, NULL);
      if (nResult == SOCKET_ERROR)
          return WSAGetLastError();
      else
          return RPC_S_OK;
  }
  
  RPC_STATUS SAN_CONNECTION::SANSend(HANDLE hFile, LPCVOID lpBuffer,
                                 DWORD nNumberOfBytesToWrite,
                                 LPDWORD lpNumberOfBytesWritten,
                                 LPOVERLAPPED lpOverlapped)
  {
      WSABUF wsaBuf;
      int nResult;
      wsaBuf.len = nNumberOfBytesToWrite;
      wsaBuf.buf = (char *)lpBuffer;
  
      nResult = WSASend((SOCKET)hFile, &wsaBuf, 1, lpNumberOfBytesWritten, 0,
          lpOverlapped, NULL);
  
      if (nResult == SOCKET_ERROR)
          return WSAGetLastError();
      else
          return RPC_S_OK;
  }
  
  RPC_STATUS WS_CONNECTION::Receive(HANDLE hFile, LPVOID lpBuffer,
                                    DWORD nNumberOfBytesToRead,
                                    LPDWORD lpNumberOfBytesRead,
                                    LPOVERLAPPED lpOverlapped)
  {
      return UTIL_ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
  }
  
  RPC_STATUS WS_CONNECTION::Send(HANDLE hFile, LPCVOID lpBuffer,
                                 DWORD nNumberOfBytesToWrite,
                                 LPDWORD lpNumberOfBytesWritten,
                                 LPOVERLAPPED lpOverlapped)
  {
      return UTIL_WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
  }
  
  void
  RPC_CLIENT_PROCESS_IDENTIFIER::SetIPv6ClientIdentifier (
      IN void *Buffer,
      IN size_t BufferSize,
      IN BOOL fLocal)
  /*++
  
  Routine Description:
  
      IPv6 servers can use this to store their IP address in
      the client identifier.
  
  Arguments:
  
      Buffer - the buffer with the client identifier
      BufferSize - the size of the data in buffer
      fLocal - TRUE if the client is guaranteed to be Local. False otherwise.
  
  Return Value:
  
  
  --*/
  {
      SOCKADDR_IN6 *IPv6Address;
      ASSERT(BufferSize >= sizeof(SOCKADDR_IN6));
  
      this->fLocal = fLocal;
      RpcpMemoryCopy(u.ULongClientId, Buffer, sizeof(SOCKADDR_IN6));
      IPv6Address = (SOCKADDR_IN6 *)u.ULongClientId;
      IPv6Address->sin6_port = 0;
      IPv6Address->sin6_flowinfo = 0;
      IPv6Address->sin6_scope_id = 0;
  }
  
  