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] 优化部署流程
- [ ] 支持 SOCK5 Auth
- [ ] 支持 UDP
- [x] 支持 UDP
- [ ] 支持 IPv6
- [ ] 性能提升

View File

@ -1,11 +1,11 @@
971e6a8bac9a149c4c51bf8ae3b2a4bb ./ua3f-0.2.3-386.tar.gz
0c6b28a26fb7fd496c2596fad56d806f ./ua3f-0.2.3-amd64.tar.gz
eb62d97cbedb95c5749d31840f300cfe ./ua3f-0.2.3-arm.tar.gz
f0ed5bd4bf7d735988d184e12842b101 ./ua3f-0.2.3-arm64.tar.gz
b997aec1bbc18f0d8bce860162580d31 ./ua3f-0.2.3-armv7.tar.gz
f0ed5bd4bf7d735988d184e12842b101 ./ua3f-0.2.3-armv8.tar.gz
16c0486a4753e7e6d4dd18ca99be948f ./ua3f-0.2.3-mips64.tar.gz
a99e0855e1f42a1b9f316bb7e210cf4e ./ua3f-0.2.3-mipsle-hardfloat.tar.gz
b3c68980761d58b1242a160aae91f1d8 ./ua3f-0.2.3-mipsle-softfloat.tar.gz
bff8cecc43baa286bdb0ea153ec0144e ./ua3f-0.2.3-mipsle.tar.gz
f130bee316c952f039e582fff0831088 ./ua3f-0.2.3-riscv64.tar.gz
2e956b0acef25c77b05862242e00e839 ./ua3f-0.3.0-386.tar.gz
0365ecb622ad8767b61338de24a94eb0 ./ua3f-0.3.0-amd64.tar.gz
b17d821a1e59baba0a169e94e14aa719 ./ua3f-0.3.0-arm.tar.gz
651e4b7e66650b967e0a6a6af00e7201 ./ua3f-0.3.0-arm64.tar.gz
76f927277bb20b89253e0fe70e8d53c4 ./ua3f-0.3.0-armv7.tar.gz
651e4b7e66650b967e0a6a6af00e7201 ./ua3f-0.3.0-armv8.tar.gz
ddc6d9ad7c3171dc5a972c3c83aa670e ./ua3f-0.3.0-mips64.tar.gz
cdbc9b71b1f7daed024e98703e0aa9c1 ./ua3f-0.3.0-mipsle-hardfloat.tar.gz
3c514bb0c3a72bc5a89045328746576f ./ua3f-0.3.0-mipsle-softfloat.tar.gz
ec9e3671cdeedd9150724968689e3afc ./ua3f-0.3.0-mipsle.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
project_name="ua3f"
release_version="0.2.3"
release_version="0.3.0"
target=cmd/ua3f.go
release_dir=./bin

View File

@ -17,7 +17,7 @@ import (
"github.com/sirupsen/logrus"
)
var version = "0.2.3"
var version = "0.3.0"
var payloadByte []byte
var cache *expirable.LRU[string, string]
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)
if err != nil {
// UDP
if strings.Contains(err.Error(), "UDP Associate") {
Socks5UDP(client)
client.Close()
return
}
logrus.Error("Connect failed: ", err)
client.Close()
return
@ -128,48 +134,134 @@ func Socks5Auth(client net.Conn) (err error) {
return nil
}
// func Socks5UDP() {
// https://datatracker.ietf.org/doc/html/rfc1928
// // _, _ = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x7f, 0, 0, 0x1, 0x04, 0x38})
// _, _ = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0x04, 0x38})
// server, _ := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 1080})
// _, _ = server.Read(buf[:4])
// frag, atyp := buf[2], buf[3]
// addr := ""
// switch atyp {
// case 1:
// n, err = server.Read(buf[:4])
// if n != 4 {
// return nil, "", errors.New("invalid IPv4:" + err.Error())
// }
// addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
// case 3:
// n, err = server.Read(buf[:1])
// if n != 1 {
// return nil, "", errors.New("invalid hostname:" + err.Error())
// }
// addrLen := int(buf[0])
// n, err = server.Read(buf[:addrLen])
// if n != addrLen {
// return nil, "", errors.New("invalid hostname:" + err.Error())
// }
// addr = string(buf[:addrLen])
// case 4:
// return nil, "", errors.New("IPv6: no supported yet")
// default:
// return nil, "", errors.New("invalid atyp")
// }
// n, err = server.Read(buf[:2])
// port := binary.BigEndian.Uint16(buf[:2])
// destAddrPort := fmt.Sprintf("%s:%d", addr, port)
// logrus.Debug(fmt.Sprintf("Connecting %s", destAddrPort))
// dest, err := net.Dial("udp", destAddrPort)
// if err != nil {
// return nil, destAddrPort, errors.New("dial dst:" + err.Error())
// }
// logrus.Debug(fmt.Sprintf("Connected %s", destAddrPort))
//
// }
func isAlive(conn net.Conn) bool {
one := make([]byte, 1)
conn.SetReadDeadline(time.Now().Add(time.Second * 5))
_, err := conn.Read(one)
if err != nil {
if err == io.EOF {
logrus.Debug(fmt.Sprintf("[%s] isAlive: EOF", conn.RemoteAddr().String()))
return false
} else if strings.Contains(err.Error(), "use of closed network connection") {
logrus.Debug(fmt.Sprintf("[%s] isAlive: closed", conn.RemoteAddr().String()))
return false
} else if strings.Contains(err.Error(), "i/o timeout") {
logrus.Debug(fmt.Sprintf("[%s] isAlive: timeout", conn.RemoteAddr().String()))
return true
} else {
logrus.Debug(fmt.Sprintf("[%s] isAlive: %s", conn.RemoteAddr().String(), err.Error()))
return false
}
}
conn.SetReadDeadline(time.Time{})
return true
}
func Socks5UDP(client net.Conn) {
udpserver, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
if err != nil {
logrus.Error(fmt.Sprintf("[%s][UDP] ListenUDP failed: %s", client.RemoteAddr().String(), err.Error()))
return
}
_, port, _ := net.SplitHostPort(udpserver.LocalAddr().String())
logrus.Debug(fmt.Sprintf("[%s][UDP] ListenUDP on %s", client.RemoteAddr().String(), port))
portInt, _ := net.LookupPort("udp", port)
portBytes := make([]byte, 2)
binary.BigEndian.PutUint16(portBytes, uint16(portInt))
_, err = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, portBytes[0], portBytes[1]})
if err != nil {
logrus.Error(fmt.Sprintf("[%s][UDP] Write rsp failed: %s", client.RemoteAddr().String(), err.Error()))
return
}
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) {
buf := make([]byte, 256)
@ -182,7 +274,7 @@ func Socks5Connect(client net.Conn) (net.Conn, string, error) {
return nil, "", errors.New("invalid ver")
}
if cmd == 3 {
return nil, "", errors.New("not support UDP")
return nil, "", errors.New("UDP Associate")
}
if cmd != 1 {
return nil, "", errors.New("invalid cmd, only support connect")

View File

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

View File

@ -3,7 +3,7 @@ local uci = require("luci.model.uci").cursor()
ua3f = Map("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>
Across the Campus we can reach every corner in the world.
]]