splitting the modules
This commit is contained in:
parent
a53a500fd7
commit
8705f87504
12 changed files with 402 additions and 396 deletions
176
yggdrasil/autopeering.go
Normal file
176
yggdrasil/autopeering.go
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
package yggdrasil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
git "github.com/go-git/go-git/v5"
|
||||
)
|
||||
|
||||
type Peer struct {
|
||||
URL url.URL
|
||||
Online bool
|
||||
Latency time.Duration
|
||||
}
|
||||
|
||||
var PublicPeers string
|
||||
|
||||
const repoURL = "https://github.com/yggdrasil-network/public-peers"
|
||||
|
||||
func readPeersFile(path string) []url.URL {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var peers []url.URL
|
||||
for line := range strings.SplitSeq(string(data), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if line != "" {
|
||||
url, err := url.Parse(line)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
peers = append(peers, *url)
|
||||
}
|
||||
}
|
||||
return peers
|
||||
}
|
||||
|
||||
// GetPublicPeers fetches the public-peers repository and returns a list of peer
|
||||
// connection strings. If fetching fails, it falls back to reading peers from
|
||||
// peers.txt in the current working directory. It returns an empty slice if no
|
||||
// peers can be retrieved.
|
||||
func getPublicPeers() []url.URL {
|
||||
tempDir, err := os.MkdirTemp("", "public-peers-*")
|
||||
if err != nil {
|
||||
return readPeersFile("peers.txt")
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
_, err = git.PlainCloneContext(context.Background(), tempDir, false, &git.CloneOptions{URL: repoURL, Depth: 1})
|
||||
if err != nil {
|
||||
return readPeersFile("peers.txt")
|
||||
}
|
||||
|
||||
var peers []url.URL
|
||||
re := regexp.MustCompile(`(?m)^\s*(tcp|tls)://[^\s]+`)
|
||||
filepath.WalkDir(tempDir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if d.IsDir() || !strings.HasSuffix(d.Name(), ".md") || d.Name() == "README.md" {
|
||||
return nil
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, m := range re.FindAllStringSubmatch(string(data), -1) {
|
||||
url, err := url.Parse(strings.TrimSpace(m[1]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
peers = append(peers, *url)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if len(peers) == 0 {
|
||||
return readPeersFile("peers.txt")
|
||||
}
|
||||
return peers
|
||||
}
|
||||
|
||||
// Get n online peers with best latency from a peer list
|
||||
func GetClosestPeers(peerList []url.URL, n int) []url.URL {
|
||||
var result []url.URL
|
||||
onlinePeers := testPeers(peerList)
|
||||
|
||||
// Filter online peers
|
||||
x := 0
|
||||
for _, p := range onlinePeers {
|
||||
if p.Online {
|
||||
onlinePeers[x] = p
|
||||
x++
|
||||
}
|
||||
}
|
||||
onlinePeers = onlinePeers[:x]
|
||||
|
||||
sort.Slice(onlinePeers, func(i, j int) bool {
|
||||
return onlinePeers[i].Latency < onlinePeers[j].Latency
|
||||
})
|
||||
|
||||
for i := 0; i < len(onlinePeers); i++ {
|
||||
if len(result) == n {
|
||||
break
|
||||
}
|
||||
result = append(result, onlinePeers[i].URL)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Pick n random peers from a list
|
||||
func RandomPick(peerList []url.URL, n int) []url.URL {
|
||||
if len(peerList) <= n {
|
||||
return peerList
|
||||
}
|
||||
|
||||
var res []url.URL
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for _, i := range r.Perm(n) {
|
||||
res = append(res, peerList[i])
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
const defaultTimeout time.Duration = time.Duration(3) * time.Second
|
||||
|
||||
func testPeers(peers []url.URL) []Peer {
|
||||
var res []Peer
|
||||
results := make(chan Peer)
|
||||
|
||||
for _, p := range peers {
|
||||
go testPeer(p, results)
|
||||
}
|
||||
|
||||
for range peers {
|
||||
res = append(res, <-results)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func testPeer(peer url.URL, results chan Peer) {
|
||||
p := Peer{peer, false, 0.0}
|
||||
p.Online = false
|
||||
t0 := time.Now()
|
||||
|
||||
var network string
|
||||
if peer.Scheme == "tcp" || peer.Scheme == "tls" {
|
||||
network = "tcp"
|
||||
} else { // skip, not supported yet
|
||||
results <- p
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := net.DialTimeout(network, peer.Host, defaultTimeout)
|
||||
if err == nil {
|
||||
t1 := time.Now()
|
||||
conn.Close()
|
||||
p.Online = true
|
||||
p.Latency = t1.Sub(t0)
|
||||
}
|
||||
results <- p
|
||||
}
|
||||
97
yggdrasil/keys.go
Normal file
97
yggdrasil/keys.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package yggdrasil
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gologme/log"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
yggConfig "github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/core"
|
||||
)
|
||||
|
||||
func GeneratePrivateKey() yggConfig.KeyBytes {
|
||||
return yggConfig.GenerateConfig().PrivateKey
|
||||
}
|
||||
|
||||
func GetPublicKey(keyPath string) (ed25519.PublicKey, error) {
|
||||
data, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
return ed25519.PublicKey{}, err
|
||||
}
|
||||
|
||||
decoded, err := hex.DecodeString(strings.TrimSpace(string(data)))
|
||||
if err != nil {
|
||||
return ed25519.PublicKey{}, err
|
||||
}
|
||||
|
||||
if len(decoded) != ed25519.PrivateKeySize {
|
||||
return ed25519.PublicKey{}, fmt.Errorf("invalid private key size: %d", len(decoded))
|
||||
}
|
||||
|
||||
privateKey := ed25519.PrivateKey(decoded)
|
||||
return privateKey.Public().(ed25519.PublicKey), nil
|
||||
}
|
||||
|
||||
func GetYggdrasilAddress(config *viper.Viper) string {
|
||||
//var remoteTcp types.TCPRemoteMappings
|
||||
ygg := config.Sub("yggdrasil")
|
||||
if ygg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
//laddr := config.Sub("p2p").GetString("laddr")
|
||||
//remoteTcp.Set(laddr)
|
||||
|
||||
cfg := yggConfig.GenerateConfig()
|
||||
|
||||
cfg.AdminListen = ygg.GetString("admin_listen")
|
||||
cfg.Listen = ygg.GetStringSlice("listen")
|
||||
cfg.Peers = ygg.GetStringSlice("peers")
|
||||
cfg.AllowedPublicKeys = ygg.GetStringSlice("allowed-public-keys")
|
||||
cfg.PrivateKeyPath = ygg.GetString("private-key-file")
|
||||
|
||||
logger := log.Default()
|
||||
|
||||
n := &node{}
|
||||
|
||||
// Setup the Yggdrasil node itself.
|
||||
{
|
||||
options := []core.SetupOption{
|
||||
core.NodeInfo(cfg.NodeInfo),
|
||||
core.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
|
||||
}
|
||||
for _, addr := range cfg.Listen {
|
||||
options = append(options, core.ListenAddress(addr))
|
||||
}
|
||||
for _, peer := range cfg.Peers {
|
||||
options = append(options, core.Peer{URI: peer})
|
||||
}
|
||||
for intf, peers := range cfg.InterfacePeers {
|
||||
for _, peer := range peers {
|
||||
options = append(options, core.Peer{URI: peer, SourceInterface: intf})
|
||||
}
|
||||
}
|
||||
for _, allowed := range cfg.AllowedPublicKeys {
|
||||
k, err := hex.DecodeString(allowed)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
options = append(options, core.AllowedPublicKey(k[:]))
|
||||
}
|
||||
|
||||
var err error
|
||||
if n.core, err = core.New(cfg.Certificate, logger, options...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
address := n.core.Address()
|
||||
n.core.Stop()
|
||||
return address.String()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,17 +2,12 @@ package yggdrasil
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"lbc/autopeering"
|
||||
ppp "lbc/persistentpeersparser"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
|
|
@ -45,88 +40,6 @@ type TCPRemoteListenerMapping struct {
|
|||
Mapped *net.TCPListener
|
||||
}
|
||||
|
||||
func GeneratePrivateKey() yggConfig.KeyBytes {
|
||||
return yggConfig.GenerateConfig().PrivateKey
|
||||
}
|
||||
|
||||
func GetPublicKey(keyPath string) (ed25519.PublicKey, error) {
|
||||
data, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
return ed25519.PublicKey{}, err
|
||||
}
|
||||
|
||||
decoded, err := hex.DecodeString(strings.TrimSpace(string(data)))
|
||||
if err != nil {
|
||||
return ed25519.PublicKey{}, err
|
||||
}
|
||||
|
||||
if len(decoded) != ed25519.PrivateKeySize {
|
||||
return ed25519.PublicKey{}, fmt.Errorf("invalid private key size: %d", len(decoded))
|
||||
}
|
||||
|
||||
privateKey := ed25519.PrivateKey(decoded)
|
||||
return privateKey.Public().(ed25519.PublicKey), nil
|
||||
}
|
||||
|
||||
func GetYggdrasilAddress(config *viper.Viper) string {
|
||||
//var remoteTcp types.TCPRemoteMappings
|
||||
ygg := config.Sub("yggdrasil")
|
||||
if ygg == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
//laddr := config.Sub("p2p").GetString("laddr")
|
||||
//remoteTcp.Set(laddr)
|
||||
|
||||
cfg := yggConfig.GenerateConfig()
|
||||
|
||||
cfg.AdminListen = ygg.GetString("admin_listen")
|
||||
cfg.Listen = ygg.GetStringSlice("listen")
|
||||
cfg.Peers = ygg.GetStringSlice("peers")
|
||||
cfg.AllowedPublicKeys = ygg.GetStringSlice("allowed-public-keys")
|
||||
cfg.PrivateKeyPath = ygg.GetString("private-key-file")
|
||||
|
||||
logger := log.Default()
|
||||
|
||||
n := &node{}
|
||||
|
||||
// Setup the Yggdrasil node itself.
|
||||
{
|
||||
options := []core.SetupOption{
|
||||
core.NodeInfo(cfg.NodeInfo),
|
||||
core.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
|
||||
}
|
||||
for _, addr := range cfg.Listen {
|
||||
options = append(options, core.ListenAddress(addr))
|
||||
}
|
||||
for _, peer := range cfg.Peers {
|
||||
options = append(options, core.Peer{URI: peer})
|
||||
}
|
||||
for intf, peers := range cfg.InterfacePeers {
|
||||
for _, peer := range peers {
|
||||
options = append(options, core.Peer{URI: peer, SourceInterface: intf})
|
||||
}
|
||||
}
|
||||
for _, allowed := range cfg.AllowedPublicKeys {
|
||||
k, err := hex.DecodeString(allowed)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
options = append(options, core.AllowedPublicKey(k[:]))
|
||||
}
|
||||
|
||||
var err error
|
||||
if n.core, err = core.New(cfg.Certificate, logger, options...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
address := n.core.Address()
|
||||
n.core.Stop()
|
||||
return address.String()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The main function is responsible for configuring and starting Yggdrasil.
|
||||
func Yggdrasil(config *viper.Viper, ch chan string) {
|
||||
var remoteTcp types.TCPRemoteMappings
|
||||
|
|
@ -157,9 +70,9 @@ func Yggdrasil(config *viper.Viper, ch chan string) {
|
|||
ch <- remoteTcp[0].Mapped.String()
|
||||
|
||||
peers := p2p.GetString("persistent_peers")
|
||||
parsed, err := ppp.ParseEntries(peers)
|
||||
parsed, err := ParseEntries(peers)
|
||||
if err != nil {
|
||||
parsed = []ppp.ParsedEntry{}
|
||||
parsed = []ParsedEntry{}
|
||||
ch <- ""
|
||||
log.Warnln("Warning: persistent peers has an error")
|
||||
}
|
||||
|
|
@ -169,7 +82,7 @@ func Yggdrasil(config *viper.Viper, ch chan string) {
|
|||
cfg.AdminListen = ygg.GetString("admin_listen")
|
||||
cfg.Listen = ygg.GetStringSlice("listen")
|
||||
if ygg.GetString("peers") == "auto" {
|
||||
publicPeers := autopeering.GetPublicPeers()
|
||||
publicPeers := getPublicPeers()
|
||||
var urlsAsStrings []string
|
||||
for _, u := range publicPeers {
|
||||
urlsAsStrings = append(urlsAsStrings, u.String())
|
||||
|
|
@ -372,23 +285,4 @@ func Yggdrasil(config *viper.Viper, ch chan string) {
|
|||
n.core.Stop()
|
||||
}
|
||||
|
||||
// Helper to detect if socket address is in use
|
||||
// https://stackoverflow.com/a/52152912
|
||||
func isErrorAddressAlreadyInUse(err error) bool {
|
||||
var eOsSyscall *os.SyscallError
|
||||
if !errors.As(err, &eOsSyscall) {
|
||||
return false
|
||||
}
|
||||
var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr)
|
||||
if !errors.As(eOsSyscall, &errErrno) {
|
||||
return false
|
||||
}
|
||||
if errors.Is(errErrno, syscall.EADDRINUSE) {
|
||||
return true
|
||||
}
|
||||
const WSAEADDRINUSE = 10048
|
||||
if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
119
yggdrasil/peers.txt
Normal file
119
yggdrasil/peers.txt
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
tcp://ipv6.campina-grande.paraiba.brazil.yggdrasil.iasylum.net:41000
|
||||
tcp://ipv4.campina-grande.paraiba.brazil.yggdrasil.iasylum.net:40000
|
||||
tls://ipv6.campina-grande.paraiba.brazil.yggdrasil.iasylum.net:51000
|
||||
tls://ipv4.campina-grande.paraiba.brazil.yggdrasil.iasylum.net:50000
|
||||
tls://192.99.145.61:58226
|
||||
tls://[2607:5300:201:3100::50a1]:58226
|
||||
tcp://kusoneko.moe:9002
|
||||
tls://ca1.servers.devices.cwinfo.net:58226
|
||||
tcp://[2a05:9403::8b]:7743
|
||||
tcp://195.123.245.146:7743
|
||||
tls://95.216.5.243:18836
|
||||
tls://[2a01:4f9:2a:60c::2]:18836
|
||||
tls://[2a01:4f9:c010:664d::1]:61995
|
||||
tls://aurora.devices.waren.io:18836
|
||||
tls://fi1.servers.devices.cwinfo.net:61995
|
||||
tls://65.21.57.122:61995
|
||||
tls://[2001:41d0:2:c44a:51:255:223:60]:54232
|
||||
tcp://62.210.85.80:39565
|
||||
tls://51.255.223.60:54232
|
||||
tcp://51.15.204.214:12345
|
||||
tls://51.15.204.214:54321
|
||||
tcp://[2001:470:1f13:e56::64]:39565
|
||||
tcp://s2.i2pd.xyz:39565
|
||||
tls://[2001:41d0:304:200::ace3]:23108
|
||||
tls://62.210.85.80:39575
|
||||
tls://[2001:470:1f13:e56::64]:39575
|
||||
tls://152.228.216.112:23108
|
||||
tls://s2.i2pd.xyz:39575
|
||||
tls://cloudberry.fr1.servers.devices.cwinfo.net:54232
|
||||
tls://fr2.servers.devices.cwinfo.net:23108
|
||||
tls://163.172.31.60:12221?key=060f2d49c6a1a2066357ea06e58f5cff8c76a5c0cc513ceb2dab75c900fe183b&sni=jorropo.net
|
||||
tls://jorropo.net:12221?key=060f2d49c6a1a2066357ea06e58f5cff8c76a5c0cc513ceb2dab75c900fe183b&sni=jorropo.net
|
||||
tcp://94.130.203.208:5999
|
||||
tls://yggdrasil.su:62586
|
||||
tcp://ygg.mkg20001.io:80
|
||||
tls://vpn.ltha.de:443?key=0000006149970f245e6cec43664bce203f2514b60a153e194f31e2b229a1339d
|
||||
tls://de-fsn-1.peer.v4.yggdrasil.chaz6.com:4444
|
||||
tcp://p2p-node.de:1337?key=000000d80a2d7b3126ea65c8c08fc751088c491a5cdd47eff11c86fa1e4644ae
|
||||
tcp://phrl42.ydns.eu:8842
|
||||
tcp://yggdrasil.su:62486
|
||||
tls://ygg.mkg20001.io:443
|
||||
tls://p2p-node.de:1338?key=000000d80a2d7b3126ea65c8c08fc751088c491a5cdd47eff11c86fa1e4644ae
|
||||
tcp://ygg1.mk16.de:1337?key=0000000087ee9949eeab56bd430ee8f324cad55abf3993ed9b9be63ce693e18a
|
||||
tls://ygg1.mk16.de:1338?key=0000000087ee9949eeab56bd430ee8f324cad55abf3993ed9b9be63ce693e18a
|
||||
tls://minecast.xyz:3785
|
||||
tls://45.147.198.155:6010
|
||||
tcp://ygg-nl.incognet.io:8883
|
||||
tcp://vpn.itrus.su:7991
|
||||
tls://ygg-nl.incognet.io:8884
|
||||
tls://109.107.173.235:9111
|
||||
tls://94.103.82.150:8080
|
||||
tls://aaoth.xyz:25565
|
||||
tcp://aaoth.xyz:7777
|
||||
tls://[2001:41d0:601:1100::cf2]:11129
|
||||
tls://54.37.137.221:11129
|
||||
tls://pl1.servers.devices.cwinfo.net:11129
|
||||
tcp://185.165.169.234:8880
|
||||
tls://185.165.169.234:8443
|
||||
tcp://188.225.9.167:18226
|
||||
tcp://92.124.136.131:30111
|
||||
tls://188.225.9.167:18227
|
||||
tcp://ygg.tomasgl.ru:61933?key=c5e0c28a600c2118e986196a0bbcbda4934d8e9278ceabea48838dc5d8fae576
|
||||
tls://[2a01:d0:ffff:4353::2]:6010
|
||||
tls://avevad.com:1337
|
||||
tcp://itcom.multed.com:7991
|
||||
tls://ygg.tomasgl.ru:61944?key=c5e0c28a600c2118e986196a0bbcbda4934d8e9278ceabea48838dc5d8fae576
|
||||
tcp://srv.itrus.su:7991
|
||||
tcp://box.paulll.cc:13337
|
||||
tcp://yggno.de:18226
|
||||
tls://yggno.de:18227
|
||||
tcp://ekb.itrus.su:7991
|
||||
tls://box.paulll.cc:13338
|
||||
tls://yggpvs.duckdns.org:8443
|
||||
tcp://158.101.229.219:17002
|
||||
tcp://[2603:c023:8001:1600:35e0:acde:2c6e:b27f]:17002
|
||||
tls://[2603:c023:8001:1600:35e0:acde:2c6e:b27f]:17001
|
||||
tls://158.101.229.219:17001
|
||||
tcp://sin.yuetau.net:6642
|
||||
tls://sin.yuetau.net:6643
|
||||
tcp://y.zbin.eu:7743
|
||||
tcp://[2a04:5b81:2010:5000:27d3:6343:a821:eb1c]:2000
|
||||
tls://[2a04:5b81:2010:5000:27d3:6343:a821:eb1c]:2001
|
||||
tls://[2a07:e01:105:444:c634:6bff:feb5:6e28]:7040
|
||||
tls://185.130.44.194:7040
|
||||
tls://ygg.ace.ctrl-c.liu.se:9999?key=5636b3af4738c3998284c4805d91209cab38921159c66a6f359f3f692af1c908
|
||||
tcp://ygg.ace.ctrl-c.liu.se:9998?key=5636b3af4738c3998284c4805d91209cab38921159c66a6f359f3f692af1c908
|
||||
tcp://212.154.86.134:8800
|
||||
tls://212.154.86.134:4433
|
||||
tcp://ip6-antalya.ddns.net:8800
|
||||
tls://ip6-antalya.ddns.net:4433
|
||||
tcp://193.111.114.28:8080
|
||||
tls://193.111.114.28:1443
|
||||
tls://91.224.254.114:18001
|
||||
tcp://78.27.153.163:33165
|
||||
tls://78.27.153.163:3784
|
||||
tls://78.27.153.163:179
|
||||
tls://78.27.153.163:3785
|
||||
tls://78.27.153.163:33166
|
||||
tls://51.38.64.12:28395
|
||||
tls://185.175.90.87:43006
|
||||
tls://[2a10:4740:40:0:2222:3f9c:b7cf:1]:43006
|
||||
tls://uk1.servers.devices.cwinfo.net:28395
|
||||
tcp://curiosity.tdjs.tech:30003
|
||||
tcp://50.236.201.218:56088
|
||||
tcp://longseason.1200bps.xyz:13121
|
||||
tcp://149.28.123.138:8008
|
||||
tcp://lancis.iscute.moe:49273
|
||||
tcp://zabugor.itrus.su:7991
|
||||
tls://108.175.10.127:61216
|
||||
tls://longseason.1200bps.xyz:13122
|
||||
tls://167.160.89.98:7040
|
||||
tcp://tasty.chowder.land:9002
|
||||
tls://44.234.134.124:443
|
||||
tls://[2605:9f80:2000:64::2]:7040
|
||||
tls://bazari.sh:3725
|
||||
tls://tasty.chowder.land:9001
|
||||
tls://5.161.114.182:443
|
||||
tls://5.161.139.99:443
|
||||
tls://lancis.iscute.moe:49274
|
||||
64
yggdrasil/persistentpeersparser.go
Normal file
64
yggdrasil/persistentpeersparser.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package yggdrasil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ParsedEntry struct {
|
||||
ID string
|
||||
Proto string
|
||||
Address string
|
||||
Port *int // nil если не указан
|
||||
}
|
||||
|
||||
var entryPattern = regexp.MustCompile(`^([a-fA-F0-9]+)@((?:[a-zA-Z]+://)?(?:\[[^\]]+\]|[^:]+))(?:[:](\d+))?$`)
|
||||
|
||||
func ParseEntries(input string) ([]ParsedEntry, error) {
|
||||
entries := strings.Split(input, ",")
|
||||
var result []ParsedEntry
|
||||
|
||||
for _, entry := range entries {
|
||||
entry = strings.TrimSpace(entry)
|
||||
if entry == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
matches := entryPattern.FindStringSubmatch(entry)
|
||||
if matches == nil {
|
||||
return nil, fmt.Errorf("invalid entry: %s", entry)
|
||||
}
|
||||
|
||||
id := matches[1]
|
||||
rawAddr := matches[2]
|
||||
portStr := matches[3]
|
||||
|
||||
proto := ""
|
||||
address := rawAddr
|
||||
|
||||
if strings.Contains(rawAddr, "://") {
|
||||
split := strings.SplitN(rawAddr, "://", 2)
|
||||
proto = split[0]
|
||||
address = split[1]
|
||||
}
|
||||
|
||||
var port *int
|
||||
if portStr != "" {
|
||||
p, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid port in entry: %s", entry)
|
||||
}
|
||||
port = &p
|
||||
}
|
||||
|
||||
result = append(result, ParsedEntry{
|
||||
ID: id,
|
||||
Proto: proto,
|
||||
Address: address,
|
||||
Port: port,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue