# Module sockets

This module implements a simple portable type-safe sockets layer.

Most procedures raise EOS on error.

For OpenSSL support compile with `-d:ssl`. When using SSL be aware that most functions will then raise `ESSL` on SSL errors.

# Imports

os, parseutils, times, openssl, winlean

# Types

    ESSL* = object of ESynch

    TSSLCVerifyMode* = enum
      CVerifyNone, CVerifyPeer

    TSSLProtVersion* = enum
      protSSLv2, protSSLv3, protTLSv1, protSSLv23

    PSSLContext* = distinct PSSLCTX

    TSSLAcceptResult* = enum
      AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess

    TSocket* = ref TSocketImpl

    TPort* = distinct int16

  port type

    TDomain* = enum
      AF_UNIX,                    ## for local socket (using a file). Unsupported on Windows.
      AF_INET = 2,                ## for network protocol IPv4 or
      AF_INET6 = 23               ## for network protocol IPv6.

  domain, which specifies the protocol family of the created socket. Other domains than those that are listed here are unsupported.

    TType* = enum
      SOCK_STREAM = 1,            ## reliable stream-oriented service or Stream Sockets
      SOCK_DGRAM = 2,             ## datagram service or Datagram Sockets
      SOCK_RAW = 3,               ## raw protocols atop the network layer.
      SOCK_SEQPACKET = 5          ## reliable sequenced packet service

  second argument to socket proc

    TProtocol* = enum
      IPPROTO_TCP = 6,            ## Transmission control protocol.
      IPPROTO_UDP = 17,           ## User datagram protocol.
      IPPROTO_IP,                 ## Internet protocol. Unsupported on Windows.
      IPPROTO_IPV6,               ## Internet Protocol Version 6. Unsupported on Windows.
      IPPROTO_RAW,                ## Raw IP Packets Protocol. Unsupported on Windows.
      IPPROTO_ICMP                ## Control message protocol. Unsupported on Windows.

  third argument to socket proc

    TServent* {.pure, final.} = object
      name*: string
      aliases*: seq[string]
      port*: TPort
      proto*: string

  information about a service

    Thostent* {.pure, final.} = object
      name*: string
      aliases*: seq[string]
      addrtype*: TDomain
      length*: int
      addrList*: seq[string]

  information about a given host

    TRecvLineResult* = enum
      RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail

  result for recvLineAsync

    ETimeout* = object of ESynch

# Lets

    InvalidSocket*: TSocket = nil

  invalid socket

