Fixes net/asyncdispatch on Windows. Ref #2976. Fixes #2996.

This commit is contained in:
Dominik Picheta 2015-06-25 22:36:53 +01:00
commit 6109e6a999
3 changed files with 59 additions and 47 deletions

View file

@ -483,7 +483,7 @@ when defined(windows) or defined(nimdoc):
RemoteSockaddr, RemoteSockaddrLength) RemoteSockaddr, RemoteSockaddrLength)
proc connect*(socket: AsyncFD, address: string, port: Port, proc connect*(socket: AsyncFD, address: string, port: Port,
af = AF_INET): Future[void] = af = rawsockets.AF_INET): Future[void] =
## Connects ``socket`` to server at ``address:port``. ## Connects ``socket`` to server at ``address:port``.
## ##
## Returns a ``Future`` which will complete when the connection succeeds ## Returns a ``Future`` which will complete when the connection succeeds
@ -861,7 +861,7 @@ when defined(windows) or defined(nimdoc):
result.SocketHandle.setBlocking(false) result.SocketHandle.setBlocking(false)
register(result) register(result)
proc newAsyncRawSocket*(domain: Domain = AF_INET, proc newAsyncRawSocket*(domain: Domain = rawsockets.AF_INET,
typ: SockType = SOCK_STREAM, typ: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): AsyncFD = protocol: Protocol = IPPROTO_TCP): AsyncFD =
## Creates a new socket and registers it with the dispatcher implicitly. ## Creates a new socket and registers it with the dispatcher implicitly.

View file

@ -27,9 +27,10 @@ else:
import posix import posix
export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL, export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET
export Sockaddr_storage
export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen, export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
Sockaddr_in6, Sockaddr_storage, Sockaddr_in6,
inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto
export export
@ -45,7 +46,7 @@ when defined(macosx):
type type
Port* = distinct uint16 ## port type Port* = distinct uint16 ## port type
Domain* = enum ## domain, which specifies the protocol family of the Domain* = enum ## domain, which specifies the protocol family of the
## created socket. Other domains than those that are listed ## created socket. Other domains than those that are listed
## here are unsupported. ## here are unsupported.
@ -60,7 +61,7 @@ type
SOCK_SEQPACKET = 5 ## reliable sequenced packet service SOCK_SEQPACKET = 5 ## reliable sequenced packet service
Protocol* = enum ## third argument to `socket` proc Protocol* = enum ## third argument to `socket` proc
IPPROTO_TCP = 6, ## Transmission control protocol. IPPROTO_TCP = 6, ## Transmission control protocol.
IPPROTO_UDP = 17, ## User datagram protocol. IPPROTO_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows. IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows. IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
@ -90,15 +91,19 @@ when useWinVersion:
const const
IOCPARM_MASK* = 127 IOCPARM_MASK* = 127
IOC_IN* = int(-2147483648) IOC_IN* = int(-2147483648)
FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
(102 shl 8) or 126 (102 shl 8) or 126
rawAfInet = winlean.AF_INET
rawAfInet6 = winlean.AF_INET6
proc ioctlsocket*(s: SocketHandle, cmd: clong, proc ioctlsocket*(s: SocketHandle, cmd: clong,
argptr: ptr clong): cint {. argptr: ptr clong): cint {.
stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".} stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
else: else:
let let
osInvalidSocket* = posix.INVALID_SOCKET osInvalidSocket* = posix.INVALID_SOCKET
rawAfInet = posix.AF_INET
rawAfInet6 = posix.AF_INET6
proc `==`*(a, b: Port): bool {.borrow.} proc `==`*(a, b: Port): bool {.borrow.}
## ``==`` for ports. ## ``==`` for ports.
@ -142,12 +147,12 @@ when not useWinVersion:
else: discard else: discard
else: else:
proc toInt(domain: Domain): cint = proc toInt(domain: Domain): cint =
result = toU16(ord(domain)) result = toU16(ord(domain))
proc toInt(typ: SockType): cint = proc toInt(typ: SockType): cint =
result = cint(ord(typ)) result = cint(ord(typ))
proc toInt(p: Protocol): cint = proc toInt(p: Protocol): cint =
result = cint(ord(p)) result = cint(ord(p))
@ -177,8 +182,8 @@ proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint
result = bindSocket(socket, name, namelen) result = bindSocket(socket, name, namelen)
proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ReadIOEffect].} = proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [ReadIOEffect].} =
## Marks ``socket`` as accepting connections. ## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the ## ``Backlog`` specifies the maximum length of the
## queue of pending connections. ## queue of pending connections.
when useWinVersion: when useWinVersion:
result = winlean.listen(socket, cint(backlog)) result = winlean.listen(socket, cint(backlog))
@ -195,7 +200,7 @@ proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockTy
hints.ai_family = toInt(af) hints.ai_family = toInt(af)
hints.ai_socktype = toInt(typ) hints.ai_socktype = toInt(typ)
hints.ai_protocol = toInt(prot) hints.ai_protocol = toInt(prot)
hints.ai_flags = posix.AI_V4MAPPED hints.ai_flags = AI_V4MAPPED
var gaiResult = getaddrinfo(address, $port, addr(hints), result) var gaiResult = getaddrinfo(address, $port, addr(hints), result)
if gaiResult != 0'i32: if gaiResult != 0'i32:
when useWinVersion: when useWinVersion:
@ -206,7 +211,7 @@ proc getAddrInfo*(address: string, port: Port, af: Domain = AF_INET, typ: SockTy
proc dealloc*(ai: ptr AddrInfo) = proc dealloc*(ai: ptr AddrInfo) =
freeaddrinfo(ai) freeaddrinfo(ai)
proc ntohl*(x: int32): int32 = proc ntohl*(x: int32): int32 =
## Converts 32-bit integers from network to host byte order. ## Converts 32-bit integers from network to host byte order.
## On machines where the host byte order is the same as network byte order, ## On machines where the host byte order is the same as network byte order,
## this is a no-op; otherwise, it performs a 4-byte swap operation. ## this is a no-op; otherwise, it performs a 4-byte swap operation.
@ -236,7 +241,7 @@ proc htons*(x: int16): int16 =
result = rawsockets.ntohs(x) result = rawsockets.ntohs(x)
proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
## Searches the database from the beginning and finds the first entry for ## Searches the database from the beginning and finds the first entry for
## which the service name specified by ``name`` matches the s_name member ## which the service name specified by ``name`` matches the s_name member
## and the protocol name specified by ``proto`` matches the s_proto member. ## and the protocol name specified by ``proto`` matches the s_proto member.
## ##
@ -250,10 +255,10 @@ proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
result.aliases = cstringArrayToSeq(s.s_aliases) result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = Port(s.s_port) result.port = Port(s.s_port)
result.proto = $s.s_proto result.proto = $s.s_proto
proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
## Searches the database from the beginning and finds the first entry for ## Searches the database from the beginning and finds the first entry for
## which the port specified by ``port`` matches the s_port member and the ## which the port specified by ``port`` matches the s_port member and the
## protocol name specified by ``proto`` matches the s_proto member. ## protocol name specified by ``proto`` matches the s_proto member.
## ##
## On posix this will search through the ``/etc/services`` file. ## On posix this will search through the ``/etc/services`` file.
@ -271,17 +276,17 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
## This function will lookup the hostname of an IP Address. ## This function will lookup the hostname of an IP Address.
var myaddr: InAddr var myaddr: InAddr
myaddr.s_addr = inet_addr(ip) myaddr.s_addr = inet_addr(ip)
when useWinVersion: when useWinVersion:
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
cint(rawsockets.AF_INET)) cint(rawsockets.AF_INET))
if s == nil: raiseOSError(osLastError()) if s == nil: raiseOSError(osLastError())
else: else:
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen, var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).Socklen,
cint(posix.AF_INET)) cint(posix.AF_INET))
if s == nil: if s == nil:
raise newException(OSError, $hstrerror(h_errno)) raise newException(OSError, $hstrerror(h_errno))
result.name = $s.h_name result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases) result.aliases = cstringArrayToSeq(s.h_aliases)
when useWinVersion: when useWinVersion:
@ -296,7 +301,7 @@ proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
result.addrList = cstringArrayToSeq(s.h_addr_list) result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length) result.length = int(s.h_length)
proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
## This function will lookup the IP address of a hostname. ## This function will lookup the IP address of a hostname.
when useWinVersion: when useWinVersion:
var s = winlean.gethostbyname(name) var s = winlean.gethostbyname(name)
@ -324,9 +329,9 @@ proc getSockDomain*(socket: SocketHandle): Domain =
if getsockname(socket, cast[ptr SockAddr](addr(name)), if getsockname(socket, cast[ptr SockAddr](addr(name)),
addr(namelen)) == -1'i32: addr(namelen)) == -1'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
if name.sa_family == posix.AF_INET: if name.sa_family == rawAfInet:
result = AF_INET result = AF_INET
elif name.sa_family == posix.AF_INET6: elif name.sa_family == rawAfInet6:
result = AF_INET6 result = AF_INET6
else: else:
raise newException(OSError, "unknown socket family in getSockFamily") raise newException(OSError, "unknown socket family in getSockFamily")
@ -334,19 +339,23 @@ proc getSockDomain*(socket: SocketHandle): Domain =
proc getAddrString*(sockAddr: ptr SockAddr): string = proc getAddrString*(sockAddr: ptr SockAddr): string =
## return the string representation of address within sockAddr ## return the string representation of address within sockAddr
if sockAddr.sa_family == posix.AF_INET: if sockAddr.sa_family == rawAfInet:
result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr) result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
elif sockAddr.sa_family == posix.AF_INET6: elif sockAddr.sa_family == rawAfInet6:
var v6addr = cast[ptr Sockaddr_in6](sockAddr).sin6_addr when not useWinVersion:
result = newString(posix.INET6_ADDRSTRLEN) # TODO: Windows
discard posix.inet_ntop(posix.AF_INET6, addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr, result.cstring, result.len.int32) var v6addr = cast[ptr Sockaddr_in6](sockAddr).sin6_addr
if posix.IN6_IS_ADDR_V4MAPPED(addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr) != 0: result = newString(posix.INET6_ADDRSTRLEN)
result = result.substr("::ffff:".len) 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.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
result = result.substr("::ffff:".len)
else: else:
raise newException(OSError, "unknown socket family in getAddrString") raise newException(OSError, "unknown socket family in getAddrString")
proc getSockName*(socket: SocketHandle): Port = proc getSockName*(socket: SocketHandle): Port =
## returns the socket's associated port number. ## returns the socket's associated port number.
var name: Sockaddr_in var name: Sockaddr_in
when useWinVersion: when useWinVersion:
@ -362,11 +371,11 @@ proc getSockName*(socket: SocketHandle): Port =
result = Port(rawsockets.ntohs(name.sin_port)) result = Port(rawsockets.ntohs(name.sin_port))
proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {. proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
tags: [ReadIOEffect].} = tags: [ReadIOEffect].} =
## getsockopt for integer options. ## getsockopt for integer options.
var res: cint var res: cint
var size = sizeof(res).SockLen var size = sizeof(res).SockLen
if getsockopt(socket, cint(level), cint(optname), if getsockopt(socket, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32: addr(res), addr(size)) < 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
result = int(res) result = int(res)
@ -375,7 +384,7 @@ proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
tags: [WriteIOEffect].} = tags: [WriteIOEffect].} =
## setsockopt for integer options. ## setsockopt for integer options.
var value = cint(optval) var value = cint(optval)
if setsockopt(socket, cint(level), cint(optname), addr(value), if setsockopt(socket, cint(level), cint(optname), addr(value),
sizeof(value).SockLen) < 0'i32: sizeof(value).SockLen) < 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
@ -402,13 +411,13 @@ proc timeValFromMilliseconds(timeout = 500): Timeval =
result.tv_sec = seconds.int32 result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) = proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) =
FD_ZERO(fd) FD_ZERO(fd)
for i in items(s): for i in items(s):
m = max(m, int(i)) m = max(m, int(i))
FD_SET(i, fd) FD_SET(i, fd)
proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) = proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
var i = 0 var i = 0
var L = s.len var L = s.len
while i < L: while i < L:
@ -422,22 +431,22 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
proc select*(readfds: var seq[SocketHandle], timeout = 500): int = proc select*(readfds: var seq[SocketHandle], timeout = 500): int =
## Traditional select function. This function will return the number of ## Traditional select function. This function will return the number of
## sockets that are ready to be read from, written to, or which have errors. ## sockets that are ready to be read from, written to, or which have errors.
## If there are none; 0 is returned. ## If there are none; 0 is returned.
## ``Timeout`` is in milliseconds and -1 can be specified for no timeout. ## ``Timeout`` is in milliseconds and -1 can be specified for no timeout.
## ##
## A socket is removed from the specific ``seq`` when it has data waiting to ## A socket is removed from the specific ``seq`` when it has data waiting to
## be read/written to or has errors (``exceptfds``). ## be read/written to or has errors (``exceptfds``).
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var rd: TFdSet var rd: TFdSet
var m = 0 var m = 0
createFdSet((rd), readfds, m) createFdSet((rd), readfds, m)
if timeout != -1: if timeout != -1:
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv))) result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
else: else:
result = int(select(cint(m+1), addr(rd), nil, nil, nil)) result = int(select(cint(m+1), addr(rd), nil, nil, nil))
pruneSocketSet(readfds, (rd)) pruneSocketSet(readfds, (rd))
proc selectWrite*(writefds: var seq[SocketHandle], proc selectWrite*(writefds: var seq[SocketHandle],
@ -450,16 +459,16 @@ proc selectWrite*(writefds: var seq[SocketHandle],
## ``timeout`` is specified in milliseconds and ``-1`` can be specified for ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
## an unlimited time. ## an unlimited time.
var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout) var tv {.noInit.}: Timeval = timeValFromMilliseconds(timeout)
var wr: TFdSet var wr: TFdSet
var m = 0 var m = 0
createFdSet((wr), writefds, m) createFdSet((wr), writefds, m)
if timeout != -1: if timeout != -1:
result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv))) result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
else: else:
result = int(select(cint(m+1), nil, addr(wr), nil, nil)) result = int(select(cint(m+1), nil, addr(wr), nil, nil))
pruneSocketSet(writefds, (wr)) pruneSocketSet(writefds, (wr))
when defined(Windows): when defined(Windows):

View file

@ -723,6 +723,9 @@ template WSAIORW*(x,y): expr = (IOC_INOUT or x or y)
const const
SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
SO_UPDATE_ACCEPT_CONTEXT* = 0x700B SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
AI_V4MAPPED* = 0x0008
AF_INET* = 2
AF_INET6* = 23
var var
WSAID_CONNECTEX*: GUID = GUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [ WSAID_CONNECTEX*: GUID = GUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [