Skip to content
This repository has been archived by the owner on Mar 17, 2024. It is now read-only.

Commit

Permalink
修复trojan传输ipv6的udp数据时出错的问题,关联#136
Browse files Browse the repository at this point in the history
这是一个愚蠢的错误,将v2ray类型的定义用在了trojan上
  • Loading branch information
e1732a364fed committed Dec 3, 2022
1 parent 4f82db8 commit d4ec27f
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 47 deletions.
2 changes: 1 addition & 1 deletion examples/trojan.client.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ port = 10800
#sniffing.enabled = true

[[dial]]
protocol = "trojans" # 还是,为了简便,直接加了尾缀s 表示使用tls. 虽然trojan文档说强制tls, 但是vs很灵活, 自行可以选择是否开启tls.
protocol = "trojans" # 为了简便,直接加了尾缀s 表示使用tls. 虽然trojan文档说强制tls, 但是vs很灵活, 自行可以选择是否开启tls.
uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" # trojan的"password",我们填写到uuid项里. 实际上trojan这个password不要求格式, 所以你可以乱写,甚至可以写成一个中文字符串, 不过我们作为示例就统一用 示例的uuid了
ip = "127.0.0.1"
host = "your-domain-name.com" # trojan-go的 服务端 要求 客户端 指定一个sni 并与服务端的配置相匹配, 否则trojan-go的服务端 会拒绝连接
Expand Down
107 changes: 80 additions & 27 deletions netLayer/addr.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const (
AtypIP6 byte = 3
)

//默认netLayer的 AType (AtypIP4,AtypIP6,AtypDomain) 遵循v2ray标准的定义;
// 默认netLayer的 AType (AtypIP4,AtypIP6,AtypDomain) 遵循v2ray标准的定义;
// 如果需要符合 socks5/trojan标准, 需要用本函数转换一下。
// 即从 123 转换到 134
func ATypeToSocks5Standard(atype byte) byte {
Expand Down Expand Up @@ -63,7 +63,7 @@ func init() {
}
}

