summary refs log tree commit diff stats
path: root/lib/pure/nativesockets.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/nativesockets.nim')
-rw-r--r--lib/pure/nativesockets.nim794
1 files changed, 497 insertions, 297 deletions
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index d9256a921..656c98a20 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -8,33 +8,43 @@
 #
 
 ## This module implements a low-level cross-platform sockets interface. Look
-## at the ``net`` module for the higher-level version.
+## at the `net` module for the higher-level version.
 
 # TODO: Clean up the exports a bit and everything else in general.
 
-import os, options
+import std/[os, options]
+import std/private/since
+import std/strbasics
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
 
 when hostOS == "solaris":
   {.passl: "-lsocket -lnsl".}
 
-const useWinVersion = defined(Windows) or defined(nimdoc)
+const useWinVersion = defined(windows) or defined(nimdoc)
+const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or
+    defined(nuttx)
 
 when useWinVersion:
-  import winlean
+  import std/winlean
   export WSAEWOULDBLOCK, WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
          WSANOTINITIALISED, WSAENOTSOCK, WSAEINPROGRESS, WSAEINTR,
          WSAEDISCON, ERROR_NETNAME_DELETED
 else:
-  import posix
+  import std/posix
   export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
     EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET, EBADF
   export Sockaddr_storage, Sockaddr_un, Sockaddr_un_path_length
 
 export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
   Sockaddr_in6, Sockaddr_storage,
-  inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto,
+  recv, `==`, connect, send, accept, recvfrom, sendto,
   freeAddrInfo
 
+when not useNimNetLite:
+  export inet_ntoa
+
 export
   SO_ERROR,
   SOL_SOCKET,
@@ -57,7 +67,7 @@ type
                    ## some procedures, such as getaddrinfo)
     AF_UNIX = 1,   ## for local socket (using a file). Unsupported on Windows.
     AF_INET = 2,   ## for network protocol IPv4 or
-    AF_INET6 = when defined(macosx): 30 else: 23 ## for network protocol IPv6.
+    AF_INET6 = when defined(macosx): 30 elif defined(windows): 23 else: 10 ## for network protocol IPv6.
 
   SockType* = enum     ## second argument to `socket` proc
     SOCK_STREAM = 1,   ## reliable stream-oriented service or Stream Sockets
@@ -68,11 +78,11 @@ type
   Protocol* = enum    ## third argument to `socket` proc
     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_IP,       ## Internet protocol.
+    IPPROTO_IPV6,     ## Internet Protocol Version 6.
     IPPROTO_RAW,      ## Raw IP Packets Protocol. Unsupported on Windows.
-    IPPROTO_ICMP      ## Control message protocol. Unsupported on Windows.
-    IPPROTO_ICMPV6    ## Control message protocol for IPv6. Unsupported on Windows.
+    IPPROTO_ICMP      ## Internet Control message protocol.
+    IPPROTO_ICMPV6    ## Internet Control message protocol for IPv6.
 
   Servent* = object ## information about a service
     name*: string
@@ -87,6 +97,8 @@ type
     length*: int
     addrList*: seq[string]
 
+const IPPROTO_NONE* = IPPROTO_IP ## Use this if your socket type requires a protocol value of zero (e.g. Unix sockets).
+
 when useWinVersion:
   let
     osInvalidSocket* = winlean.INVALID_SOCKET
@@ -110,19 +122,19 @@ else:
     nativeAfUnix = posix.AF_UNIX
 
 proc `==`*(a, b: Port): bool {.borrow.}
-  ## ``==`` for ports.
+  ## `==` for ports.
 
 proc `$`*(p: Port): string {.borrow.}
-  ## returns the port number as a string
+  ## Returns the port number as a string
 
 proc toInt*(domain: Domain): cint
-  ## Converts the Domain enum to a platform-dependent ``cint``.
+  ## Converts the Domain enum to a platform-dependent `cint`.
 
 proc toInt*(typ: SockType): cint
-  ## Converts the SockType enum to a platform-dependent ``cint``.
+  ## Converts the SockType enum to a platform-dependent `cint`.
 
 proc toInt*(p: Protocol): cint
-  ## Converts the Protocol enum to a platform-dependent ``cint``.
+  ## Converts the Protocol enum to a platform-dependent `cint`.
 
 when not useWinVersion:
   proc toInt(domain: Domain): cint =
@@ -133,8 +145,8 @@ when not useWinVersion:
     of AF_INET6: result = posix.AF_INET6.cint
 
   proc toKnownDomain*(family: cint): Option[Domain] =
-    ## Converts the platform-dependent ``cint`` to the Domain or none(),
-    ## if the ``cint`` is not known.
+    ## Converts the platform-dependent `cint` to the Domain or none(),
+    ## if the `cint` is not known.
     result = if family == posix.AF_UNSPEC: some(Domain.AF_UNSPEC)
              elif family == posix.AF_UNIX: some(Domain.AF_UNIX)
              elif family == posix.AF_INET: some(Domain.AF_INET)
