/*++

Copyright (c) 2013 Minoca Corp. All Rights Reserved

Module Name:

    socket.c

Abstract:

    This module implements kernel support for sockets.

Author:

    Evan Green 4-Apr-2013

Environment:

    Kernel

--*/

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

#include <minoca/kernel/kernel.h>
#include "iop.h"
#include "unsocket.h"

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

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

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

VOID
IopDestroySocket (
    PSOCKET Socket
    );

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

//
// Store a pointer to the core networking interface.
//

NET_INTERFACE IoNetInterface;
BOOL IoNetInterfaceInitialized = FALSE;

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

KERNEL_API
VOID
IoInitializeCoreNetworking (
    PNET_INTERFACE Interface
    )

/*++

Routine Description:

    This routine initializes the interface between the kernel and the core
    networking library. This routine should not be called by random drivers.

Arguments:

    Interface - Supplies a pointer to the core networking library interface.

Return Value:

    None.

--*/

{

    ASSERT((Interface->CreateSocket != NULL) &&
           (Interface->DestroySocket != NULL) &&
           (Interface->BindToAddress != NULL) &&
           (Interface->Listen != NULL) &&
           (Interface->Accept != NULL) &&
           (Interface->Connect != NULL) &&
           (Interface->CloseSocket != NULL) &&
           (Interface->Send != NULL) &&
           (Interface->Receive != NULL) &&
           (Interface->GetSetSocketInformation != NULL) &&
           (Interface->Shutdown != NULL) &&
           (Interface->UserControl != NULL));

    if (IoNetInterfaceInitialized != FALSE) {

        ASSERT(FALSE);

        return;
    }

    RtlCopyMemory(&IoNetInterface, Interface, sizeof(NET_INTERFACE));
    IoNetInterfaceInitialized = TRUE;
    return;
}

KERNEL_API
ULONG
IoSocketAddReference (
    PSOCKET Socket
    )

/*++

Routine Description:

    This routine increases the reference count on a socket.

Arguments:

    Socket - Supplies a pointer to the socket whose reference count should be
        incremented.

Return Value:

    Returns the old reference count.

--*/

{

    ULONG OldReferenceCount;

    OldReferenceCount = RtlAtomicAdd32(&(Socket->ReferenceCount), 1);

    ASSERT((OldReferenceCount != 0) & (OldReferenceCount < 0x20000000));

    return OldReferenceCount;
}

KERNEL_API
ULONG
IoSocketReleaseReference (
    PSOCKET Socket
    )

/*++

Routine Description:

    This routine decreases the reference count of a socket, and destroys the
    socket if in this call the reference count drops to zero.

Arguments:

    Socket - Supplies a pointer to the socket whose reference count should be
        decremented.

Return Value:

    Returns the old reference count.

--*/

{

    ULONG OldReferenceCount;

    OldReferenceCount = RtlAtomicAdd32(&(Socket->ReferenceCount), -1);

    ASSERT((OldReferenceCount != 0) & (OldReferenceCount < 0x20000000));

    if (OldReferenceCount == 1) {
        IopDestroySocket(Socket);
    }

    return OldReferenceCount;
}

KERNEL_API
KSTATUS
IoSocketCreatePair (
    NET_DOMAIN_TYPE Domain,
    NET_SOCKET_TYPE Type,
    ULONG Protocol,
    ULONG OpenFlags,
    PIO_HANDLE IoHandles[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 a bitfield of open flags governing the new handles.
        See OPEN_FLAG_* definitions.

    IoHandles - Supplies an array where the two I/O handles to the connected
        sockets will be returned on success.

Return Value:

    Status code.

--*/

{

    KSTATUS Status;

    if (Domain == NetDomainLocal) {
        Status = IopCreateUnixSocketPair(Type, Protocol, OpenFlags, IoHandles);

    } else {
        Status = STATUS_DOMAIN_NOT_SUPPORTED;
    }

    return Status;
}

KERNEL_API
KSTATUS
IoSocketCreate (
    NET_DOMAIN_TYPE Domain,
    NET_SOCKET_TYPE Type,
    ULONG Protocol,
    ULONG OpenFlags,
    PIO_HANDLE *IoHandle
    )

/*++

Routine Description:

    This routine allocates resources associated with a new socket.

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 the open flags for the socket. See OPEN_FLAG_*
        definitions.

    IoHandle - Supplies a pointer where a pointer to the new socket's I/O
        handle will be returned.

Return Value:

    Status code.

--*/

{

    SOCKET_CREATION_PARAMETERS Parameters;
    FILE_PERMISSIONS Permissions;
    PKPROCESS Process;
    PSOCKET Socket;
    KSTATUS Status;

    Process = PsGetCurrentProcess();
    Parameters.Domain = Domain;
    Parameters.Type = Type;
    Parameters.Protocol = Protocol;
    Parameters.ExistingSocket = NULL;
    Permissions = FILE_PERMISSION_ALL & ~(Process->Umask);
    Status = IopOpen(FALSE,
                     NULL,
                     NULL,
                     0,
                     IO_ACCESS_READ | IO_ACCESS_WRITE,
                     OpenFlags,
                     IoObjectSocket,
                     &Parameters,
                     Permissions,
                     IoHandle);

    if (KSUCCESS(Status)) {
        Socket = NULL;
        Status = IoGetSocketFromHandle(*IoHandle, &Socket);

        ASSERT(KSUCCESS(Status));
        ASSERT(Socket != NULL);

        Socket->IoHandle = *IoHandle;
    }

    return Status;
}

KERNEL_API
KSTATUS
IoSocketBindToAddress (
    BOOL FromKernelMode,
    PIO_HANDLE Handle,
    PVOID Link,
    PNETWORK_ADDRESS Address,
    PSTR Path,
    UINTN PathSize
    )

/*++

Routine Description:

    This routine binds the socket to the given address and starts listening for
    client requests.

Arguments:

    FromKernelMode - Supplies a boolean indicating if the request is coming
        from kernel mode or user mode. This value affects the root path node
        to traverse for local domain sockets.

    Handle - Supplies a pointer to the socket handle to bind.

    Link - Supplies an optional pointer to a link to bind to.

    Address - Supplies a pointer to the address to bind the socket to.

    Path - Supplies an optional pointer to a path, required if the network
        address is a local socket.

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

Return Value:

    Status code.

--*/

{

    PSOCKET Socket;
    KSTATUS Status;

    Status = IoGetSocketFromHandle(Handle, &Socket);
    if (!KSUCCESS(Status)) {
        goto SocketBindToAddressEnd;
    }

    //
    // If it's a local domain socket and there's an address, bind it into the
    // file system.
    //

    if (Socket->Domain == NetDomainLocal) {
        Status = IopUnixSocketBindToAddress(FromKernelMode,
                                            Handle,
                                            Address,
                                            Path,
                                            PathSize);

    } else {
        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.BindToAddress(Socket, Link, Address);
        }
    }

    if (!KSUCCESS(Status)) {
        goto SocketBindToAddressEnd;
    }

SocketBindToAddressEnd:
    return Status;
}

KERNEL_API
KSTATUS
IoSocketListen (
    PIO_HANDLE Handle,
    ULONG BacklogCount
    )

/*++

Routine Description:

    This routine adds a bound socket to the list of listening sockets,
    officially allowing sockets to attempt to connect to it.

Arguments:

    Handle - Supplies a pointer to the socket to mark as listening.

    BacklogCount - Supplies the number of attempted connections that can be
        queued before additional connections are refused.

Return Value:

    Status code.

--*/

{

    PSOCKET Socket;
    KSTATUS Status;

    Status = IoGetSocketFromHandle(Handle, &Socket);
    if (!KSUCCESS(Status)) {
        goto SocketListenEnd;
    }

    if (Socket->Domain == NetDomainLocal) {
        Status = IopUnixSocketListen(Socket, BacklogCount);

    } else {
        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.Listen(Socket, BacklogCount);
        }
    }

SocketListenEnd:
    return Status;
}