# Procs

    proc `==`*(a, b: TPort): bool {.borrow.}

  `==` for ports.

    proc `$`*(p: TPort): string

  returns the port number as a string

    proc ntohl*(x: int32): int32

  Converts 32-bit integers from network to host byte order. On machines where the host byte order is the same as network byte order, this is a no-op; otherwise, it performs a 4-byte swap operation.

    proc ntohs*(x: int16): int16

  Converts 16-bit integers from network to host byte order. On machines where the host byte order is the same as network byte order, this is a no-op; otherwise, it performs a 2-byte swap operation.

    proc htonl*(x: int32): int32

  Converts 32-bit integers from host to network byte order. On machines where the host byte order is the same as network byte order, this is a no-op; otherwise, it performs a 4-byte swap operation.

    proc htons*(x: int16): int16

  Converts 16-bit positive integers from host to network byte order. On machines where the host byte order is the same as network byte order, this is a no-op; otherwise, it performs a 2-byte swap operation.

    proc socket*(domain: TDomain = AF_INET; typ: TType = SOCK_STREAM;
                 protocol: TProtocol = IPPROTO_TCP; buffered = true): TSocket

  creates a new socket; returns InvalidSocket if an error occurs.

    proc newContext*(protVersion = ProtSSLv23; verifyMode = CVerifyPeer;
                     certFile = ""; keyFile = ""): PSSLContext

    proc wrapSocket*(ctx: PSSLContext; socket: TSocket)

  Creates a SSL context for `socket` and wraps the socket in it.

  Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are are available with the addition of `ProtSSLv23` which allows for compatibility with all of them.

  There are currently only two options for verify mode; one is `CVerifyNone` and with it certificates will not be verified the other is `CVerifyPeer` and certificates will be verified for it, `CVerifyPeer` is the safest choice.

  The last two parameters specify the certificate file path and the key file path, a server socket will most likely not work without these. Certificates can be generated using the following command: `openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem`.

  Warning: Because SSL is meant to be secure I feel the need to warn you that this "wrapper" has not been thorougly tested and is therefore most likely very prone to security vulnerabilities.

    proc listen*(socket: TSocket; backlog = SOMAXCONN)

  Marks `socket` as accepting connections. `Backlog` specifies the maximum length of the queue of pending connections.

    proc parseIp4*(s: string): int32

  parses an IP version 4 in dotted decimal form like "a.b.c.d". Raises EInvalidValue in case of an error.

    proc bindAddr*(socket: TSocket; port = TPort(0); address = "")

  binds an address/port number to a socket. Use address string in dotted decimal form like "a.b.c.d" or leave "" for any address.

    proc getSockName*(socket: TSocket): TPort

  returns the socket's associated port number.

    proc selectWrite*(writefds: var seq[TSocket]; timeout = 500): int

    proc acceptAddr*(server: TSocket; client: var TSocket; address: var string)

  Blocks until a connection is being made from a client. When a connection is made sets `client` to the client socket and `address` to the address of the connecting client. If `server` is non-blocking then this function returns immediately, and if there are no connections queued the returned socket will be `InvalidSocket`. This function will raise EOS if an error occurs.

  The resulting client will inherit any properties of the server socket. For example: whether the socket is buffered or not.

  Note: `client` must be initialised, this function makes no effort to initialise the `client` variable.

  Warning: When using SSL with non-blocking sockets, it is best to use the acceptAddrAsync procedure as this procedure will most likely block.

    proc setBlocking*(s: TSocket; blocking: bool)

    proc acceptAddrSSL*(server: TSocket; client: var TSocket; address: var string): TSSLAcceptResult

  This procedure should only be used for non-blocking SSL sockets. It will immediatelly return with one of the following values:

  `AcceptSuccess` will be returned when a client has been successfully accepted and the handshake has been successfully performed between `server` and the newly connected client.

  `AcceptNoHandshake` will be returned when a client has been accepted but no handshake could be performed. This can happen when the client connects but does not yet initiate a handshake. In this case `acceptAddrSSL` should be called again with the same parameters.

  `AcceptNoClient` will be returned when no client is currently attempting to connect.

    proc accept*(server: TSocket; client: var TSocket)

  Equivalent to `acceptAddr` but doesn't return the address, only the socket.

  Note: `client` must be initialised, this function makes no effort to initialise the `client` variable.

    proc acceptAddr*(server: TSocket): tuple[client: TSocket, address: string] {.
        deprecated.}

  Slightly different version of `acceptAddr`.

  Deprecated since version 0.9.0: Please use the function above.

    proc accept*(server: TSocket): TSocket {.deprecated.}

  Deprecated since version 0.9.0: Please use the function above.

    proc close*(socket: TSocket)

  closes a socket.

    proc getServByName*(name, proto: string): TServent

  well-known getservbyname proc.

    proc getServByPort*(port: TPort; proto: string): TServent

  well-known getservbyport proc.

    proc getHostByAddr*(ip: string): THostEnt

  This function will lookup the hostname of an IP Address.

    proc getHostByName*(name: string): THostEnt

  well-known gethostbyname proc.

    proc getSockOptInt*(socket: TSocket; level, optname: int): int

  getsockopt for integer options.

    proc setSockOptInt*(socket: TSocket; level, optname, optval: int)

  setsockopt for integer options.

    proc connect*(socket: TSocket; name: string; port = TPort(0);
                  af: TDomain = AF_INET)

  Connects socket to `name`:`port`. `Name` can be an IP address or a host name. If `name` is a host name, this function will try each IP of that host name. `htons` is already performed on `port` so you must not do it.

  If `socket` is an SSL socket a handshake will be automatically performed.

    proc connectAsync*(socket: TSocket; name: string; port = TPort(0);
                       af: TDomain = AF_INET)

  A variant of `connect` for non-blocking sockets.

  This procedure will immediatelly return, it will not block until a connection is made. It is up to the caller to make sure the connection has been established by checking (using `select`) whether the socket is writeable.

  Note: For SSL sockets, the `handshake` procedure must be called whenever the socket successfully connects to a server.

    proc handshake*(socket: TSocket): bool

  This proc needs to be called on a socket after it connects. This is only applicable when using `connectAsync`. This proc performs the SSL handshake.

  Returns `False` whenever the socket is not yet ready for a handshake, `True` whenever handshake completed successfully.

  A ESSL error is raised on any other errors.

    proc gotHandshake*(socket: TSocket): bool

  Determines whether a handshake has occurred between a client - `socket` and the server that `socket` is connected to.

  Throws ESSL if `socket` is not an SSL socket.

    proc hasDataBuffered*(s: TSocket): bool

  Determines whether a socket has data buffered.

    proc select*(readfds, writefds, exceptfds: var seq[TSocket]; timeout = 500): int

  Traditional select function. This function will return the number of sockets that are ready, if none are ready; 0 is returned. `Timeout` is in miliseconds and -1 can be specified for no timeout.

  You can determine whether a socket is ready by checking if it's still in one of the TSocket sequences.

    proc select*(readfds, writefds: var seq[TSocket]; timeout = 500): int

    proc selectWrite*(writefds: var seq[TSocket]; timeout = 500): int

    proc select*(readfds: var seq[TSocket]; timeout = 500): int

    proc recv*(socket: TSocket; data: pointer; size: int): int

  receives data from a socket

    proc recv*(socket: TSocket; data: pointer; size: int; timeout: int): int

  overload with a `timeout` parameter in miliseconds.

    proc recvLine*(socket: TSocket; line: var TaintedString): bool

  retrieves a line from `socket`. If a full line is received `\r\L` is not added to `line`, however if solely `\r\L` is received then `line` will be set to it.

  `True` is returned if data is available. `False` usually suggests an error, EOS exceptions are not raised in favour of this.

  If the socket is disconnected, `line` will be set to `""` and `True` will be returned.

  Warning: Using this function on a unbuffered ssl socket will result in an error.

    proc recvLine*(socket: TSocket; line: var TaintedString; timeout: int): bool

  variant with a `timeout` parameter, the timeout parameter specifies how many miliseconds to wait for data.

    proc recvLineAsync*(socket: TSocket; line: var TaintedString): TRecvLineResult

  similar to `recvLine` but for non-blocking sockets. The values of the returned enum should be pretty self explanatory: If a full line has been retrieved; `RecvFullLine` is returned. If some data has been retrieved; `RecvPartialLine` is returned. If the socket has been disconnected; `RecvDisconnected` is returned. If call to `recv` failed; `RecvFail` is returned.

    proc recv*(socket: TSocket): TaintedString

  receives all the data from the socket. Socket errors will result in an `EOS` error. If socket is not a connectionless socket and socket is not connected `""` will be returned.

    proc recvTimeout*(socket: TSocket; timeout: int): TaintedString

  overloaded variant to support a `timeout` parameter, the `timeout` parameter specifies the amount of miliseconds to wait for data on the socket.

    proc recvAsync*(socket: TSocket; s: var TaintedString): bool

  receives all the data from a non-blocking socket. If socket is non-blocking and there are no messages available, False will be returned. Other socket errors will result in an `EOS` error. If socket is not a connectionless socket and socket is not connected `s` will be set to `""`.

    proc recvFrom*(socket: TSocket; data: var string; length: int;
                   address: var string; port: var TPort; flags = 0'i32): int

  Receives data from `socket`. This function should normally be used with connection-less sockets (UDP sockets).

  Warning: This function does not yet have a buffered implementation, so when `socket` is buffered the non-buffered implementation will be used. Therefore if `socket` contains something in its buffer this function will make no effort to return it.

    proc recvFromAsync*(socket: TSocket; data: var String; length: int;
                        address: var string; port: var TPort; flags = 0'i32): bool

  Similar to `recvFrom` but raises an EOS error when an error occurs. Returns False if no messages could be received from `socket`.

    proc skip*(socket: TSocket)

  skips all the data that is pending for the socket

    proc send*(socket: TSocket; data: pointer; size: int): int

  sends data to a socket.

    proc send*(socket: TSocket; data: string)

  sends data to a socket.

    proc sendAsync*(socket: TSocket; data: string): bool

  sends data to a non-blocking socket. Returns whether `data` was sent.

    proc trySend*(socket: TSocket; data: string): bool

  safe alternative to `send`. Does not raise an EOS when an error occurs, and instead returns `false` on failure.

    proc sendTo*(socket: TSocket; address: string; port: TPort; data: pointer;
                 size: int; af: TDomain = AF_INET; flags = 0'i32): int

  low-level sendTo proc. This proc sends `data` to the specified `address`, which may be an IP address or a hostname, if a hostname is specified this function will try each IP of that hostname.

  Note: This proc is not available for SSL sockets.

    proc sendTo*(socket: TSocket; address: string; port: TPort; data: string): int

  Friendlier version of the low-level `sendTo`.

    proc setBlocking*(s: TSocket; blocking: bool)

  sets blocking mode on socket

    proc connect*(socket: TSocket; timeout: int; name: string; port = TPort(0);
                  af: TDomain = AF_INET)

  Overload for `connect` to support timeouts. The `timeout` parameter specifies the time in miliseconds of how long to wait for a connection to be made.

  Warning: If `socket` is non-blocking then this function will set blocking mode on `socket` to true.

    proc isSSL*(socket: TSocket): bool

  Determines whether `socket` is a SSL socket.

    proc getFD*(socket: TSocket): cint

  Returns the socket's file descriptor

Generated: 2012-09-23 21:47:53 UTC