Add channel handling to Link struct; implement methods for managing incoming channel packets, resource advertisements, and packet delivery. Enhance initialization with IncomingLinkHandler registration and improve error handling for resource requests.
Some checks failed
Go Build Multi-Platform / build (amd64, darwin) (push) Failing after 17s
Go Build Multi-Platform / build (amd64, freebsd) (push) Failing after 25s
Go Build Multi-Platform / build (amd64, windows) (push) Failing after 31s
Go Build Multi-Platform / build (amd64, linux) (push) Failing after 33s
Go Build Multi-Platform / build (arm, freebsd) (push) Failing after 31s
Go Build Multi-Platform / build (arm, linux) (push) Failing after 23s
Go Build Multi-Platform / build (arm, windows) (push) Failing after 40s
Go Build Multi-Platform / build (arm64, darwin) (push) Failing after 38s
Go Revive Lint / lint (push) Successful in 30s
Run Gosec / tests (push) Failing after 57s
Go Test Multi-Platform / Test (macos-latest, amd64) (push) Has been cancelled
Go Test Multi-Platform / Test (windows-latest, amd64) (push) Has been cancelled
Go Test Multi-Platform / Test (macos-latest, arm64) (push) Has been cancelled
Go Build Multi-Platform / build (arm64, freebsd) (push) Failing after 29s
Go Build Multi-Platform / build (arm64, linux) (push) Failing after 27s
Go Build Multi-Platform / build (arm64, windows) (push) Failing after 33s
Go Test Multi-Platform / Test (ubuntu-latest, amd64) (push) Failing after 35s
Go Build Multi-Platform / Create Release (push) Has been skipped
Go Test Multi-Platform / Test (ubuntu-latest, arm64) (push) Failing after 36s
Some checks failed
Go Build Multi-Platform / build (amd64, darwin) (push) Failing after 17s
Go Build Multi-Platform / build (amd64, freebsd) (push) Failing after 25s
Go Build Multi-Platform / build (amd64, windows) (push) Failing after 31s
Go Build Multi-Platform / build (amd64, linux) (push) Failing after 33s
Go Build Multi-Platform / build (arm, freebsd) (push) Failing after 31s
Go Build Multi-Platform / build (arm, linux) (push) Failing after 23s
Go Build Multi-Platform / build (arm, windows) (push) Failing after 40s
Go Build Multi-Platform / build (arm64, darwin) (push) Failing after 38s
Go Revive Lint / lint (push) Successful in 30s
Run Gosec / tests (push) Failing after 57s
Go Test Multi-Platform / Test (macos-latest, amd64) (push) Has been cancelled
Go Test Multi-Platform / Test (windows-latest, amd64) (push) Has been cancelled
Go Test Multi-Platform / Test (macos-latest, arm64) (push) Has been cancelled
Go Build Multi-Platform / build (arm64, freebsd) (push) Failing after 29s
Go Build Multi-Platform / build (arm64, linux) (push) Failing after 27s
Go Build Multi-Platform / build (arm64, windows) (push) Failing after 33s
Go Test Multi-Platform / Test (ubuntu-latest, amd64) (push) Failing after 35s
Go Build Multi-Platform / Create Release (push) Has been skipped
Go Test Multi-Platform / Test (ubuntu-latest, arm64) (push) Failing after 36s
This commit is contained in:
240
pkg/link/link.go
240
pkg/link/link.go
@@ -13,6 +13,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sudo-Ivan/reticulum-go/pkg/channel"
|
||||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||||
"github.com/Sudo-Ivan/reticulum-go/pkg/cryptography"
|
"github.com/Sudo-Ivan/reticulum-go/pkg/cryptography"
|
||||||
"github.com/Sudo-Ivan/reticulum-go/pkg/debug"
|
"github.com/Sudo-Ivan/reticulum-go/pkg/debug"
|
||||||
@@ -65,6 +66,16 @@ const (
|
|||||||
WATCHDOG_INTERVAL = 0.1
|
WATCHDOG_INTERVAL = 0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
destination.RegisterIncomingLinkHandler(func(pkt *packet.Packet, dest *destination.Destination, trans interface{}, networkIface common.NetworkInterface) (interface{}, error) {
|
||||||
|
transportObj, ok := trans.(*transport.Transport)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("invalid transport type")
|
||||||
|
}
|
||||||
|
return HandleIncomingLinkRequest(pkt, dest, transportObj, networkIface)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type Link struct {
|
type Link struct {
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
destination *destination.Destination
|
destination *destination.Destination
|
||||||
@@ -127,6 +138,9 @@ type Link struct {
|
|||||||
|
|
||||||
pendingRequests []*RequestReceipt
|
pendingRequests []*RequestReceipt
|
||||||
requestMutex sync.RWMutex
|
requestMutex sync.RWMutex
|
||||||
|
|
||||||
|
channel *channel.Channel
|
||||||
|
channelMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLink(dest *destination.Destination, transport *transport.Transport, networkIface common.NetworkInterface, establishedCallback func(*Link), closedCallback func(*Link)) *Link {
|
func NewLink(dest *destination.Destination, transport *transport.Transport, networkIface common.NetworkInterface, establishedCallback func(*Link), closedCallback func(*Link)) *Link {
|
||||||
@@ -691,7 +705,7 @@ func (l *Link) handleDataPacket(pkt *packet.Packet) error {
|
|||||||
case packet.ContextResponse:
|
case packet.ContextResponse:
|
||||||
return l.handleResponse(plaintext)
|
return l.handleResponse(plaintext)
|
||||||
case packet.ContextLinkIdentify:
|
case packet.ContextLinkIdentify:
|
||||||
return l.handleIdentification(plaintext)
|
return l.HandleIdentification(plaintext)
|
||||||
case packet.ContextKeepalive:
|
case packet.ContextKeepalive:
|
||||||
if !l.initiator && len(plaintext) == 1 && plaintext[0] == 0xFF {
|
if !l.initiator && len(plaintext) == 1 && plaintext[0] == 0xFF {
|
||||||
keepaliveResp := []byte{0xFE}
|
keepaliveResp := []byte{0xFE}
|
||||||
@@ -737,6 +751,35 @@ func (l *Link) handleDataPacket(pkt *packet.Packet) error {
|
|||||||
return l.handleResourceReject(pkt)
|
return l.handleResourceReject(pkt)
|
||||||
case packet.ContextResource:
|
case packet.ContextResource:
|
||||||
return l.handleResourcePart(pkt)
|
return l.handleResourcePart(pkt)
|
||||||
|
case packet.ContextChannel:
|
||||||
|
return l.handleChannelPacket(pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Link) GetChannel() *channel.Channel {
|
||||||
|
l.channelMutex.Lock()
|
||||||
|
defer l.channelMutex.Unlock()
|
||||||
|
|
||||||
|
if l.channel == nil {
|
||||||
|
l.channel = channel.NewChannel(l)
|
||||||
|
}
|
||||||
|
return l.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Link) handleChannelPacket(pkt *packet.Packet) error {
|
||||||
|
plaintext, err := l.decrypt(pkt.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.channelMutex.RLock()
|
||||||
|
ch := l.channel
|
||||||
|
l.channelMutex.RUnlock()
|
||||||
|
|
||||||
|
if ch != nil {
|
||||||
|
return ch.HandleInbound(plaintext)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -748,6 +791,48 @@ func (l *Link) handleResourceAdvertisement(pkt *packet.Packet) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adv, err := resource.UnpackResourceAdvertisement(plaintext)
|
||||||
|
if err != nil {
|
||||||
|
debug.Log(debug.DEBUG_INFO, "Failed to unpack resource advertisement", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resource.IsRequestAdvertisement(plaintext) {
|
||||||
|
requestID := resource.ReadRequestID(plaintext)
|
||||||
|
if l.destination != nil {
|
||||||
|
handler := l.destination.GetRequestHandler(requestID)
|
||||||
|
if handler != nil {
|
||||||
|
response := handler(requestID, nil, requestID, l.remoteIdentity, time.Now())
|
||||||
|
if response != nil {
|
||||||
|
return l.sendResourceResponse(requestID, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if resource.IsResponseAdvertisement(plaintext) {
|
||||||
|
requestID := resource.ReadRequestID(plaintext)
|
||||||
|
l.requestMutex.Lock()
|
||||||
|
for i, req := range l.pendingRequests {
|
||||||
|
if string(req.requestID) == string(requestID) {
|
||||||
|
req.mutex.Lock()
|
||||||
|
req.status = STATUS_ACTIVE
|
||||||
|
req.receivedAt = time.Now()
|
||||||
|
req.mutex.Unlock()
|
||||||
|
|
||||||
|
if req.responseCb != nil {
|
||||||
|
go req.responseCb(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.pendingRequests = append(l.pendingRequests[:i], l.pendingRequests[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.requestMutex.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if l.resourceStrategy == ACCEPT_NONE {
|
if l.resourceStrategy == ACCEPT_NONE {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -756,20 +841,100 @@ func (l *Link) handleResourceAdvertisement(pkt *packet.Packet) error {
|
|||||||
if l.resourceStrategy == ACCEPT_ALL {
|
if l.resourceStrategy == ACCEPT_ALL {
|
||||||
allowed = true
|
allowed = true
|
||||||
} else if l.resourceStrategy == ACCEPT_APP && l.resourceCallback != nil {
|
} else if l.resourceStrategy == ACCEPT_APP && l.resourceCallback != nil {
|
||||||
allowed = l.resourceCallback(plaintext)
|
allowed = l.resourceCallback(adv)
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowed {
|
if allowed {
|
||||||
if l.resourceStartedCallback != nil {
|
if l.resourceStartedCallback != nil {
|
||||||
l.resourceStartedCallback(plaintext)
|
l.resourceStartedCallback(adv)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
_ = l.rejectResource(adv.Hash) // #nosec G104 - best effort resource rejection
|
||||||
debug.Log(debug.DEBUG_INFO, "Resource advertisement rejected")
|
debug.Log(debug.DEBUG_INFO, "Resource advertisement rejected")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Link) rejectResource(resourceHash []byte) error {
|
||||||
|
rejectPkt := &packet.Packet{
|
||||||
|
HeaderType: packet.HeaderType1,
|
||||||
|
PacketType: packet.PacketTypeData,
|
||||||
|
TransportType: 0,
|
||||||
|
Context: packet.ContextResourceRCL,
|
||||||
|
ContextFlag: packet.FlagUnset,
|
||||||
|
Hops: 0,
|
||||||
|
DestinationType: 0x03,
|
||||||
|
DestinationHash: l.linkID,
|
||||||
|
Data: resourceHash,
|
||||||
|
CreateReceipt: false,
|
||||||
|
}
|
||||||
|
encrypted, err := l.encrypt(resourceHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rejectPkt.Data = encrypted
|
||||||
|
if err := rejectPkt.Pack(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l.lastOutbound = time.Now()
|
||||||
|
return l.transport.SendPacket(rejectPkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Link) sendResourceResponse(requestID []byte, response interface{}) error {
|
||||||
|
resData, ok := response.([]byte)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("response must be []byte")
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := resource.New(resData, false)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create resource: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res.SetRequestID(requestID)
|
||||||
|
res.SetIsResponse(true)
|
||||||
|
|
||||||
|
return l.sendResourceAdvertisement(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Link) sendResourceAdvertisement(res *resource.Resource) error {
|
||||||
|
adv := resource.NewResourceAdvertisement(res)
|
||||||
|
if adv == nil {
|
||||||
|
return errors.New("failed to create resource advertisement")
|
||||||
|
}
|
||||||
|
|
||||||
|
advData, err := adv.Pack(0)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to pack advertisement: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted, err := l.encrypt(advData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
advPkt := &packet.Packet{
|
||||||
|
HeaderType: packet.HeaderType1,
|
||||||
|
PacketType: packet.PacketTypeData,
|
||||||
|
TransportType: 0,
|
||||||
|
Context: packet.ContextResourceAdv,
|
||||||
|
ContextFlag: packet.FlagUnset,
|
||||||
|
Hops: 0,
|
||||||
|
DestinationType: 0x03,
|
||||||
|
DestinationHash: l.linkID,
|
||||||
|
Data: encrypted,
|
||||||
|
CreateReceipt: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := advPkt.Pack(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.lastOutbound = time.Now()
|
||||||
|
return l.transport.SendPacket(advPkt)
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Link) handleResourceRequest(pkt *packet.Packet) error {
|
func (l *Link) handleResourceRequest(pkt *packet.Packet) error {
|
||||||
plaintext, err := l.decrypt(pkt.Data)
|
plaintext, err := l.decrypt(pkt.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1099,6 +1264,10 @@ func (l *Link) GetRTT() float64 {
|
|||||||
return l.rtt
|
return l.rtt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Link) RTT() float64 {
|
||||||
|
return l.GetRTT()
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Link) SetRTT(rtt float64) {
|
func (l *Link) SetRTT(rtt float64) {
|
||||||
l.mutex.Lock()
|
l.mutex.Lock()
|
||||||
defer l.mutex.Unlock()
|
defer l.mutex.Unlock()
|
||||||
@@ -1111,6 +1280,67 @@ func (l *Link) GetStatus() byte {
|
|||||||
return l.status
|
return l.status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Link) Send(data []byte) interface{} {
|
||||||
|
pkt := &packet.Packet{
|
||||||
|
HeaderType: packet.HeaderType1,
|
||||||
|
PacketType: packet.PacketTypeData,
|
||||||
|
TransportType: 0,
|
||||||
|
Context: packet.ContextChannel,
|
||||||
|
ContextFlag: packet.FlagUnset,
|
||||||
|
Hops: 0,
|
||||||
|
DestinationType: 0x03,
|
||||||
|
DestinationHash: l.linkID,
|
||||||
|
Data: data,
|
||||||
|
CreateReceipt: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypted, err := l.encrypt(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pkt.Data = encrypted
|
||||||
|
|
||||||
|
if err := pkt.Pack(); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l.lastOutbound = time.Now()
|
||||||
|
if err := l.transport.SendPacket(pkt); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Link) SetPacketTimeout(pkt interface{}, callback func(interface{}), timeout time.Duration) {
|
||||||
|
if packetObj, ok := pkt.(*packet.Packet); ok {
|
||||||
|
go func() {
|
||||||
|
time.Sleep(timeout)
|
||||||
|
if callback != nil {
|
||||||
|
callback(packetObj)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Link) SetPacketDelivered(pkt interface{}, callback func(interface{})) {
|
||||||
|
if callback != nil {
|
||||||
|
go callback(pkt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Link) Resend(pkt interface{}) error {
|
||||||
|
packetObj, ok := pkt.(*packet.Packet)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid packet type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := l.transport.SendPacket(packetObj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Link) GetLinkID() []byte {
|
func (l *Link) GetLinkID() []byte {
|
||||||
l.mutex.RLock()
|
l.mutex.RLock()
|
||||||
defer l.mutex.RUnlock()
|
defer l.mutex.RUnlock()
|
||||||
@@ -1362,7 +1592,7 @@ func (l *Link) watchdog() {
|
|||||||
if l.initiator {
|
if l.initiator {
|
||||||
lastKeepalive := l.lastOutbound
|
lastKeepalive := l.lastOutbound
|
||||||
if now.After(lastKeepalive.Add(l.keepalive)) {
|
if now.After(lastKeepalive.Add(l.keepalive)) {
|
||||||
l.sendKeepalive()
|
_ = l.sendKeepalive() // #nosec G104 - best effort keepalive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1378,7 +1608,7 @@ func (l *Link) watchdog() {
|
|||||||
}
|
}
|
||||||
} else if l.status == STATUS_STALE {
|
} else if l.status == STATUS_STALE {
|
||||||
sleepTime = 0.001
|
sleepTime = 0.001
|
||||||
l.sendTeardownPacket()
|
_ = l.sendTeardownPacket() // #nosec G104 - best effort teardown
|
||||||
l.status = STATUS_CLOSED
|
l.status = STATUS_CLOSED
|
||||||
l.teardownReason = STATUS_FAILED
|
l.teardownReason = STATUS_FAILED
|
||||||
if l.closedCallback != nil {
|
if l.closedCallback != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user