/*++

Copyright (c) 2013 Minoca Corp. All Rights Reserved

Module Name:

    socket.c

Abstract:

    This module implements support for socket-based communication in user mode.

Author:

    Evan Green 3-May-2013

Environment:

    User Mode

--*/

//
// ------------------------------------------------------------------- Includes
//

#include "osbasep.h"

//
// ---------------------------------------------------------------- Definitions
//

//
// ------------------------------------------------------ Data Type Definitions
//

//
// ----------------------------------------------- Internal Function Prototypes
//

//
// -------------------------------------------------------------------- Globals
//

//
// ------------------------------------------------------------------ Functions
//

OS_API
KSTATUS
OsSocketCreatePair (
    NET_DOMAIN_TYPE Domain,
    NET_SOCKET_TYPE Type,
    ULONG Protocol,
    ULONG OpenFlags,
    HANDLE Sockets[2]
    )

/*++

Routine Description:

    This routine creates a pair of sockets that are connected to each other.

Arguments:

    Domain - Supplies the network domain to use on the socket.

    Type - Supplies the socket connection type.

    Protocol - Supplies the raw protocol value used on the network.

    OpenFlags - Supplies an optional bitfield of open flags for the new socket.
        Only SYS_OPEN_FLAG_NON_BLOCKING and SYS_OPEN_FLAG_CLOSE_ON_EXECUTE
        are accepted.

    Sockets - Supplies an array where the two handles to the connected
        sockets will be returned on success.

Return Value:

    Status code.

--*/

{

    SYSTEM_CALL_SOCKET_CREATE_PAIR Parameters;

    Parameters.Domain = Domain;
    Parameters.Type = Type;
    Parameters.Protocol = Protocol;
    Parameters.OpenFlags = OpenFlags;
    OsSystemCall(SystemCallSocketCreatePair, &Parameters);
    Sockets[0] = Parameters.Socket1;
    Sockets[1] = Parameters.Socket2;
    return Parameters.Status;
}

OS_API
KSTATUS
OsSocketCreate (
    NET_DOMAIN_TYPE Domain,
    NET_SOCKET_TYPE Type,
    ULONG Protocol,
    ULONG OpenFlags,
    PHANDLE Socket
    )

/*++

Routine Description:

    This routine creates a new socket for communication.

Arguments:

    Domain - Supplies the network domain to use for the socket.

    Type - Supplies the socket type.

    Protocol - Supplies the raw protocol value to use for the socket. This is
        network specific.

    OpenFlags - Supplies an optional bitfield of open flags for the new socket.
        Only SYS_OPEN_FLAG_NON_BLOCKING and SYS_OPEN_FLAG_CLOSE_ON_EXECUTE
        are accepted.

    Socket - Supplies a pointer where the new socket handle will be returned on
        success.

Return Value:

    Status code.

--*/

{

    SYSTEM_CALL_SOCKET_CREATE Request;

    Request.Domain = Domain;
    Request.Type = Type;
    Request.Protocol = Protocol;
    Request.OpenFlags = OpenFlags;
    OsSystemCall(SystemCallSocketCreate, &Request);
    *Socket = Request.Socket;
    return Request.Status;
}

OS_API
KSTATUS
OsSocketBind (
    HANDLE Socket,
    PNETWORK_ADDRESS Address,
    PSTR Path,
    UINTN PathSize
    )

/*++

Routine Description:

    This routine binds a newly created socket to a local address.

Arguments:

    Socket - Supplies a handle to the fresh socket.

    Address - Supplies a pointer to the local network address to bind to.

    Path - Supplies a pointer to the path to bind to in the case that this is
        a local (Unix) socket.

    PathSize - Supplies the size of the path in bytes including the null
        terminator.

Return Value:

    Status code.

--*/

{

    SYSTEM_CALL_SOCKET_BIND Request;

    Request.Socket = Socket;
    RtlCopyMemory(&(Request.Address), Address, sizeof(NETWORK_ADDRESS));
    Request.Path = Path;
    Request.PathSize = PathSize;
    OsSystemCall(SystemCallSocketBind, &Request);
    return Request.Status;
}

