feat: INP2P v0.1.0 — complete P2P tunneling system
Core modules (M1-M6): - pkg/protocol: message format, encoding, NAT type enums - pkg/config: server/client config structs, env vars, validation - pkg/auth: CRC64 token, TOTP gen/verify, one-time relay tokens - pkg/nat: UDP/TCP STUN client and server - pkg/signal: WSS message dispatch, sync request/response - pkg/punch: UDP/TCP hole punching + priority chain - pkg/mux: stream multiplexer (7B frame: StreamID+Flags+Len) - pkg/tunnel: mux-based port forwarding with stats - pkg/relay: relay manager with TOTP auth + session bridging - internal/server: signaling server (login/heartbeat/report/coordinator) - internal/client: client (NAT detect/login/punch/relay/reconnect) - cmd/inp2ps + cmd/inp2pc: main entrypoints with graceful shutdown All tests pass: 16 tests across 5 packages Code: 3559 lines core + 861 lines tests = 19 source files
This commit is contained in:
119
cmd/inp2ps/main.go
Normal file
119
cmd/inp2ps/main.go
Normal file
@@ -0,0 +1,119 @@
|
||||
// inp2ps — INP2P Signaling Server
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/openp2p-cn/inp2p/internal/server"
|
||||
"github.com/openp2p-cn/inp2p/pkg/auth"
|
||||
"github.com/openp2p-cn/inp2p/pkg/config"
|
||||
"github.com/openp2p-cn/inp2p/pkg/nat"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := config.DefaultServerConfig()
|
||||
|
||||
flag.IntVar(&cfg.WSPort, "ws-port", cfg.WSPort, "WebSocket signaling port")
|
||||
flag.IntVar(&cfg.WebPort, "web-port", cfg.WebPort, "Web console port")
|
||||
flag.IntVar(&cfg.STUNUDP1, "stun-udp1", cfg.STUNUDP1, "UDP STUN port 1")
|
||||
flag.IntVar(&cfg.STUNUDP2, "stun-udp2", cfg.STUNUDP2, "UDP STUN port 2")
|
||||
flag.IntVar(&cfg.STUNTCP1, "stun-tcp1", cfg.STUNTCP1, "TCP STUN port 1")
|
||||
flag.IntVar(&cfg.STUNTCP2, "stun-tcp2", cfg.STUNTCP2, "TCP STUN port 2")
|
||||
flag.StringVar(&cfg.DBPath, "db", cfg.DBPath, "SQLite database path")
|
||||
flag.StringVar(&cfg.CertFile, "cert", "", "TLS certificate file")
|
||||
flag.StringVar(&cfg.KeyFile, "key", "", "TLS key file")
|
||||
flag.IntVar(&cfg.LogLevel, "log-level", cfg.LogLevel, "Log level (0=debug 1=info 2=warn 3=error)")
|
||||
token := flag.Uint64("token", 0, "Master authentication token (uint64)")
|
||||
user := flag.String("user", "", "Username for token generation (requires -password)")
|
||||
pass := flag.String("password", "", "Password for token generation")
|
||||
version := flag.Bool("version", false, "Print version and exit")
|
||||
flag.Parse()
|
||||
|
||||
if *version {
|
||||
fmt.Printf("inp2ps version %s\n", config.Version)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Token: either direct value or generated from user+password
|
||||
if *token > 0 {
|
||||
cfg.Token = *token
|
||||
} else if *user != "" && *pass != "" {
|
||||
cfg.Token = auth.MakeToken(*user, *pass)
|
||||
log.Printf("[main] token generated from credentials: %d", cfg.Token)
|
||||
}
|
||||
|
||||
cfg.FillFromEnv()
|
||||
|
||||
if err := cfg.Validate(); err != nil {
|
||||
log.Fatalf("[main] config error: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("[main] inp2ps v%s starting", config.Version)
|
||||
log.Printf("[main] WSS :%d | STUN UDP :%d,%d | STUN TCP :%d,%d",
|
||||
cfg.WSPort, cfg.STUNUDP1, cfg.STUNUDP2, cfg.STUNTCP1, cfg.STUNTCP2)
|
||||
|
||||
// ─── STUN Servers ───
|
||||
stunQuit := make(chan struct{})
|
||||
|
||||
startSTUN := func(proto string, port int, fn func(int, <-chan struct{}) error) {
|
||||
go func() {
|
||||
log.Printf("[main] %s STUN listening on :%d", proto, port)
|
||||
if err := fn(port, stunQuit); err != nil {
|
||||
log.Printf("[main] %s STUN :%d error: %v", proto, port, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
startSTUN("UDP", cfg.STUNUDP1, nat.ServeUDPSTUN)
|
||||
if cfg.STUNUDP2 != cfg.STUNUDP1 {
|
||||
startSTUN("UDP", cfg.STUNUDP2, nat.ServeUDPSTUN)
|
||||
}
|
||||
startSTUN("TCP", cfg.STUNTCP1, nat.ServeTCPSTUN)
|
||||
if cfg.STUNTCP2 != cfg.STUNTCP1 {
|
||||
startSTUN("TCP", cfg.STUNTCP2, nat.ServeTCPSTUN)
|
||||
}
|
||||
|
||||
// ─── Signaling Server ───
|
||||
srv := server.New(cfg)
|
||||
srv.StartCleanup()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/ws", srv.HandleWS)
|
||||
mux.HandleFunc("/api/v1/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, `{"status":"ok","version":"%s","nodes":%d}`, config.Version, len(srv.GetOnlineNodes()))
|
||||
})
|
||||
|
||||
// ─── HTTP Listener ───
|
||||
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.WSPort))
|
||||
if err != nil {
|
||||
log.Fatalf("[main] listen :%d: %v", cfg.WSPort, err)
|
||||
}
|
||||
log.Printf("[main] signaling server on :%d (no TLS — use reverse proxy for production)", cfg.WSPort)
|
||||
|
||||
httpSrv := &http.Server{Handler: mux}
|
||||
go func() {
|
||||
if err := httpSrv.Serve(ln); err != http.ErrServerClosed {
|
||||
log.Fatalf("[main] serve: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// ─── Graceful Shutdown ───
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigCh
|
||||
|
||||
log.Println("[main] shutting down...")
|
||||
close(stunQuit)
|
||||
srv.Stop()
|
||||
httpSrv.Shutdown(context.Background())
|
||||
log.Println("[main] goodbye")
|
||||
}
|
||||
Reference in New Issue
Block a user