Initial commit: waste-go skeleton
Ed25519/X25519/ChaCha20-Poly1305 crypto, peer handshake, mesh state, IPC server, relay server, and NAT stub. Builds clean on Go 1.22+. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
87
internal/nat/nat.go
Normal file
87
internal/nat/nat.go
Normal file
@@ -0,0 +1,87 @@
|
||||
// Package nat handles relay registration and future hole-punching.
|
||||
package nat
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/waste-go/internal/mesh"
|
||||
"github.com/waste-go/internal/proto"
|
||||
)
|
||||
|
||||
// Run connects to the relay server (if configured) and keeps the connection alive.
|
||||
// Pass an empty relayAddr to skip relay entirely (LAN-only mode).
|
||||
func Run(m *mesh.Mesh, relayAddr string) error {
|
||||
if relayAddr == "" {
|
||||
log.Println("nat: no relay configured, running in LAN-only mode")
|
||||
select {} // block forever — task stays alive
|
||||
}
|
||||
|
||||
for {
|
||||
log.Printf("nat: connecting to relay at %s", relayAddr)
|
||||
if err := connectRelay(relayAddr, m); err != nil {
|
||||
log.Printf("nat: relay error: %v", err)
|
||||
}
|
||||
log.Printf("nat: reconnecting to relay in 10s...")
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func connectRelay(addr string, m *mesh.Mesh) error {
|
||||
conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dial relay: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
id := m.Identity
|
||||
enc := json.NewEncoder(conn)
|
||||
dec := json.NewDecoder(bufio.NewReader(conn))
|
||||
|
||||
// Register with the relay
|
||||
reg := proto.RelayMessage{
|
||||
Type: proto.RelayRegister,
|
||||
Peer: ptr(id.PeerInfo()),
|
||||
Signature: id.Sign([]byte(id.PeerID())),
|
||||
}
|
||||
if err := enc.Encode(reg); err != nil {
|
||||
return fmt.Errorf("sending register: %w", err)
|
||||
}
|
||||
log.Printf("nat: registered with relay as %s", id.PeerID().Short())
|
||||
|
||||
// Ask for the current peer list (bootstrap)
|
||||
if err := enc.Encode(proto.RelayMessage{Type: proto.RelayListPeers}); err != nil {
|
||||
return fmt.Errorf("sending list_peers: %w", err)
|
||||
}
|
||||
|
||||
// Read relay messages
|
||||
for {
|
||||
var msg proto.RelayMessage
|
||||
if err := dec.Decode(&msg); err != nil {
|
||||
return fmt.Errorf("reading relay message: %w", err)
|
||||
}
|
||||
|
||||
switch msg.Type {
|
||||
case proto.RelayPeerList:
|
||||
log.Printf("nat: relay gave us %d peer hints", len(msg.Peers))
|
||||
// TODO: attempt direct connections to each peer hint
|
||||
|
||||
case proto.RelayForwarded:
|
||||
from := "unknown"
|
||||
if msg.From != nil {
|
||||
from = msg.From.Short()
|
||||
}
|
||||
log.Printf("nat: relayed envelope from %s — direct connection not yet implemented", from)
|
||||
// TODO: decrypt and process as PeerMessage
|
||||
|
||||
case proto.RelayError:
|
||||
log.Printf("nat: relay error: %s", msg.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ptr[T any](v T) *T { return &v }
|
||||
Reference in New Issue
Block a user