@@ -160,11 +172,11 @@ when not useWinVersion:
 
 else:
   proc toInt(domain: Domain): cint =
-    result = toU32(ord(domain)).cint
+    result = cast[cint](uint32(ord(domain)))
 
   proc toKnownDomain*(family: cint): Option[Domain] =
-    ## Converts the platform-dependent ``cint`` to the Domain or none(),
-    ## if the ``cint`` is not known.
+    ## Converts the platform-dependent `cint` to the Domain or none(),
+    ## if the `cint` is not known.
     result = if family == winlean.AF_UNSPEC: some(Domain.AF_UNSPEC)
              elif family == winlean.AF_INET: some(Domain.AF_INET)
              elif family == winlean.AF_INET6: some(Domain.AF_INET6)
@@ -174,7 +186,21 @@ else:
     result = cint(ord(typ))
 
   proc toInt(p: Protocol): cint =
-    result = cint(ord(p))
+    case p
+    of IPPROTO_IP:
+      result = 0.cint
+    of IPPROTO_ICMP:
+      result = 1.cint
+    of IPPROTO_TCP:
+      result = 6.cint
+    of IPPROTO_UDP:
+      result = 17.cint
+    of IPPROTO_IPV6:
+      result = 41.cint
+    of IPPROTO_ICMPV6:
+      result = 58.cint
+    else:
+      result = cint(ord(p))
 
 proc toSockType*(protocol: Protocol): SockType =
   result = case protocol
@@ -185,38 +211,20 @@ proc toSockType*(protocol: Protocol): SockType =
   of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP, IPPROTO_ICMPV6:
     SOCK_RAW
 
-proc createNativeSocket*(domain: Domain = AF_INET,
-                      sockType: SockType = SOCK_STREAM,
-                      protocol: Protocol = IPPROTO_TCP): SocketHandle =
-  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
-  socket(toInt(domain), toInt(sockType), toInt(protocol))
+proc getProtoByName*(name: string): int {.since: (1, 3, 5).} =
+  ## Returns a protocol code from the database that matches the protocol `name`.
+  when useWinVersion:
+    let protoent = winlean.getprotobyname(name.cstring)
+  else:
+    let protoent = posix.getprotobyname(name.cstring)
 
-proc createNativeSocket*(domain: cint, sockType: cint,
-                      protocol: cint): SocketHandle =
-  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
-  ##
-  ## Use this overload if one of the enums specified above does
-  ## not contain what you need.
-  socket(domain, sockType, protocol)
+  if protoent == nil:
+    raise newException(OSError, "protocol not found: " & name)
 
-proc newNativeSocket*(domain: Domain = AF_INET,
-                      sockType: SockType = SOCK_STREAM,
-                      protocol: Protocol = IPPROTO_TCP): SocketHandle
-                      {.deprecated: "deprecated since v0.18.0; use 'createNativeSocket' instead".} =
-  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
-  createNativeSocket(domain, sockType, protocol)
-
-proc newNativeSocket*(domain: cint, sockType: cint,
-                      protocol: cint): SocketHandle
-                      {.deprecated: "deprecated since v0.18.0; use 'createNativeSocket' instead".} =
-  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
-  ##
-  ## Use this overload if one of the enums specified above does
-  ## not contain what you need.
-  createNativeSocket(domain, sockType, protocol)
+  result = protoent.p_proto.int
 
 proc close*(socket: SocketHandle) =
-  ## closes a socket.
+  ## Closes a socket.
   when useWinVersion:
     discard winlean.closesocket(socket)
   else:
@@ -224,14 +232,53 @@ proc close*(socket: SocketHandle) =
   # TODO: These values should not be discarded. An OSError should be raised.
   # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
 
+when declared(setInheritable) or defined(nimdoc):
+  proc setInheritable*(s: SocketHandle, inheritable: bool): bool {.inline.} =
+    ## Set whether a socket is inheritable by child processes. Returns `true`
+    ## on success.
+    ##
+    ## This function is not implemented on all platform, test for availability
+    ## with `declared() <system.html#declared,untyped>`.
+    setInheritable(FileHandle s, inheritable)
+
+proc createNativeSocket*(domain: cint, sockType: cint, protocol: cint,
+                         inheritable: bool = defined(nimInheritHandles)): SocketHandle =
+  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
+  ##
+  ## `inheritable` decides if the resulting SocketHandle can be inherited
+  ## by child processes.
+  ##
+  ## Use this overload if one of the enums specified above does
+  ## not contain what you need.
+  let sockType =
+    when (defined(linux) or defined(bsd)) and not defined(nimdoc):
+      if inheritable: sockType and not SOCK_CLOEXEC else: sockType or SOCK_CLOEXEC
+    else:
+      sockType
+  result = socket(domain, sockType, protocol)
+  when declared(setInheritable) and not (defined(linux) or defined(bsd)):
+    if not setInheritable(result, inheritable):
+      close result
+      return osInvalidSocket
+
+proc createNativeSocket*(domain: Domain = AF_INET,
+                         sockType: SockType = SOCK_STREAM,
+                         protocol: Protocol = IPPROTO_TCP,
+                         inheritable: bool = defined(nimInheritHandles)): SocketHandle =
+  ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
+  ##
+  ## `inheritable` decides if the resulting SocketHandle can be inherited
+  ## by child processes.
+  createNativeSocket(toInt(domain), toInt(sockType), toInt(protocol), inheritable)
+
 proc bindAddr*(socket: SocketHandle, name: ptr SockAddr,
     namelen: SockLen): cint =
   result = bindSocket(socket, name, namelen)
 
 proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [
     ReadIOEffect].} =
