mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-21 09:36:34 -05:00
refactor UDP dispatcher to support fullcone dispatcher
This commit is contained in:
parent
d13894ea81
commit
ac65036808
@ -56,7 +56,7 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
|
|||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
Execute: s.Cleanup,
|
Execute: s.Cleanup,
|
||||||
}
|
}
|
||||||
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
|
s.udpServer = udp.NewSplitDispatcher(dispatcher, s.HandleResponse)
|
||||||
newError("DNS: created UDP client initialized for ", address.NetAddr()).AtInfo().WriteToLog()
|
newError("DNS: created UDP client initialized for ", address.NetAddr()).AtInfo().WriteToLog()
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
||||||
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
udpServer := udp.NewSplitDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
||||||
request := protocol.RequestHeaderFromContext(ctx)
|
request := protocol.RequestHeaderFromContext(ctx)
|
||||||
if request == nil {
|
if request == nil {
|
||||||
return
|
return
|
||||||
|
@ -186,7 +186,7 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
||||||
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
udpServer := udp.NewSplitDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
||||||
payload := packet.Payload
|
payload := packet.Payload
|
||||||
newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReader, clientWriter *PacketWriter, dispatcher routing.Dispatcher) error {
|
func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReader, clientWriter *PacketWriter, dispatcher routing.Dispatcher) error {
|
||||||
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
udpServer := udp.NewSplitDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
||||||
if err := clientWriter.WriteMultiBufferWithMetadata(buf.MultiBuffer{packet.Payload}, packet.Source); err != nil {
|
if err := clientWriter.WriteMultiBufferWithMetadata(buf.MultiBuffer{packet.Payload}, packet.Source); err != nil {
|
||||||
newError("failed to write response").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
newError("failed to write response").Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx))
|
||||||
}
|
}
|
||||||
|
@ -2,196 +2,10 @@ package udp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/v2fly/v2ray-core/v5/common"
|
|
||||||
"github.com/v2fly/v2ray-core/v5/common/buf"
|
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||||
"github.com/v2fly/v2ray-core/v5/common/net"
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
"github.com/v2fly/v2ray-core/v5/common/protocol/udp"
|
|
||||||
"github.com/v2fly/v2ray-core/v5/common/session"
|
|
||||||
"github.com/v2fly/v2ray-core/v5/common/signal"
|
|
||||||
"github.com/v2fly/v2ray-core/v5/common/signal/done"
|
|
||||||
"github.com/v2fly/v2ray-core/v5/features/routing"
|
|
||||||
"github.com/v2fly/v2ray-core/v5/transport"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResponseCallback func(ctx context.Context, packet *udp.Packet)
|
type DispatcherI interface {
|
||||||
|
Dispatch(ctx context.Context, destination net.Destination, payload *buf.Buffer)
|
||||||
type connEntry struct {
|
|
||||||
link *transport.Link
|
|
||||||
timer signal.ActivityUpdater
|
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
type Dispatcher struct {
|
|
||||||
sync.RWMutex
|
|
||||||
conns map[net.Destination]*connEntry
|
|
||||||
dispatcher routing.Dispatcher
|
|
||||||
callback ResponseCallback
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) *Dispatcher {
|
|
||||||
return &Dispatcher{
|
|
||||||
conns: make(map[net.Destination]*connEntry),
|
|
||||||
dispatcher: dispatcher,
|
|
||||||
callback: callback,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Dispatcher) RemoveRay(dest net.Destination) {
|
|
||||||
v.Lock()
|
|
||||||
defer v.Unlock()
|
|
||||||
if conn, found := v.conns[dest]; found {
|
|
||||||
common.Close(conn.link.Reader)
|
|
||||||
common.Close(conn.link.Writer)
|
|
||||||
delete(v.conns, dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) *connEntry {
|
|
||||||
v.Lock()
|
|
||||||
defer v.Unlock()
|
|
||||||
|
|
||||||
if entry, found := v.conns[dest]; found {
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
|
|
||||||
newError("establishing new connection for ", dest).WriteToLog()
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
removeRay := func() {
|
|
||||||
cancel()
|
|
||||||
v.RemoveRay(dest)
|
|
||||||
}
|
|
||||||
timer := signal.CancelAfterInactivity(ctx, removeRay, time.Second*4)
|
|
||||||
link, _ := v.dispatcher.Dispatch(ctx, dest)
|
|
||||||
entry := &connEntry{
|
|
||||||
link: link,
|
|
||||||
timer: timer,
|
|
||||||
cancel: removeRay,
|
|
||||||
}
|
|
||||||
v.conns[dest] = entry
|
|
||||||
go handleInput(ctx, entry, dest, v.callback)
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Dispatcher) Dispatch(ctx context.Context, destination net.Destination, payload *buf.Buffer) {
|
|
||||||
// TODO: Add user to destString
|
|
||||||
newError("dispatch request to: ", destination).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
|
|
||||||
conn := v.getInboundRay(ctx, destination)
|
|
||||||
outputStream := conn.link.Writer
|
|
||||||
if outputStream != nil {
|
|
||||||
if err := outputStream.WriteMultiBuffer(buf.MultiBuffer{payload}); err != nil {
|
|
||||||
newError("failed to write first UDP payload").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
conn.cancel()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleInput(ctx context.Context, conn *connEntry, dest net.Destination, callback ResponseCallback) {
|
|
||||||
defer conn.cancel()
|
|
||||||
|
|
||||||
input := conn.link.Reader
|
|
||||||
timer := conn.timer
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
mb, err := input.ReadMultiBuffer()
|
|
||||||
if err != nil {
|
|
||||||
newError("failed to handle UDP input").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
timer.Update()
|
|
||||||
for _, b := range mb {
|
|
||||||
callback(ctx, &udp.Packet{
|
|
||||||
Payload: b,
|
|
||||||
Source: dest,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type dispatcherConn struct {
|
|
||||||
dispatcher *Dispatcher
|
|
||||||
cache chan *udp.Packet
|
|
||||||
done *done.Instance
|
|
||||||
}
|
|
||||||
|
|
||||||
func DialDispatcher(ctx context.Context, dispatcher routing.Dispatcher) (net.PacketConn, error) {
|
|
||||||
c := &dispatcherConn{
|
|
||||||
cache: make(chan *udp.Packet, 16),
|
|
||||||
done: done.New(),
|
|
||||||
}
|
|
||||||
|
|
||||||
d := NewDispatcher(dispatcher, c.callback)
|
|
||||||
c.dispatcher = d
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dispatcherConn) callback(ctx context.Context, packet *udp.Packet) {
|
|
||||||
select {
|
|
||||||
case <-c.done.Wait():
|
|
||||||
packet.Payload.Release()
|
|
||||||
return
|
|
||||||
case c.cache <- packet:
|
|
||||||
default:
|
|
||||||
packet.Payload.Release()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dispatcherConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
|
||||||
select {
|
|
||||||
case <-c.done.Wait():
|
|
||||||
return 0, nil, io.EOF
|
|
||||||
case packet := <-c.cache:
|
|
||||||
n := copy(p, packet.Payload.Bytes())
|
|
||||||
return n, &net.UDPAddr{
|
|
||||||
IP: packet.Source.Address.IP(),
|
|
||||||
Port: int(packet.Source.Port),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dispatcherConn) WriteTo(p []byte, addr net.Addr) (int, error) {
|
|
||||||
buffer := buf.New()
|
|
||||||
raw := buffer.Extend(buf.Size)
|
|
||||||
n := copy(raw, p)
|
|
||||||
buffer.Resize(0, int32(n))
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
c.dispatcher.Dispatch(ctx, net.DestinationFromAddr(addr), buffer)
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dispatcherConn) Close() error {
|
|
||||||
return c.done.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dispatcherConn) LocalAddr() net.Addr {
|
|
||||||
return &net.UDPAddr{
|
|
||||||
IP: []byte{0, 0, 0, 0},
|
|
||||||
Port: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dispatcherConn) SetDeadline(t time.Time) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dispatcherConn) SetReadDeadline(t time.Time) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *dispatcherConn) SetWriteDeadline(t time.Time) error {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
197
transport/internet/udp/dispatcher_split.go
Normal file
197
transport/internet/udp/dispatcher_split.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
package udp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/net"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/protocol/udp"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/session"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/signal"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/common/signal/done"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/features/routing"
|
||||||
|
"github.com/v2fly/v2ray-core/v5/transport"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResponseCallback func(ctx context.Context, packet *udp.Packet)
|
||||||
|
|
||||||
|
type connEntry struct {
|
||||||
|
link *transport.Link
|
||||||
|
timer signal.ActivityUpdater
|
||||||
|
cancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dispatcher struct {
|
||||||
|
sync.RWMutex
|
||||||
|
conns map[net.Destination]*connEntry
|
||||||
|
dispatcher routing.Dispatcher
|
||||||
|
callback ResponseCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSplitDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) *Dispatcher {
|
||||||
|
return &Dispatcher{
|
||||||
|
conns: make(map[net.Destination]*connEntry),
|
||||||
|
dispatcher: dispatcher,
|
||||||
|
callback: callback,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Dispatcher) RemoveRay(dest net.Destination) {
|
||||||
|
v.Lock()
|
||||||
|
defer v.Unlock()
|
||||||
|
if conn, found := v.conns[dest]; found {
|
||||||
|
common.Close(conn.link.Reader)
|
||||||
|
common.Close(conn.link.Writer)
|
||||||
|
delete(v.conns, dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) *connEntry {
|
||||||
|
v.Lock()
|
||||||
|
defer v.Unlock()
|
||||||
|
|
||||||
|
if entry, found := v.conns[dest]; found {
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
newError("establishing new connection for ", dest).WriteToLog()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
removeRay := func() {
|
||||||
|
cancel()
|
||||||
|
v.RemoveRay(dest)
|
||||||
|
}
|
||||||
|
timer := signal.CancelAfterInactivity(ctx, removeRay, time.Second*4)
|
||||||
|
link, _ := v.dispatcher.Dispatch(ctx, dest)
|
||||||
|
entry := &connEntry{
|
||||||
|
link: link,
|
||||||
|
timer: timer,
|
||||||
|
cancel: removeRay,
|
||||||
|
}
|
||||||
|
v.conns[dest] = entry
|
||||||
|
go handleInput(ctx, entry, dest, v.callback)
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Dispatcher) Dispatch(ctx context.Context, destination net.Destination, payload *buf.Buffer) {
|
||||||
|
// TODO: Add user to destString
|
||||||
|
newError("dispatch request to: ", destination).AtDebug().WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
|
||||||
|
conn := v.getInboundRay(ctx, destination)
|
||||||
|
outputStream := conn.link.Writer
|
||||||
|
if outputStream != nil {
|
||||||
|
if err := outputStream.WriteMultiBuffer(buf.MultiBuffer{payload}); err != nil {
|
||||||
|
newError("failed to write first UDP payload").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
conn.cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleInput(ctx context.Context, conn *connEntry, dest net.Destination, callback ResponseCallback) {
|
||||||
|
defer conn.cancel()
|
||||||
|
|
||||||
|
input := conn.link.Reader
|
||||||
|
timer := conn.timer
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
mb, err := input.ReadMultiBuffer()
|
||||||
|
if err != nil {
|
||||||
|
newError("failed to handle UDP input").Base(err).WriteToLog(session.ExportIDToError(ctx))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timer.Update()
|
||||||
|
for _, b := range mb {
|
||||||
|
callback(ctx, &udp.Packet{
|
||||||
|
Payload: b,
|
||||||
|
Source: dest,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dispatcherConn struct {
|
||||||
|
dispatcher *Dispatcher
|
||||||
|
cache chan *udp.Packet
|
||||||
|
done *done.Instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialDispatcher(ctx context.Context, dispatcher routing.Dispatcher) (net.PacketConn, error) {
|
||||||
|
c := &dispatcherConn{
|
||||||
|
cache: make(chan *udp.Packet, 16),
|
||||||
|
done: done.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
d := NewSplitDispatcher(dispatcher, c.callback)
|
||||||
|
c.dispatcher = d
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dispatcherConn) callback(ctx context.Context, packet *udp.Packet) {
|
||||||
|
select {
|
||||||
|
case <-c.done.Wait():
|
||||||
|
packet.Payload.Release()
|
||||||
|
return
|
||||||
|
case c.cache <- packet:
|
||||||
|
default:
|
||||||
|
packet.Payload.Release()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dispatcherConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
||||||
|
select {
|
||||||
|
case <-c.done.Wait():
|
||||||
|
return 0, nil, io.EOF
|
||||||
|
case packet := <-c.cache:
|
||||||
|
n := copy(p, packet.Payload.Bytes())
|
||||||
|
return n, &net.UDPAddr{
|
||||||
|
IP: packet.Source.Address.IP(),
|
||||||
|
Port: int(packet.Source.Port),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dispatcherConn) WriteTo(p []byte, addr net.Addr) (int, error) {
|
||||||
|
buffer := buf.New()
|
||||||
|
raw := buffer.Extend(buf.Size)
|
||||||
|
n := copy(raw, p)
|
||||||
|
buffer.Resize(0, int32(n))
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
c.dispatcher.Dispatch(ctx, net.DestinationFromAddr(addr), buffer)
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dispatcherConn) Close() error {
|
||||||
|
return c.done.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dispatcherConn) LocalAddr() net.Addr {
|
||||||
|
return &net.UDPAddr{
|
||||||
|
IP: []byte{0, 0, 0, 0},
|
||||||
|
Port: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dispatcherConn) SetDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dispatcherConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dispatcherConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
@ -65,7 +65,7 @@ func TestSameDestinationDispatching(t *testing.T) {
|
|||||||
b.WriteString("abcd")
|
b.WriteString("abcd")
|
||||||
|
|
||||||
var msgCount uint32
|
var msgCount uint32
|
||||||
dispatcher := NewDispatcher(td, func(ctx context.Context, packet *udp.Packet) {
|
dispatcher := NewSplitDispatcher(td, func(ctx context.Context, packet *udp.Packet) {
|
||||||
atomic.AddUint32(&msgCount, 1)
|
atomic.AddUint32(&msgCount, 1)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue
Block a user