KERNEL_API
KSTATUS
IoSocketAccept (
    PIO_HANDLE Handle,
    PIO_HANDLE *NewConnectionSocket,
    PNETWORK_ADDRESS RemoteAddress,
    PSTR *RemotePath,
    PUINTN RemotePathSize
    )

/*++

Routine Description:

    This routine accepts an incoming connection on a listening connection-based
    socket.

Arguments:

    Handle - Supplies a pointer to the socket to accept a connection from.

    NewConnectionSocket - Supplies a pointer where a new socket will be
        returned that represents the accepted connection with the remote
        host.

    RemoteAddress - Supplies a pointer where the address of the connected
        remote host will be returned.

    RemotePath - Supplies a pointer where a string containing the remote path
        will be returned on success. The caller does not own this string, it is
        connected with the new socket coming out. This only applies to local
        sockets.

    RemotePathSize - Supplies a pointer where the size of the remote path in
        bytes will be returned on success.

Return Value:

    Status code.

--*/

{

    PSOCKET Socket;
    KSTATUS Status;

    *RemotePath = NULL;
    *RemotePathSize = 0;
    Status = IoGetSocketFromHandle(Handle, &Socket);
    if (!KSUCCESS(Status)) {
        goto SocketAcceptEnd;
    }

    if (Socket->Domain == NetDomainLocal) {
        Status = IopUnixSocketAccept(Socket,
                                     NewConnectionSocket,
                                     RemoteAddress,
                                     RemotePath,
                                     RemotePathSize);

    } else {
        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.Accept(Socket,
                                           NewConnectionSocket,
                                           RemoteAddress);
        }
    }

SocketAcceptEnd:
    return Status;
}

KERNEL_API
KSTATUS
IoSocketConnect (
    BOOL FromKernelMode,
    PIO_HANDLE Handle,
    PNETWORK_ADDRESS Address,
    PSTR RemotePath,
    UINTN RemotePathSize
    )

/*++

Routine Description:

    This routine attempts to make an outgoing connection to a server.

Arguments:

    FromKernelMode - Supplies a boolean indicating if the request is coming
        from kernel mode or user mode.

    Handle - Supplies a pointer to the socket to use for the connection.

    Address - Supplies a pointer to the address to connect to.

    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.

--*/

{

    PSOCKET Socket;
    KSTATUS Status;

    Status = IoGetSocketFromHandle(Handle, &Socket);
    if (!KSUCCESS(Status)) {
        goto SocketConnectEnd;
    }

    if (Socket->Domain == NetDomainLocal) {
        Status = IopUnixSocketConnect(FromKernelMode,
                                      Socket,
                                      Address,
                                      RemotePath,
                                      RemotePathSize);

    } else {
        if (RemotePathSize != 0) {
            Status = STATUS_INVALID_PARAMETER;
            goto SocketConnectEnd;
        }

        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.Connect(Socket, Address);
        }
    }

SocketConnectEnd:
    return Status;
}

KERNEL_API
KSTATUS
IoSocketSendData (
    BOOL FromKernelMode,
    PIO_HANDLE Handle,
    PSOCKET_IO_PARAMETERS Parameters,
    PIO_BUFFER IoBuffer
    )

/*++

Routine Description:

    This routine sends the given data buffer through the network.

Arguments:

    FromKernelMode - Supplies a boolean indicating if the request is coming
        from kernel mode or user mode. This value affects the root path node
        to traverse for local domain sockets.

    Handle - Supplies a pointer to the socket to send the data to.

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

    IoBuffer - Supplies a pointer to the I/O buffer containing the data to
        send.

Return Value:

    Status code.

--*/

{

    PSOCKET Socket;
    KSTATUS Status;

    Socket = NULL;
    Status = IoGetSocketFromHandle(Handle, &Socket);
    if (!KSUCCESS(Status)) {
        goto SocketSendData;
    }

    if ((Parameters->SocketIoFlags & SOCKET_IO_NON_BLOCKING) != 0) {
        Parameters->TimeoutInMilliseconds = 0;
    }

    if (Socket->Domain == NetDomainLocal) {
        Status = IopUnixSocketSendData(FromKernelMode,
                                       Socket,
                                       Parameters,
                                       IoBuffer);

    } else {
        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.Send(FromKernelMode,
                                         Socket,
                                         Parameters,
                                         IoBuffer);
        }
    }

SocketSendData:
    return Status;
}

KERNEL_API
KSTATUS
IoSocketReceiveData (
    BOOL FromKernelMode,
    PIO_HANDLE Handle,
    PSOCKET_IO_PARAMETERS Parameters,
    PIO_BUFFER IoBuffer
    )