-  ## Marks ``socket`` as accepting connections.
-  ## ``Backlog`` specifies the maximum length of the
+  ## Marks `socket` as accepting connections.
+  ## `Backlog` specifies the maximum length of the
   ## queue of pending connections.
   when useWinVersion:
     result = winlean.listen(socket, cint(backlog))
@@ -243,7 +290,7 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
                   protocol: Protocol = IPPROTO_TCP): ptr AddrInfo =
   ##
   ##
-  ## **Warning**: The resulting ``ptr AddrInfo`` must be freed using ``freeAddrInfo``!
+  ## .. warning:: The resulting `ptr AddrInfo` must be freed using `freeAddrInfo`!
   var hints: AddrInfo
   result = nil
   hints.ai_family = toInt(domain)
@@ -258,9 +305,9 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
     if domain == AF_INET6:
       hints.ai_flags = AI_V4MAPPED
   let socketPort = if sockType == SOCK_RAW: "" else: $port
-  var gaiResult = getaddrinfo(address, socketPort, addr(hints), result)
+  var gaiResult = getaddrinfo(address, socketPort.cstring, addr(hints), result)
   if gaiResult != 0'i32:
-    when useWinVersion:
+    when useWinVersion or defined(freertos) or defined(nuttx):
       raiseOSError(osLastError())
     else:
       raiseOSError(osLastError(), $gai_strerror(gaiResult))
@@ -294,258 +341,396 @@ template htons*(x: uint16): untyped =
   ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
   nativesockets.ntohs(x)
 
-proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
-  ## Searches the database from the beginning and finds the first entry for
-  ## which the service name specified by ``name`` matches the s_name member
-  ## and the protocol name specified by ``proto`` matches the s_proto member.
-  ##
-  ## On posix this will search through the ``/etc/services`` file.
-  when useWinVersion:
-    var s = winlean.getservbyname(name, proto)
-  else:
-    var s = posix.getservbyname(name, proto)
-  if s == nil: raiseOSError(osLastError(), "Service not found.")
-  result.name = $s.s_name
-  result.aliases = cstringArrayToSeq(s.s_aliases)
-  result.port = Port(s.s_port)
-  result.proto = $s.s_proto
-
-proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
-  ## Searches the database from the beginning and finds the first entry for
-  ## which the port specified by ``port`` matches the s_port member and the
-  ## protocol name specified by ``proto`` matches the s_proto member.
-  ##
-  ## On posix this will search through the ``/etc/services`` file.
-  when useWinVersion:
-    var s = winlean.getservbyport(ze(int16(port)).cint, proto)
-  else:
-    var s = posix.getservbyport(ze(int16(port)).cint, proto)
-  if s == nil: raiseOSError(osLastError(), "Service not found.")
-  result.name = $s.s_name
-  result.aliases = cstringArrayToSeq(s.s_aliases)
-  result.port = Port(s.s_port)
-  result.proto = $s.s_proto
-
-proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
-  ## This function will lookup the hostname of an IP Address.
-  var myaddr: InAddr
-  myaddr.s_addr = inet_addr(ip)
-
-  when useWinVersion:
-    var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
-                                  cint(AF_INET))
-    if s == nil: raiseOSError(osLastError())
-  else:
-    var s =
-      when defined(android4):
-        posix.gethostbyaddr(cast[cstring](addr(myaddr)), sizeof(myaddr).cint,
-                            cint(posix.AF_INET))
-      else:
-        posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).SockLen,
-                            cint(posix.AF_INET))
-    if s == nil:
-      raiseOSError(osLastError(), $hstrerror(h_errno))
-
-  result.name = $s.h_name
-  result.aliases = cstringArrayToSeq(s.h_aliases)
-  when useWinVersion:
-    result.addrtype = Domain(s.h_addrtype)
-  else:
-    if s.h_addrtype == posix.AF_INET:
-      result.addrtype = AF_INET
-    elif s.h_addrtype == posix.AF_INET6:
-      result.addrtype = AF_INET6
-    else:
-      raiseOSError(osLastError(), "unknown h_addrtype")
-  if result.addrtype == AF_INET:
-    result.addrList = @[]
-    var i = 0
-    while not isNil(s.h_addr_list[i]):
-      var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
-      result.addrList.add($inet_ntoa(inaddrPtr[]))
-      inc(i)
-  else:
-    result.addrList = cstringArrayToSeq(s.h_addr_list)
-  result.length = int(s.h_length)
-
-proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
-  ## This function will lookup the IP address of a hostname.
-  when useWinVersion:
-    var s = winlean.gethostbyname(name)
-  else:
-    var s = posix.gethostbyname(name)
-  if s == nil: raiseOSError(osLastError())
-  result.name = $s.h_name
-  result.aliases = cstringArrayToSeq(s.h_aliases)
-  when useWinVersion:
-    result.addrtype = Domain(s.h_addrtype)
-  else:
-    if s.h_addrtype == posix.AF_INET:
-      result.addrtype = AF_INET
-    elif s.h_addrtype == posix.AF_INET6:
-      result.addrtype = AF_INET6
-    else:
-      raiseOSError(osLastError(), "unknown h_addrtype")
-  if result.addrtype == AF_INET:
-    result.addrList = @[]
-    var i = 0
-    while not isNil(s.h_addr_list[i]):
-      var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
-      result.addrList.add($inet_ntoa(inaddrPtr[]))
-      inc(i)
-  else:
-    result.addrList = cstringArrayToSeq(s.h_addr_list)
-  result.length = int(s.h_length)
-
-proc getHostname*(): string {.tags: [ReadIOEffect].} =
-  ## Returns the local hostname (not the FQDN)
-  # https://tools.ietf.org/html/rfc1035#section-2.3.1
-  # https://tools.ietf.org/html/rfc2181#section-11
-  const size = 64
-  result = newString(size)
-  when useWinVersion:
-    let success = winlean.gethostname(result, size)
-  else:
-    # Posix
-    let success = posix.gethostname(result, size)
-  if success != 0.cint:
-    raiseOSError(osLastError())
-  let x = len(cstring(result))
-  result.setLen(x)
-
 proc getSockDomain*(socket: SocketHandle): Domain =
