dae/control/udp_endpoint.go

158 lines
3.5 KiB
Go
Raw Normal View History

2023-01-23 18:54:21 +07:00
/*
* SPDX-License-Identifier: AGPL-3.0-only
2023-03-14 14:01:55 +07:00
* Copyright (c) 2022-2023, daeuniverse Organization <dae@v2raya.org>
2023-01-23 18:54:21 +07:00
*/
package control
import (
2023-02-04 19:53:29 +07:00
"errors"
2023-01-23 18:54:21 +07:00
"fmt"
2023-03-25 12:52:52 +07:00
"github.com/daeuniverse/dae/component/outbound/dialer"
"github.com/mzz2017/softwind/netproxy"
2023-01-29 12:38:15 +07:00
"github.com/mzz2017/softwind/pool"
2023-01-23 18:54:21 +07:00
"net/netip"
"sync"
"time"
)
2023-03-25 12:52:52 +07:00
const (
EthernetMtu = 1500
)
2023-01-23 18:54:21 +07:00
type UdpHandler func(data []byte, from netip.AddrPort) error
type UdpEndpoint struct {
conn netproxy.PacketConn
2023-01-23 18:54:21 +07:00
// mu protects deadlineTimer
mu sync.Mutex
deadlineTimer *time.Timer
2023-01-23 18:54:21 +07:00
handler UdpHandler
NatTimeout time.Duration
Dialer *dialer.Dialer
2023-01-23 18:54:21 +07:00
}
func (ue *UdpEndpoint) start() {
2023-03-25 12:52:52 +07:00
buf := pool.Get(EthernetMtu)
2023-01-23 18:54:21 +07:00
defer pool.Put(buf)
for {
n, from, err := ue.conn.ReadFrom(buf[:])
if err != nil {
break
}
ue.mu.Lock()
ue.deadlineTimer.Reset(ue.NatTimeout)
ue.mu.Unlock()
if err = ue.handler(buf[:n], from); err != nil {
2023-02-04 19:53:29 +07:00
if errors.Is(err, SuspectedRushAnswerError) {
continue
}
2023-01-23 18:54:21 +07:00
break
}
}
ue.mu.Lock()
ue.deadlineTimer.Stop()
2023-01-23 18:54:21 +07:00
ue.mu.Unlock()
}
func (ue *UdpEndpoint) WriteTo(b []byte, addr string) (int, error) {
return ue.conn.WriteTo(b, addr)
2023-01-23 18:54:21 +07:00
}
func (ue *UdpEndpoint) Close() error {
2023-01-23 18:54:21 +07:00
ue.mu.Lock()
if ue.deadlineTimer != nil {
ue.deadlineTimer.Stop()
}
ue.mu.Unlock()
return ue.conn.Close()
2023-01-23 18:54:21 +07:00
}
// UdpEndpointPool is a full-cone udp conn pool
type UdpEndpointPool struct {
pool map[netip.AddrPort]*UdpEndpoint
mu sync.Mutex
}
type UdpEndpointOptions struct {
Handler UdpHandler
NatTimeout time.Duration
Dialer *dialer.Dialer
// Network is useful for MagicNetwork
Network string
2023-01-23 18:54:21 +07:00
// Target is useful only if the underlay does not support Full-cone.
Target string
2023-01-23 18:54:21 +07:00
}
var DefaultUdpEndpointPool = NewUdpEndpointPool()
func NewUdpEndpointPool() *UdpEndpointPool {
return &UdpEndpointPool{
pool: make(map[netip.AddrPort]*UdpEndpoint),
}
}
func (p *UdpEndpointPool) Remove(lAddr netip.AddrPort, udpEndpoint *UdpEndpoint) (err error) {
p.mu.Lock()
defer p.mu.Unlock()
if ue, ok := p.pool[lAddr]; ok {
if ue != udpEndpoint {
return fmt.Errorf("target udp endpoint is not in the pool")
}
ue.Close()
delete(p.pool, lAddr)
}
return nil
}
func (p *UdpEndpointPool) GetOrCreate(lAddr netip.AddrPort, createOption *UdpEndpointOptions) (udpEndpoint *UdpEndpoint, isNew bool, err error) {
// TODO: fine-grained lock.
2023-01-23 18:54:21 +07:00
p.mu.Lock()
defer p.mu.Unlock()
ue, ok := p.pool[lAddr]
if !ok {
// Create an UdpEndpoint.
if createOption == nil {
createOption = &UdpEndpointOptions{}
}
if createOption.NatTimeout == 0 {
createOption.NatTimeout = DefaultNatTimeout
}
if createOption.Handler == nil {
return nil, true, fmt.Errorf("createOption.Handler cannot be nil")
2023-01-23 18:54:21 +07:00
}
udpConn, err := createOption.Dialer.Dial(createOption.Network, createOption.Target)
2023-01-23 18:54:21 +07:00
if err != nil {
return nil, true, err
2023-01-23 18:54:21 +07:00
}
if _, ok = udpConn.(netproxy.PacketConn); !ok {
return nil, true, fmt.Errorf("protocol does not support udp")
}
2023-01-28 00:50:21 +07:00
ue = &UdpEndpoint{
conn: udpConn.(netproxy.PacketConn),
2023-01-23 18:54:21 +07:00
deadlineTimer: time.AfterFunc(createOption.NatTimeout, func() {
p.mu.Lock()
defer p.mu.Unlock()
if ue, ok := p.pool[lAddr]; ok {
ue.Close()
delete(p.pool, lAddr)
}
}),
handler: createOption.Handler,
NatTimeout: createOption.NatTimeout,
Dialer: createOption.Dialer,
2023-01-23 18:54:21 +07:00
}
2023-01-28 00:50:21 +07:00
p.pool[lAddr] = ue
2023-01-23 18:54:21 +07:00
// Receive UDP messages.
go ue.start()
isNew = true
2023-01-23 18:54:21 +07:00
} else {
// Postpone the deadline.
ue.mu.Lock()
ue.deadlineTimer.Reset(ue.NatTimeout)
2023-01-23 18:54:21 +07:00
ue.mu.Unlock()
}
return ue, isNew, nil
2023-01-23 18:54:21 +07:00
}