mirror of
https://github.com/go-i2p/go-i2ptunnel.git
synced 2025-07-12 23:48:37 -04:00
135 lines
3.2 KiB
Go
135 lines
3.2 KiB
Go
package tcpclient
|
|
|
|
/**
|
|
TCP Client Tunnel
|
|
-----------------
|
|
|
|
A TCP Client tunnel operates by:
|
|
1. Running a TCP Server that listens on a local port
|
|
2. Maintaining an I2P Client connected to a specific destination
|
|
|
|
When activated:
|
|
- Local applications connect to the TCP Server
|
|
- Traffic routes through the I2P Client to the target I2P destination
|
|
- Creates a secure point-to-point connection
|
|
|
|
Both tunnel types preserve the original TCP traffic while adding I2P's anonymity and encryption layers.
|
|
|
|
When a local client connects to the I2P tunnel's destination, the traffic flows:
|
|
- Outgoing: Local Client → TCP Server → I2P Client → I2P Network
|
|
- Incoming: I2P Network → I2P Client → TCP Server → Local Client
|
|
**/
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"strconv"
|
|
|
|
"github.com/go-i2p/go-forward/config"
|
|
"github.com/go-i2p/go-forward/stream"
|
|
i2pconv "github.com/go-i2p/go-i2ptunnel-config/lib"
|
|
i2ptunnel "github.com/go-i2p/go-i2ptunnel/lib/core"
|
|
"github.com/go-i2p/i2pkeys"
|
|
"github.com/go-i2p/onramp"
|
|
)
|
|
|
|
var implementTCPClient i2ptunnel.I2PTunnel = &TCPClient{}
|
|
|
|
type TCPClient struct {
|
|
// I2P Connection to listen to the I2P network
|
|
*onramp.Garlic
|
|
// The I2P Tunnel config itself
|
|
i2pconv.TunnelConfig
|
|
// The remote I2P destination target
|
|
*i2pkeys.I2PAddr
|
|
// The tunnel status
|
|
i2ptunnel.I2PTunnelStatus
|
|
// Channel for shutdown signaling
|
|
done chan struct{}
|
|
|
|
// Error history of the tunnel
|
|
Errors []i2ptunnel.I2PTunnelError
|
|
}
|
|
|
|
func (t *TCPClient) recordError(err error) {
|
|
t.Errors = append(t.Errors, i2ptunnel.NewError(t, err))
|
|
}
|
|
|
|
// Get the tunnel's I2P address
|
|
func (t *TCPClient) Address() string {
|
|
return t.Garlic.StreamSession.Addr().Base32()
|
|
}
|
|
|
|
// Get the tunnel's error message
|
|
func (t *TCPClient) Error() error {
|
|
if len(t.Errors) > 0 {
|
|
return t.Errors[len(t.Errors)-1]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Get the tunnel's local host:port
|
|
func (t *TCPClient) LocalAddress() (string, error) {
|
|
addr := net.JoinHostPort(t.TunnelConfig.Interface, strconv.Itoa(t.TunnelConfig.Port))
|
|
return addr, nil
|
|
}
|
|
|
|
// Get the tunnel's name
|
|
func (t *TCPClient) Name() string {
|
|
return t.TunnelConfig.Name
|
|
}
|
|
|
|
// Start the tunnel
|
|
func (t *TCPClient) Start() error {
|
|
i2pConn, err := t.Garlic.Dial("tcp", t.Target())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
t.I2PTunnelStatus = i2ptunnel.I2PTunnelStatusStarting
|
|
defer i2pConn.Close()
|
|
defer t.Stop()
|
|
listener, err := net.Listen("tcp", net.JoinHostPort(t.Interface, strconv.Itoa(t.Port)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer listener.Close()
|
|
t.I2PTunnelStatus = i2ptunnel.I2PTunnelStatusRunning
|
|
for {
|
|
select {
|
|
case <-t.done:
|
|
return nil
|
|
default:
|
|
con, err := listener.Accept()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
defer con.Close()
|
|
ctx := context.Background()
|
|
stream.Forward(ctx, con, i2pConn, config.DefaultConfig())
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the tunnel's status
|
|
func (t *TCPClient) Status() i2ptunnel.I2PTunnelStatus {
|
|
return t.I2PTunnelStatus
|
|
}
|
|
|
|
// Stop the tunnel
|
|
func (t *TCPClient) Stop() error {
|
|
close(t.done)
|
|
// Cleanup resources
|
|
t.I2PTunnelStatus = i2ptunnel.I2PTunnelStatusStopped
|
|
return nil
|
|
}
|
|
|
|
// Get the tunnel's I2P target. Nil in the case of one-to-many clients like SOCKS5 and HTTP
|
|
func (t *TCPClient) Target() string {
|
|
return t.I2PAddr.Base32()
|
|
}
|
|
|
|
// Get the tunnel's type
|
|
func (t *TCPClient) Type() string {
|
|
return t.TunnelConfig.Type
|
|
}
|