nativesockets.nim 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  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 = AddrInfo(
  250. ai_family: toInt(domain),
  251. ai_socktype: toInt(sockType),
  252. ai_protocol: toInt(protocol)
  253. )
  254. result = nil
  255. # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
  256. # FreeBSD, Haiku don't support AI_V4MAPPED but defines the macro.
  257. # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
  258. # https://dev.haiku-os.org/ticket/14323
  259. when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and
  260. not defined(android) and not defined(haiku):
  261. if domain == AF_INET6:
  262. hints.ai_flags = AI_V4MAPPED
  263. let socketPort = if sockType == SOCK_RAW: "" else: $port
  264. var gaiResult = getaddrinfo(address, socketPort.cstring, addr(hints), result)
  265. if gaiResult != 0'i32:
  266. when useWinVersion or defined(freertos) or defined(nuttx):
  267. raiseOSError(osLastError())
  268. else:
  269. raiseOSError(osLastError(), $gai_strerror(gaiResult))
  270. proc ntohl*(x: uint32): uint32 =
  271. ## Converts 32-bit unsigned integers from network to host byte order.
  272. ## On machines where the host byte order is the same as network byte order,
  273. ## this is a no-op; otherwise, it performs a 4-byte swap operation.
  274. when cpuEndian == bigEndian: result = x
  275. else: result = (x shr 24'u32) or
  276. (x shr 8'u32 and 0xff00'u32) or
  277. (x shl 8'u32 and 0xff0000'u32) or
  278. (x shl 24'u32)
  279. proc ntohs*(x: uint16): uint16 =
  280. ## Converts 16-bit unsigned integers from network to host byte order. On
  281. ## machines where the host byte order is the same as network byte order,
  282. ## this is a no-op; otherwise, it performs a 2-byte swap operation.
  283. when cpuEndian == bigEndian: result = x
  284. else: result = (x shr 8'u16) or (x shl 8'u16)
  285. template htonl*(x: uint32): untyped =
  286. ## Converts 32-bit unsigned integers from host to network byte order. On
  287. ## machines where the host byte order is the same as network byte order,
  288. ## this is a no-op; otherwise, it performs a 4-byte swap operation.
  289. nativesockets.ntohl(x)
  290. template htons*(x: uint16): untyped =
  291. ## Converts 16-bit unsigned integers from host to network byte order.
  292. ## On machines where the host byte order is the same as network byte
  293. ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
  294. nativesockets.ntohs(x)
  295. proc getSockDomain*(socket: SocketHandle): Domain =
  296. ## Returns the socket's domain (AF_INET or AF_INET6).
  297. var name: Sockaddr_in6 = default(Sockaddr_in6)
  298. var namelen = sizeof(name).SockLen
  299. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  300. addr(namelen)) == -1'i32:
  301. raiseOSError(osLastError())
  302. let knownDomain = toKnownDomain(name.sin6_family.cint)
  303. if knownDomain.isSome:
  304. result = knownDomain.get()
  305. else:
  306. raise newException(IOError, "Unknown socket family in getSockDomain")
  307. when not useNimNetLite:
  308. proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
  309. ## Searches the database from the beginning and finds the first entry for
  310. ## which the service name specified by `name` matches the s_name member
  311. ## and the protocol name specified by `proto` matches the s_proto member.
  312. ##
  313. ## On posix this will search through the `/etc/services` file.
  314. when useWinVersion:
  315. var s = winlean.getservbyname(name, proto)
  316. else:
  317. var s = posix.getservbyname(name, proto)
  318. if s == nil: raiseOSError(osLastError(), "Service not found.")
  319. result = Servent(
  320. name: $s.s_name,
  321. aliases: cstringArrayToSeq(s.s_aliases),
  322. port: Port(s.s_port),
  323. proto: $s.s_proto
  324. )
  325. proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} =
  326. ## Searches the database from the beginning and finds the first entry for
  327. ## which the port specified by `port` matches the s_port member and the
  328. ## protocol name specified by `proto` matches the s_proto member.
  329. ##
  330. ## On posix this will search through the `/etc/services` file.
  331. when useWinVersion:
  332. var s = winlean.getservbyport(uint16(port).cint, proto)
  333. else:
  334. var s = posix.getservbyport(uint16(port).cint, proto)
  335. if s == nil: raiseOSError(osLastError(), "Service not found.")
  336. result = Servent(
  337. name: $s.s_name,
  338. aliases: cstringArrayToSeq(s.s_aliases),
  339. port: Port(s.s_port),
  340. proto: $s.s_proto
  341. )
  342. proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
  343. ## This function will lookup the hostname of an IP Address.
  344. var
  345. addrInfo = getAddrInfo(ip, Port(0), AF_UNSPEC)
  346. myAddr: pointer
  347. addrLen = 0
  348. family = 0
  349. defer: freeAddrInfo(addrInfo)
  350. if addrInfo.ai_addr.sa_family.cint == nativeAfInet:
  351. family = nativeAfInet
  352. myAddr = addr cast[ptr Sockaddr_in](addrInfo.ai_addr).sin_addr
  353. addrLen = 4
  354. elif addrInfo.ai_addr.sa_family.cint == nativeAfInet6:
  355. family = nativeAfInet6
  356. myAddr = addr cast[ptr Sockaddr_in6](addrInfo.ai_addr).sin6_addr
  357. addrLen = 16
  358. else:
  359. raise newException(IOError, "Unknown socket family in `getHostByAddr()`")
  360. when useWinVersion:
  361. var s = winlean.gethostbyaddr(cast[ptr InAddr](myAddr), addrLen.cuint,
  362. cint(family))
  363. if s == nil: raiseOSError(osLastError())
  364. else:
  365. var s =
  366. when defined(android4):
  367. posix.gethostbyaddr(cast[cstring](myAddr), addrLen.cint,
  368. cint(family))
  369. else:
  370. posix.gethostbyaddr(myAddr, addrLen.SockLen,
  371. cint(family))
  372. if s == nil:
  373. raiseOSError(osLastError(), $hstrerror(h_errno))
  374. result = Hostent(
  375. name: $s.h_name,
  376. aliases: cstringArrayToSeq(s.h_aliases)
  377. )
  378. when useWinVersion:
  379. result.addrtype = Domain(s.h_addrtype)
  380. else:
  381. if s.h_addrtype == posix.AF_INET:
  382. result.addrtype = AF_INET
  383. elif s.h_addrtype == posix.AF_INET6:
  384. result.addrtype = AF_INET6
  385. else:
  386. raiseOSError(osLastError(), "unknown h_addrtype")
  387. if result.addrtype == AF_INET:
  388. result.addrList = @[]
  389. var i = 0
  390. while not isNil(s.h_addr_list[i]):
  391. var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
  392. result.addrList.add($inet_ntoa(inaddrPtr[]))
  393. inc(i)
  394. else:
  395. let strAddrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
  396. else: 46
  397. var i = 0
  398. while not isNil(s.h_addr_list[i]):
  399. var ipStr = newString(strAddrLen)
  400. if inet_ntop(nativeAfInet6, cast[pointer](s.h_addr_list[i]),
  401. cstring(ipStr), len(ipStr).int32) == nil:
  402. raiseOSError(osLastError())
  403. when not useWinVersion:
  404. if posix.IN6_IS_ADDR_V4MAPPED(cast[ptr In6Addr](s.h_addr_list[i])) != 0:
  405. ipStr.setSlice("::ffff:".len..<strAddrLen)
  406. setLen(ipStr, len(cstring(ipStr)))
  407. result.addrList.add(ipStr)
  408. inc(i)
  409. result.length = int(s.h_length)
  410. proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
  411. ## This function will lookup the IP address of a hostname.
  412. when useWinVersion:
  413. var s = winlean.gethostbyname(name)
  414. else:
  415. var s = posix.gethostbyname(name)
  416. if s == nil: raiseOSError(osLastError())
  417. result = Hostent(
  418. name: $s.h_name,
  419. aliases: cstringArrayToSeq(s.h_aliases)
  420. )
  421. when useWinVersion:
  422. result.addrtype = Domain(s.h_addrtype)
  423. else:
  424. if s.h_addrtype == posix.AF_INET:
  425. result.addrtype = AF_INET
  426. elif s.h_addrtype == posix.AF_INET6:
  427. result.addrtype = AF_INET6
  428. else:
  429. raiseOSError(osLastError(), "unknown h_addrtype")
  430. if result.addrtype == AF_INET:
  431. result.addrList = @[]
  432. var i = 0
  433. while not isNil(s.h_addr_list[i]):
  434. var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i])
  435. result.addrList.add($inet_ntoa(inaddrPtr[]))
  436. inc(i)
  437. else:
  438. result.addrList = cstringArrayToSeq(s.h_addr_list)
  439. result.length = int(s.h_length)
  440. proc getHostname*(): string {.tags: [ReadIOEffect].} =
  441. ## Returns the local hostname (not the FQDN)
  442. # https://tools.ietf.org/html/rfc1035#section-2.3.1
  443. # https://tools.ietf.org/html/rfc2181#section-11
  444. const size = 256
  445. result = newString(size)
  446. when useWinVersion:
  447. let success = winlean.gethostname(result.cstring, size)
  448. else:
  449. # Posix
  450. let success = posix.gethostname(result.cstring, size)
  451. if success != 0.cint:
  452. raiseOSError(osLastError())
  453. let x = len(cstring(result))
  454. result.setLen(x)
  455. proc getAddrString*(sockAddr: ptr SockAddr): string =
  456. ## Returns the string representation of address within sockAddr
  457. if sockAddr.sa_family.cint == nativeAfInet:
  458. result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
  459. elif sockAddr.sa_family.cint == nativeAfInet6:
  460. let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
  461. else: 46 # it's actually 46 in both cases
  462. result = newString(addrLen)
  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 result[0]),
  466. result.len.int32) == nil:
  467. raiseOSError(osLastError())
  468. if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
  469. result.setSlice("::ffff:".len..<addrLen)
  470. else:
  471. if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr result[0]),
  472. result.len.int32) == nil:
  473. raiseOSError(osLastError())
  474. setLen(result, len(cstring(result)))
  475. else:
  476. when defined(posix) and not defined(nimdoc):
  477. if sockAddr.sa_family.cint == nativeAfUnix:
  478. return "unix"
  479. raise newException(IOError, "Unknown socket family in getAddrString")
  480. proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) =
  481. ## Stores in `strAddress` the string representation of the address inside
  482. ## `sockAddr`
  483. ##
  484. ## **Note**
  485. ## * `strAddress` must be initialized to 46 in length.
  486. const length = 46
  487. assert(length == len(strAddress),
  488. "`strAddress` was not initialized correctly. 46 != `len(strAddress)`")
  489. if sockAddr.sa_family.cint == nativeAfInet:
  490. let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr
  491. when not useWinVersion:
  492. if posix.inet_ntop(posix.AF_INET, addr4, cast[cstring](addr strAddress[0]),
  493. strAddress.len.int32) == nil:
  494. raiseOSError(osLastError())
  495. else:
  496. if winlean.inet_ntop(winlean.AF_INET, addr4, cast[cstring](addr strAddress[0]),
  497. strAddress.len.int32) == nil:
  498. raiseOSError(osLastError())
  499. elif sockAddr.sa_family.cint == nativeAfInet6:
  500. let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
  501. when not useWinVersion:
  502. if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
  503. strAddress.len.int32) == nil:
  504. raiseOSError(osLastError())
  505. if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
  506. strAddress.setSlice("::ffff:".len..<length)
  507. else:
  508. if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
  509. strAddress.len.int32) == nil:
  510. raiseOSError(osLastError())
  511. else:
  512. raise newException(IOError, "Unknown socket family in getAddrString")
  513. setLen(strAddress, len(cstring(strAddress)))
  514. when defined(posix) and not defined(nimdoc):
  515. proc makeUnixAddr*(path: string): Sockaddr_un =
  516. result = Sockaddr_un(sun_family: AF_UNIX.TSa_Family)
  517. if path.len >= Sockaddr_un_path_length:
  518. raise newException(ValueError, "socket path too long")
  519. copyMem(addr result.sun_path, path.cstring, path.len + 1)
  520. proc getSockName*(socket: SocketHandle): Port =
  521. ## Returns the socket's associated port number.
  522. var name: Sockaddr_in = default(Sockaddr_in)
  523. when useWinVersion:
  524. name.sin_family = uint16(ord(AF_INET))
  525. else:
  526. name.sin_family = TSa_Family(posix.AF_INET)
  527. #name.sin_port = htons(cint16(port))
  528. #name.sin_addr.s_addr = htonl(INADDR_ANY)
  529. var namelen = sizeof(name).SockLen
  530. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  531. addr(namelen)) == -1'i32:
  532. raiseOSError(osLastError())
  533. result = Port(nativesockets.ntohs(name.sin_port))
  534. proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
  535. ## Returns the socket's local address and port number.
  536. ##
  537. ## Similar to POSIX's `getsockname`:idx:.
  538. result = default((string, Port))
  539. case domain
  540. of AF_INET:
  541. var name: Sockaddr_in = default(Sockaddr_in)
  542. when useWinVersion:
  543. name.sin_family = uint16(ord(AF_INET))
  544. else:
  545. name.sin_family = TSa_Family(posix.AF_INET)
  546. var namelen = sizeof(name).SockLen
  547. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  548. addr(namelen)) == -1'i32:
  549. raiseOSError(osLastError())
  550. result = ($inet_ntoa(name.sin_addr),
  551. Port(nativesockets.ntohs(name.sin_port)))
  552. of AF_INET6:
  553. var name: Sockaddr_in6 = default(Sockaddr_in6)
  554. when useWinVersion:
  555. name.sin6_family = uint16(ord(AF_INET6))
  556. else:
  557. name.sin6_family = TSa_Family(posix.AF_INET6)
  558. var namelen = sizeof(name).SockLen
  559. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  560. addr(namelen)) == -1'i32:
  561. raiseOSError(osLastError())
  562. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
  563. result[0] = newString(64)
  564. if inet_ntop(name.sin6_family.cint,
  565. addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
  566. raiseOSError(osLastError())
  567. setLen(result[0], result[0].cstring.len)
  568. result[1] = Port(nativesockets.ntohs(name.sin6_port))
  569. else:
  570. raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
  571. proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
  572. ## Returns the socket's peer address and port number.
  573. ##
  574. ## Similar to POSIX's `getpeername`:idx:
  575. result = default((string, Port))
  576. case domain
  577. of AF_INET:
  578. var name: Sockaddr_in = default(Sockaddr_in)
  579. when useWinVersion:
  580. name.sin_family = uint16(ord(AF_INET))
  581. else:
  582. name.sin_family = TSa_Family(posix.AF_INET)
  583. var namelen = sizeof(name).SockLen
  584. if getpeername(socket, cast[ptr SockAddr](addr(name)),
  585. addr(namelen)) == -1'i32:
  586. raiseOSError(osLastError())
  587. result = ($inet_ntoa(name.sin_addr),
  588. Port(nativesockets.ntohs(name.sin_port)))
  589. of AF_INET6:
  590. var name: Sockaddr_in6 = default(Sockaddr_in6)
  591. when useWinVersion:
  592. name.sin6_family = uint16(ord(AF_INET6))
  593. else:
  594. name.sin6_family = TSa_Family(posix.AF_INET6)
  595. var namelen = sizeof(name).SockLen
  596. if getpeername(socket, cast[ptr SockAddr](addr(name)),
  597. addr(namelen)) == -1'i32:
  598. raiseOSError(osLastError())
  599. # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
  600. result[0] = newString(64)
  601. if inet_ntop(name.sin6_family.cint,
  602. addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
  603. raiseOSError(osLastError())
  604. setLen(result[0], result[0].cstring.len)
  605. result[1] = Port(nativesockets.ntohs(name.sin6_port))
  606. else:
  607. raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
  608. when useNimNetLite:
  609. when useWinVersion:
  610. const
  611. INET_ADDRSTRLEN = 16
  612. INET6_ADDRSTRLEN = 46 # it's actually 46 in both cases
  613. proc sockAddrToStr(sa: ptr SockAddr): string {.noinit.} =
  614. let af_family = sa.sa_family
  615. var nl, v4Slice: cint
  616. var si_addr: ptr InAddr
  617. if af_family == AF_INET.TSa_Family:
  618. nl = INET_ADDRSTRLEN
  619. si_addr = cast[ptr Sockaddr_in](sa).sin_addr.addr()
  620. elif af_family == AF_INET6.TSa_Family:
  621. nl = INET6_ADDRSTRLEN
  622. let si6_addr = cast[ptr Sockaddr_in6](sa).sin6_addr.addr()
  623. si_addr = cast[ptr InAddr](si6_addr) # let's us reuse logic below
  624. when defined(posix) and not defined(nimdoc) and not defined(zephyr):
  625. if posix.IN6_IS_ADDR_V4MAPPED(si6_addr) != 0:
  626. v4Slice = "::ffff:".len()
  627. else:
  628. when defined(posix) and not defined(nimdoc):
  629. if af_family.cint == nativeAfUnix:
  630. return "unix"
  631. return ""
  632. result = newString(nl)
  633. let namePtr = result.cstring()
  634. if namePtr == inet_ntop(af_family.cint, si_addr, namePtr, nl):
  635. result.setLen(len(namePtr))
  636. if v4Slice > 0: result.setSlice(v4Slice.int ..< nl.int)
  637. else:
  638. return ""
  639. proc sockAddrToStr(sa: var Sockaddr_in | var Sockaddr_in6): string =
  640. result = sockAddrToStr(cast[ptr SockAddr](unsafeAddr(sa)))
  641. proc getAddrString*(sockAddr: ptr SockAddr): string =
  642. result = sockAddrToStr(sockAddr)
  643. if result.len() == 0:
  644. raiseOSError(osLastError())
  645. proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) {.noinit.} =
  646. strAddress = getAddrString(sockAddr)
  647. proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
  648. ## Returns the socket's local address and port number.
  649. ##
  650. ## Similar to POSIX's `getsockname`:idx:.
  651. template sockGetNameOrRaiseError(socket: untyped, name: untyped) =
  652. var namelen = sizeof(socket).SockLen
  653. if getsockname(socket, cast[ptr SockAddr](addr(name)),
  654. addr(namelen)) == -1'i32:
  655. raiseOSError(osLastError())
  656. case domain
  657. of AF_INET:
  658. var name = Sockaddr_in(sin_family: TSa_Family(posix.AF_INET))
  659. sockGetNameOrRaiseError(socket, name)
  660. result = (sockAddrToStr(name),
  661. Port(nativesockets.ntohs(name.sin_port)))
  662. of AF_INET6:
  663. var name = Sockaddr_in6(sin6_family: TSa_Family(posix.AF_INET6))
  664. sockGetNameOrRaiseError(socket, name)
  665. result = (sockAddrToStr(name),
  666. Port(nativesockets.ntohs(name.sin6_port)))
  667. else:
  668. raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
  669. proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {.
  670. tags: [ReadIOEffect].} =
  671. ## getsockopt for integer options.
  672. var res: cint = cint(0)
  673. var size = sizeof(res).SockLen
  674. if getsockopt(socket, cint(level), cint(optname),
  675. addr(res), addr(size)) < 0'i32:
  676. raiseOSError(osLastError())
  677. result = int(res)
  678. proc setSockOptInt*(socket: SocketHandle, level, optname, optval: int) {.
  679. tags: [WriteIOEffect].} =
  680. ## setsockopt for integer options.
  681. var value = cint(optval)
  682. if setsockopt(socket, cint(level), cint(optname), addr(value),
  683. sizeof(value).SockLen) < 0'i32:
  684. raiseOSError(osLastError())
  685. proc setBlocking*(s: SocketHandle, blocking: bool) =
  686. ## Sets blocking mode on socket.
  687. ##
  688. ## Raises OSError on error.
  689. when useWinVersion:
  690. var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
  691. if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
  692. raiseOSError(osLastError())
  693. else: # BSD sockets
  694. var x: int = fcntl(s, F_GETFL, 0)
  695. if x == -1:
  696. raiseOSError(osLastError())
  697. else:
  698. var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
  699. if fcntl(s, F_SETFL, mode) == -1:
  700. raiseOSError(osLastError())
  701. proc timeValFromMilliseconds(timeout = 500): Timeval =
  702. ## Converts a timeout in milliseconds to a Timeval.
  703. result = default(Timeval)
  704. if timeout != -1:
  705. var seconds = timeout div 1000
  706. when useWinVersion:
  707. result.tv_sec = seconds.int32
  708. result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
  709. else:
  710. result.tv_sec = seconds.Time
  711. result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds
  712. proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) =
  713. FD_ZERO(fd)
  714. for i in items(s):
  715. m = max(m, int(i))
  716. FD_SET(i, fd)
  717. proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) =
  718. var i = 0
  719. var L = s.len
  720. while i < L:
  721. if FD_ISSET(s[i], fd) == 0'i32:
  722. s[i] = s[L-1]
  723. dec(L)
  724. else:
  725. inc(i)
  726. setLen(s, L)
  727. proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int =
  728. ## When a socket in `readfds` is ready to be read from then a non-zero
  729. ## value will be returned specifying the count of the sockets which can be
  730. ## read from. The sockets which cannot be read from will also be removed
  731. ## from `readfds`.
  732. ##
  733. ## `timeout` is specified in milliseconds and `-1` can be specified for
  734. ## an unlimited time.
  735. var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
  736. var rd: TFdSet = default(TFdSet)
  737. var m = 0
  738. createFdSet((rd), readfds, m)
  739. if timeout != -1:
  740. result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
  741. else:
  742. result = int(select(cint(m+1), addr(rd), nil, nil, nil))
  743. pruneSocketSet(readfds, (rd))
  744. proc selectWrite*(writefds: var seq[SocketHandle],
  745. timeout = 500): int {.tags: [ReadIOEffect].} =
  746. ## When a socket in `writefds` is ready to be written to then a non-zero
  747. ## value will be returned specifying the count of the sockets which can be
  748. ## written to. The sockets which cannot be written to will also be removed
  749. ## from `writefds`.
  750. ##
  751. ## `timeout` is specified in milliseconds and `-1` can be specified for
  752. ## an unlimited time.
  753. var tv {.noinit.}: Timeval = timeValFromMilliseconds(timeout)
  754. var wr: TFdSet = default(TFdSet)
  755. var m = 0
  756. createFdSet((wr), writefds, m)
  757. if timeout != -1:
  758. result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
  759. else:
  760. result = int(select(cint(m+1), nil, addr(wr), nil, nil))
  761. pruneSocketSet(writefds, (wr))
  762. proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (SocketHandle, string) =
  763. ## Accepts a new client connection.
  764. ##
  765. ## `inheritable` decides if the resulting SocketHandle can be inherited by
  766. ## child processes.
  767. ##
  768. ## Returns (osInvalidSocket, "") if an error occurred.
  769. var sockAddress: SockAddr = default(SockAddr)
  770. var addrLen = sizeof(sockAddress).SockLen
  771. var sock =
  772. when (defined(linux) or defined(bsd)) and not defined(nimdoc):
  773. accept4(fd, addr(sockAddress), addr(addrLen),
  774. if inheritable: 0 else: SOCK_CLOEXEC)
  775. else:
  776. accept(fd, addr(sockAddress), addr(addrLen))
  777. when declared(setInheritable) and not (defined(linux) or defined(bsd)):
  778. if not setInheritable(sock, inheritable):
  779. close sock
  780. sock = osInvalidSocket
  781. if sock == osInvalidSocket:
  782. return (osInvalidSocket, "")
  783. else:
  784. when useNimNetLite:
  785. var name = sockAddrToStr(addr sockAddress)
  786. return (sock, name)
  787. else:
  788. return (sock, $inet_ntoa(cast[Sockaddr_in](sockAddress).sin_addr))
  789. when defined(windows):
  790. var wsa: WSAData
  791. if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())