nativesockets.nim 32 KB

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