OS_API
KSTATUS
OsSocketListen (
    HANDLE Socket,
    ULONG SuggestedBacklog
    )

/*++

Routine Description:

    This routine activates a socket, making it eligible to accept new
    incoming connections.

Arguments:

    Socket - Supplies the socket to activate.

    SuggestedBacklog - Supplies a suggestion to the kernel as to the number of
        un-accepted incoming connections to queue up before incoming
        connections are refused.

Return Value:

    Status code.

--*/

{

    SYSTEM_CALL_SOCKET_LISTEN Request;

    Request.Socket = Socket;
    Request.BacklogCount = SuggestedBacklog;
    OsSystemCall(SystemCallSocketListen, &Request);
    return Request.Status;
}

OS_API
KSTATUS
OsSocketAccept (
    HANDLE Socket,
    PHANDLE NewSocket,
    PNETWORK_ADDRESS Address,
    PSTR RemotePath,
    PUINTN RemotePathSize,
    ULONG OpenFlags
    )

/*++

Routine Description:

    This routine accepts an incoming connection on a listening socket and spins
    it off into a new socket. This routine will block until an incoming
    connection request is received.

Arguments:

    Socket - Supplies the listening socket to accept a new connection from.

    NewSocket - Supplies a pointer where a new socket representing the
        incoming connection will be returned on success.

    Address - Supplies an optional pointer where the address of the remote host
        will be returned.

    RemotePath - Supplies a pointer where the remote path of the client socket
        will be copied on success. This only applies to local sockets.

    RemotePathSize - Supplies a pointer that on input contains the size of the
        remote path buffer. On output, contains the true size of the remote
        path, even if it was bigger than the input.

    OpenFlags - Supplies an optional bitfield of open flags for the new socket.
        Only SYS_OPEN_FLAG_NON_BLOCKING and SYS_OPEN_FLAG_CLOSE_ON_EXECUTE
        are accepted.

Return Value:

    Status code.

--*/

{

    SYSTEM_CALL_SOCKET_ACCEPT Request;

    Request.Socket = Socket;
    Request.NewSocket = INVALID_HANDLE;
    Request.RemotePath = RemotePath;
    Request.RemotePathSize = 0;
    if (RemotePathSize != NULL) {
        Request.RemotePathSize = *RemotePathSize;
    }

    Request.OpenFlags = OpenFlags;
    OsSystemCall(SystemCallSocketAccept, &Request);
    *NewSocket = Request.NewSocket;
    if (Address != NULL) {
        RtlCopyMemory(Address, &(Request.Address), sizeof(NETWORK_ADDRESS));
    }

    if (RemotePathSize != NULL) {
        *RemotePathSize = Request.RemotePathSize;
    }

    return Request.Status;
}

OS_API
KSTATUS
OsSocketConnect (
    HANDLE Socket,
    PNETWORK_ADDRESS Address,
    PSTR RemotePath,
    UINTN RemotePathSize
    )

/*++

Routine Description:

    This routine attempts to establish a new outgoing connection on a socket.

Arguments:

    Socket - Supplies a handle to the fresh socket to use to establish the
        connection.

    Address - Supplies a pointer to the destination socket address.

    RemotePath - Supplies a pointer to the path to connect to, if this is a
        local socket.

    RemotePathSize - Supplies the size of the remote path buffer in bytes,
        including the null terminator.

Return Value:

    Status code.

--*/

{

    SYSTEM_CALL_SOCKET_CONNECT Request;

    Request.Socket = Socket;
    RtlCopyMemory(&(Request.Address), Address, sizeof(NETWORK_ADDRESS));
    Request.RemotePath = RemotePath;
    Request.RemotePathSize = RemotePathSize;
    OsSystemCall(SystemCallSocketConnect, &Request);
    return Request.Status;
}

OS_API
KSTATUS
OsSocketPerformIo (
    HANDLE Socket,
    PSOCKET_IO_PARAMETERS Parameters,
    PVOID Buffer
    )

