summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/nativesockets.nim2
-rw-r--r--lib/pure/net.nim37
-rw-r--r--lib/windows/winlean.nim7
-rw-r--r--tests/stdlib/tnet.nim32
4 files changed, 76 insertions, 2 deletions
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 280c4e927..68061853b 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -31,7 +31,7 @@ else:
   export Sockaddr_storage, Sockaddr_un, Sockaddr_un_path_length
 
 export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
-  Sockaddr_in6,
+  Sockaddr_in6, Sockaddr_storage,
   inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto,
   freeAddrInfo
 
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 2d1ca8a59..ccf02a1fc 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -414,6 +414,43 @@ proc isIpAddress*(address_str: string): bool {.tags: [].} =
     return false
   return true
 
+proc toSockAddr*(address: IpAddress, port: Port, sa: var Sockaddr_storage, sl: var Socklen) =
+  ## Converts `IpAddress` and `Port` to `SockAddr` and `Socklen`
+  let port = htons(uint16(port))
+  case address.family
+  of IpAddressFamily.IPv4:
+    sl = sizeof(Sockaddr_in).Socklen
+    let s = cast[ptr Sockaddr_in](addr sa)
+    s.sin_family = type(s.sin_family)(AF_INET)
+    s.sin_port = port
+    copyMem(addr s.sin_addr, unsafeAddr address.address_v4[0], sizeof(s.sin_addr))
+  of IpAddressFamily.IPv6:
+    sl = sizeof(Sockaddr_in6).Socklen
+    let s = cast[ptr Sockaddr_in6](addr sa)
+    s.sin6_family = type(s.sin6_family)(AF_INET6)
+    s.sin6_port = port
+    copyMem(addr s.sin6_addr, unsafeAddr address.address_v6[0], sizeof(s.sin6_addr))
+
+proc fromSockAddrAux(sa: ptr Sockaddr_storage, sl: Socklen, address: var IpAddress, port: var Port) =
+  if sa.ss_family.int == AF_INET.int and sl == sizeof(Sockaddr_in).Socklen:
+    address = IpAddress(family: IpAddressFamily.IPv4)
+    let s = cast[ptr Sockaddr_in](sa)
+    copyMem(addr address.address_v4[0], addr s.sin_addr, sizeof(address.address_v4))
+    port = ntohs(s.sin_port).Port
+  elif sa.ss_family.int == AF_INET6.int and sl == sizeof(Sockaddr_in6).Socklen:
+    address = IpAddress(family: IpAddressFamily.IPv6)
+    let s = cast[ptr Sockaddr_in6](sa)
+    copyMem(addr address.address_v6[0], addr s.sin6_addr, sizeof(address.address_v6))
+    port = ntohs(s.sin6_port).Port
+  else:
+    raise newException(ValueError, "Neither IPv4 nor IPv6")
+
+proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6,
+    sl: Socklen, address: var IpAddress, port: var Port) {.inline.} =
+  ## Converts `SockAddr` and `Socklen` to `IpAddress` and `Port`. Raises
+  ## `ObjectConversionError` in case of invalid `sa` and `sl` arguments.
+  fromSockAddrAux(unsafeAddr sa, sl, address, port)
+
 when defineSsl:
   CRYPTO_malloc_init()
   doAssert SslLibraryInit() == 1
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index b2c1cc1f5..a0c784637 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -481,6 +481,13 @@ type
     sin6_flowinfo*: int32 # unsigned
     sin6_addr*: In6_addr
 
+  Sockaddr_storage* {.importc: "SOCKADDR_STORAGE",
+                      header: "winsock2.h".} = object
+    ss_family*: int16
+    ss_pad1: array[6, byte]
+    ss_align: int64
+    ss_pad2: array[112, byte]
+
   Servent* = object
     s_name*: cstring
     s_aliases*: cstringArray
diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim
index e8ada05e7..64d690fc9 100644
--- a/tests/stdlib/tnet.nim
+++ b/tests/stdlib/tnet.nim
@@ -1,4 +1,4 @@
-import net
+import net, nativesockets
 import unittest
 
 suite "isIpAddress tests":
@@ -45,3 +45,33 @@ suite "parseIpAddress tests":
   test "invalid ipv6":
     expect(ValueError):
       discard parseIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652")
+
+block: # "IpAddress/Sockaddr conversion"
+  proc test(ipaddrstr: string) =
+    var ipaddr_1 = parseIpAddress(ipaddrstr)
+    # echo ipaddrstr, " ", $ipaddr_1
+
+    doAssert($ipaddrstr == $ipaddr_1)
+
+    var sockaddr: Sockaddr_storage
+    var socklen: Socklen
+    var ipaddr_2: IpAddress
+    var port_2: Port
+
+    toSockAddr(ipaddr_1, Port(0), sockaddr, socklen)
+    fromSockAddr(sockaddr, socklen, ipaddr_2, port_2)
+
+    doAssert(ipaddrstr == $ipaddr_1)
+
+    doAssert(ipaddr_1 == ipaddr_2)
+    doAssert($ipaddr_1 == $ipaddr_2)
+
+
+  # ipv6 address of example.com
+  test("2606:2800:220:1:248:1893:25c8:1946")
+  # ipv6 address of localhost
+  test("::1")
+  # ipv4 address of example.com
+  test("93.184.216.34")
+  # ipv4 address of localhost
+  test("127.0.0.1")