0.2.4
This commit is contained in:
@@ -1,29 +1,42 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||
)
|
||||
|
||||
const (
|
||||
BITRATE_MINIMUM = 5 // Minimum required bitrate in bits/sec
|
||||
MODE_FULL = 0x01
|
||||
MODE_FULL = 0x01
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
common.NetworkInterface
|
||||
Send(data []byte, target string) error
|
||||
Detach()
|
||||
IsEnabled() bool
|
||||
GetName() string
|
||||
GetType() common.InterfaceType
|
||||
GetMode() common.InterfaceMode
|
||||
IsOnline() bool
|
||||
IsDetached() bool
|
||||
Detach()
|
||||
Send(data []byte, addr string) error
|
||||
SetPacketCallback(common.PacketCallback)
|
||||
GetPacketCallback() common.PacketCallback
|
||||
}
|
||||
|
||||
type BaseInterface struct {
|
||||
common.BaseInterface
|
||||
name string
|
||||
mode common.InterfaceMode
|
||||
ifType common.InterfaceType
|
||||
online bool
|
||||
detached bool
|
||||
mtu int
|
||||
mutex sync.RWMutex
|
||||
packetCallback common.PacketCallback
|
||||
}
|
||||
|
||||
func (i *BaseInterface) SetPacketCallback(callback common.PacketCallback) {
|
||||
@@ -36,11 +49,11 @@ 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))
|
||||
}
|
||||
|
||||
@@ -76,11 +89,11 @@ func (i *BaseInterface) SendLinkPacket(dest []byte, data []byte, timestamp time.
|
||||
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)
|
||||
@@ -124,4 +137,4 @@ func (i *BaseInterface) GetConn() net.Conn {
|
||||
|
||||
func (i *BaseInterface) IsEnabled() bool {
|
||||
return i.Online && !i.Detached
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||
)
|
||||
|
||||
@@ -18,9 +19,9 @@ const (
|
||||
KISS_TFEND = 0xDC
|
||||
KISS_TFESC = 0xDD
|
||||
|
||||
TCP_USER_TIMEOUT = 24
|
||||
TCP_PROBE_AFTER = 5
|
||||
TCP_PROBE_INTERVAL = 2
|
||||
TCP_USER_TIMEOUT = 24
|
||||
TCP_PROBE_AFTER = 5
|
||||
TCP_PROBE_INTERVAL = 2
|
||||
TCP_PROBES = 12
|
||||
RECONNECT_WAIT = 5
|
||||
INITIAL_TIMEOUT = 5
|
||||
@@ -28,30 +29,32 @@ const (
|
||||
|
||||
type TCPClientInterface struct {
|
||||
BaseInterface
|
||||
conn net.Conn
|
||||
targetAddr string
|
||||
targetPort int
|
||||
kissFraming bool
|
||||
i2pTunneled bool
|
||||
initiator bool
|
||||
reconnecting bool
|
||||
neverConnected bool
|
||||
writing bool
|
||||
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
|
||||
packetCallback common.PacketCallback
|
||||
packetBuffer []byte
|
||||
packetType byte
|
||||
packetCallback common.PacketCallback
|
||||
mutex sync.RWMutex
|
||||
detached bool
|
||||
}
|
||||
|
||||
func NewTCPClient(name string, targetAddr string, targetPort int, kissFraming bool, i2pTunneled bool) (*TCPClientInterface, error) {
|
||||
tc := &TCPClientInterface{
|
||||
BaseInterface: BaseInterface{
|
||||
BaseInterface: common.BaseInterface{
|
||||
Name: name,
|
||||
Mode: common.IF_MODE_FULL,
|
||||
MTU: 1064,
|
||||
Bitrate: 10000000, // 10Mbps estimate
|
||||
},
|
||||
name: name,
|
||||
mode: common.IF_MODE_FULL,
|
||||
ifType: common.IF_TYPE_TCP,
|
||||
online: false,
|
||||
mtu: 1064,
|
||||
detached: false,
|
||||
},
|
||||
targetAddr: targetAddr,
|
||||
targetPort: targetPort,
|
||||
@@ -285,159 +288,102 @@ func (tc *TCPClientInterface) GetName() string {
|
||||
return tc.Name
|
||||
}
|
||||
|
||||
type TCPServerInterface struct {
|
||||
BaseInterface
|
||||
server net.Listener
|
||||
bindAddr string
|
||||
bindPort int
|
||||
preferIPv6 bool
|
||||
i2pTunneled bool
|
||||
spawned []*TCPClientInterface
|
||||
spawnedMutex sync.RWMutex
|
||||
packetCallback func([]byte, interface{})
|
||||
func (tc *TCPClientInterface) GetPacketCallback() common.PacketCallback {
|
||||
tc.mutex.RLock()
|
||||
defer tc.mutex.RUnlock()
|
||||
return tc.packetCallback
|
||||
}
|
||||
|
||||
func NewTCPServer(name string, bindAddr string, bindPort int, preferIPv6 bool, i2pTunneled bool) (*TCPServerInterface, error) {
|
||||
func (tc *TCPClientInterface) IsDetached() bool {
|
||||
tc.mutex.RLock()
|
||||
defer tc.mutex.RUnlock()
|
||||
return tc.detached
|
||||
}
|
||||
|
||||
func (tc *TCPClientInterface) IsOnline() bool {
|
||||
tc.mutex.RLock()
|
||||
defer tc.mutex.RUnlock()
|
||||
return tc.online
|
||||
}
|
||||
|
||||
type TCPServerInterface struct {
|
||||
BaseInterface
|
||||
listener net.Listener
|
||||
connections map[string]net.Conn
|
||||
mutex sync.RWMutex
|
||||
bindAddr string
|
||||
bindPort int
|
||||
preferIPv6 bool
|
||||
spawned bool
|
||||
port int
|
||||
host string
|
||||
kissFraming bool
|
||||
i2pTunneled bool
|
||||
packetCallback common.PacketCallback
|
||||
detached bool
|
||||
}
|
||||
|
||||
func NewTCPServer(name string, bindAddr string, bindPort int, kissFraming bool, i2pTunneled bool, preferIPv6 bool) (*TCPServerInterface, error) {
|
||||
ts := &TCPServerInterface{
|
||||
BaseInterface: BaseInterface{
|
||||
BaseInterface: common.BaseInterface{
|
||||
Name: name,
|
||||
Mode: common.IF_MODE_FULL,
|
||||
Type: common.IF_TYPE_TCP,
|
||||
MTU: 1064,
|
||||
Bitrate: 10000000,
|
||||
},
|
||||
name: name,
|
||||
mode: common.IF_MODE_FULL,
|
||||
ifType: common.IF_TYPE_TCP,
|
||||
online: false,
|
||||
mtu: common.DEFAULT_MTU,
|
||||
detached: false,
|
||||
},
|
||||
connections: make(map[string]net.Conn),
|
||||
bindAddr: bindAddr,
|
||||
bindPort: bindPort,
|
||||
preferIPv6: preferIPv6,
|
||||
kissFraming: kissFraming,
|
||||
i2pTunneled: i2pTunneled,
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create new client interface for this connection
|
||||
client := &TCPClientInterface{
|
||||
BaseInterface: BaseInterface{
|
||||
BaseInterface: common.BaseInterface{
|
||||
Name: fmt.Sprintf("Client-%s-%s", ts.Name, conn.RemoteAddr()),
|
||||
Mode: ts.Mode,
|
||||
Type: common.IF_TYPE_TCP,
|
||||
MTU: ts.MTU,
|
||||
Bitrate: ts.Bitrate,
|
||||
},
|
||||
},
|
||||
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.BaseInterface.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"
|
||||
addr = "0.0.0.0"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("TCPServerInterface[%s/%s:%d]", ts.Name, addr, ts.bindPort)
|
||||
return fmt.Sprintf("TCPServerInterface[%s/%s:%d]", ts.name, addr, ts.bindPort)
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) SetPacketCallback(cb func([]byte, interface{})) {
|
||||
ts.packetCallback = cb
|
||||
func (ts *TCPServerInterface) SetPacketCallback(callback common.PacketCallback) {
|
||||
ts.mutex.Lock()
|
||||
defer ts.mutex.Unlock()
|
||||
ts.packetCallback = callback
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) GetPacketCallback() common.PacketCallback {
|
||||
ts.mutex.RLock()
|
||||
defer ts.mutex.RUnlock()
|
||||
return ts.packetCallback
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) IsEnabled() bool {
|
||||
return ts.Online
|
||||
return ts.online
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) GetName() string {
|
||||
return ts.Name
|
||||
}
|
||||
return ts.name
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) IsDetached() bool {
|
||||
ts.mutex.RLock()
|
||||
defer ts.mutex.RUnlock()
|
||||
return ts.detached
|
||||
}
|
||||
|
||||
func (ts *TCPServerInterface) IsOnline() bool {
|
||||
ts.mutex.RLock()
|
||||
defer ts.mutex.RUnlock()
|
||||
return ts.online
|
||||
}
|
||||
|
||||
@@ -3,95 +3,139 @@ package interfaces
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||
)
|
||||
|
||||
type UDPInterface struct {
|
||||
BaseInterface
|
||||
conn *net.UDPConn
|
||||
listenAddr *net.UDPAddr
|
||||
conn *net.UDPConn
|
||||
addr *net.UDPAddr
|
||||
targetAddr *net.UDPAddr
|
||||
mutex sync.RWMutex
|
||||
readBuffer []byte
|
||||
txBytes uint64
|
||||
rxBytes uint64
|
||||
mtu int
|
||||
bitrate int
|
||||
}
|
||||
|
||||
func NewUDPInterface(name string, listenAddr string, targetAddr string) (*UDPInterface, error) {
|
||||
func NewUDPInterface(name string, addr string, target string) (*UDPInterface, error) {
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var targetAddr *net.UDPAddr
|
||||
if target != "" {
|
||||
targetAddr, err = net.ResolveUDPAddr("udp", target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ui := &UDPInterface{
|
||||
BaseInterface: BaseInterface{
|
||||
BaseInterface: common.BaseInterface{
|
||||
Name: name,
|
||||
Mode: common.IF_MODE_FULL,
|
||||
Type: common.IF_TYPE_UDP,
|
||||
MTU: 1500,
|
||||
Bitrate: 100000000, // 100Mbps estimate
|
||||
},
|
||||
name: name,
|
||||
mode: common.IF_MODE_FULL,
|
||||
ifType: common.IF_TYPE_UDP,
|
||||
online: false,
|
||||
mtu: common.DEFAULT_MTU,
|
||||
detached: false,
|
||||
},
|
||||
readBuffer: make([]byte, 65535),
|
||||
addr: udpAddr,
|
||||
targetAddr: targetAddr,
|
||||
readBuffer: make([]byte, common.DEFAULT_MTU),
|
||||
}
|
||||
|
||||
// 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.BaseInterface.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.BaseInterface.IN = true
|
||||
ui.BaseInterface.Online = true
|
||||
|
||||
// Start read loop
|
||||
go ui.readLoop()
|
||||
|
||||
return ui, nil
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) readLoop() {
|
||||
for {
|
||||
if !ui.BaseInterface.Online {
|
||||
return
|
||||
}
|
||||
func (ui *UDPInterface) GetName() string {
|
||||
return ui.name
|
||||
}
|
||||
|
||||
n, remoteAddr, err := ui.conn.ReadFromUDP(ui.readBuffer)
|
||||
func (ui *UDPInterface) GetType() common.InterfaceType {
|
||||
return ui.ifType
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) GetMode() common.InterfaceMode {
|
||||
return ui.mode
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) IsOnline() bool {
|
||||
ui.mutex.RLock()
|
||||
defer ui.mutex.RUnlock()
|
||||
return ui.online
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) IsDetached() bool {
|
||||
ui.mutex.RLock()
|
||||
defer ui.mutex.RUnlock()
|
||||
return ui.detached
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) Detach() {
|
||||
ui.mutex.Lock()
|
||||
defer ui.mutex.Unlock()
|
||||
ui.detached = true
|
||||
if ui.conn != nil {
|
||||
ui.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) Send(data []byte, addr string) error {
|
||||
if !ui.IsOnline() {
|
||||
return fmt.Errorf("interface offline")
|
||||
}
|
||||
|
||||
targetAddr := ui.targetAddr
|
||||
if addr != "" {
|
||||
var err error
|
||||
targetAddr, err = net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
if !ui.BaseInterface.Detached {
|
||||
continue
|
||||
}
|
||||
return
|
||||
return fmt.Errorf("invalid target address: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// If no target address is set, use the first sender's address
|
||||
if ui.targetAddr == nil {
|
||||
ui.targetAddr = remoteAddr
|
||||
ui.BaseInterface.OUT = true
|
||||
}
|
||||
if targetAddr == nil {
|
||||
return fmt.Errorf("no target address configured")
|
||||
}
|
||||
|
||||
// Copy received data
|
||||
data := make([]byte, n)
|
||||
copy(data, ui.readBuffer[:n])
|
||||
_, err := ui.conn.WriteToUDP(data, targetAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("UDP write failed: %v", err)
|
||||
}
|
||||
|
||||
// Process packet
|
||||
ui.ProcessIncoming(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) SetPacketCallback(callback common.PacketCallback) {
|
||||
ui.mutex.Lock()
|
||||
defer ui.mutex.Unlock()
|
||||
ui.packetCallback = callback
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) GetPacketCallback() common.PacketCallback {
|
||||
ui.mutex.RLock()
|
||||
defer ui.mutex.RUnlock()
|
||||
return ui.packetCallback
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) ProcessIncoming(data []byte) {
|
||||
if callback := ui.GetPacketCallback(); callback != nil {
|
||||
callback(data, ui)
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) ProcessOutgoing(data []byte) error {
|
||||
if !ui.BaseInterface.Online || ui.targetAddr == nil {
|
||||
return fmt.Errorf("interface offline or no target address configured")
|
||||
if !ui.IsOnline() {
|
||||
return fmt.Errorf("interface offline")
|
||||
}
|
||||
|
||||
if ui.targetAddr == nil {
|
||||
return fmt.Errorf("no target address configured")
|
||||
}
|
||||
|
||||
_, err := ui.conn.WriteToUDP(data, ui.targetAddr)
|
||||
@@ -99,17 +143,33 @@ func (ui *UDPInterface) ProcessOutgoing(data []byte) error {
|
||||
return fmt.Errorf("UDP write failed: %v", err)
|
||||
}
|
||||
|
||||
ui.BaseInterface.ProcessOutgoing(data)
|
||||
return nil
|
||||
}
|
||||
ui.mutex.Lock()
|
||||
ui.txBytes += uint64(len(data))
|
||||
ui.mutex.Unlock()
|
||||
|
||||
func (ui *UDPInterface) Detach() {
|
||||
ui.BaseInterface.Detach()
|
||||
if ui.conn != nil {
|
||||
ui.conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) GetConn() net.Conn {
|
||||
return ui.conn
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) GetTxBytes() uint64 {
|
||||
ui.mutex.RLock()
|
||||
defer ui.mutex.RUnlock()
|
||||
return ui.txBytes
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) GetRxBytes() uint64 {
|
||||
ui.mutex.RLock()
|
||||
defer ui.mutex.RUnlock()
|
||||
return ui.rxBytes
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) GetMTU() int {
|
||||
return ui.mtu
|
||||
}
|
||||
|
||||
func (ui *UDPInterface) GetBitrate() int {
|
||||
return ui.bitrate
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user