Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion libp2p/autotls/service.nim
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ when defined(libp2p_autotls_support):
../crypto/rsa,
../utils/heartbeat,
../transports/transport,
../utils/ipaddr,
../transports/tcptransport,
../nameresolving/dnsresolver

Expand Down Expand Up @@ -150,7 +151,10 @@ when defined(libp2p_autotls_support):
if self.config.ipAddress.isNone():
try:
self.config.ipAddress = Opt.some(getPublicIPAddress())
except AutoTLSError as exc:
except ValueError as exc:
error "Failed to get public IP address", err = exc.msg
return false
except OSError as exc:
error "Failed to get public IP address", err = exc.msg
return false
self.managerFut = self.run(switch)
Expand Down
32 changes: 1 addition & 31 deletions libp2p/autotls/utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const
type AutoTLSError* = object of LPError

when defined(libp2p_autotls_support):
import net, strutils
import strutils
from times import DateTime, toTime, toUnix
import stew/base36
import
Expand All @@ -33,36 +33,6 @@ when defined(libp2p_autotls_support):
../nameresolving/nameresolver,
./acme/client

proc checkedGetPrimaryIPAddr*(): IpAddress {.raises: [AutoTLSError].} =
# This is so that we don't need to catch Exceptions directly
# since we support 1.6.16 and getPrimaryIPAddr before nim 2 didn't have explicit .raises. pragmas
try:
return getPrimaryIPAddr()
except Exception as exc:
raise newException(AutoTLSError, "Error while getting primary IP address", exc)

proc isIPv4*(ip: IpAddress): bool =
ip.family == IpAddressFamily.IPv4

proc isPublic*(ip: IpAddress): bool {.raises: [AutoTLSError].} =
let ip = $ip
try:
not (
ip.startsWith("10.") or
(ip.startsWith("172.") and parseInt(ip.split(".")[1]) in 16 .. 31) or
ip.startsWith("192.168.") or ip.startsWith("127.") or ip.startsWith("169.254.")
)
except ValueError as exc:
raise newException(AutoTLSError, "Failed to parse IP address", exc)

proc getPublicIPAddress*(): IpAddress {.raises: [AutoTLSError].} =
let ip = checkedGetPrimaryIPAddr()
if not ip.isIPv4():
raise newException(AutoTLSError, "Host does not have an IPv4 address")
if not ip.isPublic():
raise newException(AutoTLSError, "Host does not have a public IPv4 address")
return ip

proc asMoment*(dt: DateTime): Moment =
let unixTime: int64 = dt.toTime.toUnix
return Moment.init(unixTime, Second)
Expand Down
37 changes: 37 additions & 0 deletions libp2p/multiaddress.nim
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,14 @@ proc init*(
res.data.finish()
ok(res)

proc getPart*(ma: MultiAddress, codec: MultiCodec): MaResult[MultiAddress] =
## Returns the first multiaddress in ``value`` with codec ``codec``
for part in ma:
let part = ?part
if codec == ?part.protoCode:
return ok(part)
err("no such codec in multiaddress")

proc getProtocol(name: string): MAProtocol {.inline.} =
let mc = MultiCodec.codec(name)
if mc != InvalidMultiCodec:
Expand Down Expand Up @@ -1119,3 +1127,32 @@ proc getRepeatedField*(
err(ProtoError.IncorrectBlob)
else:
ok(true)

proc areAddrsConsistent*(a, b: MultiAddress): bool =
## Checks if two multiaddresses have the same protocol stack.
let protosA = a.protocols().get()
let protosB = b.protocols().get()
if protosA.len != protosB.len:
return false

for idx in 0 ..< protosA.len:
let protoA = protosA[idx]
let protoB = protosB[idx]

if protoA != protoB:
if idx == 0:
# allow DNS ↔ IP at the first component
if protoB == multiCodec("dns") or protoB == multiCodec("dnsaddr"):
if not (protoA == multiCodec("ip4") or protoA == multiCodec("ip6")):
return false
elif protoB == multiCodec("dns4"):
if protoA != multiCodec("ip4"):
return false
elif protoB == multiCodec("dns6"):
if protoA != multiCodec("ip6"):
return false
else:
return false
else:
return false
true
2 changes: 1 addition & 1 deletion libp2p/protocols/connectivity/autonat/client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import results
import chronos, chronicles
import ../../../switch, ../../../multiaddress, ../../../peerid
import core
import types

logScope:
topics = "libp2p autonat"
Expand Down
4 changes: 2 additions & 2 deletions libp2p/protocols/connectivity/autonat/server.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import
../../../peerid,
../../../utils/[semaphore, future],
../../../errors
import core
import types

export core
export types

logScope:
topics = "libp2p autonat"
Expand Down
4 changes: 2 additions & 2 deletions libp2p/protocols/connectivity/autonat/service.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import chronos, metrics
import ../../../switch
import ../../../wire
import client
from core import NetworkReachability, AutonatUnreachableError
from types import NetworkReachability, AutonatUnreachableError
import ../../../utils/heartbeat
import ../../../crypto/crypto

export core.NetworkReachability
export NetworkReachability

logScope:
topics = "libp2p autonatservice"
Expand Down
89 changes: 51 additions & 38 deletions libp2p/protocols/connectivity/autonatv2/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,35 @@
{.push raises: [].}

import results, chronos, chronicles
import ../../../multiaddress, ../../../peerid #, ../../../errors
import ../../../protobuf/minprotobuf
import
../../../multiaddress, ../../../peerid, ../../../protobuf/minprotobuf, ../../../switch
from ../autonat/types import NetworkReachability

logScope:
topics = "libp2p autonat v2"
type
AutonatV2Codec* {.pure.} = enum
DialRequest = "/libp2p/autonat/2/dial-request"
DialBack = "/libp2p/autonat/2/dial-back"

const
AutonatV2DialRequestCodec* = "/libp2p/autonat/2/dial-request"
AutonatV2DialBackCodec* = "/libp2p/autonat/2/dial-back"
AutonatV2Response* = object
reachability*: NetworkReachability
dialResp*: DialResponse
addrs*: Opt[MultiAddress]

AutonatV2Error* = object of LPError

Nonce* = uint64

AddrIdx* = uint32

NumBytes* = uint64

type
# DialBack and DialBackResponse are not defined as AutonatV2Msg as per the spec
# likely because they are expected in response to some other message
MsgType* {.pure.} = enum
Unused = 0 # nim requires the first variant to be zero
DialRequest = 1
DialResponse = 2
DialDataRequest = 3
DialDataResponse = 4
# DialBack and DialBackResponse are not defined as AutonatV2Msg as per the spec
# likely because they are expected in response to some other message
DialRequest
DialResponse
DialDataRequest
DialDataResponse

ResponseStatus* {.pure.} = enum
EInternalError = 0
Expand All @@ -47,30 +57,28 @@ type

DialRequest* = object
addrs*: seq[MultiAddress]
nonce*: uint64
nonce*: Nonce

DialResponse* = object
status*: ResponseStatus
addrIdx*: Opt[uint32]
addrIdx*: Opt[AddrIdx]
dialStatus*: Opt[DialStatus]

DialBack* = object
nonce*: uint64
nonce*: Nonce

DialBackResponse* = object
status*: DialBackStatus

DialDataRequest* = object
addrIdx*: uint32
numBytes*: uint64
addrIdx*: AddrIdx
numBytes*: NumBytes

DialDataResponse* = object
data*: seq[byte]

AutonatV2Msg* = object
case msgType*: MsgType
of MsgType.Unused:
discard
of MsgType.DialRequest:
dialReq*: DialRequest
of MsgType.DialResponse:
Expand All @@ -92,7 +100,7 @@ proc encode*(dialReq: DialRequest): ProtoBuffer =
proc decode*(T: typedesc[DialRequest], pb: ProtoBuffer): Opt[T] =
var
addrs: seq[MultiAddress]
nonce: uint64
nonce: Nonce
if not ?pb.getRepeatedField(1, addrs).toOpt():
return Opt.none(T)
if not ?pb.getField(2, nonce).toOpt():
Expand All @@ -114,13 +122,13 @@ proc encode*(dialResp: DialResponse): ProtoBuffer =
proc decode*(T: typedesc[DialResponse], pb: ProtoBuffer): Opt[T] =
var
status: uint
addrIdx: uint32
addrIdx: AddrIdx
dialStatus: uint

if not ?pb.getField(1, status).toOpt():
return Opt.none(T)

var optAddrIdx = Opt.none(uint32)
var optAddrIdx = Opt.none(AddrIdx)
if ?pb.getField(2, addrIdx).toOpt():
optAddrIdx = Opt.some(addrIdx)

Expand All @@ -144,7 +152,7 @@ proc encode*(dialBack: DialBack): ProtoBuffer =
encoded

proc decode*(T: typedesc[DialBack], pb: ProtoBuffer): Opt[T] =
var nonce: uint64
var nonce: Nonce
if not ?pb.getField(1, nonce).toOpt():
return Opt.none(T)
Opt.some(T(nonce: nonce))
Expand Down Expand Up @@ -172,8 +180,8 @@ proc encode*(dialDataReq: DialDataRequest): ProtoBuffer =

proc decode*(T: typedesc[DialDataRequest], pb: ProtoBuffer): Opt[T] =
var
addrIdx: uint32
numBytes: uint64
addrIdx: AddrIdx
numBytes: NumBytes
if not ?pb.getField(1, addrIdx).toOpt():
return Opt.none(T)
if not ?pb.getField(2, numBytes).toOpt():
Expand All @@ -193,20 +201,25 @@ proc decode*(T: typedesc[DialDataResponse], pb: ProtoBuffer): Opt[T] =
return Opt.none(T)
Opt.some(T(data: data))

proc protoField(msgType: MsgType): int =
case msgType
of MsgType.DialRequest: 1.int
of MsgType.DialResponse: 2.int
of MsgType.DialDataRequest: 3.int
of MsgType.DialDataResponse: 4.int

# AutonatV2Msg
proc encode*(msg: AutonatV2Msg): ProtoBuffer =
var encoded = initProtoBuffer()
case msg.msgType
of MsgType.Unused:
doAssert false, "invalid enum variant: Unused"
of MsgType.DialRequest:
encoded.write(MsgType.DialRequest.int, msg.dialReq.encode())
encoded.write(MsgType.DialRequest.protoField, msg.dialReq.encode())
of MsgType.DialResponse:
encoded.write(MsgType.DialResponse.int, msg.dialResp.encode())
encoded.write(MsgType.DialResponse.protoField, msg.dialResp.encode())
of MsgType.DialDataRequest:
encoded.write(MsgType.DialDataRequest.int, msg.dialDataReq.encode())
encoded.write(MsgType.DialDataRequest.protoField, msg.dialDataReq.encode())
of MsgType.DialDataResponse:
encoded.write(MsgType.DialDataResponse.int, msg.dialDataResp.encode())
encoded.write(MsgType.DialDataResponse.protoField, msg.dialDataResp.encode())
encoded.finish()
encoded

Expand All @@ -215,19 +228,19 @@ proc decode*(T: typedesc[AutonatV2Msg], pb: ProtoBuffer): Opt[T] =
msgTypeOrd: uint32
msg: ProtoBuffer

if ?pb.getField(MsgType.DialRequest.int, msg).toOpt():
if ?pb.getField(MsgType.DialRequest.protoField, msg).toOpt():
let dialReq = DialRequest.decode(msg).valueOr:
return Opt.none(AutonatV2Msg)
Opt.some(AutonatV2Msg(msgType: MsgType.DialRequest, dialReq: dialReq))
elif ?pb.getField(MsgType.DialResponse.int, msg).toOpt():
elif ?pb.getField(MsgType.DialResponse.protoField, msg).toOpt():
let dialResp = DialResponse.decode(msg).valueOr:
return Opt.none(AutonatV2Msg)
Opt.some(AutonatV2Msg(msgType: MsgType.DialResponse, dialResp: dialResp))
elif ?pb.getField(MsgType.DialDataRequest.int, msg).toOpt():
elif ?pb.getField(MsgType.DialDataRequest.protoField, msg).toOpt():
let dialDataReq = DialDataRequest.decode(msg).valueOr:
return Opt.none(AutonatV2Msg)
Opt.some(AutonatV2Msg(msgType: MsgType.DialDataRequest, dialDataReq: dialDataReq))
elif ?pb.getField(MsgType.DialDataResponse.int, msg).toOpt():
elif ?pb.getField(MsgType.DialDataResponse.protoField, msg).toOpt():
let dialDataResp = DialDataResponse.decode(msg).valueOr:
return Opt.none(AutonatV2Msg)
Opt.some(
Expand Down
47 changes: 47 additions & 0 deletions libp2p/protocols/connectivity/autonatv2/utils.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{.push raises: [].}

import results
import chronos
import
../../protocol,
../../../switch,
../../../multiaddress,
../../../multicodec,
../../../peerid,
../../../protobuf/minprotobuf,
../autonat/service,
./types

proc asNetworkReachability*(self: DialResponse): NetworkReachability =
if self.status == EInternalError:
return Unknown
if self.status == ERequestRejected:
return Unknown
if self.status == EDialRefused:
return Unknown

# if got here it means a dial was attempted
let dialStatus = self.dialStatus.valueOr:
return Unknown
if dialStatus == Unused:
return Unknown
if dialStatus == EDialError:
return NotReachable
if dialStatus == EDialBackError:
return NotReachable
return Reachable

proc asAutonatV2Response*(
self: DialResponse, testAddrs: seq[MultiAddress]
): AutonatV2Response =
let addrIdx = self.addrIdx.valueOr:
return AutonatV2Response(
reachability: self.asNetworkReachability(),
dialResp: self,
addrs: Opt.none(MultiAddress),
)
AutonatV2Response(
reachability: self.asNetworkReachability(),
dialResp: self,
addrs: Opt.some(testAddrs[addrIdx]),
)
4 changes: 2 additions & 2 deletions libp2p/transports/transport.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import
../multicodec,
../muxers/muxer,
../upgrademngrs/upgrade,
../protocols/connectivity/autonat/core
../protocols/connectivity/autonat/types

export core.NetworkReachability
export types.NetworkReachability

logScope:
topics = "libp2p transport"
Expand Down
Loading
Loading