net.nim 61 KB

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