-  ## returns the socket's domain (AF_INET or AF_INET6).
+  ## Returns the socket's domain (AF_INET or AF_INET6).
   var name: Sockaddr_in6
   var namelen = sizeof(name).SockLen
   if getsockname(socket, cast[ptr SockAddr](addr(name)),
                  addr(namelen)) == -1'i32:
     raiseOSError(osLastError())
-  try:
-    result = toKnownDomain(name.sin6_family.cint).get()
-  except UnpackError:
+  let knownDomain = toKnownDomain(name.sin6_family.cint)
+  if knownDomain.isSome:
+    result = knownDomain.get()
+  else:
     raise newException(IOError, "Unknown socket family in getSockDomain")
 
-proc getAddrString*(sockAddr: ptr SockAddr): string =
-  ## return the string representation of address within sockAddr
-  if sockAddr.sa_family.cint == nativeAfInet:
-    result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
-  elif sockAddr.sa_family.cint == nativeAfInet6:
-    let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN
-                  else: 46 # it's actually 46 in both cases
-    result = newString(addrLen)
-    let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
-    when not useWinVersion:
-      if posix.inet_ntop(posix.AF_INET6, addr6, addr result[0],
-                         result.len.int32) == nil:
-        raiseOSError(osLastError())
-      if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
-        result = result.substr("::ffff:".len)
+when not useNimNetLite:
+  proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
+    ## Searches the database from the beginning and finds the first entry for
+    ## which the service name specified by `name` matches the s_name member
+    ## and the protocol name specified by `proto` matches the s_proto member.
+    ##
+    ## On posix this will search through the `/etc/services` file.
+    when useWinVersion:
+      var s = winlean.getservbyname(name, proto)
     else:
