New
This commit is contained in:
@@ -15,11 +15,32 @@ import (
|
||||
"time"
|
||||
|
||||
"dragontcp/internal/protocol"
|
||||
"dragontcp/internal/wire"
|
||||
)
|
||||
|
||||
var active int64
|
||||
|
||||
// idleDeadline avoids a SetDeadline system call for every small protocol
|
||||
// record. It refreshes halfway through the idle window, preserving idle-client
|
||||
// cleanup while making persistent high-throughput lanes substantially cheaper.
|
||||
type idleDeadline struct {
|
||||
conn net.Conn
|
||||
timeout time.Duration
|
||||
next time.Time
|
||||
}
|
||||
|
||||
func newIdleDeadline(conn net.Conn, timeout time.Duration) *idleDeadline {
|
||||
return &idleDeadline{conn: conn, timeout: timeout}
|
||||
}
|
||||
|
||||
func (d *idleDeadline) refresh() error {
|
||||
now := time.Now()
|
||||
if !d.next.IsZero() && now.Before(d.next.Add(-d.timeout/2)) {
|
||||
return nil
|
||||
}
|
||||
d.next = now.Add(d.timeout)
|
||||
return d.conn.SetDeadline(d.next)
|
||||
}
|
||||
|
||||
type dnsEntry struct {
|
||||
ips []netip.Addr
|
||||
expires time.Time
|
||||
@@ -160,10 +181,11 @@ func handle(
|
||||
tcpBuffer int,
|
||||
slots chan struct{},
|
||||
manager *streamManager,
|
||||
bhttpManager *bhttpSessionManager,
|
||||
xorManager *chunkManager,
|
||||
chunkMax int,
|
||||
bufferBytes int,
|
||||
chunkBuffered int,
|
||||
xorBufferBytes int,
|
||||
chunkPollWait time.Duration,
|
||||
debug *serverDebug,
|
||||
) {
|
||||
@@ -176,46 +198,71 @@ func handle(
|
||||
protocol.TuneTCP(conn)
|
||||
protocol.TuneTCPBuffer(conn, tcpBuffer)
|
||||
|
||||
// One listener serves both wires. The legacy XOR framing starts every
|
||||
// request with the ASCII magic "UP"; the binary framing starts with a mode
|
||||
// byte of 0-4, so the two are never ambiguous.
|
||||
// One listener serves both wires and every startup-selected header profile.
|
||||
// sniffWire partitions the full first-byte space so B and X remain
|
||||
// unambiguous even when their legacy mode/UP bytes are masked.
|
||||
_ = conn.SetDeadline(time.Now().Add(30 * time.Second))
|
||||
conn, isXOR, err := sniffWire(conn)
|
||||
conn, isXOR, headerMask, err := sniffWire(conn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if isXOR {
|
||||
if debug != nil && debug.enabled {
|
||||
debug.logf("WIRE peer=%v mode=xor", conn.RemoteAddr())
|
||||
debug.logf("WIRE peer=%v mode=xor header_mask=%02x", conn.RemoteAddr(), headerMask)
|
||||
}
|
||||
handleXOR(conn, token, allowPrivate, cache, tcpBuffer, xorManager,
|
||||
chunkMax, chunkBuffered, chunkPollWait, debug)
|
||||
handleXOR(conn, headerMask, token, allowPrivate, cache, tcpBuffer, xorManager,
|
||||
chunkMax, xorBufferBytes, chunkPollWait, debug)
|
||||
return
|
||||
}
|
||||
clearPayload := false
|
||||
if profiled, ok := conn.(interface{ ClearPayload() bool }); ok {
|
||||
clearPayload = profiled.ClearPayload()
|
||||
}
|
||||
if debug != nil && debug.enabled {
|
||||
debug.logf("WIRE peer=%v mode=binary", conn.RemoteAddr())
|
||||
debug.logf("WIRE peer=%v mode=binary header_mask=%02x clear_payload=%t", conn.RemoteAddr(), headerMask, clearPayload)
|
||||
}
|
||||
|
||||
handleBinary(conn, headerMask, clearPayload, token, allowPrivate, cache, tcpBuffer, manager,
|
||||
bhttpManager, chunkMax, bufferBytes, chunkPollWait, debug)
|
||||
}
|
||||
|
||||
func acceptLoop(
|
||||
ln net.Listener,
|
||||
token string,
|
||||
allowPrivate bool,
|
||||
cache *dnsCache,
|
||||
tcpBuffer int,
|
||||
slots chan struct{},
|
||||
manager *streamManager,
|
||||
bhttpManager *bhttpSessionManager,
|
||||
xorManager *chunkManager,
|
||||
chunkMax int,
|
||||
bufferBytes int,
|
||||
xorBufferBytes int,
|
||||
chunkPollWait time.Duration,
|
||||
debug *serverDebug,
|
||||
) {
|
||||
for {
|
||||
_ = conn.SetDeadline(time.Now().Add(30 * time.Second))
|
||||
req, err := wire.ReadRequest(conn)
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
fmt.Fprintln(os.Stderr, "accept:", err)
|
||||
continue
|
||||
}
|
||||
if err := processWireRequest(
|
||||
conn,
|
||||
req,
|
||||
token,
|
||||
allowPrivate,
|
||||
cache,
|
||||
tcpBuffer,
|
||||
manager,
|
||||
chunkMax,
|
||||
bufferBytes,
|
||||
chunkPollWait,
|
||||
debug,
|
||||
); err != nil {
|
||||
return
|
||||
|
||||
select {
|
||||
case slots <- struct{}{}:
|
||||
atomic.AddInt64(&active, 1)
|
||||
if debug.enabled {
|
||||
debug.logf("ACCEPT local=%v peer=%v active_connections=%d", conn.LocalAddr(), conn.RemoteAddr(), atomic.LoadInt64(&active))
|
||||
}
|
||||
go handle(conn, token, allowPrivate, cache, tcpBuffer, slots, manager,
|
||||
bhttpManager, xorManager, chunkMax, bufferBytes, xorBufferBytes,
|
||||
chunkPollWait, debug)
|
||||
default:
|
||||
if debug.enabled {
|
||||
debug.errorf("REJECT peer=%v reason=max-connections", conn.RemoteAddr())
|
||||
}
|
||||
_ = conn.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,6 +271,7 @@ func main() {
|
||||
var (
|
||||
host = flag.String("host", "0.0.0.0", "listen host")
|
||||
port = flag.Int("port", 53, "listen port")
|
||||
portAlt = flag.Int("port-alt", 80, "second simultaneous listen port; 0 disables")
|
||||
token = flag.String("token", "", "optional shared token")
|
||||
maxConnections = flag.Int("max-connections", 20000, "max simultaneous tunnels")
|
||||
allowPrivate = flag.Bool("allow-private", false, "allow private/loopback targets")
|
||||
@@ -231,7 +279,7 @@ func main() {
|
||||
dnsCacheSize = flag.Int("dns-cache-size", 4096, "maximum cached DNS hostnames")
|
||||
tcpBuffer = flag.Int("tcp-buffer", 0, "optional TCP read/write buffer bytes; 0 keeps OS autotuning")
|
||||
chunkMax = flag.Int("chunk-max", 1048576, "maximum adaptive chunk payload bytes (32 bytes to 1 MiB)")
|
||||
chunkBuffered = flag.Int("chunk-buffered", 256, "compatibility buffer units; 256 = about 16 MiB per active session")
|
||||
chunkBuffered = flag.Int("chunk-buffered", 32, "per-session download buffer in 64 KiB units; 32 = about 2 MiB")
|
||||
chunkPollWait = flag.Duration("chunk-poll-wait", 200*time.Millisecond, "server long-poll wait for chunk data")
|
||||
sessionTimeout = flag.Duration("chunk-session-timeout", 2*time.Minute, "idle chunk session timeout")
|
||||
debugEnabled = flag.Bool("debug", false, "log session/connect/errors and periodic statistics")
|
||||
@@ -256,8 +304,23 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
listeners := []net.Listener{ln}
|
||||
fmt.Printf("DragonTCP Go server listening on %s\n", listenAddr)
|
||||
if *portAlt < 0 || *portAlt > 65535 {
|
||||
fmt.Fprintln(os.Stderr, "--port-alt must be between 0 and 65535")
|
||||
os.Exit(2)
|
||||
}
|
||||
if *portAlt != 0 && *portAlt != *port {
|
||||
altAddr := net.JoinHostPort(*host, strconv.Itoa(*portAlt))
|
||||
alt, altErr := net.Listen("tcp", altAddr)
|
||||
if altErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: secondary listener %s unavailable: %v\n", altAddr, altErr)
|
||||
} else {
|
||||
defer alt.Close()
|
||||
listeners = append(listeners, alt)
|
||||
fmt.Printf("DragonTCP Go server listening on %s\n", altAddr)
|
||||
}
|
||||
}
|
||||
fmt.Printf("max_connections=%d tcp_buffer=%d\n", *maxConnections, *tcpBuffer)
|
||||
|
||||
slots := make(chan struct{}, *maxConnections)
|
||||
@@ -271,45 +334,17 @@ func main() {
|
||||
bufferBytes = 64 * 1024 * 1024
|
||||
}
|
||||
manager := newStreamManager(*sessionTimeout, debug)
|
||||
bhttpManager := newBHTTPSessionManager(*sessionTimeout, *maxConnections)
|
||||
xorManager := newChunkManager(*sessionTimeout, debug)
|
||||
fmt.Printf("binary_transport=true chunk_max=%d buffer_bytes=%d poll_wait=%s\n", *chunkMax, bufferBytes, chunkPollWait.String())
|
||||
fmt.Printf("binary_transport=true bp_compat=true chunk_max=%d buffer_bytes=%d poll_wait=%s\n", *chunkMax, bufferBytes, chunkPollWait.String())
|
||||
if debug.enabled {
|
||||
fmt.Printf("debug=true debug_chunks=%t stats_interval=%s\n", debug.chunks, debug.statsEvery)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "accept:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case slots <- struct{}{}:
|
||||
atomic.AddInt64(&active, 1)
|
||||
if debug.enabled {
|
||||
debug.logf("ACCEPT peer=%v active_connections=%d", conn.RemoteAddr(), atomic.LoadInt64(&active))
|
||||
}
|
||||
go handle(
|
||||
conn,
|
||||
*token,
|
||||
*allowPrivate,
|
||||
cache,
|
||||
*tcpBuffer,
|
||||
slots,
|
||||
manager,
|
||||
xorManager,
|
||||
*chunkMax,
|
||||
bufferBytes,
|
||||
*chunkBuffered,
|
||||
*chunkPollWait,
|
||||
debug,
|
||||
)
|
||||
default:
|
||||
if debug.enabled {
|
||||
debug.errorf("REJECT peer=%v reason=max-connections", conn.RemoteAddr())
|
||||
}
|
||||
_ = conn.Close()
|
||||
}
|
||||
for _, listener := range listeners {
|
||||
go acceptLoop(listener, *token, *allowPrivate, cache, *tcpBuffer, slots,
|
||||
manager, bhttpManager, xorManager, *chunkMax, bufferBytes,
|
||||
bufferBytes, *chunkPollWait, debug)
|
||||
}
|
||||
select {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user