Skip to content

Commit

Permalink
feat: support udp associate
Browse files Browse the repository at this point in the history
  • Loading branch information
SunBK201 committed Mar 4, 2024
1 parent 34baa9e commit 467fc51
Show file tree
Hide file tree
Showing 28 changed files with 154 additions and 61 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,6 @@ rules:
- [x] 支持 LuCI
- [x] 优化部署流程
- [ ] 支持 SOCK5 Auth
- [ ] 支持 UDP
- [x] 支持 UDP
- [ ] 支持 IPv6
- [ ] 性能提升
22 changes: 11 additions & 11 deletions bin/sha1sum.txt
Original file line number Diff line number Diff line change
@@ -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 removed bin/ua3f-0.2.3-386.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-amd64.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-arm.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-arm64.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-armv7.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-armv8.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-mips64.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-mipsle-hardfloat.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-mipsle-softfloat.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-mipsle.tar.gz
Binary file not shown.
Binary file removed bin/ua3f-0.2.3-riscv64.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-386.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-amd64.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-arm.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-arm64.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-armv7.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-armv8.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-mips64.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-mipsle-hardfloat.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-mipsle-softfloat.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-mipsle.tar.gz
Binary file not shown.
Binary file added bin/ua3f-0.3.0-riscv64.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
@@ -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
Expand Down
180 changes: 136 additions & 44 deletions cmd/ua3f.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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")
Expand Down
7 changes: 4 additions & 3 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion luci/cbi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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.
]]
Expand Down

0 comments on commit 467fc51

Please sign in to comment.