package main import ( "crypto/subtle" "encoding/hex" "errors" "flag" "fmt" "io" "net" "net/netip" "os" "os/exec" "os/signal" "strconv" "strings" "sync" "sync/atomic" "syscall" "time" "unsafe" "dragontcpvpn/internal/protocol" ) const ( defaultVPNv4Prefix = "10.123.0.0/16" defaultVPNv6Prefix = "fd7a:4472:6167:6f6e::/64" ) type debugStats struct { enabled bool packets bool started time.Time activeConns atomic.Int64 activeSessions atomic.Int64 upPackets atomic.Uint64 downPackets atomic.Uint64 upBytes atomic.Uint64 downBytes atomic.Uint64 dropped atomic.Uint64 errors atomic.Uint64 } func (d *debugStats) logf(format string, args ...any) { if d != nil && d.enabled { fmt.Printf("[DEBUG] "+format+"\n", args...) } } func (d *debugStats) packetf(format string, args ...any) { if d != nil && d.packets { fmt.Printf("[PACKET] "+format+"\n", args...) } } func (d *debugStats) errorf(format string, args ...any) { if d != nil { d.errors.Add(1) if d.enabled { fmt.Printf("[ERROR] "+format+"\n", args...) } } } func tokenEqual(a, b string) bool { if len(a) != len(b) { return false } return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1 } type vpnSession struct { sid protocol.VPNSessionID ipv4 netip.Addr ipv6 netip.Addr mtu int maxChunk int maxPackets int manager *vpnManager mu sync.Mutex notify chan struct{} packets map[uint32][]byte nextDown uint32 closed bool lastSeen time.Time upMu sync.Mutex expectedUp uint32 currentSeq uint32 currentTotal int currentBuf []byte haveCurrent bool lastComplete uint32 lastCompleteTotal int haveLastComplete bool } func newVPNSession(m *vpnManager, sid protocol.VPNSessionID, v4, v6 netip.Addr, mtu, maxChunk, maxPackets int) *vpnSession { return &vpnSession{ sid: sid, ipv4: v4, ipv6: v6, mtu: mtu, maxChunk: maxChunk, maxPackets: maxPackets, manager: m, notify: make(chan struct{}), packets: make(map[uint32][]byte, maxPackets), lastSeen: time.Now(), } } func (s *vpnSession) signalLocked() { close(s.notify) s.notify = make(chan struct{}) } func (s *vpnSession) touchLocked() { s.lastSeen = time.Now() } func (s *vpnSession) touch() { s.mu.Lock(); s.touchLocked(); s.mu.Unlock() } func (s *vpnSession) enqueue(packet []byte) bool { if len(packet) == 0 || len(packet) > 65535 { return false } s.mu.Lock() defer s.mu.Unlock() if s.closed { return false } if len(s.packets) >= s.maxPackets { if s.manager.debug != nil { s.manager.debug.dropped.Add(1) } return false } seq := s.nextDown s.nextDown++ s.packets[seq] = append([]byte(nil), packet...) s.touchLocked() s.signalLocked() if s.manager.debug != nil { s.manager.debug.downPackets.Add(1) s.manager.debug.downBytes.Add(uint64(len(packet))) s.manager.debug.packetf("QUEUE sid=%s seq=%d bytes=%d", shortSID(s.sid), seq, len(packet)) } return true } func (s *vpnSession) push(seq uint32, offset, total int, data []byte) (int, error) { s.upMu.Lock() defer s.upMu.Unlock() if total < 1 || total > 65535 || len(data) < 1 || len(data) > s.maxChunk || offset < 0 || offset+len(data) > total { return 0, errors.New("invalid packet fragment") } if s.haveLastComplete && seq == s.lastComplete { s.touch() return s.lastCompleteTotal, nil } if seq < s.expectedUp { return 0, fmt.Errorf("old upload sequence %d", seq) } if seq > s.expectedUp { return 0, fmt.Errorf("upload sequence %d expected %d", seq, s.expectedUp) } if !s.haveCurrent { if offset != 0 { return 0, errors.New("first fragment offset must be zero") } s.haveCurrent = true s.currentSeq = seq s.currentTotal = total s.currentBuf = make([]byte, 0, total) } if s.currentSeq != seq || s.currentTotal != total { return 0, errors.New("packet fragment metadata changed") } // Idempotent retry: if this exact offset was already accepted, acknowledge // the existing bytes instead of appending duplicate data. if offset < len(s.currentBuf) { end := offset + len(data) if end <= len(s.currentBuf) && string(s.currentBuf[offset:end]) == string(data) { return len(s.currentBuf), nil } return 0, errors.New("retry fragment does not match accepted data") } if offset != len(s.currentBuf) { return 0, fmt.Errorf("fragment offset %d expected %d", offset, len(s.currentBuf)) } s.currentBuf = append(s.currentBuf, data...) accepted := len(s.currentBuf) if accepted < total { s.touch() return accepted, nil } packet := append([]byte(nil), s.currentBuf...) s.haveCurrent = false s.currentBuf = nil if err := s.manager.acceptClientPacket(s, packet); err != nil { return 0, err } s.lastComplete = seq s.lastCompleteTotal = total s.haveLastComplete = true s.expectedUp++ s.touch() if s.manager.debug != nil { s.manager.debug.upPackets.Add(1) s.manager.debug.upBytes.Add(uint64(len(packet))) s.manager.debug.packetf("UP sid=%s seq=%d bytes=%d", shortSID(s.sid), seq, len(packet)) } return accepted, nil } func (s *vpnSession) pull(ack, want uint32, offset, limit int, wait time.Duration) ([]byte, int, bool, error) { if offset < 0 || limit < 1 || limit > s.maxChunk { return nil, 0, false, errors.New("invalid pull") } timer := time.NewTimer(wait) defer timer.Stop() for { s.mu.Lock() s.touchLocked() if ack != protocol.VPNNoAck { for seq := range s.packets { if seq <= ack { delete(s.packets, seq) } } } if packet, ok := s.packets[want]; ok { if offset >= len(packet) { s.mu.Unlock() return nil, len(packet), false, errors.New("pull offset beyond packet") } end := offset + limit if end > len(packet) { end = len(packet) } out := append([]byte(nil), packet[offset:end]...) total := len(packet) s.mu.Unlock() return out, total, false, nil } if s.closed { s.mu.Unlock() return nil, 0, false, net.ErrClosed } ch := s.notify s.mu.Unlock() select { case <-ch: case <-timer.C: return nil, 0, true, nil } } } func (s *vpnSession) close() { s.mu.Lock() if !s.closed { s.closed = true s.signalLocked() } s.mu.Unlock() } type vpnManager struct { mu sync.RWMutex sessions map[protocol.VPNSessionID]*vpnSession byIPv4 map[netip.Addr]*vpnSession byIPv6 map[netip.Addr]*vpnSession maxChunk int maxPackets int pollWait time.Duration timeout time.Duration tun *os.File tunWriteMu sync.Mutex mockEcho bool allowPrivate bool debug *debugStats v4Prefix netip.Prefix v6Prefix netip.Prefix } func newVPNManager(tun *os.File, mockEcho bool, maxChunk, maxPackets int, pollWait, timeout time.Duration, allowPrivate bool, debug *debugStats) *vpnManager { v4p := netip.MustParsePrefix(defaultVPNv4Prefix) v6p := netip.MustParsePrefix(defaultVPNv6Prefix) m := &vpnManager{ sessions: make(map[protocol.VPNSessionID]*vpnSession), byIPv4: make(map[netip.Addr]*vpnSession), byIPv6: make(map[netip.Addr]*vpnSession), maxChunk: maxChunk, maxPackets: maxPackets, pollWait: pollWait, timeout: timeout, tun: tun, mockEcho: mockEcho, allowPrivate: allowPrivate, debug: debug, v4Prefix: v4p, v6Prefix: v6p, } if tun != nil { go m.tunReadLoop() } go m.cleanupLoop() return m } func (m *vpnManager) addOrGet(sid protocol.VPNSessionID, v4, v6 netip.Addr, mtu int) (*vpnSession, error) { if !m.v4Prefix.Contains(v4) || v4 == netip.MustParseAddr("10.123.0.1") { return nil, errors.New("client IPv4 outside DragonTCP subnet") } if !m.v6Prefix.Contains(v6) || v6 == netip.MustParseAddr("fd7a:4472:6167:6f6e::1") { return nil, errors.New("client IPv6 outside DragonTCP subnet") } if mtu < 576 || mtu > 9000 { return nil, errors.New("invalid client MTU") } m.mu.Lock() defer m.mu.Unlock() if old := m.sessions[sid]; old != nil { if old.ipv4 != v4 || old.ipv6 != v6 { return nil, errors.New("session address mismatch") } old.touch() return old, nil } if m.byIPv4[v4] != nil || m.byIPv6[v6] != nil { return nil, errors.New("client VPN address already in use") } s := newVPNSession(m, sid, v4, v6, mtu, m.maxChunk, m.maxPackets) m.sessions[sid] = s m.byIPv4[v4] = s m.byIPv6[v6] = s if m.debug != nil { m.debug.activeSessions.Add(1) m.debug.logf("SESSION OPEN sid=%s ipv4=%s ipv6=%s mtu=%d", shortSID(sid), v4, v6, mtu) } return s, nil } func (m *vpnManager) get(sid protocol.VPNSessionID) *vpnSession { m.mu.RLock() s := m.sessions[sid] m.mu.RUnlock() return s } func (m *vpnManager) remove(sid protocol.VPNSessionID) { m.mu.Lock() s := m.sessions[sid] if s != nil { delete(m.sessions, sid) delete(m.byIPv4, s.ipv4) delete(m.byIPv6, s.ipv6) } m.mu.Unlock() if s != nil { s.close() if m.debug != nil { m.debug.activeSessions.Add(-1) m.debug.logf("SESSION CLOSE sid=%s", shortSID(sid)) } } } func (m *vpnManager) cleanupLoop() { ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() for range ticker.C { cutoff := time.Now().Add(-m.timeout) var stale []protocol.VPNSessionID m.mu.RLock() for sid, s := range m.sessions { s.mu.Lock() last := s.lastSeen closed := s.closed s.mu.Unlock() if closed || last.Before(cutoff) { stale = append(stale, sid) } } m.mu.RUnlock() for _, sid := range stale { m.remove(sid) } } } func packetAddresses(packet []byte) (src, dst netip.Addr, err error) { if len(packet) < 1 { return src, dst, errors.New("empty IP packet") } switch packet[0] >> 4 { case 4: if len(packet) < 20 { return src, dst, errors.New("short IPv4 packet") } total := int(packet[2])<<8 | int(packet[3]) if total < 20 || total > len(packet) { return src, dst, errors.New("invalid IPv4 total length") } var a, b [4]byte copy(a[:], packet[12:16]) copy(b[:], packet[16:20]) return netip.AddrFrom4(a), netip.AddrFrom4(b), nil case 6: if len(packet) < 40 { return src, dst, errors.New("short IPv6 packet") } total := 40 + (int(packet[4])<<8 | int(packet[5])) if total > len(packet) { return src, dst, errors.New("invalid IPv6 payload length") } var a, b [16]byte copy(a[:], packet[8:24]) copy(b[:], packet[24:40]) return netip.AddrFrom16(a), netip.AddrFrom16(b), nil default: return src, dst, errors.New("unsupported IP version") } } func destinationAllowed(dst netip.Addr, allowPrivate bool) bool { if dst.IsUnspecified() || dst.IsMulticast() { return false } if allowPrivate { return true } if dst.IsLoopback() || dst.IsLinkLocalUnicast() || dst.IsPrivate() { return false } return true } func (m *vpnManager) acceptClientPacket(s *vpnSession, packet []byte) error { src, dst, err := packetAddresses(packet) if err != nil { return err } if src != s.ipv4 && src != s.ipv6 { return fmt.Errorf("source %s does not match session address", src) } if !destinationAllowed(dst, m.allowPrivate) { return fmt.Errorf("destination %s is blocked; use --allow-private to permit it", dst) } if m.mockEcho { s.enqueue(packet) return nil } if m.tun == nil { return errors.New("VPN TUN is unavailable") } m.tunWriteMu.Lock() n, err := m.tun.Write(packet) m.tunWriteMu.Unlock() if err != nil { return err } if n != len(packet) { return io.ErrShortWrite } return nil } func (m *vpnManager) tunReadLoop() { buf := make([]byte, 65535) for { n, err := m.tun.Read(buf) if err != nil { if m.debug != nil { m.debug.errorf("TUN read: %v", err) } return } if n < 1 { continue } packet := append([]byte(nil), buf[:n]...) _, dst, e := packetAddresses(packet) if e != nil { continue } m.mu.RLock() var s *vpnSession if dst.Is4() { s = m.byIPv4[dst] } else { s = m.byIPv6[dst] } m.mu.RUnlock() if s != nil { s.enqueue(packet) } } } func shortSID(sid protocol.VPNSessionID) string { return hex.EncodeToString(sid[:4]) } func processVPN(conn net.Conn, requestID uint32, payload []byte, token string, m *vpnManager) error { switch payload[0] { case protocol.VPNCmdOpen: sid, tok, v4, v6, mtu, err := protocol.ParseVPNOpen(payload) if err != nil { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError(err.Error())) } if !tokenEqual(tok, token) { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError("authentication failed")) } _, err = m.addOrGet(sid, v4, v6, mtu) if err != nil { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError(err.Error())) } return protocol.WriteResponseFrame(conn, requestID, protocol.BuildVPNOpened(m.maxChunk)) case protocol.VPNCmdPush: sid, seq, offset, total, data, err := protocol.ParseVPNPush(payload) if err != nil { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError(err.Error())) } s := m.get(sid) if s == nil { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError("unknown VPN session")) } accepted, err := s.push(seq, offset, total, data) if err != nil { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError(err.Error())) } return protocol.WriteResponseFrame(conn, requestID, protocol.BuildVPNAck(seq, accepted)) case protocol.VPNCmdPull: sid, ack, want, offset, limit, err := protocol.ParseVPNPull(payload) if err != nil { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError(err.Error())) } s := m.get(sid) if s == nil { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError("unknown VPN session")) } if limit > s.maxChunk { limit = s.maxChunk } data, total, wait, err := s.pull(ack, want, offset, limit, m.pollWait) if err != nil { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError(err.Error())) } if wait { return protocol.WriteResponseFrame(conn, requestID, []byte{protocol.VPNRespWait}) } m.debug.packetf("DOWN sid=%s seq=%d offset=%d bytes=%d total=%d", shortSID(sid), want, offset, len(data), total) return protocol.WriteResponseFrame(conn, requestID, protocol.BuildVPNData(want, offset, total, data)) case protocol.VPNCmdClose: sid, err := protocol.ParseVPNClose(payload) if err != nil { return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError(err.Error())) } m.remove(sid) return protocol.WriteResponseFrame(conn, requestID, []byte{protocol.VPNRespClosed}) default: return protocol.WriteResponseFrame(conn, requestID, protocol.VPNError("unknown VPN command")) } } func handleConn(conn net.Conn, token string, m *vpnManager, slots chan struct{}, debug *debugStats) { defer func() { <-slots; debug.activeConns.Add(-1); _ = conn.Close() }() protocol.TuneTCP(conn) for { _ = conn.SetDeadline(time.Now().Add(30 * time.Second)) requestID, _, payload, err := protocol.ReadRequestFrame(conn) if err != nil { if !errors.Is(err, io.EOF) && !errors.Is(err, net.ErrClosed) { debug.errorf("peer=%v read: %v", conn.RemoteAddr(), err) } return } if !protocol.IsVPNCommand(payload) { _ = protocol.WriteResponseFrame(conn, requestID, protocol.VPNError("this binary accepts DragonTCP VPN packet commands only")) continue } if err := processVPN(conn, requestID, payload, token, m); err != nil { return } } } // Linux TUN setup. type ifreq struct { Name [16]byte Flags uint16 _ [22]byte } const tunSetIFF = 0x400454ca const iffTun = 0x0001 const iffNoPI = 0x1000 func openTun(name string) (*os.File, error) { fd, err := syscall.Open("/dev/net/tun", syscall.O_RDWR|syscall.O_CLOEXEC, 0) if err != nil { return nil, err } var req ifreq copy(req.Name[:], []byte(name)) req.Flags = iffTun | iffNoPI _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(tunSetIFF), uintptr(unsafe.Pointer(&req))) if errno != 0 { syscall.Close(fd) return nil, errno } return os.NewFile(uintptr(fd), name), nil } func run(cmd string, args ...string) error { c := exec.Command(cmd, args...) out, err := c.CombinedOutput() if err != nil { return fmt.Errorf("%s %s: %v: %s", cmd, strings.Join(args, " "), err, strings.TrimSpace(string(out))) } return nil } func runOptional(debug *debugStats, cmd string, args ...string) { if err := run(cmd, args...); err != nil { debug.logf("optional command failed: %v", err) } } func ensureRule(debug *debugStats, binary string, argsCheck, argsAdd []string) { if err := exec.Command(binary, argsCheck...).Run(); err == nil { return } if err := run(binary, argsAdd...); err != nil { debug.logf("NAT rule warning: %v", err) } } func setupLinuxVPN(tunName string, mtu int, autoNAT bool, debug *debugStats) (*os.File, error) { tun, err := openTun(tunName) if err != nil { return nil, fmt.Errorf("open /dev/net/tun: %w", err) } fail := func(e error) (*os.File, error) { tun.Close(); return nil, e } if err := run("ip", "link", "set", "dev", tunName, "mtu", strconv.Itoa(mtu)); err != nil { return fail(err) } if err := run("ip", "addr", "replace", "10.123.0.1/16", "dev", tunName); err != nil { return fail(err) } // IPv6 may be disabled on some hosts; report clearly instead of silently bypassing it. if err := run("ip", "-6", "addr", "replace", "fd7a:4472:6167:6f6e::1/64", "dev", tunName); err != nil { return fail(err) } if err := run("ip", "link", "set", "dev", tunName, "up"); err != nil { return fail(err) } if err := os.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte("1\n"), 0644); err != nil { return fail(fmt.Errorf("enable IPv4 forwarding: %w", err)) } if err := os.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte("1\n"), 0644); err != nil { return fail(fmt.Errorf("enable IPv6 forwarding: %w", err)) } if autoNAT { if _, err := exec.LookPath("iptables"); err != nil { return fail(errors.New("iptables not found; install iptables or start with --auto-nat=false and configure NAT yourself")) } ensureRule(debug, "iptables", []string{"-t", "nat", "-C", "POSTROUTING", "-s", "10.123.0.0/16", "-j", "MASQUERADE"}, []string{"-t", "nat", "-A", "POSTROUTING", "-s", "10.123.0.0/16", "-j", "MASQUERADE"}) ensureRule(debug, "iptables", []string{"-C", "FORWARD", "-i", tunName, "-j", "ACCEPT"}, []string{"-A", "FORWARD", "-i", tunName, "-j", "ACCEPT"}) ensureRule(debug, "iptables", []string{"-C", "FORWARD", "-o", tunName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}, []string{"-A", "FORWARD", "-o", tunName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}) if _, err := exec.LookPath("ip6tables"); err == nil { ensureRule(debug, "ip6tables", []string{"-t", "nat", "-C", "POSTROUTING", "-s", "fd7a:4472:6167:6f6e::/64", "-j", "MASQUERADE"}, []string{"-t", "nat", "-A", "POSTROUTING", "-s", "fd7a:4472:6167:6f6e::/64", "-j", "MASQUERADE"}) ensureRule(debug, "ip6tables", []string{"-C", "FORWARD", "-i", tunName, "-j", "ACCEPT"}, []string{"-A", "FORWARD", "-i", tunName, "-j", "ACCEPT"}) ensureRule(debug, "ip6tables", []string{"-C", "FORWARD", "-o", tunName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}, []string{"-A", "FORWARD", "-o", tunName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}) } else { debug.logf("WARNING: ip6tables not found; IPv6 Internet access needs manual routing/NAT") } } return tun, nil } func main() { host := flag.String("host", "0.0.0.0", "listen host") port := flag.Int("port", 53, "listen TCP port") token := flag.String("token", "change-this-token", "shared token") maxConnections := flag.Int("max-connections", 20000, "maximum simultaneous TCP/53 connections") maxChunk := flag.Int("chunk-max", 65535, "maximum VPN fragment payload bytes (32-65535)") maxPackets := flag.Int("vpn-buffered-packets", 2048, "maximum queued return IP packets per client") pollWait := flag.Duration("poll-wait", 100*time.Millisecond, "long-poll wait for a return packet") sessionTimeout := flag.Duration("session-timeout", 5*time.Minute, "idle VPN session timeout") tunName := flag.String("tun", "dragontcp0", "Linux TUN interface name") mtu := flag.Int("mtu", 1280, "server TUN MTU") autoNAT := flag.Bool("auto-nat", true, "configure IPv4/IPv6 forwarding and iptables MASQUERADE") allowPrivate := flag.Bool("allow-private", false, "allow VPN clients to access private/link-local destinations") mockEcho := flag.Bool("mock-echo", false, "test mode: echo client IP packets back instead of using Linux TUN/NAT") debugOn := flag.Bool("debug", false, "debug sessions and statistics") debugPackets := flag.Bool("debug-packets", false, "very verbose per-IP-packet logging") statsEvery := flag.Duration("debug-stats-interval", 10*time.Second, "debug statistics interval; 0 disables") flag.Parse() if *maxChunk < 32 || *maxChunk > protocol.VPNMaxFragment { fmt.Fprintf(os.Stderr, "--chunk-max must be 32-%d\n", protocol.VPNMaxFragment) os.Exit(2) } if *mtu < 576 || *mtu > 9000 { fmt.Fprintln(os.Stderr, "--mtu must be 576-9000") os.Exit(2) } debug := &debugStats{enabled: *debugOn, packets: *debugPackets, started: time.Now()} var tun *os.File var err error if !*mockEcho { tun, err = setupLinuxVPN(*tunName, *mtu, *autoNAT, debug) if err != nil { fmt.Fprintln(os.Stderr, "VPN setup failed:", err) os.Exit(1) } defer tun.Close() } manager := newVPNManager(tun, *mockEcho, *maxChunk, *maxPackets, *pollWait, *sessionTimeout, *allowPrivate, debug) addr := net.JoinHostPort(*host, strconv.Itoa(*port)) ln, err := net.Listen("tcp", addr) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer ln.Close() fmt.Printf("DragonTCP VPN server listening on %s\n", addr) if *mockEcho { fmt.Println("mode=mock-echo (no Internet forwarding)") } else { fmt.Printf("tun=%s mtu=%d IPv4=10.123.0.1/16 IPv6=fd7a:4472:6167:6f6e::1/64 auto_nat=%t\n", *tunName, *mtu, *autoNAT) } fmt.Printf("chunk_max=%d poll_wait=%s buffered_packets=%d\n", *maxChunk, pollWait.String(), *maxPackets) if debug.enabled && *statsEvery > 0 { go func() { t := time.NewTicker(*statsEvery) defer t.Stop() for range t.C { fmt.Printf("[DEBUG] STATS uptime=%s conns=%d sessions=%d up_packets=%d down_packets=%d up_bytes=%d down_bytes=%d dropped=%d errors=%d\n", time.Since(debug.started).Round(time.Second), debug.activeConns.Load(), debug.activeSessions.Load(), debug.upPackets.Load(), debug.downPackets.Load(), debug.upBytes.Load(), debug.downBytes.Load(), debug.dropped.Load(), debug.errors.Load()) } }() } sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) go func() { <-sig; fmt.Println("Stopping DragonTCP VPN server..."); ln.Close() }() slots := make(chan struct{}, *maxConnections) for { conn, err := ln.Accept() if err != nil { break } select { case slots <- struct{}{}: debug.activeConns.Add(1) go handleConn(conn, *token, manager, slots, debug) default: _ = conn.Close() } } }