mirror of
https://github.com/luscis/openlan.git
synced 2025-10-06 09:06:54 +08:00
254 lines
5.4 KiB
Go
Executable File
254 lines
5.4 KiB
Go
Executable File
package ss
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/shadowsocks/go-shadowsocks2/socks"
|
|
)
|
|
|
|
type mode int
|
|
|
|
const (
|
|
remoteServer mode = iota
|
|
relayClient
|
|
socksClient
|
|
)
|
|
|
|
const udpBufSize = 64 * 1024
|
|
|
|
// Listen on laddr for UDP packets, encrypt and send to server to reach target.
|
|
func udpLocal(laddr, server, target string, shadow func(net.PacketConn) net.PacketConn) {
|
|
srvAddr, err := net.ResolveUDPAddr("udp", server)
|
|
if err != nil {
|
|
logf("UDP server address error: %v", err)
|
|
return
|
|
}
|
|
|
|
tgt := socks.ParseAddr(target)
|
|
if tgt == nil {
|
|
err = fmt.Errorf("invalid target address: %q", target)
|
|
logf("UDP target address error: %v", err)
|
|
return
|
|
}
|
|
|
|
c, err := net.ListenPacket("udp", laddr)
|
|
if err != nil {
|
|
logf("UDP local listen error: %v", err)
|
|
return
|
|
}
|
|
defer c.Close()
|
|
|
|
nm := newNATmap(config.UDPTimeout)
|
|
buf := make([]byte, udpBufSize)
|
|
copy(buf, tgt)
|
|
|
|
logf("UDP tunnel %s <-> %s <-> %s", laddr, server, target)
|
|
for {
|
|
n, raddr, err := c.ReadFrom(buf[len(tgt):])
|
|
if err != nil {
|
|
logf("UDP local read error: %v", err)
|
|
continue
|
|
}
|
|
|
|
pc := nm.Get(raddr.String())
|
|
if pc == nil {
|
|
pc, err = net.ListenPacket("udp", "")
|
|
if err != nil {
|
|
logf("UDP local listen error: %v", err)
|
|
continue
|
|
}
|
|
|
|
pc = shadow(pc)
|
|
nm.Add(raddr, c, pc, relayClient)
|
|
}
|
|
|
|
_, err = pc.WriteTo(buf[:len(tgt)+n], srvAddr)
|
|
if err != nil {
|
|
logf("UDP local write error: %v", err)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// Listen on laddr for Socks5 UDP packets, encrypt and send to server to reach target.
|
|
func udpSocksLocal(laddr, server string, shadow func(net.PacketConn) net.PacketConn) {
|
|
srvAddr, err := net.ResolveUDPAddr("udp", server)
|
|
if err != nil {
|
|
logf("UDP server address error: %v", err)
|
|
return
|
|
}
|
|
|
|
c, err := net.ListenPacket("udp", laddr)
|
|
if err != nil {
|
|
logf("UDP local listen error: %v", err)
|
|
return
|
|
}
|
|
defer c.Close()
|
|
|
|
nm := newNATmap(config.UDPTimeout)
|
|
buf := make([]byte, udpBufSize)
|
|
|
|
for {
|
|
n, raddr, err := c.ReadFrom(buf)
|
|
if err != nil {
|
|
logf("UDP local read error: %v", err)
|
|
continue
|
|
}
|
|
|
|
pc := nm.Get(raddr.String())
|
|
if pc == nil {
|
|
pc, err = net.ListenPacket("udp", "")
|
|
if err != nil {
|
|
logf("UDP local listen error: %v", err)
|
|
continue
|
|
}
|
|
logf("UDP socks tunnel %s <-> %s <-> %s", laddr, server, socks.Addr(buf[3:]))
|
|
pc = shadow(pc)
|
|
nm.Add(raddr, c, pc, socksClient)
|
|
}
|
|
|
|
_, err = pc.WriteTo(buf[3:n], srvAddr)
|
|
if err != nil {
|
|
logf("UDP local write error: %v", err)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// Listen on addr for encrypted packets and basically do UDP NAT.
|
|
func udpRemote(addr string, shadow func(net.PacketConn) net.PacketConn) {
|
|
c, err := net.ListenPacket("udp", addr)
|
|
if err != nil {
|
|
logf("UDP remote listen error: %v", err)
|
|
return
|
|
}
|
|
defer c.Close()
|
|
c = shadow(c)
|
|
|
|
nm := newNATmap(config.UDPTimeout)
|
|
buf := make([]byte, udpBufSize)
|
|
|
|
logf("listening UDP on %s", addr)
|
|
for {
|
|
n, raddr, err := c.ReadFrom(buf)
|
|
if err != nil {
|
|
logf("UDP remote read error: %v", err)
|
|
continue
|
|
}
|
|
|
|
tgtAddr := socks.SplitAddr(buf[:n])
|
|
if tgtAddr == nil {
|
|
logf("failed to split target address from packet: %q", buf[:n])
|
|
continue
|
|
}
|
|
|
|
tgtUDPAddr, err := net.ResolveUDPAddr("udp", tgtAddr.String())
|
|
if err != nil {
|
|
logf("failed to resolve target UDP address: %v", err)
|
|
continue
|
|
}
|
|
|
|
payload := buf[len(tgtAddr):n]
|
|
|
|
pc := nm.Get(raddr.String())
|
|
if pc == nil {
|
|
pc, err = net.ListenPacket("udp", "")
|
|
if err != nil {
|
|
logf("UDP remote listen error: %v", err)
|
|
continue
|
|
}
|
|
|
|
nm.Add(raddr, c, pc, remoteServer)
|
|
}
|
|
|
|
_, err = pc.WriteTo(payload, tgtUDPAddr) // accept only UDPAddr despite the signature
|
|
if err != nil {
|
|
logf("UDP remote write error: %v", err)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// Packet NAT table
|
|
type natmap struct {
|
|
sync.RWMutex
|
|
m map[string]net.PacketConn
|
|
timeout time.Duration
|
|
}
|
|
|
|
func newNATmap(timeout time.Duration) *natmap {
|
|
m := &natmap{}
|
|
m.m = make(map[string]net.PacketConn)
|
|
m.timeout = timeout
|
|
return m
|
|
}
|
|
|
|
func (m *natmap) Get(key string) net.PacketConn {
|
|
m.RLock()
|
|
defer m.RUnlock()
|
|
return m.m[key]
|
|
}
|
|
|
|
func (m *natmap) Set(key string, pc net.PacketConn) {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
m.m[key] = pc
|
|
}
|
|
|
|
func (m *natmap) Del(key string) net.PacketConn {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
pc, ok := m.m[key]
|
|
if ok {
|
|
delete(m.m, key)
|
|
return pc
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *natmap) Add(peer net.Addr, dst, src net.PacketConn, role mode) {
|
|
m.Set(peer.String(), src)
|
|
|
|
go func() {
|
|
timedCopy(dst, peer, src, m.timeout, role)
|
|
if pc := m.Del(peer.String()); pc != nil {
|
|
pc.Close()
|
|
}
|
|
}()
|
|
}
|
|
|
|
// copy from src to dst at target with read timeout
|
|
func timedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration, role mode) error {
|
|
buf := make([]byte, udpBufSize)
|
|
|
|
for {
|
|
src.SetReadDeadline(time.Now().Add(timeout))
|
|
n, raddr, err := src.ReadFrom(buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch role {
|
|
case remoteServer: // server -> client: add original packet source
|
|
srcAddr := socks.ParseAddr(raddr.String())
|
|
copy(buf[len(srcAddr):], buf[:n])
|
|
copy(buf, srcAddr)
|
|
_, err = dst.WriteTo(buf[:len(srcAddr)+n], target)
|
|
case relayClient: // client -> user: strip original packet source
|
|
srcAddr := socks.SplitAddr(buf[:n])
|
|
_, err = dst.WriteTo(buf[len(srcAddr):n], target)
|
|
case socksClient: // client -> socks5 program: just set RSV and FRAG = 0
|
|
_, err = dst.WriteTo(append([]byte{0, 0, 0}, buf[:n]...), target)
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|