nativesockets.nim 31 KB

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