WIP
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
							
								
								
									
										153
									
								
								peer/main2.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								peer/main2.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,153 @@ | ||||
| package peer | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Usage: | ||||
| // | ||||
| // vppn netName run | ||||
| // vppn netName status | ||||
| func Main2() { | ||||
| 	printUsage := func() { | ||||
| 		fmt.Fprintf(os.Stderr, `%s COMMAND [ARGUMENTS...] | ||||
|  | ||||
| Available commands: | ||||
|     run | ||||
|     status | ||||
| `, os.Args[0]) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
|  | ||||
| 	if len(os.Args) < 2 { | ||||
| 		printUsage() | ||||
| 	} | ||||
|  | ||||
| 	command := os.Args[1] | ||||
|  | ||||
| 	switch command { | ||||
| 	case "run": | ||||
| 		main_run() | ||||
| 	case "status": | ||||
| 		main_status() | ||||
| 	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] | ||||
|  | ||||
| 	client := http.Client{ | ||||
| 		Transport: &http.Transport{ | ||||
| 			Dial: func(_, _ string) (net.Conn, error) { | ||||
| 				return net.Dial("unix", statusSocketPath(netName)) | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	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) | ||||
| 	} | ||||
|  | ||||
| 	b := strings.Builder{} | ||||
|  | ||||
| 	for _, status := range report.Remotes { | ||||
| 		b.WriteString(fmt.Sprintf("%3d ", status.PeerIP)) | ||||
| 		if status.Up { | ||||
| 			b.WriteString("UP   ") | ||||
| 		} else { | ||||
| 			b.WriteString("DOWN ") | ||||
| 		} | ||||
| 		if status.Relay && status.Direct { | ||||
| 			b.WriteString("RELAY  ") | ||||
| 		} else if status.Server { | ||||
| 			b.WriteString("SERVER ") | ||||
| 		} else { | ||||
| 			b.WriteString("CLIENT ") | ||||
| 		} | ||||
|  | ||||
| 		if status.Direct { | ||||
| 			b.WriteString("DIRECT  ") | ||||
| 		} else { | ||||
| 			b.WriteString("RELAYED ") | ||||
| 		} | ||||
|  | ||||
| 		b.WriteString(fmt.Sprintf("%45s ", status.DirectAddr)) | ||||
|  | ||||
| 		b.WriteString(status.Name) | ||||
|  | ||||
| 		b.WriteString("\n") | ||||
| 	} | ||||
|  | ||||
| 	fmt.Print(b.String()) | ||||
| } | ||||
| @@ -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. | ||||
|   | ||||
| @@ -171,7 +171,7 @@ func (r *remoteFSM) stateServer_onSyn(msg controlMsg[packetSyn]) { | ||||
| 	conf.DirectAddr = msg.SrcAddr | ||||
|  | ||||
| 	// Update data cipher if the key has changed. | ||||
| 	if !conf.DataCipher.HasKey(p.SharedKey) { | ||||
| 	if conf.DataCipher == nil || !conf.DataCipher.HasKey(p.SharedKey) { | ||||
| 		conf.DataCipher = newDataCipherFromKey(p.SharedKey) | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										57
									
								
								peer/statusserver.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								peer/statusserver.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| package peer | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/netip" | ||||
| 	"os" | ||||
| ) | ||||
|  | ||||
| type StatusReport struct { | ||||
| 	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{ | ||||
| 			Remotes: make([]RemoteStatus, 0, 255), | ||||
| 		} | ||||
| 		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