-      if winlean.inet_ntop(winlean.AF_INET6, addr6, addr result[0],
-                           result.len.int32) == nil:
-        raiseOSError(osLastError())
-    setLen(result, len(cstring(result)))
-  else:
-    when defined(posix) and not defined(nimdoc):
-      if sockAddr.sa_family.cint == nativeAfUnix:
-        return "unix"
-    raise newException(IOError, "Unknown socket family in getAddrString")
-
-when defined(posix) and not defined(nimdoc):
-  proc makeUnixAddr*(path: string): Sockaddr_un =
-    result.sun_family = AF_UNIX.TSa_Family
-    if path.len >= Sockaddr_un_path_length:
-      raise newException(ValueError, "socket path too long")
-    copyMem(addr result.sun_path, path.cstring, path.len + 1)
-
-proc getSockName*(socket: SocketHandle): Port =
-  ## returns the socket's associated port number.
-  var name: Sockaddr_in
-  when useWinVersion:
-    name.sin_family = uint16(ord(AF_INET))
-  else:
-    name.sin_family = TSa_Family(posix.AF_INET)
-  #name.sin_port = htons(cint16(port))
-  #name.sin_addr.s_addr = htonl(INADDR_ANY)
-  var namelen = sizeof(name).SockLen
-  if getsockname(socket, cast[ptr SockAddr](addr(name)),
-                 addr(namelen)) == -1'i32:
-    raiseOSError(osLastError())
-  result = Port(nativesockets.ntohs(name.sin_port))
+      var s = posix.getservbyname(name, proto)
+    if s == nil: raiseOSError(osLastError(), "Service not found.")
+    result.name = $s.s_name
+    result.aliases = cstringArrayToSeq(s.s_aliases)
+    result.port = Port(s.s_port)
+    result.proto = $s.s_proto
+
+  proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
+    ## Searches the database from the beginning and finds the first entry for
+    ## which the port specified by `port` matches the s_port member and the
+    ## protocol name specified by `proto` matches the s_proto member.
+    ##
+    ## On posix this will search through the `/etc/services` file.
+    when useWinVersion:
+      var s = winlean.getservbyport(uint16(port).cint, proto)
+    else:
+      var s = posix.getservbyport(uint16(port).cint, proto)
+    if s == nil: raiseOSError(osLastError(), "Service not found.")
+    result.name = $s.s_name
+    result.aliases = cstringArrayToSeq(s.s_aliases)
+    result.port = Port(s.s_port)
+    result.proto = $s.s_proto
+
+  proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
+    ## This function will lookup the hostname of an IP Address.
+    var
+      addrInfo = getAddrInfo(ip, Port(0), AF_UNSPEC)
+      myAddr: pointer
+      addrLen = 0
+      family = 0
+    
+    defer: freeAddrInfo(addrInfo)
+
+    if addrInfo.ai_addr.sa_family.cint == nativeAfInet:
+      family = nativeAfInet
+      myAddr = addr cast[ptr Sockaddr_in](addrInfo.ai_addr).sin_addr
+      addrLen = 4
+    elif addrInfo.ai_addr.sa_family.cint == nativeAfInet6:
+      family = nativeAfInet6
+      myAddr = addr cast[ptr Sockaddr_in6](addrInfo.ai_addr).sin6_addr
+      addrLen = 16
+    else:
+      raise newException(IOError, "Unknown socket family in `getHostByAddr()`")
 
-proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
-  ## returns the socket's local address and port number.
-  ##
-  ## Similar to POSIX's `getsockname`:idx:.
-  case domain
-  of AF_INET:
-    var name: Sockaddr_in
     when useWinVersion:
-      name.sin_family = uint16(ord(AF_INET))
+      var s = winlean.gethostbyaddr(cast[ptr InAddr](myAddr), addrLen.cuint,
+                                    cint(family))
+      if s == nil: raiseOSError(osLastError())
     else:
-      name.sin_family = TSa_Family(posix.AF_INET)
-    var namelen = sizeof(name).SockLen
-    if getsockname(socket, cast[ptr SockAddr](addr(name)),
-                   addr(namelen)) == -1'i32:
-      raiseOSError(osLastError())
-    result = ($inet_ntoa(name.sin_addr),
-              Port(nativesockets.ntohs(name.sin_port)))
-  of AF_INET6:
-    var name: Sockaddr_in6
+      var s =
+        when defined(android4):
+          posix.gethostbyaddr(cast[cstring](myAddr), addrLen.cint,
+                              cint(family))
+        else:
+          posix.gethostbyaddr(myAddr, addrLen.SockLen,
+                              cint(family))
+      if s == nil:
+        raiseOSError(osLastError(), $hstrerror(h_errno))
+
+    result.name = $s.h_name
+    result.aliases = cstringArrayToSeq(s.h_aliases)
     when useWinVersion:
-      name.sin6_family = uint16(ord(AF_INET6))
+      result.addrtype = Domain(s.h_addrtype)
     else:
-      name.sin6_family = TSa_Family(posix.AF_INET6)
-    var namelen = sizeof(name).SockLen
-    if getsockname(socket, cast[ptr SockAddr](addr(name)),
-                   addr(namelen)) == -1'i32:
-      raiseOSError(osLastError())
-    # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
-    result[0] = newString(64)
-    if inet_ntop(name.sin6_family.cint,
-        addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
+      if s.h_addrtype == posix.AF_INET:
+        result.addrtype = AF_INET
+      elif s.h_addrtype == posix.AF_INET6:
+        result.addrtype = AF_INET6
+      else:
+        raiseOSError(osLastError(), "unknown h_addrtype")
+    if result.addrtype == AF_INET:
+      result.addrList = @[]
+      var i = 0
+      while not isNil(s.h_addr_list[i]):
+        var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
+        result.addrList.add($inet_ntoa(inaddrPtr[]))
+        inc(i)
+    else:
+      let strAddrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
+                       else: 46
+      var i = 0
+      while not isNil(s.h_addr_list[i]):
+        var ipStr = newString(strAddrLen)
+        if inet_ntop(nativeAfInet6, cast[pointer](s.h_addr_list[i]),
+                     cstring(ipStr), len(ipStr).int32) == nil:
+          raiseOSError(osLastError())
+        when not useWinVersion:
+          if posix.IN6_IS_ADDR_V4MAPPED(cast[ptr In6Addr](s.h_addr_list[i])) != 0:
+            ipStr.setSlice("::ffff:".len..<strAddrLen)
+        setLen(ipStr, len(cstring(ipStr)))
+        result.addrList.add(ipStr)
+        inc(i)
+    result.length = int(s.h_length)
+
+  proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
+    ## This function will lookup the IP address of a hostname.
+    when useWinVersion:
+      var s = winlean.gethostbyname(name)
+    else:
+      var s = posix.gethostbyname(name)
+    if s == nil: raiseOSError(osLastError())
+    result.name = $s.h_name
+    result.aliases = cstringArrayToSeq(s.h_aliases)
+    when useWinVersion:
+      result.addrtype = Domain(s.h_addrtype)
+    else:
+      if s.h_addrtype == posix.AF_INET:
+        result.addrtype = AF_INET
+      elif s.h_addrtype == posix.AF_INET6:
+        result.addrtype = AF_INET6
+      else:
+        raiseOSError(osLastError(), "unknown h_addrtype")
+    if result.addrtype == AF_INET:
+      result.addrList = @[]
+      var i = 0
+      while not isNil(s.h_addr_list[i]):
+        var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
+        result.addrList.add($inet_ntoa(inaddrPtr[]))
+        inc(i)
+    else:
+      result.addrList = cstringArrayToSeq(s.h_addr_list)
+    result.length = int(s.h_length)
+
+  proc getHostname*(): string {.tags: [ReadIOEffect].} =
+    ## Returns the local hostname (not the FQDN)
+    # https://tools.ietf.org/html/rfc1035#section-2.3.1
+    # https://tools.ietf.org/html/rfc2181#section-11
+    const size = 256
+    result = newString(size)
+    when useWinVersion:
+      let success = winlean.gethostname(result.cstring, size)
+    else:
+      # Posix
+      let success = posix.gethostname(result.cstring, size)
+    if success != 0.cint:
       raiseOSError(osLastError())
-    setLen(result[0], result[0].cstring.len)
-    result[1] = Port(nativesockets.ntohs(name.sin6_port))
-  else:
-    raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
-
-proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
-  ## returns the socket's peer address and port number.
-  ##
-  ## Similar to POSIX's `getpeername`:idx:
-  case domain
-  of AF_INET:
+    let x = len(cstring(result))
+    result.setLen(x)
+
+  proc getAddrString*(sockAddr: ptr SockAddr): string =
+    ## Returns the string representation of address within sockAddr
+    if sockAddr.sa_family.cint == nativeAfInet:
+      result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
+    elif sockAddr.sa_family.cint == nativeAfInet6:
+      let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
+                    else: 46 # it's actually 46 in both cases
+      result = newString(addrLen)
+      let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
+      when not useWinVersion:
+        if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr result[0]),
+                          result.len.int32) == nil:
+          raiseOSError(osLastError())
+        if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
+          result.setSlice("::ffff:".len..<addrLen)
+      else:
+        if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr result[0]),
+                            result.len.int32) == nil:
+          raiseOSError(osLastError())
+      setLen(result, len(cstring(result)))
+    else:
+      when defined(posix) and not defined(nimdoc):
+        if sockAddr.sa_family.cint == nativeAfUnix:
+          return "unix"
+      raise newException(IOError, "Unknown socket family in getAddrString")
+
+  proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) =
+    ## Stores in `strAddress` the string representation of the address inside
+    ## `sockAddr`
+    ##
+    ## **Note**
+    ## * `strAddress` must be initialized to 46 in length.
+    const length = 46
+    assert(length == len(strAddress),
+          "`strAddress` was not initialized correctly. 46 != `len(strAddress)`")
+    if sockAddr.sa_family.cint == nativeAfInet:
+      let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr
+      when not useWinVersion:
+        if posix.inet_ntop(posix.AF_INET, addr4, cast[cstring](addr strAddress[0]),
+                          strAddress.len.int32) == nil:
+          raiseOSError(osLastError())
+      else:
+        if winlean.inet_ntop(winlean.AF_INET, addr4, cast[cstring](addr strAddress[0]),
+                            strAddress.len.int32) == nil:
+          raiseOSError(osLastError())
+    elif sockAddr.sa_family.cint == nativeAfInet6:
+      let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
+      when not useWinVersion:
+        if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
+                          strAddress.len.int32) == nil:
+          raiseOSError(osLastError())
+        if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
+          strAddress.setSlice("::ffff:".len..<length)
+      else:
+        if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
+                            strAddress.len.int32) == nil:
+          raiseOSError(osLastError())
+    else:
+      raise newException(IOError, "Unknown socket family in getAddrString")
+    setLen(strAddress, len(cstring(strAddress)))
+
+  when defined(posix) and not defined(nimdoc):
+    proc makeUnixAddr*(path: string): Sockaddr_un =
+      result.sun_family = AF_UNIX.TSa_Family
+      if path.len >= Sockaddr_un_path_length:
+        raise newException(ValueError, "socket path too long")
+      copyMem(addr result.sun_path, path.cstring, path.len + 1)
+
+  proc getSockName*(socket: SocketHandle): Port =
+    ## Returns the socket's associated port number.
     var name: Sockaddr_in
     when useWinVersion:
       name.sin_family = uint16(ord(AF_INET))
     else:
       name.sin_family = TSa_Family(posix.AF_INET)
