init v0.1.0
This commit is contained in:
408
pkg/interfaces/tcp.go
Normal file
408
pkg/interfaces/tcp.go
Normal file
@@ -0,0 +1,408 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
HDLC_FLAG = 0x7E
|
||||
HDLC_ESC = 0x7D
|
||||
HDLC_ESC_MASK = 0x20
|
||||
|
||||
KISS_FEND = 0xC0
|
||||
KISS_FESC = 0xDB
|
||||
KISS_TFEND = 0xDC
|
||||
KISS_TFESC = 0xDD
|
||||
|
||||
TCP_USER_TIMEOUT = 24
|
||||
TCP_PROBE_AFTER = 5
|
||||
TCP_PROBE_INTERVAL = 2
|
||||
TCP_PROBES = 12
|
||||
RECONNECT_WAIT = 5
|
||||
INITIAL_TIMEOUT = 5
|
||||
)
|
||||
|
||||
type TCPClientInterface struct {
|
||||
Interface
|
||||
conn net.Conn
|
||||
targetAddr string
|
||||
targetPort int
|
||||
kissFraming bool
|
||||
i2pTunneled bool
|
||||
initiator bool
|
||||
reconnecting bool
|
||||
neverConnected bool
|
||||
writing bool
|
||||
maxReconnectTries int
|
||||
packetBuffer []byte
|
||||
packetType byte
|
||||
}
|
||||
|
||||
func NewTCPClient(name string, targetAddr string, targetPort int, kissFraming bool, i2pTunneled bool) (*TCPClientInterface, error) {
|
||||
tc := &TCPClientInterface{
|
||||
Interface: Interface{
|
||||
Name: name,
|
||||
Mode: MODE_FULL,
|
||||
MTU: 1064,
|
||||
Bitrate: 10000000, // 10Mbps estimate
|
||||
},
|
||||
targetAddr: targetAddr,
|
||||
targetPort: targetPort,
|
||||
kissFraming: kissFraming,
|
||||
i2pTunneled: i2pTunneled,
|
||||
initiator: true,
|
||||
}
|
||||
|
||||
if err := tc.connect(true); err != nil {
|
||||
go tc.reconnect()
|
||||
} else {
|
||||
go tc.readLoop()
|
||||
}
|
||||
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
func (tc *TCPClientInterface) connect(initial bool) error {
|
||||
addr := fmt.Sprintf("%s:%d", tc.targetAddr, tc.targetPort)
|
||||
conn, err := net.DialTimeout("tcp", addr, time.Second*INITIAL_TIMEOUT)
|
||||
if err != nil {
|
||||
if initial {
|
||||
return fmt.Errorf("initial connection failed: %v", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
tc.conn = conn
|
||||
tc.Online = true
|
||||
tc.writing = false
|
||||
tc.neverConnected = false
|
||||
|
||||
// Set TCP options
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.SetNoDelay(true)
|
||||
tcpConn.SetKeepAlive(true)
|
||||
tcpConn.SetKeepAlivePeriod(time.Second * TCP_PROBE_INTERVAL)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tc *TCPClientInterface) reconnect() {
|
||||
if tc.initiator && !tc.reconnecting {
|
||||
tc.reconnecting = true
|
||||
attempts := 0
|
||||
|
||||
for !tc.Online {
|
||||
time.Sleep(time.Second * RECONNECT_WAIT)
|
||||
attempts++
|
||||
|
||||
if tc.maxReconnectTries > 0 && attempts > tc.maxReconnectTries {
|
||||
tc.teardown()
|
||||
break
|
||||
}
|
||||
|
||||
if err := tc.connect(false); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
go tc.readLoop()
|
||||
break
|
||||
}
|
||||
|
||||
tc.reconnecting = false
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *TCPClientInterface) readLoop() {
|
||||
buffer := make([]byte, tc.MTU)
|
||||
inFrame := false
|
||||
escape := false
|
||||
dataBuffer := make([]byte, 0)
|
||||
|
||||
for {
|
||||
n, err := tc.conn.Read(buffer)
|
||||
if err != nil {
|
||||
tc.Online = false
|
||||
if tc.initiator && !tc.Detached {
|
||||
go tc.reconnect()
|
||||
} else {
|
||||
tc.teardown()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
b := buffer[i]
|
||||
|
||||
if tc.kissFraming {
|
||||
// KISS framing logic
|
||||
if inFrame && b == KISS_FEND {
|
||||
inFrame = false
|
||||
tc.handlePacket(dataBuffer)
|
||||
dataBuffer = dataBuffer[:0]
|
||||
} else if b == KISS_FEND {
|
||||
inFrame = true
|
||||
} else if inFrame {
|
||||
if b == KISS_FESC {
|
||||
escape = true
|
||||
} else {
|
||||
if escape {
|
||||
if b == KISS_TFEND {
|
||||
b = KISS_FEND
|
||||
}
|
||||
if b == KISS_TFESC {
|
||||
b = KISS_FESC
|
||||
}
|
||||
escape = false
|
||||
}
|
||||
dataBuffer = append(dataBuffer, b)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// HDLC framing logic
|
||||
if inFrame && b == HDLC_FLAG {
|
||||
inFrame = false
|
||||
tc.handlePacket(dataBuffer)
|
||||
dataBuffer = dataBuffer[:0]
|
||||
} else if b == HDLC_FLAG {
|
||||
inFrame = true
|
||||
} else if inFrame {
|
||||
if b == HDLC_ESC {
|
||||
escape = true
|
||||
} else {
|
||||
if escape {
|
||||
b ^= HDLC_ESC_MASK
|
||||
escape = false
|
||||
}
|
||||
dataBuffer = append(dataBuffer, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *TCPClientInterface) handlePacket(data []byte) {
|
||||
if len(data) < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
packetType := data[0]
|
||||
payload := data[1:]
|
||||
|
||||
switch packetType {
|
||||
case 0x01: // Path request
|
||||
tc.Interface.ProcessIncoming(payload)
|
||||
case 0x02: // Link packet
|
||||
if len(payload) < 40 { // minimum size for link packet
|
||||
return
|
||||
}
|
||||
tc.Interface.ProcessIncoming(payload)
|
||||
default:
|
||||
// Unknown packet type
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *TCPClientInterface) ProcessOutgoing(data []byte) error {
|
||||
if !tc.Online {
|
||||
return fmt.Errorf("interface offline")
|
||||
}
|
||||
|
||||
tc.writing = true
|
||||
defer func() { tc.writing = false }()
|
||||
|
||||
var frame []byte
|
||||
if tc.kissFraming {
|
||||
frame = append([]byte{KISS_FEND}, escapeKISS(data)...)
|
||||
frame = append(frame, KISS_FEND)
|
||||
} else {
|
||||
frame = append([]byte{HDLC_FLAG}, escapeHDLC(data)...)
|
||||
frame = append(frame, HDLC_FLAG)
|
||||
}
|
||||
|
||||
if _, err := tc.conn.Write(frame); err != nil {
|
||||
tc.teardown()
|
||||
return fmt.Errorf("write failed: %v", err)
|
||||
}
|
||||
|
||||
tc.Interface.ProcessOutgoing(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tc *TCPClientInterface) teardown() {
|
||||
tc.Online = false
|
||||
tc.IN = false
|
||||
tc.OUT = false
|
||||
if tc.conn != nil {
|
||||
tc.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions for escaping data
|
||||
func escapeHDLC(data []byte) []byte {
|
||||
escaped := make([]byte, 0, len(data)*2)
|
||||
for _, b := range data {
|
||||
if b == HDLC_FLAG || b == HDLC_ESC {
|
||||
escaped = append(escaped, HDLC_ESC, b^HDLC_ESC_MASK)
|
||||
} else {
|
||||
escaped = append(escaped, b)
|
||||
}
|
||||
}
|
||||
return escaped
|
||||
}
|
||||
|
||||
func escapeKISS(data []byte) []byte {
|
||||
escaped := make([]byte, 0, len(data)*2)
|
||||
for _, b := range data {
|
||||
if b == KISS_FEND {
|
||||
escaped = append(escaped, KISS_FESC, KISS_TFEND)
|
||||
} else if b == KISS_FESC {
|
||||
escaped = append(escaped, KISS_FESC, KISS_TFESC)
|
||||
} else {
|
||||
escaped = append(escaped, b)
|
||||
}
|
||||
}
|
||||
return escaped
|
||||
}
|
||||
|
||||
type TCPServerInterface struct {
|
||||
Interface
|
||||
server net.Listener
|
||||
bindAddr string
|
||||
bindPort int
|
||||
i2pTunneled bool
|
||||
preferIPv6 bool
|
||||
spawned []*TCPClientInterface
|
||||
spawnedMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func NewTCPServer(name string, bindAddr string, bindPort int, i2pTunneled bool, preferIPv6 bool) (*TCPServerInterface, error) {
|
||||
ts := &TCPServerInterface{
|
||||
Interface: Interface{
|
||||
Name: name,
|
||||
Mode: MODE_FULL,
|
||||
MTU: 1064,
|
||||
Bitrate: 10000000, // 10Mbps estimate
|
||||
},
|
||||
bindAddr: bindAddr,
|
||||
bindPort: bindPort,
|
||||
i2pTunneled: i2pTunneled,
|
||||
preferIPv6: preferIPv6,
|
||||
spawned: make([]*TCPClientInterface, 0),
|
||||
}
|
||||
|
||||
// Resolve bind address
|
||||
var addr string
|
||||
if ts.bindAddr == "" {
|
||||
if ts.preferIPv6 {
|
||||
addr = fmt.Sprintf("[::0]:%d", ts.bindPort)
|
||||
} else {
|
||||
addr = fmt.Sprintf("0.0.0.0:%d", ts.bindPort)
|
||||
}
|
||||
} else {
|
||||
addr = fmt.Sprintf("%s:%d", ts.bindAddr, ts.bindPort)
|
||||
}
|
||||
|
||||
// Create listener
|
||||
var err error
|
||||
ts.server, err = net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create TCP listener: %v", err)
|
||||
}
|
||||
|
||||
ts.Online = true
|
||||
ts.IN = true
|
||||
|
||||
// Start accept loop
|
||||
go ts.acceptLoop()
|
||||
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) acceptLoop() {
|
||||
for {
|
||||
conn, err := ts.server.Accept()
|
||||
if err != nil {
|
||||
if !ts.Detached {
|
||||
// Log error and continue accepting
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create new client interface for this connection
|
||||
client := &TCPClientInterface{
|
||||
Interface: Interface{
|
||||
Name: fmt.Sprintf("Client-%s-%s", ts.Name, conn.RemoteAddr()),
|
||||
Mode: ts.Mode,
|
||||
MTU: ts.MTU,
|
||||
},
|
||||
conn: conn,
|
||||
i2pTunneled: ts.i2pTunneled,
|
||||
}
|
||||
|
||||
// Configure TCP options
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.SetNoDelay(true)
|
||||
tcpConn.SetKeepAlive(true)
|
||||
tcpConn.SetKeepAlivePeriod(time.Duration(TCP_PROBE_INTERVAL) * time.Second)
|
||||
}
|
||||
|
||||
client.Online = true
|
||||
client.IN = ts.IN
|
||||
client.OUT = ts.OUT
|
||||
|
||||
// Add to spawned interfaces
|
||||
ts.spawnedMutex.Lock()
|
||||
ts.spawned = append(ts.spawned, client)
|
||||
ts.spawnedMutex.Unlock()
|
||||
|
||||
// Start client read loop
|
||||
go client.readLoop()
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) Detach() {
|
||||
ts.Interface.Detach()
|
||||
|
||||
if ts.server != nil {
|
||||
ts.server.Close()
|
||||
}
|
||||
|
||||
ts.spawnedMutex.Lock()
|
||||
for _, client := range ts.spawned {
|
||||
client.Detach()
|
||||
}
|
||||
ts.spawned = nil
|
||||
ts.spawnedMutex.Unlock()
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) ProcessOutgoing(data []byte) error {
|
||||
ts.spawnedMutex.RLock()
|
||||
defer ts.spawnedMutex.RUnlock()
|
||||
|
||||
var lastErr error
|
||||
for _, client := range ts.spawned {
|
||||
if err := client.ProcessOutgoing(data); err != nil {
|
||||
lastErr = err
|
||||
}
|
||||
}
|
||||
|
||||
return lastErr
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) String() string {
|
||||
addr := ts.bindAddr
|
||||
if addr == "" {
|
||||
if ts.preferIPv6 {
|
||||
addr = "[::0]"
|
||||
} else {
|
||||
addr = "0.0.0.0"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("TCPServerInterface[%s/%s:%d]", ts.Name, addr, ts.bindPort)
|
||||
}
|
||||
Reference in New Issue
Block a user