diff options
Diffstat (limited to 'lib/pure')
-rw-r--r-- | lib/pure/asyncnet.nim | 69 | ||||
-rw-r--r-- | lib/pure/nativesockets.nim | 34 |
2 files changed, 85 insertions, 18 deletions
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 3fc1a0177..410310e29 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -853,40 +853,49 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string, else: raise newException(IOError, "Couldn't resolve address: " & address) -proc recvFrom*(socket: AsyncSocket, size: int, - flags = {SocketFlag.SafeDisconn}): - owned(Future[tuple[data: string, address: string, port: Port]]) +proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int, + address: FutureVar[string], port: FutureVar[Port], + flags = {SocketFlag.SafeDisconn}): owned(Future[int]) {.async, since: (1, 3).} = - ## Receives a datagram data from ``socket``, which must be at least of size - ## ``size``. Returned future will complete once one datagram has been received - ## and will return tuple with: data of packet received; and address and port - ## of datagram's sender. - ## + ## Receives a datagram data from ``socket`` into ``data``, which must be at + ## least of size ``size``. The address and port of datagram's sender will be + ## stored into ``address`` and ``port``, respectively. Returned future will + ## complete once one datagram has been received, and will return size of + ## packet received. + ## ## If an error occurs an OSError exception will be raised. ## ## This proc is normally used with connectionless sockets (UDP sockets). + ## + ## **Notes** + ## * ``data`` must be initialized to the length of ``size``. + ## * ``address`` must be initialized to 46 in length. template adaptRecvFromToDomain(domain: Domain) = var lAddr = sizeof(sAddr).SockLen - let fut = await recvFromInto(AsyncFD(getFd(socket)), cstring(data), size, - cast[ptr SockAddr](addr sAddr), addr lAddr, - flags) + result = await recvFromInto(AsyncFD(getFd(socket)), cstring(data.mget()), size, + cast[ptr SockAddr](addr sAddr), addr lAddr, + flags) - data.setLen(fut) + data.mget().setLen(result) + data.complete() + + getAddrString(cast[ptr SockAddr](addr sAddr), address.mget()) - result.data = data - result.address = getAddrString(cast[ptr SockAddr](addr sAddr)) + address.complete() when domain == AF_INET6: - result.port = ntohs(sAddr.sin6_port).Port + port.complete(ntohs(sAddr.sin6_port).Port) else: - result.port = ntohs(sAddr.sin_port).Port + port.complete(ntohs(sAddr.sin_port).Port) assert(socket.protocol != IPPROTO_TCP, "Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead") assert(not socket.closed, "Cannot `recvFrom` on a closed socket") - - var data = newString(size) + assert(size == len(data.mget()), + "`date` was not initialized correctly. `size` != `len(data.mget())`") + assert(46 == len(address.mget()), + "`address` was not initialized correctly. 46 != `len(address.mget())`") case socket.domain of AF_INET6: @@ -898,6 +907,30 @@ proc recvFrom*(socket: AsyncSocket, size: int, else: raise newException(ValueError, "Unknown socket address family") +proc recvFrom*(socket: AsyncSocket, size: int, + flags = {SocketFlag.SafeDisconn}): + owned(Future[tuple[data: string, address: string, port: Port]]) + {.async, since: (1, 3).} = + ## Receives a datagram data from ``socket``, which must be at least of size + ## ``size``. Returned future will complete once one datagram has been received + ## and will return tuple with: data of packet received; and address and port + ## of datagram's sender. + ## + ## If an error occurs an OSError exception will be raised. + ## + ## This proc is normally used with connectionless sockets (UDP sockets). + var + data = newFutureVar[string]() + address = newFutureVar[string]() + port = newFutureVar[Port]() + + data.mget().setLen(size) + address.mget().setLen(46) + + let read = await recvFrom(socket, data, size, address, port, flags) + + result = (data.mget(), address.mget(), port.mget()) + when not defined(testing) and isMainModule: type TestCases = enum diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 67e24eedc..35a7396b9 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -461,6 +461,40 @@ proc getAddrString*(sockAddr: ptr SockAddr): string = 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. + assert(46 == 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, addr strAddress[0], + strAddress.len.int32) == nil: + raiseOSError(osLastError()) + else: + if winlean.inet_ntop(winlean.AF_INET, addr4, 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, addr strAddress[0], + strAddress.len.int32) == nil: + raiseOSError(osLastError()) + if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0: + strAddress = strAddress.substr("::ffff:".len) + else: + if winlean.inet_ntop(winlean.AF_INET6, addr6, 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 |