+    #name.sin_port = htons(cint16(port))
+    #name.sin_addr.s_addr = htonl(INADDR_ANY)
     var namelen = sizeof(name).SockLen
-    if getpeername(socket, cast[ptr SockAddr](addr(name)),
-                   addr(namelen)) == -1'i32:
+    if getsockname(socket, cast[ptr SockAddr](addr(name)),
+                  addr(namelen)) == -1'i32:
       raiseOSError(osLastError())
-    result = ($inet_ntoa(name.sin_addr),
-              Port(nativesockets.ntohs(name.sin_port)))
-  of AF_INET6:
-    var name: Sockaddr_in6
-    when useWinVersion:
-      name.sin6_family = uint16(ord(AF_INET6))
+    result = Port(nativesockets.ntohs(name.sin_port))
+
+  proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
+    ## Returns the socket's local address and port number.
+    ##
+    ## Similar to POSIX's `getsockname`:idx:.
+    case domain
+    of AF_INET:
+      var name: Sockaddr_in
+      when useWinVersion:
+        name.sin_family = uint16(ord(AF_INET))
+      else:
+        name.sin_family = TSa_Family(posix.AF_INET)
+      var namelen = sizeof(name).SockLen
+      if getsockname(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+      result = ($inet_ntoa(name.sin_addr),
+                Port(nativesockets.ntohs(name.sin_port)))
+    of AF_INET6:
+      var name: Sockaddr_in6
+      when useWinVersion:
+        name.sin6_family = uint16(ord(AF_INET6))
+      else:
+        name.sin6_family = TSa_Family(posix.AF_INET6)
+      var namelen = sizeof(name).SockLen
+      if getsockname(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+      # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
+      result[0] = newString(64)
+      if inet_ntop(name.sin6_family.cint,
+          addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
+        raiseOSError(osLastError())
+      setLen(result[0], result[0].cstring.len)
+      result[1] = Port(nativesockets.ntohs(name.sin6_port))
     else:
-      name.sin6_family = TSa_Family(posix.AF_INET6)
-    var namelen = sizeof(name).SockLen
-    if getpeername(socket, cast[ptr SockAddr](addr(name)),
-                   addr(namelen)) == -1'i32:
-      raiseOSError(osLastError())
-    # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
-    result[0] = newString(64)
-    if inet_ntop(name.sin6_family.cint,
-        addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
+      raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
+
+  proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
+    ## Returns the socket's peer address and port number.
+    ##
+    ## Similar to POSIX's `getpeername`:idx:
+    case domain
+    of AF_INET:
+      var name: Sockaddr_in
+      when useWinVersion:
+        name.sin_family = uint16(ord(AF_INET))
+      else:
+        name.sin_family = TSa_Family(posix.AF_INET)
+      var namelen = sizeof(name).SockLen
+      if getpeername(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+      result = ($inet_ntoa(name.sin_addr),
+                Port(nativesockets.ntohs(name.sin_port)))
+    of AF_INET6:
+      var name: Sockaddr_in6
+      when useWinVersion:
+        name.sin6_family = uint16(ord(AF_INET6))
+      else:
+        name.sin6_family = TSa_Family(posix.AF_INET6)
+      var namelen = sizeof(name).SockLen
+      if getpeername(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+      # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
+      result[0] = newString(64)
+      if inet_ntop(name.sin6_family.cint,
+          addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
+        raiseOSError(osLastError())
+      setLen(result[0], result[0].cstring.len)
+      result[1] = Port(nativesockets.ntohs(name.sin6_port))
+    else:
+      raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
+
+when useNimNetLite: 
+
+  when useWinVersion:
+    const
+      INET_ADDRSTRLEN = 16
+      INET6_ADDRSTRLEN = 46 # it's actually 46 in both cases
+
+  proc sockAddrToStr(sa: ptr SockAddr): string {.noinit.} =
+    let af_family = sa.sa_family
+    var nl, v4Slice: cint
+    var si_addr: ptr InAddr
+
+    if af_family == AF_INET.TSa_Family:
+      nl = INET_ADDRSTRLEN
+      si_addr = cast[ptr Sockaddr_in](sa).sin_addr.addr()
+    elif af_family == AF_INET6.TSa_Family:
+      nl = INET6_ADDRSTRLEN
+      let si6_addr = cast[ptr Sockaddr_in6](sa).sin6_addr.addr()
+      si_addr = cast[ptr InAddr](si6_addr) # let's us reuse logic below 
+      when defined(posix) and not defined(nimdoc) and not defined(zephyr):
+        if posix.IN6_IS_ADDR_V4MAPPED(si6_addr) != 0:
+          v4Slice = "::ffff:".len()
+    else:
+      when defined(posix) and not defined(nimdoc):
+        if af_family.cint == nativeAfUnix:
+          return "unix"
+      return ""
+
+    result = newString(nl)
+    let namePtr = result.cstring()
+    if namePtr == inet_ntop(af_family.cint, si_addr, namePtr, nl):
+      result.setLen(len(namePtr))
+      if v4Slice > 0: result.setSlice(v4Slice.int ..< nl.int)
+    else:
+      return ""
+
+  proc sockAddrToStr(sa: var Sockaddr_in | var Sockaddr_in6): string =
+    result = sockAddrToStr(cast[ptr SockAddr](unsafeAddr(sa)))
+
+  proc getAddrString*(sockAddr: ptr SockAddr): string =
+    result = sockAddrToStr(sockAddr)
+    if result.len() == 0:
       raiseOSError(osLastError())
-    setLen(result[0], result[0].cstring.len)
-    result[1] = Port(nativesockets.ntohs(name.sin6_port))
-  else:
-    raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
+
+  proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) {.noinit.} =
+    strAddress = getAddrString(sockAddr)
+
+  proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
+    ## Returns the socket's local address and port number.
+    ##
+    ## Similar to POSIX's `getsockname`:idx:.
+    template sockGetNameOrRaiseError(socket: untyped, name: untyped) =
+      var namelen = sizeof(socket).SockLen
+      if getsockname(socket, cast[ptr SockAddr](addr(name)),
+                    addr(namelen)) == -1'i32:
+        raiseOSError(osLastError())
+
+    case domain
+    of AF_INET:
+      var name = Sockaddr_in(sin_family: TSa_Family(posix.AF_INET))
+      sockGetNameOrRaiseError(socket, name)
+      result = (sockAddrToStr(name),
+                Port(nativesockets.ntohs(name.sin_port)))
+    of AF_INET6:
+      var name = Sockaddr_in6(sin6_family: TSa_Family(posix.AF_INET6))
+      sockGetNameOrRaiseError(socket, name)
+      result = (sockAddrToStr(name),
+                Port(nativesockets.ntohs(name.sin6_port)))
+    else:
+      raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
+
 
 proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
   tags: [ReadIOEffect].} =
@@ -610,12 +795,12 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
   setLen(s, L)
 
 proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
-  ## When a socket in ``readfds`` is ready to be read from then a non-zero
+  ## When a socket in `readfds` is ready to be read from then a non-zero
   ## value will be returned specifying the count of the sockets which can be
-  ## read from. The sockets which can be read from will also be removed
-  ## from ``readfds``.
+  ## read from. The sockets which cannot be read from will also be removed
+  ## from `readfds`.
   ##
-  ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
+  ## `timeout` is specified in milliseconds and `-1` can be specified for
   ## an unlimited time.
   var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
 
@@ -632,12 +817,12 @@ proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
 
 proc selectWrite*(writefds: var seq[SocketHandle],
                   timeout = 500): int {.tags: [ReadIOEffect].} =
-  ## When a socket in ``writefds`` is ready to be written to then a non-zero
+  ## When a socket in `writefds` is ready to be written to then a non-zero
   ## value will be returned specifying the count of the sockets which can be
-  ## written to. The sockets which can be written to will also be removed
-  ## from ``writefds``.
+  ## written to. The sockets which cannot be written to will also be removed
+  ## from `writefds`.
   ##
-  ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
+  ## `timeout` is specified in milliseconds and `-1` can be specified for
   ## an unlimited time.
   var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
 
@@ -652,19 +837,34 @@ proc selectWrite*(writefds: var seq[SocketHandle],
 
   pruneSocketSet(writefds, (wr))
 
-proc accept*(fd: SocketHandle): (SocketHandle, string) =
+proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (SocketHandle, string) =
   ## Accepts a new client connection.
   ##
+  ## `inheritable` decides if the resulting SocketHandle can be inherited by
+  ## child processes.
+  ##
   ## Returns (osInvalidSocket, "") if an error occurred.
-  var sockAddress: Sockaddr_in
+  var sockAddress: SockAddr
   var addrLen = sizeof(sockAddress).SockLen
-  var sock = accept(fd, cast[ptr SockAddr](addr(sockAddress)),
-                    addr(addrLen))
+  var sock =
+    when (defined(linux) or defined(bsd)) and not defined(nimdoc):
+      accept4(fd, addr(sockAddress), addr(addrLen),
+              if inheritable: 0 else: SOCK_CLOEXEC)
+    else:
+      accept(fd, addr(sockAddress), addr(addrLen))
+  when declared(setInheritable) and not (defined(linux) or defined(bsd)):
+    if not setInheritable(sock, inheritable):
+      close sock
+      sock = osInvalidSocket
   if sock == osInvalidSocket:
     return (osInvalidSocket, "")
   else:
-    return (sock, $inet_ntoa(sockAddress.sin_addr))
+    when useNimNetLite:
+      var name = sockAddrToStr(addr sockAddress)
+      return (sock, name)
+    else:
+      return (sock, $inet_ntoa(cast[Sockaddr_in](sockAddress).sin_addr))
 
-when defined(Windows):
+when defined(windows):
   var wsa: WSAData
   if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())