mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-08-27 06:00:20 +00:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3d15da34ad | ||
![]() |
741f825b8e | ||
![]() |
676ae52503 | ||
![]() |
fef553ed18 | ||
![]() |
f6f669617f | ||
![]() |
39c4b24395 | ||
![]() |
0d676c6a3b | ||
![]() |
a0b3897278 | ||
![]() |
abec2256ae | ||
![]() |
7aca869170 | ||
![]() |
b759683b76 | ||
![]() |
6677d70648 | ||
![]() |
7ac38e3e58 | ||
![]() |
49c424ef21 | ||
![]() |
0346af46da | ||
![]() |
93a5adfd18 | ||
![]() |
ddb75700a0 | ||
![]() |
ae997a5acb | ||
![]() |
6a9c90d3eb | ||
![]() |
41e045fe5b | ||
![]() |
e5e8c84d7c | ||
![]() |
e41b838d8f | ||
![]() |
7f9d4f3f6d | ||
![]() |
a6b316ef08 |
42
CHANGELOG.md
42
CHANGELOG.md
@@ -26,6 +26,48 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||||||
- in case of vulnerabilities.
|
- in case of vulnerabilities.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## [0.5.4] - 2023-11-27
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed a crash that could happen when calculating the size of bloom filters during encoding
|
||||||
|
|
||||||
|
## [0.5.3] - 2023-11-26
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed a data race from buffered pathfinder traffic
|
||||||
|
* Fix a bug where the next-hop selection may not take shortcuts through treespace
|
||||||
|
* Backoffs are now reset correctly when a successful handshake is completed
|
||||||
|
* Backoffs will no longer exceed roughly 4.5 hours when peers are down for a long time
|
||||||
|
* The `-normaliseconf` option will now work correctly with `PrivateKeyPath`
|
||||||
|
* Improved the reliability of QUIC peering setup by disabling 0-RTT
|
||||||
|
|
||||||
|
## [0.5.2] - 2023-11-06
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* New `-publickey` command line option that prints the derived public key from a configuration file
|
||||||
|
* Support for connecting to TLS peers via SOCKS with the new `sockstls://` link schema
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Stabilise tree parent selection algorithm
|
||||||
|
* Improved logging when the TUN interface fails to set up
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed a panic that could occur when a connection reaches an inconsistent error state
|
||||||
|
* The admin socket will now report more peering handshake error conditions in `getPeers`
|
||||||
|
* Yggdrasil will no longer panic at startup when duplicate peers are configured
|
||||||
|
* The `build` script will no longer incorrectly import `LDFLAGS` from the environment
|
||||||
|
|
||||||
|
## [0.5.1] - 2023-10-28
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix the Debian package so that upgrades are handled more smoothly
|
||||||
|
|
||||||
## [0.5.0] - 2023-10-28
|
## [0.5.0] - 2023-10-28
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
2
build
2
build
@@ -6,7 +6,7 @@ PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version}
|
|||||||
PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
|
PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)}
|
||||||
PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
|
PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)}
|
||||||
|
|
||||||
LDFLAGS="${LDFLAGS} -X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
|
LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER"
|
||||||
ARGS="-v"
|
ARGS="-v"
|
||||||
|
|
||||||
while getopts "utc:l:dro:p" option
|
while getopts "utc:l:dro:p" option
|
||||||
|
@@ -48,8 +48,9 @@ func main() {
|
|||||||
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
||||||
ver := flag.Bool("version", false, "prints the version of this build")
|
ver := flag.Bool("version", false, "prints the version of this build")
|
||||||
logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"")
|
logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"")
|
||||||
getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration")
|
getaddr := flag.Bool("address", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 address")
|
||||||
getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration")
|
getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet")
|
||||||
|
getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key")
|
||||||
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@@ -155,7 +156,15 @@ func main() {
|
|||||||
fmt.Println(ipnet.String())
|
fmt.Println(ipnet.String())
|
||||||
return
|
return
|
||||||
|
|
||||||
|
case *getpkey:
|
||||||
|
fmt.Println(hex.EncodeToString(publicKey))
|
||||||
|
return
|
||||||
|
|
||||||
case *normaliseconf:
|
case *normaliseconf:
|
||||||
|
cfg.AdminListen = ""
|
||||||
|
if cfg.PrivateKeyPath != "" {
|
||||||
|
cfg.PrivateKey = nil
|
||||||
|
}
|
||||||
var bs []byte
|
var bs []byte
|
||||||
if *confjson {
|
if *confjson {
|
||||||
bs, err = json.MarshalIndent(cfg, "", " ")
|
bs, err = json.MarshalIndent(cfg, "", " ")
|
||||||
@@ -207,9 +216,9 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
address, subnet := n.core.Address(), n.core.Subnet()
|
address, subnet := n.core.Address(), n.core.Subnet()
|
||||||
logger.Infof("Your public key is %s", hex.EncodeToString(n.core.PublicKey()))
|
logger.Printf("Your public key is %s", hex.EncodeToString(n.core.PublicKey()))
|
||||||
logger.Infof("Your IPv6 address is %s", address.String())
|
logger.Printf("Your IPv6 address is %s", address.String())
|
||||||
logger.Infof("Your IPv6 subnet is %s", subnet.String())
|
logger.Printf("Your IPv6 subnet is %s", subnet.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the admin socket.
|
// Setup the admin socket.
|
||||||
|
@@ -21,16 +21,16 @@ if [ $PKGBRANCH = "master" ]; then
|
|||||||
PKGREPLACES=yggdrasil-develop
|
PKGREPLACES=yggdrasil-develop
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export LDFLAGS="-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultConfig=/etc/yggdrasil/yggdrasil.conf"
|
GOLDFLAGS="-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultConfig=/etc/yggdrasil/yggdrasil.conf"
|
||||||
export LDFLAGS="${LDFLAGS} -X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix://var/run/yggdrasil/yggdrasil.sock"
|
GOLDFLAGS="${GOLDFLAGS} -X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix:///var/run/yggdrasil/yggdrasil.sock"
|
||||||
|
|
||||||
if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build
|
if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build
|
elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
elif [ $PKGARCH = "mipsel" ]; then GOARCH=mipsle GOOS=linux ./build
|
elif [ $PKGARCH = "mipsel" ]; then GOARCH=mipsle GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
elif [ $PKGARCH = "mips" ]; then GOARCH=mips64 GOOS=linux ./build
|
elif [ $PKGARCH = "mips" ]; then GOARCH=mips64 GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=6 ./build
|
elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=6 ./build -l "${GOLDFLAGS}"
|
||||||
elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build
|
elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
elif [ $PKGARCH = "armel" ]; then GOARCH=arm GOOS=linux GOARM=5 ./build
|
elif [ $PKGARCH = "armel" ]; then GOARCH=arm GOOS=linux GOARM=5 ./build -l "${GOLDFLAGS}"
|
||||||
else
|
else
|
||||||
echo "Specify PKGARCH=amd64,i386,mips,mipsel,armhf,arm64,armel"
|
echo "Specify PKGARCH=amd64,i386,mips,mipsel,armhf,arm64,armel"
|
||||||
exit 1
|
exit 1
|
||||||
|
2
go.mod
2
go.mod
@@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go
|
|||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Arceliar/ironwood v0.0.0-20231028101932-ceac99571f43
|
github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd
|
||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
|
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
|
||||||
github.com/cheggaaa/pb/v3 v3.1.4
|
github.com/cheggaaa/pb/v3 v3.1.4
|
||||||
github.com/gologme/log v1.3.0
|
github.com/gologme/log v1.3.0
|
||||||
|
4
go.sum
4
go.sum
@@ -1,5 +1,5 @@
|
|||||||
github.com/Arceliar/ironwood v0.0.0-20231028101932-ceac99571f43 h1:M4NczBPk7Fy0Uy2YvNoXwSkk3dGoGTOYtUjyqpOC5ko=
|
github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd h1:458tnmZ4zM2gbLtefdYbaxyAJevDNEWu6tLKEqbK4wg=
|
||||||
github.com/Arceliar/ironwood v0.0.0-20231028101932-ceac99571f43/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
|
github.com/Arceliar/ironwood v0.0.0-20231127131626-465b82dfb5bd/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
|
||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
|
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
|
||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
|
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
|
||||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||||
|
@@ -40,8 +40,8 @@ import (
|
|||||||
// options that are necessary for an Yggdrasil node to run. You will need to
|
// options that are necessary for an Yggdrasil node to run. You will need to
|
||||||
// supply one of these structs to the Yggdrasil core when starting a node.
|
// supply one of these structs to the Yggdrasil core when starting a node.
|
||||||
type NodeConfig struct {
|
type NodeConfig struct {
|
||||||
PrivateKey KeyBytes `comment:"Your private key. DO NOT share this with anyone!"`
|
PrivateKey KeyBytes `json:",omitempty" comment:"Your private key. DO NOT share this with anyone!"`
|
||||||
PrivateKeyPath string `json:",omitempty"`
|
PrivateKeyPath string `json:",omitempty" comment:"The path to your private key file in PEM format."`
|
||||||
Certificate *tls.Certificate `json:"-"`
|
Certificate *tls.Certificate `json:"-"`
|
||||||
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
|
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
|
||||||
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
|
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
|
||||||
|
@@ -179,6 +179,22 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
|||||||
}
|
}
|
||||||
options.password = []byte(p)
|
options.password = []byte(p)
|
||||||
}
|
}
|
||||||
|
// SNI headers must contain hostnames and not IP addresses, so we must make sure
|
||||||
|
// that we do not populate the SNI with an IP literal. We do this by splitting
|
||||||
|
// the host-port combo from the query option and then seeing if it parses to an
|
||||||
|
// IP address successfully or not.
|
||||||
|
if sni := u.Query().Get("sni"); sni != "" {
|
||||||
|
if net.ParseIP(sni) == nil {
|
||||||
|
options.tlsSNI = sni
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the SNI is not configured still because the above failed then we'll try
|
||||||
|
// again but this time we'll use the host part of the peering URI instead.
|
||||||
|
if options.tlsSNI == "" {
|
||||||
|
if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil {
|
||||||
|
options.tlsSNI = host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we think we're already connected to this peer, load up
|
// If we think we're already connected to this peer, load up
|
||||||
// the existing peer state. Try to kick the peer if possible,
|
// the existing peer state. Try to kick the peer if possible,
|
||||||
@@ -219,7 +235,9 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
|||||||
// The caller should check the return value to decide whether
|
// The caller should check the return value to decide whether
|
||||||
// or not to give up trying.
|
// or not to give up trying.
|
||||||
backoffNow := func() bool {
|
backoffNow := func() bool {
|
||||||
backoff++
|
if backoff < 14 { // Cap at roughly 4.5 hours maximum.
|
||||||
|
backoff++
|
||||||
|
}
|
||||||
duration := time.Second * time.Duration(math.Exp2(float64(backoff)))
|
duration := time.Second * time.Duration(math.Exp2(float64(backoff)))
|
||||||
select {
|
select {
|
||||||
case <-state.kick:
|
case <-state.kick:
|
||||||
@@ -233,6 +251,12 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resetBackoff is called by the connection handler when the
|
||||||
|
// handshake has successfully completed.
|
||||||
|
resetBackoff := func() {
|
||||||
|
backoff = 0
|
||||||
|
}
|
||||||
|
|
||||||
// The goroutine is responsible for attempting the connection
|
// The goroutine is responsible for attempting the connection
|
||||||
// and then running the handler. If the connection is persistent
|
// and then running the handler. If the connection is persistent
|
||||||
// then the loop will run endlessly, using backoffs as needed.
|
// then the loop will run endlessly, using backoffs as needed.
|
||||||
@@ -257,7 +281,10 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn, err := l.connect(state.ctx, u, info, options)
|
conn, err := l.connect(state.ctx, u, info, options)
|
||||||
if err != nil {
|
if err != nil || conn == nil {
|
||||||
|
if err == nil && conn == nil {
|
||||||
|
l.core.log.Warnf("Link %q reached inconsistent error state", u.String())
|
||||||
|
}
|
||||||
if linkType == linkTypePersistent {
|
if linkType == linkTypePersistent {
|
||||||
// If the link is a persistent configured peering,
|
// If the link is a persistent configured peering,
|
||||||
// store information about the connection error so
|
// store information about the connection error so
|
||||||
@@ -306,10 +333,8 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
|||||||
|
|
||||||
// Give the connection to the handler. The handler will block
|
// Give the connection to the handler. The handler will block
|
||||||
// for the lifetime of the connection.
|
// for the lifetime of the connection.
|
||||||
if err = l.handler(linkType, options, lc); err != nil && err != io.EOF {
|
if err = l.handler(linkType, options, lc, resetBackoff); err != nil && err != io.EOF {
|
||||||
l.core.log.Debugf("Link %s error: %s\n", info.uri, err)
|
l.core.log.Debugf("Link %s error: %s\n", info.uri, err)
|
||||||
} else {
|
|
||||||
backoff = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The handler has stopped running so the connection is dead,
|
// The handler has stopped running so the connection is dead,
|
||||||
@@ -407,8 +432,8 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
l.core.log.Printf("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr())
|
l.core.log.Infof("%s listener started on %s", strings.ToUpper(u.Scheme), listener.Addr())
|
||||||
defer l.core.log.Printf("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr())
|
defer l.core.log.Infof("%s listener stopped on %s", strings.ToUpper(u.Scheme), listener.Addr())
|
||||||
for {
|
for {
|
||||||
conn, err := listener.Accept()
|
conn, err := listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -472,7 +497,7 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
|
|||||||
|
|
||||||
// Give the connection to the handler. The handler will block
|
// Give the connection to the handler. The handler will block
|
||||||
// for the lifetime of the connection.
|
// for the lifetime of the connection.
|
||||||
if err = l.handler(linkTypeIncoming, options, lc); err != nil && err != io.EOF {
|
if err = l.handler(linkTypeIncoming, options, lc, nil); err != nil && err != io.EOF {
|
||||||
l.core.log.Debugf("Link %s error: %s\n", u.Host, err)
|
l.core.log.Debugf("Link %s error: %s\n", u.Host, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,24 +522,8 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options
|
|||||||
case "tcp":
|
case "tcp":
|
||||||
dialer = l.tcp
|
dialer = l.tcp
|
||||||
case "tls":
|
case "tls":
|
||||||
// SNI headers must contain hostnames and not IP addresses, so we must make sure
|
|
||||||
// that we do not populate the SNI with an IP literal. We do this by splitting
|
|
||||||
// the host-port combo from the query option and then seeing if it parses to an
|
|
||||||
// IP address successfully or not.
|
|
||||||
if sni := u.Query().Get("sni"); sni != "" {
|
|
||||||
if net.ParseIP(sni) == nil {
|
|
||||||
options.tlsSNI = sni
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the SNI is not configured still because the above failed then we'll try
|
|
||||||
// again but this time we'll use the host part of the peering URI instead.
|
|
||||||
if options.tlsSNI == "" {
|
|
||||||
if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil {
|
|
||||||
options.tlsSNI = host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dialer = l.tls
|
dialer = l.tls
|
||||||
case "socks":
|
case "socks", "sockstls":
|
||||||
dialer = l.socks
|
dialer = l.socks
|
||||||
case "unix":
|
case "unix":
|
||||||
dialer = l.unix
|
dialer = l.unix
|
||||||
@@ -526,7 +535,7 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options
|
|||||||
return dialer.dial(ctx, u, info, options)
|
return dialer.dial(ctx, u, info, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) error {
|
func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, success func()) error {
|
||||||
meta := version_getBaseMetadata()
|
meta := version_getBaseMetadata()
|
||||||
meta.publicKey = l.core.public
|
meta.publicKey = l.core.public
|
||||||
meta.priority = options.priority
|
meta.priority = options.priority
|
||||||
@@ -546,8 +555,9 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e
|
|||||||
}
|
}
|
||||||
meta = version_metadata{}
|
meta = version_metadata{}
|
||||||
base := version_getBaseMetadata()
|
base := version_getBaseMetadata()
|
||||||
if !meta.decode(conn, options.password) {
|
if err := meta.decode(conn, options.password); err != nil {
|
||||||
return conn.Close()
|
_ = conn.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if !meta.check() {
|
if !meta.check() {
|
||||||
return fmt.Errorf("remote node incompatible version (local %s, remote %s)",
|
return fmt.Errorf("remote node incompatible version (local %s, remote %s)",
|
||||||
@@ -596,6 +606,9 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e
|
|||||||
}
|
}
|
||||||
l.core.log.Infof("Connected %s: %s, source %s",
|
l.core.log.Infof("Connected %s: %s, source %s",
|
||||||
dir, remoteStr, localStr)
|
dir, remoteStr, localStr)
|
||||||
|
if success != nil {
|
||||||
|
success()
|
||||||
|
}
|
||||||
|
|
||||||
err = l.core.HandleConn(meta.publicKey, conn, priority)
|
err = l.core.HandleConn(meta.publicKey, conn, priority)
|
||||||
switch err {
|
switch err {
|
||||||
|
@@ -24,7 +24,7 @@ type linkQUICStream struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type linkQUICListener struct {
|
type linkQUICListener struct {
|
||||||
*quic.EarlyListener
|
*quic.Listener
|
||||||
ch <-chan *linkQUICStream
|
ch <-chan *linkQUICStream
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,11 +50,11 @@ func (l *links) newLinkQUIC() *linkQUIC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
|
||||||
qc, err := quic.DialAddrEarly(ctx, url.Host, l.tlsconfig, l.quicconfig)
|
qc, err := quic.DialAddr(ctx, url.Host, l.tlsconfig, l.quicconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
qs, err := qc.OpenStream()
|
qs, err := qc.OpenStreamSync(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -65,14 +65,14 @@ func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, option
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||||
ql, err := quic.ListenAddrEarly(url.Host, l.tlsconfig, l.quicconfig)
|
ql, err := quic.ListenAddr(url.Host, l.tlsconfig, l.quicconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ch := make(chan *linkQUICStream)
|
ch := make(chan *linkQUICStream)
|
||||||
lql := &linkQUICListener{
|
lql := &linkQUICListener{
|
||||||
EarlyListener: ql,
|
Listener: ql,
|
||||||
ch: ch,
|
ch: ch,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
@@ -2,6 +2,7 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -34,7 +35,16 @@ func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options
|
|||||||
return nil, fmt.Errorf("failed to configure proxy")
|
return nil, fmt.Errorf("failed to configure proxy")
|
||||||
}
|
}
|
||||||
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
|
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
|
||||||
return dialer.Dial("tcp", pathtokens[0])
|
conn, err := dialer.Dial("tcp", pathtokens[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to dial: %w", err)
|
||||||
|
}
|
||||||
|
if url.Scheme == "sockstls" {
|
||||||
|
tlsconfig := l.tls.config.Clone()
|
||||||
|
tlsconfig.ServerName = options.tlsSNI
|
||||||
|
conn = tls.Client(conn, tlsconfig)
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||||
|
@@ -13,7 +13,15 @@ func (c *Core) _applyOption(opt SetupOption) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse peering URI: %w", err)
|
return fmt.Errorf("unable to parse peering URI: %w", err)
|
||||||
}
|
}
|
||||||
return c.links.add(u, v.SourceInterface, linkTypePersistent)
|
err = c.links.add(u, v.SourceInterface, linkTypePersistent)
|
||||||
|
switch err {
|
||||||
|
case ErrLinkAlreadyConfigured:
|
||||||
|
// Don't return this error, otherwise we'll panic at startup
|
||||||
|
// if there are multiple of the same peer configured
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
case ListenAddress:
|
case ListenAddress:
|
||||||
c.config._listeners[v] = struct{}{}
|
c.config._listeners[v] = struct{}{}
|
||||||
case NodeInfo:
|
case NodeInfo:
|
||||||
|
41
src/core/options_test.go
Normal file
41
src/core/options_test.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tests that duplicate peers in the configuration file
|
||||||
|
// won't cause an error when the node starts. Otherwise
|
||||||
|
// we can panic unnecessarily.
|
||||||
|
func TestDuplicatePeerAtStartup(t *testing.T) {
|
||||||
|
cfg := config.GenerateConfig()
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
cfg.Peers = append(cfg.Peers, "tcp://1.2.3.4:4321")
|
||||||
|
}
|
||||||
|
if _, err := New(cfg.Certificate, nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that duplicate peers given to us through the
|
||||||
|
// API will still error as expected, even if they didn't
|
||||||
|
// at startup. We expect to notify the user through the
|
||||||
|
// admin socket if they try to add a peer that is already
|
||||||
|
// configured.
|
||||||
|
func TestDuplicatePeerFromAPI(t *testing.T) {
|
||||||
|
cfg := config.GenerateConfig()
|
||||||
|
c, err := New(cfg.Certificate, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
u, _ := url.Parse("tcp://1.2.3.4:4321")
|
||||||
|
if err := c.AddPeer(u, ""); err != nil {
|
||||||
|
t.Fatalf("Adding peer failed on first attempt: %s", err)
|
||||||
|
}
|
||||||
|
if err := c.AddPeer(u, ""); err == nil {
|
||||||
|
t.Fatalf("Adding peer should have failed on second attempt")
|
||||||
|
}
|
||||||
|
}
|
@@ -87,22 +87,22 @@ func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decodes version metadata from its wire format into the struct.
|
// Decodes version metadata from its wire format into the struct.
|
||||||
func (m *version_metadata) decode(r io.Reader, password []byte) bool {
|
func (m *version_metadata) decode(r io.Reader, password []byte) error {
|
||||||
bh := [6]byte{}
|
bh := [6]byte{}
|
||||||
if _, err := io.ReadFull(r, bh[:]); err != nil {
|
if _, err := io.ReadFull(r, bh[:]); err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
meta := [4]byte{'m', 'e', 't', 'a'}
|
meta := [4]byte{'m', 'e', 't', 'a'}
|
||||||
if !bytes.Equal(bh[:4], meta[:]) {
|
if !bytes.Equal(bh[:4], meta[:]) {
|
||||||
return false
|
return fmt.Errorf("invalid handshake preamble")
|
||||||
}
|
}
|
||||||
bs := make([]byte, binary.BigEndian.Uint16(bh[4:6]))
|
hl := binary.BigEndian.Uint16(bh[4:6])
|
||||||
|
if hl < ed25519.SignatureSize {
|
||||||
|
return fmt.Errorf("invalid handshake length")
|
||||||
|
}
|
||||||
|
bs := make([]byte, hl)
|
||||||
if _, err := io.ReadFull(r, bs); err != nil {
|
if _, err := io.ReadFull(r, bs); err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
if len(bs) < ed25519.SignatureSize {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
sig := bs[len(bs)-ed25519.SignatureSize:]
|
sig := bs[len(bs)-ed25519.SignatureSize:]
|
||||||
bs = bs[:len(bs)-ed25519.SignatureSize]
|
bs = bs[:len(bs)-ed25519.SignatureSize]
|
||||||
@@ -132,14 +132,17 @@ func (m *version_metadata) decode(r io.Reader, password []byte) bool {
|
|||||||
|
|
||||||
hasher, err := blake2b.New512(password)
|
hasher, err := blake2b.New512(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return fmt.Errorf("invalid password supplied")
|
||||||
}
|
}
|
||||||
n, err := hasher.Write(m.publicKey)
|
n, err := hasher.Write(m.publicKey)
|
||||||
if err != nil || n != ed25519.PublicKeySize {
|
if err != nil || n != ed25519.PublicKeySize {
|
||||||
return false
|
return fmt.Errorf("failed to generate hash")
|
||||||
}
|
}
|
||||||
hash := hasher.Sum(nil)
|
hash := hasher.Sum(nil)
|
||||||
return ed25519.Verify(m.publicKey, hash, sig)
|
if !ed25519.Verify(m.publicKey, hash, sig) {
|
||||||
|
return fmt.Errorf("password is incorrect")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that the "meta" bytes and the version numbers are the expected values.
|
// Checks that the "meta" bytes and the version numbers are the expected values.
|
||||||
|
@@ -34,7 +34,7 @@ func TestVersionPasswordAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var decoded version_metadata
|
var decoded version_metadata
|
||||||
if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2); allowed != tt.allowed {
|
if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2) == nil; allowed != tt.allowed {
|
||||||
t.Fatalf("Permutation %q -> %q should have been %v but was %v", tt.password1, tt.password2, tt.allowed, allowed)
|
t.Fatalf("Permutation %q -> %q should have been %v but was %v", tt.password1, tt.password2, tt.allowed, allowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,8 +67,8 @@ func TestVersionRoundtrip(t *testing.T) {
|
|||||||
}
|
}
|
||||||
encoded := bytes.NewBuffer(meta)
|
encoded := bytes.NewBuffer(meta)
|
||||||
decoded := &version_metadata{}
|
decoded := &version_metadata{}
|
||||||
if !decoded.decode(encoded, password) {
|
if err := decoded.decode(encoded, password); err != nil {
|
||||||
t.Fatalf("failed to decode")
|
t.Fatalf("failed to decode: %s", err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(test, decoded) {
|
if !reflect.DeepEqual(test, decoded) {
|
||||||
t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded)
|
t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded)
|
||||||
|
@@ -29,7 +29,7 @@ func (tun *TunAdapter) write() {
|
|||||||
bs := buf[TUN_OFFSET_BYTES:]
|
bs := buf[TUN_OFFSET_BYTES:]
|
||||||
n, err := tun.rwc.Read(bs)
|
n, err := tun.rwc.Read(bs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.log.Errorln("Exiting tun writer due to core read error:", err)
|
tun.log.Errorln("Exiting TUN writer due to core read error:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !tun.isEnabled {
|
if !tun.isEnabled {
|
||||||
|
@@ -78,7 +78,7 @@ type in6_ifreq_lifetime struct {
|
|||||||
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||||
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("failed to create TUN: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if mtu, err := iface.MTU(); err == nil {
|
if mtu, err := iface.MTU(); err == nil {
|
||||||
|
@@ -7,6 +7,7 @@ package tun
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -24,7 +25,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
|||||||
}
|
}
|
||||||
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("failed to create TUN: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if m, err := iface.MTU(); err == nil {
|
if m, err := iface.MTU(); err == nil {
|
||||||
@@ -42,17 +43,17 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
|||||||
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
||||||
dfd, err := unix.Dup(int(fd))
|
dfd, err := unix.Dup(int(fd))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to duplicate FD: %w", err)
|
||||||
}
|
}
|
||||||
err = unix.SetNonblock(dfd, true)
|
err = unix.SetNonblock(dfd, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unix.Close(dfd)
|
unix.Close(dfd)
|
||||||
return err
|
return fmt.Errorf("failed to set FD as non-blocking: %w", err)
|
||||||
}
|
}
|
||||||
iface, err := wgtun.CreateTUNFromFile(os.NewFile(uintptr(dfd), "/dev/tun"), 0)
|
iface, err := wgtun.CreateTUNFromFile(os.NewFile(uintptr(dfd), "/dev/tun"), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unix.Close(dfd)
|
unix.Close(dfd)
|
||||||
return err
|
return fmt.Errorf("failed to create TUN from FD: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if m, err := iface.MTU(); err == nil {
|
if m, err := iface.MTU(); err == nil {
|
||||||
@@ -110,8 +111,8 @@ func (tun *TunAdapter) setupAddress(addr string) error {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
if fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil {
|
if fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil {
|
||||||
tun.log.Printf("Create AF_SYSTEM socket failed: %v.", err)
|
tun.log.Errorf("Create AF_SYSTEM socket failed: %v.", err)
|
||||||
return err
|
return fmt.Errorf("failed to open AF_SYSTEM: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ar in6_aliasreq
|
var ar in6_aliasreq
|
||||||
@@ -149,13 +150,13 @@ func (tun *TunAdapter) setupAddress(addr string) error {
|
|||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { // nolint:staticcheck
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { // nolint:staticcheck
|
||||||
err = errno
|
err = errno
|
||||||
tun.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
tun.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
||||||
return err
|
return fmt.Errorf("failed to call SIOCAIFADDR_IN6: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { // nolint:staticcheck
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { // nolint:staticcheck
|
||||||
err = errno
|
err = errno
|
||||||
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||||
return err
|
return fmt.Errorf("failed to call SIOCSIFMTU: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@@ -19,7 +19,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
|||||||
}
|
}
|
||||||
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("failed to create TUN: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if mtu, err := iface.MTU(); err == nil {
|
if mtu, err := iface.MTU(); err == nil {
|
||||||
@@ -45,20 +45,20 @@ func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
|||||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||||
nladdr, err := netlink.ParseAddr(addr)
|
nladdr, err := netlink.ParseAddr(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("couldn't parse address %q: %w", addr, err)
|
||||||
}
|
}
|
||||||
nlintf, err := netlink.LinkByName(tun.Name())
|
nlintf, err := netlink.LinkByName(tun.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to find link by name: %w", err)
|
||||||
}
|
}
|
||||||
if err := netlink.AddrAdd(nlintf, nladdr); err != nil {
|
if err := netlink.AddrAdd(nlintf, nladdr); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to add address to link: %w", err)
|
||||||
}
|
}
|
||||||
if err := netlink.LinkSetMTU(nlintf, int(tun.mtu)); err != nil {
|
if err := netlink.LinkSetMTU(nlintf, int(tun.mtu)); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to set link MTU: %w", err)
|
||||||
}
|
}
|
||||||
if err := netlink.LinkSetUp(nlintf); err != nil {
|
if err := netlink.LinkSetUp(nlintf); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to bring link up: %w", err)
|
||||||
}
|
}
|
||||||
// Friendly output
|
// Friendly output
|
||||||
tun.log.Infof("Interface name: %s", tun.Name())
|
tun.log.Infof("Interface name: %s", tun.Name())
|
||||||
|
@@ -16,7 +16,7 @@ import (
|
|||||||
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||||
iface, err := wgtun.CreateTUN(ifname, mtu)
|
iface, err := wgtun.CreateTUN(ifname, mtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("failed to create TUN: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if mtu, err := iface.MTU(); err == nil {
|
if mtu, err := iface.MTU(); err == nil {
|
||||||
|
Reference in New Issue
Block a user