nativesockets.nim 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  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 myaddr: InAddr
  339. myaddr.s_addr = inet_addr(ip)
  340. when useWinVersion:
  341. var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
  342. cint(AF_INET))
  343. if s == nil: raiseOSError(osLastError())
  344. else:
  345. var s =
  346. when defined(android4):
  347. posix.gethostbyaddr(cast[cstring](addr(myaddr)), sizeof(myaddr).cint,
  348. cint(posix.AF_INET))
  349. else:
  350. posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).SockLen,
  351. cint(posix.AF_INET))
  352. if s == nil:
  353. raiseOSError(osLastError(), $hstrerror(h_errno))
  354. result.name = $s.h_name
  355. result.aliases = cstringArrayToSeq(s.h_aliases)
  356. when useWinVersion:
  357. result.addrtype = Domain(s.h_addrtype)
  358. else:
  359. if s.h_addrtype == posix.AF_INET:
  360. result.addrtype = AF_INET
  361. elif s.h_addrtype == posix.AF_INET6:
  362. result.addrtype = AF_INET6
  363. else:
  364. raiseOSError(osLastError(), "unknown h_addrtype")
  365. if result.addrtype == AF_INET:
  366. result.addrList = @[]
  367. var i = 0
  368. while not isNil(s.h_addr_list[i]):
  369. var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
  370. result.addrList.add($inet_ntoa(inaddrPtr[]))
  371. inc(i)
  372. else:
  373. result.addrList = cstringArrayToSeq(s.h_addr_list)
  374. result.length = int(s.h_length)
  375. proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
  376. ## This function will lookup the IP address of a hostname.
  377. when useWinVersion:
  378. var s = winlean.gethostbyname(name)
  379. else:
  380. var s = posix.gethostbyname(name)
  381. if s == nil: raiseOSError(osLastError())
  382. result.name = $s.h_name
  383. result.aliases = cstringArrayToSeq(s.h_aliases)
  384. when useWinVersion:
  385. result.addrtype = Domain(s.h_addrtype)
  386. else:
  387. if s.h_addrtype == posix.AF_INET:
  388. result.addrtype = AF_INET
  389. elif s.h_addrtype == posix.AF_INET6:
  390. result.addrtype = AF_INET6
  391. else:
  392. raiseOSError(osLastError(), "unknown h_addrtype")
  393. if result.addrtype == AF_INET:
  394. result.addrList = @[]
  395. var i = 0
  396. while not isNil(s.h_addr_list[i]):
  397. var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
  398. result.addrList.add($inet_ntoa(inaddrPtr[]))
  399. inc(i)
  400. else:
  401. result.addrList = cstringArrayToSeq(s.h_addr_list)
  402. result.length = int(s.h_length)
  403. proc getHostname*(): string {.tags: [ReadIOEffect].} =
  404. ## Returns the local hostname (not the FQDN)
  405. # https://tools.ietf.org/html/rfc1035#section-2.3.1
  406. # https://tools.ietf.org/html/rfc2181#section-11
  407. const size = 256
  408. result = newString(size)
  409. when useWinVersion:
  410. let success = winlean.gethostname(result.cstring, size)
  411. else:
  412. # Posix
  413. let success = posix.gethostname(result.cstring, size)
  414. if success != 0.cint:
  415. raiseOSError(osLastError())
  416. let x = len(cstring(result))
  417. result.setLen(x)
  418. proc getAddrString*(sockAddr: ptr SockAddr): string =
  419. ## Returns the string representation of address within sockAddr
  420. if sockAddr.sa_family.cint == nativeAfInet:
  421. result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
  422. elif sockAddr.sa_family.cint == nativeAfInet6:
  423. let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
  424. else: 46 # it's actually 46 in both cases
  425. result = newString(addrLen)
  426. let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
  427. when not useWinVersion:
  428. if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr result[0]),
  429. result.len.int32) == nil:
  430. raiseOSError(osLastError())
  431. if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
  432. result.setSlice("::ffff:".len..<addrLen)
  433. else:
  434. if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr result[0]),
  435. result.len.int32) == nil:
  436. raiseOSError(osLastError())
  437. setLen(result, len(cstring(result)))
  438. else:
  439. when defined(posix) and not defined(nimdoc):
  440. if sockAddr.sa_family.cint == nativeAfUnix:
  441. return "unix"
  442. raise newException(IOError, "Unknown socket family in getAddrString")
  443. proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) =
  444. ## Stores in `strAddress` the string representation of the address inside
  445. ## `sockAddr`
  446. ##
  447. ## **Note**
  448. ## * `strAddress` must be initialized to 46 in length.
  449. const length = 46
  450. assert(length == len(strAddress),
  451. "`strAddress` was not initialized correctly. 46 != `len(strAddress)`")
  452. if sockAddr.sa_family.cint == nativeAfInet:
  453. let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr
  454. when not useWinVersion:
  455. if posix.inet_ntop(posix.AF_INET, addr4, cast[cstring](addr strAddress[0]),
  456. strAddress.len.int32) == nil:
  457. raiseOSError(osLastError())
  458. else:
  459. if winlean.inet_ntop(winlean.AF_INET, addr4, cast[cstring](addr strAddress[0]),
  460. strAddress.len.int32) == nil:
  461. raiseOSError(osLastError())
  462. elif sockAddr.sa_family.cint == nativeAfInet6:
  463. let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
  464. when not useWinVersion:
  465. if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
  466. strAddress.len.int32) == nil:
  467. raiseOSError(osLastError())
  468. if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
  469. strAddress.setSlice("::ffff:".len..<length)
  470. else:
  471. if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
  472. strAddress.len.int32) == nil:
  473. raiseOSError(osLastError())
  474. else:
  475. raise newException(IOError, "Unknown socket family in getAddrString")
  476. setLen(strAddress, len(cstring(strAddress)))
  477. when defined(posix) and not defined(nimdoc):
  478. proc makeUnixAddr*(path: string): Sockaddr_un =
  479. result.sun_family = AF_UNIX.TSa_Family
  480. if path.len >= Sockaddr_un_path_length:
  481. raise newException(ValueError, "socket path too long")
  482. copyMem(addr result.sun_path, path.cstring, path.len + 1)
  483. proc getSockName*(socket: SocketHandle): Port =
  484. ## Returns the socket's associated port number.
  485. var name: Sockaddr_in
  486. when useWinVersion:
  487. name.sin_family = uint16(ord(AF_INET))
  488. else:
  489. name.sin_family = TSa_Family(posix.AF_INET)
  490. #name.sin_port = htons(cint16(port))
  491. #name.sin_addr.s_addr = htonl(INADDR_ANY)
  492. var namelen = sizeof(name).SockLen
  493. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  494. addr(namelen)) == -1'i32:
  495. raiseOSError(osLastError())
  496. result = Port(nativesockets.ntohs(name.sin_port))
  497. proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
  498. ## Returns the socket's local address and port number.
  499. ##
  500. ## Similar to POSIX's `getsockname`:idx:.
  501. case domain
  502. of AF_INET:
  503. var name: Sockaddr_in
  504. when useWinVersion:
  505. name.sin_family = uint16(ord(AF_INET))
  506. else:
  507. name.sin_family = TSa_Family(posix.AF_INET)
  508. var namelen = sizeof(name).SockLen
  509. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  510. addr(namelen)) == -1'i32:
  511. raiseOSError(osLastError())
  512. result = ($inet_ntoa(name.sin_addr),
  513. Port(nativesockets.ntohs(name.sin_port)))
  514. of AF_INET6:
  515. var name: Sockaddr_in6
  516. when useWinVersion:
  517. name.sin6_family = uint16(ord(AF_INET6))
  518. else:
  519. name.sin6_family = TSa_Family(posix.AF_INET6)
  520. var namelen = sizeof(name).SockLen
  521. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  522. addr(namelen)) == -1'i32:
  523. raiseOSError(osLastError())
  524. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
  525. result[0] = newString(64)
  526. if inet_ntop(name.sin6_family.cint,
  527. addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
  528. raiseOSError(osLastError())
  529. setLen(result[0], result[0].cstring.len)
  530. result[1] = Port(nativesockets.ntohs(name.sin6_port))
  531. else:
  532. raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
  533. proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
  534. ## Returns the socket's peer address and port number.
  535. ##
  536. ## Similar to POSIX's `getpeername`:idx:
  537. case domain
  538. of AF_INET:
  539. var name: Sockaddr_in
  540. when useWinVersion:
  541. name.sin_family = uint16(ord(AF_INET))
  542. else:
  543. name.sin_family = TSa_Family(posix.AF_INET)
  544. var namelen = sizeof(name).SockLen
  545. if getpeername(socket, cast[ptr SockAddr](addr(name)),
  546. addr(namelen)) == -1'i32:
  547. raiseOSError(osLastError())
  548. result = ($inet_ntoa(name.sin_addr),
  549. Port(nativesockets.ntohs(name.sin_port)))
  550. of AF_INET6:
  551. var name: Sockaddr_in6
  552. when useWinVersion:
  553. name.sin6_family = uint16(ord(AF_INET6))
  554. else:
  555. name.sin6_family = TSa_Family(posix.AF_INET6)
  556. var namelen = sizeof(name).SockLen
  557. if getpeername(socket, cast[ptr SockAddr](addr(name)),
  558. addr(namelen)) == -1'i32:
  559. raiseOSError(osLastError())
  560. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
  561. result[0] = newString(64)
  562. if inet_ntop(name.sin6_family.cint,
  563. addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
  564. raiseOSError(osLastError())
  565. setLen(result[0], result[0].cstring.len)
  566. result[1] = Port(nativesockets.ntohs(name.sin6_port))
  567. else:
  568. raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
  569. when useNimNetLite:
  570. when useWinVersion:
  571. const
  572. INET_ADDRSTRLEN = 16
  573. INET6_ADDRSTRLEN = 46 # it's actually 46 in both cases
  574. proc sockAddrToStr(sa: ptr SockAddr): string {.noinit.} =
  575. let af_family = sa.sa_family
  576. var nl, v4Slice: cint
  577. var si_addr: ptr InAddr
  578. if af_family == AF_INET.TSa_Family:
  579. nl = INET_ADDRSTRLEN
  580. si_addr = cast[ptr Sockaddr_in](sa).sin_addr.addr()
  581. elif af_family == AF_INET6.TSa_Family:
  582. nl = INET6_ADDRSTRLEN
  583. let si6_addr = cast[ptr Sockaddr_in6](sa).sin6_addr.addr()
  584. si_addr = cast[ptr InAddr](si6_addr) # let's us reuse logic below
  585. when defined(posix) and not defined(nimdoc) and not defined(zephyr):
  586. if posix.IN6_IS_ADDR_V4MAPPED(si6_addr) != 0:
  587. v4Slice = "::ffff:".len()
  588. else:
  589. when defined(posix) and not defined(nimdoc):
  590. if af_family.cint == nativeAfUnix:
  591. return "unix"
  592. return ""
  593. result = newString(nl)
  594. let namePtr = result.cstring()
  595. if namePtr == inet_ntop(af_family.cint, si_addr, namePtr, nl):
  596. result.setLen(len(namePtr))
  597. if v4Slice > 0: result.setSlice(v4Slice.int ..< nl.int)
  598. else:
  599. return ""
  600. proc sockAddrToStr(sa: var Sockaddr_in | var Sockaddr_in6): string =
  601. result = sockAddrToStr(cast[ptr SockAddr](unsafeAddr(sa)))
  602. proc getAddrString*(sockAddr: ptr SockAddr): string =
  603. result = sockAddrToStr(sockAddr)
  604. if result.len() == 0:
  605. raiseOSError(osLastError())
  606. proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) {.noinit.} =
  607. strAddress = getAddrString(sockAddr)
  608. proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
  609. ## Returns the socket's local address and port number.
  610. ##
  611. ## Similar to POSIX's `getsockname`:idx:.
  612. template sockGetNameOrRaiseError(socket: untyped, name: untyped) =
  613. var namelen = sizeof(socket).SockLen
  614. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  615. addr(namelen)) == -1'i32:
  616. raiseOSError(osLastError())
  617. case domain
  618. of AF_INET:
  619. var name = Sockaddr_in(sin_family: TSa_Family(posix.AF_INET))
  620. sockGetNameOrRaiseError(socket, name)
  621. result = (sockAddrToStr(name),
  622. Port(nativesockets.ntohs(name.sin_port)))
  623. of AF_INET6:
  624. var name = Sockaddr_in6(sin6_family: TSa_Family(posix.AF_INET6))
  625. sockGetNameOrRaiseError(socket, name)
  626. result = (sockAddrToStr(name),
  627. Port(nativesockets.ntohs(name.sin6_port)))
  628. else:
  629. raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
  630. proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
  631. tags: [ReadIOEffect].} =
  632. ## getsockopt for integer options.
  633. var res: cint
  634. var size = sizeof(res).SockLen
  635. if getsockopt(socket, cint(level), cint(optname),
  636. addr(res), addr(size)) < 0'i32:
  637. raiseOSError(osLastError())
  638. result = int(res)
  639. proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
  640. tags: [WriteIOEffect].} =
  641. ## setsockopt for integer options.
  642. var value = cint(optval)
  643. if setsockopt(socket, cint(level), cint(optname), addr(value),
  644. sizeof(value).SockLen) < 0'i32:
  645. raiseOSError(osLastError())
  646. proc setBlocking*(s: SocketHandle, blocking: bool) =
  647. ## Sets blocking mode on socket.
  648. ##
  649. ## Raises OSError on error.
  650. when useWinVersion:
  651. var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
  652. if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
  653. raiseOSError(osLastError())
  654. else: # BSD sockets
  655. var x: int = fcntl(s, F_GETFL, 0)
  656. if x == -1:
  657. raiseOSError(osLastError())
  658. else:
  659. var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
  660. if fcntl(s, F_SETFL, mode) == -1:
  661. raiseOSError(osLastError())
  662. proc timeValFromMilliseconds(timeout = 500): Timeval =
  663. if timeout != -1:
  664. var seconds = timeout div 1000
  665. when useWinVersion:
  666. result.tv_sec = seconds.int32
  667. result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
  668. else:
  669. result.tv_sec = seconds.Time
  670. result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
  671. proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) =
  672. FD_ZERO(fd)
  673. for i in items(s):
  674. m = max(m, int(i))
  675. FD_SET(i, fd)
  676. proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
  677. var i = 0
  678. var L = s.len
  679. while i < L:
  680. if FD_ISSET(s[i], fd) == 0'i32:
  681. s[i] = s[L-1]
  682. dec(L)
  683. else:
  684. inc(i)
  685. setLen(s, L)
  686. proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
  687. ## When a socket in `readfds` is ready to be read from then a non-zero
  688. ## value will be returned specifying the count of the sockets which can be
  689. ## read from. The sockets which cannot be read from will also be removed
  690. ## from `readfds`.
  691. ##
  692. ## `timeout` is specified in milliseconds and `-1` can be specified for
  693. ## an unlimited time.
  694. var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
  695. var rd: TFdSet
  696. var m = 0
  697. createFdSet((rd), readfds, m)
  698. if timeout != -1:
  699. result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
  700. else:
  701. result = int(select(cint(m+1), addr(rd), nil, nil, nil))
  702. pruneSocketSet(readfds, (rd))
  703. proc selectWrite*(writefds: var seq[SocketHandle],
  704. timeout = 500): int {.tags: [ReadIOEffect].} =
  705. ## When a socket in `writefds` is ready to be written to then a non-zero
  706. ## value will be returned specifying the count of the sockets which can be
  707. ## written to. The sockets which cannot be written to will also be removed
  708. ## from `writefds`.
  709. ##
  710. ## `timeout` is specified in milliseconds and `-1` can be specified for
  711. ## an unlimited time.
  712. var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
  713. var wr: TFdSet
  714. var m = 0
  715. createFdSet((wr), writefds, m)
  716. if timeout != -1:
  717. result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
  718. else:
  719. result = int(select(cint(m+1), nil, addr(wr), nil, nil))
  720. pruneSocketSet(writefds, (wr))
  721. proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (SocketHandle, string) =
  722. ## Accepts a new client connection.
  723. ##
  724. ## `inheritable` decides if the resulting SocketHandle can be inherited by
  725. ## child processes.
  726. ##
  727. ## Returns (osInvalidSocket, "") if an error occurred.
  728. var sockAddress: SockAddr
  729. var addrLen = sizeof(sockAddress).SockLen
  730. var sock =
  731. when (defined(linux) or defined(bsd)) and not defined(nimdoc):
  732. accept4(fd, addr(sockAddress), addr(addrLen),
  733. if inheritable: 0 else: SOCK_CLOEXEC)
  734. else:
  735. accept(fd, addr(sockAddress), addr(addrLen))
  736. when declared(setInheritable) and not (defined(linux) or defined(bsd)):
  737. if not setInheritable(sock, inheritable):
  738. close sock
  739. sock = osInvalidSocket
  740. if sock == osInvalidSocket:
  741. return (osInvalidSocket, "")
  742. else:
  743. when useNimNetLite:
  744. var name = sockAddrToStr(addr sockAddress)
  745. return (sock, name)
  746. else:
  747. return (sock, $inet_ntoa(cast[Sockaddr_in](sockAddress).sin_addr))
  748. when defined(windows):
  749. var wsa: WSAData
  750. if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())