//if mustValid is true, a valid port is assured.
// if mustValid is true, a valid port is assured.
// isudp is used to determine whether you want to use udp.
// depth 填0 即可,用于递归。
func RandPort(mustValid, isudp bool, depth int) (p int) {
Expand Down Expand Up @@ -128,7 +128,7 @@ func RandPort(mustValid, isudp bool, depth int) (p int) {
return
}

//use a new seed each time called
// use a new seed each time called
func RandPortStr_safe(mustValid, isudp bool) string {
rand.Seed(time.Now().UnixNano())
return strconv.Itoa(RandPort(mustValid, isudp, 0))
Expand Down Expand Up @@ -166,7 +166,7 @@ func NewAddrFromTCPAddr(addr *net.TCPAddr) Addr {
}
}

//addrStr格式一般为 host:port ;如果不含冒号,将直接认为该字符串是域名或文件名
// addrStr格式一般为 host:port ;如果不含冒号,将直接认为该字符串是域名或文件名
func NewAddr(addrStr string) (Addr, error) {
if !strings.Contains(addrStr, ":") {
//unix domain socket, or 域名默认端口的情况
Expand All @@ -176,7 +176,7 @@ func NewAddr(addrStr string) (Addr, error) {
return NewAddrByHostPort(addrStr)
}

//hostPortStr格式 必须为 host:port,本函数不对此检查
// hostPortStr格式 必须为 host:port,本函数不对此检查
func NewAddrByHostPort(hostPortStr string) (Addr, error) {
host, portStr, err := net.SplitHostPort(hostPortStr)
if err != nil {
Expand Down Expand Up @@ -204,40 +204,85 @@ func NewAddrByHostPort(hostPortStr string) (Addr, error) {
// 不支持unix domain socket, 因为它是文件路径, / 还需要转义,太麻烦;不是我们代码麻烦, 而是怕用户嫌麻烦
func NewAddrByURL(addrStr string) (Addr, error) {

u, err := url.Parse(addrStr)
if err != nil {
return Addr{}, err
/*
u, err := url.Parse(addrStr)
if err != nil {
return Addr{}, err
}
if u.Scheme == "unix" {
return Addr{}, errors.New("parse unix domain socket by url is not supported")
}
addrStr = u.Host
host, portStr, err := net.SplitHostPort(addrStr)
if err != nil {
return Addr{}, err
}
if host == "" {
host = "127.0.0.1"
}
port, err := strconv.Atoi(portStr)
if err != nil {
return Addr{}, err
}
a := Addr{Port: port}
if ip := net.ParseIP(host); ip != nil {
a.IP = ip
} else {
a.Name = host
}
a.Network = u.Scheme
return a, nil
*/
//后来发现,这种方式无法应用于ipv6的情况,因为ipv6必须要给出中括号,
// 但是中括号会被url转义, 而我们作为配置字符串,不需要转义

isUDP := false
if strings.HasPrefix(addrStr, "tcp://") {

} else if strings.HasPrefix(addrStr, "udp://") {
isUDP = true
} else {
return Addr{}, errors.New("unsupported url")

}
if u.Scheme == "unix" {
return Addr{}, errors.New("parse unix domain socket by url is not supported")

addrStr = addrStr[6:]

lastColonIndex := strings.LastIndex(addrStr, ":")
if lastColonIndex == -1 {
return Addr{}, errors.New("unsupported url")
}
addrStr = u.Host
host := addrStr[:lastColonIndex]

host, portStr, err := net.SplitHostPort(addrStr)
portStr := addrStr[lastColonIndex+1:]

port, err := strconv.Atoi(portStr)
if err != nil {
return Addr{}, err
}
if host == "" {
host = "127.0.0.1"
}
port, err := strconv.Atoi(portStr)
if err != nil {
return Addr{}, err
}

a := Addr{Port: port}
if ip := net.ParseIP(host); ip != nil {
a.IP = ip
} else {
a.Name = host
}

a.Network = u.Scheme

if isUDP {
a.Network = "udp"
} else {
a.Network = "tcp"
}
return a, nil
}

//会根据thing的类型 生成实际addr; 可以为数字端口, 或 类似tcp://ip:port的url, 或ip:port字符串,或一个 文件路径(unix domain socket), or *net.TCPAddr / *net.UDPAddr / net.Addr
// 会根据thing的类型 生成实际addr; 可以为数字端口, 或 类似tcp://ip:port的url, 或ip:port字符串,或一个 文件路径(unix domain socket), or *net.TCPAddr / *net.UDPAddr / net.Addr
func NewAddrFromAny(thing any) (addr Addr, err error) {
var integer int
var dest_type byte = 0 //0: port, 1: ip:port, 2: unix domain socket
Expand Down Expand Up @@ -396,15 +441,22 @@ func (a *Addr) String() string {
if a.IP == nil {
return net.JoinHostPort(a.Name, port)
}

return net.JoinHostPort(a.IP.String(), port)
}

}

//返回以url表示的 地址. unix的话文件名若带斜杠则会被转义
// 返回以url表示的 地址. unix的话文件名若带斜杠则会被转义。ipv6的中括号不会被转义
func (a *Addr) UrlString() string {
if a.Network != "" {
return a.Network + "://" + url.PathEscape(a.String())
if a.Network == "unix" {
return a.Network + "://" + url.PathEscape(a.String())

} else {
return a.Network + "://" + a.String()

}

} else {
return "tcp://" + a.String()
Expand All @@ -431,12 +483,12 @@ func (a *Addr) GetNetIPAddr() (na netip.Addr) {
return
}

//a.Network == "udp", "udp4", "udp6"
// a.Network == "udp", "udp4", "udp6"
func (a *Addr) IsUDP() bool {
return IsStrUDP_network(a.Network)
}

//如果a里只含有域名,则会自动解析域名为IP。注意,若出现错误,则会返回nil
// 如果a里只含有域名,则会自动解析域名为IP。注意,若出现错误,则会返回nil
func (a *Addr) ToUDPAddr() *net.UDPAddr {
ua, err := net.ResolveUDPAddr("udp", a.String())
if err != nil {
Expand Down Expand Up @@ -491,8 +543,9 @@ func (a *Addr) AddressBytes() (addr []byte, atyp byte) {
}

// ParseAddr 分析字符串,并按照特定方式返回 地址类型 atyp,地址数据 addr []byte,以及端口号,
// 如果解析出的地址是ip,则 addr返回 net.IP;
// 如果解析出的地址是 域名,则第一字节为域名总长度, 剩余字节为域名内容
//
// 如果解析出的地址是ip,则 addr返回 net.IP;
// 如果解析出的地址是 域名,则第一字节为域名总长度, 剩余字节为域名内容
func ParseStrToAddr(s string) (atyp byte, addr []byte, port_uint16 uint16, err error) {

var host string
Expand Down Expand Up @@ -555,7 +608,7 @@ func UDPAddr2AddrPort(ua *net.UDPAddr) netip.AddrPort {
return netip.AddrPortFrom(a, uint16(ua.Port))
}

//依照 vmess/vless 协议的格式 依次读取 地址的 port, 域名/ip 信息
// 依照 vmess/vless 协议的格式 依次读取 地址的 port, 域名/ip 信息
func V2rayGetAddrFrom(buf utils.ByteReader) (addr Addr, err error) {

pb1, err := buf.ReadByte()
Expand Down
9 changes: 4 additions & 5 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ import (
// default recommended handshake read timeout
const CommonReadTimeout = time.Second * 4

//set read timeout after CommonReadTimeout
// set read timeout after CommonReadTimeout
func SetCommonReadTimeout(c net.Conn) error {
return c.SetReadDeadline(time.Now().Add(CommonReadTimeout))
}

//规定,如果 proxy的server的handshake如果返回的是具有内层mux的连接,该连接要实现 MuxMarker 接口.
// 规定,如果 proxy的server的handshake如果返回的是具有内层mux的连接,该连接要实现 MuxMarker 接口.
type MuxMarker interface {
io.ReadWriteCloser
IsMux()
}

//实现 MuxMarker
// 实现 MuxMarker
type MuxMarkerConn struct {
netLayer.ReadWrapper
}
Expand Down Expand Up @@ -78,7 +78,7 @@ type UserClient interface {
type Server interface {
BaseInterface

//ReadWriteCloser is for TCP request, net.PacketConn is for UDP request.
//net.Conn is for TCP request, netLayer.MsgConn is for UDP request.
// 约定,如果error返回的是 utils.ErrHandled, 则调用代码停止进一步处理。
Handshake(underlay net.Conn) (net.Conn, netLayer.MsgConn, netLayer.Addr, error)

Expand All @@ -95,7 +95,6 @@ type UserServer interface {
// We think tcp/udp/kcp/raw_socket is FirstName,protocol of the proxy is LastName, and the rest is MiddleName。
//
// An Example of a full name: tcp+tls+ws+vless.
// 总之,类似【域名】的规则,只不过分隔符从 点号 变成了加号, 且层级关系是从左到右。
func GetFullName(pc BaseInterface) string {
if n := pc.Name(); n == DirectName {
return n
Expand Down
7 changes: 5 additions & 2 deletions proxy/trojan/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,16 @@ func (c *Client) GetUser() utils.User {
func WriteAddrToBuf(target netLayer.Addr, buf *bytes.Buffer) {
if len(target.IP) > 0 {
if ip4 := target.IP.To4(); ip4 == nil {
buf.WriteByte(netLayer.AtypIP6)

buf.WriteByte(ATypIP6)
buf.Write(target.IP)
} else {
buf.WriteByte(netLayer.AtypIP4)

buf.WriteByte(ATypIP4)
buf.Write(ip4)
}
} else if l := len(target.Name); l > 0 {

buf.WriteByte(ATypDomain)
buf.WriteByte(byte(l))
buf.WriteString(target.Name)
Expand Down
24 changes: 12 additions & 12 deletions proxy/trojan/trojan.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//package trojan implements trojan protocol for proxy.Client and proxy.Server.
// package trojan implements trojan protocol for proxy.Client and proxy.Server.
//
//See https://trojan-gfw.github.io/trojan/protocol .
// See https://trojan-gfw.github.io/trojan/protocol .
package trojan

import (
Expand Down Expand Up @@ -38,15 +38,15 @@ var (
crlf = []byte{0x0d, 0x0a}
)

//即trojan 的 任意长度密码 的 定长二进制表示。
// 即trojan 的 任意长度密码 的 定长二进制表示。
func SHA224(password string) (r [passBytesLen]byte) {
hash := sha256.New224()
hash.Write([]byte(password))
copy(r[:], hash.Sum(nil))
return
}

//56字节数据转28字节字符串
// 56字节数据转28字节字符串
func PassStrToBytes(str string) []byte {
bs, err := hex.DecodeString(str)
if err != nil {
Expand All @@ -56,7 +56,7 @@ func PassStrToBytes(str string) []byte {
return bs
}

//28字节数据转56字节字符串
// 28字节数据转56字节字符串
func PassBytesToStr(bs []byte) string {
return hex.EncodeToString(bs)
}
Expand All @@ -76,22 +76,22 @@ func NewUserByPlainTextPassword(plainPass string) User {
}
}

//明文密码
// 明文密码
func (u User) IdentityStr() string {
return u.plainStr
}

//明文密码
// 明文密码
func (u User) IdentityBytes() []byte {
return []byte(u.plainStr)
}

//56字节hex
// 56字节hex
func (u User) AuthStr() string {
return u.hexStr
}

//28字节纯二进制
// 28字节纯二进制
func (u User) AuthBytes() []byte {
return u.hexBs
}
Expand All @@ -105,7 +105,7 @@ func InitUsers(uc []utils.UserConf) (us []utils.User) {
return
}

//trojan协议 的前56字节 是 sha224的28字节 每字节 转义成 base 16, with lower-case letters for a-f 的 两个字符。
// trojan协议 的前56字节 是 sha224的28字节 每字节 转义成 base 16, with lower-case letters for a-f 的 两个字符。
// 实际上trojan协议文档写的不严谨,它只说了用hex,没说用大写还是小写。我看它代码实现用的是小写。
func SHA224_hexStringBytes(password string) []byte {
hash := sha256.New224()
Expand All @@ -123,7 +123,7 @@ func SHA224_hexString(password string) string {
return PassBytesToStr(bs)
}

//依照trojan协议的格式读取 地址的域名、ip、port信息
// 依照trojan协议的格式读取 地址的域名、ip、port信息
func GetAddrFrom(buf utils.ByteReader, ismux bool) (addr netLayer.Addr, err error) {
var b1 byte

Expand Down Expand Up @@ -212,7 +212,7 @@ func GetAddrFrom(buf utils.ByteReader, ismux bool) (addr netLayer.Addr, err erro
return
}

//https://p4gefau1t.github.io/trojan-go/developer/url/
// https://p4gefau1t.github.io/trojan-go/developer/url/
func GenerateOfficialDraftShareURL(dialconf *proxy.DialConf) string {

var u url.URL
Expand Down

0 comments on commit d4ec27f

Please sign in to comment.