nativesockets.nim 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Dominik Picheta
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements a low-level cross-platform sockets interface. Look
  10. ## at the ``net`` module for the higher-level version.
  11. # TODO: Clean up the exports a bit and everything else in general.
  12. import os, options
  13. import std/private/since
  14. when hostOS == "solaris":
  15. {.passl: "-lsocket -lnsl".}
  16. const useWinVersion = defined(Windows) or defined(nimdoc)
  17. when useWinVersion:
  18. import winlean
  19. export WSAEWOULDBLOCK, WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
  20. WSANOTINITIALISED, WSAENOTSOCK, WSAEINPROGRESS, WSAEINTR,
  21. WSAEDISCON, ERROR_NETNAME_DELETED
  22. else:
  23. import posix
  24. export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
  25. EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET, EBADF
  26. export Sockaddr_storage, Sockaddr_un, Sockaddr_un_path_length
  27. export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen,
  28. Sockaddr_in6, Sockaddr_storage,
  29. inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto,
  30. freeAddrInfo
  31. export
  32. SO_ERROR,
  33. SOL_SOCKET,
  34. SOMAXCONN,
  35. SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE,
  36. SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR, SO_REUSEPORT,
  37. MSG_PEEK
  38. when defined(macosx) and not defined(nimdoc):
  39. export SO_NOSIGPIPE
  40. type
  41. Port* = distinct uint16 ## port type
  42. Domain* = enum ## \
  43. ## domain, which specifies the protocol family of the
  44. ## created socket. Other domains than those that are listed
  45. ## here are unsupported.
  46. AF_UNSPEC = 0, ## unspecified domain (can be detected automatically by
  47. ## some procedures, such as getaddrinfo)
  48. AF_UNIX = 1, ## for local socket (using a file). Unsupported on Windows.
  49. AF_INET = 2, ## for network protocol IPv4 or
  50. AF_INET6 = when defined(macosx): 30 else: 23 ## for network protocol IPv6.
  51. SockType* = enum ## second argument to `socket` proc
  52. SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
  53. SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
  54. SOCK_RAW = 3, ## raw protocols atop the network layer.
  55. SOCK_SEQPACKET = 5 ## reliable sequenced packet service
  56. Protocol* = enum ## third argument to `socket` proc
  57. IPPROTO_TCP = 6, ## Transmission control protocol.
  58. IPPROTO_UDP = 17, ## User datagram protocol.
  59. IPPROTO_IP, ## Internet protocol.
  60. IPPROTO_IPV6, ## Internet Protocol Version 6.
  61. IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
  62. IPPROTO_ICMP ## Internet Control message protocol.
  63. IPPROTO_ICMPV6 ## Internet Control message protocol for IPv6.
  64. Servent* = object ## information about a service
  65. name*: string
  66. aliases*: seq[string]
  67. port*: Port
  68. proto*: string
  69. Hostent* = object ## information about a given host
  70. name*: string
  71. aliases*: seq[string]
  72. addrtype*: Domain
  73. length*: int
  74. addrList*: seq[string]
  75. when useWinVersion:
  76. let
  77. osInvalidSocket* = winlean.INVALID_SOCKET
  78. const
  79. IOCPARM_MASK* = 127
  80. IOC_IN* = int(-2147483648)
  81. FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
  82. (102 shl 8) or 126
  83. nativeAfInet = winlean.AF_INET
  84. nativeAfInet6 = winlean.AF_INET6
  85. proc ioctlsocket*(s: SocketHandle, cmd: clong,
  86. argptr: ptr clong): cint {.
  87. stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
  88. else:
  89. let
  90. osInvalidSocket* = posix.INVALID_SOCKET
  91. nativeAfInet = posix.AF_INET
  92. nativeAfInet6 = posix.AF_INET6
  93. nativeAfUnix = posix.AF_UNIX
  94. proc `==`*(a, b: Port): bool {.borrow.}
  95. ## ``==`` for ports.
  96. proc `$`*(p: Port): string {.borrow.}
  97. ## Returns the port number as a string
  98. proc toInt*(domain: Domain): cint
  99. ## Converts the Domain enum to a platform-dependent ``cint``.
  100. proc toInt*(typ: SockType): cint
  101. ## Converts the SockType enum to a platform-dependent ``cint``.
  102. proc toInt*(p: Protocol): cint
  103. ## Converts the Protocol enum to a platform-dependent ``cint``.
  104. when not useWinVersion:
  105. proc toInt(domain: Domain): cint =
  106. case domain
  107. of AF_UNSPEC: result = posix.AF_UNSPEC.cint
  108. of AF_UNIX: result = posix.AF_UNIX.cint
  109. of AF_INET: result = posix.AF_INET.cint
  110. of AF_INET6: result = posix.AF_INET6.cint
  111. proc toKnownDomain*(family: cint): Option[Domain] =
  112. ## Converts the platform-dependent ``cint`` to the Domain or none(),
  113. ## if the ``cint`` is not known.
  114. result = if family == posix.AF_UNSPEC: some(Domain.AF_UNSPEC)
  115. elif family == posix.AF_UNIX: some(Domain.AF_UNIX)
  116. elif family == posix.AF_INET: some(Domain.AF_INET)
  117. elif family == posix.AF_INET6: some(Domain.AF_INET6)
  118. else: none(Domain)
  119. proc toInt(typ: SockType): cint =
  120. case typ
  121. of SOCK_STREAM: result = posix.SOCK_STREAM
  122. of SOCK_DGRAM: result = posix.SOCK_DGRAM
  123. of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
  124. of SOCK_RAW: result = posix.SOCK_RAW
  125. proc toInt(p: Protocol): cint =
  126. case p
  127. of IPPROTO_TCP: result = posix.IPPROTO_TCP
  128. of IPPROTO_UDP: result = posix.IPPROTO_UDP
  129. of IPPROTO_IP: result = posix.IPPROTO_IP
  130. of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
  131. of IPPROTO_RAW: result = posix.IPPROTO_RAW
  132. of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
  133. of IPPROTO_ICMPV6: result = posix.IPPROTO_ICMPV6
  134. else:
  135. proc toInt(domain: Domain): cint =
  136. result = toU32(ord(domain)).cint
  137. proc toKnownDomain*(family: cint): Option[Domain] =
  138. ## Converts the platform-dependent ``cint`` to the Domain or none(),
  139. ## if the ``cint`` is not known.
  140. result = if family == winlean.AF_UNSPEC: some(Domain.AF_UNSPEC)
  141. elif family == winlean.AF_INET: some(Domain.AF_INET)
  142. elif family == winlean.AF_INET6: some(Domain.AF_INET6)
  143. else: none(Domain)
  144. proc toInt(typ: SockType): cint =
  145. result = cint(ord(typ))
  146. proc toInt(p: Protocol): cint =
  147. case p
  148. of IPPROTO_IP:
  149. result = 0.cint
  150. of IPPROTO_ICMP:
  151. result = 1.cint
  152. of IPPROTO_TCP:
  153. result = 6.cint
  154. of IPPROTO_UDP:
  155. result = 17.cint
  156. of IPPROTO_IPV6:
  157. result = 41.cint
  158. of IPPROTO_ICMPV6:
  159. result = 58.cint
  160. else:
  161. result = cint(ord(p))
  162. proc toSockType*(protocol: Protocol): SockType =
  163. result = case protocol
  164. of IPPROTO_TCP:
  165. SOCK_STREAM
  166. of IPPROTO_UDP:
  167. SOCK_DGRAM
  168. of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP, IPPROTO_ICMPV6:
  169. SOCK_RAW
  170. proc getProtoByName*(name: string): int {.since: (1, 3, 5).} =
  171. ## Returns a protocol code from the database that matches the protocol ``name``.
  172. when useWinVersion:
  173. let protoent = winlean.getprotobyname(name.cstring)
  174. else:
  175. let protoent = posix.getprotobyname(name.cstring)
  176. if protoent == nil:
  177. raise newException(OsError, "protocol not found")
  178. result = protoent.p_proto.int
  179. proc close*(socket: SocketHandle) =
  180. ## Closes a socket.
  181. when useWinVersion:
  182. discard winlean.closesocket(socket)
  183. else:
  184. discard posix.close(socket)
  185. # TODO: These values should not be discarded. An OSError should be raised.
  186. # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
  187. when declared(setInheritable) or defined(nimdoc):
  188. proc setInheritable*(s: SocketHandle, inheritable: bool): bool {.inline.} =
  189. ## Set whether a socket is inheritable by child processes. Returns `true`
  190. ## on success.
  191. ##
  192. ## This function is not implemented on all platform, test for availability
  193. ## with `declared() <system.html#declared,untyped>`.
  194. setInheritable(FileHandle s, inheritable)
  195. proc createNativeSocket*(domain: cint, sockType: cint, protocol: cint,
  196. inheritable: bool = defined(nimInheritHandles)): SocketHandle =
  197. ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
  198. ##
  199. ## `inheritable` decides if the resulting SocketHandle can be inherited
  200. ## by child processes.
  201. ##
  202. ## Use this overload if one of the enums specified above does
  203. ## not contain what you need.
  204. let sockType =
  205. when (defined(linux) or defined(bsd)) and not defined(nimdoc):
  206. if inheritable: sockType and not SOCK_CLOEXEC else: sockType or SOCK_CLOEXEC
  207. else:
  208. sockType
  209. result = socket(domain, sockType, protocol)
  210. when declared(setInheritable) and not (defined(linux) or defined(bsd)):
  211. if not setInheritable(result, inheritable):
  212. close result
  213. return osInvalidSocket
  214. proc createNativeSocket*(domain: Domain = AF_INET,
  215. sockType: SockType = SOCK_STREAM,
  216. protocol: Protocol = IPPROTO_TCP,
  217. inheritable: bool = defined(nimInheritHandles)): SocketHandle =
  218. ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
  219. ##
  220. ## `inheritable` decides if the resulting SocketHandle can be inherited
  221. ## by child processes.
  222. createNativeSocket(toInt(domain), toInt(sockType), toInt(protocol), inheritable)
  223. proc bindAddr*(socket: SocketHandle, name: ptr SockAddr,
  224. namelen: SockLen): cint =
  225. result = bindSocket(socket, name, namelen)
  226. proc listen*(socket: SocketHandle, backlog = SOMAXCONN): cint {.tags: [
  227. ReadIOEffect].} =
  228. ## Marks ``socket`` as accepting connections.
  229. ## ``Backlog`` specifies the maximum length of the
  230. ## queue of pending connections.
  231. when useWinVersion:
  232. result = winlean.listen(socket, cint(backlog))
  233. else:
  234. result = posix.listen(socket, cint(backlog))
  235. proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
  236. sockType: SockType = SOCK_STREAM,
  237. protocol: Protocol = IPPROTO_TCP): ptr AddrInfo =
  238. ##
  239. ##
  240. ## **Warning**: The resulting ``ptr AddrInfo`` must be freed using ``freeAddrInfo``!
  241. var hints: AddrInfo
  242. result = nil
  243. hints.ai_family = toInt(domain)
  244. hints.ai_socktype = toInt(sockType)
  245. hints.ai_protocol = toInt(protocol)
  246. # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
  247. # FreeBSD, Haiku don't support AI_V4MAPPED but defines the macro.
  248. # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
  249. # https://dev.haiku-os.org/ticket/14323
  250. when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and
  251. not defined(android) and not defined(haiku):
  252. if domain == AF_INET6:
  253. hints.ai_flags = AI_V4MAPPED
  254. let socketPort = if sockType == SOCK_RAW: "" else: $port
  255. var gaiResult = getaddrinfo(address, socketPort, addr(hints), result)
  256. if gaiResult != 0'i32:
  257. when useWinVersion or defined(freertos):
  258. raiseOSError(osLastError())
  259. else:
  260. raiseOSError(osLastError(), $gai_strerror(gaiResult))
  261. proc ntohl*(x: uint32): uint32 =
  262. ## Converts 32-bit unsigned integers from network to host byte order.
  263. ## On machines where the host byte order is the same as network byte order,
  264. ## this is a no-op; otherwise, it performs a 4-byte swap operation.
  265. when cpuEndian == bigEndian: result = x
  266. else: result = (x shr 24'u32) or
  267. (x shr 8'u32 and 0xff00'u32) or
  268. (x shl 8'u32 and 0xff0000'u32) or
  269. (x shl 24'u32)
  270. proc ntohs*(x: uint16): uint16 =
  271. ## Converts 16-bit unsigned integers from network to host byte order. On
  272. ## machines where the host byte order is the same as network byte order,
  273. ## this is a no-op; otherwise, it performs a 2-byte swap operation.
  274. when cpuEndian == bigEndian: result = x
  275. else: result = (x shr 8'u16) or (x shl 8'u16)
  276. template htonl*(x: uint32): untyped =
  277. ## Converts 32-bit unsigned integers from host to network byte order. On
  278. ## machines where the host byte order is the same as network byte order,
  279. ## this is a no-op; otherwise, it performs a 4-byte swap operation.
  280. nativesockets.ntohl(x)
  281. template htons*(x: uint16): untyped =
  282. ## Converts 16-bit unsigned integers from host to network byte order.
  283. ## On machines where the host byte order is the same as network byte
  284. ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
  285. nativesockets.ntohs(x)
  286. proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
  287. ## Searches the database from the beginning and finds the first entry for
  288. ## which the service name specified by ``name`` matches the s_name member
  289. ## and the protocol name specified by ``proto`` matches the s_proto member.
  290. ##
  291. ## On posix this will search through the ``/etc/services`` file.
  292. when useWinVersion:
  293. var s = winlean.getservbyname(name, proto)
  294. else:
  295. var s = posix.getservbyname(name, proto)
  296. if s == nil: raiseOSError(osLastError(), "Service not found.")
  297. result.name = $s.s_name
  298. result.aliases = cstringArrayToSeq(s.s_aliases)
  299. result.port = Port(s.s_port)
  300. result.proto = $s.s_proto
  301. proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
  302. ## Searches the database from the beginning and finds the first entry for
  303. ## which the port specified by ``port`` matches the s_port member and the
  304. ## protocol name specified by ``proto`` matches the s_proto member.
  305. ##
  306. ## On posix this will search through the ``/etc/services`` file.
  307. when useWinVersion:
  308. var s = winlean.getservbyport(ze(int16(port)).cint, proto)
  309. else:
  310. var s = posix.getservbyport(ze(int16(port)).cint, proto)
  311. if s == nil: raiseOSError(osLastError(), "Service not found.")
  312. result.name = $s.s_name
  313. result.aliases = cstringArrayToSeq(s.s_aliases)
  314. result.port = Port(s.s_port)
  315. result.proto = $s.s_proto
  316. proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
  317. ## This function will lookup the hostname of an IP Address.
  318. var myaddr: InAddr
  319. myaddr.s_addr = inet_addr(ip)
  320. when useWinVersion:
  321. var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
  322. cint(AF_INET))
  323. if s == nil: raiseOSError(osLastError())
  324. else:
  325. var s =
  326. when defined(android4):
  327. posix.gethostbyaddr(cast[cstring](addr(myaddr)), sizeof(myaddr).cint,
  328. cint(posix.AF_INET))
  329. else:
  330. posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).SockLen,
  331. cint(posix.AF_INET))
  332. if s == nil:
  333. raiseOSError(osLastError(), $hstrerror(h_errno))
  334. result.name = $s.h_name
  335. result.aliases = cstringArrayToSeq(s.h_aliases)
  336. when useWinVersion:
  337. result.addrtype = Domain(s.h_addrtype)
  338. else:
  339. if s.h_addrtype == posix.AF_INET:
  340. result.addrtype = AF_INET
  341. elif s.h_addrtype == posix.AF_INET6:
  342. result.addrtype = AF_INET6
  343. else:
  344. raiseOSError(osLastError(), "unknown h_addrtype")
  345. if result.addrtype == AF_INET:
  346. result.addrList = @[]
  347. var i = 0
  348. while not isNil(s.h_addr_list[i]):
  349. var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
  350. result.addrList.add($inet_ntoa(inaddrPtr[]))
  351. inc(i)
  352. else:
  353. result.addrList = cstringArrayToSeq(s.h_addr_list)
  354. result.length = int(s.h_length)
  355. proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
  356. ## This function will lookup the IP address of a hostname.
  357. when useWinVersion:
  358. var s = winlean.gethostbyname(name)
  359. else:
  360. var s = posix.gethostbyname(name)
  361. if s == nil: raiseOSError(osLastError())
  362. result.name = $s.h_name
  363. result.aliases = cstringArrayToSeq(s.h_aliases)
  364. when useWinVersion:
  365. result.addrtype = Domain(s.h_addrtype)
  366. else:
  367. if s.h_addrtype == posix.AF_INET:
  368. result.addrtype = AF_INET
  369. elif s.h_addrtype == posix.AF_INET6:
  370. result.addrtype = AF_INET6
  371. else:
  372. raiseOSError(osLastError(), "unknown h_addrtype")
  373. if result.addrtype == AF_INET:
  374. result.addrList = @[]
  375. var i = 0
  376. while not isNil(s.h_addr_list[i]):
  377. var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
  378. result.addrList.add($inet_ntoa(inaddrPtr[]))
  379. inc(i)
  380. else:
  381. result.addrList = cstringArrayToSeq(s.h_addr_list)
  382. result.length = int(s.h_length)
  383. proc getHostname*(): string {.tags: [ReadIOEffect].} =
  384. ## Returns the local hostname (not the FQDN)
  385. # https://tools.ietf.org/html/rfc1035#section-2.3.1
  386. # https://tools.ietf.org/html/rfc2181#section-11
  387. const size = 64
  388. result = newString(size)
  389. when useWinVersion:
  390. let success = winlean.gethostname(result, size)
  391. else:
  392. # Posix
  393. let success = posix.gethostname(result, size)
  394. if success != 0.cint:
  395. raiseOSError(osLastError())
  396. let x = len(cstring(result))
  397. result.setLen(x)
  398. proc getSockDomain*(socket: SocketHandle): Domain =
  399. ## Returns the socket's domain (AF_INET or AF_INET6).
  400. var name: Sockaddr_in6
  401. var namelen = sizeof(name).SockLen
  402. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  403. addr(namelen)) == -1'i32:
  404. raiseOSError(osLastError())
  405. let knownDomain = toKnownDomain(name.sin6_family.cint)
  406. if knownDomain.isSome:
  407. result = knownDomain.get()
  408. else:
  409. raise newException(IOError, "Unknown socket family in getSockDomain")
  410. proc getAddrString*(sockAddr: ptr SockAddr): string =
  411. ## Returns the string representation of address within sockAddr
  412. if sockAddr.sa_family.cint == nativeAfInet:
  413. result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
  414. elif sockAddr.sa_family.cint == nativeAfInet6:
  415. let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN
  416. else: 46 # it's actually 46 in both cases
  417. result = newString(addrLen)
  418. let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
  419. when not useWinVersion:
  420. if posix.inet_ntop(posix.AF_INET6, addr6, addr result[0],
  421. result.len.int32) == nil:
  422. raiseOSError(osLastError())
  423. if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
  424. result = result.substr("::ffff:".len)
  425. else:
  426. if winlean.inet_ntop(winlean.AF_INET6, addr6, addr result[0],
  427. result.len.int32) == nil:
  428. raiseOSError(osLastError())
  429. setLen(result, len(cstring(result)))
  430. else:
  431. when defined(posix) and not defined(nimdoc):
  432. if sockAddr.sa_family.cint == nativeAfUnix:
  433. return "unix"
  434. raise newException(IOError, "Unknown socket family in getAddrString")
  435. proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) =
  436. ## Stores in ``strAddress`` the string representation of the address inside
  437. ## ``sockAddr``
  438. ##
  439. ## **Note**
  440. ## * ``strAddress`` must be initialized to 46 in length.
  441. assert(46 == len(strAddress),
  442. "`strAddress` was not initialized correctly. 46 != `len(strAddress)`")
  443. if sockAddr.sa_family.cint == nativeAfInet:
  444. let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr
  445. when not useWinVersion:
  446. if posix.inet_ntop(posix.AF_INET, addr4, addr strAddress[0],
  447. strAddress.len.int32) == nil:
  448. raiseOSError(osLastError())
  449. else:
  450. if winlean.inet_ntop(winlean.AF_INET, addr4, addr strAddress[0],
  451. strAddress.len.int32) == nil:
  452. raiseOSError(osLastError())
  453. elif sockAddr.sa_family.cint == nativeAfInet6:
  454. let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
  455. when not useWinVersion:
  456. if posix.inet_ntop(posix.AF_INET6, addr6, addr strAddress[0],
  457. strAddress.len.int32) == nil:
  458. raiseOSError(osLastError())
  459. if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
  460. strAddress = strAddress.substr("::ffff:".len)
  461. else:
  462. if winlean.inet_ntop(winlean.AF_INET6, addr6, addr strAddress[0],
  463. strAddress.len.int32) == nil:
  464. raiseOSError(osLastError())
  465. else:
  466. raise newException(IOError, "Unknown socket family in getAddrString")
  467. setLen(strAddress, len(cstring(strAddress)))
  468. when defined(posix) and not defined(nimdoc):
  469. proc makeUnixAddr*(path: string): Sockaddr_un =
  470. result.sun_family = AF_UNIX.TSa_Family
  471. if path.len >= Sockaddr_un_path_length:
  472. raise newException(ValueError, "socket path too long")
  473. copyMem(addr result.sun_path, path.cstring, path.len + 1)
  474. proc getSockName*(socket: SocketHandle): Port =
  475. ## Returns the socket's associated port number.
  476. var name: Sockaddr_in
  477. when useWinVersion:
  478. name.sin_family = uint16(ord(AF_INET))
  479. else:
  480. name.sin_family = TSa_Family(posix.AF_INET)
  481. #name.sin_port = htons(cint16(port))
  482. #name.sin_addr.s_addr = htonl(INADDR_ANY)
  483. var namelen = sizeof(name).SockLen
  484. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  485. addr(namelen)) == -1'i32:
  486. raiseOSError(osLastError())
  487. result = Port(nativesockets.ntohs(name.sin_port))
  488. proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
  489. ## Returns the socket's local address and port number.
  490. ##
  491. ## Similar to POSIX's `getsockname`:idx:.
  492. case domain
  493. of AF_INET:
  494. var name: Sockaddr_in
  495. when useWinVersion:
  496. name.sin_family = uint16(ord(AF_INET))
  497. else:
  498. name.sin_family = TSa_Family(posix.AF_INET)
  499. var namelen = sizeof(name).SockLen
  500. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  501. addr(namelen)) == -1'i32:
  502. raiseOSError(osLastError())
  503. result = ($inet_ntoa(name.sin_addr),
  504. Port(nativesockets.ntohs(name.sin_port)))
  505. of AF_INET6:
  506. var name: Sockaddr_in6
  507. when useWinVersion:
  508. name.sin6_family = uint16(ord(AF_INET6))
  509. else:
  510. name.sin6_family = TSa_Family(posix.AF_INET6)
  511. var namelen = sizeof(name).SockLen
  512. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  513. addr(namelen)) == -1'i32:
  514. raiseOSError(osLastError())
  515. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
  516. result[0] = newString(64)
  517. if inet_ntop(name.sin6_family.cint,
  518. addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
  519. raiseOSError(osLastError())
  520. setLen(result[0], result[0].cstring.len)
  521. result[1] = Port(nativesockets.ntohs(name.sin6_port))
  522. else:
  523. raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
  524. proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
  525. ## Returns the socket's peer address and port number.
  526. ##
  527. ## Similar to POSIX's `getpeername`:idx:
  528. case domain
  529. of AF_INET:
  530. var name: Sockaddr_in
  531. when useWinVersion:
  532. name.sin_family = uint16(ord(AF_INET))
  533. else:
  534. name.sin_family = TSa_Family(posix.AF_INET)
  535. var namelen = sizeof(name).SockLen
  536. if getpeername(socket, cast[ptr SockAddr](addr(name)),
  537. addr(namelen)) == -1'i32:
  538. raiseOSError(osLastError())
  539. result = ($inet_ntoa(name.sin_addr),
  540. Port(nativesockets.ntohs(name.sin_port)))
  541. of AF_INET6:
  542. var name: Sockaddr_in6
  543. when useWinVersion:
  544. name.sin6_family = uint16(ord(AF_INET6))
  545. else:
  546. name.sin6_family = TSa_Family(posix.AF_INET6)
  547. var namelen = sizeof(name).SockLen
  548. if getpeername(socket, cast[ptr SockAddr](addr(name)),
  549. addr(namelen)) == -1'i32:
  550. raiseOSError(osLastError())
  551. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
  552. result[0] = newString(64)
  553. if inet_ntop(name.sin6_family.cint,
  554. addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
  555. raiseOSError(osLastError())
  556. setLen(result[0], result[0].cstring.len)
  557. result[1] = Port(nativesockets.ntohs(name.sin6_port))
  558. else:
  559. raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
  560. proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
  561. tags: [ReadIOEffect].} =
  562. ## getsockopt for integer options.
  563. var res: cint
  564. var size = sizeof(res).SockLen
  565. if getsockopt(socket, cint(level), cint(optname),
  566. addr(res), addr(size)) < 0'i32:
  567. raiseOSError(osLastError())
  568. result = int(res)
  569. proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
  570. tags: [WriteIOEffect].} =
  571. ## setsockopt for integer options.
  572. var value = cint(optval)
  573. if setsockopt(socket, cint(level), cint(optname), addr(value),
  574. sizeof(value).SockLen) < 0'i32:
  575. raiseOSError(osLastError())
  576. proc setBlocking*(s: SocketHandle, blocking: bool) =
  577. ## Sets blocking mode on socket.
  578. ##
  579. ## Raises OSError on error.
  580. when useWinVersion:
  581. var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
  582. if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
  583. raiseOSError(osLastError())
  584. else: # BSD sockets
  585. var x: int = fcntl(s, F_GETFL, 0)
  586. if x == -1:
  587. raiseOSError(osLastError())
  588. else:
  589. var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
  590. if fcntl(s, F_SETFL, mode) == -1:
  591. raiseOSError(osLastError())
  592. proc timeValFromMilliseconds(timeout = 500): Timeval =
  593. if timeout != -1:
  594. var seconds = timeout div 1000
  595. when useWinVersion:
  596. result.tv_sec = seconds.int32
  597. result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
  598. else:
  599. result.tv_sec = seconds.Time
  600. result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
  601. proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) =
  602. FD_ZERO(fd)
  603. for i in items(s):
  604. m = max(m, int(i))
  605. FD_SET(i, fd)
  606. proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
  607. var i = 0
  608. var L = s.len
  609. while i < L:
  610. if FD_ISSET(s[i], fd) == 0'i32:
  611. s[i] = s[L-1]
  612. dec(L)
  613. else:
  614. inc(i)
  615. setLen(s, L)
  616. proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
  617. ## When a socket in ``readfds`` is ready to be read from then a non-zero
  618. ## value will be returned specifying the count of the sockets which can be
  619. ## read from. The sockets which cannot be read from will also be removed
  620. ## from ``readfds``.
  621. ##
  622. ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
  623. ## an unlimited time.
  624. var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
  625. var rd: TFdSet
  626. var m = 0
  627. createFdSet((rd), readfds, m)
  628. if timeout != -1:
  629. result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
  630. else:
  631. result = int(select(cint(m+1), addr(rd), nil, nil, nil))
  632. pruneSocketSet(readfds, (rd))
  633. proc selectWrite*(writefds: var seq[SocketHandle],
  634. timeout = 500): int {.tags: [ReadIOEffect].} =
  635. ## When a socket in ``writefds`` is ready to be written to then a non-zero
  636. ## value will be returned specifying the count of the sockets which can be
  637. ## written to. The sockets which cannot be written to will also be removed
  638. ## from ``writefds``.
  639. ##
  640. ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for
  641. ## an unlimited time.
  642. var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
  643. var wr: TFdSet
  644. var m = 0
  645. createFdSet((wr), writefds, m)
  646. if timeout != -1:
  647. result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
  648. else:
  649. result = int(select(cint(m+1), nil, addr(wr), nil, nil))
  650. pruneSocketSet(writefds, (wr))
  651. proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (SocketHandle, string) =
  652. ## Accepts a new client connection.
  653. ##
  654. ## `inheritable` decides if the resulting SocketHandle can be inherited by
  655. ## child processes.
  656. ##
  657. ## Returns (osInvalidSocket, "") if an error occurred.
  658. var sockAddress: Sockaddr_in
  659. var addrLen = sizeof(sockAddress).SockLen
  660. var sock =
  661. when (defined(linux) or defined(bsd)) and not defined(nimdoc):
  662. accept4(fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen),
  663. if inheritable: 0 else: SOCK_CLOEXEC)
  664. else:
  665. accept(fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
  666. when declared(setInheritable) and not (defined(linux) or defined(bsd)):
  667. if not setInheritable(sock, inheritable):
  668. close sock
  669. sock = osInvalidSocket
  670. if sock == osInvalidSocket:
  671. return (osInvalidSocket, "")
  672. else:
  673. return (sock, $inet_ntoa(sockAddress.sin_addr))
  674. when defined(Windows):
  675. var wsa: WSAData
  676. if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())