mirror of
https://github.com/SunBK201/UA3F.git
synced 2025-12-16 16:57:08 +00:00
feat: support udp associate
This commit is contained in:
parent
41ff05c4b6
commit
b624435a6c
@ -109,6 +109,6 @@ rules:
|
|||||||
- [x] 支持 LuCI
|
- [x] 支持 LuCI
|
||||||
- [x] 优化部署流程
|
- [x] 优化部署流程
|
||||||
- [ ] 支持 SOCK5 Auth
|
- [ ] 支持 SOCK5 Auth
|
||||||
- [ ] 支持 UDP
|
- [x] 支持 UDP
|
||||||
- [ ] 支持 IPv6
|
- [ ] 支持 IPv6
|
||||||
- [ ] 性能提升
|
- [ ] 性能提升
|
||||||
2
build.sh
2
build.sh
@ -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
|
||||||
|
|||||||
180
cmd/ua3f.go
180
cmd/ua3f.go
@ -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")
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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.
|
||||||
]]
|
]]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user