diff --git a/libp2p/transports/tcptransport.nim b/libp2p/transports/tcptransport.nim index bcf398c10f..acdd70b4b2 100644 --- a/libp2p/transports/tcptransport.nim +++ b/libp2p/transports/tcptransport.nim @@ -335,4 +335,4 @@ method dial*( method handles*(t: TcpTransport, address: MultiAddress): bool = if procCall Transport(t).handles(address): if address.protocols.isOk: - return TCP.match(address) \ No newline at end of file + return TCP.match(address) diff --git a/libp2p/transports/tortransport.nim b/libp2p/transports/tortransport.nim index d70d1873a3..f98fa83840 100644 --- a/libp2p/transports/tortransport.nim +++ b/libp2p/transports/tortransport.nim @@ -15,13 +15,14 @@ import std/strformat import chronos, chronicles, strutils import stew/[byteutils, endians2, results, objects] import ../multicodec -import transport, - tcptransport, - ../switch, - ../builders, - ../stream/[lpstream, connection, chronosstream], - ../multiaddress, - ../upgrademngrs/upgrade +import + transport, + tcptransport, + ../switch, + ../builders, + ../stream/[lpstream, connection, chronosstream], + ../multiaddress, + ../upgrademngrs/upgrade const IPTcp = mapAnd(IP, mapEq("tcp")) @@ -44,16 +45,24 @@ type NoAcceptableMethod = 0xff Socks5RequestCommand* {.pure.} = enum - Connect = 1, Bind = 2, UdpAssoc = 3 + Connect = 1 + Bind = 2 + UdpAssoc = 3 Socks5AddressType* {.pure.} = enum - IPv4 = 1, FQDN = 3, IPv6 = 4 + IPv4 = 1 + FQDN = 3 + IPv6 = 4 Socks5ReplyType* {.pure.} = enum - Succeeded = (0, "Succeeded"), ServerFailure = (1, "Server Failure"), - ConnectionNotAllowed = (2, "Connection Not Allowed"), NetworkUnreachable = (3, "Network Unreachable"), - HostUnreachable = (4, "Host Unreachable"), ConnectionRefused = (5, "Connection Refused"), - TtlExpired = (6, "Ttl Expired"), CommandNotSupported = (7, "Command Not Supported"), + Succeeded = (0, "Succeeded") + ServerFailure = (1, "Server Failure") + ConnectionNotAllowed = (2, "Connection Not Allowed") + NetworkUnreachable = (3, "Network Unreachable") + HostUnreachable = (4, "Host Unreachable") + ConnectionRefused = (5, "Connection Refused") + TtlExpired = (6, "Ttl Expired") + CommandNotSupported = (7, "Command Not Supported") AddressTypeNotSupported = (8, "Address Type Not Supported") TransportStartError* = object of transport.TransportError @@ -64,16 +73,18 @@ type Socks5ServerReplyError* = object of Socks5Error proc new*( - T: typedesc[TorTransport], - transportAddress: TransportAddress, - flags: set[ServerFlags] = {}, - upgrade: Upgrade): T {.public.} = + T: typedesc[TorTransport], + transportAddress: TransportAddress, + flags: set[ServerFlags] = {}, + upgrade: Upgrade, +): T {.public.} = ## Creates a Tor transport T( transportAddress: transportAddress, upgrader: upgrade, - tcpTransport: TcpTransport.new(flags, upgrade)) + tcpTransport: TcpTransport.new(flags, upgrade), + ) proc handlesDial(address: MultiAddress): bool {.gcsafe.} = return Onion3.match(address) or TCP.match(address) or DNSANY.match(address) @@ -82,10 +93,13 @@ proc handlesStart(address: MultiAddress): bool {.gcsafe.} = return TcpOnion3.match(address) proc connectToTorServer( - transportAddress: TransportAddress): Future[StreamTransport] {.async.} = + transportAddress: TransportAddress +): Future[StreamTransport] {.async.} = let transp = await connect(transportAddress) try: - discard await transp.write(@[Socks5ProtocolVersion, NMethods, Socks5AuthMethod.NoAuth.byte]) + discard await transp.write( + @[Socks5ProtocolVersion, NMethods, Socks5AuthMethod.NoAuth.byte] + ) let serverReply = await transp.read(2) socks5ProtocolVersion = serverReply[0] @@ -115,35 +129,40 @@ proc readServerReply(transp: StreamTransport) {.async.} = if serverReply != Socks5ReplyType.Succeeded.byte: var socks5ReplyType: Socks5ReplyType if socks5ReplyType.checkedEnumAssign(serverReply): - raise newException(Socks5ServerReplyError, fmt"Server reply error: {socks5ReplyType}") + raise + newException(Socks5ServerReplyError, fmt"Server reply error: {socks5ReplyType}") else: raise newException(LPError, fmt"Unexpected server reply: {serverReply}") let atyp = firstFourOctets[3] - case atyp: - of Socks5AddressType.IPv4.byte: - discard await transp.read(ipV4NumOctets + portNumOctets) - of Socks5AddressType.FQDN.byte: - let fqdnNumOctets = await transp.read(1) - discard await transp.read(int(uint8.fromBytes(fqdnNumOctets)) + portNumOctets) - of Socks5AddressType.IPv6.byte: - discard await transp.read(ipV6NumOctets + portNumOctets) - else: - raise newException(LPError, "Address not supported") + case atyp + of Socks5AddressType.IPv4.byte: + discard await transp.read(ipV4NumOctets + portNumOctets) + of Socks5AddressType.FQDN.byte: + let fqdnNumOctets = await transp.read(1) + discard await transp.read(int(uint8.fromBytes(fqdnNumOctets)) + portNumOctets) + of Socks5AddressType.IPv6.byte: + discard await transp.read(ipV6NumOctets + portNumOctets) + else: + raise newException(LPError, "Address not supported") -proc parseOnion3(address: MultiAddress): (byte, seq[byte], seq[byte]) {.raises: [LPError, ValueError].} = +proc parseOnion3( + address: MultiAddress +): (byte, seq[byte], seq[byte]) {.raises: [LPError, ValueError].} = var addressArray = ($address).split('/') - if addressArray.len < 2: raise newException(LPError, fmt"Onion address not supported {address}") + if addressArray.len < 2: + raise newException(LPError, fmt"Onion address not supported {address}") addressArray = addressArray[2].split(':') - if addressArray.len == 0: raise newException(LPError, fmt"Onion address not supported {address}") + if addressArray.len == 0: + raise newException(LPError, fmt"Onion address not supported {address}") let addressStr = addressArray[0] & ".onion" dstAddr = @(uint8(addressStr.len).toBytes()) & addressStr.toBytes() - dstPort = address.data.buffer[37..38] + dstPort = address.data.buffer[37 .. 38] return (Socks5AddressType.FQDN.byte, dstAddr, dstPort) -proc parseIpTcp(address: MultiAddress): - (byte, seq[byte], seq[byte]) - {.raises: [LPError, ValueError].} = +proc parseIpTcp( + address: MultiAddress +): (byte, seq[byte], seq[byte]) {.raises: [LPError, ValueError].} = let (codec, atyp) = if IPv4Tcp.match(address): (multiCodec("ip4"), Socks5AddressType.IPv4.byte) @@ -156,17 +175,16 @@ proc parseIpTcp(address: MultiAddress): dstPort = address[multiCodec("tcp")].tryGet().protoArgument().tryGet() (atyp, dstAddr, dstPort) -proc parseDnsTcp(address: MultiAddress): - (byte, seq[byte], seq[byte]) - {.raises: [LPError, ValueError].} = +proc parseDnsTcp( + address: MultiAddress +): (byte, seq[byte], seq[byte]) {.raises: [LPError, ValueError].} = let dnsAddress = address[multiCodec("dns")].tryGet().protoArgument().tryGet() dstAddr = @(uint8(dnsAddress.len).toBytes()) & dnsAddress dstPort = address[multiCodec("tcp")].tryGet().protoArgument().tryGet() (Socks5AddressType.FQDN.byte, dstAddr, dstPort) -proc dialPeer( - transp: StreamTransport, address: MultiAddress) {.async.} = +proc dialPeer(transp: StreamTransport, address: MultiAddress) {.async.} = let (atyp, dstAddr, dstPort) = if Onion3.match(address): parseOnion3(address) @@ -178,19 +196,18 @@ proc dialPeer( raise newException(LPError, fmt"Address not supported: {address}") let reserved = byte(0) - let request = @[ - Socks5ProtocolVersion, - Socks5RequestCommand.Connect.byte, - reserved, - atyp] & dstAddr & dstPort + let request = + @[Socks5ProtocolVersion, Socks5RequestCommand.Connect.byte, reserved, atyp] & dstAddr & + dstPort discard await transp.write(request) await readServerReply(transp) method dial*( - self: TorTransport, - hostname: string, - address: MultiAddress, - peerId: Opt[PeerId] = Opt.none(PeerId)): Future[Connection] {.async.} = + self: TorTransport, + hostname: string, + address: MultiAddress, + peerId: Opt[PeerId] = Opt.none(PeerId), +): Future[Connection] {.async.} = ## dial a peer ## if not handlesDial(address): @@ -205,9 +222,7 @@ method dial*( await transp.closeWait() raise err -method start*( - self: TorTransport, - addrs: seq[MultiAddress]) {.async.} = +method start*(self: TorTransport, addrs: seq[MultiAddress]) {.async.} = ## listen on the transport ## @@ -215,10 +230,10 @@ method start*( var onion3Addrs: seq[MultiAddress] for i, ma in addrs: if not handlesStart(ma): - warn "Invalid address detected, skipping!", address = ma - continue + warn "Invalid address detected, skipping!", address = ma + continue - let listenAddress = ma[0..1].tryGet() + let listenAddress = ma[0 .. 1].tryGet() listenAddrs.add(listenAddress) let onion3 = ma[multiCodec("onion3")].tryGet() onion3Addrs.add(onion3) @@ -227,7 +242,10 @@ method start*( await procCall Transport(self).start(onion3Addrs) await self.tcpTransport.start(listenAddrs) else: - raise newException(TransportStartError, "Tor Transport couldn't start, no supported addr was provided.") + raise newException( + TransportStartError, + "Tor Transport couldn't start, no supported addr was provided.", + ) method accept*(self: TorTransport): Future[Connection] {.async.} = ## accept a new Tor connection @@ -246,35 +264,37 @@ method handles*(t: TorTransport, address: MultiAddress): bool {.gcsafe.} = if procCall Transport(t).handles(address): return handlesDial(address) or handlesStart(address) -type - TorSwitch* = ref object of Switch +type TorSwitch* = ref object of Switch proc new*( - T: typedesc[TorSwitch], - torServer: TransportAddress, - rng: ref HmacDrbgContext, - addresses: seq[MultiAddress] = @[], - flags: set[ServerFlags] = {}): TorSwitch - {.raises: [LPError], public.} = - var builder = SwitchBuilder.new() - .withRng(rng) - .withTransport(proc(upgr: Upgrade): Transport = TorTransport.new(torServer, flags, upgr)) - if addresses.len != 0: - builder = builder.withAddresses(addresses) - let switch = builder.withMplex() - .withNoise() - .build() - let torSwitch = T( - peerInfo: switch.peerInfo, - ms: switch.ms, - transports: switch.transports, - connManager: switch.connManager, - peerStore: switch.peerStore, - dialer: Dialer.new(switch.peerInfo.peerId, switch.connManager, switch.peerStore, switch.transports, nil), - nameResolver: nil) - - torSwitch.connManager.peerStore = switch.peerStore - return torSwitch + T: typedesc[TorSwitch], + torServer: TransportAddress, + rng: ref HmacDrbgContext, + addresses: seq[MultiAddress] = @[], + flags: set[ServerFlags] = {}, +): TorSwitch {.raises: [LPError], public.} = + var builder = SwitchBuilder.new().withRng(rng).withTransport( + proc(upgr: Upgrade): Transport = + TorTransport.new(torServer, flags, upgr) + ) + if addresses.len != 0: + builder = builder.withAddresses(addresses) + let switch = builder.withMplex().withNoise().build() + let torSwitch = T( + peerInfo: switch.peerInfo, + ms: switch.ms, + transports: switch.transports, + connManager: switch.connManager, + peerStore: switch.peerStore, + dialer: Dialer.new( + switch.peerInfo.peerId, switch.connManager, switch.peerStore, switch.transports, + nil, + ), + nameResolver: nil, + ) + + torSwitch.connManager.peerStore = switch.peerStore + return torSwitch method addTransport*(s: TorSwitch, t: Transport) = doAssert(false, "not implemented!") diff --git a/libp2p/transports/transport.nim b/libp2p/transports/transport.nim index 7eec34cd4b..94c605eb72 100644 --- a/libp2p/transports/transport.nim +++ b/libp2p/transports/transport.nim @@ -12,12 +12,13 @@ import sequtils import chronos, chronicles -import ../stream/connection, - ../multiaddress, - ../multicodec, - ../muxers/muxer, - ../upgrademngrs/upgrade, - ../protocols/connectivity/autonat/core +import + ../stream/connection, + ../multiaddress, + ../multicodec, + ../muxers/muxer, + ../upgrademngrs/upgrade, + ../protocols/connectivity/autonat/core export core.NetworkReachability @@ -36,12 +37,9 @@ type networkReachability*: NetworkReachability proc newTransportClosedError*(parent: ref Exception = nil): ref TransportError = - newException(TransportClosedError, - "Transport closed, no more connections!", parent) + newException(TransportClosedError, "Transport closed, no more connections!", parent) -method start*( - self: Transport, - addrs: seq[MultiAddress]) {.base, async.} = +method start*(self: Transport, addrs: seq[MultiAddress]) {.base, async.} = ## start the transport ## @@ -57,49 +55,42 @@ method stop*(self: Transport) {.base, async.} = trace "stopping transport", address = $self.addrs self.running = false -method accept*(self: Transport): Future[Connection] - {.base, gcsafe.} = +method accept*(self: Transport): Future[Connection] {.base, gcsafe.} = ## accept incoming connections ## doAssert(false, "Not implemented!") method dial*( - self: Transport, - hostname: string, - address: MultiAddress, - peerId: Opt[PeerId] = Opt.none(PeerId)): Future[Connection] {.base, gcsafe.} = + self: Transport, + hostname: string, + address: MultiAddress, + peerId: Opt[PeerId] = Opt.none(PeerId), +): Future[Connection] {.base, gcsafe.} = ## dial a peer ## doAssert(false, "Not implemented!") proc dial*( - self: Transport, - address: MultiAddress, - peerId: Opt[PeerId] = Opt.none(PeerId)): Future[Connection] {.gcsafe.} = + self: Transport, address: MultiAddress, peerId: Opt[PeerId] = Opt.none(PeerId) +): Future[Connection] {.gcsafe.} = self.dial("", address) method upgrade*( - self: Transport, - conn: Connection, - peerId: Opt[PeerId] -): Future[Muxer] {.base, async: (raises: [ - CancelledError, LPError], raw: true).} = + self: Transport, conn: Connection, peerId: Opt[PeerId] +): Future[Muxer] {.base, async: (raises: [CancelledError, LPError], raw: true).} = ## base upgrade method that the transport uses to perform ## transport specific upgrades ## self.upgrader.upgrade(conn, peerId) -method handles*( - self: Transport, - address: MultiAddress): bool {.base, gcsafe.} = +method handles*(self: Transport, address: MultiAddress): bool {.base, gcsafe.} = ## check if transport supports the multiaddress ## # by default we skip circuit addresses to avoid # having to repeat the check in every transport - let protocols = address.protocols.valueOr: return false - protocols - .filterIt( - it == multiCodec("p2p-circuit") - ).len == 0 + let protocols = address.protocols.valueOr: + return false + + protocols.filterIt(it == multiCodec("p2p-circuit")).len == 0 diff --git a/libp2p/transports/wstransport.nim b/libp2p/transports/wstransport.nim index 8760dca897..893b1e4cd8 100644 --- a/libp2p/transports/wstransport.nim +++ b/libp2p/transports/wstransport.nim @@ -14,29 +14,28 @@ import std/[sequtils] import stew/results import chronos, chronicles -import transport, - ../errors, - ../wire, - ../multicodec, - ../multistream, - ../connmanager, - ../multiaddress, - ../utility, - ../stream/connection, - ../upgrademngrs/upgrade, - websock/websock +import + transport, + ../errors, + ../wire, + ../multicodec, + ../multistream, + ../connmanager, + ../multiaddress, + ../utility, + ../stream/connection, + ../upgrademngrs/upgrade, + websock/websock logScope: topics = "libp2p wstransport" export transport, websock, results -const - DefaultHeadersTimeout = 3.seconds +const DefaultHeadersTimeout = 3.seconds -type - WsStream = ref object of Connection - session: WSSession +type WsStream = ref object of Connection + session: WSSession method initStream*(s: WsStream) = if s.objName.len == 0: @@ -49,13 +48,10 @@ proc new*( session: WSSession, dir: Direction, observedAddr: Opt[MultiAddress], - timeout = 10.minutes): T = - - let stream = T( - session: session, - timeout: timeout, - dir: dir, - observedAddr: observedAddr) + timeout = 10.minutes, +): T = + let stream = + T(session: session, timeout: timeout, dir: dir, observedAddr: observedAddr) stream.initStream() return stream @@ -77,9 +73,7 @@ template mapExceptions(body: untyped) = raise newLPStreamEOFError() method readOnce*( - s: WsStream, - pbytes: pointer, - nbytes: int + s: WsStream, pbytes: pointer, nbytes: int ): Future[int] {.async: (raises: [CancelledError, LPStreamError]).} = let res = mapExceptions(await s.session.recv(pbytes, nbytes)) @@ -89,8 +83,7 @@ method readOnce*( return res method write*( - s: WsStream, - msg: seq[byte] + s: WsStream, msg: seq[byte] ): Future[void] {.async: (raises: [CancelledError, LPStreamError]).} = mapExceptions(await s.session.send(msg, Opcode.Binary)) s.activity = true # reset activity flag @@ -102,30 +95,28 @@ method closeImpl*(s: WsStream): Future[void] {.async: (raises: []).} = discard await procCall Connection(s).closeImpl() -method getWrapped*(s: WsStream): Connection = nil +method getWrapped*(s: WsStream): Connection = + nil -type - WsTransport* = ref object of Transport - httpservers: seq[HttpServer] - wsserver: WSServer - connections: array[Direction, seq[WsStream]] +type WsTransport* = ref object of Transport + httpservers: seq[HttpServer] + wsserver: WSServer + connections: array[Direction, seq[WsStream]] - acceptFuts: seq[Future[HttpRequest]] + acceptFuts: seq[Future[HttpRequest]] - tlsPrivateKey: TLSPrivateKey - tlsCertificate: TLSCertificate - tlsFlags: set[TLSFlags] - flags: set[ServerFlags] - handshakeTimeout: Duration - factories: seq[ExtFactory] - rng: ref HmacDrbgContext + tlsPrivateKey: TLSPrivateKey + tlsCertificate: TLSCertificate + tlsFlags: set[TLSFlags] + flags: set[ServerFlags] + handshakeTimeout: Duration + factories: seq[ExtFactory] + rng: ref HmacDrbgContext proc secure*(self: WsTransport): bool = not (isNil(self.tlsPrivateKey) or isNil(self.tlsCertificate)) -method start*( - self: WsTransport, - addrs: seq[MultiAddress]) {.async.} = +method start*(self: WsTransport, addrs: seq[MultiAddress]) {.async.} = ## listen on the transport ## @@ -136,19 +127,18 @@ method start*( await procCall Transport(self).start(addrs) trace "Starting WS transport" - self.wsserver = WSServer.new( - factories = self.factories, - rng = self.rng) - + self.wsserver = WSServer.new(factories = self.factories, rng = self.rng) for i, ma in addrs: let isWss = if WSS.match(ma): - if self.secure: true + if self.secure: + true else: warn "Trying to listen on a WSS address without setting certificate!" false - else: false + else: + false let httpserver = if isWss: @@ -157,11 +147,11 @@ method start*( tlsPrivateKey = self.tlsPrivateKey, tlsCertificate = self.tlsCertificate, flags = self.flags, - handshakeTimeout = self.handshakeTimeout) + handshakeTimeout = self.handshakeTimeout, + ) else: HttpServer.create( - ma.initTAddress().tryGet(), - handshakeTimeout = self.handshakeTimeout + ma.initTAddress().tryGet(), handshakeTimeout = self.handshakeTimeout ) self.httpservers &= httpserver @@ -176,8 +166,8 @@ method start*( MultiAddress.init("/ws") # always get the resolved address in case we're bound to 0.0.0.0:0 - self.addrs[i] = MultiAddress.init( - httpserver.localAddress()).tryGet() & codec.tryGet() + self.addrs[i] = + MultiAddress.init(httpserver.localAddress()).tryGet() & codec.tryGet() trace "Listening on", addresses = self.addrs @@ -196,7 +186,9 @@ method stop*(self: WsTransport) {.async.} = checkFutures( await allFinished( self.connections[Direction.In].mapIt(it.close()) & - self.connections[Direction.Out].mapIt(it.close()))) + self.connections[Direction.Out].mapIt(it.close()) + ) + ) var toWait: seq[Future[void]] for fut in self.acceptFuts: @@ -216,10 +208,9 @@ method stop*(self: WsTransport) {.async.} = except CatchableError as exc: trace "Error shutting down ws transport", exc = exc.msg -proc connHandler(self: WsTransport, - stream: WSSession, - secure: bool, - dir: Direction): Future[Connection] {.async.} = +proc connHandler( + self: WsTransport, stream: WSSession, secure: bool, dir: Direction +): Future[Connection] {.async.} = let observedAddr = try: let @@ -233,7 +224,7 @@ proc connHandler(self: WsTransport, MultiAddress.init(remoteAddr).tryGet() & codec.tryGet() except CatchableError as exc: trace "Failed to create observedAddr", exc = exc.msg - if not(isNil(stream) and stream.stream.reader.closed): + if not (isNil(stream) and stream.stream.reader.closed): await stream.close() raise exc @@ -244,6 +235,7 @@ proc connHandler(self: WsTransport, await conn.session.stream.reader.join() self.connections[dir].keepItIf(it != conn) trace "Cleaned up client" + asyncSpawn onClose() return conn @@ -302,10 +294,11 @@ method accept*(self: WsTransport): Future[Connection] {.async.} = raise exc method dial*( - self: WsTransport, - hostname: string, - address: MultiAddress, - peerId: Opt[PeerId] = Opt.none(PeerId)): Future[Connection] {.async.} = + self: WsTransport, + hostname: string, + address: MultiAddress, + peerId: Opt[PeerId] = Opt.none(PeerId), +): Future[Connection] {.async.} = ## dial a peer ## @@ -318,7 +311,8 @@ method dial*( "", secure = secure, hostName = hostname, - flags = self.tlsFlags) + flags = self.tlsFlags, + ) try: return await self.connHandler(transp, secure, Direction.Out) @@ -332,15 +326,16 @@ method handles*(t: WsTransport, address: MultiAddress): bool {.gcsafe.} = return WebSockets.match(address) proc new*( - T: typedesc[WsTransport], - upgrade: Upgrade, - tlsPrivateKey: TLSPrivateKey, - tlsCertificate: TLSCertificate, - tlsFlags: set[TLSFlags] = {}, - flags: set[ServerFlags] = {}, - factories: openArray[ExtFactory] = [], - rng: ref HmacDrbgContext = nil, - handshakeTimeout = DefaultHeadersTimeout): T {.public.} = + T: typedesc[WsTransport], + upgrade: Upgrade, + tlsPrivateKey: TLSPrivateKey, + tlsCertificate: TLSCertificate, + tlsFlags: set[TLSFlags] = {}, + flags: set[ServerFlags] = {}, + factories: openArray[ExtFactory] = [], + rng: ref HmacDrbgContext = nil, + handshakeTimeout = DefaultHeadersTimeout, +): T {.public.} = ## Creates a secure WebSocket transport T( @@ -351,15 +346,17 @@ proc new*( flags: flags, factories: @factories, rng: rng, - handshakeTimeout: handshakeTimeout) + handshakeTimeout: handshakeTimeout, + ) proc new*( - T: typedesc[WsTransport], - upgrade: Upgrade, - flags: set[ServerFlags] = {}, - factories: openArray[ExtFactory] = [], - rng: ref HmacDrbgContext = nil, - handshakeTimeout = DefaultHeadersTimeout): T {.public.} = + T: typedesc[WsTransport], + upgrade: Upgrade, + flags: set[ServerFlags] = {}, + factories: openArray[ExtFactory] = [], + rng: ref HmacDrbgContext = nil, + handshakeTimeout = DefaultHeadersTimeout, +): T {.public.} = ## Creates a clear-text WebSocket transport T.new( @@ -369,4 +366,5 @@ proc new*( flags = flags, factories = @factories, rng = rng, - handshakeTimeout = handshakeTimeout) + handshakeTimeout = handshakeTimeout, + )