123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871 |
- #
- #
- # Nim's Runtime Library
- # (c) Copyright 2015 Dominik Picheta
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## This module implements a low-level cross-platform sockets interface. Look
- ## at the `net` module for the higher-level version.
- # TODO: Clean up the exports a bit and everything else in general.
- import std/[os, options]
- import std/private/since
- import std/strbasics
- when defined(nimPreviewSlimSystem):
- import std/[assertions, syncio]
- when hostOS == "solaris":
- {.passl: "-lsocket -lnsl".}
- const useWinVersion = defined(windows) or defined(nimdoc)
- const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or
- defined(nuttx)
- when useWinVersion:
- import std/winlean
- export WSAEWOULDBLOCK, WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
- WSANOTINITIALISED, WSAENOTSOCK, WSAEINPROGRESS, WSAEINTR,
- WSAEDISCON, ERROR_NETNAME_DELETED
- else:
- import std/posix
- export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
- EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET, EBADF
- export Sockaddr_storage, Sockaddr_un, Sockaddr_un_path_length
- export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
- Sockaddr_in6, Sockaddr_storage,
- recv, `==`, connect, send, accept, recvfrom, sendto,
- freeAddrInfo
- when not useNimNetLite:
- export inet_ntoa
- export
- SO_ERROR,
- SOL_SOCKET,
- SOMAXCONN,
- SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE,
- SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR, SO_REUSEPORT,
- MSG_PEEK
- when defined(macosx) and not defined(nimdoc):
- export SO_NOSIGPIPE
- type
- Port* = distinct uint16 ## port type
- Domain* = enum ## \
- ## domain, which specifies the protocol family of the
- ## created socket. Other domains than those that are listed
- ## here are unsupported.
- AF_UNSPEC = 0, ## unspecified domain (can be detected automatically by
- ## some procedures, such as getaddrinfo)
- AF_UNIX = 1, ## for local socket (using a file). Unsupported on Windows.
- AF_INET = 2, ## for network protocol IPv4 or
- AF_INET6 = when defined(macosx): 30 elif defined(windows): 23 else: 10 ## for network protocol IPv6.
- SockType* = enum ## second argument to `socket` proc
- SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
- SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
- SOCK_RAW = 3, ## raw protocols atop the network layer.
- SOCK_SEQPACKET = 5 ## reliable sequenced packet service
- Protocol* = enum ## third argument to `socket` proc
- IPPROTO_TCP = 6, ## Transmission control protocol.
- IPPROTO_UDP = 17, ## User datagram protocol.
- IPPROTO_IP, ## Internet protocol.
- IPPROTO_IPV6, ## Internet Protocol Version 6.
- IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
- IPPROTO_ICMP ## Internet Control message protocol.
- IPPROTO_ICMPV6 ## Internet Control message protocol for IPv6.
- Servent* = object ## information about a service
- name*: string
- aliases*: seq[string]
- port*: Port
- proto*: string
- Hostent* = object ## information about a given host
- name*: string
- aliases*: seq[string]
- addrtype*: Domain
- length*: int
- addrList*: seq[string]
- const IPPROTO_NONE* = IPPROTO_IP ## Use this if your socket type requires a protocol value of zero (e.g. Unix sockets).
- when useWinVersion:
- let
- osInvalidSocket* = winlean.INVALID_SOCKET
- const
- IOCPARM_MASK* = 127
- IOC_IN* = int(-2147483648)
- FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
- (102 shl 8) or 126
- nativeAfInet = winlean.AF_INET
- nativeAfInet6 = winlean.AF_INET6
- proc ioctlsocket*(s: SocketHandle, cmd: clong,
- argptr: ptr clong): cint {.
- stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
- else:
- let
- osInvalidSocket* = posix.INVALID_SOCKET
- nativeAfInet = posix.AF_INET
- nativeAfInet6 = posix.AF_INET6
- nativeAfUnix = posix.AF_UNIX
- proc `==`*(a, b: Port): bool {.borrow.}
- ## `==` for ports.
- proc `$`*(p: Port): string {.borrow.}
- ## Returns the port number as a string
- proc toInt*(domain: Domain): cint
- ## Converts the Domain enum to a platform-dependent `cint`.
- proc toInt*(typ: SockType): cint
- ## Converts the SockType enum to a platform-dependent `cint`.
- proc toInt*(p: Protocol): cint
- ## Converts the Protocol enum to a platform-dependent `cint`.
- when not useWinVersion:
- proc toInt(domain: Domain): cint =
- case domain
- of AF_UNSPEC: result = posix.AF_UNSPEC.cint
- of AF_UNIX: result = posix.AF_UNIX.cint
- of AF_INET: result = posix.AF_INET.cint
- of AF_INET6: result = posix.AF_INET6.cint
- proc toKnownDomain*(family: cint): Option[Domain] =
- ## Converts the platform-dependent `cint` to the Domain or none(),
- ## if the `cint` is not known.
- result = if family == posix.AF_UNSPEC: some(Domain.AF_UNSPEC)
- elif family == posix.AF_UNIX: some(Domain.AF_UNIX)
- elif family == posix.AF_INET: some(Domain.AF_INET)
- elif family == posix.AF_INET6: some(Domain.AF_INET6)
- else: none(Domain)
- proc toInt(typ: SockType): cint =
- case typ
- of SOCK_STREAM: result = posix.SOCK_STREAM
- of SOCK_DGRAM: result = posix.SOCK_DGRAM
- of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
- of SOCK_RAW: result = posix.SOCK_RAW
- proc toInt(p: Protocol): cint =
- case p
- of IPPROTO_TCP: result = posix.IPPROTO_TCP
- of IPPROTO_UDP: result = posix.IPPROTO_UDP
- of IPPROTO_IP: result = posix.IPPROTO_IP
- of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
- of IPPROTO_RAW: result = posix.IPPROTO_RAW
- of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
- of IPPROTO_ICMPV6: result = posix.IPPROTO_ICMPV6
- else:
- proc toInt(domain: Domain): cint =
- result = cast[cint](uint32(ord(domain)))
- proc toKnownDomain*(family: cint): Option[Domain] =
- ## Converts the platform-dependent `cint` to the Domain or none(),
- ## if the `cint` is not known.
- result = if family == winlean.AF_UNSPEC: some(Domain.AF_UNSPEC)
- elif family == winlean.AF_INET: some(Domain.AF_INET)
- elif family == winlean.AF_INET6: some(Domain.AF_INET6)
- else: none(Domain)
- proc toInt(typ: SockType): cint =
- result = cint(ord(typ))
- proc toInt(p: Protocol): cint =
- case p
- of IPPROTO_IP:
- result = 0.cint
- of IPPROTO_ICMP:
- result = 1.cint
- of IPPROTO_TCP:
- result = 6.cint
- of IPPROTO_UDP:
- result = 17.cint
- of IPPROTO_IPV6:
- result = 41.cint
- of IPPROTO_ICMPV6:
- result = 58.cint
- else:
- result = cint(ord(p))
- proc toSockType*(protocol: Protocol): SockType =
- result = case protocol
- of IPPROTO_TCP:
- SOCK_STREAM
- of IPPROTO_UDP:
- SOCK_DGRAM
- of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP, IPPROTO_ICMPV6:
- SOCK_RAW
- proc getProtoByName*(name: string): int {.since: (1, 3, 5).} =
- ## Returns a protocol code from the database that matches the protocol `name`.
- when useWinVersion:
- let protoent = winlean.getprotobyname(name.cstring)
- else:
- let protoent = posix.getprotobyname(name.cstring)
- if protoent == nil:
- raise newException(OSError, "protocol not found: " & name)
- result = protoent.p_proto.int
- proc close*(socket: SocketHandle) =
- ## Closes a socket.
- when useWinVersion:
- discard winlean.closesocket(socket)
- else:
- discard posix.close(socket)
- # TODO: These values should not be discarded. An OSError should be raised.
- # https://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
- when declared(setInheritable) or defined(nimdoc):
- proc setInheritable*(s: SocketHandle, inheritable: bool): bool {.inline.} =
- ## Set whether a socket is inheritable by child processes. Returns `true`
- ## on success.
- ##
- ## This function is not implemented on all platform, test for availability
- ## with `declared() <system.html#declared,untyped>`.
- setInheritable(FileHandle s, inheritable)
- proc createNativeSocket*(domain: cint, sockType: cint, protocol: cint,
- inheritable: bool = defined(nimInheritHandles)): SocketHandle =
- ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
- ##
- ## `inheritable` decides if the resulting SocketHandle can be inherited
- ## by child processes.
- ##
- ## Use this overload if one of the enums specified above does
- ## not contain what you need.
- let sockType =
- when (defined(linux) or defined(bsd)) and not defined(nimdoc):
- if inheritable: sockType and not SOCK_CLOEXEC else: sockType or SOCK_CLOEXEC
- else:
- sockType
- result = socket(domain, sockType, protocol)
- when declared(setInheritable) and not (defined(linux) or defined(bsd)):
- if not setInheritable(result, inheritable):
- close result
- return osInvalidSocket
- proc createNativeSocket*(domain: Domain = AF_INET,
- sockType: SockType = SOCK_STREAM,
- protocol: Protocol = IPPROTO_TCP,
- inheritable: bool = defined(nimInheritHandles)): SocketHandle =
- ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
- ##
- ## `inheritable` decides if the resulting SocketHandle can be inherited
- ## by child processes.
- createNativeSocket(toInt(domain), toInt(sockType), toInt(protocol), inheritable)
- proc bindAddr*(socket: SocketHandle, name: ptr SockAddr,
- namelen: SockLen): cint =
- result = bindSocket(socket, name, namelen)
- proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [
- ReadIOEffect].} =
- ## Marks `socket` as accepting connections.
- ## `Backlog` specifies the maximum length of the
- ## queue of pending connections.
- when useWinVersion:
- result = winlean.listen(socket, cint(backlog))
- else:
- result = posix.listen(socket, cint(backlog))
- proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
- sockType: SockType = SOCK_STREAM,
- protocol: Protocol = IPPROTO_TCP): ptr AddrInfo =
- ##
- ##
- ## .. warning:: The resulting `ptr AddrInfo` must be freed using `freeAddrInfo`!
- var hints: AddrInfo
- result = nil
- hints.ai_family = toInt(domain)
- hints.ai_socktype = toInt(sockType)
- hints.ai_protocol = toInt(protocol)
- # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
- # FreeBSD, Haiku don't support AI_V4MAPPED but defines the macro.
- # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
- # https://dev.haiku-os.org/ticket/14323
- when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and
- not defined(android) and not defined(haiku):
- if domain == AF_INET6:
- hints.ai_flags = AI_V4MAPPED
- let socketPort = if sockType == SOCK_RAW: "" else: $port
- var gaiResult = getaddrinfo(address, socketPort.cstring, addr(hints), result)
- if gaiResult != 0'i32:
- when useWinVersion or defined(freertos) or defined(nuttx):
- raiseOSError(osLastError())
- else:
- raiseOSError(osLastError(), $gai_strerror(gaiResult))
- proc ntohl*(x: uint32): uint32 =
- ## Converts 32-bit unsigned integers from network to host 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.
- when cpuEndian == bigEndian: result = x
- else: result = (x shr 24'u32) or
- (x shr 8'u32 and 0xff00'u32) or
- (x shl 8'u32 and 0xff0000'u32) or
- (x shl 24'u32)
- proc ntohs*(x: uint16): uint16 =
- ## Converts 16-bit unsigned integers from network to host 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 2-byte swap operation.
- when cpuEndian == bigEndian: result = x
- else: result = (x shr 8'u16) or (x shl 8'u16)
- template htonl*(x: uint32): untyped =
- ## Converts 32-bit unsigned integers from host to 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.
- nativesockets.ntohl(x)
- template htons*(x: uint16): untyped =
- ## Converts 16-bit unsigned integers from host to 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 2-byte swap operation.
- nativesockets.ntohs(x)
- proc getSockDomain*(socket: SocketHandle): Domain =
- ## Returns the socket's domain (AF_INET or AF_INET6).
- var name: Sockaddr_in6
- var namelen = sizeof(name).SockLen
- if getsockname(socket, cast[ptr SockAddr](addr(name)),
- addr(namelen)) == -1'i32:
- raiseOSError(osLastError())
- let knownDomain = toKnownDomain(name.sin6_family.cint)
- if knownDomain.isSome:
- result = knownDomain.get()
- else:
- raise newException(IOError, "Unknown socket family in getSockDomain")
- when not useNimNetLite:
- proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
- ## Searches the database from the beginning and finds the first entry for
- ## which the service name specified by `name` matches the s_name member
- ## and the protocol name specified by `proto` matches the s_proto member.
- ##
- ## On posix this will search through the `/etc/services` file.
- when useWinVersion:
- var s = winlean.getservbyname(name, proto)
- else:
- var s = posix.getservbyname(name, proto)
- if s == nil: raiseOSError(osLastError(), "Service not found.")
- result.name = $s.s_name
- result.aliases = cstringArrayToSeq(s.s_aliases)
- result.port = Port(s.s_port)
- result.proto = $s.s_proto
- proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
- ## 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
- ## protocol name specified by `proto` matches the s_proto member.
- ##
- ## On posix this will search through the `/etc/services` file.
- when useWinVersion:
- var s = winlean.getservbyport(uint16(port).cint, proto)
- else:
- var s = posix.getservbyport(uint16(port).cint, proto)
- if s == nil: raiseOSError(osLastError(), "Service not found.")
- result.name = $s.s_name
- result.aliases = cstringArrayToSeq(s.s_aliases)
- result.port = Port(s.s_port)
- result.proto = $s.s_proto
- proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
- ## This function will lookup the hostname of an IP Address.
- var
- addrInfo = getAddrInfo(ip, Port(0), AF_UNSPEC)
- myAddr: pointer
- addrLen = 0
- family = 0
-
- defer: freeAddrInfo(addrInfo)
- if addrInfo.ai_addr.sa_family.cint == nativeAfInet:
- family = nativeAfInet
- myAddr = addr cast[ptr Sockaddr_in](addrInfo.ai_addr).sin_addr
- addrLen = 4
- elif addrInfo.ai_addr.sa_family.cint == nativeAfInet6:
- family = nativeAfInet6
- myAddr = addr cast[ptr Sockaddr_in6](addrInfo.ai_addr).sin6_addr
- addrLen = 16
- else:
- raise newException(IOError, "Unknown socket family in `getHostByAddr()`")
- when useWinVersion:
- var s = winlean.gethostbyaddr(cast[ptr InAddr](myAddr), addrLen.cuint,
- cint(family))
- if s == nil: raiseOSError(osLastError())
- else:
- var s =
- when defined(android4):
- posix.gethostbyaddr(cast[cstring](myAddr), addrLen.cint,
- cint(family))
- else:
- posix.gethostbyaddr(myAddr, addrLen.SockLen,
- cint(family))
- if s == nil:
- raiseOSError(osLastError(), $hstrerror(h_errno))
- result.name = $s.h_name
- result.aliases = cstringArrayToSeq(s.h_aliases)
- when useWinVersion:
- result.addrtype = Domain(s.h_addrtype)
- else:
- if s.h_addrtype == posix.AF_INET:
- result.addrtype = AF_INET
- elif s.h_addrtype == posix.AF_INET6:
- result.addrtype = AF_INET6
- else:
- raiseOSError(osLastError(), "unknown h_addrtype")
- if result.addrtype == AF_INET:
- result.addrList = @[]
- var i = 0
- while not isNil(s.h_addr_list[i]):
- var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
- result.addrList.add($inet_ntoa(inaddrPtr[]))
- inc(i)
- else:
- let strAddrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
- else: 46
- var i = 0
- while not isNil(s.h_addr_list[i]):
- var ipStr = newString(strAddrLen)
- if inet_ntop(nativeAfInet6, cast[pointer](s.h_addr_list[i]),
- cstring(ipStr), len(ipStr).int32) == nil:
- raiseOSError(osLastError())
- when not useWinVersion:
- if posix.IN6_IS_ADDR_V4MAPPED(cast[ptr In6Addr](s.h_addr_list[i])) != 0:
- ipStr.setSlice("::ffff:".len..<strAddrLen)
- setLen(ipStr, len(cstring(ipStr)))
- result.addrList.add(ipStr)
- inc(i)
- result.length = int(s.h_length)
- proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
- ## This function will lookup the IP address of a hostname.
- when useWinVersion:
- var s = winlean.gethostbyname(name)
- else:
- var s = posix.gethostbyname(name)
- if s == nil: raiseOSError(osLastError())
- result.name = $s.h_name
- result.aliases = cstringArrayToSeq(s.h_aliases)
- when useWinVersion:
- result.addrtype = Domain(s.h_addrtype)
- else:
- if s.h_addrtype == posix.AF_INET:
- result.addrtype = AF_INET
- elif s.h_addrtype == posix.AF_INET6:
- result.addrtype = AF_INET6
- else:
- raiseOSError(osLastError(), "unknown h_addrtype")
- if result.addrtype == AF_INET:
- result.addrList = @[]
- var i = 0
- while not isNil(s.h_addr_list[i]):
- var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
- result.addrList.add($inet_ntoa(inaddrPtr[]))
- inc(i)
- else:
- result.addrList = cstringArrayToSeq(s.h_addr_list)
- result.length = int(s.h_length)
- proc getHostname*(): string {.tags: [ReadIOEffect].} =
- ## Returns the local hostname (not the FQDN)
- # https://tools.ietf.org/html/rfc1035#section-2.3.1
- # https://tools.ietf.org/html/rfc2181#section-11
- const size = 256
- result = newString(size)
- when useWinVersion:
- let success = winlean.gethostname(result.cstring, size)
- else:
- # Posix
- let success = posix.gethostname(result.cstring, size)
- if success != 0.cint:
- raiseOSError(osLastError())
- let x = len(cstring(result))
- result.setLen(x)
- proc getAddrString*(sockAddr: ptr SockAddr): string =
- ## Returns the string representation of address within sockAddr
- if sockAddr.sa_family.cint == nativeAfInet:
- result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
- elif sockAddr.sa_family.cint == nativeAfInet6:
- let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
- else: 46 # it's actually 46 in both cases
- result = newString(addrLen)
- let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
- when not useWinVersion:
- if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr result[0]),
- result.len.int32) == nil:
- raiseOSError(osLastError())
- if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
- result.setSlice("::ffff:".len..<addrLen)
- else:
- if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr result[0]),
- result.len.int32) == nil:
- raiseOSError(osLastError())
- setLen(result, len(cstring(result)))
- else:
- when defined(posix) and not defined(nimdoc):
- if sockAddr.sa_family.cint == nativeAfUnix:
- 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.
- const length = 46
- assert(length == 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, cast[cstring](addr strAddress[0]),
- strAddress.len.int32) == nil:
- raiseOSError(osLastError())
- else:
- if winlean.inet_ntop(winlean.AF_INET, addr4, cast[cstring](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, cast[cstring](addr strAddress[0]),
- strAddress.len.int32) == nil:
- raiseOSError(osLastError())
- if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
- strAddress.setSlice("::ffff:".len..<length)
- else:
- if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](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
- if path.len >= Sockaddr_un_path_length:
- raise newException(ValueError, "socket path too long")
- copyMem(addr result.sun_path, path.cstring, path.len + 1)
- proc getSockName*(socket: SocketHandle): Port =
- ## Returns the socket's associated port number.
- var name: Sockaddr_in
- when useWinVersion:
- name.sin_family = uint16(ord(AF_INET))
- else:
- name.sin_family = TSa_Family(posix.AF_INET)
- #name.sin_port = htons(cint16(port))
- #name.sin_addr.s_addr = htonl(INADDR_ANY)
- var namelen = sizeof(name).SockLen
- if getsockname(socket, cast[ptr SockAddr](addr(name)),
- addr(namelen)) == -1'i32:
- raiseOSError(osLastError())
- result = Port(nativesockets.ntohs(name.sin_port))
- proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
- ## Returns the socket's local address and port number.
- ##
- ## Similar to POSIX's `getsockname`:idx:.
- case domain
- of AF_INET:
- var name: Sockaddr_in
- when useWinVersion:
- name.sin_family = uint16(ord(AF_INET))
- else:
- name.sin_family = TSa_Family(posix.AF_INET)
- var namelen = sizeof(name).SockLen
- if getsockname(socket, cast[ptr SockAddr](addr(name)),
- addr(namelen)) == -1'i32:
- raiseOSError(osLastError())
- result = ($inet_ntoa(name.sin_addr),
- Port(nativesockets.ntohs(name.sin_port)))
- of AF_INET6:
- var name: Sockaddr_in6
- when useWinVersion:
- name.sin6_family = uint16(ord(AF_INET6))
- else:
- name.sin6_family = TSa_Family(posix.AF_INET6)
- var namelen = sizeof(name).SockLen
- if getsockname(socket, cast[ptr SockAddr](addr(name)),
- addr(namelen)) == -1'i32:
- raiseOSError(osLastError())
- # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
- result[0] = newString(64)
- if inet_ntop(name.sin6_family.cint,
- addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
- raiseOSError(osLastError())
- setLen(result[0], result[0].cstring.len)
- result[1] = Port(nativesockets.ntohs(name.sin6_port))
- else:
- raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
- proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
- ## Returns the socket's peer address and port number.
- ##
- ## Similar to POSIX's `getpeername`:idx:
- case domain
- of AF_INET:
- var name: Sockaddr_in
- when useWinVersion:
- name.sin_family = uint16(ord(AF_INET))
- else:
- name.sin_family = TSa_Family(posix.AF_INET)
- var namelen = sizeof(name).SockLen
- if getpeername(socket, cast[ptr SockAddr](addr(name)),
- addr(namelen)) == -1'i32:
- raiseOSError(osLastError())
- result = ($inet_ntoa(name.sin_addr),
- Port(nativesockets.ntohs(name.sin_port)))
- of AF_INET6:
- var name: Sockaddr_in6
- when useWinVersion:
- name.sin6_family = uint16(ord(AF_INET6))
- else:
- name.sin6_family = TSa_Family(posix.AF_INET6)
- var namelen = sizeof(name).SockLen
- if getpeername(socket, cast[ptr SockAddr](addr(name)),
- addr(namelen)) == -1'i32:
- raiseOSError(osLastError())
- # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
- result[0] = newString(64)
- if inet_ntop(name.sin6_family.cint,
- addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
- raiseOSError(osLastError())
- setLen(result[0], result[0].cstring.len)
- result[1] = Port(nativesockets.ntohs(name.sin6_port))
- else:
- raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
- when useNimNetLite:
- when useWinVersion:
- const
- INET_ADDRSTRLEN = 16
- INET6_ADDRSTRLEN = 46 # it's actually 46 in both cases
- proc sockAddrToStr(sa: ptr SockAddr): string {.noinit.} =
- let af_family = sa.sa_family
- var nl, v4Slice: cint
- var si_addr: ptr InAddr
- if af_family == AF_INET.TSa_Family:
- nl = INET_ADDRSTRLEN
- si_addr = cast[ptr Sockaddr_in](sa).sin_addr.addr()
- elif af_family == AF_INET6.TSa_Family:
- nl = INET6_ADDRSTRLEN
- let si6_addr = cast[ptr Sockaddr_in6](sa).sin6_addr.addr()
- si_addr = cast[ptr InAddr](si6_addr) # let's us reuse logic below
- when defined(posix) and not defined(nimdoc) and not defined(zephyr):
- if posix.IN6_IS_ADDR_V4MAPPED(si6_addr) != 0:
- v4Slice = "::ffff:".len()
- else:
- when defined(posix) and not defined(nimdoc):
- if af_family.cint == nativeAfUnix:
- return "unix"
- return ""
- result = newString(nl)
- let namePtr = result.cstring()
- if namePtr == inet_ntop(af_family.cint, si_addr, namePtr, nl):
- result.setLen(len(namePtr))
- if v4Slice > 0: result.setSlice(v4Slice.int ..< nl.int)
- else:
- return ""
- proc sockAddrToStr(sa: var Sockaddr_in | var Sockaddr_in6): string =
- result = sockAddrToStr(cast[ptr SockAddr](unsafeAddr(sa)))
- proc getAddrString*(sockAddr: ptr SockAddr): string =
- result = sockAddrToStr(sockAddr)
- if result.len() == 0:
- raiseOSError(osLastError())
- proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) {.noinit.} =
- strAddress = getAddrString(sockAddr)
- proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
- ## Returns the socket's local address and port number.
- ##
- ## Similar to POSIX's `getsockname`:idx:.
- template sockGetNameOrRaiseError(socket: untyped, name: untyped) =
- var namelen = sizeof(socket).SockLen
- if getsockname(socket, cast[ptr SockAddr](addr(name)),
- addr(namelen)) == -1'i32:
- raiseOSError(osLastError())
- case domain
- of AF_INET:
- var name = Sockaddr_in(sin_family: TSa_Family(posix.AF_INET))
- sockGetNameOrRaiseError(socket, name)
- result = (sockAddrToStr(name),
- Port(nativesockets.ntohs(name.sin_port)))
- of AF_INET6:
- var name = Sockaddr_in6(sin6_family: TSa_Family(posix.AF_INET6))
- sockGetNameOrRaiseError(socket, name)
- result = (sockAddrToStr(name),
- Port(nativesockets.ntohs(name.sin6_port)))
- else:
- raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
- proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
- tags: [ReadIOEffect].} =
- ## getsockopt for integer options.
- var res: cint
- var size = sizeof(res).SockLen
- if getsockopt(socket, cint(level), cint(optname),
- addr(res), addr(size)) < 0'i32:
- raiseOSError(osLastError())
- result = int(res)
- proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
- tags: [WriteIOEffect].} =
- ## setsockopt for integer options.
- var value = cint(optval)
- if setsockopt(socket, cint(level), cint(optname), addr(value),
- sizeof(value).SockLen) < 0'i32:
- raiseOSError(osLastError())
- proc setBlocking*(s: SocketHandle, blocking: bool) =
- ## Sets blocking mode on socket.
- ##
- ## Raises OSError on error.
- when useWinVersion:
- var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
- if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
- raiseOSError(osLastError())
- else: # BSD sockets
- var x: int = fcntl(s, F_GETFL, 0)
- if x == -1:
- raiseOSError(osLastError())
- else:
- var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
- if fcntl(s, F_SETFL, mode) == -1:
- raiseOSError(osLastError())
- proc timeValFromMilliseconds(timeout = 500): Timeval =
- if timeout != -1:
- var seconds = timeout div 1000
- when useWinVersion:
- result.tv_sec = seconds.int32
- result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
- else:
- result.tv_sec = seconds.Time
- result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
- proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) =
- FD_ZERO(fd)
- for i in items(s):
- m = max(m, int(i))
- FD_SET(i, fd)
- proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
- var i = 0
- var L = s.len
- while i < L:
- if FD_ISSET(s[i], fd) == 0'i32:
- s[i] = s[L-1]
- dec(L)
- else:
- inc(i)
- setLen(s, L)
- proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
- ## When a socket in `readfds` is ready to be read from then a non-zero
- ## value will be returned specifying the count of the sockets which can be
- ## read from. The sockets which cannot be read from will also be removed
- ## from `readfds`.
- ##
- ## `timeout` is specified in milliseconds and `-1` can be specified for
- ## an unlimited time.
- var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
- var rd: TFdSet
- var m = 0
- createFdSet((rd), readfds, m)
- if timeout != -1:
- result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
- else:
- result = int(select(cint(m+1), addr(rd), nil, nil, nil))
- pruneSocketSet(readfds, (rd))
- proc selectWrite*(writefds: var seq[SocketHandle],
- timeout = 500): int {.tags: [ReadIOEffect].} =
- ## When a socket in `writefds` is ready to be written to then a non-zero
- ## value will be returned specifying the count of the sockets which can be
- ## written to. The sockets which cannot be written to will also be removed
- ## from `writefds`.
- ##
- ## `timeout` is specified in milliseconds and `-1` can be specified for
- ## an unlimited time.
- var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
- var wr: TFdSet
- var m = 0
- createFdSet((wr), writefds, m)
- if timeout != -1:
- result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
- else:
- result = int(select(cint(m+1), nil, addr(wr), nil, nil))
- pruneSocketSet(writefds, (wr))
- proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (SocketHandle, string) =
- ## Accepts a new client connection.
- ##
- ## `inheritable` decides if the resulting SocketHandle can be inherited by
- ## child processes.
- ##
- ## Returns (osInvalidSocket, "") if an error occurred.
- var sockAddress: SockAddr
- var addrLen = sizeof(sockAddress).SockLen
- var sock =
- when (defined(linux) or defined(bsd)) and not defined(nimdoc):
- accept4(fd, addr(sockAddress), addr(addrLen),
- if inheritable: 0 else: SOCK_CLOEXEC)
- else:
- accept(fd, addr(sockAddress), addr(addrLen))
- when declared(setInheritable) and not (defined(linux) or defined(bsd)):
- if not setInheritable(sock, inheritable):
- close sock
- sock = osInvalidSocket
- if sock == osInvalidSocket:
- return (osInvalidSocket, "")
- else:
- when useNimNetLite:
- var name = sockAddrToStr(addr sockAddress)
- return (sock, name)
- else:
- return (sock, $inet_ntoa(cast[Sockaddr_in](sockAddress).sin_addr))
- when defined(windows):
- var wsa: WSAData
- if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())
|