feat: support udp associate

This commit is contained in:
SunBK201 2024-03-05 00:27:13 +08:00
parent 34baa9ecde
commit 467fc515f7
28 changed files with 154 additions and 61 deletions

View File

@ -109,6 +109,6 @@ rules:
- [x] 支持 LuCI - [x] 支持 LuCI
- [x] 优化部署流程 - [x] 优化部署流程
- [ ] 支持 SOCK5 Auth - [ ] 支持 SOCK5 Auth
- [ ] 支持 UDP - [x] 支持 UDP
- [ ] 支持 IPv6 - [ ] 支持 IPv6
- [ ] 性能提升 - [ ] 性能提升

View File

@ -1,11 +1,11 @@
971e6a8bac9a149c4c51bf8ae3b2a4bb ./ua3f-0.2.3-386.tar.gz 2e956b0acef25c77b05862242e00e839 ./ua3f-0.3.0-386.tar.gz
0c6b28a26fb7fd496c2596fad56d806f ./ua3f-0.2.3-amd64.tar.gz 0365ecb622ad8767b61338de24a94eb0 ./ua3f-0.3.0-amd64.tar.gz
eb62d97cbedb95c5749d31840f300cfe ./ua3f-0.2.3-arm.tar.gz b17d821a1e59baba0a169e94e14aa719 ./ua3f-0.3.0-arm.tar.gz
f0ed5bd4bf7d735988d184e12842b101 ./ua3f-0.2.3-arm64.tar.gz 651e4b7e66650b967e0a6a6af00e7201 ./ua3f-0.3.0-arm64.tar.gz
b997aec1bbc18f0d8bce860162580d31 ./ua3f-0.2.3-armv7.tar.gz 76f927277bb20b89253e0fe70e8d53c4 ./ua3f-0.3.0-armv7.tar.gz
f0ed5bd4bf7d735988d184e12842b101 ./ua3f-0.2.3-armv8.tar.gz 651e4b7e66650b967e0a6a6af00e7201 ./ua3f-0.3.0-armv8.tar.gz
16c0486a4753e7e6d4dd18ca99be948f ./ua3f-0.2.3-mips64.tar.gz ddc6d9ad7c3171dc5a972c3c83aa670e ./ua3f-0.3.0-mips64.tar.gz
a99e0855e1f42a1b9f316bb7e210cf4e ./ua3f-0.2.3-mipsle-hardfloat.tar.gz cdbc9b71b1f7daed024e98703e0aa9c1 ./ua3f-0.3.0-mipsle-hardfloat.tar.gz
b3c68980761d58b1242a160aae91f1d8 ./ua3f-0.2.3-mipsle-softfloat.tar.gz 3c514bb0c3a72bc5a89045328746576f ./ua3f-0.3.0-mipsle-softfloat.tar.gz
bff8cecc43baa286bdb0ea153ec0144e ./ua3f-0.2.3-mipsle.tar.gz ec9e3671cdeedd9150724968689e3afc ./ua3f-0.3.0-mipsle.tar.gz
f130bee316c952f039e582fff0831088 ./ua3f-0.2.3-riscv64.tar.gz 37b6239381190e8dbc695e6f8b08cbf8 ./ua3f-0.3.0-riscv64.tar.gz

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
bin/ua3f-0.3.0-386.tar.gz Normal file

Binary file not shown.

BIN
bin/ua3f-0.3.0-amd64.tar.gz Normal file

Binary file not shown.

BIN
bin/ua3f-0.3.0-arm.tar.gz Normal file

Binary file not shown.

BIN
bin/ua3f-0.3.0-arm64.tar.gz Normal file

Binary file not shown.

BIN
bin/ua3f-0.3.0-armv7.tar.gz Normal file

Binary file not shown.

BIN
bin/ua3f-0.3.0-armv8.tar.gz Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
project_name="ua3f" project_name="ua3f"
release_version="0.2.3" release_version="0.3.0"
target=cmd/ua3f.go target=cmd/ua3f.go
release_dir=./bin release_dir=./bin

View File

