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.nim79
1 files changed, 55 insertions, 24 deletions
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 0a7ffb3b3..7568408a6 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -12,7 +12,7 @@
 
 # TODO: Clean up the exports a bit and everything else in general.
 
-import os
+import os, options
 
 when hostOS == "solaris":
   {.passl: "-lsocket -lnsl".}
@@ -52,9 +52,11 @@ type
   Domain* = enum    ## domain, which specifies the protocol family of the
                     ## created socket. Other domains than those that are listed
                     ## here are unsupported.
-    AF_UNIX,        ## for local socket (using a file). Unsupported on Windows.
+    AF_UNSPEC = 0,  ## unspecified domain (can be detected automatically by
+                    ## 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 = 23   ## for network protocol IPv6.
+    AF_INET6 = when defined(macosx): 30 else: 23   ## for network protocol IPv6.
 
   SockType* = enum     ## second argument to `socket` proc
     SOCK_STREAM = 1,   ## reliable stream-oriented service or Stream Sockets
@@ -113,7 +115,7 @@ proc `==`*(a, b: Port): bool {.borrow.}
 proc `$`*(p: Port): string {.borrow.}
   ## returns the port number as a string
 
-proc toInt*(domain: Domain): cint
+proc toInt*(domain: Domain): cshort
   ## Converts the Domain enum to a platform-dependent ``cint``.
 
 proc toInt*(typ: SockType): cint
@@ -123,12 +125,21 @@ proc toInt*(p: Protocol): cint
   ## Converts the Protocol enum to a platform-dependent ``cint``.
 
 when not useWinVersion:
-  proc toInt(domain: Domain): cint =
+  proc toInt(domain: Domain): cshort =
     case domain
-    of AF_UNIX:        result = posix.AF_UNIX
-    of AF_INET:        result = posix.AF_INET
-    of AF_INET6:       result = posix.AF_INET6
-    else: discard
+    of AF_UNSPEC:      result = posix.AF_UNSPEC.cshort
+    of AF_UNIX:        result = posix.AF_UNIX.cshort
+    of AF_INET:        result = posix.AF_INET.cshort
+    of AF_INET6:       result = posix.AF_INET6.cshort
+
+  proc toKnownDomain*(family: cint): Option[Domain] =
+    ## 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)
+             elif family == posix.AF_INET6:  some(Domain.AF_INET6)
+             else: none(Domain)
 
   proc toInt(typ: SockType): cint =
     case typ
@@ -136,7 +147,6 @@ when not useWinVersion:
     of SOCK_DGRAM:     result = posix.SOCK_DGRAM
     of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
     of SOCK_RAW:       result = posix.SOCK_RAW
-    else: discard
 
   proc toInt(p: Protocol): cint =
     case p
@@ -146,18 +156,33 @@ when not useWinVersion:
     of IPPROTO_IPV6:   result = posix.IPPROTO_IPV6
     of IPPROTO_RAW:    result = posix.IPPROTO_RAW
     of IPPROTO_ICMP:   result = posix.IPPROTO_ICMP
-    else: discard
 
 else:
-  proc toInt(domain: Domain): cint =
+  proc toInt(domain: Domain): cshort =
     result = toU16(ord(domain))
 
+  proc toKnownDomain*(family: cint): Option[Domain] =
+    ## 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)
+             else: none(Domain)
+
   proc toInt(typ: SockType): cint =
     result = cint(ord(typ))
 
   proc toInt(p: Protocol): cint =
     result = cint(ord(p))
 
+proc toSockType*(protocol: Protocol): SockType =
+  result = case protocol
+  of IPPROTO_TCP:
+    SOCK_STREAM
+  of IPPROTO_UDP:
+    SOCK_DGRAM
+  of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP:
+    SOCK_RAW
 
 proc newNativeSocket*(domain: Domain = AF_INET,
                       sockType: SockType = SOCK_STREAM,
@@ -239,7 +264,7 @@ template ntohl*(x: int32): untyped {.deprecated.} =
   ## **Warning**: This template is deprecated since 0.14.0, IPv4
   ## addresses are now treated as unsigned integers. Please use the unsigned
   ## version of this template.
-  cast[int32](ntohl(cast[uint32](x)))
+  cast[int32](nativesockets.ntohl(cast[uint32](x)))
 
 proc ntohs*(x: uint16): uint16 =
   ## Converts 16-bit unsigned integers from network to host byte order. On
@@ -255,7 +280,7 @@ template ntohs*(x: int16): untyped {.deprecated.} =
   ## **Warning**: This template is deprecated since 0.14.0, where port
   ## numbers became unsigned integers. Please use the unsigned version of
   ## this template.
-  cast[int16](ntohs(cast[uint16](x)))
+  cast[int16](nativesockets.ntohs(cast[uint16](x)))
 
 template htonl*(x: int32): untyped {.deprecated.} =
   ## Converts 32-bit integers from host to network byte order. On machines
@@ -392,14 +417,14 @@ proc getHostname*(): string {.tags: [ReadIOEffect].} =
 
 proc getSockDomain*(socket: SocketHandle): Domain =
   ## returns the socket's domain (AF_INET or AF_INET6).
-  var name: SockAddr
+  var name: Sockaddr_in6
   var namelen = sizeof(name).SockLen
   if getsockname(socket, cast[ptr SockAddr](addr(name)),
                  addr(namelen)) == -1'i32:
     raiseOSError(osLastError())
-  if name.sa_family == nativeAfInet:
+  if name.sin6_family == nativeAfInet:
     result = AF_INET
-  elif name.sa_family == nativeAfInet6:
+  elif name.sin6_family == nativeAfInet6:
     result = AF_INET6
   else:
     raiseOSError(osLastError(), "unknown socket family in getSockFamily")
@@ -410,17 +435,23 @@ proc getAddrString*(sockAddr: ptr SockAddr): string =
   if sockAddr.sa_family == nativeAfInet:
     result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
   elif sockAddr.sa_family == 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:
-      # TODO: Windows
-      result = newString(posix.INET6_ADDRSTRLEN)
-      let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
-      discard posix.inet_ntop(posix.AF_INET6, addr6, result.cstring,
-          result.len.int32)
+      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)
+    else:
+      if winlean.inet_ntop(winlean.AF_INET6, addr6, addr result[0],
+                           result.len.int32) == nil:
+        raiseOSError(osLastError())
+    setLen(result, len(cstring(result)))
   else:
-    raiseOSError(osLastError(), "unknown socket family in getAddrString")
-
+    raise newException(IOError, "Unknown socket family in getAddrString")
 
 proc getSockName*(socket: SocketHandle): Port =
   ## returns the socket's associated port number.