Compare commits
	
		
			14 Commits
		
	
	
		
			v0.9.0
			...
			client-int
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3ebfe754e7 | ||
|  | 069243e5d4 | ||
|  | bd78ffd669 | ||
|  | a90ab3f5d6 | ||
|  | 650c74c013 | ||
|  | b308150d21 | ||
|  | a0b7ecbfe0 | ||
|  | 69dff24344 | ||
|  | 257fac67ce | ||
|  | 2ff8aaf5c4 | ||
|  | fccc4f7d57 | ||
|  | c6d35856bc | ||
|  | 5844584219 | ||
|  | e458e43d83 | 
| @@ -65,9 +65,10 @@ AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN | ||||
| Type=simple | ||||
| User=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 | ||||
| RestartSec=8 | ||||
| TimeoutStopSec=24 | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								cmd/hub/hub
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								cmd/hub/hub
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -7,5 +7,5 @@ import ( | ||||
|  | ||||
| func main() { | ||||
| 	log.SetFlags(0) | ||||
| 	peer.Main() | ||||
| 	peer.Main2() | ||||
| } | ||||
|   | ||||
							
								
								
									
										14
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,15 +1,15 @@ | ||||
| module vppn | ||||
|  | ||||
| go 1.24.1 | ||||
| go 1.25.1 | ||||
|  | ||||
| require ( | ||||
| 	git.crumpington.com/lib/go v0.9.0 | ||||
| 	golang.org/x/crypto v0.36.0 | ||||
| 	golang.org/x/sys v0.31.0 | ||||
| 	git.crumpington.com/lib/go v0.9.1 | ||||
| 	golang.org/x/crypto v0.42.0 | ||||
| 	golang.org/x/sys v0.36.0 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	github.com/mattn/go-sqlite3 v1.14.24 // indirect | ||||
| 	golang.org/x/net v0.37.0 // indirect | ||||
| 	golang.org/x/text v0.23.0 // indirect | ||||
| 	github.com/mattn/go-sqlite3 v1.14.32 // indirect | ||||
| 	golang.org/x/net v0.44.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.0/go.mod h1:i3DXiPDo/pgPMHAxUTpyo1Xj2spcvXwXcBef3aSYlnQ= | ||||
| github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= | ||||
| github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | ||||
| golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= | ||||
| golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= | ||||
| golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= | ||||
| golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= | ||||
| golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= | ||||
| golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | ||||
| golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= | ||||
| golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= | ||||
| git.crumpington.com/lib/go v0.9.1 h1:xLBzcgiZRB6Ky3Ce9hKE+Ko0YbkA4USF4eJk5i5RJF4= | ||||
| git.crumpington.com/lib/go v0.9.1/go.mod h1:5nnfjdnUnj/FHhakaliKQKsKeSkUb0GEUKF3PqRgUXg= | ||||
| github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= | ||||
| github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | ||||
| golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= | ||||
| golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= | ||||
| golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= | ||||
| golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= | ||||
| golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= | ||||
| golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= | ||||
| golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= | ||||
| golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package peer | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/aes" | ||||
| 	"crypto/cipher" | ||||
| 	"crypto/rand" | ||||
| @@ -39,10 +38,6 @@ func (sc *dataCipher) Key() [32]byte { | ||||
| 	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 { | ||||
| 	const s = dataHeaderSize | ||||
| 	out = out[:s+dataCipherOverhead+len(data)] | ||||
|   | ||||
| @@ -29,6 +29,10 @@ func configDir(netName string) string { | ||||
| 	return filepath.Join(d, ".vppn", netName) | ||||
| } | ||||
|  | ||||
| func lockFilePath(netName string) string { | ||||
| 	return filepath.Join(configDir(netName), "__lock__") | ||||
| } | ||||
|  | ||||
| func peerConfigPath(netName string) string { | ||||
| 	return filepath.Join(configDir(netName), "config.json") | ||||
| } | ||||
| @@ -41,6 +45,10 @@ func startupCountPath(netName string) string { | ||||
| 	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 { | ||||
| 	outDir := filepath.Dir(outPath) | ||||
| 	_ = os.MkdirAll(outDir, 0700) | ||||
|   | ||||
| @@ -19,7 +19,7 @@ const ( | ||||
|  | ||||
| 	controlCipherOverhead = 16 | ||||
| 	dataCipherOverhead    = 16 | ||||
| 	signOverhead          = 64 | ||||
| 	signingOverhead       = 64 | ||||
|  | ||||
| 	pingInterval                  = 8 * time.Second | ||||
| 	timeoutInterval               = 30 * time.Second | ||||
|   | ||||
							
								
								
									
										208
									
								
								peer/main.go
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								peer/main.go
									
									
									
									
									
								
							| @@ -1,23 +1,207 @@ | ||||
| package peer | ||||
|  | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/netip" | ||||
| 	"os" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| func Main() { | ||||
| 	args := mainArgs{} | ||||
| // Usage: | ||||
| // | ||||
| // 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.") | ||||
| 	flag.StringVar(&args.HubAddress, "hub-address", "", "[REQUIRED] The hub address.") | ||||
| 	flag.StringVar(&args.APIKey, "api-key", "", "[REQUIRED] The node's API key.") | ||||
| 	flag.Parse() | ||||
|  | ||||
| 	if args.NetName == "" || args.HubAddress == "" || args.APIKey == "" { | ||||
| 		flag.Usage() | ||||
| Available commands: | ||||
|     run | ||||
|     status | ||||
|     hosts | ||||
| `, os.Args[0]) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
|  | ||||
| 	peer := newPeerMain(args) | ||||
| 	peer.Run() | ||||
| 	if len(os.Args) < 2 { | ||||
| 		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) | ||||
| 	h.Marshal(buf) | ||||
| 	out := make([]byte, headerSize+signOverhead) | ||||
| 	out := make([]byte, headerSize+signingOverhead) | ||||
| 	return sign.Sign(out[:0], buf, (*[64]byte)(signingKey)) | ||||
| } | ||||
|  | ||||
| func headerFromLocalDiscoveryPacket(pkt []byte) (h Header, ok bool) { | ||||
| 	if len(pkt) != headerSize+signOverhead { | ||||
| 	if len(pkt) != headerSize+signingOverhead { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	h.Parse(pkt[signOverhead:]) | ||||
| 	h.Parse(pkt[signingOverhead:]) | ||||
| 	ok = true | ||||
| 	return | ||||
| } | ||||
|   | ||||
							
								
								
									
										21
									
								
								peer/peer.go
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								peer/peer.go
									
									
									
									
									
								
							| @@ -13,6 +13,8 @@ import ( | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"vppn/m" | ||||
|  | ||||
| 	"git.crumpington.com/lib/go/flock" | ||||
| ) | ||||
|  | ||||
| type peerMain struct { | ||||
| @@ -20,12 +22,7 @@ type peerMain struct { | ||||
| 	ifReader   *IFReader | ||||
| 	connReader *ConnReader | ||||
| 	hubPoller  *HubPoller | ||||
| } | ||||
|  | ||||
| type mainArgs struct { | ||||
| 	NetName    string | ||||
| 	HubAddress string | ||||
| 	APIKey     string | ||||
| 	lockFile   *os.File | ||||
| } | ||||
|  | ||||
| func newPeerMain(args mainArgs) *peerMain { | ||||
| @@ -33,6 +30,14 @@ func newPeerMain(args mainArgs) *peerMain { | ||||
| 		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) | ||||
| 	if err != nil { | ||||
| 		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) | ||||
| 	} | ||||
|  | ||||
| 	// Start status server. | ||||
| 	go runStatusServer(g, statusSocketPath(args.NetName)) | ||||
|  | ||||
| 	return &peerMain{ | ||||
| 		Globals:    g, | ||||
| 		ifReader:   NewIFReader(g), | ||||
| 		connReader: NewConnReader(g, conn), | ||||
| 		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)]) | ||||
| } | ||||
|  | ||||
| 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. | ||||
|   | ||||
| @@ -154,6 +154,8 @@ func (r *remoteFSM) stateServer_onInit(msg controlMsg[packetInit]) { | ||||
| 		Version: version, | ||||
| 	} | ||||
|  | ||||
| 	// Reset traceID to force state update on SYN. | ||||
| 	r.traceID = 0 | ||||
| 	r.sendControl(conf, init.Marshal(r.buf)) | ||||
| } | ||||
|  | ||||
| @@ -161,23 +163,19 @@ func (r *remoteFSM) stateServer_onSyn(msg controlMsg[packetSyn]) { | ||||
| 	r.lastSeen = time.Now() | ||||
| 	p := msg.Packet | ||||
|  | ||||
| 	// Before we can respond to this packet, we need to make sure the | ||||
| 	// route is setup properly. | ||||
| 	conf := r.conf() | ||||
| 	logSyn := !conf.Up || conf.Direct != p.Direct | ||||
|  | ||||
| 	conf.Up = true | ||||
| 	conf.Direct = p.Direct | ||||
| 	conf.DirectAddr = msg.SrcAddr | ||||
| 	// New trace ID => Update the route configuration. | ||||
| 	if p.TraceID != r.traceID { | ||||
| 		r.traceID = p.TraceID | ||||
|  | ||||
| 		conf.Up = true | ||||
| 		conf.Direct = p.Direct | ||||
| 		conf.DirectAddr = msg.SrcAddr | ||||
|  | ||||
| 	// Update data cipher if the key has changed. | ||||
| 	if !conf.DataCipher.HasKey(p.SharedKey) { | ||||
| 		conf.DataCipher = newDataCipherFromKey(p.SharedKey) | ||||
| 	} | ||||
|  | ||||
| 	r.updateConf(conf) | ||||
|  | ||||
| 	if logSyn { | ||||
| 		r.updateConf(conf) | ||||
| 		r.logf("Got SYN.") | ||||
| 	} | ||||
|  | ||||
| @@ -191,13 +189,14 @@ func (r *remoteFSM) stateServer_onSyn(msg controlMsg[packetSyn]) { | ||||
| 		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 { | ||||
| 		if !addr.IsValid() { | ||||
| 			break | ||||
| 		} | ||||
| 		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() { | ||||
| 	conf := r.conf() | ||||
| 	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 | ||||
| 		r.updateConf(conf) | ||||
| 		r.logf("Timeout.") | ||||
| @@ -310,7 +311,7 @@ func (r *remoteFSM) stateClientInit_onPing() stateFunc { | ||||
|  | ||||
| func (r *remoteFSM) enterClient() stateFunc { | ||||
| 	conf := r.conf() | ||||
| 	r.probes = make(map[uint64]sentProbe, 8) | ||||
| 	clear(r.probes) | ||||
|  | ||||
| 	r.traceID = r.NewTraceID() | ||||
| 	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