/*++

Routine Description:

    This routine is called by the user to receive data from the socket.

Arguments:

    FromKernelMode - Supplies a boolean indicating if the request is coming
        from kernel mode or user mode. This value affects the root path node
        to traverse for local domain sockets.

    Handle - Supplies a pointer to the socket to receive data from.

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

    IoBuffer - Supplies a pointer to the I/O buffer where the received data
        will be returned.

Return Value:

    STATUS_SUCCESS if any bytes were read.

    STATUS_TIMEOUT if the request timed out.

    STATUS_BUFFER_TOO_SMALL if the incoming datagram was too large for the
        buffer. The remainder of the datagram is discarded in this case.

    Other error codes on other failures.

--*/

{

    PSOCKET Socket;
    KSTATUS Status;

    Socket = NULL;
    Status = IoGetSocketFromHandle(Handle, &Socket);
    if (!KSUCCESS(Status)) {
        goto SocketReceiveDataEnd;
    }

    if ((Parameters->SocketIoFlags & SOCKET_IO_NON_BLOCKING) != 0) {
        Parameters->TimeoutInMilliseconds = 0;
    }

    if (Socket->Domain == NetDomainLocal) {
        Status = IopUnixSocketReceiveData(FromKernelMode,
                                          Socket,
                                          Parameters,
                                          IoBuffer);

    } else {
        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.Receive(FromKernelMode,
                                            Socket,
                                            Parameters,
                                            IoBuffer);
        }
    }

SocketReceiveDataEnd:
    return Status;
}

KERNEL_API
KSTATUS
IoSocketGetSetInformation (
    PIO_HANDLE IoHandle,
    SOCKET_INFORMATION_TYPE InformationType,
    UINTN SocketOption,
    PVOID Data,
    PUINTN DataSize,
    BOOL Set
    )

/*++

Routine Description:

    This routine gets or sets information about the given socket.

Arguments:

    IoHandle - Supplies a pointer to the I/O handle of the socket.

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

    SocketOption - 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
        data buffer is too small on a get operation, the truncated information
        will be copied to the data buffer and the routine will return
        STATUS_BUFFER_TOO_SMALL.

    DataSize - Supplies a pointer that on input constains 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_SUCCESS on success.

    STATUS_INVALID_PARAMETER if the data is not appropriate for the socket
        option.

    STATUS_BUFFER_TOO_SMALL if the socket option information does not fit in
        the supplied buffer. On a get request, the data buffer will be filled
        with the truncated socket information.

    STATUS_NOT_SUPPORTED_BY_PROTOCOL if the socket option or information type
        is not supported by the socket.

    STATUS_NOT_A_SOCKET if the given handle wasn't a socket.

--*/

{

    PSOCKET Socket;
    KSTATUS Status;

    ASSERT((Data == NULL) || (Data >= KERNEL_VA_START));

    Status = IoGetSocketFromHandle(IoHandle, &Socket);
    if (!KSUCCESS(Status)) {
        goto SocketGetSetInformationEnd;
    }

    if (Socket->Domain == NetDomainLocal) {
        Status = IopUnixSocketGetSetSocketInformation(Socket,
                                                      InformationType,
                                                      SocketOption,
                                                      Data,
                                                      DataSize,
                                                      Set);

    } else {
        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.GetSetSocketInformation(Socket,
                                                            InformationType,
                                                            SocketOption,
                                                            Data,
                                                            DataSize,
                                                            Set);
        }
    }

SocketGetSetInformationEnd:
    return Status;
}

KERNEL_API
KSTATUS
IoSocketShutdown (
    PIO_HANDLE IoHandle,
    ULONG ShutdownType
    )

/*++

Routine Description:

    This routine shuts down communication with a given socket.

Arguments:

    IoHandle - Supplies a pointer to the I/O handle of the socket.

    ShutdownType - Supplies the shutdown type to perform. See the
        SOCKET_SHUTDOWN_* definitions.

Return Value:

    STATUS_SUCCESS on success.

    STATUS_NOT_A_SOCKET if the given handle wasn't a socket.

    Other error codes on failure.

--*/

{

    PSOCKET Socket;
    KSTATUS Status;

    Status = IoGetSocketFromHandle(IoHandle, &Socket);
    if (!KSUCCESS(Status)) {
        goto SocketShutdownEnd;
    }

    if (Socket->Domain == NetDomainLocal) {
        Status = IopUnixSocketShutdown(Socket, ShutdownType);

    } else {
        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.Shutdown(Socket, ShutdownType);
        }
    }

SocketShutdownEnd:
    return Status;
}

KERNEL_API
KSTATUS
IoSocketUserControl (
    PIO_HANDLE Handle,
    ULONG CodeNumber,
    BOOL FromKernelMode,
    PVOID ContextBuffer,
    UINTN ContextBufferSize
    )

/*++

Routine Description:

    This routine handles user control requests destined for a socket.

Arguments:

    Handle - Supplies the open file handle.

    CodeNumber - Supplies the minor code of the request.

    FromKernelMode - Supplies a boolean indicating whether or not this request
        (and the buffer associated with it) originates from user mode (FALSE)
        or kernel mode (TRUE).

    ContextBuffer - Supplies a pointer to the context buffer allocated by the
        caller for the request.

    ContextBufferSize - Supplies the size of the supplied context buffer.

Return Value:

    Status code.

--*/

{

    PSOCKET Socket;
    KSTATUS Status;

    Status = IoGetSocketFromHandle(Handle, &Socket);
    if (!KSUCCESS(Status)) {
        goto SocketUserControlEnd;
    }

    if (Socket->Domain == NetDomainLocal) {
        Status = STATUS_NOT_SUPPORTED;

    } else {
        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.UserControl(Socket,
                                                CodeNumber,
                                                FromKernelMode,
                                                ContextBuffer,
                                                ContextBufferSize);
        }
    }

SocketUserControlEnd:
    return Status;
}

KERNEL_API
KSTATUS
IoGetSocketFromHandle (
    PIO_HANDLE IoHandle,
    PSOCKET *Socket
    )

/*++

Routine Description:

    This routine returns the socket structure from inside an I/O handle. This
    routine is usually only used by networking protocol to get their own
    structures for the socket they create in the "accept" function.

Arguments:

    IoHandle - Supplies a pointer to the I/O handle whose corresponding socket
        is desired.

    Socket - Supplies a pointer where a pointer to the socket corresponding to
        the given handle will be returned on success.

Return Value:

    STATUS_SUCCESS on success.

    STATUS_NOT_A_SOCKET if the given handle wasn't a socket.

--*/

