gost_software/gost/node.go
2017-08-03 18:08:55 +08:00

166 lines
3.6 KiB
Go

package gost
import (
"bufio"
"net"
"net/url"
"os"
"strconv"
"strings"
"github.com/go-log/log"
)
// Node is a proxy node, mainly used to construct a proxy chain.
type Node struct {
Addr string
Protocol string
Transport string
Remote string // remote address, used by tcp/udp port forwarding
User *url.Userinfo
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)
}