init v0.1.0

This commit is contained in:
Sudo-Ivan
2024-12-30 01:56:25 -06:00
commit 668d7c56b5
27 changed files with 3921 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
package interfaces
import (
"fmt"
"sync"
"time"
"encoding/binary"
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
)
const (
BITRATE_MINIMUM = 5 // Minimum required bitrate in bits/sec
)
// BaseInterface embeds common.BaseInterface and implements common.Interface
type BaseInterface struct {
common.BaseInterface
}
func (i *BaseInterface) SetPacketCallback(callback common.PacketCallback) {
i.mutex.Lock()
defer i.mutex.Unlock()
i.packetCallback = callback
}
func (i *BaseInterface) ProcessIncoming(data []byte) {
i.mutex.RLock()
callback := i.packetCallback
i.mutex.RUnlock()
if callback != nil {
callback(data, i)
}
i.RxBytes += uint64(len(data))
}
func (i *BaseInterface) ProcessOutgoing(data []byte) error {
i.TxBytes += uint64(len(data))
return nil
}
func (i *BaseInterface) Detach() {
i.mutex.Lock()
defer i.mutex.Unlock()
i.Detached = true
i.Online = false
}
func (i *BaseInterface) SendPathRequest(packet []byte) error {
if !i.Online || i.Detached {
return fmt.Errorf("interface offline or detached")
}
frame := make([]byte, 0, len(packet)+2)
frame = append(frame, 0x01)
frame = append(frame, packet...)
return i.ProcessOutgoing(frame)
}
func (i *BaseInterface) SendLinkPacket(dest []byte, data []byte, timestamp time.Time) error {
if !i.Online || i.Detached {
return fmt.Errorf("interface offline or detached")
}
frame := make([]byte, 0, len(dest)+len(data)+9)
frame = append(frame, 0x02)
frame = append(frame, dest...)
ts := make([]byte, 8)
binary.BigEndian.PutUint64(ts, uint64(timestamp.Unix()))
frame = append(frame, ts...)
frame = append(frame, data...)
return i.ProcessOutgoing(frame)
}

408
pkg/interfaces/tcp.go Normal file
View 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)
}

102
pkg/interfaces/udp.go Normal file
View File

@@ -0,0 +1,102 @@
package interfaces
import (
"fmt"
"net"
"sync"
)
type UDPInterface struct {
Interface
conn *net.UDPConn
listenAddr *net.UDPAddr
targetAddr *net.UDPAddr
readBuffer []byte
}
func NewUDPInterface(name string, listenAddr string, targetAddr string) (*UDPInterface, error) {
ui := &UDPInterface{
Interface: Interface{
Name: name,
Mode: MODE_FULL,
MTU: 1500,
Bitrate: 100000000, // 100Mbps estimate for UDP
},
readBuffer: make([]byte, 65535),
}
// Parse listen address
laddr, err := net.ResolveUDPAddr("udp", listenAddr)
if err != nil {
return nil, fmt.Errorf("invalid listen address: %v", err)
}
ui.listenAddr = laddr
// Parse target address if provided
if targetAddr != "" {
taddr, err := net.ResolveUDPAddr("udp", targetAddr)
if err != nil {
return nil, fmt.Errorf("invalid target address: %v", err)
}
ui.targetAddr = taddr
ui.OUT = true
}
// Create UDP connection
conn, err := net.ListenUDP("udp", ui.listenAddr)
if err != nil {
return nil, fmt.Errorf("failed to listen on UDP: %v", err)
}
ui.conn = conn
ui.IN = true
ui.Online = true
// Start read loop
go ui.readLoop()
return ui, nil
}
func (ui *UDPInterface) readLoop() {
for {
if !ui.Online {
return
}
n, addr, err := ui.conn.ReadFromUDP(ui.readBuffer)
if err != nil {
if !ui.Detached {
// Log error
}
continue
}
// Copy received data
data := make([]byte, n)
copy(data, ui.readBuffer[:n])
// Process packet
ui.ProcessIncoming(data)
}
}
func (ui *UDPInterface) ProcessOutgoing(data []byte) error {
if !ui.Online || ui.targetAddr == nil {
return fmt.Errorf("interface offline or no target address configured")
}
_, err := ui.conn.WriteToUDP(data, ui.targetAddr)
if err != nil {
return fmt.Errorf("UDP write failed: %v", err)
}
ui.Interface.ProcessOutgoing(data)
return nil
}
func (ui *UDPInterface) Detach() {
ui.Interface.Detach()
if ui.conn != nil {
ui.conn.Close()
}
}