mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-21 00:29:03 +01:00
* **New Features** * Multiplexed TLS port: HTTP API and a custom stream protocol can share one port via ALPN. * Agent-side TCP and DTLS/UDP stream tunneling with health-check support and runtime capability detection. * Agents now advertise per-agent stream support (TCP/UDP). * **Documentation** * Added comprehensive stream protocol documentation. * **Tests** * Extended integration and concurrency tests covering multiplexing, TCP/UDP streams, and health checks. * **Chores** * Compose/template updated to expose both TCP and UDP ports.
123 lines
2.9 KiB
Go
123 lines
2.9 KiB
Go
package stream
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/yusing/godoxy/agent/pkg/agent/common"
|
|
)
|
|
|
|
type TCPClient struct {
|
|
conn net.Conn
|
|
}
|
|
|
|
// NewTCPClient creates a new TCP client for the agent.
|
|
//
|
|
// It will establish a TLS connection and send a stream request header to the server.
|
|
//
|
|
// It returns an error if
|
|
// - the target address is invalid
|
|
// - the stream request header is invalid
|
|
// - the TLS configuration is invalid
|
|
// - the TLS connection fails
|
|
// - the stream request header is not sent
|
|
func NewTCPClient(serverAddr, targetAddress string, caCert *x509.Certificate, clientCert *tls.Certificate) (net.Conn, error) {
|
|
host, port, err := net.SplitHostPort(targetAddress)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
header, err := NewStreamRequestHeader(host, port)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return newTCPClientWIthHeader(serverAddr, header, caCert, clientCert)
|
|
}
|
|
|
|
func TCPHealthCheck(serverAddr string, caCert *x509.Certificate, clientCert *tls.Certificate) error {
|
|
header := NewStreamHealthCheckHeader()
|
|
|
|
conn, err := newTCPClientWIthHeader(serverAddr, header, caCert, clientCert)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
conn.Close()
|
|
return nil
|
|
}
|
|
|
|
func newTCPClientWIthHeader(serverAddr string, header *StreamRequestHeader, caCert *x509.Certificate, clientCert *tls.Certificate) (net.Conn, error) {
|
|
// Setup TLS configuration
|
|
caCertPool := x509.NewCertPool()
|
|
caCertPool.AddCert(caCert)
|
|
|
|
tlsConfig := &tls.Config{
|
|
Certificates: []tls.Certificate{*clientCert},
|
|
RootCAs: caCertPool,
|
|
MinVersion: tls.VersionTLS12,
|
|
NextProtos: []string{StreamALPN},
|
|
ServerName: common.CertsDNSName,
|
|
}
|
|
|
|
// Establish TLS connection
|
|
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: dialTimeout}, "tcp", serverAddr, tlsConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Send the stream header once as a handshake.
|
|
if _, err := conn.Write(header.Bytes()); err != nil {
|
|
_ = conn.Close()
|
|
return nil, err
|
|
}
|
|
|
|
return &TCPClient{
|
|
conn: conn,
|
|
}, nil
|
|
}
|
|
|
|
func (c *TCPClient) Read(p []byte) (n int, err error) {
|
|
return c.conn.Read(p)
|
|
}
|
|
|
|
func (c *TCPClient) Write(p []byte) (n int, err error) {
|
|
return c.conn.Write(p)
|
|
}
|
|
|
|
func (c *TCPClient) LocalAddr() net.Addr {
|
|
return c.conn.LocalAddr()
|
|
}
|
|
|
|
func (c *TCPClient) RemoteAddr() net.Addr {
|
|
return c.conn.RemoteAddr()
|
|
}
|
|
|
|
func (c *TCPClient) SetDeadline(t time.Time) error {
|
|
return c.conn.SetDeadline(t)
|
|
}
|
|
|
|
func (c *TCPClient) SetReadDeadline(t time.Time) error {
|
|
return c.conn.SetReadDeadline(t)
|
|
}
|
|
|
|
func (c *TCPClient) SetWriteDeadline(t time.Time) error {
|
|
return c.conn.SetWriteDeadline(t)
|
|
}
|
|
|
|
func (c *TCPClient) Close() error {
|
|
return c.conn.Close()
|
|
}
|
|
|
|
// ConnectionState exposes the underlying TLS connection state when the client is
|
|
// backed by *tls.Conn.
|
|
//
|
|
// This is primarily used by tests and diagnostics.
|
|
func (c *TCPClient) ConnectionState() tls.ConnectionState {
|
|
if tc, ok := c.conn.(*tls.Conn); ok {
|
|
return tc.ConnectionState()
|
|
}
|
|
return tls.ConnectionState{}
|
|
}
|