nativesockets.nim 32 KB

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