Compare commits
	
		
			1 Commits
		
	
	
		
			client-int
			...
			v0.8.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 3d93c0206c | 
| @@ -65,9 +65,10 @@ AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN | |||||||
| Type=simple | Type=simple | ||||||
| User=user | User=user | ||||||
| WorkingDirectory=/home/user/ | WorkingDirectory=/home/user/ | ||||||
| ExecStart=/home/user/vppn -name mynetwork -hub-address https://my.hub -api-key 1234567890 | ExecStart=/home/user/vppn run my_net_name https://my.hub my_api_key | ||||||
| Restart=always | Restart=always | ||||||
| RestartSec=8 | RestartSec=8 | ||||||
|  | TimeoutStopSec=24 | ||||||
|  |  | ||||||
| [Install] | [Install] | ||||||
| WantedBy=multi-user.target | WantedBy=multi-user.target | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								cmd/hub/hub
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								cmd/hub/hub
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -7,5 +7,5 @@ import ( | |||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| 	log.SetFlags(0) | 	log.SetFlags(0) | ||||||
| 	peer.Main() | 	peer.Main2() | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,15 +1,15 @@ | |||||||
| module vppn | module vppn | ||||||
|  |  | ||||||
| go 1.24.1 | go 1.25.1 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	git.crumpington.com/lib/go v0.9.0 | 	git.crumpington.com/lib/go v0.9.1 | ||||||
| 	golang.org/x/crypto v0.36.0 | 	golang.org/x/crypto v0.42.0 | ||||||
| 	golang.org/x/sys v0.31.0 | 	golang.org/x/sys v0.36.0 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/mattn/go-sqlite3 v1.14.24 // indirect | 	github.com/mattn/go-sqlite3 v1.14.32 // indirect | ||||||
| 	golang.org/x/net v0.37.0 // indirect | 	golang.org/x/net v0.44.0 // indirect | ||||||
| 	golang.org/x/text v0.23.0 // indirect | 	golang.org/x/text v0.29.0 // indirect | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,12 +1,12 @@ | |||||||
| git.crumpington.com/lib/go v0.9.0 h1:QXoMhsycSgEUWNiiPZWl0jgBls+NI9TNR5Z6nNXslCM= | git.crumpington.com/lib/go v0.9.1 h1:xLBzcgiZRB6Ky3Ce9hKE+Ko0YbkA4USF4eJk5i5RJF4= | ||||||
| git.crumpington.com/lib/go v0.9.0/go.mod h1:i3DXiPDo/pgPMHAxUTpyo1Xj2spcvXwXcBef3aSYlnQ= | git.crumpington.com/lib/go v0.9.1/go.mod h1:5nnfjdnUnj/FHhakaliKQKsKeSkUb0GEUKF3PqRgUXg= | ||||||
| github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= | github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= | ||||||
| github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | ||||||
| golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= | golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= | ||||||
| golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= | golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= | ||||||
| golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= | golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= | ||||||
| golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= | golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= | ||||||
| golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= | golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= | ||||||
| golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= | ||||||
| golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= | golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= | ||||||
| golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= | golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| package peer | package peer | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"bytes" |  | ||||||
| 	"crypto/aes" | 	"crypto/aes" | ||||||
| 	"crypto/cipher" | 	"crypto/cipher" | ||||||
| 	"crypto/rand" | 	"crypto/rand" | ||||||
| @@ -39,10 +38,6 @@ func (sc *dataCipher) Key() [32]byte { | |||||||
| 	return sc.key | 	return sc.key | ||||||
| } | } | ||||||
|  |  | ||||||
| func (sc *dataCipher) HasKey(k [32]byte) bool { |  | ||||||
| 	return bytes.Equal(k[:], sc.key[:]) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (sc *dataCipher) Encrypt(h Header, data, out []byte) []byte { | func (sc *dataCipher) Encrypt(h Header, data, out []byte) []byte { | ||||||
| 	const s = dataHeaderSize | 	const s = dataHeaderSize | ||||||
| 	out = out[:s+dataCipherOverhead+len(data)] | 	out = out[:s+dataCipherOverhead+len(data)] | ||||||
|   | |||||||
| @@ -29,6 +29,10 @@ func configDir(netName string) string { | |||||||
| 	return filepath.Join(d, ".vppn", netName) | 	return filepath.Join(d, ".vppn", netName) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func lockFilePath(netName string) string { | ||||||
|  | 	return filepath.Join(configDir(netName), "__lock__") | ||||||
|  | } | ||||||
|  |  | ||||||
| func peerConfigPath(netName string) string { | func peerConfigPath(netName string) string { | ||||||
| 	return filepath.Join(configDir(netName), "config.json") | 	return filepath.Join(configDir(netName), "config.json") | ||||||
| } | } | ||||||
| @@ -41,6 +45,10 @@ func startupCountPath(netName string) string { | |||||||
| 	return filepath.Join(configDir(netName), "startup_count.json") | 	return filepath.Join(configDir(netName), "startup_count.json") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func statusSocketPath(netName string) string { | ||||||
|  | 	return filepath.Join(configDir(netName), "status.sock") | ||||||
|  | } | ||||||
|  |  | ||||||
| func storeJson(x any, outPath string) error { | func storeJson(x any, outPath string) error { | ||||||
| 	outDir := filepath.Dir(outPath) | 	outDir := filepath.Dir(outPath) | ||||||
| 	_ = os.MkdirAll(outDir, 0700) | 	_ = os.MkdirAll(outDir, 0700) | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ const ( | |||||||
|  |  | ||||||
| 	controlCipherOverhead = 16 | 	controlCipherOverhead = 16 | ||||||
| 	dataCipherOverhead    = 16 | 	dataCipherOverhead    = 16 | ||||||
| 	signOverhead          = 64 | 	signingOverhead       = 64 | ||||||
|  |  | ||||||
| 	pingInterval                  = 8 * time.Second | 	pingInterval                  = 8 * time.Second | ||||||
| 	timeoutInterval               = 30 * time.Second | 	timeoutInterval               = 30 * time.Second | ||||||
|   | |||||||
							
								
								
									
										208
									
								
								peer/main.go
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								peer/main.go
									
									
									
									
									
								
							| @@ -1,23 +1,207 @@ | |||||||
| package peer | package peer | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"flag" | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 	"net" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/netip" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Main() { | // Usage: | ||||||
| 	args := mainArgs{} | // | ||||||
|  | // vppn netName run | ||||||
|  | // vppn netName status | ||||||
|  | func Main2() { | ||||||
|  | 	printUsage := func() { | ||||||
|  | 		fmt.Fprintf(os.Stderr, `%s COMMAND [ARGUMENTS...] | ||||||
|  |  | ||||||
| 	flag.StringVar(&args.NetName, "name", "", "[REQUIRED] The network name.") | Available commands: | ||||||
| 	flag.StringVar(&args.HubAddress, "hub-address", "", "[REQUIRED] The hub address.") |     run | ||||||
| 	flag.StringVar(&args.APIKey, "api-key", "", "[REQUIRED] The node's API key.") |     status | ||||||
| 	flag.Parse() |     hosts | ||||||
|  | `, os.Args[0]) | ||||||
| 	if args.NetName == "" || args.HubAddress == "" || args.APIKey == "" { |  | ||||||
| 		flag.Usage() |  | ||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	peer := newPeerMain(args) | 	if len(os.Args) < 2 { | ||||||
| 	peer.Run() | 		printUsage() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	command := os.Args[1] | ||||||
|  |  | ||||||
|  | 	switch command { | ||||||
|  | 	case "run": | ||||||
|  | 		main_run() | ||||||
|  | 	case "status": | ||||||
|  | 		main_status() | ||||||
|  | 	case "hosts": | ||||||
|  | 		main_hosts() | ||||||
|  | 	default: | ||||||
|  | 		printUsage() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | type mainArgs struct { | ||||||
|  | 	NetName    string | ||||||
|  | 	HubAddress string | ||||||
|  | 	APIKey     string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func main_run() { | ||||||
|  | 	printUsage := func() { | ||||||
|  | 		fmt.Fprintf(os.Stderr, `Usage: %s run NETWORK_NAME HUB_ADDRESS API_KEY | ||||||
|  |  | ||||||
|  |     NETWORK_NAME | ||||||
|  |         Unique name of the network interface created. The network name | ||||||
|  |         shouldn't change between invocations of the application. | ||||||
|  |  | ||||||
|  |     HUB_ADDRESS | ||||||
|  |         The address of the hub server. This should also contain the scheme, for | ||||||
|  |         example https://hub.domain.com/. | ||||||
|  |  | ||||||
|  |     API_KEY | ||||||
|  |         The API key assigned to this peer by the hub. | ||||||
|  |  | ||||||
|  | `, os.Args[0]) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(os.Args) != 5 { | ||||||
|  | 		printUsage() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	args := mainArgs{ | ||||||
|  | 		NetName:    os.Args[2], | ||||||
|  | 		HubAddress: os.Args[3], | ||||||
|  | 		APIKey:     os.Args[4], | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	newPeerMain(args).Run() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | func main_status() { | ||||||
|  | 	printUsage := func() { | ||||||
|  | 		fmt.Fprintf(os.Stderr, `Usage: %s status NETWORK_NAME | ||||||
|  |  | ||||||
|  |     NETWORK_NAME | ||||||
|  |         Unique name of the network interface created. | ||||||
|  |  | ||||||
|  | `, os.Args[0]) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(os.Args) != 3 { | ||||||
|  | 		printUsage() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	netName := os.Args[2] | ||||||
|  | 	report := fetchStatusReport(netName) | ||||||
|  |  | ||||||
|  | 	fmt.Printf("\n%s Status\n\n", netName) | ||||||
|  |  | ||||||
|  | 	if len(report.Network) != 4 { | ||||||
|  | 		fmt.Printf("Network: %v\n\n", report.Network) | ||||||
|  | 	} else { | ||||||
|  | 		nw := report.Network | ||||||
|  | 		fmt.Printf("%-8s %d.%d.%d.%d/24\n", "Network", nw[0], nw[1], nw[2], nw[3]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if report.RelayPeerIP != 0 { | ||||||
|  | 		fmt.Printf("%-8s %d\n\n", "Relay", report.RelayPeerIP) | ||||||
|  | 	} else { | ||||||
|  | 		fmt.Printf("%-8s -\n\n", "Relay") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, status := range report.Remotes { | ||||||
|  | 		fmt.Printf("%3d %s\n", status.PeerIP, status.Name) | ||||||
|  | 		fmt.Printf("    %-11s %v\n", "Up", status.Up) | ||||||
|  |  | ||||||
|  | 		pubIP, ok := netip.AddrFromSlice(status.PublicIP) | ||||||
|  | 		if ok { | ||||||
|  | 			fmt.Printf("    %-11s %v\n", "Public IP", pubIP) | ||||||
|  | 		} else { | ||||||
|  | 			fmt.Printf("    %-11s\n", "Public IP") | ||||||
|  | 		} | ||||||
|  | 		fmt.Printf("    %-11s %d\n", "Port", status.Port) | ||||||
|  | 		fmt.Printf("    %-11s %v\n", "Relay", status.Relay) | ||||||
|  | 		fmt.Printf("    %-11s %v\n", "Server", status.Server) | ||||||
|  | 		fmt.Printf("    %-11s %v\n", "Direct", status.Direct) | ||||||
|  | 		if status.DirectAddr.IsValid() { | ||||||
|  | 			fmt.Printf("    %-11s %v\n", "Address", status.DirectAddr) | ||||||
|  | 		} | ||||||
|  | 		fmt.Println("") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | func main_hosts() { | ||||||
|  | 	printUsage := func() { | ||||||
|  | 		fmt.Fprintf(os.Stderr, `Usage: %s hosts NETWORK_NAME | ||||||
|  |  | ||||||
|  |     NETWORK_NAME | ||||||
|  |         Unique name of the network interface created. | ||||||
|  |  | ||||||
|  | `, os.Args[0]) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(os.Args) != 3 { | ||||||
|  | 		printUsage() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	netName := os.Args[2] | ||||||
|  | 	state, err := loadNetworkState(netName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("Failed to load network state: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	config, err := loadPeerConfig(netName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("Failed to load config: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	nw := config.Network | ||||||
|  | 	for _, peer := range state.Peers { | ||||||
|  | 		if peer == nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		fmt.Printf("%d.%d.%d.%d %s\n", | ||||||
|  | 			nw[0], nw[1], nw[2], peer.PeerIP, peer.Name) | ||||||
|  | 	} | ||||||
|  | 	fmt.Println("") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | func fetchStatusReport(netName string) StatusReport { | ||||||
|  | 	client := http.Client{ | ||||||
|  | 		Transport: &http.Transport{ | ||||||
|  | 			Dial: func(_, _ string) (net.Conn, error) { | ||||||
|  | 				return net.Dial("unix", statusSocketPath(netName)) | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		Timeout: 8 * time.Second, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	getURL := "http://unix" + statusSocketPath(netName) | ||||||
|  | 	resp, err := client.Get(getURL) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("Failed to get response: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	report := StatusReport{} | ||||||
|  | 	if err := json.NewDecoder(resp.Body).Decode(&report); err != nil { | ||||||
|  | 		log.Fatalf("Failed to decode status report: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return report | ||||||
| } | } | ||||||
|   | |||||||
| @@ -15,16 +15,16 @@ func createLocalDiscoveryPacket(localIP byte, signingKey []byte) []byte { | |||||||
| 	} | 	} | ||||||
| 	buf := make([]byte, headerSize) | 	buf := make([]byte, headerSize) | ||||||
| 	h.Marshal(buf) | 	h.Marshal(buf) | ||||||
| 	out := make([]byte, headerSize+signOverhead) | 	out := make([]byte, headerSize+signingOverhead) | ||||||
| 	return sign.Sign(out[:0], buf, (*[64]byte)(signingKey)) | 	return sign.Sign(out[:0], buf, (*[64]byte)(signingKey)) | ||||||
| } | } | ||||||
|  |  | ||||||
| func headerFromLocalDiscoveryPacket(pkt []byte) (h Header, ok bool) { | func headerFromLocalDiscoveryPacket(pkt []byte) (h Header, ok bool) { | ||||||
| 	if len(pkt) != headerSize+signOverhead { | 	if len(pkt) != headerSize+signingOverhead { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	h.Parse(pkt[signOverhead:]) | 	h.Parse(pkt[signingOverhead:]) | ||||||
| 	ok = true | 	ok = true | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								peer/peer.go
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								peer/peer.go
									
									
									
									
									
								
							| @@ -13,6 +13,8 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
| 	"vppn/m" | 	"vppn/m" | ||||||
|  |  | ||||||
|  | 	"git.crumpington.com/lib/go/flock" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type peerMain struct { | type peerMain struct { | ||||||
| @@ -20,12 +22,7 @@ type peerMain struct { | |||||||
| 	ifReader   *IFReader | 	ifReader   *IFReader | ||||||
| 	connReader *ConnReader | 	connReader *ConnReader | ||||||
| 	hubPoller  *HubPoller | 	hubPoller  *HubPoller | ||||||
| } | 	lockFile   *os.File | ||||||
|  |  | ||||||
| type mainArgs struct { |  | ||||||
| 	NetName    string |  | ||||||
| 	HubAddress string |  | ||||||
| 	APIKey     string |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func newPeerMain(args mainArgs) *peerMain { | func newPeerMain(args mainArgs) *peerMain { | ||||||
| @@ -33,6 +30,14 @@ func newPeerMain(args mainArgs) *peerMain { | |||||||
| 		log.Printf("[Main] "+s, args...) | 		log.Printf("[Main] "+s, args...) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	lockFile, err := flock.TryLock(lockFilePath(args.NetName)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("Failed to open lock file: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if lockFile == nil { | ||||||
|  | 		log.Fatalf("Failed to obtain file lock.") | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	config, err := loadPeerConfig(args.NetName) | 	config, err := loadPeerConfig(args.NetName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		logf("Failed to load configuration: %v", err) | 		logf("Failed to load configuration: %v", err) | ||||||
| @@ -100,11 +105,15 @@ func newPeerMain(args mainArgs) *peerMain { | |||||||
| 		log.Fatalf("Failed to create hub poller: %v", err) | 		log.Fatalf("Failed to create hub poller: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Start status server. | ||||||
|  | 	go runStatusServer(g, statusSocketPath(args.NetName)) | ||||||
|  |  | ||||||
| 	return &peerMain{ | 	return &peerMain{ | ||||||
| 		Globals:    g, | 		Globals:    g, | ||||||
| 		ifReader:   NewIFReader(g), | 		ifReader:   NewIFReader(g), | ||||||
| 		connReader: NewConnReader(g, conn), | 		connReader: NewConnReader(g, conn), | ||||||
| 		hubPoller:  hubPoller, | 		hubPoller:  hubPoller, | ||||||
|  | 		lockFile:   lockFile, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -106,6 +106,25 @@ func (r *Remote) encryptControl(conf remoteConfig, packet []byte) []byte { | |||||||
| 	return conf.ControlCipher.Encrypt(h, packet, packet[len(packet):cap(packet)]) | 	return conf.ControlCipher.Encrypt(h, packet, packet[len(packet):cap(packet)]) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (r *Remote) Status() (RemoteStatus, bool) { | ||||||
|  | 	conf := r.conf() | ||||||
|  | 	if conf.Peer == nil { | ||||||
|  | 		return RemoteStatus{}, false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return RemoteStatus{ | ||||||
|  | 		PeerIP:     conf.Peer.PeerIP, | ||||||
|  | 		Up:         conf.Up, | ||||||
|  | 		Name:       conf.Peer.Name, | ||||||
|  | 		PublicIP:   conf.Peer.PublicIP, | ||||||
|  | 		Port:       conf.Peer.Port, | ||||||
|  | 		Relay:      conf.Peer.Relay, | ||||||
|  | 		Server:     conf.Server, | ||||||
|  | 		Direct:     conf.Direct, | ||||||
|  | 		DirectAddr: conf.DirectAddr, | ||||||
|  | 	}, true | ||||||
|  | } | ||||||
|  |  | ||||||
| // ---------------------------------------------------------------------------- | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
| // SendDataTo sends a data packet to the remote, called by the IFReader. | // SendDataTo sends a data packet to the remote, called by the IFReader. | ||||||
|   | |||||||
| @@ -154,6 +154,8 @@ func (r *remoteFSM) stateServer_onInit(msg controlMsg[packetInit]) { | |||||||
| 		Version: version, | 		Version: version, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Reset traceID to force state update on SYN. | ||||||
|  | 	r.traceID = 0 | ||||||
| 	r.sendControl(conf, init.Marshal(r.buf)) | 	r.sendControl(conf, init.Marshal(r.buf)) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -161,23 +163,19 @@ func (r *remoteFSM) stateServer_onSyn(msg controlMsg[packetSyn]) { | |||||||
| 	r.lastSeen = time.Now() | 	r.lastSeen = time.Now() | ||||||
| 	p := msg.Packet | 	p := msg.Packet | ||||||
|  |  | ||||||
| 	// Before we can respond to this packet, we need to make sure the |  | ||||||
| 	// route is setup properly. |  | ||||||
| 	conf := r.conf() | 	conf := r.conf() | ||||||
| 	logSyn := !conf.Up || conf.Direct != p.Direct |  | ||||||
|  | 	// New trace ID => Update the route configuration. | ||||||
|  | 	if p.TraceID != r.traceID { | ||||||
|  | 		r.traceID = p.TraceID | ||||||
|  |  | ||||||
| 		conf.Up = true | 		conf.Up = true | ||||||
| 		conf.Direct = p.Direct | 		conf.Direct = p.Direct | ||||||
| 		conf.DirectAddr = msg.SrcAddr | 		conf.DirectAddr = msg.SrcAddr | ||||||
|  |  | ||||||
| 	// Update data cipher if the key has changed. |  | ||||||
| 	if !conf.DataCipher.HasKey(p.SharedKey) { |  | ||||||
| 		conf.DataCipher = newDataCipherFromKey(p.SharedKey) | 		conf.DataCipher = newDataCipherFromKey(p.SharedKey) | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 		r.updateConf(conf) | 		r.updateConf(conf) | ||||||
|  |  | ||||||
| 	if logSyn { |  | ||||||
| 		r.logf("Got SYN.") | 		r.logf("Got SYN.") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -191,13 +189,14 @@ func (r *remoteFSM) stateServer_onSyn(msg controlMsg[packetSyn]) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Send probes if not a direct connection. | 	// Send probes if not a direct connection. The server sends probes without | ||||||
|  | 	// trace IDs unless responding to a client probe. | ||||||
| 	for _, addr := range msg.Packet.PossibleAddrs { | 	for _, addr := range msg.Packet.PossibleAddrs { | ||||||
| 		if !addr.IsValid() { | 		if !addr.IsValid() { | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		r.logf("Probing %v...", addr) | 		r.logf("Probing %v...", addr) | ||||||
| 		r.sendControlToAddr(packetProbe{TraceID: r.NewTraceID()}.Marshal(r.buf), addr) | 		r.sendControlToAddr(packetProbe{}.Marshal(r.buf), addr) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -213,6 +212,8 @@ func (r *remoteFSM) stateServer_onProbe(msg controlMsg[packetProbe]) { | |||||||
| func (r *remoteFSM) stateServer_onPingTimer() { | func (r *remoteFSM) stateServer_onPingTimer() { | ||||||
| 	conf := r.conf() | 	conf := r.conf() | ||||||
| 	if time.Since(r.lastSeen) > timeoutInterval && conf.Up { | 	if time.Since(r.lastSeen) > timeoutInterval && conf.Up { | ||||||
|  | 		// Reset trace ID to ensure connection goes up on next SYN. | ||||||
|  | 		r.traceID = 0 | ||||||
| 		conf.Up = false | 		conf.Up = false | ||||||
| 		r.updateConf(conf) | 		r.updateConf(conf) | ||||||
| 		r.logf("Timeout.") | 		r.logf("Timeout.") | ||||||
| @@ -310,7 +311,7 @@ func (r *remoteFSM) stateClientInit_onPing() stateFunc { | |||||||
|  |  | ||||||
| func (r *remoteFSM) enterClient() stateFunc { | func (r *remoteFSM) enterClient() stateFunc { | ||||||
| 	conf := r.conf() | 	conf := r.conf() | ||||||
| 	r.probes = make(map[uint64]sentProbe, 8) | 	clear(r.probes) | ||||||
|  |  | ||||||
| 	r.traceID = r.NewTraceID() | 	r.traceID = r.NewTraceID() | ||||||
| 	r.stateClient_sendSyn(conf) | 	r.stateClient_sendSyn(conf) | ||||||
|   | |||||||
							
								
								
									
										69
									
								
								peer/statusserver.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								peer/statusserver.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | package peer | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"log" | ||||||
|  | 	"net" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/netip" | ||||||
|  | 	"os" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type StatusReport struct { | ||||||
|  | 	Network     []byte | ||||||
|  | 	RelayPeerIP byte | ||||||
|  | 	Remotes     []RemoteStatus | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type RemoteStatus struct { | ||||||
|  | 	PeerIP     byte | ||||||
|  | 	Up         bool | ||||||
|  | 	Name       string | ||||||
|  | 	PublicIP   []byte | ||||||
|  | 	Port       uint16 | ||||||
|  | 	Relay      bool | ||||||
|  | 	Server     bool | ||||||
|  | 	Direct     bool | ||||||
|  | 	DirectAddr netip.AddrPort | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func runStatusServer(g Globals, socketPath string) { | ||||||
|  | 	_ = os.RemoveAll(socketPath) | ||||||
|  |  | ||||||
|  | 	handler := func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 		report := StatusReport{ | ||||||
|  | 			Network: g.Network, | ||||||
|  | 			Remotes: make([]RemoteStatus, 0, 255), | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		relay := g.RelayHandler.Load() | ||||||
|  | 		if relay != nil { | ||||||
|  | 			if relayStatus, ok := relay.Status(); ok { | ||||||
|  | 				report.RelayPeerIP = relayStatus.PeerIP | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for i := range g.RemotePeers { | ||||||
|  | 			remote := g.RemotePeers[i].Load() | ||||||
|  | 			status, ok := remote.Status() | ||||||
|  | 			if !ok { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			report.Remotes = append(report.Remotes, status) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		json.NewEncoder(w).Encode(report) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	server := http.Server{ | ||||||
|  | 		Handler: http.HandlerFunc(handler), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	unixListener, err := net.Listen("unix", socketPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("Failed to bind to unix socket: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := server.Serve(unixListener); err != nil { | ||||||
|  | 		log.Fatalf("Failed to serve on unix socket: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user