mirror of https://github.com/ginuerzh/gost
15 changed files with 386 additions and 214 deletions
@ -1,139 +0,0 @@ |
|||||
package main |
|
||||
|
|
||||
import ( |
|
||||
"bufio" |
|
||||
"net" |
|
||||
"net/url" |
|
||||
"os" |
|
||||
"strings" |
|
||||
|
|
||||
"github.com/ginuerzh/gost/gost" |
|
||||
"github.com/go-log/log" |
|
||||
) |
|
||||
|
|
||||
type node struct { |
|
||||
Addr string |
|
||||
Protocol string // protocol: http/socks5/ss
|
|
||||
Transport string // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp
|
|
||||
Remote string // remote address, used by tcp/udp port forwarding
|
|
||||
Users []*url.Userinfo // authentication for proxy
|
|
||||
Whitelist *gost.Permissions |
|
||||
Blacklist *gost.Permissions |
|
||||
values url.Values |
|
||||
serverName string |
|
||||
} |
|
||||
|
|
||||
func parseNode(s string) (n node, err error) { |
|
||||
if !strings.Contains(s, "://") { |
|
||||
s = "gost://" + s |
|
||||
} |
|
||||
u, err := url.Parse(s) |
|
||||
if err != nil { |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
query := u.Query() |
|
||||
|
|
||||
n = node{ |
|
||||
Addr: u.Host, |
|
||||
} |
|
||||
|
|
||||
if query.Get("whitelist") != "" { |
|
||||
if n.Whitelist, err = gost.ParsePermissions(query.Get("whitelist")); err != nil { |
|
||||
return |
|
||||
} |
|
||||
} else { |
|
||||
// By default allow for everyting
|
|
||||
n.Whitelist, _ = gost.ParsePermissions("*:*:*") |
|
||||
} |
|
||||
|
|
||||
if query.Get("blacklist") != "" { |
|
||||
if n.Blacklist, err = gost.ParsePermissions(query.Get("blacklist")); err != nil { |
|
||||
return |
|
||||
} |
|
||||
} else { |
|
||||
// By default block nothing
|
|
||||
n.Blacklist, _ = gost.ParsePermissions("") |
|
||||
} |
|
||||
|
|
||||
if u.User != nil { |
|
||||
n.Users = append(n.Users, u.User) |
|
||||
} |
|
||||
|
|
||||
users, er := parseUsers(n.values.Get("secrets")) |
|
||||
if users != nil { |
|
||||
n.Users = append(n.Users, users...) |
|
||||
} |
|
||||
if er != nil { |
|
||||
log.Log("load secrets:", er) |
|
||||
} |
|
||||
|
|
||||
if strings.Contains(u.Host, ":") { |
|
||||
n.serverName, _, _ = net.SplitHostPort(u.Host) |
|
||||
if n.serverName == "" { |
|
||||
n.serverName = "localhost" // default server name
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
schemes := strings.Split(u.Scheme, "+") |
|
||||
if len(schemes) == 1 { |
|
||||
n.Protocol = schemes[0] |
|
||||
n.Transport = schemes[0] |
|
||||
} |
|
||||
if len(schemes) == 2 { |
|
||||
n.Protocol = schemes[0] |
|
||||
n.Transport = schemes[1] |
|
||||
} |
|
||||
|
|
||||
switch n.Transport { |
|
||||
case "ws", "wss", "tls", "h2", "h2c", "quic", "kcp", "redirect", "ssu", "ssh": |
|
||||
case "https": |
|
||||
n.Protocol = "http" |
|
||||
n.Transport = "tls" |
|
||||
case "http2": // http2 -> http2+tls, h2c mode is http2+tcp
|
|
||||
n.Protocol = "http2" |
|
||||
n.Transport = "tls" |
|
||||
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
|
|
||||
n.Remote = strings.Trim(u.EscapedPath(), "/") |
|
||||
case "rtcp", "rudp": // rtcp and rudp are for remote port forwarding
|
|
||||
n.Remote = strings.Trim(u.EscapedPath(), "/") |
|
||||
default: |
|
||||
n.Transport = "" |
|
||||
} |
|
||||
|
|
||||
switch n.Protocol { |
|
||||
case "http", "http2", "socks", "socks4", "socks4a", "socks5", "ss": |
|
||||
default: |
|
||||
n.Protocol = "" |
|
||||
} |
|
||||
|
|
||||
return |
|
||||
} |
|
||||
|
|
||||
func parseUsers(s string) (users []*url.Userinfo, err error) { |
|
||||
if s == "" { |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
f, err := os.Open(s) |
|
||||
if err != nil { |
|
||||
return |
|
||||
} |
|
||||
scanner := bufio.NewScanner(f) |
|
||||
for scanner.Scan() { |
|
||||
line := strings.TrimSpace(scanner.Text()) |
|
||||
if line == "" || strings.HasPrefix(line, "#") { |
|
||||
continue |
|
||||
} |
|
||||
|
|
||||
s := strings.SplitN(line, " ", 2) |
|
||||
if len(s) == 1 { |
|
||||
users = append(users, url.User(strings.TrimSpace(s[0]))) |
|
||||
} else if len(s) == 2 { |
|
||||
users = append(users, url.UserPassword(strings.TrimSpace(s[0]), strings.TrimSpace(s[1]))) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
err = scanner.Err() |
|
||||
return |
|
||||
} |
|
||||
@ -1,15 +1,165 @@ |
|||||
package gost |
package gost |
||||
|
|
||||
import ( |
import ( |
||||
|
"bufio" |
||||
|
"net" |
||||
"net/url" |
"net/url" |
||||
|
"os" |
||||
|
"strconv" |
||||
|
"strings" |
||||
|
|
||||
|
"github.com/go-log/log" |
||||
) |
) |
||||
|
|
||||
// Node is a proxy node, mainly used to construct a proxy chain.
|
// Node is a proxy node, mainly used to construct a proxy chain.
|
||||
type Node struct { |
type Node struct { |
||||
Addr string |
Addr string |
||||
Protocol string |
Protocol string |
||||
Transport string |
Transport string |
||||
User *url.Userinfo |
Remote string // remote address, used by tcp/udp port forwarding
|
||||
Client *Client |
User *url.Userinfo |
||||
Server *Server |
users []*url.Userinfo // authentication or cipher for proxy
|
||||
|
Whitelist *Permissions |
||||
|
Blacklist *Permissions |
||||
|
values url.Values |
||||
|
serverName string |
||||
|
Client *Client |
||||
|
Server *Server |
||||
|
} |
||||
|
|
||||
|
func ParseNode(s string) (node Node, err error) { |
||||
|
if !strings.Contains(s, "://") { |
||||
|
s = "auto://" + s |
||||
|
} |
||||
|
u, err := url.Parse(s) |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
query := u.Query() |
||||
|
node = Node{ |
||||
|
Addr: u.Host, |
||||
|
values: query, |
||||
|
serverName: u.Host, |
||||
|
} |
||||
|
|
||||
|
if query.Get("whitelist") != "" { |
||||
|
if node.Whitelist, err = ParsePermissions(query.Get("whitelist")); err != nil { |
||||
|
return |
||||
|
} |
||||
|
} else { |
||||
|
// By default allow for everyting
|
||||
|
node.Whitelist, _ = ParsePermissions("*:*:*") |
||||
|
} |
||||
|
|
||||
|
if query.Get("blacklist") != "" { |
||||
|
if node.Blacklist, err = ParsePermissions(query.Get("blacklist")); err != nil { |
||||
|
return |
||||
|
} |
||||
|
} else { |
||||
|
// By default block nothing
|
||||
|
node.Blacklist, _ = ParsePermissions("") |
||||
|
} |
||||
|
|
||||
|
if u.User != nil { |
||||
|
node.User = u.User |
||||
|
node.users = append(node.users, u.User) |
||||
|
} |
||||
|
|
||||
|
users, er := parseUsers(node.values.Get("secrets")) |
||||
|
if users != nil { |
||||
|
node.users = append(node.users, users...) |
||||
|
} |
||||
|
if er != nil { |
||||
|
log.Log("load secrets:", er) |
||||
|
} |
||||
|
|
||||
|
if strings.Contains(u.Host, ":") { |
||||
|
node.serverName, _, _ = net.SplitHostPort(u.Host) |
||||
|
if node.serverName == "" { |
||||
|
node.serverName = "localhost" // default server name
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
schemes := strings.Split(u.Scheme, "+") |
||||
|
if len(schemes) == 1 { |
||||
|
node.Protocol = schemes[0] |
||||
|
node.Transport = schemes[0] |
||||
|
} |
||||
|
if len(schemes) == 2 { |
||||
|
node.Protocol = schemes[0] |
||||
|
node.Transport = schemes[1] |
||||
|
} |
||||
|
|
||||
|
switch node.Transport { |
||||
|
case "tls", "ws", "wss", "kcp", "ssh", "quic", "ssu", "http2", "h2", "h2c", "redirect": |
||||
|
case "https": |
||||
|
node.Protocol = "http" |
||||
|
node.Transport = "tls" |
||||
|
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
|
||||
|
node.Remote = strings.Trim(u.EscapedPath(), "/") |
||||
|
case "rtcp", "rudp": // rtcp and rudp are for remote port forwarding
|
||||
|
node.Remote = strings.Trim(u.EscapedPath(), "/") |
||||
|
default: |
||||
|
node.Transport = "" |
||||
|
} |
||||
|
|
||||
|
switch node.Protocol { |
||||
|
case "http", "http2", "socks4", "socks4a", "socks", "socks5", "ss": |
||||
|
case "tcp", "udp", "rtcp", "rudp": // port forwarding
|
||||
|
case "direct", "remote": // SSH port forwarding
|
||||
|
default: |
||||
|
node.Protocol = "" |
||||
|
} |
||||
|
|
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func parseUsers(authFile string) (users []*url.Userinfo, err error) { |
||||
|
if authFile == "" { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
file, err := os.Open(authFile) |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
scanner := bufio.NewScanner(file) |
||||
|
for scanner.Scan() { |
||||
|
line := strings.TrimSpace(scanner.Text()) |
||||
|
if line == "" || strings.HasPrefix(line, "#") { |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
s := strings.SplitN(line, " ", 2) |
||||
|
if len(s) == 1 { |
||||
|
users = append(users, url.User(strings.TrimSpace(s[0]))) |
||||
|
} else if len(s) == 2 { |
||||
|
users = append(users, url.UserPassword(strings.TrimSpace(s[0]), strings.TrimSpace(s[1]))) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
err = scanner.Err() |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func Can(action string, addr string, whitelist, blacklist *Permissions) bool { |
||||
|
if !strings.Contains(addr, ":") { |
||||
|
addr = addr + ":80" |
||||
|
} |
||||
|
host, strport, err := net.SplitHostPort(addr) |
||||
|
|
||||
|
if err != nil { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
port, err := strconv.Atoi(strport) |
||||
|
|
||||
|
if err != nil { |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
log.Logf("Can action: %s, host: %s, port %d", action, host, port) |
||||
|
|
||||
|
return whitelist.Can(action, host, port) && !blacklist.Can(action, host, port) |
||||
} |
} |
||||
|
|||||
Loading…
Reference in new issue