sing-box/service/derp/stun.go
2025-03-31 22:58:55 +08:00

89 lines
2.1 KiB
Go

package derp
import (
"context"
"net"
"net/netip"
"time"
"github.com/sagernet/sing-box/adapter"
boxService "github.com/sagernet/sing-box/adapter/service"
"github.com/sagernet/sing-box/common/listener"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/tailscale/net/stun"
)
func RegisterSTUN(registry *boxService.Registry) {
boxService.Register[option.DERPSTUNServiceOptions](registry, C.TypeDERPSTUN, NewSTUNService)
}
type STUNService struct {
boxService.Adapter
ctx context.Context
logger logger.ContextLogger
listener *listener.Listener
}
func NewSTUNService(ctx context.Context, logger log.ContextLogger, tag string, options option.DERPSTUNServiceOptions) (adapter.Service, error) {
return &STUNService{
Adapter: boxService.NewAdapter(C.TypeDERPSTUN, tag),
ctx: ctx,
logger: logger,
listener: listener.New(listener.Options{
Context: ctx,
Logger: logger,
Network: []string{N.NetworkUDP},
Listen: options.ListenOptions,
}),
}, nil
}
func (d *STUNService) Start(stage adapter.StartStage) error {
if stage != adapter.StartStateStart {
return nil
}
packetConn, err := d.listener.ListenUDP()
if err != nil {
return err
}
go d.loopPacket(packetConn.(*net.UDPConn))
return nil
}
func (d *STUNService) Close() error {
return d.listener.Close()
}
func (d *STUNService) loopPacket(packetConn *net.UDPConn) {
buffer := make([]byte, 65535)
oob := make([]byte, 1024)
var (
n int
oobN int
addrPort netip.AddrPort
err error
)
for {
n, oobN, _, addrPort, err = packetConn.ReadMsgUDPAddrPort(buffer, oob)
if err != nil {
if E.IsClosedOrCanceled(err) {
return
}
time.Sleep(time.Second)
continue
}
if !stun.Is(buffer[:n]) {
continue
}
txid, err := stun.ParseBindingRequest(buffer[:n])
if err != nil {
continue
}
packetConn.WriteMsgUDPAddrPort(stun.Response(txid, addrPort), oob[:oobN], addrPort)
}
}