@ -17,7 +17,7 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
var version = "0.2.3" var version = "0.3.0"
var payloadByte []byte var payloadByte []byte
var cache *expirable.LRU[string, string] var cache *expirable.LRU[string, string]
var HTTP_METHOD = []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS", "TRACE", "CONNECT"} var HTTP_METHOD = []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS", "TRACE", "CONNECT"}
@ -91,6 +91,12 @@ func process(client net.Conn) {
} }
target, destAddrPort, err := Socks5Connect(client) target, destAddrPort, err := Socks5Connect(client)
if err != nil { if err != nil {
// UDP
if strings.Contains(err.Error(), "UDP Associate") {
Socks5UDP(client)
client.Close()
return
}
logrus.Error("Connect failed: ", err) logrus.Error("Connect failed: ", err)
client.Close() client.Close()
return return
@ -128,48 +134,134 @@ func Socks5Auth(client net.Conn) (err error) {
return nil return nil
} }
// func Socks5UDP() { func isAlive(conn net.Conn) bool {
// https://datatracker.ietf.org/doc/html/rfc1928 one := make([]byte, 1)
// // _, _ = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x7f, 0, 0, 0x1, 0x04, 0x38}) conn.SetReadDeadline(time.Now().Add(time.Second * 5))
// _, _ = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0x04, 0x38}) _, err := conn.Read(one)
// server, _ := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 1080}) if err != nil {
// _, _ = server.Read(buf[:4]) if err == io.EOF {
// frag, atyp := buf[2], buf[3] logrus.Debug(fmt.Sprintf("[%s] isAlive: EOF", conn.RemoteAddr().String()))
// addr := "" return false
// switch atyp { } else if strings.Contains(err.Error(), "use of closed network connection") {
// case 1: logrus.Debug(fmt.Sprintf("[%s] isAlive: closed", conn.RemoteAddr().String()))
// n, err = server.Read(buf[:4]) return false
// if n != 4 { } else if strings.Contains(err.Error(), "i/o timeout") {
// return nil, "", errors.New("invalid IPv4:" + err.Error()) logrus.Debug(fmt.Sprintf("[%s] isAlive: timeout", conn.RemoteAddr().String()))
// } return true
// addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]) } else {
// case 3: logrus.Debug(fmt.Sprintf("[%s] isAlive: %s", conn.RemoteAddr().String(), err.Error()))
// n, err = server.Read(buf[:1]) return false
// if n != 1 { }
// return nil, "", errors.New("invalid hostname:" + err.Error()) }
// } conn.SetReadDeadline(time.Time{})
// addrLen := int(buf[0]) return true
// n, err = server.Read(buf[:addrLen]) }
// if n != addrLen {
// return nil, "", errors.New("invalid hostname:" + err.Error()) func Socks5UDP(client net.Conn) {
// } udpserver, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
// addr = string(buf[:addrLen]) if err != nil {
// case 4: logrus.Error(fmt.Sprintf("[%s][UDP] ListenUDP failed: %s", client.RemoteAddr().String(), err.Error()))
// return nil, "", errors.New("IPv6: no supported yet") return
// default: }
// return nil, "", errors.New("invalid atyp") _, port, _ := net.SplitHostPort(udpserver.LocalAddr().String())
// } logrus.Debug(fmt.Sprintf("[%s][UDP] ListenUDP on %s", client.RemoteAddr().String(), port))
// n, err = server.Read(buf[:2]) portInt, _ := net.LookupPort("udp", port)
// port := binary.BigEndian.Uint16(buf[:2]) portBytes := make([]byte, 2)
// destAddrPort := fmt.Sprintf("%s:%d", addr, port) binary.BigEndian.PutUint16(portBytes, uint16(portInt))
// logrus.Debug(fmt.Sprintf("Connecting %s", destAddrPort)) _, err = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, portBytes[0], portBytes[1]})
// dest, err := net.Dial("udp", destAddrPort) if err != nil {
// if err != nil { logrus.Error(fmt.Sprintf("[%s][UDP] Write rsp failed: %s", client.RemoteAddr().String(), err.Error()))
// return nil, destAddrPort, errors.New("dial dst:" + err.Error()) return
// } }
// logrus.Debug(fmt.Sprintf("Connected %s", destAddrPort)) buf := make([]byte, 65535)
// udpPortMap := make(map[string][]byte)
// } var clientAddr *net.UDPAddr
var isDomain bool = false
for {
udpserver.SetReadDeadline(time.Now().Add(time.Second * 10))
n, fromAddr, err := udpserver.ReadFromUDP(buf)
if err != nil {
if strings.Contains(err.Error(), "i/o timeout") {
logrus.Debug(fmt.Sprintf("[%s][UDP] ReadFromUDP failed: %s", client.RemoteAddr().String(), err.Error()))
if !isAlive(client) {
logrus.Debug(fmt.Sprintf("[%s][UDP] client is not alive", client.RemoteAddr().String()))
udpserver.Close()
return
}
} else {
logrus.Error(fmt.Sprintf("[%s][UDP] ReadFromUDP failed: %s", client.RemoteAddr().String(), err.Error()))
}
continue
}
if clientAddr == nil {
clientAddr = fromAddr
}
if clientAddr.IP.Equal(fromAddr.IP) && clientAddr.Port == fromAddr.Port {
// from client
atyp := buf[3]
targetAddr := ""
var targetPort uint16 = 0
var payload []byte
var header []byte
var targetIP net.IP
if atyp == 1 {
isDomain = false
targetAddr = fmt.Sprintf("%d.%d.%d.%d", buf[4], buf[5], buf[6], buf[7])
targetIP = net.ParseIP(targetAddr)
targetPort = binary.BigEndian.Uint16(buf[8:10])
payload = buf[10:n]
header = buf[0:10]
} else if atyp == 3 {
isDomain = true
addrLen := int(buf[4])
targetAddr = string(buf[5 : 5+addrLen])
targetIPaddr, err := net.ResolveIPAddr("ip", targetAddr)
if err != nil {
logrus.Error(fmt.Sprintf("[%s][UDP] ResolveIPAddr failed: %s", client.RemoteAddr().String(), err.Error()))
continue
}
targetIP = targetIPaddr.IP
targetPort = binary.BigEndian.Uint16(buf[5+addrLen : 5+addrLen+2])
payload = buf[5+addrLen+2 : n]
header = buf[0 : 5+addrLen+2]
} else if atyp == 4 {
logrus.Error(fmt.Sprintf("[%s][UDP] IPv6: no supported yet", client.RemoteAddr().String()))
continue
} else {
logrus.Error(fmt.Sprintf("[%s][UDP] invalid atyp", client.RemoteAddr().String()))
continue
}
// targetAddrPort := fmt.Sprintf("%s:%d", targetAddr, targetPort)
remoteAddr := &net.UDPAddr{IP: targetIP, Port: int(targetPort)}
udpPortMap[remoteAddr.String()] = make([]byte, len(header))
copy(udpPortMap[remoteAddr.String()], header)
udpserver.SetWriteDeadline(time.Now().Add(time.Second * 10))
if _, err = udpserver.WriteToUDP(payload, remoteAddr); err != nil {
logrus.Error(fmt.Sprintf("[%s][UDP] WriteToUDP failed: %s", client.RemoteAddr().String(), err.Error()))
continue
}
} else {
// from remote
fmt.Print(fromAddr.String())
header := udpPortMap[fromAddr.String()]
if header == nil {
logrus.Error(fmt.Sprintf("[%s][UDP] udpPortMap invalid header", client.RemoteAddr().String()))
continue
}
// header + body
if isDomain {
header = header[0:4]
}
body := append(header, buf[:n]...)
if _, err = udpserver.WriteToUDP(body, clientAddr); err != nil {
logrus.Error(fmt.Sprintf("[%s][UDP] WriteToUDP failed: %s", client.RemoteAddr().String(), err.Error()))
continue
}
}
}
}
func Socks5Connect(client net.Conn) (net.Conn, string, error) { func Socks5Connect(client net.Conn) (net.Conn, string, error) {
buf := make([]byte, 256) buf := make([]byte, 256)
@ -182,7 +274,7 @@ func Socks5Connect(client net.Conn) (net.Conn, string, error) {
return nil, "", errors.New("invalid ver") return nil, "", errors.New("invalid ver")
} }
if cmd == 3 { if cmd == 3 {
return nil, "", errors.New("not support UDP") return nil, "", errors.New("UDP Associate")
} }
if cmd != 1 { if cmd != 1 {
return nil, "", errors.New("invalid cmd, only support connect") return nil, "", errors.New("invalid cmd, only support connect")

View File

@ -20,7 +20,7 @@ ckcmd() {
cd /root cd /root
getcpucore getcpucore
version=0.2.3 version=0.3.0
ua3f_tar=ua3f-$version-$cpucore.tar.gz ua3f_tar=ua3f-$version-$cpucore.tar.gz
if id -u shellclash >/dev/null 2>&1; then if id -u shellclash >/dev/null 2>&1; then
@ -91,11 +91,12 @@ fi
mkdir -p /usr/lib/lua/luci/controller mkdir -p /usr/lib/lua/luci/controller
mv controller.lua /usr/lib/lua/luci/controller/ua3f.lua mv controller.lua /usr/lib/lua/luci/controller/ua3f.lua
rm /tmp/luci-modulecache/* >/dev/null 2>&1 chmod +x /etc/config/ua3f >/dev/null 2>&1
rm /tmp/luci-indexcache* >/dev/null 2>&1
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "Install UA3F Success." echo "Install UA3F Success."
fi fi
rm /tmp/luci-modulecache/* >/dev/null 2>&1
rm /tmp/luci-indexcache* >/dev/null 2>&1
service ua3f.service reload >/dev/null 2>&1 service ua3f.service reload >/dev/null 2>&1

View File

@ -3,7 +3,7 @@ local uci = require("luci.model.uci").cursor()
ua3f = Map("ua3f", ua3f = Map("ua3f",
"UA3F", "UA3F",
[[ [[
<a href="https://github.com/SunBK201/UA3F" target="_blank">Version: 0.2.3</a> <a href="https://github.com/SunBK201/UA3F" target="_blank">Version: 0.3.0</a>
<br> <br>
Across the Campus we can reach every corner in the world. Across the Campus we can reach every corner in the world.
]] ]]