net.nim 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720
  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 high-level cross-platform sockets interface.
  10. ## The procedures implemented in this module are primarily for blocking sockets.
  11. ## For asynchronous non-blocking sockets use the ``asyncnet`` module together
  12. ## with the ``asyncdispatch`` module.
  13. ##
  14. ## The first thing you will always need to do in order to start using sockets,
  15. ## is to create a new instance of the ``Socket`` type using the ``newSocket``
  16. ## procedure.
  17. ##
  18. ## SSL
  19. ## ====
  20. ##
  21. ## In order to use the SSL procedures defined in this module, you will need to
  22. ## compile your application with the ``-d:ssl`` flag.
  23. ##
  24. ## Examples
  25. ## ========
  26. ##
  27. ## Connecting to a server
  28. ## ----------------------
  29. ##
  30. ## After you create a socket with the ``newSocket`` procedure, you can easily
  31. ## connect it to a server running at a known hostname (or IP address) and port.
  32. ## To do so over TCP, use the example below.
  33. ##
  34. ## .. code-block:: Nim
  35. ## var socket = newSocket()
  36. ## socket.connect("google.com", Port(80))
  37. ##
  38. ## UDP is a connectionless protocol, so UDP sockets don't have to explicitly
  39. ## call the ``connect`` procedure. They can simply start sending data
  40. ## immediately.
  41. ##
  42. ## .. code-block:: Nim
  43. ## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
  44. ## socket.sendTo("192.168.0.1", Port(27960), "status\n")
  45. ##
  46. ## Creating a server
  47. ## -----------------
  48. ##
  49. ## After you create a socket with the ``newSocket`` procedure, you can create a
  50. ## TCP server by calling the ``bindAddr`` and ``listen`` procedures.
  51. ##
  52. ## .. code-block:: Nim
  53. ## var socket = newSocket()
  54. ## socket.bindAddr(Port(1234))
  55. ## socket.listen()
  56. ##
  57. ## You can then begin accepting connections using the ``accept`` procedure.
  58. ##
  59. ## .. code-block:: Nim
  60. ## var client: Socket
  61. ## var address = ""
  62. ## while true:
  63. ## socket.acceptAddr(client, address)
  64. ## echo("Client connected from: ", address)
  65. {.deadCodeElim: on.} # dce option deprecated
  66. import nativesockets, os, strutils, parseutils, times, sets, options,
  67. std/monotimes
  68. export nativesockets.Port, nativesockets.`$`, nativesockets.`==`
  69. export Domain, SockType, Protocol
  70. const useWinVersion = defined(Windows) or defined(nimdoc)
  71. const defineSsl = defined(ssl) or defined(nimdoc)
  72. when defineSsl:
  73. import openssl
  74. # Note: The enumerations are mapped to Window's constants.
  75. when defineSsl:
  76. type
  77. SslError* = object of Exception
  78. SslCVerifyMode* = enum
  79. CVerifyNone, CVerifyPeer
  80. SslProtVersion* = enum
  81. protSSLv2, protSSLv3, protTLSv1, protSSLv23
  82. SslContext* = ref object
  83. context*: SslCtx
  84. referencedData: HashSet[int]
  85. extraInternal: SslContextExtraInternal
  86. SslAcceptResult* = enum
  87. AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
  88. SslHandshakeType* = enum
  89. handshakeAsClient, handshakeAsServer
  90. SslClientGetPskFunc* = proc(hint: string): tuple[identity: string, psk: string]
  91. SslServerGetPskFunc* = proc(identity: string): string
  92. SslContextExtraInternal = ref object of RootRef
  93. serverGetPskFunc: SslServerGetPskFunc
  94. clientGetPskFunc: SslClientGetPskFunc
  95. else:
  96. type
  97. SslContext* = void # TODO: Workaround #4797.
  98. const
  99. BufferSize*: int = 4000 ## size of a buffered socket's buffer
  100. MaxLineLength* = 1_000_000
  101. type
  102. SocketImpl* = object ## socket type
  103. fd: SocketHandle
  104. isBuffered: bool # determines whether this socket is buffered.
  105. buffer: array[0..BufferSize, char]
  106. currPos: int # current index in buffer
  107. bufLen: int # current length of buffer
  108. when defineSsl:
  109. isSsl: bool
  110. sslHandle: SslPtr
  111. sslContext: SslContext
  112. sslNoHandshake: bool # True if needs handshake.
  113. sslHasPeekChar: bool
  114. sslPeekChar: char
  115. lastError: OSErrorCode ## stores the last error on this socket
  116. domain: Domain
  117. sockType: SockType
  118. protocol: Protocol
  119. Socket* = ref SocketImpl
  120. SOBool* = enum ## Boolean socket options.
  121. OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
  122. OptOOBInline, OptReuseAddr, OptReusePort, OptNoDelay
  123. ReadLineResult* = enum ## result for readLineAsync
  124. ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
  125. TimeoutError* = object of Exception
  126. SocketFlag* {.pure.} = enum
  127. Peek,
  128. SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
  129. when defined(nimHasStyleChecks):
  130. {.push styleChecks: off.}
  131. type
  132. IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
  133. IPv6, ## IPv6 address
  134. IPv4 ## IPv4 address
  135. IpAddress* = object ## stores an arbitrary IP address
  136. case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
  137. of IpAddressFamily.IPv6:
  138. address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
  139. ## case of IPv6
  140. of IpAddressFamily.IPv4:
  141. address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
  142. ## case of IPv4
  143. when defined(nimHasStyleChecks):
  144. {.pop.}
  145. proc socketError*(socket: Socket, err: int = -1, async = false,
  146. lastError = (-1).OSErrorCode): void {.gcsafe.}
  147. proc isDisconnectionError*(flags: set[SocketFlag],
  148. lastError: OSErrorCode): bool =
  149. ## Determines whether ``lastError`` is a disconnection error. Only does this
  150. ## if flags contains ``SafeDisconn``.
  151. when useWinVersion:
  152. SocketFlag.SafeDisconn in flags and
  153. lastError.int32 in {WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
  154. WSAEDISCON, ERROR_NETNAME_DELETED}
  155. else:
  156. SocketFlag.SafeDisconn in flags and
  157. lastError.int32 in {ECONNRESET, EPIPE, ENETRESET}
  158. proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
  159. ## Converts the flags into the underlying OS representation.
  160. for f in socketFlags:
  161. case f
  162. of SocketFlag.Peek:
  163. result = result or MSG_PEEK
  164. of SocketFlag.SafeDisconn: continue
  165. proc newSocket*(fd: SocketHandle, domain: Domain = AF_INET,
  166. sockType: SockType = SOCK_STREAM,
  167. protocol: Protocol = IPPROTO_TCP, buffered = true): owned(Socket) =
  168. ## Creates a new socket as specified by the params.
  169. assert fd != osInvalidSocket
  170. result = Socket(
  171. fd: fd,
  172. isBuffered: buffered,
  173. domain: domain,
  174. sockType: sockType,
  175. protocol: protocol)
  176. if buffered:
  177. result.currPos = 0
  178. # Set SO_NOSIGPIPE on OS X.
  179. when defined(macosx) and not defined(nimdoc):
  180. setSockOptInt(fd, SOL_SOCKET, SO_NOSIGPIPE, 1)
  181. proc newSocket*(domain, sockType, protocol: cint, buffered = true): owned(Socket) =
  182. ## Creates a new socket.
  183. ##
  184. ## If an error occurs OSError will be raised.
  185. let fd = createNativeSocket(domain, sockType, protocol)
  186. if fd == osInvalidSocket:
  187. raiseOSError(osLastError())
  188. result = newSocket(fd, domain.Domain, sockType.SockType, protocol.Protocol,
  189. buffered)
  190. proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
  191. protocol: Protocol = IPPROTO_TCP, buffered = true): owned(Socket) =
  192. ## Creates a new socket.
  193. ##
  194. ## If an error occurs OSError will be raised.
  195. let fd = createNativeSocket(domain, sockType, protocol)
  196. if fd == osInvalidSocket:
  197. raiseOSError(osLastError())
  198. result = newSocket(fd, domain, sockType, protocol, buffered)
  199. proc parseIPv4Address(addressStr: string): IpAddress =
  200. ## Parses IPv4 addresses
  201. ## Raises ValueError on errors
  202. var
  203. byteCount = 0
  204. currentByte: uint16 = 0
  205. separatorValid = false
  206. result = IpAddress(family: IpAddressFamily.IPv4)
  207. for i in 0 .. high(addressStr):
  208. if addressStr[i] in strutils.Digits: # Character is a number
  209. currentByte = currentByte * 10 +
  210. cast[uint16](ord(addressStr[i]) - ord('0'))
  211. if currentByte > 255'u16:
  212. raise newException(ValueError,
  213. "Invalid IP Address. Value is out of range")
  214. separatorValid = true
  215. elif addressStr[i] == '.': # IPv4 address separator
  216. if not separatorValid or byteCount >= 3:
  217. raise newException(ValueError,
  218. "Invalid IP Address. The address consists of too many groups")
  219. result.address_v4[byteCount] = cast[uint8](currentByte)
  220. currentByte = 0
  221. byteCount.inc
  222. separatorValid = false
  223. else:
  224. raise newException(ValueError,
  225. "Invalid IP Address. Address contains an invalid character")
  226. if byteCount != 3 or not separatorValid:
  227. raise newException(ValueError, "Invalid IP Address")
  228. result.address_v4[byteCount] = cast[uint8](currentByte)
  229. proc parseIPv6Address(addressStr: string): IpAddress =
  230. ## Parses IPv6 addresses
  231. ## Raises ValueError on errors
  232. result = IpAddress(family: IpAddressFamily.IPv6)
  233. if addressStr.len < 2:
  234. raise newException(ValueError, "Invalid IP Address")
  235. var
  236. groupCount = 0
  237. currentGroupStart = 0
  238. currentShort: uint32 = 0
  239. separatorValid = true
  240. dualColonGroup = -1
  241. lastWasColon = false
  242. v4StartPos = -1
  243. byteCount = 0
  244. for i, c in addressStr:
  245. if c == ':':
  246. if not separatorValid:
  247. raise newException(ValueError,
  248. "Invalid IP Address. Address contains an invalid separator")
  249. if lastWasColon:
  250. if dualColonGroup != -1:
  251. raise newException(ValueError,
  252. "Invalid IP Address. Address contains more than one \"::\" separator")
  253. dualColonGroup = groupCount
  254. separatorValid = false
  255. elif i != 0 and i != high(addressStr):
  256. if groupCount >= 8:
  257. raise newException(ValueError,
  258. "Invalid IP Address. The address consists of too many groups")
  259. result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
  260. result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
  261. currentShort = 0
  262. groupCount.inc()
  263. if dualColonGroup != -1: separatorValid = false
  264. elif i == 0: # only valid if address starts with ::
  265. if addressStr[1] != ':':
  266. raise newException(ValueError,
  267. "Invalid IP Address. Address may not start with \":\"")
  268. else: # i == high(addressStr) - only valid if address ends with ::
  269. if addressStr[high(addressStr)-1] != ':':
  270. raise newException(ValueError,
  271. "Invalid IP Address. Address may not end with \":\"")
  272. lastWasColon = true
  273. currentGroupStart = i + 1
  274. elif c == '.': # Switch to parse IPv4 mode
  275. if i < 3 or not separatorValid or groupCount >= 7:
  276. raise newException(ValueError, "Invalid IP Address")
  277. v4StartPos = currentGroupStart
  278. currentShort = 0
  279. separatorValid = false
  280. break
  281. elif c in strutils.HexDigits:
  282. if c in strutils.Digits: # Normal digit
  283. currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
  284. elif c >= 'a' and c <= 'f': # Lower case hex
  285. currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
  286. else: # Upper case hex
  287. currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
  288. if currentShort > 65535'u32:
  289. raise newException(ValueError,
  290. "Invalid IP Address. Value is out of range")
  291. lastWasColon = false
  292. separatorValid = true
  293. else:
  294. raise newException(ValueError,
  295. "Invalid IP Address. Address contains an invalid character")
  296. if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
  297. if separatorValid: # Copy remaining data
  298. if groupCount >= 8:
  299. raise newException(ValueError,
  300. "Invalid IP Address. The address consists of too many groups")
  301. result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
  302. result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
  303. groupCount.inc()
  304. else: # Must parse IPv4 address
  305. for i, c in addressStr[v4StartPos..high(addressStr)]:
  306. if c in strutils.Digits: # Character is a number
  307. currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
  308. if currentShort > 255'u32:
  309. raise newException(ValueError,
  310. "Invalid IP Address. Value is out of range")
  311. separatorValid = true
  312. elif c == '.': # IPv4 address separator
  313. if not separatorValid or byteCount >= 3:
  314. raise newException(ValueError, "Invalid IP Address")
  315. result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
  316. currentShort = 0
  317. byteCount.inc()
  318. separatorValid = false
  319. else: # Invalid character
  320. raise newException(ValueError,
  321. "Invalid IP Address. Address contains an invalid character")
  322. if byteCount != 3 or not separatorValid:
  323. raise newException(ValueError, "Invalid IP Address")
  324. result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
  325. groupCount += 2
  326. # Shift and fill zeros in case of ::
  327. if groupCount > 8:
  328. raise newException(ValueError,
  329. "Invalid IP Address. The address consists of too many groups")
  330. elif groupCount < 8: # must fill
  331. if dualColonGroup == -1:
  332. raise newException(ValueError,
  333. "Invalid IP Address. The address consists of too few groups")
  334. var toFill = 8 - groupCount # The number of groups to fill
  335. var toShift = groupCount - dualColonGroup # Nr of known groups after ::
  336. for i in 0..2*toShift-1: # shift
  337. result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
  338. for i in 0..2*toFill-1: # fill with 0s
  339. result.address_v6[dualColonGroup*2+i] = 0
  340. elif dualColonGroup != -1:
  341. raise newException(ValueError,
  342. "Invalid IP Address. The address consists of too many groups")
  343. proc parseIpAddress*(addressStr: string): IpAddress =
  344. ## Parses an IP address
  345. ## Raises ValueError on error
  346. if addressStr.len == 0:
  347. raise newException(ValueError, "IP Address string is empty")
  348. if addressStr.contains(':'):
  349. return parseIPv6Address(addressStr)
  350. else:
  351. return parseIPv4Address(addressStr)
  352. proc isIpAddress*(addressStr: string): bool {.tags: [].} =
  353. ## Checks if a string is an IP address
  354. ## Returns true if it is, false otherwise
  355. try:
  356. discard parseIpAddress(addressStr)
  357. except ValueError:
  358. return false
  359. return true
  360. proc toSockAddr*(address: IpAddress, port: Port, sa: var Sockaddr_storage,
  361. sl: var SockLen) =
  362. ## Converts `IpAddress` and `Port` to `SockAddr` and `SockLen`
  363. let port = htons(uint16(port))
  364. case address.family
  365. of IpAddressFamily.IPv4:
  366. sl = sizeof(Sockaddr_in).SockLen
  367. let s = cast[ptr Sockaddr_in](addr sa)
  368. s.sin_family = type(s.sin_family)(toInt(AF_INET))
  369. s.sin_port = port
  370. copyMem(addr s.sin_addr, unsafeAddr address.address_v4[0],
  371. sizeof(s.sin_addr))
  372. of IpAddressFamily.IPv6:
  373. sl = sizeof(Sockaddr_in6).SockLen
  374. let s = cast[ptr Sockaddr_in6](addr sa)
  375. s.sin6_family = type(s.sin6_family)(toInt(AF_INET6))
  376. s.sin6_port = port
  377. copyMem(addr s.sin6_addr, unsafeAddr address.address_v6[0],
  378. sizeof(s.sin6_addr))
  379. proc fromSockAddrAux(sa: ptr Sockaddr_storage, sl: SockLen,
  380. address: var IpAddress, port: var Port) =
  381. if sa.ss_family.cint == toInt(AF_INET) and sl == sizeof(Sockaddr_in).SockLen:
  382. address = IpAddress(family: IpAddressFamily.IPv4)
  383. let s = cast[ptr Sockaddr_in](sa)
  384. copyMem(addr address.address_v4[0], addr s.sin_addr,
  385. sizeof(address.address_v4))
  386. port = ntohs(s.sin_port).Port
  387. elif sa.ss_family.cint == toInt(AF_INET6) and
  388. sl == sizeof(Sockaddr_in6).SockLen:
  389. address = IpAddress(family: IpAddressFamily.IPv6)
  390. let s = cast[ptr Sockaddr_in6](sa)
  391. copyMem(addr address.address_v6[0], addr s.sin6_addr,
  392. sizeof(address.address_v6))
  393. port = ntohs(s.sin6_port).Port
  394. else:
  395. raise newException(ValueError, "Neither IPv4 nor IPv6")
  396. proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6,
  397. sl: SockLen, address: var IpAddress, port: var Port) {.inline.} =
  398. ## Converts `SockAddr` and `SockLen` to `IpAddress` and `Port`. Raises
  399. ## `ObjectConversionError` in case of invalid `sa` and `sl` arguments.
  400. fromSockAddrAux(cast[ptr Sockaddr_storage](unsafeAddr sa), sl, address, port)
  401. when defineSsl:
  402. CRYPTO_malloc_init()
  403. doAssert SslLibraryInit() == 1
  404. SSL_load_error_strings()
  405. ERR_load_BIO_strings()
  406. OpenSSL_add_all_algorithms()
  407. proc raiseSSLError*(s = "") =
  408. ## Raises a new SSL error.
  409. if s != "":
  410. raise newException(SslError, s)
  411. let err = ERR_peek_last_error()
  412. if err == 0:
  413. raise newException(SslError, "No error reported.")
  414. if err == -1:
  415. raiseOSError(osLastError())
  416. var errStr = $ERR_error_string(err, nil)
  417. case err
  418. of 336032814, 336032784:
  419. errStr = "Please upgrade your OpenSSL library, it does not support the " &
  420. "necessary protocols. OpenSSL error is: " & errStr
  421. else:
  422. discard
  423. raise newException(SslError, errStr)
  424. proc getExtraData*(ctx: SslContext, index: int): RootRef =
  425. ## Retrieves arbitrary data stored inside SslContext.
  426. if index notin ctx.referencedData:
  427. raise newException(IndexError, "No data with that index.")
  428. let res = ctx.context.SSL_CTX_get_ex_data(index.cint)
  429. if cast[int](res) == 0:
  430. raiseSSLError()
  431. return cast[RootRef](res)
  432. proc setExtraData*(ctx: SslContext, index: int, data: RootRef) =
  433. ## Stores arbitrary data inside SslContext. The unique `index`
  434. ## should be retrieved using getSslContextExtraDataIndex.
  435. if index in ctx.referencedData:
  436. GC_unref(getExtraData(ctx, index))
  437. if ctx.context.SSL_CTX_set_ex_data(index.cint, cast[pointer](data)) == -1:
  438. raiseSSLError()
  439. if index notin ctx.referencedData:
  440. ctx.referencedData.incl(index)
  441. GC_ref(data)
  442. # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html
  443. proc loadCertificates(ctx: SslCtx, certFile, keyFile: string) =
  444. if certFile != "" and not existsFile(certFile):
  445. raise newException(system.IOError,
  446. "Certificate file could not be found: " & certFile)
  447. if keyFile != "" and not existsFile(keyFile):
  448. raise newException(system.IOError, "Key file could not be found: " & keyFile)
  449. if certFile != "":
  450. var ret = SSL_CTX_use_certificate_chain_file(ctx, certFile)
  451. if ret != 1:
  452. raiseSSLError()
  453. # TODO: Password? www.rtfm.com/openssl-examples/part1.pdf
  454. if keyFile != "":
  455. if SSL_CTX_use_PrivateKey_file(ctx, keyFile,
  456. SSL_FILETYPE_PEM) != 1:
  457. raiseSSLError()
  458. if SSL_CTX_check_private_key(ctx) != 1:
  459. raiseSSLError("Verification of private key file failed.")
  460. proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
  461. certFile = "", keyFile = "", cipherList = "ALL"): SslContext =
  462. ## Creates an SSL context.
  463. ##
  464. ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1
  465. ## are available with the addition of ``protSSLv23`` which allows for
  466. ## compatibility with all of them.
  467. ##
  468. ## There are currently only two options for verify mode;
  469. ## one is ``CVerifyNone`` and with it certificates will not be verified
  470. ## the other is ``CVerifyPeer`` and certificates will be verified for
  471. ## it, ``CVerifyPeer`` is the safest choice.
  472. ##
  473. ## The last two parameters specify the certificate file path and the key file
  474. ## path, a server socket will most likely not work without these.
  475. ##
  476. ## Certificates can be generated using the following command:
  477. ## - ``openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout mykey.pem -out mycert.pem``
  478. ## or using ECDSA:
  479. ## - ``openssl ecparam -out mykey.pem -name secp256k1 -genkey``
  480. ## - ``openssl req -new -key mykey.pem -x509 -nodes -days 365 -out mycert.pem``
  481. var newCTX: SslCtx
  482. case protVersion
  483. of protSSLv23:
  484. newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support.
  485. of protSSLv2:
  486. raiseSSLError("SSLv2 is no longer secure and has been deprecated, use protSSLv23")
  487. of protSSLv3:
  488. raiseSSLError("SSLv3 is no longer secure and has been deprecated, use protSSLv23")
  489. of protTLSv1:
  490. newCTX = SSL_CTX_new(TLSv1_method())
  491. if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1:
  492. raiseSSLError()
  493. case verifyMode
  494. of CVerifyPeer:
  495. newCTX.SSL_CTX_set_verify(SSL_VERIFY_PEER, nil)
  496. of CVerifyNone:
  497. newCTX.SSL_CTX_set_verify(SSL_VERIFY_NONE, nil)
  498. if newCTX == nil:
  499. raiseSSLError()
  500. discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
  501. newCTX.loadCertificates(certFile, keyFile)
  502. result = SslContext(context: newCTX, referencedData: initSet[int](),
  503. extraInternal: new(SslContextExtraInternal))
  504. proc getExtraInternal(ctx: SslContext): SslContextExtraInternal =
  505. return ctx.extraInternal
  506. proc destroyContext*(ctx: SslContext) =
  507. ## Free memory referenced by SslContext.
  508. # We assume here that OpenSSL's internal indexes increase by 1 each time.
  509. # That means we can assume that the next internal index is the length of
  510. # extra data indexes.
  511. for i in ctx.referencedData:
  512. GC_unref(getExtraData(ctx, i).RootRef)
  513. ctx.context.SSL_CTX_free()
  514. proc `pskIdentityHint=`*(ctx: SslContext, hint: string) =
  515. ## Sets the identity hint passed to server.
  516. ##
  517. ## Only used in PSK ciphersuites.
  518. if ctx.context.SSL_CTX_use_psk_identity_hint(hint) <= 0:
  519. raiseSSLError()
  520. proc clientGetPskFunc*(ctx: SslContext): SslClientGetPskFunc =
  521. return ctx.getExtraInternal().clientGetPskFunc
  522. proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring;
  523. max_identity_len: cuint; psk: ptr cuchar;
  524. max_psk_len: cuint): cuint {.cdecl.} =
  525. let ctx = SslContext(context: ssl.SSL_get_SSL_CTX)
  526. let hintString = if hint == nil: "" else: $hint
  527. let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString)
  528. if psk.len.cuint > max_psk_len:
  529. return 0
  530. if identityString.len.cuint >= max_identity_len:
  531. return 0
  532. copyMem(identity, identityString.cstring, pskString.len + 1) # with the last zero byte
  533. copyMem(psk, pskString.cstring, pskString.len)
  534. return pskString.len.cuint
  535. proc `clientGetPskFunc=`*(ctx: SslContext, fun: SslClientGetPskFunc) =
  536. ## Sets function that returns the client identity and the PSK based on identity
  537. ## hint from the server.
  538. ##
  539. ## Only used in PSK ciphersuites.
  540. ctx.getExtraInternal().clientGetPskFunc = fun
  541. ctx.context.SSL_CTX_set_psk_client_callback(
  542. if fun == nil: nil else: pskClientCallback)
  543. proc serverGetPskFunc*(ctx: SslContext): SslServerGetPskFunc =
  544. return ctx.getExtraInternal().serverGetPskFunc
  545. proc pskServerCallback(ssl: SslCtx; identity: cstring; psk: ptr cuchar;
  546. max_psk_len: cint): cuint {.cdecl.} =
  547. let ctx = SslContext(context: ssl.SSL_get_SSL_CTX)
  548. let pskString = (ctx.serverGetPskFunc)($identity)
  549. if psk.len.cint > max_psk_len:
  550. return 0
  551. copyMem(psk, pskString.cstring, pskString.len)
  552. return pskString.len.cuint
  553. proc `serverGetPskFunc=`*(ctx: SslContext, fun: SslServerGetPskFunc) =
  554. ## Sets function that returns PSK based on the client identity.
  555. ##
  556. ## Only used in PSK ciphersuites.
  557. ctx.getExtraInternal().serverGetPskFunc = fun
  558. ctx.context.SSL_CTX_set_psk_server_callback(if fun == nil: nil
  559. else: pskServerCallback)
  560. proc getPskIdentity*(socket: Socket): string =
  561. ## Gets the PSK identity provided by the client.
  562. assert socket.isSsl
  563. return $(socket.sslHandle.SSL_get_psk_identity)
  564. proc wrapSocket*(ctx: SslContext, socket: Socket) =
  565. ## Wraps a socket in an SSL context. This function effectively turns
  566. ## ``socket`` into an SSL socket.
  567. ##
  568. ## This must be called on an unconnected socket; an SSL session will
  569. ## be started when the socket is connected.
  570. ##
  571. ## **Disclaimer**: This code is not well tested, may be very unsafe and
  572. ## prone to security vulnerabilities.
  573. assert(not socket.isSsl)
  574. socket.isSsl = true
  575. socket.sslContext = ctx
  576. socket.sslHandle = SSL_new(socket.sslContext.context)
  577. socket.sslNoHandshake = false
  578. socket.sslHasPeekChar = false
  579. if socket.sslHandle == nil:
  580. raiseSSLError()
  581. if SSL_set_fd(socket.sslHandle, socket.fd) != 1:
  582. raiseSSLError()
  583. proc wrapConnectedSocket*(ctx: SslContext, socket: Socket,
  584. handshake: SslHandshakeType,
  585. hostname: string = "") =
  586. ## Wraps a connected socket in an SSL context. This function effectively
  587. ## turns ``socket`` into an SSL socket.
  588. ## ``hostname`` should be specified so that the client knows which hostname
  589. ## the server certificate should be validated against.
  590. ##
  591. ## This should be called on a connected socket, and will perform
  592. ## an SSL handshake immediately.
  593. ##
  594. ## **Disclaimer**: This code is not well tested, may be very unsafe and
  595. ## prone to security vulnerabilities.
  596. wrapSocket(ctx, socket)
  597. case handshake
  598. of handshakeAsClient:
  599. if hostname.len > 0 and not isIpAddress(hostname):
  600. # Discard result in case OpenSSL version doesn't support SNI, or we're
  601. # not using TLSv1+
  602. discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
  603. let ret = SSL_connect(socket.sslHandle)
  604. socketError(socket, ret)
  605. of handshakeAsServer:
  606. let ret = SSL_accept(socket.sslHandle)
  607. socketError(socket, ret)
  608. proc getSocketError*(socket: Socket): OSErrorCode =
  609. ## Checks ``osLastError`` for a valid error. If it has been reset it uses
  610. ## the last error stored in the socket object.
  611. result = osLastError()
  612. if result == 0.OSErrorCode:
  613. result = socket.lastError
  614. if result == 0.OSErrorCode:
  615. raiseOSError(result, "No valid socket error code available")
  616. proc socketError*(socket: Socket, err: int = -1, async = false,
  617. lastError = (-1).OSErrorCode) =
  618. ## Raises an OSError based on the error code returned by ``SSL_get_error``
  619. ## (for SSL sockets) and ``osLastError`` otherwise.
  620. ##
  621. ## If ``async`` is ``true`` no error will be thrown in the case when the
  622. ## error was caused by no data being available to be read.
  623. ##
  624. ## If ``err`` is not lower than 0 no exception will be raised.
  625. when defineSsl:
  626. if socket.isSsl:
  627. if err <= 0:
  628. var ret = SSL_get_error(socket.sslHandle, err.cint)
  629. case ret
  630. of SSL_ERROR_ZERO_RETURN:
  631. raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
  632. of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
  633. if async:
  634. return
  635. else: raiseSSLError("Not enough data on socket.")
  636. of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
  637. if async:
  638. return
  639. else: raiseSSLError("Not enough data on socket.")
  640. of SSL_ERROR_WANT_X509_LOOKUP:
  641. raiseSSLError("Function for x509 lookup has been called.")
  642. of SSL_ERROR_SYSCALL:
  643. var errStr = "IO error has occurred "
  644. let sslErr = ERR_peek_last_error()
  645. if sslErr == 0 and err == 0:
  646. errStr.add "because an EOF was observed that violates the protocol"
  647. elif sslErr == 0 and err == -1:
  648. errStr.add "in the BIO layer"
  649. else:
  650. let errStr = $ERR_error_string(sslErr, nil)
  651. raiseSSLError(errStr & ": " & errStr)
  652. let osErr = osLastError()
  653. raiseOSError(osErr, errStr)
  654. of SSL_ERROR_SSL:
  655. raiseSSLError()
  656. else: raiseSSLError("Unknown Error")
  657. if err == -1 and not (when defineSsl: socket.isSsl else: false):
  658. var lastE = if lastError.int == -1: getSocketError(socket) else: lastError
  659. if async:
  660. when useWinVersion:
  661. if lastE.int32 == WSAEWOULDBLOCK:
  662. return
  663. else: raiseOSError(lastE)
  664. else:
  665. if lastE.int32 == EAGAIN or lastE.int32 == EWOULDBLOCK:
  666. return
  667. else: raiseOSError(lastE)
  668. else: raiseOSError(lastE)
  669. proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
  670. ## Marks ``socket`` as accepting connections.
  671. ## ``Backlog`` specifies the maximum length of the
  672. ## queue of pending connections.
  673. ##
  674. ## Raises an OSError error upon failure.
  675. if nativesockets.listen(socket.fd, backlog) < 0'i32:
  676. raiseOSError(osLastError())
  677. proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
  678. tags: [ReadIOEffect].} =
  679. ## Binds ``address``:``port`` to the socket.
  680. ##
  681. ## If ``address`` is "" then ADDR_ANY will be bound.
  682. var realaddr = address
  683. if realaddr == "":
  684. case socket.domain
  685. of AF_INET6: realaddr = "::"
  686. of AF_INET: realaddr = "0.0.0.0"
  687. else:
  688. raise newException(ValueError,
  689. "Unknown socket address family and no address specified to bindAddr")
  690. var aiList = getAddrInfo(realaddr, port, socket.domain)
  691. if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32:
  692. freeaddrinfo(aiList)
  693. raiseOSError(osLastError())
  694. freeaddrinfo(aiList)
  695. proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string,
  696. flags = {SocketFlag.SafeDisconn}) {.
  697. tags: [ReadIOEffect], gcsafe, locks: 0.} =
  698. ## Blocks until a connection is being made from a client. When a connection
  699. ## is made sets ``client`` to the client socket and ``address`` to the address
  700. ## of the connecting client.
  701. ## This function will raise OSError if an error occurs.
  702. ##
  703. ## The resulting client will inherit any properties of the server socket. For
  704. ## example: whether the socket is buffered or not.
  705. ##
  706. ## The ``accept`` call may result in an error if the connecting socket
  707. ## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
  708. ## flag is specified then this error will not be raised and instead
  709. ## accept will be called again.
  710. if client.isNil:
  711. new(client)
  712. let ret = accept(server.fd)
  713. let sock = ret[0]
  714. if sock == osInvalidSocket:
  715. let err = osLastError()
  716. if flags.isDisconnectionError(err):
  717. acceptAddr(server, client, address, flags)
  718. raiseOSError(err)
  719. else:
  720. address = ret[1]
  721. client.fd = sock
  722. client.domain = getSockDomain(sock)
  723. client.isBuffered = server.isBuffered
  724. # Handle SSL.
  725. when defineSsl:
  726. if server.isSsl:
  727. # We must wrap the client sock in a ssl context.
  728. server.sslContext.wrapSocket(client)
  729. let ret = SSL_accept(client.sslHandle)
  730. socketError(client, ret, false)
  731. when false: #defineSsl:
  732. proc acceptAddrSSL*(server: Socket, client: var Socket,
  733. address: var string): SSL_acceptResult {.
  734. tags: [ReadIOEffect].} =
  735. ## This procedure should only be used for non-blocking **SSL** sockets.
  736. ## It will immediately return with one of the following values:
  737. ##
  738. ## ``AcceptSuccess`` will be returned when a client has been successfully
  739. ## accepted and the handshake has been successfully performed between
  740. ## ``server`` and the newly connected client.
  741. ##
  742. ## ``AcceptNoHandshake`` will be returned when a client has been accepted
  743. ## but no handshake could be performed. This can happen when the client
  744. ## connects but does not yet initiate a handshake. In this case
  745. ## ``acceptAddrSSL`` should be called again with the same parameters.
  746. ##
  747. ## ``AcceptNoClient`` will be returned when no client is currently attempting
  748. ## to connect.
  749. template doHandshake(): untyped =
  750. when defineSsl:
  751. if server.isSsl:
  752. client.setBlocking(false)
  753. # We must wrap the client sock in a ssl context.
  754. if not client.isSsl or client.sslHandle == nil:
  755. server.sslContext.wrapSocket(client)
  756. let ret = SSL_accept(client.sslHandle)
  757. while ret <= 0:
  758. let err = SSL_get_error(client.sslHandle, ret)
  759. if err != SSL_ERROR_WANT_ACCEPT:
  760. case err
  761. of SSL_ERROR_ZERO_RETURN:
  762. raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
  763. of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
  764. SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
  765. client.sslNoHandshake = true
  766. return AcceptNoHandshake
  767. of SSL_ERROR_WANT_X509_LOOKUP:
  768. raiseSSLError("Function for x509 lookup has been called.")
  769. of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
  770. raiseSSLError()
  771. else:
  772. raiseSSLError("Unknown error")
  773. client.sslNoHandshake = false
  774. if client.isSsl and client.sslNoHandshake:
  775. doHandshake()
  776. return AcceptSuccess
  777. else:
  778. acceptAddrPlain(AcceptNoClient, AcceptSuccess):
  779. doHandshake()
  780. proc accept*(server: Socket, client: var owned(Socket),
  781. flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} =
  782. ## Equivalent to ``acceptAddr`` but doesn't return the address, only the
  783. ## socket.
  784. ##
  785. ## The ``accept`` call may result in an error if the connecting socket
  786. ## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
  787. ## flag is specified then this error will not be raised and instead
  788. ## accept will be called again.
  789. var addrDummy = ""
  790. acceptAddr(server, client, addrDummy, flags)
  791. proc close*(socket: Socket) =
  792. ## Closes a socket.
  793. try:
  794. when defineSsl:
  795. if socket.isSsl and socket.sslHandle != nil:
  796. ErrClearError()
  797. # As we are closing the underlying socket immediately afterwards,
  798. # it is valid, under the TLS standard, to perform a unidirectional
  799. # shutdown i.e not wait for the peers "close notify" alert with a second
  800. # call to SSL_shutdown
  801. let res = SSL_shutdown(socket.sslHandle)
  802. if res == 0:
  803. discard
  804. elif res != 1:
  805. socketError(socket, res)
  806. finally:
  807. when defineSsl:
  808. if socket.isSsl and socket.sslHandle != nil:
  809. SSL_free(socket.sslHandle)
  810. socket.sslHandle = nil
  811. socket.fd.close()
  812. socket.fd = osInvalidSocket
  813. when defined(posix):
  814. from posix import TCP_NODELAY
  815. else:
  816. from winlean import TCP_NODELAY
  817. proc toCInt*(opt: SOBool): cint =
  818. ## Converts a ``SOBool`` into its Socket Option cint representation.
  819. case opt
  820. of OptAcceptConn: SO_ACCEPTCONN
  821. of OptBroadcast: SO_BROADCAST
  822. of OptDebug: SO_DEBUG
  823. of OptDontRoute: SO_DONTROUTE
  824. of OptKeepAlive: SO_KEEPALIVE
  825. of OptOOBInline: SO_OOBINLINE
  826. of OptReuseAddr: SO_REUSEADDR
  827. of OptReusePort: SO_REUSEPORT
  828. of OptNoDelay: TCP_NODELAY
  829. proc getSockOpt*(socket: Socket, opt: SOBool, level = SOL_SOCKET): bool {.
  830. tags: [ReadIOEffect].} =
  831. ## Retrieves option ``opt`` as a boolean value.
  832. var res = getSockOptInt(socket.fd, cint(level), toCInt(opt))
  833. result = res != 0
  834. proc getLocalAddr*(socket: Socket): (string, Port) =
  835. ## Get the socket's local address and port number.
  836. ##
  837. ## This is high-level interface for `getsockname`:idx:.
  838. getLocalAddr(socket.fd, socket.domain)
  839. proc getPeerAddr*(socket: Socket): (string, Port) =
  840. ## Get the socket's peer address and port number.
  841. ##
  842. ## This is high-level interface for `getpeername`:idx:.
  843. getPeerAddr(socket.fd, socket.domain)
  844. proc setSockOpt*(socket: Socket, opt: SOBool, value: bool,
  845. level = SOL_SOCKET) {.tags: [WriteIOEffect].} =
  846. ## Sets option ``opt`` to a boolean value specified by ``value``.
  847. ##
  848. ## .. code-block:: Nim
  849. ## var socket = newSocket()
  850. ## socket.setSockOpt(OptReusePort, true)
  851. ## socket.setSockOpt(OptNoDelay, true, level=IPPROTO_TCP.toInt)
  852. ##
  853. var valuei = cint(if value: 1 else: 0)
  854. setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
  855. when defined(posix) or defined(nimdoc):
  856. proc connectUnix*(socket: Socket, path: string) =
  857. ## Connects to Unix socket on `path`.
  858. ## This only works on Unix-style systems: Mac OS X, BSD and Linux
  859. when not defined(nimdoc):
  860. var socketAddr = makeUnixAddr(path)
  861. if socket.fd.connect(cast[ptr SockAddr](addr socketAddr),
  862. (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32:
  863. raiseOSError(osLastError())
  864. proc bindUnix*(socket: Socket, path: string) =
  865. ## Binds Unix socket to `path`.
  866. ## This only works on Unix-style systems: Mac OS X, BSD and Linux
  867. when not defined(nimdoc):
  868. var socketAddr = makeUnixAddr(path)
  869. if socket.fd.bindAddr(cast[ptr SockAddr](addr socketAddr),
  870. (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32:
  871. raiseOSError(osLastError())
  872. when defined(ssl):
  873. proc gotHandshake*(socket: Socket): bool =
  874. ## Determines whether a handshake has occurred between a client (``socket``)
  875. ## and the server that ``socket`` is connected to.
  876. ##
  877. ## Throws SslError if ``socket`` is not an SSL socket.
  878. if socket.isSsl:
  879. return not socket.sslNoHandshake
  880. else:
  881. raiseSSLError("Socket is not an SSL socket.")
  882. proc hasDataBuffered*(s: Socket): bool =
  883. ## Determines whether a socket has data buffered.
  884. result = false
  885. if s.isBuffered:
  886. result = s.bufLen > 0 and s.currPos != s.bufLen
  887. when defineSsl:
  888. if s.isSsl and not result:
  889. result = s.sslHasPeekChar
  890. proc select(readfd: Socket, timeout = 500): int =
  891. ## Used for socket operation timeouts.
  892. if readfd.hasDataBuffered:
  893. return 1
  894. var fds = @[readfd.fd]
  895. result = selectRead(fds, timeout)
  896. proc isClosed(socket: Socket): bool =
  897. socket.fd == osInvalidSocket
  898. proc uniRecv(socket: Socket, buffer: pointer, size, flags: cint): int =
  899. ## Handles SSL and non-ssl recv in a nice package.
  900. ##
  901. ## In particular handles the case where socket has been closed properly
  902. ## for both SSL and non-ssl.
  903. result = 0
  904. assert(not socket.isClosed, "Cannot `recv` on a closed socket")
  905. when defineSsl:
  906. if socket.isSsl:
  907. return SSL_read(socket.sslHandle, buffer, size)
  908. return recv(socket.fd, buffer, size, flags)
  909. proc readIntoBuf(socket: Socket, flags: int32): int =
  910. result = 0
  911. result = uniRecv(socket, addr(socket.buffer), socket.buffer.high, flags)
  912. if result < 0:
  913. # Save it in case it gets reset (the Nim codegen occasionally may call
  914. # Win API functions which reset it).
  915. socket.lastError = osLastError()
  916. if result <= 0:
  917. socket.bufLen = 0
  918. socket.currPos = 0
  919. return result
  920. socket.bufLen = result
  921. socket.currPos = 0
  922. template retRead(flags, readBytes: int) {.dirty.} =
  923. let res = socket.readIntoBuf(flags.int32)
  924. if res <= 0:
  925. if readBytes > 0:
  926. return readBytes
  927. else:
  928. return res
  929. proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [
  930. ReadIOEffect].} =
  931. ## Receives data from a socket.
  932. ##
  933. ## **Note**: This is a low-level function, you may be interested in the higher
  934. ## level versions of this function which are also named ``recv``.
  935. if size == 0: return
  936. if socket.isBuffered:
  937. if socket.bufLen == 0:
  938. retRead(0'i32, 0)
  939. var read = 0
  940. while read < size:
  941. if socket.currPos >= socket.bufLen:
  942. retRead(0'i32, read)
  943. let chunk = min(socket.bufLen-socket.currPos, size-read)
  944. var d = cast[cstring](data)
  945. assert size-read >= chunk
  946. copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk)
  947. read.inc(chunk)
  948. socket.currPos.inc(chunk)
  949. result = read
  950. else:
  951. when defineSsl:
  952. if socket.isSsl:
  953. if socket.sslHasPeekChar: # TODO: Merge this peek char mess into uniRecv
  954. copyMem(data, addr(socket.sslPeekChar), 1)
  955. socket.sslHasPeekChar = false
  956. if size-1 > 0:
  957. var d = cast[cstring](data)
  958. result = uniRecv(socket, addr(d[1]), cint(size-1), 0'i32) + 1
  959. else:
  960. result = 1
  961. else:
  962. result = uniRecv(socket, data, size.cint, 0'i32)
  963. else:
  964. result = recv(socket.fd, data, size.cint, 0'i32)
  965. else:
  966. result = recv(socket.fd, data, size.cint, 0'i32)
  967. if result < 0:
  968. # Save the error in case it gets reset.
  969. socket.lastError = osLastError()
  970. proc waitFor(socket: Socket, waited: var Duration, timeout, size: int,
  971. funcName: string): int {.tags: [TimeEffect].} =
  972. ## determines the amount of characters that can be read. Result will never
  973. ## be larger than ``size``. For unbuffered sockets this will be ``1``.
  974. ## For buffered sockets it can be as big as ``BufferSize``.
  975. ##
  976. ## If this function does not determine that there is data on the socket
  977. ## within ``timeout`` ms, a TimeoutError error will be raised.
  978. result = 1
  979. if size <= 0: assert false
  980. if timeout == -1: return size
  981. if socket.isBuffered and socket.bufLen != 0 and
  982. socket.bufLen != socket.currPos:
  983. result = socket.bufLen - socket.currPos
  984. result = min(result, size)
  985. else:
  986. if timeout - waited.inMilliseconds < 1:
  987. raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
  988. when defineSsl:
  989. if socket.isSsl:
  990. if socket.hasDataBuffered:
  991. # sslPeekChar is present.
  992. return 1
  993. let sslPending = SSL_pending(socket.sslHandle)
  994. if sslPending != 0:
  995. return min(sslPending, size)
  996. var startTime = getMonoTime()
  997. let selRet = select(socket, (timeout - waited.inMilliseconds).int)
  998. if selRet < 0: raiseOSError(osLastError())
  999. if selRet != 1:
  1000. raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
  1001. waited += (getMonoTime() - startTime)
  1002. proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
  1003. tags: [ReadIOEffect, TimeEffect].} =
  1004. ## overload with a ``timeout`` parameter in milliseconds.
  1005. var waited: Duration # duration already waited
  1006. var read = 0
  1007. while read < size:
  1008. let avail = waitFor(socket, waited, timeout, size-read, "recv")
  1009. var d = cast[cstring](data)
  1010. assert avail <= size-read
  1011. result = recv(socket, addr(d[read]), avail)
  1012. if result == 0: break
  1013. if result < 0:
  1014. return result
  1015. inc(read, result)
  1016. result = read
  1017. proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
  1018. flags = {SocketFlag.SafeDisconn}): int =
  1019. ## Higher-level version of ``recv``.
  1020. ##
  1021. ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``.
  1022. ##
  1023. ## For buffered sockets this function will attempt to read all the requested
  1024. ## data. It will read this data in ``BufferSize`` chunks.
  1025. ##
  1026. ## For unbuffered sockets this function makes no effort to read
  1027. ## all the data requested. It will return as much data as the operating system
  1028. ## gives it.
  1029. ##
  1030. ## When 0 is returned the socket's connection has been closed.
  1031. ##
  1032. ## This function will throw an OSError exception when an error occurs. A value
  1033. ## lower than 0 is never returned.
  1034. ##
  1035. ## A timeout may be specified in milliseconds, if enough data is not received
  1036. ## within the time specified a TimeoutError exception will be raised.
  1037. ##
  1038. ## **Note**: ``data`` must be initialised.
  1039. ##
  1040. ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
  1041. data.setLen(size)
  1042. result =
  1043. if timeout == -1:
  1044. recv(socket, cstring(data), size)
  1045. else:
  1046. recv(socket, cstring(data), size, timeout)
  1047. if result < 0:
  1048. data.setLen(0)
  1049. let lastError = getSocketError(socket)
  1050. if flags.isDisconnectionError(lastError): return
  1051. socket.socketError(result, lastError = lastError)
  1052. data.setLen(result)
  1053. proc recv*(socket: Socket, size: int, timeout = -1,
  1054. flags = {SocketFlag.SafeDisconn}): string {.inline.} =
  1055. ## Higher-level version of ``recv`` which returns a string.
  1056. ##
  1057. ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``.
  1058. ##
  1059. ## For buffered sockets this function will attempt to read all the requested
  1060. ## data. It will read this data in ``BufferSize`` chunks.
  1061. ##
  1062. ## For unbuffered sockets this function makes no effort to read
  1063. ## all the data requested. It will return as much data as the operating system
  1064. ## gives it.
  1065. ##
  1066. ## When ``""`` is returned the socket's connection has been closed.
  1067. ##
  1068. ## This function will throw an OSError exception when an error occurs.
  1069. ##
  1070. ## A timeout may be specified in milliseconds, if enough data is not received
  1071. ## within the time specified a TimeoutError exception will be raised.
  1072. ##
  1073. ##
  1074. ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
  1075. result = newString(size)
  1076. discard recv(socket, result, size, timeout, flags)
  1077. proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
  1078. if socket.isBuffered:
  1079. result = 1
  1080. if socket.bufLen == 0 or socket.currPos > socket.bufLen-1:
  1081. var res = socket.readIntoBuf(0'i32)
  1082. if res <= 0:
  1083. result = res
  1084. c = socket.buffer[socket.currPos]
  1085. else:
  1086. when defineSsl:
  1087. if socket.isSsl:
  1088. if not socket.sslHasPeekChar:
  1089. result = uniRecv(socket, addr(socket.sslPeekChar), 1, 0'i32)
  1090. socket.sslHasPeekChar = true
  1091. c = socket.sslPeekChar
  1092. return
  1093. result = recv(socket.fd, addr(c), 1, MSG_PEEK)
  1094. proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
  1095. flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.
  1096. tags: [ReadIOEffect, TimeEffect].} =
  1097. ## Reads a line of data from ``socket``.
  1098. ##
  1099. ## If a full line is read ``\r\L`` is not
  1100. ## added to ``line``, however if solely ``\r\L`` is read then ``line``
  1101. ## will be set to it.
  1102. ##
  1103. ## If the socket is disconnected, ``line`` will be set to ``""``.
  1104. ##
  1105. ## An OSError exception will be raised in the case of a socket error.
  1106. ##
  1107. ## A timeout can be specified in milliseconds, if data is not received within
  1108. ## the specified time a TimeoutError exception will be raised.
  1109. ##
  1110. ## The ``maxLength`` parameter determines the maximum amount of characters
  1111. ## that can be read. The result is truncated after that.
  1112. ##
  1113. ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
  1114. template addNLIfEmpty() =
  1115. if line.len == 0:
  1116. line.string.add("\c\L")
  1117. template raiseSockError() {.dirty.} =
  1118. let lastError = getSocketError(socket)
  1119. if flags.isDisconnectionError(lastError): setLen(line.string, 0); return
  1120. socket.socketError(n, lastError = lastError)
  1121. var waited: Duration
  1122. setLen(line.string, 0)
  1123. while true:
  1124. var c: char
  1125. discard waitFor(socket, waited, timeout, 1, "readLine")
  1126. var n = recv(socket, addr(c), 1)
  1127. if n < 0: raiseSockError()
  1128. elif n == 0: setLen(line.string, 0); return
  1129. if c == '\r':
  1130. discard waitFor(socket, waited, timeout, 1, "readLine")
  1131. n = peekChar(socket, c)
  1132. if n > 0 and c == '\L':
  1133. discard recv(socket, addr(c), 1)
  1134. elif n <= 0: raiseSockError()
  1135. addNLIfEmpty()
  1136. return
  1137. elif c == '\L':
  1138. addNLIfEmpty()
  1139. return
  1140. add(line.string, c)
  1141. # Verify that this isn't a DOS attack: #3847.
  1142. if line.string.len > maxLength: break
  1143. proc recvLine*(socket: Socket, timeout = -1,
  1144. flags = {SocketFlag.SafeDisconn},
  1145. maxLength = MaxLineLength): TaintedString =
  1146. ## Reads a line of data from ``socket``.
  1147. ##
  1148. ## If a full line is read ``\r\L`` is not
  1149. ## added to the result, however if solely ``\r\L`` is read then the result
  1150. ## will be set to it.
  1151. ##
  1152. ## If the socket is disconnected, the result will be set to ``""``.
  1153. ##
  1154. ## An OSError exception will be raised in the case of a socket error.
  1155. ##
  1156. ## A timeout can be specified in milliseconds, if data is not received within
  1157. ## the specified time a TimeoutError exception will be raised.
  1158. ##
  1159. ## The ``maxLength`` parameter determines the maximum amount of characters
  1160. ## that can be read. The result is truncated after that.
  1161. ##
  1162. ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
  1163. result = "".TaintedString
  1164. readLine(socket, result, timeout, flags, maxLength)
  1165. proc recvFrom*(socket: Socket, data: var string, length: int,
  1166. address: var string, port: var Port, flags = 0'i32): int {.
  1167. tags: [ReadIOEffect].} =
  1168. ## Receives data from ``socket``. This function should normally be used with
  1169. ## connection-less sockets (UDP sockets).
  1170. ##
  1171. ## If an error occurs an OSError exception will be raised. Otherwise the return
  1172. ## value will be the length of data received.
  1173. ##
  1174. ## **Warning:** This function does not yet have a buffered implementation,
  1175. ## so when ``socket`` is buffered the non-buffered implementation will be
  1176. ## used. Therefore if ``socket`` contains something in its buffer this
  1177. ## function will make no effort to return it.
  1178. assert(socket.protocol != IPPROTO_TCP, "Cannot `recvFrom` on a TCP socket")
  1179. # TODO: Buffered sockets
  1180. data.setLen(length)
  1181. var sockAddress: Sockaddr_in
  1182. var addrLen = sizeof(sockAddress).SockLen
  1183. result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint,
  1184. cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
  1185. if result != -1:
  1186. data.setLen(result)
  1187. address = getAddrString(cast[ptr SockAddr](addr(sockAddress)))
  1188. port = ntohs(sockAddress.sin_port).Port
  1189. else:
  1190. raiseOSError(osLastError())
  1191. proc skip*(socket: Socket, size: int, timeout = -1) =
  1192. ## Skips ``size`` amount of bytes.
  1193. ##
  1194. ## An optional timeout can be specified in milliseconds, if skipping the
  1195. ## bytes takes longer than specified a TimeoutError exception will be raised.
  1196. ##
  1197. ## Returns the number of skipped bytes.
  1198. var waited: Duration
  1199. var dummy = alloc(size)
  1200. var bytesSkipped = 0
  1201. while bytesSkipped != size:
  1202. let avail = waitFor(socket, waited, timeout, size-bytesSkipped, "skip")
  1203. bytesSkipped += recv(socket, dummy, avail)
  1204. dealloc(dummy)
  1205. proc send*(socket: Socket, data: pointer, size: int): int {.
  1206. tags: [WriteIOEffect].} =
  1207. ## Sends data to a socket.
  1208. ##
  1209. ## **Note**: This is a low-level version of ``send``. You likely should use
  1210. ## the version below.
  1211. assert(not socket.isClosed, "Cannot `send` on a closed socket")
  1212. when defineSsl:
  1213. if socket.isSsl:
  1214. return SSL_write(socket.sslHandle, cast[cstring](data), size)
  1215. when useWinVersion or defined(macosx):
  1216. result = send(socket.fd, data, size.cint, 0'i32)
  1217. else:
  1218. when defined(solaris):
  1219. const MSG_NOSIGNAL = 0
  1220. result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
  1221. proc send*(socket: Socket, data: string,
  1222. flags = {SocketFlag.SafeDisconn}) {.tags: [WriteIOEffect].} =
  1223. ## sends data to a socket.
  1224. let sent = send(socket, cstring(data), data.len)
  1225. if sent < 0:
  1226. let lastError = osLastError()
  1227. if flags.isDisconnectionError(lastError): return
  1228. socketError(socket, lastError = lastError)
  1229. if sent != data.len:
  1230. raiseOSError(osLastError(), "Could not send all data.")
  1231. template `&=`*(socket: Socket; data: typed) =
  1232. ## an alias for 'send'.
  1233. send(socket, data)
  1234. proc trySend*(socket: Socket, data: string): bool {.tags: [WriteIOEffect].} =
  1235. ## Safe alternative to ``send``. Does not raise an OSError when an error occurs,
  1236. ## and instead returns ``false`` on failure.
  1237. result = send(socket, cstring(data), data.len) == data.len
  1238. proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
  1239. size: int, af: Domain = AF_INET, flags = 0'i32) {.
  1240. tags: [WriteIOEffect].} =
  1241. ## This proc sends ``data`` to the specified ``address``,
  1242. ## which may be an IP address or a hostname, if a hostname is specified
  1243. ## this function will try each IP of that hostname.
  1244. ##
  1245. ## If an error occurs an OSError exception will be raised.
  1246. ##
  1247. ## **Note:** You may wish to use the high-level version of this function
  1248. ## which is defined below.
  1249. ##
  1250. ## **Note:** This proc is not available for SSL sockets.
  1251. assert(socket.protocol != IPPROTO_TCP, "Cannot `sendTo` on a TCP socket")
  1252. assert(not socket.isClosed, "Cannot `sendTo` on a closed socket")
  1253. var aiList = getAddrInfo(address, port, af, socket.sockType, socket.protocol)
  1254. # try all possibilities:
  1255. var success = false
  1256. var it = aiList
  1257. var result = 0
  1258. while it != nil:
  1259. result = sendto(socket.fd, data, size.cint, flags.cint, it.ai_addr,
  1260. it.ai_addrlen.SockLen)
  1261. if result != -1'i32:
  1262. success = true
  1263. break
  1264. it = it.ai_next
  1265. let osError = osLastError()
  1266. freeaddrinfo(aiList)
  1267. if not success:
  1268. raiseOSError(osError)
  1269. proc sendTo*(socket: Socket, address: string, port: Port,
  1270. data: string) {.tags: [WriteIOEffect].} =
  1271. ## This proc sends ``data`` to the specified ``address``,
  1272. ## which may be an IP address or a hostname, if a hostname is specified
  1273. ## this function will try each IP of that hostname.
  1274. ##
  1275. ## If an error occurs an OSError exception will be raised.
  1276. ##
  1277. ## This is the high-level version of the above ``sendTo`` function.
  1278. socket.sendTo(address, port, cstring(data), data.len, socket.domain)
  1279. proc isSsl*(socket: Socket): bool =
  1280. ## Determines whether ``socket`` is a SSL socket.
  1281. when defineSsl:
  1282. result = socket.isSsl
  1283. else:
  1284. result = false
  1285. proc getFd*(socket: Socket): SocketHandle = return socket.fd
  1286. ## Returns the socket's file descriptor
  1287. when defined(nimHasStyleChecks):
  1288. {.push styleChecks: off.}
  1289. proc IPv4_any*(): IpAddress =
  1290. ## Returns the IPv4 any address, which can be used to listen on all available
  1291. ## network adapters
  1292. result = IpAddress(
  1293. family: IpAddressFamily.IPv4,
  1294. address_v4: [0'u8, 0, 0, 0])
  1295. proc IPv4_loopback*(): IpAddress =
  1296. ## Returns the IPv4 loopback address (127.0.0.1)
  1297. result = IpAddress(
  1298. family: IpAddressFamily.IPv4,
  1299. address_v4: [127'u8, 0, 0, 1])
  1300. proc IPv4_broadcast*(): IpAddress =
  1301. ## Returns the IPv4 broadcast address (255.255.255.255)
  1302. result = IpAddress(
  1303. family: IpAddressFamily.IPv4,
  1304. address_v4: [255'u8, 255, 255, 255])
  1305. proc IPv6_any*(): IpAddress =
  1306. ## Returns the IPv6 any address (::0), which can be used
  1307. ## to listen on all available network adapters
  1308. result = IpAddress(
  1309. family: IpAddressFamily.IPv6,
  1310. address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
  1311. proc IPv6_loopback*(): IpAddress =
  1312. ## Returns the IPv6 loopback address (::1)
  1313. result = IpAddress(
  1314. family: IpAddressFamily.IPv6,
  1315. address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
  1316. when defined(nimHasStyleChecks):
  1317. {.pop.}
  1318. proc `==`*(lhs, rhs: IpAddress): bool =
  1319. ## Compares two IpAddresses for Equality. Returns true if the addresses are equal
  1320. if lhs.family != rhs.family: return false
  1321. if lhs.family == IpAddressFamily.IPv4:
  1322. for i in low(lhs.address_v4) .. high(lhs.address_v4):
  1323. if lhs.address_v4[i] != rhs.address_v4[i]: return false
  1324. else: # IPv6
  1325. for i in low(lhs.address_v6) .. high(lhs.address_v6):
  1326. if lhs.address_v6[i] != rhs.address_v6[i]: return false
  1327. return true
  1328. proc `$`*(address: IpAddress): string =
  1329. ## Converts an IpAddress into the textual representation
  1330. result = ""
  1331. case address.family
  1332. of IpAddressFamily.IPv4:
  1333. for i in 0 .. 3:
  1334. if i != 0:
  1335. result.add('.')
  1336. result.add($address.address_v4[i])
  1337. of IpAddressFamily.IPv6:
  1338. var
  1339. currentZeroStart = -1
  1340. currentZeroCount = 0
  1341. biggestZeroStart = -1
  1342. biggestZeroCount = 0
  1343. # Look for the largest block of zeros
  1344. for i in 0..7:
  1345. var isZero = address.address_v6[i*2] == 0 and address.address_v6[i*2+1] == 0
  1346. if isZero:
  1347. if currentZeroStart == -1:
  1348. currentZeroStart = i
  1349. currentZeroCount = 1
  1350. else:
  1351. currentZeroCount.inc()
  1352. if currentZeroCount > biggestZeroCount:
  1353. biggestZeroCount = currentZeroCount
  1354. biggestZeroStart = currentZeroStart
  1355. else:
  1356. currentZeroStart = -1
  1357. if biggestZeroCount == 8: # Special case ::0
  1358. result.add("::")
  1359. else: # Print address
  1360. var printedLastGroup = false
  1361. for i in 0..7:
  1362. var word: uint16 = (cast[uint16](address.address_v6[i*2])) shl 8
  1363. word = word or cast[uint16](address.address_v6[i*2+1])
  1364. if biggestZeroCount != 0 and # Check if group is in skip group
  1365. (i >= biggestZeroStart and i < (biggestZeroStart + biggestZeroCount)):
  1366. if i == biggestZeroStart: # skip start
  1367. result.add("::")
  1368. printedLastGroup = false
  1369. else:
  1370. if printedLastGroup:
  1371. result.add(':')
  1372. var
  1373. afterLeadingZeros = false
  1374. mask = 0xF000'u16
  1375. for j in 0'u16..3'u16:
  1376. var val = (mask and word) shr (4'u16*(3'u16-j))
  1377. if val != 0 or afterLeadingZeros:
  1378. if val < 0xA:
  1379. result.add(chr(uint16(ord('0'))+val))
  1380. else: # val >= 0xA
  1381. result.add(chr(uint16(ord('a'))+val-0xA))
  1382. afterLeadingZeros = true
  1383. mask = mask shr 4
  1384. printedLastGroup = true
  1385. proc dial*(address: string, port: Port,
  1386. protocol = IPPROTO_TCP, buffered = true): owned(Socket)
  1387. {.tags: [ReadIOEffect, WriteIOEffect].} =
  1388. ## Establishes connection to the specified ``address``:``port`` pair via the
  1389. ## specified protocol. The procedure iterates through possible
  1390. ## resolutions of the ``address`` until it succeeds, meaning that it
  1391. ## seamlessly works with both IPv4 and IPv6.
  1392. ## Returns Socket ready to send or receive data.
  1393. let sockType = protocol.toSockType()
  1394. let aiList = getAddrInfo(address, port, AF_UNSPEC, sockType, protocol)
  1395. var fdPerDomain: array[low(Domain).ord..high(Domain).ord, SocketHandle]
  1396. for i in low(fdPerDomain)..high(fdPerDomain):
  1397. fdPerDomain[i] = osInvalidSocket
  1398. template closeUnusedFds(domainToKeep = -1) {.dirty.} =
  1399. for i, fd in fdPerDomain:
  1400. if fd != osInvalidSocket and i != domainToKeep:
  1401. fd.close()
  1402. var success = false
  1403. var lastError: OSErrorCode
  1404. var it = aiList
  1405. var domain: Domain
  1406. var lastFd: SocketHandle
  1407. while it != nil:
  1408. let domainOpt = it.ai_family.toKnownDomain()
  1409. if domainOpt.isNone:
  1410. it = it.ai_next
  1411. continue
  1412. domain = domainOpt.unsafeGet()
  1413. lastFd = fdPerDomain[ord(domain)]
  1414. if lastFd == osInvalidSocket:
  1415. lastFd = createNativeSocket(domain, sockType, protocol)
  1416. if lastFd == osInvalidSocket:
  1417. # we always raise if socket creation failed, because it means a
  1418. # network system problem (e.g. not enough FDs), and not an unreachable
  1419. # address.
  1420. let err = osLastError()
  1421. freeaddrinfo(aiList)
  1422. closeUnusedFds()
  1423. raiseOSError(err)
  1424. fdPerDomain[ord(domain)] = lastFd
  1425. if connect(lastFd, it.ai_addr, it.ai_addrlen.SockLen) == 0'i32:
  1426. success = true
  1427. break
  1428. lastError = osLastError()
  1429. it = it.ai_next
  1430. freeaddrinfo(aiList)
  1431. closeUnusedFds(ord(domain))
  1432. if success:
  1433. result = newSocket(lastFd, domain, sockType, protocol)
  1434. elif lastError != 0.OSErrorCode:
  1435. raiseOSError(lastError)
  1436. else:
  1437. raise newException(IOError, "Couldn't resolve address: " & address)
  1438. proc connect*(socket: Socket, address: string,
  1439. port = Port(0)) {.tags: [ReadIOEffect].} =
  1440. ## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
  1441. ## host name. If ``address`` is a host name, this function will try each IP
  1442. ## of that host name. ``htons`` is already performed on ``port`` so you must
  1443. ## not do it.
  1444. ##
  1445. ## If ``socket`` is an SSL socket a handshake will be automatically performed.
  1446. var aiList = getAddrInfo(address, port, socket.domain)
  1447. # try all possibilities:
  1448. var success = false
  1449. var lastError: OSErrorCode
  1450. var it = aiList
  1451. while it != nil:
  1452. if connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen) == 0'i32:
  1453. success = true
  1454. break
  1455. else: lastError = osLastError()
  1456. it = it.ai_next
  1457. freeaddrinfo(aiList)
  1458. if not success: raiseOSError(lastError)
  1459. when defineSsl:
  1460. if socket.isSsl:
  1461. # RFC3546 for SNI specifies that IP addresses are not allowed.
  1462. if not isIpAddress(address):
  1463. # Discard result in case OpenSSL version doesn't support SNI, or we're
  1464. # not using TLSv1+
  1465. discard SSL_set_tlsext_host_name(socket.sslHandle, address)
  1466. let ret = SSL_connect(socket.sslHandle)
  1467. socketError(socket, ret)
  1468. proc connectAsync(socket: Socket, name: string, port = Port(0),
  1469. af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
  1470. ## A variant of ``connect`` for non-blocking sockets.
  1471. ##
  1472. ## This procedure will immediately return, it will not block until a connection
  1473. ## is made. It is up to the caller to make sure the connection has been established
  1474. ## by checking (using ``select``) whether the socket is writeable.
  1475. ##
  1476. ## **Note**: For SSL sockets, the ``handshake`` procedure must be called
  1477. ## whenever the socket successfully connects to a server.
  1478. var aiList = getAddrInfo(name, port, af)
  1479. # try all possibilities:
  1480. var success = false
  1481. var lastError: OSErrorCode
  1482. var it = aiList
  1483. while it != nil:
  1484. var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.SockLen)
  1485. if ret == 0'i32:
  1486. success = true
  1487. break
  1488. else:
  1489. lastError = osLastError()
  1490. when useWinVersion:
  1491. # Windows EINTR doesn't behave same as POSIX.
  1492. if lastError.int32 == WSAEWOULDBLOCK:
  1493. success = true
  1494. break
  1495. else:
  1496. if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
  1497. success = true
  1498. break
  1499. it = it.ai_next
  1500. freeaddrinfo(aiList)
  1501. if not success: raiseOSError(lastError)
  1502. proc connect*(socket: Socket, address: string, port = Port(0),
  1503. timeout: int) {.tags: [ReadIOEffect, WriteIOEffect].} =
  1504. ## Connects to server as specified by ``address`` on port specified by ``port``.
  1505. ##
  1506. ## The ``timeout`` parameter specifies the time in milliseconds to allow for
  1507. ## the connection to the server to be made.
  1508. socket.fd.setBlocking(false)
  1509. socket.connectAsync(address, port, socket.domain)
  1510. var s = @[socket.fd]
  1511. if selectWrite(s, timeout) != 1:
  1512. raise newException(TimeoutError, "Call to 'connect' timed out.")
  1513. else:
  1514. let res = getSockOptInt(socket.fd, SOL_SOCKET, SO_ERROR)
  1515. if res != 0:
  1516. raiseOSError(OSErrorCode(res))
  1517. when defineSsl and not defined(nimdoc):
  1518. if socket.isSsl:
  1519. socket.fd.setBlocking(true)
  1520. doAssert socket.gotHandshake()
  1521. socket.fd.setBlocking(true)
  1522. proc getPrimaryIPAddr*(dest = parseIpAddress("8.8.8.8")): IpAddress =
  1523. ## Finds the local IP address, usually assigned to eth0 on LAN or wlan0 on WiFi,
  1524. ## used to reach an external address. Useful to run local services.
  1525. ##
  1526. ## No traffic is sent.
  1527. ##
  1528. ## Supports IPv4 and v6.
  1529. ## Raises OSError if external networking is not set up.
  1530. ##
  1531. ## .. code-block:: Nim
  1532. ## echo $getPrimaryIPAddr() # "192.168.1.2"
  1533. let socket =
  1534. if dest.family == IpAddressFamily.IPv4:
  1535. newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
  1536. else:
  1537. newSocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
  1538. socket.connect($dest, 80.Port)
  1539. socket.getLocalAddr()[0].parseIpAddress()