{

    PFILE_OBJECT FileObject;

    FileObject = IoHandle->FileObject;
    if (FileObject->Properties.Type != IoObjectSocket) {
        return STATUS_NOT_A_SOCKET;
    }

    *Socket = FileObject->SpecialIo;
    return STATUS_SUCCESS;
}

VOID
IoSysSocketCreatePair (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine handles the system call that creates a pair of connected
    sockets.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    ULONG HandleFlags;
    ULONG OpenFlags;
    PSYSTEM_CALL_SOCKET_CREATE_PAIR Parameters;
    PKPROCESS Process;
    PIO_HANDLE Sockets[2];
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketCreatePair);

    Parameters = (PSYSTEM_CALL_SOCKET_CREATE_PAIR)SystemCallParameter;
    Parameters->Socket1 = INVALID_HANDLE;
    Parameters->Socket2 = INVALID_HANDLE;
    Process = PsGetCurrentProcess();
    Sockets[0] = NULL;
    Sockets[1] = NULL;
    HandleFlags = 0;
    if ((Parameters->OpenFlags & SYS_OPEN_FLAG_CLOSE_ON_EXECUTE) != 0) {
        HandleFlags |= FILE_DESCRIPTOR_CLOSE_ON_EXECUTE;
    }

    OpenFlags = Parameters->OpenFlags & OPEN_FLAG_NON_BLOCKING;
    Status = IoSocketCreatePair(Parameters->Domain,
                                Parameters->Type,
                                Parameters->Protocol,
                                OpenFlags,
                                Sockets);

    if (!KSUCCESS(Status)) {
        goto CreateSocketPairEnd;
    }

    //
    // Create the handles for the sockets.
    //

    Status = ObCreateHandle(Process->HandleTable,
                            Sockets[0],
                            HandleFlags,
                            &(Parameters->Socket1));

    if (!KSUCCESS(Status)) {
        goto CreateSocketPairEnd;
    }

    Status = ObCreateHandle(Process->HandleTable,
                            Sockets[1],
                            HandleFlags,
                            &(Parameters->Socket2));

    if (!KSUCCESS(Status)) {

        //
        // Destory the first handle manually.
        //

        ObDestroyHandle(Process->HandleTable, Parameters->Socket1);
        Parameters->Socket1 = INVALID_HANDLE;
        goto CreateSocketPairEnd;
    }

    Status = STATUS_SUCCESS;

CreateSocketPairEnd:
    if (!KSUCCESS(Status)) {

        ASSERT((Parameters->Socket1 == INVALID_HANDLE) &&
               (Parameters->Socket2 == INVALID_HANDLE));

        if (Sockets[0] != NULL) {
            IoClose(Sockets[0]);
            Sockets[0] = NULL;
        }

        if (Sockets[1] != NULL) {
            IoClose(Sockets[1]);
            Sockets[1] = NULL;
        }
    }

    Parameters->Status = Status;
    return;
}

VOID
IoSysSocketCreate (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine handles the system call that creates a new socket.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    ULONG HandleFlags;
    PIO_HANDLE IoHandle;
    ULONG OpenFlags;
    PSYSTEM_CALL_SOCKET_CREATE Parameters;
    PKPROCESS Process;
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketCreate);

    Parameters = (PSYSTEM_CALL_SOCKET_CREATE)SystemCallParameter;
    Parameters->Socket = INVALID_HANDLE;
    Process = PsGetCurrentProcess();

    ASSERT(Process != PsGetKernelProcess());

    HandleFlags = 0;
    if ((Parameters->OpenFlags & SYS_OPEN_FLAG_CLOSE_ON_EXECUTE) != 0) {
        HandleFlags |= FILE_DESCRIPTOR_CLOSE_ON_EXECUTE;
    }

    OpenFlags = Parameters->OpenFlags & SYS_OPEN_FLAG_NON_BLOCKING;
    IoHandle = NULL;
    Status = IoSocketCreate(Parameters->Domain,
                            Parameters->Type,
                            Parameters->Protocol,
                            OpenFlags,
                            &IoHandle);

    if (!KSUCCESS(Status)) {
        goto CreateSocketEnd;
    }

    //
    // Create a handle table entry for this socket.
    //

    Status = ObCreateHandle(Process->HandleTable,
                            IoHandle,
                            HandleFlags,
                            &(Parameters->Socket));

    if (!KSUCCESS(Status)) {
        goto CreateSocketEnd;
    }

CreateSocketEnd:
    if (!KSUCCESS(Status)) {
        if (IoHandle != NULL) {
            IoIoHandleReleaseReference(IoHandle);
        }

        Parameters->Socket = INVALID_HANDLE;
    }

    Parameters->Status = Status;
    return;
}

VOID
IoSysSocketBind (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine attempts to bind a socket to a local address.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    PIO_HANDLE IoHandle;
    PSYSTEM_CALL_SOCKET_BIND Parameters;
    PKPROCESS Process;
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketBind);

    Parameters = (PSYSTEM_CALL_SOCKET_BIND)SystemCallParameter;
    Process = PsGetCurrentProcess();

    //
    // Get the I/O handle.
    //

    IoHandle = ObGetHandleValue(Process->HandleTable, Parameters->Socket, NULL);
    if (IoHandle == NULL) {
        Status = STATUS_INVALID_HANDLE;
        goto SysSocketBindEnd;
    }

    Status = IoSocketBindToAddress(FALSE,
                                   IoHandle,
                                   NULL,
                                   &(Parameters->Address),
                                   Parameters->Path,
                                   Parameters->PathSize);

    if (!KSUCCESS(Status)) {
        goto SysSocketBindEnd;
    }

SysSocketBindEnd:

    //
    // Release the reference that was added when the handle was looked up.
    //

    if (IoHandle != NULL) {
        IoIoHandleReleaseReference(IoHandle);
    }

    Parameters->Status = Status;
    return;
}

VOID
IoSysSocketListen (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine handles the system call that makes a socket listen and become
    eligible to accept new incoming connections.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    PIO_HANDLE IoHandle;
    PSYSTEM_CALL_SOCKET_LISTEN Parameters;
    PKPROCESS Process;
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketListen);

    Parameters = (PSYSTEM_CALL_SOCKET_LISTEN)SystemCallParameter;
    Process = PsGetCurrentProcess();
    IoHandle = ObGetHandleValue(Process->HandleTable, Parameters->Socket, NULL);
    if (IoHandle == NULL) {
        Status = STATUS_INVALID_HANDLE;
        goto SysSocketListenEnd;
    }

    Status = IoSocketListen(IoHandle, Parameters->BacklogCount);
    if (!KSUCCESS(Status)) {
        goto SysSocketListenEnd;
    }

SysSocketListenEnd:

    //
    // Release the reference that was added when the handle was looked up.
    //

    if (IoHandle != NULL) {
        IoIoHandleReleaseReference(IoHandle);
    }

    Parameters->Status = Status;
    return;
}

VOID
IoSysSocketAccept (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine handles the system call that accepts a new incoming
    connection on a socket and spins it off into another socket.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    UINTN CopySize;
    ULONG HandleFlags;
    PIO_HANDLE IoHandle;
    PIO_HANDLE NewHandle;
    PSYSTEM_CALL_SOCKET_ACCEPT Parameters;
    PKPROCESS Process;
    PSTR RemotePath;
    UINTN RemotePathSize;
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketAccept);

    NewHandle = NULL;
    Parameters = (PSYSTEM_CALL_SOCKET_ACCEPT)SystemCallParameter;
    Parameters->NewSocket = INVALID_HANDLE;
    Process = PsGetCurrentProcess();
    IoHandle = ObGetHandleValue(Process->HandleTable, Parameters->Socket, NULL);
    if (IoHandle == NULL) {
        Status = STATUS_INVALID_HANDLE;
        goto SysSocketAcceptEnd;
    }

    //
    // Run the actual accept function, which will pop out a new socket that is
    // unconnected to an I/O handle.
    //

    RemotePath = NULL;
    RemotePathSize = 0;
    Status = IoSocketAccept(IoHandle,
                            &NewHandle,
                            &(Parameters->Address),
                            &RemotePath,
                            &RemotePathSize);

    if (!KSUCCESS(Status)) {
        goto SysSocketAcceptEnd;
    }

    if ((Parameters->OpenFlags & SYS_OPEN_FLAG_NON_BLOCKING) != 0) {
        NewHandle->OpenFlags |= OPEN_FLAG_NON_BLOCKING;
    }

    HandleFlags = 0;
    if ((Parameters->OpenFlags & SYS_OPEN_FLAG_CLOSE_ON_EXECUTE) != 0) {
        HandleFlags |= FILE_DESCRIPTOR_CLOSE_ON_EXECUTE;
    }

    //
    // Finally, create a user mode handle for this socket.
    //

    Status = ObCreateHandle(Process->HandleTable,
                            NewHandle,
                            HandleFlags,
                            &(Parameters->NewSocket));

    if (!KSUCCESS(Status)) {
        goto SysSocketAcceptEnd;
    }

    //
    // Copy the remote path over.
    //

    if (RemotePath != NULL) {
        CopySize = RemotePathSize;
        if (CopySize > Parameters->RemotePathSize) {
            CopySize = Parameters->RemotePathSize;
        }

        if (CopySize != 0) {
            Status = MmCopyToUserMode(Parameters->RemotePath,
                                      RemotePath,
                                      CopySize);
        }

        Parameters->RemotePathSize = RemotePathSize;
        if (!KSUCCESS(Status)) {
            goto SysSocketAcceptEnd;
        }
    }

SysSocketAcceptEnd:
    if (!KSUCCESS(Status)) {
        if (NewHandle != NULL) {
            IoIoHandleReleaseReference(NewHandle);
        }
    }

    //
    // Release the reference that was added when the handle was looked up.
    //

    if (IoHandle != NULL) {
        IoIoHandleReleaseReference(IoHandle);
    }

    Parameters->Status = Status;
    return;
}

VOID
IoSysSocketConnect (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine handles the system call that reaches and and attempts to
    connect with another socket.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    PIO_HANDLE IoHandle;
    PSYSTEM_CALL_SOCKET_CONNECT Parameters;
    PSTR PathCopy;
    PKPROCESS Process;
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketConnect);

    Parameters = (PSYSTEM_CALL_SOCKET_CONNECT)SystemCallParameter;
    PathCopy = NULL;
    Process = PsGetCurrentProcess();
    IoHandle = ObGetHandleValue(Process->HandleTable, Parameters->Socket, NULL);
    if (IoHandle == NULL) {
        Status = STATUS_INVALID_HANDLE;
        goto SysSocketConnectEnd;
    }

    if (Parameters->RemotePathSize != 0) {
        Status = MmCreateCopyOfUserModeString(Parameters->RemotePath,
                                              Parameters->RemotePathSize,
                                              UNIX_SOCKET_ALLOCATION_TAG,
                                              &PathCopy);

        if (!KSUCCESS(Status)) {
            goto SysSocketConnectEnd;
        }
    }

    Status = IoSocketConnect(FALSE,
                             IoHandle,
                             &(Parameters->Address),
                             PathCopy,
                             Parameters->RemotePathSize);

    if (!KSUCCESS(Status)) {
        goto SysSocketConnectEnd;
    }

SysSocketConnectEnd:
    if (PathCopy != NULL) {
        MmFreePagedPool(PathCopy);
    }

    //
    // Release the reference that was added when the handle was looked up.
    //

    if (IoHandle != NULL) {
        IoIoHandleReleaseReference(IoHandle);
    }

    Parameters->Status = Status;
    return;
}

VOID
IoSysSocketPerformIo (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine handles the system call that sends a packet to a specific
    destination or receives data from a destination. Sockets may also use the
    generic perform I/O operations if the identity of the remote address is
    either already known or not needed.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    IO_BUFFER IoBuffer;
    PIO_HANDLE IoHandle;
    SOCKET_IO_PARAMETERS IoParameters;
    PSYSTEM_CALL_SOCKET_PERFORM_IO Parameters;
    PKPROCESS Process;
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketPerformIo);

    Parameters = (PSYSTEM_CALL_SOCKET_PERFORM_IO)SystemCallParameter;
    Process = PsGetCurrentProcess();

    ASSERT(SYS_WAIT_TIME_INDEFINITE == WAIT_TIME_INDEFINITE);

    IoHandle = ObGetHandleValue(Process->HandleTable, Parameters->Socket, NULL);
    if (IoHandle == NULL) {
        Status = STATUS_INVALID_HANDLE;
        goto SysSocketPerformIoEnd;
    }

    Status = MmCopyFromUserMode(&IoParameters,
                                Parameters->Parameters,
                                sizeof(SOCKET_IO_PARAMETERS));

    if (!KSUCCESS(Status)) {
        goto SysSocketPerformIoEnd;
    }

    IoParameters.IoFlags &= SYS_IO_FLAG_MASK;
    Status = MmInitializeIoBuffer(&IoBuffer,
                                  Parameters->Buffer,
                                  INVALID_PHYSICAL_ADDRESS,
                                  IoParameters.Size,
                                  0);

    if (!KSUCCESS(Status)) {
        goto SysSocketPerformIoEnd;
    }

    //
    // Non-blocking handles always have a timeout of zero.
    //

    if ((IoHandle->OpenFlags & OPEN_FLAG_NON_BLOCKING) != 0) {
        IoParameters.TimeoutInMilliseconds = 0;
    }

    if ((IoParameters.IoFlags & SYS_IO_FLAG_WRITE) != 0) {
        Status = IoSocketSendData(FALSE, IoHandle, &IoParameters, &IoBuffer);

        //
        // Send a pipe signal if the returning status was "broken pipe".
        //

        if (Status == STATUS_BROKEN_PIPE) {

            ASSERT(Process != PsGetKernelProcess());

            PsSignalProcess(Process, SIGNAL_BROKEN_PIPE, NULL);
        }

    } else {
        Status = IoSocketReceiveData(FALSE, IoHandle, &IoParameters, &IoBuffer);
    }

    if (!KSUCCESS(Status)) {
        goto SysSocketPerformIoEnd;
    }

SysSocketPerformIoEnd:
    MmCopyToUserMode(Parameters->Parameters,
                     &IoParameters,
                     sizeof(SOCKET_IO_PARAMETERS));

    //
    // Release the reference that was added when the handle was looked up.
    //

    if (IoHandle != NULL) {
        IoIoHandleReleaseReference(IoHandle);
    }

    Parameters->Status = Status;
    return;
}

VOID
IoSysSocketPerformVectoredIo (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine handles the system call that performs socket I/O using I/O
    vectors.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    PIO_BUFFER IoBuffer;
    PIO_HANDLE IoHandle;
    SOCKET_IO_PARAMETERS IoParameters;
    PSYSTEM_CALL_SOCKET_PERFORM_VECTORED_IO Parameters;
    PKPROCESS Process;
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketPerformVectoredIo);

    IoBuffer = NULL;
    Parameters = (PSYSTEM_CALL_SOCKET_PERFORM_VECTORED_IO)SystemCallParameter;
    Process = PsGetCurrentProcess();

    ASSERT(SYS_WAIT_TIME_INDEFINITE == WAIT_TIME_INDEFINITE);

    IoHandle = ObGetHandleValue(Process->HandleTable, Parameters->Socket, NULL);
    if (IoHandle == NULL) {
        Status = STATUS_INVALID_HANDLE;
        goto SysSocketPerformVectoredIoEnd;
    }

    Status = MmCopyFromUserMode(&IoParameters,
                                Parameters->Parameters,
                                sizeof(SOCKET_IO_PARAMETERS));

    if (!KSUCCESS(Status)) {
        goto SysSocketPerformVectoredIoEnd;
    }

    IoParameters.IoFlags &= SYS_IO_FLAG_MASK;
    Status = MmCreateIoBufferFromVector(Parameters->VectorArray,
                                        FALSE,
                                        Parameters->VectorCount,
                                        &IoBuffer);

    if (!KSUCCESS(Status)) {
        goto SysSocketPerformVectoredIoEnd;
    }

    //
    // Non-blocking handles always have a timeout of zero.
    //

    if ((IoHandle->OpenFlags & OPEN_FLAG_NON_BLOCKING) != 0) {
        IoParameters.TimeoutInMilliseconds = 0;
    }

    if ((IoParameters.IoFlags & SYS_IO_FLAG_WRITE) != 0) {
        Status = IoSocketSendData(FALSE, IoHandle, &IoParameters, IoBuffer);

        //
        // Send a pipe signal if the returning status was "broken pipe".
        //

        if (Status == STATUS_BROKEN_PIPE) {

            ASSERT(Process != PsGetKernelProcess());

            PsSignalProcess(Process, SIGNAL_BROKEN_PIPE, NULL);
        }

    } else {
        Status = IoSocketReceiveData(FALSE, IoHandle, &IoParameters, IoBuffer);
    }

    if (!KSUCCESS(Status)) {
        goto SysSocketPerformVectoredIoEnd;
    }

SysSocketPerformVectoredIoEnd:
    if (IoBuffer != NULL) {
        MmFreeIoBuffer(IoBuffer);
    }

    MmCopyToUserMode(Parameters->Parameters,
                     &IoParameters,
                     sizeof(SOCKET_IO_PARAMETERS));

    //
    // Release the reference that was added when the handle was looked up.
    //

    if (IoHandle != NULL) {
        IoIoHandleReleaseReference(IoHandle);
    }

    Parameters->Status = Status;
    return;
}

VOID
IoSysSocketGetSetInformation (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine implements the system call for getting or setting socket
    information.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    PVOID Buffer;
    UINTN CopySize;
    KSTATUS CopyStatus;
    PIO_HANDLE IoHandle;
    PSYSTEM_CALL_SOCKET_GET_SET_INFORMATION Parameters;
    PKPROCESS Process;
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketGetSetInformation);

    Buffer = NULL;
    Parameters = (PSYSTEM_CALL_SOCKET_GET_SET_INFORMATION)SystemCallParameter;
    Process = PsGetCurrentProcess();
    IoHandle = ObGetHandleValue(Process->HandleTable, Parameters->Socket, NULL);
    if (IoHandle == NULL) {
        Status = STATUS_INVALID_HANDLE;
        goto SysSocketGetSetInformationEnd;
    }

    //
    // Create a paged pool buffer to hold the data.
    //

    CopySize = 0;
    if (Parameters->DataSize != 0) {
        Buffer = MmAllocatePagedPool(Parameters->DataSize,
                                     SOCKET_INFORMATION_ALLOCATION_TAG);

        if (Buffer == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto SysSocketGetSetInformationEnd;
        }

        CopySize = Parameters->DataSize;

        //
        // Copy the data into the kernel mode buffer.
        //

        Status = MmCopyFromUserMode(Buffer,
                                    Parameters->Data,
                                    Parameters->DataSize);

        if (!KSUCCESS(Status)) {
            goto SysSocketGetSetInformationEnd;
        }
    }

    Status = IoSocketGetSetInformation(IoHandle,
                                       Parameters->InformationType,
                                       Parameters->Option,
                                       Buffer,
                                       &(Parameters->DataSize),
                                       Parameters->Set);

    //
    // Copy the data back into user mode, even on set operations.
    //

    if (CopySize > Parameters->DataSize) {
        CopySize = Parameters->DataSize;
    }

    if (CopySize != 0) {
        CopyStatus = MmCopyToUserMode(Parameters->Data, Buffer, CopySize);
        if ((KSUCCESS(Status)) && (!KSUCCESS(CopyStatus))) {
            Status = CopyStatus;
        }
    }

SysSocketGetSetInformationEnd:
    if (Buffer != NULL) {
        MmFreePagedPool(Buffer);
    }

    if (IoHandle != NULL) {
        IoIoHandleReleaseReference(IoHandle);
    }

    Parameters->Status = Status;
    return;
}

VOID
IoSysSocketShutdown (
    ULONG SystemCallNumber,
    PVOID SystemCallParameter,
    PTRAP_FRAME TrapFrame,
    PULONG ResultSize
    )

/*++

Routine Description:

    This routine implements the system call for shutting down communication to
    a socket.

Arguments:

    SystemCallNumber - Supplies the system call number that was requested.

    SystemCallParameter - Supplies a pointer to the parameters supplied with
        the system call. This structure will be a stack-local copy of the
        actual parameters passed from user-mode.

    TrapFrame - Supplies a pointer to the trap frame generated by this jump
        from user mode to kernel mode.

    ResultSize - Supplies a pointer where the system call routine returns the
        size of the parameter structure to be copied back to user mode. The
        value returned here must be no larger than the original parameter
        structure size. The default is the original size of the parameters.

Return Value:

    None.

--*/

{

    PIO_HANDLE IoHandle;
    PSYSTEM_CALL_SOCKET_SHUTDOWN Parameters;
    PKPROCESS Process;
    KSTATUS Status;

    ASSERT(SystemCallNumber == SystemCallSocketShutdown);

    Parameters = (PSYSTEM_CALL_SOCKET_SHUTDOWN)SystemCallParameter;
    Process = PsGetCurrentProcess();
    IoHandle = ObGetHandleValue(Process->HandleTable, Parameters->Socket, NULL);
    if (IoHandle == NULL) {
        Status = STATUS_INVALID_HANDLE;
        goto SysSocketShutdownEnd;
    }

    Status = IoSocketShutdown(IoHandle, Parameters->ShutdownType);

SysSocketShutdownEnd:

    //
    // Release the reference that was added when the handle was looked up.
    //

    if (IoHandle != NULL) {
        IoIoHandleReleaseReference(IoHandle);
    }

    Parameters->Status = Status;
    return;
}

KSTATUS
IopCreateSocket (
    PVOID Parameter,
    FILE_PERMISSIONS CreatePermissions,
    PFILE_OBJECT *FileObject
    )

/*++

Routine Description:

    This routine allocates resources associated with a new socket.

Arguments:

    Parameter - Supplies the parameters the socket was created with.

    CreatePermissions - Supplies the permissions to create the file with.

    FileObject - Supplies a pointer where the new file object representing the
        socket will be returned on success.

Return Value:

    Status code.

--*/

{

    BOOL Created;
    NETWORK_ADDRESS LocalAddress;
    PFILE_OBJECT NewFileObject;
    PSOCKET_CREATION_PARAMETERS Parameters;
    FILE_PROPERTIES Properties;
    PVOID RootObject;
    PSOCKET Socket;
    KSTATUS Status;
    PKTHREAD Thread;

    Created = FALSE;
    NewFileObject = NULL;
    Parameters = (PSOCKET_CREATION_PARAMETERS)Parameter;

    //
    // If there are no parameters, then this file object is being created from
    // a leftover file system entry. Just succeed, but it will never be able to
    // be opened. The common case for this is a request for the file properties.
    //

    if (Parameters == NULL) {

        ASSERT(*FileObject != NULL);

        Status = STATUS_SUCCESS;
        goto CreateSocketEnd;
    }

    Socket = NULL;

    //
    // In cases where a Unix socket is trying to bind to a new path entry,
    // there's already a socket that's been created. Use that one.
    //

    if (Parameters->ExistingSocket != NULL) {
        Socket = Parameters->ExistingSocket;

        ASSERT(Socket->IoState != NULL);

        IoSocketAddReference(Socket);

    //
    // Most of the time, a socket needs to be created.
    //

    } else {
        if (Parameters->Domain == NetDomainLocal) {
            Status = IopCreateUnixSocket(Parameters->Domain,
                                         Parameters->Type,
                                         Parameters->Protocol,
                                         &Socket);

        } else {
            if (IoNetInterfaceInitialized == FALSE) {
                Status = STATUS_NOT_IMPLEMENTED;
                goto CreateSocketEnd;
            }

            Status = IoNetInterface.CreateSocket(Parameters->Domain,
                                                 Parameters->Type,
                                                 Parameters->Protocol,
                                                 &Socket);
        }

        if (!KSUCCESS(Status)) {
            goto CreateSocketEnd;
        }

        IoSocketAddReference(Socket);

        //
        // Fill in the standard parts of the socket structure.
        //

        Socket->Domain = Parameters->Domain;
        Socket->Type = Parameters->Type;
        if (Socket->IoState == NULL) {
            Socket->IoState = IoCreateIoObjectState(FALSE);
            if (Socket->IoState == NULL) {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto CreateSocketEnd;
            }
        }
    }

    //
    // Create or look up a file object for the socket if needed.
    //

    if (*FileObject == NULL) {
        Thread = KeGetCurrentThread();
        RtlZeroMemory(&Properties, sizeof(FILE_PROPERTIES));
        RootObject = ObGetRootObject();
        Properties.DeviceId = OBJECT_MANAGER_DEVICE_ID;
        Properties.FileId = (UINTN)Socket;
        Properties.Type = IoObjectSocket;
        Properties.UserId = Thread->Identity.EffectiveUserId;
        Properties.GroupId = Thread->Identity.EffectiveGroupId;
        Properties.HardLinkCount = 1;
        Properties.Permissions = CreatePermissions;
        KeGetSystemTime(&(Properties.StatusChangeTime));
        RtlCopyMemory(&(Properties.ModifiedTime),
                      &(Properties.StatusChangeTime),
                      sizeof(SYSTEM_TIME));

        RtlCopyMemory(&(Properties.AccessTime),
                      &(Properties.StatusChangeTime),
                      sizeof(SYSTEM_TIME));

        Status = IopCreateOrLookupFileObject(&Properties,
                                             RootObject,
                                             FILE_OBJECT_FLAG_EXTERNAL_IO_STATE,
                                             &NewFileObject,
                                             &Created);

        if (!KSUCCESS(Status)) {
            goto CreateSocketEnd;
        }

        ASSERT((Created != FALSE) || (Socket == Parameters->ExistingSocket));

        *FileObject = NewFileObject;
    }

    ASSERT(((*FileObject)->IoState == NULL) &&
           (((*FileObject)->Flags & FILE_OBJECT_FLAG_EXTERNAL_IO_STATE) != 0));

    (*FileObject)->IoState = Socket->IoState;
    (*FileObject)->SpecialIo = Socket;

    //
    // The socket is finally ready to go. If it is a raw socket it needs to get
    // bound to the any address.
    //

    if ((Socket->Type == NetSocketRaw) &&
        (Socket->Domain != NetDomainLocal)) {

        ASSERT(Parameters->ExistingSocket == NULL);

        RtlZeroMemory(&LocalAddress, sizeof(NETWORK_ADDRESS));
        LocalAddress.Domain = Socket->Domain;
        Status = IoNetInterface.BindToAddress(Socket, NULL, &LocalAddress);
        if (!KSUCCESS(Status)) {
            goto CreateSocketEnd;
        }
    }

    Status = STATUS_SUCCESS;

CreateSocketEnd:

    //
    // On both success and failure, the file object's ready event needs to be
    // signaled. Other threads may be waiting on the event.
    //

    if (*FileObject != NULL) {

        ASSERT((KeGetEventState((*FileObject)->ReadyEvent) == NotSignaled) ||
               (KeGetEventState((*FileObject)->ReadyEvent) ==
                NotSignaledWithWaiters));

        KeSignalEvent((*FileObject)->ReadyEvent, SignalOptionSignalAll);
    }

    if (!KSUCCESS(Status)) {
        if ((Socket != NULL) && ((*FileObject)->SpecialIo != Socket)) {
            IoSocketReleaseReference(Socket);
        }

        if (NewFileObject != NULL) {
            IopFileObjectReleaseReference(NewFileObject);
            *FileObject = NULL;
        }
    }

    return Status;
}

