Merge branch 'bigbreak' of https://github.com/Araq/Nimrod into bigbreak
This commit is contained in:
commit
de29ce8ca8
7 changed files with 408 additions and 144 deletions
|
|
@ -255,7 +255,7 @@ proc doUpload(ftp: AsyncFtpClient, file: TFile,
|
||||||
|
|
||||||
await countdownFut or sendFut
|
await countdownFut or sendFut
|
||||||
|
|
||||||
proc storeFile*(ftp: AsyncFtpClient, file, dest: string,
|
proc store*(ftp: AsyncFtpClient, file, dest: string,
|
||||||
onProgressChanged = defaultOnProgressChanged) {.async.} =
|
onProgressChanged = defaultOnProgressChanged) {.async.} =
|
||||||
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
|
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
|
||||||
## function asynchronously is recommended to view the progress of
|
## function asynchronously is recommended to view the progress of
|
||||||
|
|
@ -288,7 +288,7 @@ when isMainModule:
|
||||||
await ftp.connect()
|
await ftp.connect()
|
||||||
echo await ftp.pwd()
|
echo await ftp.pwd()
|
||||||
echo await ftp.listDirs()
|
echo await ftp.listDirs()
|
||||||
await ftp.storeFile("payload.jpg", "payload.jpg")
|
await ftp.store("payload.jpg", "payload.jpg")
|
||||||
await ftp.retrFile("payload.jpg", "payload2.jpg")
|
await ftp.retrFile("payload.jpg", "payload2.jpg")
|
||||||
echo("Finished")
|
echo("Finished")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
import asyncdispatch
|
import asyncdispatch
|
||||||
import rawsockets
|
import rawsockets
|
||||||
import net
|
import net
|
||||||
|
import os
|
||||||
|
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
import openssl
|
import openssl
|
||||||
|
|
@ -54,7 +55,22 @@ when defined(ssl):
|
||||||
type
|
type
|
||||||
# TODO: I would prefer to just do:
|
# TODO: I would prefer to just do:
|
||||||
# PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work.
|
# PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work.
|
||||||
AsyncSocketDesc {.borrow: `.`.} = distinct TSocketImpl
|
AsyncSocketDesc = object
|
||||||
|
fd*: SocketHandle
|
||||||
|
case isBuffered*: bool # determines whether this socket is buffered.
|
||||||
|
of true:
|
||||||
|
buffer*: array[0..BufferSize, char]
|
||||||
|
currPos*: int # current index in buffer
|
||||||
|
bufLen*: int # current length of buffer
|
||||||
|
of false: nil
|
||||||
|
case isSsl: bool
|
||||||
|
of true:
|
||||||
|
when defined(ssl):
|
||||||
|
sslHandle: SslPtr
|
||||||
|
sslContext: SslContext
|
||||||
|
bioIn: BIO
|
||||||
|
bioOut: BIO
|
||||||
|
of false: nil
|
||||||
AsyncSocket* = ref AsyncSocketDesc
|
AsyncSocket* = ref AsyncSocketDesc
|
||||||
|
|
||||||
{.deprecated: [PAsyncSocket: AsyncSocket].}
|
{.deprecated: [PAsyncSocket: AsyncSocket].}
|
||||||
|
|
@ -63,7 +79,7 @@ type
|
||||||
|
|
||||||
proc newSocket(fd: TAsyncFD, isBuff: bool): PAsyncSocket =
|
proc newSocket(fd: TAsyncFD, isBuff: bool): PAsyncSocket =
|
||||||
assert fd != osInvalidSocket.TAsyncFD
|
assert fd != osInvalidSocket.TAsyncFD
|
||||||
new(result.PSocket)
|
new(result)
|
||||||
result.fd = fd.SocketHandle
|
result.fd = fd.SocketHandle
|
||||||
result.isBuffered = isBuff
|
result.isBuffered = isBuff
|
||||||
if isBuff:
|
if isBuff:
|
||||||
|
|
@ -74,19 +90,91 @@ proc newAsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||||
## Creates a new asynchronous socket.
|
## Creates a new asynchronous socket.
|
||||||
result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
|
result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
|
||||||
|
|
||||||
|
when defined(ssl):
|
||||||
|
proc getSslError(handle: SslPtr, err: cint): cint =
|
||||||
|
assert err < 0
|
||||||
|
var ret = SSLGetError(handle, err.cint)
|
||||||
|
case ret
|
||||||
|
of SSL_ERROR_ZERO_RETURN:
|
||||||
|
raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||||
|
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
||||||
|
return ret
|
||||||
|
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
|
||||||
|
return ret
|
||||||
|
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
|
raiseSSLError("Function for x509 lookup has been called.")
|
||||||
|
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||||
|
raiseSSLError()
|
||||||
|
else: raiseSSLError("Unknown Error")
|
||||||
|
|
||||||
|
proc sendPendingSslData(socket: AsyncSocket,
|
||||||
|
flags: set[TSocketFlags]) {.async.} =
|
||||||
|
let len = bioCtrlPending(socket.bioOut)
|
||||||
|
if len > 0:
|
||||||
|
var data = newStringOfCap(len)
|
||||||
|
let read = bioRead(socket.bioOut, addr data[0], len)
|
||||||
|
assert read != 0
|
||||||
|
if read < 0:
|
||||||
|
raiseSslError()
|
||||||
|
data.setLen(read)
|
||||||
|
await socket.fd.TAsyncFd.send(data, flags)
|
||||||
|
|
||||||
|
proc appeaseSsl(socket: AsyncSocket, flags: set[TSocketFlags],
|
||||||
|
sslError: cint) {.async.} =
|
||||||
|
case sslError
|
||||||
|
of SSL_ERROR_WANT_WRITE:
|
||||||
|
await sendPendingSslData(socket, flags)
|
||||||
|
of SSL_ERROR_WANT_READ:
|
||||||
|
var data = await recv(socket.fd.TAsyncFD, BufferSize, flags)
|
||||||
|
let ret = bioWrite(socket.bioIn, addr data[0], data.len.cint)
|
||||||
|
if ret < 0:
|
||||||
|
raiseSSLError()
|
||||||
|
else:
|
||||||
|
raiseSSLError("Cannot appease SSL.")
|
||||||
|
|
||||||
|
template sslLoop(socket: AsyncSocket, flags: set[TSocketFlags],
|
||||||
|
op: expr) =
|
||||||
|
var opResult {.inject.} = -1.cint
|
||||||
|
while opResult < 0:
|
||||||
|
opResult = op
|
||||||
|
# Bit hackish here.
|
||||||
|
# TODO: Introduce an async template transformation pragma?
|
||||||
|
yield sendPendingSslData(socket, flags)
|
||||||
|
if opResult < 0:
|
||||||
|
let err = getSslError(socket.sslHandle, opResult.cint)
|
||||||
|
yield appeaseSsl(socket, flags, err.cint)
|
||||||
|
|
||||||
proc connect*(socket: PAsyncSocket, address: string, port: TPort,
|
proc connect*(socket: PAsyncSocket, address: string, port: TPort,
|
||||||
af = AF_INET): Future[void] =
|
af = AF_INET) {.async.} =
|
||||||
## Connects ``socket`` to server at ``address:port``.
|
## Connects ``socket`` to server at ``address:port``.
|
||||||
##
|
##
|
||||||
## Returns a ``Future`` which will complete when the connection succeeds
|
## Returns a ``Future`` which will complete when the connection succeeds
|
||||||
## or an error occurs.
|
## or an error occurs.
|
||||||
result = connect(socket.fd.TAsyncFD, address, port, af)
|
await connect(socket.fd.TAsyncFD, address, port, af)
|
||||||
|
let flags = {TSocketFlags.SafeDisconn}
|
||||||
|
if socket.isSsl:
|
||||||
|
when defined(ssl):
|
||||||
|
sslSetConnectState(socket.sslHandle)
|
||||||
|
sslLoop(socket, flags, sslDoHandshake(socket.sslHandle))
|
||||||
|
|
||||||
proc readIntoBuf(socket: PAsyncSocket,
|
proc readIntoBuf(socket: PAsyncSocket,
|
||||||
flags: set[TSocketFlags]): Future[int] {.async.} =
|
flags: set[TSocketFlags]): Future[int] {.async.} =
|
||||||
var data = await recv(socket.fd.TAsyncFD, BufferSize, flags)
|
var data = await recv(socket.fd.TAsyncFD, BufferSize, flags)
|
||||||
if data.len != 0:
|
if data.len != 0:
|
||||||
copyMem(addr socket.buffer[0], addr data[0], data.len)
|
copyMem(addr socket.buffer[0], addr data[0], data.len)
|
||||||
|
if socket.isSsl:
|
||||||
|
when defined(ssl):
|
||||||
|
# SSL mode.
|
||||||
|
let ret = bioWrite(socket.bioIn, addr socket.buffer[0], data.len.cint)
|
||||||
|
if ret < 0:
|
||||||
|
raiseSSLError()
|
||||||
|
sslLoop(socket, flags,
|
||||||
|
sslRead(socket.sslHandle, addr socket.buffer[0], BufferSize.cint))
|
||||||
|
socket.currPos = 0
|
||||||
|
socket.bufLen = opResult # Injected from sslLoop template.
|
||||||
|
result = opResult
|
||||||
|
else:
|
||||||
|
# Not in SSL mode.
|
||||||
socket.bufLen = data.len
|
socket.bufLen = data.len
|
||||||
socket.currPos = 0
|
socket.currPos = 0
|
||||||
result = data.len
|
result = data.len
|
||||||
|
|
@ -131,11 +219,18 @@ proc recv*(socket: PAsyncSocket, size: int,
|
||||||
result = await recv(socket.fd.TAsyncFD, size, flags)
|
result = await recv(socket.fd.TAsyncFD, size, flags)
|
||||||
|
|
||||||
proc send*(socket: PAsyncSocket, data: string,
|
proc send*(socket: PAsyncSocket, data: string,
|
||||||
flags = {TSocketFlags.SafeDisconn}): Future[void] =
|
flags = {TSocketFlags.SafeDisconn}) {.async.} =
|
||||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||||
## data has been sent.
|
## data has been sent.
|
||||||
assert socket != nil
|
assert socket != nil
|
||||||
result = send(socket.fd.TAsyncFD, data, flags)
|
if socket.isSsl:
|
||||||
|
when defined(ssl):
|
||||||
|
var copy = data
|
||||||
|
sslLoop(socket, flags,
|
||||||
|
sslWrite(socket.sslHandle, addr copy[0], copy.len.cint))
|
||||||
|
await sendPendingSslData(socket, flags)
|
||||||
|
else:
|
||||||
|
await send(socket.fd.TAsyncFD, data, flags)
|
||||||
|
|
||||||
proc acceptAddr*(socket: PAsyncSocket, flags = {TSocketFlags.SafeDisconn}):
|
proc acceptAddr*(socket: PAsyncSocket, flags = {TSocketFlags.SafeDisconn}):
|
||||||
Future[tuple[address: string, client: PAsyncSocket]] =
|
Future[tuple[address: string, client: PAsyncSocket]] =
|
||||||
|
|
@ -240,24 +335,67 @@ proc recvLine*(socket: PAsyncSocket,
|
||||||
return
|
return
|
||||||
add(result.string, c)
|
add(result.string, c)
|
||||||
|
|
||||||
proc bindAddr*(socket: PAsyncSocket, port = TPort(0), address = "") =
|
proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||||
## Binds ``address``:``port`` to the socket.
|
|
||||||
##
|
|
||||||
## If ``address`` is "" then ADDR_ANY will be bound.
|
|
||||||
socket.PSocket.bindAddr(port, address)
|
|
||||||
|
|
||||||
proc listen*(socket: PAsyncSocket, backlog = SOMAXCONN) =
|
|
||||||
## Marks ``socket`` as accepting connections.
|
## Marks ``socket`` as accepting connections.
|
||||||
## ``Backlog`` specifies the maximum length of the
|
## ``Backlog`` specifies the maximum length of the
|
||||||
## queue of pending connections.
|
## queue of pending connections.
|
||||||
##
|
##
|
||||||
## Raises an EOS error upon failure.
|
## Raises an EOS error upon failure.
|
||||||
socket.PSocket.listen(backlog)
|
if listen(socket.fd, backlog) < 0'i32: raiseOSError(osLastError())
|
||||||
|
|
||||||
|
proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
|
||||||
|
tags: [ReadIOEffect].} =
|
||||||
|
## Binds ``address``:``port`` to the socket.
|
||||||
|
##
|
||||||
|
## If ``address`` is "" then ADDR_ANY will be bound.
|
||||||
|
|
||||||
|
if address == "":
|
||||||
|
var name: Sockaddr_in
|
||||||
|
when defined(Windows) or defined(nimdoc):
|
||||||
|
name.sin_family = toInt(AF_INET).int16
|
||||||
|
else:
|
||||||
|
name.sin_family = toInt(AF_INET)
|
||||||
|
name.sin_port = htons(int16(port))
|
||||||
|
name.sin_addr.s_addr = htonl(INADDR_ANY)
|
||||||
|
if bindAddr(socket.fd, cast[ptr SockAddr](addr(name)),
|
||||||
|
sizeof(name).Socklen) < 0'i32:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
|
var aiList = getAddrInfo(address, port, AF_INET)
|
||||||
|
if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
|
||||||
|
dealloc(aiList)
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
dealloc(aiList)
|
||||||
|
|
||||||
proc close*(socket: PAsyncSocket) =
|
proc close*(socket: PAsyncSocket) =
|
||||||
## Closes the socket.
|
## Closes the socket.
|
||||||
socket.fd.TAsyncFD.closeSocket()
|
socket.fd.TAsyncFD.closeSocket()
|
||||||
# TODO SSL
|
when defined(ssl):
|
||||||
|
if socket.isSSL:
|
||||||
|
let res = SslShutdown(socket.sslHandle)
|
||||||
|
if res == 0:
|
||||||
|
if SslShutdown(socket.sslHandle) != 1:
|
||||||
|
raiseSslError()
|
||||||
|
elif res != 1:
|
||||||
|
raiseSslError()
|
||||||
|
|
||||||
|
when defined(ssl):
|
||||||
|
proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) =
|
||||||
|
## Wraps a socket in an SSL context. This function effectively turns
|
||||||
|
## ``socket`` into an SSL socket.
|
||||||
|
##
|
||||||
|
## **Disclaimer**: This code is not well tested, may be very unsafe and
|
||||||
|
## prone to security vulnerabilities.
|
||||||
|
socket.isSsl = true
|
||||||
|
socket.sslContext = ctx
|
||||||
|
socket.sslHandle = SSLNew(PSSLCTX(socket.sslContext))
|
||||||
|
if socket.sslHandle == nil:
|
||||||
|
raiseSslError()
|
||||||
|
|
||||||
|
socket.bioIn = bioNew(bio_s_mem())
|
||||||
|
socket.bioOut = bioNew(bio_s_mem())
|
||||||
|
sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@
|
||||||
import sockets, strutils, parseurl, parseutils, strtabs, base64, os
|
import sockets, strutils, parseurl, parseutils, strtabs, base64, os
|
||||||
import asyncnet, asyncdispatch
|
import asyncnet, asyncdispatch
|
||||||
import rawsockets
|
import rawsockets
|
||||||
|
from net import nil
|
||||||
|
|
||||||
type
|
type
|
||||||
Response* = tuple[
|
Response* = tuple[
|
||||||
|
|
@ -164,6 +165,7 @@ proc parseBody(s: TSocket, headers: PStringTable, timeout: int): string =
|
||||||
var contentLengthHeader = headers["Content-Length"]
|
var contentLengthHeader = headers["Content-Length"]
|
||||||
if contentLengthHeader != "":
|
if contentLengthHeader != "":
|
||||||
var length = contentLengthHeader.parseint()
|
var length = contentLengthHeader.parseint()
|
||||||
|
if length > 0:
|
||||||
result = newString(length)
|
result = newString(length)
|
||||||
var received = 0
|
var received = 0
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -181,7 +183,7 @@ proc parseBody(s: TSocket, headers: PStringTable, timeout: int): string =
|
||||||
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5
|
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5
|
||||||
if headers["Connection"] == "close":
|
if headers["Connection"] == "close":
|
||||||
var buf = ""
|
var buf = ""
|
||||||
while True:
|
while true:
|
||||||
buf = newString(4000)
|
buf = newString(4000)
|
||||||
let r = s.recv(addr(buf[0]), 4000, timeout)
|
let r = s.recv(addr(buf[0]), 4000, timeout)
|
||||||
if r == 0: break
|
if r == 0: break
|
||||||
|
|
@ -194,7 +196,7 @@ proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse =
|
||||||
var fullyRead = false
|
var fullyRead = false
|
||||||
var line = ""
|
var line = ""
|
||||||
result.headers = newStringTable(modeCaseInsensitive)
|
result.headers = newStringTable(modeCaseInsensitive)
|
||||||
while True:
|
while true:
|
||||||
line = ""
|
line = ""
|
||||||
linei = 0
|
linei = 0
|
||||||
s.readLine(line, timeout)
|
s.readLine(line, timeout)
|
||||||
|
|
@ -294,7 +296,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
|
||||||
add(headers, "\c\L")
|
add(headers, "\c\L")
|
||||||
|
|
||||||
var s = socket()
|
var s = socket()
|
||||||
if s == InvalidSocket: raiseOSError(osLastError())
|
if s == invalidSocket: raiseOSError(osLastError())
|
||||||
var port = sockets.TPort(80)
|
var port = sockets.TPort(80)
|
||||||
if r.scheme == "https":
|
if r.scheme == "https":
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
|
|
@ -321,7 +323,7 @@ proc redirection(status: string): bool =
|
||||||
const redirectionNRs = ["301", "302", "303", "307"]
|
const redirectionNRs = ["301", "302", "303", "307"]
|
||||||
for i in items(redirectionNRs):
|
for i in items(redirectionNRs):
|
||||||
if status.startsWith(i):
|
if status.startsWith(i):
|
||||||
return True
|
return true
|
||||||
|
|
||||||
proc getNewLocation(lastUrl: string, headers: PStringTable): string =
|
proc getNewLocation(lastUrl: string, headers: PStringTable): string =
|
||||||
result = headers["Location"]
|
result = headers["Location"]
|
||||||
|
|
@ -444,11 +446,13 @@ type
|
||||||
headers: StringTableRef
|
headers: StringTableRef
|
||||||
maxRedirects: int
|
maxRedirects: int
|
||||||
userAgent: string
|
userAgent: string
|
||||||
|
when defined(ssl):
|
||||||
|
sslContext: net.SslContext
|
||||||
|
|
||||||
{.deprecated: [PAsyncHttpClient: AsyncHttpClient].}
|
{.deprecated: [PAsyncHttpClient: AsyncHttpClient].}
|
||||||
|
|
||||||
proc newAsyncHttpClient*(userAgent = defUserAgent,
|
proc newAsyncHttpClient*(userAgent = defUserAgent,
|
||||||
maxRedirects = 5): AsyncHttpClient =
|
maxRedirects = 5, sslContext = defaultSslContext): AsyncHttpClient =
|
||||||
## Creates a new PAsyncHttpClient instance.
|
## Creates a new PAsyncHttpClient instance.
|
||||||
##
|
##
|
||||||
## ``userAgent`` specifies the user agent that will be used when making
|
## ``userAgent`` specifies the user agent that will be used when making
|
||||||
|
|
@ -456,10 +460,13 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
|
||||||
##
|
##
|
||||||
## ``maxRedirects`` specifies the maximum amount of redirects to follow,
|
## ``maxRedirects`` specifies the maximum amount of redirects to follow,
|
||||||
## default is 5.
|
## default is 5.
|
||||||
|
##
|
||||||
|
## ``sslContext`` specifies the SSL context to use for HTTPS requests.
|
||||||
new result
|
new result
|
||||||
result.headers = newStringTable(modeCaseInsensitive)
|
result.headers = newStringTable(modeCaseInsensitive)
|
||||||
result.userAgent = defUserAgent
|
result.userAgent = defUserAgent
|
||||||
result.maxRedirects = maxRedirects
|
result.maxRedirects = maxRedirects
|
||||||
|
result.sslContext = net.SslContext(sslContext)
|
||||||
|
|
||||||
proc close*(client: AsyncHttpClient) =
|
proc close*(client: AsyncHttpClient) =
|
||||||
## Closes any connections held by the HTTP client.
|
## Closes any connections held by the HTTP client.
|
||||||
|
|
@ -467,7 +474,7 @@ proc close*(client: AsyncHttpClient) =
|
||||||
client.socket.close()
|
client.socket.close()
|
||||||
client.connected = false
|
client.connected = false
|
||||||
|
|
||||||
proc recvFull(socket: PAsyncSocket, size: int): PFuture[string] {.async.} =
|
proc recvFull(socket: PAsyncSocket, size: int): Future[string] {.async.} =
|
||||||
## Ensures that all the data requested is read and returned.
|
## Ensures that all the data requested is read and returned.
|
||||||
result = ""
|
result = ""
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -476,7 +483,7 @@ proc recvFull(socket: PAsyncSocket, size: int): PFuture[string] {.async.} =
|
||||||
if data == "": break # We've been disconnected.
|
if data == "": break # We've been disconnected.
|
||||||
result.add data
|
result.add data
|
||||||
|
|
||||||
proc parseChunks(client: PAsyncHttpClient): PFuture[string] {.async.} =
|
proc parseChunks(client: PAsyncHttpClient): Future[string] {.async.} =
|
||||||
result = ""
|
result = ""
|
||||||
var ri = 0
|
var ri = 0
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -509,7 +516,7 @@ proc parseChunks(client: PAsyncHttpClient): PFuture[string] {.async.} =
|
||||||
# them: http://tools.ietf.org/html/rfc2616#section-3.6.1
|
# them: http://tools.ietf.org/html/rfc2616#section-3.6.1
|
||||||
|
|
||||||
proc parseBody(client: PAsyncHttpClient,
|
proc parseBody(client: PAsyncHttpClient,
|
||||||
headers: PStringTable): PFuture[string] {.async.} =
|
headers: PStringTable): Future[string] {.async.} =
|
||||||
result = ""
|
result = ""
|
||||||
if headers["Transfer-Encoding"] == "chunked":
|
if headers["Transfer-Encoding"] == "chunked":
|
||||||
result = await parseChunks(client)
|
result = await parseChunks(client)
|
||||||
|
|
@ -519,6 +526,7 @@ proc parseBody(client: PAsyncHttpClient,
|
||||||
var contentLengthHeader = headers["Content-Length"]
|
var contentLengthHeader = headers["Content-Length"]
|
||||||
if contentLengthHeader != "":
|
if contentLengthHeader != "":
|
||||||
var length = contentLengthHeader.parseint()
|
var length = contentLengthHeader.parseint()
|
||||||
|
if length > 0:
|
||||||
result = await client.socket.recvFull(length)
|
result = await client.socket.recvFull(length)
|
||||||
if result == "":
|
if result == "":
|
||||||
httpError("Got disconnected while trying to read body.")
|
httpError("Got disconnected while trying to read body.")
|
||||||
|
|
@ -532,19 +540,19 @@ proc parseBody(client: PAsyncHttpClient,
|
||||||
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5
|
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5
|
||||||
if headers["Connection"] == "close":
|
if headers["Connection"] == "close":
|
||||||
var buf = ""
|
var buf = ""
|
||||||
while True:
|
while true:
|
||||||
buf = await client.socket.recvFull(4000)
|
buf = await client.socket.recvFull(4000)
|
||||||
if buf == "": break
|
if buf == "": break
|
||||||
result.add(buf)
|
result.add(buf)
|
||||||
|
|
||||||
proc parseResponse(client: PAsyncHttpClient,
|
proc parseResponse(client: PAsyncHttpClient,
|
||||||
getBody: bool): PFuture[TResponse] {.async.} =
|
getBody: bool): Future[TResponse] {.async.} =
|
||||||
var parsedStatus = false
|
var parsedStatus = false
|
||||||
var linei = 0
|
var linei = 0
|
||||||
var fullyRead = false
|
var fullyRead = false
|
||||||
var line = ""
|
var line = ""
|
||||||
result.headers = newStringTable(modeCaseInsensitive)
|
result.headers = newStringTable(modeCaseInsensitive)
|
||||||
while True:
|
while true:
|
||||||
linei = 0
|
linei = 0
|
||||||
line = await client.socket.recvLine()
|
line = await client.socket.recvLine()
|
||||||
if line == "": break # We've been disconnected.
|
if line == "": break # We've been disconnected.
|
||||||
|
|
@ -590,20 +598,29 @@ proc newConnection(client: PAsyncHttpClient, url: TURL) {.async.} =
|
||||||
client.currentURL.scheme != url.scheme:
|
client.currentURL.scheme != url.scheme:
|
||||||
if client.connected: client.close()
|
if client.connected: client.close()
|
||||||
client.socket = newAsyncSocket()
|
client.socket = newAsyncSocket()
|
||||||
if url.scheme == "https":
|
|
||||||
assert false, "TODO SSL"
|
|
||||||
|
|
||||||
# TODO: I should be able to write 'net.TPort' here...
|
# TODO: I should be able to write 'net.TPort' here...
|
||||||
let port =
|
let port =
|
||||||
if url.port == "": rawsockets.TPort(80)
|
if url.port == "":
|
||||||
|
if url.scheme.toLower() == "https":
|
||||||
|
rawsockets.TPort(443)
|
||||||
|
else:
|
||||||
|
rawsockets.TPort(80)
|
||||||
else: rawsockets.TPort(url.port.parseInt)
|
else: rawsockets.TPort(url.port.parseInt)
|
||||||
|
|
||||||
|
if url.scheme.toLower() == "https":
|
||||||
|
when defined(ssl):
|
||||||
|
client.sslContext.wrapSocket(client.socket)
|
||||||
|
else:
|
||||||
|
raise newException(EHttpRequestErr,
|
||||||
|
"SSL support is not available. Cannot connect over SSL.")
|
||||||
|
|
||||||
await client.socket.connect(url.hostname, port)
|
await client.socket.connect(url.hostname, port)
|
||||||
client.currentURL = url
|
client.currentURL = url
|
||||||
client.connected = true
|
client.connected = true
|
||||||
|
|
||||||
proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET,
|
proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET,
|
||||||
body = ""): PFuture[TResponse] {.async.} =
|
body = ""): Future[TResponse] {.async.} =
|
||||||
## Connects to the hostname specified by the URL and performs a request
|
## Connects to the hostname specified by the URL and performs a request
|
||||||
## using the method specified.
|
## using the method specified.
|
||||||
##
|
##
|
||||||
|
|
@ -626,7 +643,7 @@ proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET,
|
||||||
|
|
||||||
result = await parseResponse(client, httpMethod != httpHEAD)
|
result = await parseResponse(client, httpMethod != httpHEAD)
|
||||||
|
|
||||||
proc get*(client: PAsyncHttpClient, url: string): PFuture[TResponse] {.async.} =
|
proc get*(client: PAsyncHttpClient, url: string): Future[TResponse] {.async.} =
|
||||||
## Connects to the hostname specified by the URL and performs a GET request.
|
## Connects to the hostname specified by the URL and performs a GET request.
|
||||||
##
|
##
|
||||||
## This procedure will follow redirects up to a maximum number of redirects
|
## This procedure will follow redirects up to a maximum number of redirects
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,17 @@ when defined(ssl):
|
||||||
|
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
type
|
type
|
||||||
SSLError* = object of Exception
|
SslError* = object of Exception
|
||||||
|
|
||||||
SSLCVerifyMode* = enum
|
SslCVerifyMode* = enum
|
||||||
CVerifyNone, CVerifyPeer
|
CVerifyNone, CVerifyPeer
|
||||||
|
|
||||||
SSLProtVersion* = enum
|
SslProtVersion* = enum
|
||||||
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
||||||
|
|
||||||
SSLContext* = distinct PSSLCTX
|
SslContext* = distinct SslCtx
|
||||||
|
|
||||||
SSLAcceptResult* = enum
|
SslAcceptResult* = enum
|
||||||
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
||||||
|
|
||||||
{.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
|
{.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
|
||||||
|
|
@ -125,7 +125,8 @@ when defined(ssl):
|
||||||
ErrLoadBioStrings()
|
ErrLoadBioStrings()
|
||||||
OpenSSL_add_all_algorithms()
|
OpenSSL_add_all_algorithms()
|
||||||
|
|
||||||
proc raiseSSLError(s = "") =
|
proc raiseSSLError*(s = "") =
|
||||||
|
## Raises a new SSL error.
|
||||||
if s != "":
|
if s != "":
|
||||||
raise newException(SSLError, s)
|
raise newException(SSLError, s)
|
||||||
let err = ErrPeekLastError()
|
let err = ErrPeekLastError()
|
||||||
|
|
@ -161,7 +162,7 @@ when defined(ssl):
|
||||||
certFile = "", keyFile = ""): PSSLContext =
|
certFile = "", keyFile = ""): PSSLContext =
|
||||||
## Creates an SSL context.
|
## Creates an SSL context.
|
||||||
##
|
##
|
||||||
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are
|
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1
|
||||||
## are available with the addition of ``protSSLv23`` which allows for
|
## are available with the addition of ``protSSLv23`` which allows for
|
||||||
## compatibility with all of them.
|
## compatibility with all of them.
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@
|
||||||
## For SSL support this module relies on OpenSSL. If you want to
|
## For SSL support this module relies on OpenSSL. If you want to
|
||||||
## enable SSL, compile with ``-d:ssl``.
|
## enable SSL, compile with ``-d:ssl``.
|
||||||
|
|
||||||
import sockets, strutils, strtabs, base64, os
|
import net, strutils, strtabs, base64, os
|
||||||
|
import asyncnet, asyncdispatch
|
||||||
|
|
||||||
type
|
type
|
||||||
Smtp* = object
|
Smtp* = object
|
||||||
|
|
@ -44,6 +45,13 @@ type
|
||||||
|
|
||||||
ReplyError* = object of IOError
|
ReplyError* = object of IOError
|
||||||
|
|
||||||
|
AsyncSmtp* = ref object
|
||||||
|
sock: AsyncSocket
|
||||||
|
address: string
|
||||||
|
port: Port
|
||||||
|
useSsl: bool
|
||||||
|
debug: bool
|
||||||
|
|
||||||
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
|
{.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].}
|
||||||
|
|
||||||
proc debugSend(smtp: TSMTP, cmd: string) =
|
proc debugSend(smtp: TSMTP, cmd: string) =
|
||||||
|
|
@ -70,19 +78,25 @@ proc checkReply(smtp: var TSMTP, reply: string) =
|
||||||
|
|
||||||
const compiledWithSsl = defined(ssl)
|
const compiledWithSsl = defined(ssl)
|
||||||
|
|
||||||
proc connect*(address: string, port = 25,
|
when not defined(ssl):
|
||||||
ssl = false, debug = false): TSMTP =
|
type PSSLContext = ref object
|
||||||
|
let defaultSSLContext: PSSLContext = nil
|
||||||
|
else:
|
||||||
|
let defaultSSLContext = newContext(verifyMode = CVerifyNone)
|
||||||
|
|
||||||
|
proc connect*(address: string, port = Port(25),
|
||||||
|
ssl = false, debug = false,
|
||||||
|
sslContext = defaultSSLContext): TSMTP =
|
||||||
## Establishes a connection with a SMTP server.
|
## Establishes a connection with a SMTP server.
|
||||||
## May fail with EInvalidReply or with a socket error.
|
## May fail with EInvalidReply or with a socket error.
|
||||||
result.sock = socket()
|
result.sock = newSocket()
|
||||||
if ssl:
|
if ssl:
|
||||||
when compiledWithSsl:
|
when compiledWithSsl:
|
||||||
let ctx = newContext(verifyMode = CVerifyNone)
|
sslContext.wrapSocket(result.sock)
|
||||||
ctx.wrapSocket(result.sock)
|
|
||||||
else:
|
else:
|
||||||
raise newException(ESystem,
|
raise newException(ESystem,
|
||||||
"SMTP module compiled without SSL support")
|
"SMTP module compiled without SSL support")
|
||||||
result.sock.connect(address, TPort(port))
|
result.sock.connect(address, port)
|
||||||
result.debug = debug
|
result.debug = debug
|
||||||
|
|
||||||
result.checkReply("220")
|
result.checkReply("220")
|
||||||
|
|
@ -162,6 +176,82 @@ proc `$`*(msg: TMessage): string =
|
||||||
result.add("\c\L")
|
result.add("\c\L")
|
||||||
result.add(msg.msgBody)
|
result.add(msg.msgBody)
|
||||||
|
|
||||||
|
proc newAsyncSmtp*(address: string, port: Port, useSsl = false,
|
||||||
|
sslContext = defaultSslContext): AsyncSmtp =
|
||||||
|
## Creates a new ``AsyncSmtp`` instance.
|
||||||
|
new result
|
||||||
|
result.address = address
|
||||||
|
result.port = port
|
||||||
|
result.useSsl = useSsl
|
||||||
|
|
||||||
|
result.sock = newAsyncSocket()
|
||||||
|
if useSsl:
|
||||||
|
when compiledWithSsl:
|
||||||
|
sslContext.wrapSocket(result.sock)
|
||||||
|
else:
|
||||||
|
raise newException(ESystem,
|
||||||
|
"SMTP module compiled without SSL support")
|
||||||
|
|
||||||
|
proc quitExcpt(smtp: AsyncSmtp, msg: string): PFuture[void] =
|
||||||
|
var retFuture = newFuture[void]()
|
||||||
|
var sendFut = smtp.sock.send("QUIT")
|
||||||
|
sendFut.callback =
|
||||||
|
proc () =
|
||||||
|
# TODO: Fix this in async procs.
|
||||||
|
raise newException(ReplyError, msg)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc checkReply(smtp: AsyncSmtp, reply: string) {.async.} =
|
||||||
|
var line = await smtp.sock.recvLine()
|
||||||
|
if not line.string.startswith(reply):
|
||||||
|
await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
|
||||||
|
|
||||||
|
proc connect*(smtp: AsyncSmtp) {.async.} =
|
||||||
|
## Establishes a connection with a SMTP server.
|
||||||
|
## May fail with EInvalidReply or with a socket error.
|
||||||
|
await smtp.sock.connect(smtp.address, smtp.port)
|
||||||
|
|
||||||
|
await smtp.checkReply("220")
|
||||||
|
await smtp.sock.send("HELO " & smtp.address & "\c\L")
|
||||||
|
await smtp.checkReply("250")
|
||||||
|
|
||||||
|
proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} =
|
||||||
|
## Sends an AUTH command to the server to login as the `username`
|
||||||
|
## using `password`.
|
||||||
|
## May fail with EInvalidReply.
|
||||||
|
|
||||||
|
await smtp.sock.send("AUTH LOGIN\c\L")
|
||||||
|
await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
|
||||||
|
# i.e "334 VXNlcm5hbWU6"
|
||||||
|
await smtp.sock.send(encode(username) & "\c\L")
|
||||||
|
await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
|
||||||
|
|
||||||
|
await smtp.sock.send(encode(password) & "\c\L")
|
||||||
|
await smtp.checkReply("235") # Check whether the authentification was successful.
|
||||||
|
|
||||||
|
proc sendMail*(smtp: AsyncSmtp, fromAddr: string,
|
||||||
|
toAddrs: seq[string], msg: string) {.async.} =
|
||||||
|
## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``.
|
||||||
|
## Messages may be formed using ``createMessage`` by converting the
|
||||||
|
## TMessage into a string.
|
||||||
|
|
||||||
|
await smtp.sock.send("MAIL FROM:<" & fromAddr & ">\c\L")
|
||||||
|
await smtp.checkReply("250")
|
||||||
|
for address in items(toAddrs):
|
||||||
|
await smtp.sock.send("RCPT TO:<" & smtp.address & ">\c\L")
|
||||||
|
await smtp.checkReply("250")
|
||||||
|
|
||||||
|
# Send the message
|
||||||
|
await smtp.sock.send("DATA " & "\c\L")
|
||||||
|
await smtp.checkReply("354")
|
||||||
|
await smtp.sock.send(msg & "\c\L")
|
||||||
|
await smtp.sock.send(".\c\L")
|
||||||
|
await smtp.checkReply("250")
|
||||||
|
|
||||||
|
proc close*(smtp: AsyncSmtp) {.async.} =
|
||||||
|
## Disconnects from the SMTP server and closes the socket.
|
||||||
|
await smtp.sock.send("QUIT\c\L")
|
||||||
|
smtp.sock.close()
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
#var msg = createMessage("Test subject!",
|
#var msg = createMessage("Test subject!",
|
||||||
|
|
@ -172,15 +262,16 @@ when isMainModule:
|
||||||
#smtp.sendmail("root@localhost", @["dominik@localhost"], $msg)
|
#smtp.sendmail("root@localhost", @["dominik@localhost"], $msg)
|
||||||
|
|
||||||
#echo(decode("a17sm3701420wbe.12"))
|
#echo(decode("a17sm3701420wbe.12"))
|
||||||
|
proc main() {.async.} =
|
||||||
|
var client = newAsyncSmtp("smtp.gmail.com", Port(465), true)
|
||||||
|
await client.connect()
|
||||||
|
await client.auth("johndoe", "foo")
|
||||||
var msg = createMessage("Hello from Nim's SMTP!",
|
var msg = createMessage("Hello from Nim's SMTP!",
|
||||||
"Hello!!!!.\n Is this awesome or what?",
|
"Hello!!!!.\n Is this awesome or what?",
|
||||||
@["someone@yahoo.com", "someone@gmail.com"])
|
@["blah@gmail.com"])
|
||||||
echo(msg)
|
echo(msg)
|
||||||
|
await client.sendMail("blah@gmail.com", @["blah@gmail.com"], $msg)
|
||||||
|
|
||||||
var smtp = connect("smtp.gmail.com", 465, True, True)
|
await client.close()
|
||||||
smtp.auth("someone", "password")
|
|
||||||
smtp.sendmail("someone@gmail.com",
|
|
||||||
@["someone@yahoo.com", "someone@gmail.com"], $msg)
|
|
||||||
smtp.close()
|
|
||||||
|
|
||||||
|
|
||||||
|
waitFor main()
|
||||||
|
|
|
||||||
|
|
@ -251,14 +251,14 @@ when defined(ssl):
|
||||||
ErrLoadBioStrings()
|
ErrLoadBioStrings()
|
||||||
OpenSSL_add_all_algorithms()
|
OpenSSL_add_all_algorithms()
|
||||||
|
|
||||||
proc SSLError(s = "") =
|
proc raiseSSLError(s = "") =
|
||||||
if s != "":
|
if s != "":
|
||||||
raise newException(ESSL, s)
|
raise newException(ESSL, s)
|
||||||
let err = ErrPeekLastError()
|
let err = ErrPeekLastError()
|
||||||
if err == 0:
|
if err == 0:
|
||||||
raise newException(ESSL, "No error reported.")
|
raise newException(ESSL, "No error reported.")
|
||||||
if err == -1:
|
if err == -1:
|
||||||
OSError(OSLastError())
|
raiseOSError(osLastError())
|
||||||
var errStr = ErrErrorString(err, nil)
|
var errStr = ErrErrorString(err, nil)
|
||||||
raise newException(ESSL, $errStr)
|
raise newException(ESSL, $errStr)
|
||||||
|
|
||||||
|
|
@ -272,18 +272,18 @@ when defined(ssl):
|
||||||
if certFile != "":
|
if certFile != "":
|
||||||
var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
|
var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
|
||||||
if ret != 1:
|
if ret != 1:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
|
|
||||||
# TODO: Password? www.rtfm.com/openssl-examples/part1.pdf
|
# TODO: Password? www.rtfm.com/openssl-examples/part1.pdf
|
||||||
if keyFile != "":
|
if keyFile != "":
|
||||||
if SSL_CTX_use_PrivateKey_file(ctx, keyFile,
|
if SSL_CTX_use_PrivateKey_file(ctx, keyFile,
|
||||||
SSL_FILETYPE_PEM) != 1:
|
SSL_FILETYPE_PEM) != 1:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
|
|
||||||
if SSL_CTX_check_private_key(ctx) != 1:
|
if SSL_CTX_check_private_key(ctx) != 1:
|
||||||
SSLError("Verification of private key file failed.")
|
raiseSslError("Verification of private key file failed.")
|
||||||
|
|
||||||
proc newContext*(protVersion = ProtSSLv23, verifyMode = CVerifyPeer,
|
proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
|
||||||
certFile = "", keyFile = ""): PSSLContext =
|
certFile = "", keyFile = ""): PSSLContext =
|
||||||
## Creates an SSL context.
|
## Creates an SSL context.
|
||||||
##
|
##
|
||||||
|
|
@ -308,21 +308,21 @@ when defined(ssl):
|
||||||
when not defined(linux) and not defined(OpenBSD):
|
when not defined(linux) and not defined(OpenBSD):
|
||||||
newCTX = SSL_CTX_new(SSLv2_method())
|
newCTX = SSL_CTX_new(SSLv2_method())
|
||||||
else:
|
else:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
of protSSLv3:
|
of protSSLv3:
|
||||||
newCTX = SSL_CTX_new(SSLv3_method())
|
newCTX = SSL_CTX_new(SSLv3_method())
|
||||||
of protTLSv1:
|
of protTLSv1:
|
||||||
newCTX = SSL_CTX_new(TLSv1_method())
|
newCTX = SSL_CTX_new(TLSv1_method())
|
||||||
|
|
||||||
if newCTX.SSLCTXSetCipherList("ALL") != 1:
|
if newCTX.SSLCTXSetCipherList("ALL") != 1:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
case verifyMode
|
case verifyMode
|
||||||
of CVerifyPeer:
|
of CVerifyPeer:
|
||||||
newCTX.SSLCTXSetVerify(SSLVerifyPeer, nil)
|
newCTX.SSLCTXSetVerify(SSLVerifyPeer, nil)
|
||||||
of CVerifyNone:
|
of CVerifyNone:
|
||||||
newCTX.SSLCTXSetVerify(SSLVerifyNone, nil)
|
newCTX.SSLCTXSetVerify(SSLVerifyNone, nil)
|
||||||
if newCTX == nil:
|
if newCTX == nil:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
|
|
||||||
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
|
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
|
||||||
newCTX.loadCertificates(certFile, keyFile)
|
newCTX.loadCertificates(certFile, keyFile)
|
||||||
|
|
@ -341,10 +341,10 @@ when defined(ssl):
|
||||||
socket.sslNoHandshake = false
|
socket.sslNoHandshake = false
|
||||||
socket.sslHasPeekChar = false
|
socket.sslHasPeekChar = false
|
||||||
if socket.sslHandle == nil:
|
if socket.sslHandle == nil:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
|
|
||||||
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
|
|
||||||
proc raiseSocketError*(socket: Socket, err: int = -1, async = false) =
|
proc raiseSocketError*(socket: Socket, err: int = -1, async = false) =
|
||||||
## Raises proper errors based on return values of ``recv`` functions.
|
## Raises proper errors based on return values of ``recv`` functions.
|
||||||
|
|
@ -359,20 +359,20 @@ proc raiseSocketError*(socket: Socket, err: int = -1, async = false) =
|
||||||
var ret = SSLGetError(socket.sslHandle, err.cint)
|
var ret = SSLGetError(socket.sslHandle, err.cint)
|
||||||
case ret
|
case ret
|
||||||
of SSL_ERROR_ZERO_RETURN:
|
of SSL_ERROR_ZERO_RETURN:
|
||||||
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||||
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
||||||
if async:
|
if async:
|
||||||
return
|
return
|
||||||
else: SSLError("Not enough data on socket.")
|
else: raiseSslError("Not enough data on socket.")
|
||||||
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
|
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
|
||||||
if async:
|
if async:
|
||||||
return
|
return
|
||||||
else: SSLError("Not enough data on socket.")
|
else: raiseSslError("Not enough data on socket.")
|
||||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
SSLError("Function for x509 lookup has been called.")
|
raiseSslError("Function for x509 lookup has been called.")
|
||||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
else: SSLError("Unknown Error")
|
else: raiseSslError("Unknown Error")
|
||||||
|
|
||||||
if err == -1 and not (when defined(ssl): socket.isSSL else: false):
|
if err == -1 and not (when defined(ssl): socket.isSSL else: false):
|
||||||
let lastError = osLastError()
|
let lastError = osLastError()
|
||||||
|
|
@ -545,16 +545,16 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string) {.
|
||||||
if err != SSL_ERROR_WANT_ACCEPT:
|
if err != SSL_ERROR_WANT_ACCEPT:
|
||||||
case err
|
case err
|
||||||
of SSL_ERROR_ZERO_RETURN:
|
of SSL_ERROR_ZERO_RETURN:
|
||||||
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||||
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
|
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
|
||||||
SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
||||||
SSLError("acceptAddrSSL should be used for non-blocking SSL sockets.")
|
raiseSslError("acceptAddrSSL should be used for non-blocking SSL sockets.")
|
||||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
SSLError("Function for x509 lookup has been called.")
|
raiseSslError("Function for x509 lookup has been called.")
|
||||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
else:
|
else:
|
||||||
SSLError("Unknown error")
|
raiseSslError("Unknown error")
|
||||||
|
|
||||||
proc setBlocking*(s: Socket, blocking: bool) {.tags: [], gcsafe.}
|
proc setBlocking*(s: Socket, blocking: bool) {.tags: [], gcsafe.}
|
||||||
## Sets blocking mode on socket
|
## Sets blocking mode on socket
|
||||||
|
|
@ -591,17 +591,17 @@ when defined(ssl):
|
||||||
if err != SSL_ERROR_WANT_ACCEPT:
|
if err != SSL_ERROR_WANT_ACCEPT:
|
||||||
case err
|
case err
|
||||||
of SSL_ERROR_ZERO_RETURN:
|
of SSL_ERROR_ZERO_RETURN:
|
||||||
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||||
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
|
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
|
||||||
SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
||||||
client.sslNoHandshake = true
|
client.sslNoHandshake = true
|
||||||
return AcceptNoHandshake
|
return AcceptNoHandshake
|
||||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
SSLError("Function for x509 lookup has been called.")
|
raiseSslError("Function for x509 lookup has been called.")
|
||||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
else:
|
else:
|
||||||
SSLError("Unknown error")
|
raiseSslError("Unknown error")
|
||||||
client.sslNoHandshake = false
|
client.sslNoHandshake = false
|
||||||
|
|
||||||
if client.isSSL and client.sslNoHandshake:
|
if client.isSSL and client.sslNoHandshake:
|
||||||
|
|
@ -813,16 +813,16 @@ proc connect*(socket: Socket, address: string, port = Port(0),
|
||||||
let err = SSLGetError(socket.sslHandle, ret)
|
let err = SSLGetError(socket.sslHandle, ret)
|
||||||
case err
|
case err
|
||||||
of SSL_ERROR_ZERO_RETURN:
|
of SSL_ERROR_ZERO_RETURN:
|
||||||
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||||
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT,
|
of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT,
|
||||||
SSL_ERROR_WANT_ACCEPT:
|
SSL_ERROR_WANT_ACCEPT:
|
||||||
SSLError("The operation did not complete. Perhaps you should use connectAsync?")
|
raiseSslError("The operation did not complete. Perhaps you should use connectAsync?")
|
||||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
SSLError("Function for x509 lookup has been called.")
|
raiseSslError("Function for x509 lookup has been called.")
|
||||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
else:
|
else:
|
||||||
SSLError("Unknown error")
|
raiseSslError("Unknown error")
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
var s: TSockAddrIn
|
var s: TSockAddrIn
|
||||||
|
|
@ -901,19 +901,19 @@ when defined(ssl):
|
||||||
var errret = SSLGetError(socket.sslHandle, ret)
|
var errret = SSLGetError(socket.sslHandle, ret)
|
||||||
case errret
|
case errret
|
||||||
of SSL_ERROR_ZERO_RETURN:
|
of SSL_ERROR_ZERO_RETURN:
|
||||||
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||||
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT,
|
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT,
|
||||||
SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE:
|
SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE:
|
||||||
return false
|
return false
|
||||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
SSLError("Function for x509 lookup has been called.")
|
raiseSslError("Function for x509 lookup has been called.")
|
||||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
else:
|
else:
|
||||||
SSLError("Unknown Error")
|
raiseSslError("Unknown Error")
|
||||||
socket.sslNoHandshake = false
|
socket.sslNoHandshake = false
|
||||||
else:
|
else:
|
||||||
SSLError("Socket is not an SSL socket.")
|
raiseSslError("Socket is not an SSL socket.")
|
||||||
|
|
||||||
proc gotHandshake*(socket: TSocket): bool =
|
proc gotHandshake*(socket: TSocket): bool =
|
||||||
## Determines whether a handshake has occurred between a client (``socket``)
|
## Determines whether a handshake has occurred between a client (``socket``)
|
||||||
|
|
@ -923,7 +923,7 @@ when defined(ssl):
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
return not socket.sslNoHandshake
|
return not socket.sslNoHandshake
|
||||||
else:
|
else:
|
||||||
SSLError("Socket is not an SSL socket.")
|
raiseSslError("Socket is not an SSL socket.")
|
||||||
|
|
||||||
proc timeValFromMilliseconds(timeout = 500): Timeval =
|
proc timeValFromMilliseconds(timeout = 500): Timeval =
|
||||||
if timeout != -1:
|
if timeout != -1:
|
||||||
|
|
@ -1412,7 +1412,7 @@ proc recv*(socket: Socket): TaintedString {.tags: [ReadIOEffect], deprecated.} =
|
||||||
while true:
|
while true:
|
||||||
var bytesRead = recv(socket, cstring(buf), bufSize-1)
|
var bytesRead = recv(socket, cstring(buf), bufSize-1)
|
||||||
# Error
|
# Error
|
||||||
if bytesRead == -1: OSError(OSLastError())
|
if bytesRead == -1: OSError(osLastError())
|
||||||
|
|
||||||
buf[bytesRead] = '\0' # might not be necessary
|
buf[bytesRead] = '\0' # might not be necessary
|
||||||
setLen(buf, bytesRead)
|
setLen(buf, bytesRead)
|
||||||
|
|
@ -1457,16 +1457,16 @@ proc recvAsync*(socket: Socket, s: var TaintedString): bool {.
|
||||||
var ret = SSLGetError(socket.sslHandle, bytesRead.cint)
|
var ret = SSLGetError(socket.sslHandle, bytesRead.cint)
|
||||||
case ret
|
case ret
|
||||||
of SSL_ERROR_ZERO_RETURN:
|
of SSL_ERROR_ZERO_RETURN:
|
||||||
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||||
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
||||||
SSLError("Unexpected error occured.") # This should just not happen.
|
raiseSslError("Unexpected error occured.") # This should just not happen.
|
||||||
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
|
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
|
||||||
return false
|
return false
|
||||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
SSLError("Function for x509 lookup has been called.")
|
raiseSslError("Function for x509 lookup has been called.")
|
||||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
else: SSLError("Unknown Error")
|
else: raiseSslError("Unknown Error")
|
||||||
|
|
||||||
if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
|
if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
|
||||||
let err = osLastError()
|
let err = osLastError()
|
||||||
|
|
@ -1578,7 +1578,7 @@ proc send*(socket: Socket, data: string) {.tags: [WriteIOEffect].} =
|
||||||
if sent < 0:
|
if sent < 0:
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
|
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
|
|
@ -1600,16 +1600,16 @@ proc sendAsync*(socket: Socket, data: string): int {.tags: [WriteIOEffect].} =
|
||||||
let ret = SSLGetError(socket.sslHandle, result.cint)
|
let ret = SSLGetError(socket.sslHandle, result.cint)
|
||||||
case ret
|
case ret
|
||||||
of SSL_ERROR_ZERO_RETURN:
|
of SSL_ERROR_ZERO_RETURN:
|
||||||
SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
raiseSslError("TLS/SSL connection failed to initiate, socket closed prematurely.")
|
||||||
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT:
|
||||||
SSLError("Unexpected error occured.") # This should just not happen.
|
raiseSslError("Unexpected error occured.") # This should just not happen.
|
||||||
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
|
of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ:
|
||||||
return 0
|
return 0
|
||||||
of SSL_ERROR_WANT_X509_LOOKUP:
|
of SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
SSLError("Function for x509 lookup has been called.")
|
raiseSslError("Function for x509 lookup has been called.")
|
||||||
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
|
||||||
SSLError()
|
raiseSslError()
|
||||||
else: SSLError("Unknown Error")
|
else: raiseSslError("Unknown Error")
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
if result == -1:
|
if result == -1:
|
||||||
|
|
@ -1692,7 +1692,7 @@ discard """ proc setReuseAddr*(s: TSocket) =
|
||||||
var blah: int = 1
|
var blah: int = 1
|
||||||
var mode = SO_REUSEADDR
|
var mode = SO_REUSEADDR
|
||||||
if setsockopt(s.fd, SOL_SOCKET, mode, addr blah, TSOcklen(sizeof(int))) == -1:
|
if setsockopt(s.fd, SOL_SOCKET, mode, addr blah, TSOcklen(sizeof(int))) == -1:
|
||||||
OSError(OSLastError()) """
|
raiseOSError(osLastError()) """
|
||||||
|
|
||||||
proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
|
proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
|
||||||
af: Domain = AF_INET) {.tags: [ReadIOEffect, WriteIOEffect].} =
|
af: Domain = AF_INET) {.tags: [ReadIOEffect, WriteIOEffect].} =
|
||||||
|
|
|
||||||
|
|
@ -63,14 +63,13 @@ type
|
||||||
SslStruct {.final, pure.} = object
|
SslStruct {.final, pure.} = object
|
||||||
SslPtr* = ptr SslStruct
|
SslPtr* = ptr SslStruct
|
||||||
PSslPtr* = ptr SslPtr
|
PSslPtr* = ptr SslPtr
|
||||||
PSSL_CTX* = SslPtr
|
SslCtx* = SslPtr
|
||||||
PSSL* = SslPtr
|
|
||||||
PSSL_METHOD* = SslPtr
|
PSSL_METHOD* = SslPtr
|
||||||
PX509* = SslPtr
|
PX509* = SslPtr
|
||||||
PX509_NAME* = SslPtr
|
PX509_NAME* = SslPtr
|
||||||
PEVP_MD* = SslPtr
|
PEVP_MD* = SslPtr
|
||||||
PBIO_METHOD* = SslPtr
|
PBIO_METHOD* = SslPtr
|
||||||
PBIO* = SslPtr
|
BIO* = SslPtr
|
||||||
EVP_PKEY* = SslPtr
|
EVP_PKEY* = SslPtr
|
||||||
PRSA* = SslPtr
|
PRSA* = SslPtr
|
||||||
PASN1_UTCTIME* = SslPtr
|
PASN1_UTCTIME* = SslPtr
|
||||||
|
|
@ -85,6 +84,8 @@ type
|
||||||
|
|
||||||
des_key_schedule* = array[1..16, des_ks_struct]
|
des_key_schedule* = array[1..16, des_ks_struct]
|
||||||
|
|
||||||
|
{.deprecated: [PSSL: SslPtr, PSSL_CTX: SslCtx, PBIO: BIO].}
|
||||||
|
|
||||||
const
|
const
|
||||||
EVP_MAX_MD_SIZE* = 16 + 20
|
EVP_MAX_MD_SIZE* = 16 + 20
|
||||||
SSL_ERROR_NONE* = 0
|
SSL_ERROR_NONE* = 0
|
||||||
|
|
@ -282,6 +283,34 @@ proc SSL_CTX_ctrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: pointer): int{.
|
||||||
proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int =
|
proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int =
|
||||||
result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil)
|
result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil)
|
||||||
|
|
||||||
|
proc bioNew*(b: PBIO_METHOD): PBIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".}
|
||||||
|
proc bioFreeAll*(b: PBIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".}
|
||||||
|
proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".}
|
||||||
|
proc bioCtrlPending*(b: PBIO): cInt{.cdecl, dynlib: DLLUtilName, importc: "BIO_ctrl_pending".}
|
||||||
|
proc bioRead*(b: PBIO, Buf: cstring, length: cInt): cInt{.cdecl,
|
||||||
|
dynlib: DLLUtilName, importc: "BIO_read".}
|
||||||
|
proc bioWrite*(b: PBIO, Buf: cstring, length: cInt): cInt{.cdecl,
|
||||||
|
dynlib: DLLUtilName, importc: "BIO_write".}
|
||||||
|
|
||||||
|
proc sslSetConnectState*(s: SslPtr) {.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc: "SSL_set_connect_state".}
|
||||||
|
proc sslSetAcceptState*(s: SslPtr) {.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc: "SSL_set_accept_state".}
|
||||||
|
|
||||||
|
proc sslRead*(ssl: SslPtr, buf: cstring, num: cInt): cInt{.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc: "SSL_read".}
|
||||||
|
proc sslPeek*(ssl: SslPtr, buf: cstring, num: cInt): cInt{.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc: "SSL_peek".}
|
||||||
|
proc sslWrite*(ssl: SslPtr, buf: cstring, num: cInt): cInt{.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc: "SSL_write".}
|
||||||
|
|
||||||
|
proc sslSetBio*(ssl: SslPtr, rbio, wbio: BIO) {.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc: "SSL_set_bio".}
|
||||||
|
|
||||||
|
proc sslDoHandshake*(ssl: SslPtr): cint {.cdecl,
|
||||||
|
dynlib: DLLSSLName, importc: "SSL_do_handshake".}
|
||||||
|
|
||||||
|
|
||||||
when true:
|
when true:
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
|
|
@ -328,12 +357,7 @@ else:
|
||||||
|
|
||||||
proc SslConnect*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.}
|
proc SslConnect*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.}
|
||||||
|
|
||||||
proc SslRead*(ssl: PSSL, buf: SslPtr, num: cInt): cInt{.cdecl,
|
|
||||||
dynlib: DLLSSLName, importc.}
|
|
||||||
proc SslPeek*(ssl: PSSL, buf: SslPtr, num: cInt): cInt{.cdecl,
|
|
||||||
dynlib: DLLSSLName, importc.}
|
|
||||||
proc SslWrite*(ssl: PSSL, buf: SslPtr, num: cInt): cInt{.cdecl,
|
|
||||||
dynlib: DLLSSLName, importc.}
|
|
||||||
proc SslGetVersion*(ssl: PSSL): cstring{.cdecl, dynlib: DLLSSLName, importc.}
|
proc SslGetVersion*(ssl: PSSL): cstring{.cdecl, dynlib: DLLSSLName, importc.}
|
||||||
proc SslGetPeerCertificate*(ssl: PSSL): PX509{.cdecl, dynlib: DLLSSLName,
|
proc SslGetPeerCertificate*(ssl: PSSL): PX509{.cdecl, dynlib: DLLSSLName,
|
||||||
importc.}
|
importc.}
|
||||||
|
|
@ -393,14 +417,7 @@ else:
|
||||||
proc OPENSSLaddallalgorithms*(){.cdecl, dynlib: DLLUtilName, importc.}
|
proc OPENSSLaddallalgorithms*(){.cdecl, dynlib: DLLUtilName, importc.}
|
||||||
proc CRYPTOcleanupAllExData*(){.cdecl, dynlib: DLLUtilName, importc.}
|
proc CRYPTOcleanupAllExData*(){.cdecl, dynlib: DLLUtilName, importc.}
|
||||||
proc RandScreen*(){.cdecl, dynlib: DLLUtilName, importc.}
|
proc RandScreen*(){.cdecl, dynlib: DLLUtilName, importc.}
|
||||||
proc BioNew*(b: PBIO_METHOD): PBIO{.cdecl, dynlib: DLLUtilName, importc.}
|
|
||||||
proc BioFreeAll*(b: PBIO){.cdecl, dynlib: DLLUtilName, importc.}
|
|
||||||
proc BioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc.}
|
|
||||||
proc BioCtrlPending*(b: PBIO): cInt{.cdecl, dynlib: DLLUtilName, importc.}
|
|
||||||
proc BioRead*(b: PBIO, Buf: cstring, length: cInt): cInt{.cdecl,
|
|
||||||
dynlib: DLLUtilName, importc.}
|
|
||||||
proc BioWrite*(b: PBIO, Buf: cstring, length: cInt): cInt{.cdecl,
|
|
||||||
dynlib: DLLUtilName, importc.}
|
|
||||||
proc d2iPKCS12bio*(b: PBIO, Pkcs12: SslPtr): SslPtr{.cdecl, dynlib: DLLUtilName,
|
proc d2iPKCS12bio*(b: PBIO, Pkcs12: SslPtr): SslPtr{.cdecl, dynlib: DLLUtilName,
|
||||||
importc.}
|
importc.}
|
||||||
proc PKCS12parse*(p12: SslPtr, pass: cstring, pkey, cert, ca: var SslPtr): cint{.
|
proc PKCS12parse*(p12: SslPtr, pass: cstring, pkey, cert, ca: var SslPtr): cint{.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue