Merge branch 'starttls' of https://github.com/wiml/Nim into wiml-starttls
Conflicts: lib/pure/net.nim
This commit is contained in:
commit
8853dfb353
2 changed files with 89 additions and 53 deletions
|
|
@ -472,6 +472,15 @@ when defined(ssl):
|
||||||
socket.bioOut = bioNew(bio_s_mem())
|
socket.bioOut = bioNew(bio_s_mem())
|
||||||
sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
|
sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
|
||||||
|
|
||||||
|
proc wrapSocket*(ctx: SslContext, socket: AsyncSocket, handshake: SslHandshakeType) =
|
||||||
|
wrapSocket(ctx, socket)
|
||||||
|
|
||||||
|
case handshake
|
||||||
|
of handshakeAsClient:
|
||||||
|
sslSetConnectState(socket.sslHandle)
|
||||||
|
of handshakeAsServer:
|
||||||
|
sslSetAcceptState(socket.sslHandle)
|
||||||
|
|
||||||
proc getSockOpt*(socket: AsyncSocket, opt: SOBool, level = SOL_SOCKET): bool {.
|
proc getSockOpt*(socket: AsyncSocket, opt: SOBool, level = SOL_SOCKET): bool {.
|
||||||
tags: [ReadIOEffect].} =
|
tags: [ReadIOEffect].} =
|
||||||
## Retrieves option ``opt`` as a boolean value.
|
## Retrieves option ``opt`` as a boolean value.
|
||||||
|
|
|
||||||
133
lib/pure/net.nim
133
lib/pure/net.nim
|
|
@ -26,15 +26,18 @@ when defined(ssl):
|
||||||
|
|
||||||
SslCVerifyMode* = enum
|
SslCVerifyMode* = enum
|
||||||
CVerifyNone, CVerifyPeer
|
CVerifyNone, CVerifyPeer
|
||||||
|
|
||||||
SslProtVersion* = enum
|
SslProtVersion* = enum
|
||||||
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
protSSLv2, protSSLv3, protTLSv1, protSSLv23
|
||||||
|
|
||||||
SslContext* = distinct SslCtx
|
SslContext* = distinct SslCtx
|
||||||
|
|
||||||
SslAcceptResult* = enum
|
SslAcceptResult* = enum
|
||||||
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
|
||||||
|
|
||||||
|
SslHandshakeType* = enum
|
||||||
|
handshakeAsClient, handshakeAsServer
|
||||||
|
|
||||||
{.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
|
{.deprecated: [ESSL: SSLError, TSSLCVerifyMode: SSLCVerifyMode,
|
||||||
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
|
TSSLProtVersion: SSLProtVersion, PSSLContext: SSLContext,
|
||||||
TSSLAcceptResult: SSLAcceptResult].}
|
TSSLAcceptResult: SSLAcceptResult].}
|
||||||
|
|
@ -86,7 +89,7 @@ type
|
||||||
IPv6, ## IPv6 address
|
IPv6, ## IPv6 address
|
||||||
IPv4 ## IPv4 address
|
IPv4 ## IPv4 address
|
||||||
|
|
||||||
IpAddress* = object ## stores an arbitrary IP address
|
IpAddress* = object ## stores an arbitrary IP address
|
||||||
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
|
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
|
||||||
of IpAddressFamily.IPv6:
|
of IpAddressFamily.IPv6:
|
||||||
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
|
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
|
||||||
|
|
@ -98,6 +101,8 @@ type
|
||||||
|
|
||||||
proc isIpAddress*(address_str: string): bool {.tags: [].}
|
proc isIpAddress*(address_str: string): bool {.tags: [].}
|
||||||
proc parseIpAddress*(address_str: string): IpAddress
|
proc parseIpAddress*(address_str: string): IpAddress
|
||||||
|
proc socketError*(socket: Socket, err: int = -1, async = false,
|
||||||
|
lastError = (-1).OSErrorCode): void
|
||||||
|
|
||||||
proc isDisconnectionError*(flags: set[SocketFlag],
|
proc isDisconnectionError*(flags: set[SocketFlag],
|
||||||
lastError: OSErrorCode): bool =
|
lastError: OSErrorCode): bool =
|
||||||
|
|
@ -109,7 +114,7 @@ proc isDisconnectionError*(flags: set[SocketFlag],
|
||||||
WSAEDISCON, ERROR_NETNAME_DELETED}
|
WSAEDISCON, ERROR_NETNAME_DELETED}
|
||||||
else:
|
else:
|
||||||
SocketFlag.SafeDisconn in flags and
|
SocketFlag.SafeDisconn in flags and
|
||||||
lastError.int32 in {ECONNRESET, EPIPE, ENETRESET}
|
lastError.int32 in {ECONNRESET, EPIPE, ENETRESET}
|
||||||
|
|
||||||
proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
|
proc toOSFlags*(socketFlags: set[SocketFlag]): cint =
|
||||||
## Converts the flags into the underlying OS representation.
|
## Converts the flags into the underlying OS representation.
|
||||||
|
|
@ -172,27 +177,27 @@ when defined(ssl):
|
||||||
raise newException(system.IOError, "Certificate file could not be found: " & certFile)
|
raise newException(system.IOError, "Certificate file could not be found: " & certFile)
|
||||||
if keyFile != "" and not existsFile(keyFile):
|
if keyFile != "" and not existsFile(keyFile):
|
||||||
raise newException(system.IOError, "Key file could not be found: " & keyFile)
|
raise newException(system.IOError, "Key file could not be found: " & keyFile)
|
||||||
|
|
||||||
if certFile != "":
|
if certFile != "":
|
||||||
var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
|
var ret = SSLCTXUseCertificateChainFile(ctx, certFile)
|
||||||
if ret != 1:
|
if ret != 1:
|
||||||
raiseSSLError()
|
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:
|
||||||
raiseSSLError()
|
raiseSSLError()
|
||||||
|
|
||||||
if SSL_CTX_check_private_key(ctx) != 1:
|
if SSL_CTX_check_private_key(ctx) != 1:
|
||||||
raiseSSLError("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 = ""): SSLContext =
|
certFile = "", keyFile = ""): SSLContext =
|
||||||
## Creates an SSL context.
|
## Creates an SSL context.
|
||||||
##
|
##
|
||||||
## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1
|
## 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.
|
||||||
##
|
##
|
||||||
## There are currently only two options for verify mode;
|
## There are currently only two options for verify mode;
|
||||||
|
|
@ -217,7 +222,7 @@ when defined(ssl):
|
||||||
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:
|
||||||
raiseSSLError()
|
raiseSSLError()
|
||||||
case verifyMode
|
case verifyMode
|
||||||
|
|
@ -236,9 +241,13 @@ when defined(ssl):
|
||||||
## Wraps a socket in an SSL context. This function effectively turns
|
## Wraps a socket in an SSL context. This function effectively turns
|
||||||
## ``socket`` into an SSL socket.
|
## ``socket`` into an SSL socket.
|
||||||
##
|
##
|
||||||
|
## This must be called on an unconnected socket; an SSL session will
|
||||||
|
## be started when the socket is connected.
|
||||||
|
##
|
||||||
## **Disclaimer**: This code is not well tested, may be very unsafe and
|
## **Disclaimer**: This code is not well tested, may be very unsafe and
|
||||||
## prone to security vulnerabilities.
|
## prone to security vulnerabilities.
|
||||||
|
|
||||||
|
assert (not socket.isSSL)
|
||||||
socket.isSSL = true
|
socket.isSSL = true
|
||||||
socket.sslContext = ctx
|
socket.sslContext = ctx
|
||||||
socket.sslHandle = SSLNew(SSLCTX(socket.sslContext))
|
socket.sslHandle = SSLNew(SSLCTX(socket.sslContext))
|
||||||
|
|
@ -246,10 +255,28 @@ when defined(ssl):
|
||||||
socket.sslHasPeekChar = false
|
socket.sslHasPeekChar = false
|
||||||
if socket.sslHandle == nil:
|
if socket.sslHandle == nil:
|
||||||
raiseSSLError()
|
raiseSSLError()
|
||||||
|
|
||||||
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
if SSLSetFd(socket.sslHandle, socket.fd) != 1:
|
||||||
raiseSSLError()
|
raiseSSLError()
|
||||||
|
|
||||||
|
proc wrapSocket*(ctx: SSLContext, socket: Socket, handshake: SslHandshakeType) =
|
||||||
|
## Wraps a socket in an SSL context. This function effectively turns
|
||||||
|
## ``socket`` into an SSL socket.
|
||||||
|
##
|
||||||
|
## This should be called on a connected socket, and will perform
|
||||||
|
## an SSL handshake immediately.
|
||||||
|
##
|
||||||
|
## **Disclaimer**: This code is not well tested, may be very unsafe and
|
||||||
|
## prone to security vulnerabilities.
|
||||||
|
wrapSocket(ctx, socket)
|
||||||
|
case handshake
|
||||||
|
of handshakeAsClient:
|
||||||
|
let ret = SSLConnect(socket.sslHandle)
|
||||||
|
socketError(socket, ret)
|
||||||
|
of handshakeAsServer:
|
||||||
|
let ret = SSLAccept(socket.sslHandle)
|
||||||
|
socketError(socket, ret)
|
||||||
|
|
||||||
proc getSocketError*(socket: Socket): OSErrorCode =
|
proc getSocketError*(socket: Socket): OSErrorCode =
|
||||||
## Checks ``osLastError`` for a valid error. If it has been reset it uses
|
## Checks ``osLastError`` for a valid error. If it has been reset it uses
|
||||||
## the last error stored in the socket object.
|
## the last error stored in the socket object.
|
||||||
|
|
@ -302,7 +329,7 @@ proc socketError*(socket: Socket, err: int = -1, async = false,
|
||||||
of SSL_ERROR_SSL:
|
of SSL_ERROR_SSL:
|
||||||
raiseSSLError()
|
raiseSSLError()
|
||||||
else: raiseSSLError("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):
|
||||||
var lastE = if lastError.int == -1: getSocketError(socket) else: lastError
|
var lastE = if lastError.int == -1: getSocketError(socket) else: lastError
|
||||||
if async:
|
if async:
|
||||||
|
|
@ -317,8 +344,8 @@ proc socketError*(socket: Socket, err: int = -1, async = false,
|
||||||
else: raiseOSError(lastE)
|
else: raiseOSError(lastE)
|
||||||
|
|
||||||
proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
|
||||||
## 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.
|
||||||
|
|
@ -360,7 +387,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
|
||||||
## The resulting client will inherit any properties of the server socket. For
|
## The resulting client will inherit any properties of the server socket. For
|
||||||
## example: whether the socket is buffered or not.
|
## example: whether the socket is buffered or not.
|
||||||
##
|
##
|
||||||
## **Note**: ``client`` must be initialised (with ``new``), this function
|
## **Note**: ``client`` must be initialised (with ``new``), this function
|
||||||
## makes no effort to initialise the ``client`` variable.
|
## makes no effort to initialise the ``client`` variable.
|
||||||
##
|
##
|
||||||
## The ``accept`` call may result in an error if the connecting socket
|
## The ``accept`` call may result in an error if the connecting socket
|
||||||
|
|
@ -372,7 +399,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
|
||||||
var addrLen = sizeof(sockAddress).SockLen
|
var addrLen = sizeof(sockAddress).SockLen
|
||||||
var sock = accept(server.fd, cast[ptr SockAddr](addr(sockAddress)),
|
var sock = accept(server.fd, cast[ptr SockAddr](addr(sockAddress)),
|
||||||
addr(addrLen))
|
addr(addrLen))
|
||||||
|
|
||||||
if sock == osInvalidSocket:
|
if sock == osInvalidSocket:
|
||||||
let err = osLastError()
|
let err = osLastError()
|
||||||
if flags.isDisconnectionError(err):
|
if flags.isDisconnectionError(err):
|
||||||
|
|
@ -386,11 +413,11 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
if server.isSSL:
|
if server.isSSL:
|
||||||
# We must wrap the client sock in a ssl context.
|
# We must wrap the client sock in a ssl context.
|
||||||
|
|
||||||
server.sslContext.wrapSocket(client)
|
server.sslContext.wrapSocket(client)
|
||||||
let ret = SSLAccept(client.sslHandle)
|
let ret = SSLAccept(client.sslHandle)
|
||||||
socketError(client, ret, false)
|
socketError(client, ret, false)
|
||||||
|
|
||||||
# Client socket is set above.
|
# Client socket is set above.
|
||||||
address = $inet_ntoa(sockAddress.sin_addr)
|
address = $inet_ntoa(sockAddress.sin_addr)
|
||||||
|
|
||||||
|
|
@ -398,9 +425,9 @@ when false: #defined(ssl):
|
||||||
proc acceptAddrSSL*(server: Socket, client: var Socket,
|
proc acceptAddrSSL*(server: Socket, client: var Socket,
|
||||||
address: var string): SSLAcceptResult {.
|
address: var string): SSLAcceptResult {.
|
||||||
tags: [ReadIOEffect].} =
|
tags: [ReadIOEffect].} =
|
||||||
## This procedure should only be used for non-blocking **SSL** sockets.
|
## This procedure should only be used for non-blocking **SSL** sockets.
|
||||||
## It will immediately return with one of the following values:
|
## It will immediately return with one of the following values:
|
||||||
##
|
##
|
||||||
## ``AcceptSuccess`` will be returned when a client has been successfully
|
## ``AcceptSuccess`` will be returned when a client has been successfully
|
||||||
## accepted and the handshake has been successfully performed between
|
## accepted and the handshake has been successfully performed between
|
||||||
## ``server`` and the newly connected client.
|
## ``server`` and the newly connected client.
|
||||||
|
|
@ -417,7 +444,7 @@ when false: #defined(ssl):
|
||||||
if server.isSSL:
|
if server.isSSL:
|
||||||
client.setBlocking(false)
|
client.setBlocking(false)
|
||||||
# We must wrap the client sock in a ssl context.
|
# We must wrap the client sock in a ssl context.
|
||||||
|
|
||||||
if not client.isSSL or client.sslHandle == nil:
|
if not client.isSSL or client.sslHandle == nil:
|
||||||
server.sslContext.wrapSocket(client)
|
server.sslContext.wrapSocket(client)
|
||||||
let ret = SSLAccept(client.sslHandle)
|
let ret = SSLAccept(client.sslHandle)
|
||||||
|
|
@ -450,7 +477,7 @@ proc accept*(server: Socket, client: var Socket,
|
||||||
flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} =
|
flags = {SocketFlag.SafeDisconn}) {.tags: [ReadIOEffect].} =
|
||||||
## Equivalent to ``acceptAddr`` but doesn't return the address, only the
|
## Equivalent to ``acceptAddr`` but doesn't return the address, only the
|
||||||
## socket.
|
## socket.
|
||||||
##
|
##
|
||||||
## **Note**: ``client`` must be initialised (with ``new``), this function
|
## **Note**: ``client`` must be initialised (with ``new``), this function
|
||||||
## makes no effort to initialise the ``client`` variable.
|
## makes no effort to initialise the ``client`` variable.
|
||||||
##
|
##
|
||||||
|
|
@ -504,7 +531,7 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {
|
||||||
var valuei = cint(if value: 1 else: 0)
|
var valuei = cint(if value: 1 else: 0)
|
||||||
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
|
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
|
||||||
|
|
||||||
proc connect*(socket: Socket, address: string, port = Port(0),
|
proc connect*(socket: Socket, address: string, port = Port(0),
|
||||||
af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
|
af: Domain = AF_INET) {.tags: [ReadIOEffect].} =
|
||||||
## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
|
## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
|
||||||
## host name. If ``address`` is a host name, this function will try each IP
|
## host name. If ``address`` is a host name, this function will try each IP
|
||||||
|
|
@ -526,7 +553,7 @@ proc connect*(socket: Socket, address: string, port = Port(0),
|
||||||
|
|
||||||
dealloc(aiList)
|
dealloc(aiList)
|
||||||
if not success: raiseOSError(lastError)
|
if not success: raiseOSError(lastError)
|
||||||
|
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
# RFC3546 for SNI specifies that IP addresses are not allowed.
|
# RFC3546 for SNI specifies that IP addresses are not allowed.
|
||||||
|
|
@ -634,12 +661,12 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect]
|
||||||
if socket.isBuffered:
|
if socket.isBuffered:
|
||||||
if socket.bufLen == 0:
|
if socket.bufLen == 0:
|
||||||
retRead(0'i32, 0)
|
retRead(0'i32, 0)
|
||||||
|
|
||||||
var read = 0
|
var read = 0
|
||||||
while read < size:
|
while read < size:
|
||||||
if socket.currPos >= socket.bufLen:
|
if socket.currPos >= socket.bufLen:
|
||||||
retRead(0'i32, read)
|
retRead(0'i32, read)
|
||||||
|
|
||||||
let chunk = min(socket.bufLen-socket.currPos, size-read)
|
let chunk = min(socket.bufLen-socket.currPos, size-read)
|
||||||
var d = cast[cstring](data)
|
var d = cast[cstring](data)
|
||||||
assert size-read >= chunk
|
assert size-read >= chunk
|
||||||
|
|
@ -686,7 +713,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
|
||||||
else:
|
else:
|
||||||
if timeout - int(waited * 1000.0) < 1:
|
if timeout - int(waited * 1000.0) < 1:
|
||||||
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
|
raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
|
||||||
|
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
if socket.hasDataBuffered:
|
if socket.hasDataBuffered:
|
||||||
|
|
@ -695,7 +722,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
|
||||||
let sslPending = SSLPending(socket.sslHandle)
|
let sslPending = SSLPending(socket.sslHandle)
|
||||||
if sslPending != 0:
|
if sslPending != 0:
|
||||||
return sslPending
|
return sslPending
|
||||||
|
|
||||||
var startTime = epochTime()
|
var startTime = epochTime()
|
||||||
let selRet = select(socket, timeout - int(waited * 1000.0))
|
let selRet = select(socket, timeout - int(waited * 1000.0))
|
||||||
if selRet < 0: raiseOSError(osLastError())
|
if selRet < 0: raiseOSError(osLastError())
|
||||||
|
|
@ -706,8 +733,8 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
|
||||||
proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
|
proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
|
||||||
tags: [ReadIOEffect, TimeEffect].} =
|
tags: [ReadIOEffect, TimeEffect].} =
|
||||||
## overload with a ``timeout`` parameter in milliseconds.
|
## overload with a ``timeout`` parameter in milliseconds.
|
||||||
var waited = 0.0 # number of seconds already waited
|
var waited = 0.0 # number of seconds already waited
|
||||||
|
|
||||||
var read = 0
|
var read = 0
|
||||||
while read < size:
|
while read < size:
|
||||||
let avail = waitFor(socket, waited, timeout, size-read, "recv")
|
let avail = waitFor(socket, waited, timeout, size-read, "recv")
|
||||||
|
|
@ -718,7 +745,7 @@ proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
|
||||||
if result < 0:
|
if result < 0:
|
||||||
return result
|
return result
|
||||||
inc(read, result)
|
inc(read, result)
|
||||||
|
|
||||||
result = read
|
result = read
|
||||||
|
|
||||||
proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
|
proc recv*(socket: Socket, data: var string, size: int, timeout = -1,
|
||||||
|
|
@ -752,7 +779,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
|
||||||
var res = socket.readIntoBuf(0'i32)
|
var res = socket.readIntoBuf(0'i32)
|
||||||
if res <= 0:
|
if res <= 0:
|
||||||
result = res
|
result = res
|
||||||
|
|
||||||
c = socket.buffer[socket.currPos]
|
c = socket.buffer[socket.currPos]
|
||||||
else:
|
else:
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
|
|
@ -760,7 +787,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} =
|
||||||
if not socket.sslHasPeekChar:
|
if not socket.sslHasPeekChar:
|
||||||
result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
|
result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1)
|
||||||
socket.sslHasPeekChar = true
|
socket.sslHasPeekChar = true
|
||||||
|
|
||||||
c = socket.sslPeekChar
|
c = socket.sslPeekChar
|
||||||
return
|
return
|
||||||
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
|
result = recv(socket.fd, addr(c), 1, MSG_PEEK)
|
||||||
|
|
@ -773,7 +800,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
||||||
## If a full line is read ``\r\L`` is not
|
## If a full line is read ``\r\L`` is not
|
||||||
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||||
## will be set to it.
|
## will be set to it.
|
||||||
##
|
##
|
||||||
## If the socket is disconnected, ``line`` will be set to ``""``.
|
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||||
##
|
##
|
||||||
## An EOS exception will be raised in the case of a socket error.
|
## An EOS exception will be raised in the case of a socket error.
|
||||||
|
|
@ -782,7 +809,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
||||||
## the specified time an ETimeout exception will be raised.
|
## the specified time an ETimeout exception will be raised.
|
||||||
##
|
##
|
||||||
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
|
||||||
|
|
||||||
template addNLIfEmpty(): stmt =
|
template addNLIfEmpty(): stmt =
|
||||||
if line.len == 0:
|
if line.len == 0:
|
||||||
line.add("\c\L")
|
line.add("\c\L")
|
||||||
|
|
@ -809,7 +836,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
|
||||||
elif n <= 0: raiseSockError()
|
elif n <= 0: raiseSockError()
|
||||||
addNLIfEmpty()
|
addNLIfEmpty()
|
||||||
return
|
return
|
||||||
elif c == '\L':
|
elif c == '\L':
|
||||||
addNLIfEmpty()
|
addNLIfEmpty()
|
||||||
return
|
return
|
||||||
add(line.string, c)
|
add(line.string, c)
|
||||||
|
|
@ -827,7 +854,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int,
|
||||||
## so when ``socket`` is buffered the non-buffered implementation will be
|
## so when ``socket`` is buffered the non-buffered implementation will be
|
||||||
## used. Therefore if ``socket`` contains something in its buffer this
|
## used. Therefore if ``socket`` contains something in its buffer this
|
||||||
## function will make no effort to return it.
|
## function will make no effort to return it.
|
||||||
|
|
||||||
# TODO: Buffered sockets
|
# TODO: Buffered sockets
|
||||||
data.setLen(length)
|
data.setLen(length)
|
||||||
var sockAddress: Sockaddr_in
|
var sockAddress: Sockaddr_in
|
||||||
|
|
@ -861,16 +888,16 @@ proc send*(socket: Socket, data: pointer, size: int): int {.
|
||||||
tags: [WriteIOEffect].} =
|
tags: [WriteIOEffect].} =
|
||||||
## Sends data to a socket.
|
## Sends data to a socket.
|
||||||
##
|
##
|
||||||
## **Note**: This is a low-level version of ``send``. You likely should use
|
## **Note**: This is a low-level version of ``send``. You likely should use
|
||||||
## the version below.
|
## the version below.
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
return SSLWrite(socket.sslHandle, cast[cstring](data), size)
|
return SSLWrite(socket.sslHandle, cast[cstring](data), size)
|
||||||
|
|
||||||
when useWinVersion or defined(macosx):
|
when useWinVersion or defined(macosx):
|
||||||
result = send(socket.fd, data, size.cint, 0'i32)
|
result = send(socket.fd, data, size.cint, 0'i32)
|
||||||
else:
|
else:
|
||||||
when defined(solaris):
|
when defined(solaris):
|
||||||
const MSG_NOSIGNAL = 0
|
const MSG_NOSIGNAL = 0
|
||||||
result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
|
result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
|
||||||
|
|
||||||
|
|
@ -895,7 +922,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
|
||||||
size: int, af: Domain = AF_INET, flags = 0'i32): int {.
|
size: int, af: Domain = AF_INET, flags = 0'i32): int {.
|
||||||
tags: [WriteIOEffect].} =
|
tags: [WriteIOEffect].} =
|
||||||
## This proc sends ``data`` to the specified ``address``,
|
## This proc sends ``data`` to the specified ``address``,
|
||||||
## which may be an IP address or a hostname, if a hostname is specified
|
## which may be an IP address or a hostname, if a hostname is specified
|
||||||
## this function will try each IP of that hostname.
|
## this function will try each IP of that hostname.
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
|
|
@ -904,7 +931,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
|
||||||
##
|
##
|
||||||
## **Note:** This proc is not available for SSL sockets.
|
## **Note:** This proc is not available for SSL sockets.
|
||||||
var aiList = getAddrInfo(address, port, af)
|
var aiList = getAddrInfo(address, port, af)
|
||||||
|
|
||||||
# try all possibilities:
|
# try all possibilities:
|
||||||
var success = false
|
var success = false
|
||||||
var it = aiList
|
var it = aiList
|
||||||
|
|
@ -918,10 +945,10 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
|
||||||
|
|
||||||
dealloc(aiList)
|
dealloc(aiList)
|
||||||
|
|
||||||
proc sendTo*(socket: Socket, address: string, port: Port,
|
proc sendTo*(socket: Socket, address: string, port: Port,
|
||||||
data: string): int {.tags: [WriteIOEffect].} =
|
data: string): int {.tags: [WriteIOEffect].} =
|
||||||
## This proc sends ``data`` to the specified ``address``,
|
## This proc sends ``data`` to the specified ``address``,
|
||||||
## which may be an IP address or a hostname, if a hostname is specified
|
## which may be an IP address or a hostname, if a hostname is specified
|
||||||
## this function will try each IP of that hostname.
|
## this function will try each IP of that hostname.
|
||||||
##
|
##
|
||||||
## This is the high-level version of the above ``sendTo`` function.
|
## This is the high-level version of the above ``sendTo`` function.
|
||||||
|
|
@ -958,7 +985,7 @@ proc connectAsync(socket: Socket, name: string, port = Port(0),
|
||||||
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
||||||
success = true
|
success = true
|
||||||
break
|
break
|
||||||
|
|
||||||
it = it.ai_next
|
it = it.ai_next
|
||||||
|
|
||||||
dealloc(aiList)
|
dealloc(aiList)
|
||||||
|
|
@ -971,7 +998,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
|
||||||
## The ``timeout`` paremeter specifies the time in milliseconds to allow for
|
## The ``timeout`` paremeter specifies the time in milliseconds to allow for
|
||||||
## the connection to the server to be made.
|
## the connection to the server to be made.
|
||||||
socket.fd.setBlocking(false)
|
socket.fd.setBlocking(false)
|
||||||
|
|
||||||
socket.connectAsync(address, port, af)
|
socket.connectAsync(address, port, af)
|
||||||
var s = @[socket.fd]
|
var s = @[socket.fd]
|
||||||
if selectWrite(s, timeout) != 1:
|
if selectWrite(s, timeout) != 1:
|
||||||
|
|
@ -983,7 +1010,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), timeout: int,
|
||||||
doAssert socket.handshake()
|
doAssert socket.handshake()
|
||||||
socket.fd.setBlocking(true)
|
socket.fd.setBlocking(true)
|
||||||
|
|
||||||
proc isSsl*(socket: Socket): bool =
|
proc isSsl*(socket: Socket): bool =
|
||||||
## Determines whether ``socket`` is a SSL socket.
|
## Determines whether ``socket`` is a SSL socket.
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
result = socket.isSSL
|
result = socket.isSSL
|
||||||
|
|
@ -1014,7 +1041,7 @@ proc IPv4_broadcast*(): IpAddress =
|
||||||
|
|
||||||
proc IPv6_any*(): IpAddress =
|
proc IPv6_any*(): IpAddress =
|
||||||
## Returns the IPv6 any address (::0), which can be used
|
## Returns the IPv6 any address (::0), which can be used
|
||||||
## to listen on all available network adapters
|
## to listen on all available network adapters
|
||||||
result = IpAddress(
|
result = IpAddress(
|
||||||
family: IpAddressFamily.IPv6,
|
family: IpAddressFamily.IPv6,
|
||||||
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
|
|
@ -1152,7 +1179,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
|
||||||
if not seperatorValid:
|
if not seperatorValid:
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"Invalid IP Address. Address contains an invalid seperator")
|
"Invalid IP Address. Address contains an invalid seperator")
|
||||||
if lastWasColon:
|
if lastWasColon:
|
||||||
if dualColonGroup != -1:
|
if dualColonGroup != -1:
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"Invalid IP Address. Address contains more than one \"::\" seperator")
|
"Invalid IP Address. Address contains more than one \"::\" seperator")
|
||||||
|
|
@ -1165,14 +1192,14 @@ proc parseIPv6Address(address_str: string): IpAddress =
|
||||||
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
|
result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
|
||||||
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
|
result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
|
||||||
currentShort = 0
|
currentShort = 0
|
||||||
groupCount.inc()
|
groupCount.inc()
|
||||||
if dualColonGroup != -1: seperatorValid = false
|
if dualColonGroup != -1: seperatorValid = false
|
||||||
elif i == 0: # only valid if address starts with ::
|
elif i == 0: # only valid if address starts with ::
|
||||||
if address_str[1] != ':':
|
if address_str[1] != ':':
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"Invalid IP Address. Address may not start with \":\"")
|
"Invalid IP Address. Address may not start with \":\"")
|
||||||
else: # i == high(address_str) - only valid if address ends with ::
|
else: # i == high(address_str) - only valid if address ends with ::
|
||||||
if address_str[high(address_str)-1] != ':':
|
if address_str[high(address_str)-1] != ':':
|
||||||
raise newException(ValueError,
|
raise newException(ValueError,
|
||||||
"Invalid IP Address. Address may not end with \":\"")
|
"Invalid IP Address. Address may not end with \":\"")
|
||||||
lastWasColon = true
|
lastWasColon = true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue