|  | @@ -5,28 +5,95 @@ import (
 | 
											
												
													
														|  |  	"sync"
 |  |  	"sync"
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	"v2ray.com/core/common/alloc"
 |  |  	"v2ray.com/core/common/alloc"
 | 
											
												
													
														|  | 
 |  | +	"v2ray.com/core/common/dice"
 | 
											
												
													
														|  |  	"v2ray.com/core/common/log"
 |  |  	"v2ray.com/core/common/log"
 | 
											
												
													
														|  |  	v2net "v2ray.com/core/common/net"
 |  |  	v2net "v2ray.com/core/common/net"
 | 
											
												
													
														|  | 
 |  | +	"v2ray.com/core/common/signal"
 | 
											
												
													
														|  |  	"v2ray.com/core/proxy"
 |  |  	"v2ray.com/core/proxy"
 | 
											
												
													
														|  |  	"v2ray.com/core/transport/internet/internal"
 |  |  	"v2ray.com/core/transport/internet/internal"
 | 
											
												
													
														|  |  )
 |  |  )
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +type UDPPayload struct {
 | 
											
												
													
														|  | 
 |  | +	payload *alloc.Buffer
 | 
											
												
													
														|  | 
 |  | +	session *proxy.SessionInfo
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  type UDPPayloadHandler func(*alloc.Buffer, *proxy.SessionInfo)
 |  |  type UDPPayloadHandler func(*alloc.Buffer, *proxy.SessionInfo)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -type UDPHub struct {
 |  | 
 | 
											
												
													
														|  | -	sync.RWMutex
 |  | 
 | 
											
												
													
														|  | -	conn      *net.UDPConn
 |  | 
 | 
											
												
													
														|  | -	option    ListenOption
 |  | 
 | 
											
												
													
														|  | -	accepting bool
 |  | 
 | 
											
												
													
														|  | -	pool      *alloc.BufferPool
 |  | 
 | 
											
												
													
														|  | 
 |  | +type UDPPayloadQueue struct {
 | 
											
												
													
														|  | 
 |  | +	queue    []chan UDPPayload
 | 
											
												
													
														|  | 
 |  | +	callback UDPPayloadHandler
 | 
											
												
													
														|  | 
 |  | +	cancel   *signal.CancelSignal
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +func NewUDPPayloadQueue(option ListenOption) *UDPPayloadQueue {
 | 
											
												
													
														|  | 
 |  | +	queue := &UDPPayloadQueue{
 | 
											
												
													
														|  | 
 |  | +		callback: option.Callback,
 | 
											
												
													
														|  | 
 |  | +		cancel:   signal.NewCloseSignal(),
 | 
											
												
													
														|  | 
 |  | +		queue:    make([]chan UDPPayload, option.Concurrency),
 | 
											
												
													
														|  | 
 |  | +	}
 | 
											
												
													
														|  | 
 |  | +	for i := range queue.queue {
 | 
											
												
													
														|  | 
 |  | +		queue.queue[i] = make(chan UDPPayload, 64)
 | 
											
												
													
														|  | 
 |  | +		go queue.Dequeue(queue.queue[i])
 | 
											
												
													
														|  | 
 |  | +	}
 | 
											
												
													
														|  | 
 |  | +	return queue
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +func (this *UDPPayloadQueue) Enqueue(payload UDPPayload) {
 | 
											
												
													
														|  | 
 |  | +	size := len(this.queue)
 | 
											
												
													
														|  | 
 |  | +	for i := 0; i < size; i++ {
 | 
											
												
													
														|  | 
 |  | +		idx := 0
 | 
											
												
													
														|  | 
 |  | +		if size > 1 {
 | 
											
												
													
														|  | 
 |  | +			idx = dice.Roll(size)
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +		select {
 | 
											
												
													
														|  | 
 |  | +		case this.queue[idx] <- payload:
 | 
											
												
													
														|  | 
 |  | +			return
 | 
											
												
													
														|  | 
 |  | +		default:
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +	}
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +func (this *UDPPayloadQueue) Dequeue(queue <-chan UDPPayload) {
 | 
											
												
													
														|  | 
 |  | +	this.cancel.WaitThread()
 | 
											
												
													
														|  | 
 |  | +	defer this.cancel.FinishThread()
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +	for !this.cancel.Cancelled() {
 | 
											
												
													
														|  | 
 |  | +		payload, open := <-queue
 | 
											
												
													
														|  | 
 |  | +		if !open {
 | 
											
												
													
														|  | 
 |  | +			return
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +		this.callback(payload.payload, payload.session)
 | 
											
												
													
														|  | 
 |  | +	}
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +func (this *UDPPayloadQueue) Close() {
 | 
											
												
													
														|  | 
 |  | +	this.cancel.Cancel()
 | 
											
												
													
														|  | 
 |  | +	for _, queue := range this.queue {
 | 
											
												
													
														|  | 
 |  | +		close(queue)
 | 
											
												
													
														|  | 
 |  | +	}
 | 
											
												
													
														|  | 
 |  | +	this.cancel.WaitForDone()
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  type ListenOption struct {
 |  |  type ListenOption struct {
 | 
											
												
													
														|  |  	Callback            UDPPayloadHandler
 |  |  	Callback            UDPPayloadHandler
 | 
											
												
													
														|  |  	ReceiveOriginalDest bool
 |  |  	ReceiveOriginalDest bool
 | 
											
												
													
														|  | 
 |  | +	Concurrency         int
 | 
											
												
													
														|  | 
 |  | +}
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +type UDPHub struct {
 | 
											
												
													
														|  | 
 |  | +	sync.RWMutex
 | 
											
												
													
														|  | 
 |  | +	conn   *net.UDPConn
 | 
											
												
													
														|  | 
 |  | +	pool   *alloc.BufferPool
 | 
											
												
													
														|  | 
 |  | +	cancel *signal.CancelSignal
 | 
											
												
													
														|  | 
 |  | +	queue  *UDPPayloadQueue
 | 
											
												
													
														|  | 
 |  | +	option ListenOption
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  func ListenUDP(address v2net.Address, port v2net.Port, option ListenOption) (*UDPHub, error) {
 |  |  func ListenUDP(address v2net.Address, port v2net.Port, option ListenOption) (*UDPHub, error) {
 | 
											
												
													
														|  | 
 |  | +	if option.Concurrency < 1 {
 | 
											
												
													
														|  | 
 |  | +		option.Concurrency = 1
 | 
											
												
													
														|  | 
 |  | +	}
 | 
											
												
													
														|  |  	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{
 |  |  	udpConn, err := net.ListenUDP("udp", &net.UDPAddr{
 | 
											
												
													
														|  |  		IP:   address.IP(),
 |  |  		IP:   address.IP(),
 | 
											
												
													
														|  |  		Port: int(port),
 |  |  		Port: int(port),
 | 
											
										
											
												
													
														|  | @@ -48,8 +115,10 @@ func ListenUDP(address v2net.Address, port v2net.Port, option ListenOption) (*UD
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  	hub := &UDPHub{
 |  |  	hub := &UDPHub{
 | 
											
												
													
														|  |  		conn:   udpConn,
 |  |  		conn:   udpConn,
 | 
											
												
													
														|  | -		option: option,
 |  | 
 | 
											
												
													
														|  |  		pool:   alloc.NewBufferPool(2048, 64),
 |  |  		pool:   alloc.NewBufferPool(2048, 64),
 | 
											
												
													
														|  | 
 |  | +		queue:  NewUDPPayloadQueue(option),
 | 
											
												
													
														|  | 
 |  | +		option: option,
 | 
											
												
													
														|  | 
 |  | +		cancel: signal.NewCloseSignal(),
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  	go hub.start()
 |  |  	go hub.start()
 | 
											
												
													
														|  |  	return hub, nil
 |  |  	return hub, nil
 | 
											
										
											
												
													
														|  | @@ -59,8 +128,10 @@ func (this *UDPHub) Close() {
 | 
											
												
													
														|  |  	this.Lock()
 |  |  	this.Lock()
 | 
											
												
													
														|  |  	defer this.Unlock()
 |  |  	defer this.Unlock()
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	this.accepting = false
 |  | 
 | 
											
												
													
														|  | 
 |  | +	this.cancel.Cancel()
 | 
											
												
													
														|  |  	this.conn.Close()
 |  |  	this.conn.Close()
 | 
											
												
													
														|  | 
 |  | +	this.cancel.WaitForDone()
 | 
											
												
													
														|  | 
 |  | +	this.queue.Close()
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  func (this *UDPHub) WriteTo(payload []byte, dest v2net.Destination) (int, error) {
 |  |  func (this *UDPHub) WriteTo(payload []byte, dest v2net.Destination) (int, error) {
 | 
											
										
											
												
													
														|  | @@ -71,9 +142,8 @@ func (this *UDPHub) WriteTo(payload []byte, dest v2net.Destination) (int, error)
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  func (this *UDPHub) start() {
 |  |  func (this *UDPHub) start() {
 | 
											
												
													
														|  | -	this.Lock()
 |  | 
 | 
											
												
													
														|  | -	this.accepting = true
 |  | 
 | 
											
												
													
														|  | -	this.Unlock()
 |  | 
 | 
											
												
													
														|  | 
 |  | +	this.cancel.WaitThread()
 | 
											
												
													
														|  | 
 |  | +	defer this.cancel.FinishThread()
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	oobBytes := make([]byte, 256)
 |  |  	oobBytes := make([]byte, 256)
 | 
											
												
													
														|  |  	for this.Running() {
 |  |  	for this.Running() {
 | 
											
										
											
												
													
														|  | @@ -91,15 +161,15 @@ func (this *UDPHub) start() {
 | 
											
												
													
														|  |  		if this.option.ReceiveOriginalDest && noob > 0 {
 |  |  		if this.option.ReceiveOriginalDest && noob > 0 {
 | 
											
												
													
														|  |  			session.Destination = RetrieveOriginalDest(oobBytes[:noob])
 |  |  			session.Destination = RetrieveOriginalDest(oobBytes[:noob])
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  | -		go this.option.Callback(buffer, session)
 |  | 
 | 
											
												
													
														|  | 
 |  | +		this.queue.Enqueue(UDPPayload{
 | 
											
												
													
														|  | 
 |  | +			payload: buffer,
 | 
											
												
													
														|  | 
 |  | +			session: session,
 | 
											
												
													
														|  | 
 |  | +		})
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  func (this *UDPHub) Running() bool {
 |  |  func (this *UDPHub) Running() bool {
 | 
											
												
													
														|  | -	this.RLock()
 |  | 
 | 
											
												
													
														|  | -	defer this.RUnlock()
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -	return this.accepting
 |  | 
 | 
											
												
													
														|  | 
 |  | +	return !this.cancel.Cancelled()
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  // Connection return the net.Conn underneath this hub.
 |  |  // Connection return the net.Conn underneath this hub.
 |