Various std net improvements (#19132)
* Variant of that works with raw IpAddresses. - Add doc tests for new net proc's. - Aadd recvFrom impl - Add recvFrom impl -- tweak handling data var - Update lib/pure/net.nim Co-authored-by: Dominik Picheta <dominikpicheta@googlemail.com> - cleaning up sendTo args - remove extra connect test - cleaning up sendTo args - fix inet_ntop test - fix test failing - byte len * fix test failing - byte len * debugging odd windows build failure * debugging odd windows build failure * more experiments to figure out the windows failure * try manual assigment on InAddr Co-authored-by: Jaremy Creechley <jaremy.creechley@panthalassa.com>
This commit is contained in:
parent
a3ef5df680
commit
4b5cecd902
3 changed files with 97 additions and 56 deletions
|
|
@ -155,9 +155,9 @@ when not defined(zephyr):
|
||||||
proc inet_addr*(a1: cstring): InAddrT {.importc, header: "<arpa/inet.h>".}
|
proc inet_addr*(a1: cstring): InAddrT {.importc, header: "<arpa/inet.h>".}
|
||||||
proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "<arpa/inet.h>".}
|
proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "<arpa/inet.h>".}
|
||||||
|
|
||||||
proc inet_ntop*(a1: cint, a2: pointer, a3: cstring, a4: int32): cstring {.
|
proc inet_ntop*(a1: cint, a2: pointer | ptr InAddr | ptr In6Addr, a3: cstring, a4: int32): cstring {.
|
||||||
importc:"(char *)$1", header: "<arpa/inet.h>".}
|
importc:"(char *)$1", header: "<arpa/inet.h>".}
|
||||||
proc inet_pton*(a1: cint, a2: cstring, a3: pointer): cint {.
|
proc inet_pton*(a1: cint, a2: cstring, a3: pointer | ptr InAddr | ptr In6Addr): cint {.
|
||||||
importc, header: "<arpa/inet.h>".}
|
importc, header: "<arpa/inet.h>".}
|
||||||
|
|
||||||
var
|
var
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,11 @@ runnableExamples("-r:off"):
|
||||||
let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
|
let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
|
||||||
socket.sendTo("192.168.0.1", Port(27960), "status\n")
|
socket.sendTo("192.168.0.1", Port(27960), "status\n")
|
||||||
|
|
||||||
|
runnableExamples("-r:off"):
|
||||||
|
let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
|
||||||
|
let ip = parseIpAddress("192.168.0.1")
|
||||||
|
doAssert socket.sendTo(ip, Port(27960), "status\c\l") == 8
|
||||||
|
|
||||||
## Creating a server
|
## Creating a server
|
||||||
## -----------------
|
## -----------------
|
||||||
##
|
##
|
||||||
|
|
@ -1607,11 +1612,12 @@ proc recvLine*(socket: Socket, timeout = -1,
|
||||||
result = ""
|
result = ""
|
||||||
readLine(socket, result, timeout, flags, maxLength)
|
readLine(socket, result, timeout, flags, maxLength)
|
||||||
|
|
||||||
proc recvFrom*(socket: Socket, data: var string, length: int,
|
proc recvFrom*[T: string | IpAddress](socket: Socket, data: var string, length: int,
|
||||||
address: var string, port: var Port, flags = 0'i32): int {.
|
address: var T, port: var Port, flags = 0'i32): int {.
|
||||||
tags: [ReadIOEffect].} =
|
tags: [ReadIOEffect].} =
|
||||||
## Receives data from `socket`. This function should normally be used with
|
## Receives data from `socket`. This function should normally be used with
|
||||||
## connection-less sockets (UDP sockets).
|
## connection-less sockets (UDP sockets). The source address of the data
|
||||||
|
## packet is stored in the `address` argument as either a string or an IpAddress.
|
||||||
##
|
##
|
||||||
## If an error occurs an OSError exception will be raised. Otherwise the return
|
## If an error occurs an OSError exception will be raised. Otherwise the return
|
||||||
## value will be the length of data received.
|
## value will be the length of data received.
|
||||||
|
|
@ -1620,31 +1626,37 @@ proc recvFrom*(socket: Socket, data: var string, length: int,
|
||||||
## so when `socket` is buffered the non-buffered implementation will be
|
## so when `socket` is buffered the non-buffered implementation will be
|
||||||
## used. Therefore if `socket` contains something in its buffer this
|
## used. Therefore if `socket` contains something in its buffer this
|
||||||
## function will make no effort to return it.
|
## function will make no effort to return it.
|
||||||
template adaptRecvFromToDomain(domain: Domain) =
|
template adaptRecvFromToDomain(sockAddress: untyped, domain: Domain) =
|
||||||
var addrLen = sizeof(sockAddress).SockLen
|
var addrLen = sizeof(sockAddress).SockLen
|
||||||
result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
|
result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
|
||||||
cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
|
cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
|
||||||
|
|
||||||
if result != -1:
|
if result != -1:
|
||||||
data.setLen(result)
|
data.setLen(result)
|
||||||
address = getAddrString(cast[ptr SockAddr](addr(sockAddress)))
|
|
||||||
when domain == AF_INET6:
|
when typeof(address) is string:
|
||||||
port = ntohs(sockAddress.sin6_port).Port
|
address = getAddrString(cast[ptr SockAddr](addr(sockAddress)))
|
||||||
|
when domain == AF_INET6:
|
||||||
|
port = ntohs(sockAddress.sin6_port).Port
|
||||||
|
else:
|
||||||
|
port = ntohs(sockAddress.sin_port).Port
|
||||||
else:
|
else:
|
||||||
port = ntohs(sockAddress.sin_port).Port
|
data.setLen(result)
|
||||||
|
sockAddress.fromSockAddr(addrLen, address, port)
|
||||||
else:
|
else:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
assert(socket.protocol != IPPROTO_TCP, "Cannot `recvFrom` on a TCP socket")
|
assert(socket.protocol != IPPROTO_TCP, "Cannot `recvFrom` on a TCP socket")
|
||||||
# TODO: Buffered sockets
|
# TODO: Buffered sockets
|
||||||
data.setLen(length)
|
data.setLen(length)
|
||||||
|
|
||||||
case socket.domain
|
case socket.domain
|
||||||
of AF_INET6:
|
of AF_INET6:
|
||||||
var sockAddress: Sockaddr_in6
|
var sockAddress: Sockaddr_in6
|
||||||
adaptRecvFromToDomain(AF_INET6)
|
adaptRecvFromToDomain(sockAddress, AF_INET6)
|
||||||
of AF_INET:
|
of AF_INET:
|
||||||
var sockAddress: Sockaddr_in
|
var sockAddress: Sockaddr_in
|
||||||
adaptRecvFromToDomain(AF_INET)
|
adaptRecvFromToDomain(sockAddress, AF_INET)
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "Unknown socket address family")
|
raise newException(ValueError, "Unknown socket address family")
|
||||||
|
|
||||||
|
|
@ -1707,7 +1719,8 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
|
||||||
tags: [WriteIOEffect].} =
|
tags: [WriteIOEffect].} =
|
||||||
## This proc sends `data` to the specified `address`,
|
## This proc sends `data` to the specified `address`,
|
||||||
## which may be an IP address or a hostname, if a hostname is specified
|
## which may be an IP address or a hostname, if a hostname is specified
|
||||||
## this function will try each IP of that hostname.
|
## this function will try each IP of that hostname. This function
|
||||||
|
## should normally be used with connection-less sockets (UDP sockets).
|
||||||
##
|
##
|
||||||
## If an error occurs an OSError exception will be raised.
|
## If an error occurs an OSError exception will be raised.
|
||||||
##
|
##
|
||||||
|
|
@ -1741,12 +1754,38 @@ proc sendTo*(socket: Socket, address: string, port: Port,
|
||||||
## This proc sends `data` to the specified `address`,
|
## This proc sends `data` to the specified `address`,
|
||||||
## which may be an IP address or a hostname, if a hostname is specified
|
## which may be an IP address or a hostname, if a hostname is specified
|
||||||
## this function will try each IP of that hostname.
|
## this function will try each IP of that hostname.
|
||||||
|
##
|
||||||
|
## Generally for use with connection-less (UDP) sockets.
|
||||||
##
|
##
|
||||||
## If an error occurs an OSError exception will be raised.
|
## If an error occurs an OSError exception will be raised.
|
||||||
##
|
##
|
||||||
## This is the high-level version of the above `sendTo` function.
|
## This is the high-level version of the above `sendTo` function.
|
||||||
socket.sendTo(address, port, cstring(data), data.len, socket.domain)
|
socket.sendTo(address, port, cstring(data), data.len, socket.domain)
|
||||||
|
|
||||||
|
proc sendTo*(socket: Socket, address: IpAddress, port: Port,
|
||||||
|
data: string, flags = 0'i32): int {.
|
||||||
|
discardable, tags: [WriteIOEffect].} =
|
||||||
|
## This proc sends `data` to the specified `IpAddress` and returns
|
||||||
|
## the number of bytes written.
|
||||||
|
##
|
||||||
|
## Generally for use with connection-less (UDP) sockets.
|
||||||
|
##
|
||||||
|
## If an error occurs an OSError exception will be raised.
|
||||||
|
##
|
||||||
|
## This is the high-level version of the above `sendTo` function.
|
||||||
|
assert(socket.protocol != IPPROTO_TCP, "Cannot `sendTo` on a TCP socket")
|
||||||
|
assert(not socket.isClosed, "Cannot `sendTo` on a closed socket")
|
||||||
|
|
||||||
|
var sa: Sockaddr_storage
|
||||||
|
var sl: Socklen
|
||||||
|
toSockAddr(address, port, sa, sl)
|
||||||
|
result = sendto(socket.fd, cstring(data), data.len().cint, flags.cint,
|
||||||
|
cast[ptr SockAddr](addr sa), sl)
|
||||||
|
|
||||||
|
if result == -1'i32:
|
||||||
|
let osError = osLastError()
|
||||||
|
raiseOSError(osError)
|
||||||
|
|
||||||
|
|
||||||
proc isSsl*(socket: Socket): bool =
|
proc isSsl*(socket: Socket): bool =
|
||||||
## Determines whether `socket` is a SSL socket.
|
## Determines whether `socket` is a SSL socket.
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,45 @@
|
||||||
discard """
|
discard """
|
||||||
action: run
|
action: run
|
||||||
output: '''
|
output: '''
|
||||||
|
|
||||||
[Suite] inet_ntop tests
|
[Suite] inet_ntop tests
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
import winlean
|
import winlean
|
||||||
elif defined(posix):
|
elif defined(posix):
|
||||||
import posix
|
import posix
|
||||||
else:
|
else:
|
||||||
{.error: "Unsupported OS".}
|
{.error: "Unsupported OS".}
|
||||||
|
|
||||||
import unittest, strutils
|
import unittest, strutils
|
||||||
|
|
||||||
suite "inet_ntop tests":
|
suite "inet_ntop tests":
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var wsa: WSAData
|
var wsa: WSAData
|
||||||
discard wsaStartup(0x101'i16, wsa.addr)
|
discard wsaStartup(0x101'i16, wsa.addr)
|
||||||
|
|
||||||
test "IP V4":
|
test "IP V4":
|
||||||
var ip4 = 0x10111213
|
# regular
|
||||||
var buff: array[0..255, char]
|
var ip4 = InAddr()
|
||||||
let r = inet_ntop(AF_INET, ip4.addr, buff[0].addr, buff.sizeof.int32)
|
ip4.s_addr = 0x10111213'u32
|
||||||
let res = if r == nil: "" else: $r
|
|
||||||
check: res == "19.18.17.16"
|
var buff: array[0..255, char]
|
||||||
|
let r = inet_ntop(AF_INET, cast[pointer](ip4.s_addr.addr), buff[0].addr, buff.len.int32)
|
||||||
|
let res = if r == nil: "" else: $r
|
||||||
test "IP V6":
|
check: res == "19.18.17.16"
|
||||||
when defined(windows):
|
|
||||||
let ipv6Support = (getVersion() and 0xff) > 0x5
|
test "IP V6":
|
||||||
else:
|
when defined(windows):
|
||||||
let ipv6Support = true
|
let ipv6Support = (getVersion() and 0xff) > 0x5
|
||||||
|
else:
|
||||||
var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001]
|
let ipv6Support = true
|
||||||
var buff: array[0..255, char]
|
|
||||||
let r = inet_ntop(AF_INET6, ip6[0].addr, buff[0].addr, buff.sizeof.int32)
|
var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001]
|
||||||
let res = if r == nil: "" else: $r
|
var buff: array[0..255, char]
|
||||||
check: not ipv6Support or res == "10:110:20:120:30:130:40:140"
|
let r = inet_ntop(AF_INET6, cast[pointer](ip6[0].addr), buff[0].addr, buff.len.int32)
|
||||||
|
let res = if r == nil: "" else: $r
|
||||||
|
check: not ipv6Support or res == "10:110:20:120:30:130:40:140"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue