/* --------------------------------------------------------------------
File : tcltclnt.c

Title : client loadable transport for DOS TCP/IP - client side

Description :

History :

6-26-91    Jim Teague    Initial version.

-------------------------------------------------------------------- */

#include "tcltclnt.h"


#define ByteSwapLong(Value) \
    Value = (  (((Value) & 0xFF000000) >> 24) \
             | (((Value) & 0x00FF0000) >> 8) \
             | (((Value) & 0x0000FF00) << 8) \
             | (((Value) & 0x000000FF) << 24))

#define ByteSwapShort(Value) \
    Value = (  (((Value) & 0x00FF) << 8) \
             | (((Value) & 0xFF00) >> 8))


/*
   Following Macros and structs are needed for Tower Stuff
*/

#pragma pack(1)
#define TCP_TRANSPORTID      0x07
#define TCP_TRANSPORTHOSTID  0x09
#define TCP_TOWERFLOORS         5
#define TCP_IP_EP            "135"

#define TCP_PROTSEQ          "ncacn_ip_tcp"

typedef struct _FLOOR_234 {
   unsigned short ProtocolIdByteCount;
   unsigned char FloorId;
   unsigned short AddressByteCount;
   unsigned char Data[2];
} FLOOR_234, PAPI * PFLOOR_234;


#define NEXTFLOOR(t,x) (t)((unsigned char PAPI *)x +((t)x)->ProtocolIdByteCount\
                                        + ((t)x)->AddressByteCount\
                                        + sizeof(((t)x)->ProtocolIdByteCount)\
                                        + sizeof(((t)x)->AddressByteCount))

RPC_STATUS RPC_ENTRY
ClientClose (
    PCONNECTION pConn
    );

/*
  End of Tower Stuff!
*/

#pragma pack()



RPC_STATUS RPC_ENTRY
ClientOpen (
    PCONNECTION  pConn,
    IN unsigned char * NetworkAddress,
    IN unsigned char * Endpoint,
    IN unsigned char * NetworkOptions,
    IN unsigned char * TransportAddress,
    IN unsigned char * RpcProtocolSequence,
    IN unsigned int Timeout
    )

// Open a client connection

{
    struct sockaddr_in server;
    struct hostent * hostentry;
    unsigned long dotaddress;
    int i;
    //
    // See if host address is in numeric format...
    //
    if ((i = strcspn(NetworkAddress,".0123456789")) == 0)
         {
         /*
         dotaddress = inet_addr(NetworkAddress);
         hostentry = gethostbyaddr((struct in_addr *) &dotaddress,
                                   sizeof(long),AF_INET);
         */
       /*
        * To avoid having to resolve the address from a hosts file
        * or a nameserver, fill in the hostentry manually.  For some
        * mysterious reason, however, we must have a hostentry that
        * was handed to us from WinSock, so we do an inquiry for
        * a known entry, "localhost".
        *
        */
        hostentry           = gethostbyname((unsigned char *) "localhost");
        dotaddress          = inet_addr(NetworkAddress);
        hostentry->h_addr   = &dotaddress;
        hostentry->h_length = (short) sizeof(unsigned long);
         }
    else
         hostentry = gethostbyname(NetworkAddress);

    if (hostentry == (struct hostent *) 0 )
        return (RPC_S_SERVER_UNAVAILABLE);

    server.sin_family    = AF_INET;
    server.sin_port       = htons(atoi(Endpoint));
    memcpy((char *) &server.sin_addr.s_addr,
                     (char *) hostentry->h_addr,
                     hostentry->h_length);
    //
    // Get a socket
    //
    if ((pConn->Socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        return (RPC_S_OUT_OF_RESOURCES);

    //
    // Try to connect...
    //

    if (connect(pConn->Socket, (struct sockaddr *) &server, sizeof (server)) 
                                                                         < 0)
       {
        ClientClose(pConn);
        return (RPC_S_SERVER_UNAVAILABLE);
       }

    return (RPC_S_OK);

}

RPC_STATUS RPC_ENTRY
ClientClose (
    PCONNECTION pConn
    )

// Close a client connection

{
    // Don't close a connection that is already closed...

    close_socket(pConn->Socket);

    return (RPC_S_OK);
}

RPC_ENTRY RPC_ENTRY
ClientWrite (
    PCONNECTION pConn,
    void * Buffer,
    unsigned int BufferLength
    )

// Write a message to a connection.  This operation is retried in case
// the server is "busy".

{
    int bytes;
    //
    // Send a message on the socket
    //
    bytes = send(pConn->Socket, (char *) Buffer, (int) BufferLength, 0);

    if (bytes != (int) BufferLength)
        {
        ClientClose(pConn);
        return(RPC_P_SEND_FAILED);
        }

    return(RPC_S_OK);

}

RPC_STATUS RPC_ENTRY
ClientRead (
    PCONNECTION pConn,
    void ** Buffer,
    unsigned int * BufferLength
    )

// Read a message from a connection.

{
    int bytes = 0;
    unsigned short total_bytes = 0;
    message_header header;
    unsigned short native_length;
    RPC_STATUS status;

    //
    // Read protocol header to see how big
    //   the record is...
    //

    while (total_bytes < sizeof(message_header))
       {
          bytes = recv (
                        pConn->Socket, 
                        (char *) &header + total_bytes, 
                        sizeof (message_header) - total_bytes, 0
                       );

         if (bytes <= 0)
           {
             ClientClose(pConn);
             return(RPC_P_CONNECTION_CLOSED);
           }

        total_bytes += bytes;
      }


    //
    // If this fragment header came from a reverse-endian machine, then
    //   we will have to byte swap the frag_length field...
    //
    if ( (header.drep[0] & ENDIAN_MASK) == 0 )
        {
        // Big endian...swap bytes...
        //
        ((unsigned char *) &native_length)[0] =
            ((unsigned char *) &header.frag_length)[1];
        ((unsigned char *) &native_length)[1] =
            ((unsigned char *) &header.frag_length)[0];
        }
    else
        // Little endian, just like us...
        //
        native_length = header.frag_length;

    //
    // Make sure buffer is big enough.  If it isn't, then go back
    //    to the runtime to reallocate it.
    //
    if (native_length > *BufferLength)
       {
       status = I_RpcTransClientReallocBuffer ( pConn,
                                                Buffer,
                                                0,
                                                native_length);
       if (status)
           {
           ClientClose(pConn);
           return (RPC_S_OUT_OF_MEMORY);
           }
       }

    *BufferLength = native_length;
    //
    // Read message segments until we get what we expect...
    //
    memcpy (*Buffer, &header, sizeof(message_header));

    while (total_bytes < native_length)
         {
         if((bytes = recv( pConn->Socket,
                           (unsigned char *) *Buffer + total_bytes,
                           (int) (*BufferLength - total_bytes), 0)) == -1)
             {
             ClientClose(pConn);
             return (RPC_P_RECEIVE_FAILED);
             }
         else
             total_bytes += bytes;
         }

    return(RPC_S_OK);

}


#pragma pack(1)
RPC_STATUS RPC_ENTRY
ClientTowerConstruct(
     IN  char PAPI * Endpoint,
     IN  char PAPI * NetworkAddress,
     OUT unsigned short PAPI * Floors,
     OUT unsigned long  PAPI * ByteCount,
     OUT unsigned char PAPI * PAPI * Tower,
     IN  char PAPI * Protseq
    )
/*++


Routine Description:

    This function constructs upper floors of DCE tower from
    the supplied endpoint and network address. It returns #of floors
    [lower+upper] for this protocol/transport, bytes in upper floors
    and the tower [floors 4,5]

Arguments:

    Endpoint- A pointer to string representation of Endpoint

    NetworkAddress - A pointer to string representation of NW Address

    Floors - A pointer to #of floors in the tower

    ByteCount - Size of upper floors of tower.

    Tower - The constructed tower returmed - The memory is allocated
            by  the routine and caller will have to free it.

Return Value:

    RPC_S_OK

    RPC_S_OUT_OF_MEMORY - There is no memory to return the constructed
                          Tower.
--*/
{

  unsigned long TowerSize, * HostId, hostval;
  unsigned short * Port, portnum;
  PFLOOR_234 Floor;

  if (Protseq) ; //Keep compiler happy

  *Floors = TCP_TOWERFLOORS;
  TowerSize  =  6; /*Endpoint = 2 bytes, HostId = 4 bytes*/

  TowerSize += 2*sizeof(FLOOR_234) - 4;

  if ((*Tower = (unsigned char PAPI*)I_RpcAllocate((unsigned int)
                                                    (*ByteCount = TowerSize)))
           == NULL)
     {
       return (RPC_S_OUT_OF_MEMORY);
     }

  Floor = (PFLOOR_234) *Tower;
  Floor->ProtocolIdByteCount = 1;
  Floor->FloorId = (unsigned char)(TCP_TRANSPORTID & 0xFF);
  Floor->AddressByteCount = 2;

  Port  = (unsigned short *) &Floor->Data[0];

  if (Endpoint == NULL || *Endpoint == '\0')
     {
       Endpoint = TCP_IP_EP;
     }
  *Port = htons ( atoi (Endpoint));

  //Onto the next floor
  Floor = NEXTFLOOR(PFLOOR_234, Floor);
  Floor->ProtocolIdByteCount = 1;
  Floor->FloorId = (unsigned char)(TCP_TRANSPORTHOSTID & 0xFF);
  Floor->AddressByteCount = 4;

  HostId = (unsigned long *)&Floor->Data[0];

  if ((NetworkAddress) && (*NetworkAddress))
     {
       *HostId = inet_addr ((char *) NetworkAddress);
     }
  else
     {
       *HostId = 0;
     }

  return(RPC_S_OK);
}



RPC_STATUS RPC_ENTRY
ClientTowerExplode(
     IN unsigned char PAPI * Tower,
     OUT char PAPI * PAPI * Protseq,
     OUT char PAPI * PAPI * Endpoint,
     OUT char PAPI * PAPI * NetworkAddress
    )
/*++


Routine Description:

    This function takes the protocol/transport specific floors
    and returns Protseq, Endpoint and NwAddress

    Note: Since ther is no need to return NW Address, currently
    nothing is done for NW Address.

Arguments:

    Tower - The DCE tower, upper floors

    Protseq - Protocol Sequence returned- memory is allocated by the
              routine and caller will have to free using I_RpcFree

    Endpoitn- Endpoint returned- memory is allocated by the
              routine and caller will have to free using I_RpcFree

    NWAddress- Nothing is done here - just incase we need it later

Return Value:

    RPC_S_OK

    RPC_S_OUT_OF_MEMORY - There is no memory to return the constructed
                          Tower.
--*/
{
  PFLOOR_234 Floor = (PFLOOR_234) Tower;
  RPC_STATUS Status = RPC_S_OK;
  unsigned short portnum,  *Port;

  if (Protseq != NULL)
    {
      *Protseq = I_RpcAllocate(strlen(TCP_PROTSEQ) + 1);
      if (*Protseq == NULL)
        Status = RPC_S_OUT_OF_MEMORY;
      else
        memcpy(*Protseq, TCP_PROTSEQ, strlen(TCP_PROTSEQ) + 1);
    }

  if ((Endpoint == NULL) || (Status != RPC_S_OK))
    {
      return (Status);
    }

  *Endpoint  = I_RpcAllocate(6);  //Ports are all <64K [5 decimal dig +1]
  if (*Endpoint == NULL)
    {
      Status = RPC_S_OUT_OF_MEMORY;
      if (Protseq != NULL)
         {
          I_RpcFree(*Protseq);
         }
    }
  else
   {
     Port = (unsigned short *)&Floor->Data[0];
     portnum = *Port;
     RpcItoa(ByteSwapShort(portnum), *Endpoint, 10);
   }

 return(Status);
}

#pragma pack()



RPC_CLIENT_TRANSPORT_INFO TransInfo = {
   RPC_TRANSPORT_INTERFACE_VERSION,
   TCP_MAXIMUM_SEND,
   sizeof (CONNECTION),
   ClientOpen,
   ClientClose,
   ClientWrite,
   ClientRead,
   NULL,
   ClientTowerConstruct,
   ClientTowerExplode,
   TCP_TRANSPORTID,
   0
};

RPC_CLIENT_TRANSPORT_INFO *  RPC_ENTRY TransPortLoad (
    IN RPC_CHAR * RpcProtocolSequence
    )

// Loadable transport initialization function

{
    return(&TransInfo);
}