KSTATUS
IopPerformSocketIoOperation (
    PIO_HANDLE Handle,
    PIO_CONTEXT IoContext
    )

/*++

Routine Description:

    This routine reads from or writes to a socket.

Arguments:

    Handle - Supplies a pointer to the socket I/O handle.

    IoContext - Supplies a pointer to the I/O context.

Return Value:

    Status code. A failing status code does not necessarily mean no I/O made it
    in or out. Check the bytes completed value in the I/O context to find out
    how much occurred.

--*/

{

    SOCKET_IO_PARAMETERS IoParameters;
    KSTATUS Status;

    ASSERT(IoContext->IoBuffer != NULL);

    RtlZeroMemory(&IoParameters, sizeof(SOCKET_IO_PARAMETERS));
    IoParameters.Size = IoContext->SizeInBytes;
    IoParameters.TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
    if (IoContext->Write != FALSE) {
        Status = IoSocketSendData(FALSE,
                                  Handle,
                                  &IoParameters,
                                  IoContext->IoBuffer);

    } else {
        Status = IoSocketReceiveData(FALSE,
                                     Handle,
                                     &IoParameters,
                                     IoContext->IoBuffer);
    }

    IoContext->BytesCompleted = IoParameters.Size;
    return Status;
}

KSTATUS
IopOpenSocket (
    PIO_HANDLE IoHandle
    )

/*++

Routine Description:

    This routine opens a socket connection.

Arguments:

    IoHandle - Supplies a pointer to the I/O handle for the socket being opened.

Return Value:

    Status code.

--*/

{

    PFILE_OBJECT FileObject;
    PSOCKET Socket;

    FileObject = IoHandle->FileObject;

    ASSERT(FileObject->Properties.Type == IoObjectSocket);

    if (IoHandle->Access == 0) {
        return STATUS_SUCCESS;
    }

    Socket = FileObject->SpecialIo;
    if (Socket == NULL) {
        return STATUS_NOT_READY;
    }

    return STATUS_SUCCESS;
}

KSTATUS
IopCloseSocket (
    PIO_HANDLE IoHandle
    )

/*++

Routine Description:

    This routine closes a socket connection.

Arguments:

    IoHandle - Supplies a pointer to the socket handle to close.

Return Value:

    Status code.

--*/

{

    PFILE_OBJECT FileObject;
    PSOCKET Socket;
    KSTATUS Status;

    FileObject = IoHandle->FileObject;
    if (FileObject->Properties.Type != IoObjectSocket) {
        Status = STATUS_NOT_A_SOCKET;
        goto CloseSocketEnd;
    }

    if (IoHandle->Access == 0) {
        return STATUS_SUCCESS;
    }

    Socket = FileObject->SpecialIo;
    if (Socket->Domain == NetDomainLocal) {
        Status = IopUnixSocketClose(Socket);

    } else {
        if (IoNetInterfaceInitialized == FALSE) {
            Status = STATUS_NOT_IMPLEMENTED;

        } else {
            Status = IoNetInterface.CloseSocket(Socket);
            if (!KSUCCESS(Status)) {
                goto CloseSocketEnd;
            }
        }
    }

CloseSocketEnd:
    return Status;
}

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

VOID
IopDestroySocket (
    PSOCKET Socket
    )

/*++

Routine Description:

    This routine destroys a socket object.

Arguments:

    Socket - Supplies a pointer to the socket to close.

Return Value:

    Status code.

--*/

{

    ASSERT(Socket->ReferenceCount == 0);

    ASSERT(Socket->IoState != NULL);

    IoDestroyIoObjectState(Socket->IoState);
    Socket->IoState = NULL;
    if (Socket->Domain == NetDomainLocal) {
        IopDestroyUnixSocket(Socket);

    } else {
        IoNetInterface.DestroySocket(Socket);
    }

    return;
}
