| 85 | } |
| 86 | |
| 87 | func ParseTrojanLink(link string) (*Trojan, error) { |
| 88 | if !strings.HasPrefix(link, "trojan://") && !strings.HasPrefix(link, "trojan-go://") { |
| 89 | return nil, ErrorNotTrojanink |
| 90 | } |
| 91 | |
| 92 | /** |
| 93 | trojan-go:// |
| 94 | $(trojan-password) |
| 95 | @ |
| 96 | trojan-host |
| 97 | : |
| 98 | port |
| 99 | /? |
| 100 | sni=$(tls-sni.com)& |
| 101 | type=$(original|ws|h2|h2+ws)& |
| 102 | host=$(websocket-host.com)& |
| 103 | path=$(/websocket/path)& |
| 104 | encryption=$(ss;aes-256-gcm;ss-password)& |
| 105 | plugin=$(...) |
| 106 | #$(descriptive-text) |
| 107 | */ |
| 108 | |
| 109 | uri, err := url.Parse(link) |
| 110 | if err != nil { |
| 111 | return nil, ErrorNotSSLink |
| 112 | } |
| 113 | |
| 114 | password := uri.User.Username() |
| 115 | password, _ = url.QueryUnescape(password) |
| 116 | |
| 117 | server := uri.Hostname() |
| 118 | port, _ := strconv.Atoi(uri.Port()) |
| 119 | |
| 120 | moreInfos := uri.Query() |
| 121 | sni := moreInfos.Get("sni") |
| 122 | sni, _ = url.QueryUnescape(sni) |
| 123 | transformType := moreInfos.Get("type") |
| 124 | transformType, _ = url.QueryUnescape(transformType) |
| 125 | host := moreInfos.Get("host") |
| 126 | host, _ = url.QueryUnescape(host) |
| 127 | path := moreInfos.Get("path") |
| 128 | path, _ = url.QueryUnescape(path) |
| 129 | |
| 130 | alpn := make([]string, 0) |
| 131 | if transformType == "h2" { |
| 132 | alpn = append(alpn, "h2") |
| 133 | } |
| 134 | |
| 135 | if port == 0 { |
| 136 | return nil, ErrorNotTrojanink |
| 137 | } |
| 138 | |
| 139 | return &Trojan{ |
| 140 | Base: Base{ |
| 141 | Name: strconv.Itoa(rand.Int()), |
| 142 | Server: server, |
| 143 | Port: port, |
| 144 | Type: "trojan", |