From f340f76f45170535effa959543f4a458e1964815 Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Tue, 23 Jun 2015 22:59:48 +0200 Subject: [PATCH 01/11] Add getSockDomain to return AF_INET or AF_INET6 of a SocketHandle. --- lib/pure/rawsockets.nim | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index 6d656c983..c99f9a7a9 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -315,6 +315,21 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = result.addrList = cstringArrayToSeq(s.h_addr_list) result.length = int(s.h_length) +proc getSockDomain*(socket: SocketHandle): Domain = + ## returns the socket's domain (AF_INET or AF_INET6). + var name: SockAddr + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + if name.sa_family == posix.AF_INET: + result = AF_INET + elif name.sa_family == posix.AF_INET6: + result = AF_INET6 + else: + raise newException(OSError, "unknown socket family in getSockFamily") + + proc getSockName*(socket: SocketHandle): Port = ## returns the socket's associated port number. var name: Sockaddr_in From 93e2d9f9794c637352a167eba2364e2ee7154954 Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Tue, 23 Jun 2015 23:02:10 +0200 Subject: [PATCH 02/11] Make asyncnet.bindAddr IPv4+IPv6 compatible. --- lib/pure/asyncnet.nim | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 712bba33b..2921de681 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -423,24 +423,21 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {. ## Binds ``address``:``port`` to the socket. ## ## If ``address`` is "" then ADDR_ANY will be bound. + var sockDomain = getSockDomain(socket.fd) - if address == "": - var name: Sockaddr_in - when defined(Windows) or defined(nimdoc): - name.sin_family = toInt(AF_INET).int16 + var realaddr = address + if realaddr == "": + case sockDomain + of AF_INET6: realaddr = "::" + of AF_INET: realaddr = "0.0.0.0" else: - name.sin_family = toInt(AF_INET) - name.sin_port = htons(int16(port)) - name.sin_addr.s_addr = htonl(INADDR_ANY) - if bindAddr(socket.fd, cast[ptr SockAddr](addr(name)), - sizeof(name).Socklen) < 0'i32: - raiseOSError(osLastError()) - else: - var aiList = getAddrInfo(address, port, AF_INET) - if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: - dealloc(aiList) - raiseOSError(osLastError()) + raiseOSError("Unknown socket address family and no address specified to bindAddr") + + var aiList = getAddrInfo(realaddr, port, sockDomain) + if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: dealloc(aiList) + raiseOSError(osLastError()) + dealloc(aiList) proc close*(socket: AsyncSocket) = ## Closes the socket. From c42b08988c8ff9b64af94329d61dc97efe261265 Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Tue, 23 Jun 2015 23:03:35 +0200 Subject: [PATCH 03/11] Make newAsyncSocket AF_INET6 the default domain. --- lib/pure/asyncnet.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 2921de681..d7ac60147 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -100,7 +100,7 @@ proc newAsyncSocket*(fd: AsyncFD, buffered = true): AsyncSocket = if buffered: result.currPos = 0 -proc newAsyncSocket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, +proc newAsyncSocket*(domain: Domain = AF_INET6, typ: SockType = SOCK_STREAM, protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket = ## Creates a new asynchronous socket. ## From ec33471aeec1e54d3f6ec79762f2f63307d12496 Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Wed, 24 Jun 2015 01:23:09 +0200 Subject: [PATCH 04/11] Update posix.Sockaddr_in6 and posix.Sockaddr_storage, deprecate old T* types. --- lib/posix/posix.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index dba79cbf6..92bafe07d 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -448,7 +448,7 @@ type sa_family*: TSa_Family ## Address family. sa_data*: array [0..255, char] ## Socket address (variable-length data). - Tsockaddr_storage* {.importc: "struct sockaddr_storage", + Sockaddr_storage* {.importc: "struct sockaddr_storage", header: "", pure, final.} = object ## struct sockaddr_storage ss_family*: TSa_Family ## Address family. @@ -506,7 +506,7 @@ type header: "".} = object ## struct in6_addr s6_addr*: array [0..15, char] - Tsockaddr_in6* {.importc: "struct sockaddr_in6", pure, final, + Sockaddr_in6* {.importc: "struct sockaddr_in6", pure, final, header: "".} = object ## struct sockaddr_in6 sin6_family*: TSa_Family ## AF_INET6. sin6_port*: InPort ## Port number. @@ -581,6 +581,7 @@ type {.deprecated: [TSockaddr_in: Sockaddr_in, TAddrinfo: AddrInfo, TSockAddr: SockAddr, TSockLen: SockLen, TTimeval: Timeval, + Tsockaddr_storage: Sockaddr_storage, Tsockaddr_in6: Sockaddr_in6, Thostent: Hostent, TServent: Servent, TInAddr: InAddr, TIOVec: IOVec, TInPort: InPort, TInAddrT: InAddrT, TIn6Addr: In6Addr, TInAddrScalar: InAddrScalar, TProtoent: Protoent].} From 61336b2404c7b09b7ddb73d17426e6b291b15c9c Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Wed, 24 Jun 2015 01:36:09 +0200 Subject: [PATCH 05/11] Expose INET6_ADDRSTRLEN. --- lib/posix/posix.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 92bafe07d..9b9bf390a 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -1663,6 +1663,8 @@ var INET_ADDRSTRLEN* {.importc, header: "".}: cint ## 16. Length of the string form for IP. + INET6_ADDRSTRLEN* {.importc, header: "".}: cint + ## Length of the string form for IPv6. IPV6_JOIN_GROUP* {.importc, header: "".}: cint ## Join a multicast group. From 48ff0d84d0719045d0d5fd907ff11789ce82dd10 Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Wed, 24 Jun 2015 01:37:57 +0200 Subject: [PATCH 06/11] Add getAddrString which returns string representing the address within SockAddr. --- lib/pure/rawsockets.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index c99f9a7a9..d1922de65 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -330,6 +330,18 @@ proc getSockDomain*(socket: SocketHandle): Domain = raise newException(OSError, "unknown socket family in getSockFamily") +proc getAddrString*(sockAddr: ptr SockAddr): string = + ## return the string representation of address within sockAddr + if sockAddr.sa_family == posix.AF_INET: + result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr) + elif sockAddr.sa_family == posix.AF_INET6: + var v6addr = cast[ptr Sockaddr_in6](sockAddr).sin6_addr + result = newString(posix.INET6_ADDRSTRLEN) + discard posix.inet_ntop(posix.AF_INET6, addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr, result.cstring, result.len.int32) + else: + raise newException(OSError, "unknown socket family in getAddrString") + + proc getSockName*(socket: SocketHandle): Port = ## returns the socket's associated port number. var name: Sockaddr_in From 0d3e2277e96abecaa1ca83cff1fae4d22066dc3d Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Wed, 24 Jun 2015 01:38:46 +0200 Subject: [PATCH 07/11] Export Sockaddr_in6, Sockaddr_storage. --- lib/pure/rawsockets.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index d1922de65..12ec50ec6 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -29,6 +29,7 @@ else: EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen, + Sockaddr_in6, Sockaddr_storage, inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto export From 37f50695eed1059089ea069b263b5fbeaa51ed79 Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Wed, 24 Jun 2015 01:39:58 +0200 Subject: [PATCH 08/11] Make asyncdispatch.connectAddr IPv6-aware. --- lib/pure/asyncdispatch.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 550b82f49..3e9e5ac30 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1135,7 +1135,7 @@ else: client: AsyncFD]]("acceptAddr") proc cb(sock: AsyncFD): bool = result = true - var sockAddress: SockAddr_in + var sockAddress: Sockaddr_storage var addrLen = sizeof(sockAddress).Socklen var client = accept(sock.SocketHandle, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) @@ -1151,7 +1151,7 @@ else: retFuture.fail(newException(OSError, osErrorMsg(lastError))) else: register(client.AsyncFD) - retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.AsyncFD)) + retFuture.complete((getAddrString(cast[ptr SockAddr](addr sockAddress)), client.AsyncFD)) addRead(socket, cb) return retFuture From 21ee21cc2374bef7de57932f2eadcd82667022cf Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Wed, 24 Jun 2015 02:07:06 +0200 Subject: [PATCH 09/11] Return address in pure IPv4 notation by getAddrString if IPv6 address is IPv4-mapped. --- lib/pure/rawsockets.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index 12ec50ec6..c97718b0a 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -339,6 +339,8 @@ proc getAddrString*(sockAddr: ptr SockAddr): string = var v6addr = cast[ptr Sockaddr_in6](sockAddr).sin6_addr result = newString(posix.INET6_ADDRSTRLEN) discard posix.inet_ntop(posix.AF_INET6, addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr, result.cstring, result.len.int32) + if posix.IN6_IS_ADDR_V4MAPPED(addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr) != 0: + result = result.substr("::ffff:".len) else: raise newException(OSError, "unknown socket family in getAddrString") From 41be1c6c4dfe99989d746175dbb1904152302e8e Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Wed, 24 Jun 2015 02:08:17 +0200 Subject: [PATCH 10/11] Allow to connect to IPv4-mapped addresses for IPv6 sockets - thus full IPv4 compatibility. --- lib/pure/rawsockets.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index c97718b0a..9bfe9dab5 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -195,6 +195,7 @@ proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockTy hints.ai_family = toInt(af) hints.ai_socktype = toInt(typ) hints.ai_protocol = toInt(prot) + hints.ai_flags = posix.AI_V4MAPPED var gaiResult = getaddrinfo(address, $port, addr(hints), result) if gaiResult != 0'i32: when useWinVersion: From c4a25d23b6220cd5740b40d8c20e8dcde45310bc Mon Sep 17 00:00:00 2001 From: Andrew Yourtchenko Date: Wed, 24 Jun 2015 02:09:21 +0200 Subject: [PATCH 11/11] Match the AF of the socket in connect(). --- lib/pure/asyncdispatch.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 3e9e5ac30..5a059ec8e 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1009,7 +1009,7 @@ else: processTimers(p) proc connect*(socket: AsyncFD, address: string, port: Port, - af = AF_INET): Future[void] = + af_unused = AF_INET): Future[void] = var retFuture = newFuture[void]("connect") proc cb(fd: AsyncFD): bool = @@ -1017,7 +1017,8 @@ else: retFuture.complete() return true - var aiList = getAddrInfo(address, port, af) + var sockDomain = getSockDomain(socket.SocketHandle) + var aiList = getAddrInfo(address, port, sockDomain) var success = false var lastError: OSErrorCode var it = aiList