/*++

Routine Description:

    This routine performs I/O on an open handle.

Arguments:

    Socket - Supplies a pointer to the socket.

    Parameters - Supplies a pointer to the socket I/O request details.

    Buffer - Supplies a pointer to the buffer containing the data to write or
        where the read data should be returned, depending on the operation.

Return Value:

    Status code.

--*/

{

    SYSTEM_CALL_SOCKET_PERFORM_IO Request;

    Request.Socket = Socket;
    Request.Parameters = Parameters;
    Request.Buffer = Buffer;
    OsSystemCall(SystemCallSocketPerformIo, &Request);
    return Request.Status;
}

OS_API
KSTATUS
OsSocketPerformVectoredIo (
    HANDLE Socket,
    PSOCKET_IO_PARAMETERS Parameters,
    PIO_VECTOR VectorArray,
    UINTN VectorCount
    )

/*++

Routine Description:

    This routine performs vectored I/O on an open handle.

Arguments:

    Socket - Supplies a pointer to the socket.

    Parameters - Supplies a pointer to the socket I/O request details.

    VectorArray - Supplies an array of I/O vector structures to do I/O to/from.

    VectorCount - Supplies the number of elements in the vector array.

Return Value:

    Status code.

--*/

{

    SYSTEM_CALL_SOCKET_PERFORM_VECTORED_IO Request;

    Request.Socket = Socket;
    Request.Parameters = Parameters;
    Request.VectorArray = VectorArray;
    Request.VectorCount = VectorCount;
    OsSystemCall(SystemCallSocketPerformVectoredIo, &Request);
    return Request.Status;
}

OS_API
KSTATUS
OsSocketGetSetInformation (
    HANDLE Socket,
    SOCKET_INFORMATION_TYPE InformationType,
    UINTN Option,
    PVOID Data,
    PUINTN DataSize,
    BOOL Set
    )

/*++

Routine Description:

    This routine gets or sets socket information.

Arguments:

    Socket - Supplies the socket handle.

    InformationType - Supplies the socket information type category to which
        specified option belongs.

    Option - Supplies the option to get or set, which is specific to the
        information type. The type of this value is generally
        SOCKET_<information_type>_OPTION.

    Data - Supplies a pointer to the data buffer where the data is either
        returned for a get operation or given for a set operation. If the
        buffer is too small for a get request, the truncated data will be
        returned and the routine will fail with STATUS_BUFFER_TOO_SMALL.

    DataSize - Supplies a pointer that on input contains the size of the data
        buffer. On output, this contains the required size of the data buffer.

    Set - Supplies a boolean indicating if this is a get operation (FALSE) or
        a set operation (TRUE).

Return Value:

    Status code.

--*/

{

    SYSTEM_CALL_SOCKET_GET_SET_INFORMATION Request;

    Request.Socket = Socket;
    Request.InformationType = InformationType;
    Request.Option = Option;
    Request.Data = Data;
    Request.DataSize = *DataSize;
    Request.Set = Set;
    OsSystemCall(SystemCallSocketGetSetInformation, &Request);
    *DataSize = Request.DataSize;
    return Request.Status;
}

OS_API
KSTATUS
OsSocketShutdown (
    HANDLE Socket,
    ULONG ShutdownType
    )

/*++

Routine Description:

    This routine shuts down communication with a given socket.

Arguments:

    Socket - Supplies the socket handle.

    ShutdownType - Supplies the shutdown type to perform. See the
        SOCKET_SHUTDOWN_* definitions. These are NOT the same as the C library
        definitions.

Return Value:

    STATUS_SUCCESS on success.

    STATUS_NOT_A_SOCKET if the given handle wasn't a socket.

    Other error codes on failure.

--*/

{

    SYSTEM_CALL_SOCKET_SHUTDOWN Parameters;

    Parameters.Socket = Socket;
    Parameters.ShutdownType = ShutdownType;
    OsSystemCall(SystemCallSocketShutdown, &Parameters);
    return Parameters.Status;
}

//
// --------------------------------------------------------- Internal Functions
//

