nativesockets.nim 27 KB

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