291 lines
6.2 KiB
Go
291 lines
6.2 KiB
Go
package gost
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"net"
|
||
"net/url"
|
||
"strconv"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
var (
|
||
// ErrInvalidNode is an error that implies the node is invalid.
|
||
ErrInvalidNode = errors.New("invalid node")
|
||
)
|
||
|
||
// Node is a proxy node, mainly used to construct a proxy chain.
|
||
type Node struct {
|
||
ID int
|
||
Addr string
|
||
Host string
|
||
Protocol string
|
||
Transport string
|
||
BindAddr net.IP // 绑定出口
|
||
OutFromIn bool // 是否使用入口IP网卡作为出口网卡,只有当没有绑定出口时才有效
|
||
Remote string // remote address, used by tcp/udp port forwarding
|
||
url *url.URL // raw url
|
||
User *url.Userinfo
|
||
Values url.Values
|
||
DialOptions []DialOption
|
||
HandshakeOptions []HandshakeOption
|
||
ConnectOptions []ConnectOption
|
||
Client *Client
|
||
marker *failMarker
|
||
Bypass *Bypass
|
||
}
|
||
|
||
// ParseNode parses the node info.
|
||
// The proxy node string pattern is [scheme://][user:pass@host]:port.
|
||
// Scheme can be divided into two parts by character '+', such as: http+tls.
|
||
func ParseNode(s string) (node Node, err error) {
|
||
s = strings.TrimSpace(s)
|
||
if s == "" {
|
||
return Node{}, ErrInvalidNode
|
||
}
|
||
|
||
if !strings.Contains(s, "://") {
|
||
s = "auto://" + s
|
||
}
|
||
u, err := url.Parse(s)
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
node = Node{
|
||
Addr: u.Host,
|
||
Host: u.Host,
|
||
BindAddr: net.ParseIP(u.Query().Get("bind")),
|
||
Remote: strings.Trim(u.EscapedPath(), "/"),
|
||
Values: u.Query(),
|
||
User: u.User,
|
||
marker: &failMarker{},
|
||
url: u,
|
||
}
|
||
outFromIn, _ := strconv.ParseBool(u.Query().Get("outFromIn"))
|
||
node.OutFromIn = outFromIn
|
||
|
||
u.RawQuery = ""
|
||
u.User = nil
|
||
|
||
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 "https":
|
||
node.Transport = "tls"
|
||
case "tls", "mtls":
|
||
case "http2", "h2", "h2c":
|
||
case "ws", "mws", "wss", "mwss":
|
||
case "kcp", "ssh", "quic":
|
||
case "ssu":
|
||
node.Transport = "udp"
|
||
case "ohttp", "otls", "obfs4": // obfs
|
||
case "tcp", "udp":
|
||
case "rtcp", "rudp": // rtcp and rudp are for remote port forwarding
|
||
case "tun", "tap": // tun/tap device
|
||
case "ftcp": // fake TCP
|
||
case "dns":
|
||
case "redu", "redirectu": // UDP tproxy
|
||
default:
|
||
node.Transport = "tcp"
|
||
}
|
||
|
||
switch node.Protocol {
|
||
case "http", "http2":
|
||
case "https":
|
||
node.Protocol = "http"
|
||
case "socks4", "socks4a":
|
||
case "socks", "socks5":
|
||
node.Protocol = "socks5"
|
||
case "ss", "ssu":
|
||
case "ss2": // as of 2.10.1, ss2 is same as ss
|
||
node.Protocol = "ss"
|
||
case "sni":
|
||
case "tcp", "udp", "rtcp", "rudp": // port forwarding
|
||
case "direct", "remote", "forward": // forwarding
|
||
case "red", "redirect", "redu", "redirectu": // TCP,UDP transparent proxy
|
||
case "tun", "tap": // tun/tap device
|
||
case "ftcp": // fake TCP
|
||
case "dns", "dot", "doh":
|
||
case "relay":
|
||
default:
|
||
node.Protocol = ""
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// MarkDead marks the node fail status.
|
||
func (node *Node) MarkDead() {
|
||
if node.marker == nil {
|
||
return
|
||
}
|
||
node.marker.Mark()
|
||
}
|
||
|
||
// ResetDead resets the node fail status.
|
||
func (node *Node) ResetDead() {
|
||
if node.marker == nil {
|
||
return
|
||
}
|
||
node.marker.Reset()
|
||
}
|
||
|
||
// Clone clones the node, it will prevent data race.
|
||
func (node *Node) Clone() Node {
|
||
nd := *node
|
||
if node.marker != nil {
|
||
nd.marker = node.marker.Clone()
|
||
}
|
||
return nd
|
||
}
|
||
|
||
// Get returns node parameter specified by key.
|
||
func (node *Node) Get(key string) string {
|
||
return node.Values.Get(key)
|
||
}
|
||
|
||
// GetBool converts node parameter value to bool.
|
||
func (node *Node) GetBool(key string) bool {
|
||
b, _ := strconv.ParseBool(node.Values.Get(key))
|
||
return b
|
||
}
|
||
|
||
// GetInt converts node parameter value to int.
|
||
func (node *Node) GetInt(key string) int {
|
||
n, _ := strconv.Atoi(node.Get(key))
|
||
return n
|
||
}
|
||
|
||
// GetDuration converts node parameter value to time.Duration.
|
||
func (node *Node) GetDuration(key string) time.Duration {
|
||
d, err := time.ParseDuration(node.Get(key))
|
||
if err != nil {
|
||
d = time.Duration(node.GetInt(key)) * time.Second
|
||
}
|
||
return d
|
||
}
|
||
|
||
func (node Node) String() string {
|
||
var scheme string
|
||
if node.url != nil {
|
||
scheme = node.url.Scheme
|
||
}
|
||
if scheme == "" {
|
||
scheme = fmt.Sprintf("%s+%s", node.Protocol, node.Transport)
|
||
}
|
||
return fmt.Sprintf("%s://%s",
|
||
scheme, node.Addr)
|
||
}
|
||
|
||
// NodeGroup is a group of nodes.
|
||
type NodeGroup struct {
|
||
ID int
|
||
nodes []Node
|
||
selectorOptions []SelectOption
|
||
selector NodeSelector
|
||
mux sync.RWMutex
|
||
}
|
||
|
||
// NewNodeGroup creates a node group
|
||
func NewNodeGroup(nodes ...Node) *NodeGroup {
|
||
return &NodeGroup{
|
||
nodes: nodes,
|
||
}
|
||
}
|
||
|
||
// AddNode appends node or node list into group node.
|
||
func (group *NodeGroup) AddNode(node ...Node) {
|
||
if group == nil {
|
||
return
|
||
}
|
||
group.mux.Lock()
|
||
defer group.mux.Unlock()
|
||
|
||
group.nodes = append(group.nodes, node...)
|
||
}
|
||
|
||
// SetNodes replaces the group nodes to the specified nodes,
|
||
// and returns the previous nodes.
|
||
func (group *NodeGroup) SetNodes(nodes ...Node) []Node {
|
||
if group == nil {
|
||
return nil
|
||
}
|
||
|
||
group.mux.Lock()
|
||
defer group.mux.Unlock()
|
||
|
||
old := group.nodes
|
||
group.nodes = nodes
|
||
return old
|
||
}
|
||
|
||
// SetSelector sets node selector with options for the group.
|
||
func (group *NodeGroup) SetSelector(selector NodeSelector, opts ...SelectOption) {
|
||
if group == nil {
|
||
return
|
||
}
|
||
group.mux.Lock()
|
||
defer group.mux.Unlock()
|
||
|
||
group.selector = selector
|
||
group.selectorOptions = opts
|
||
}
|
||
|
||
// Nodes returns the node list in the group
|
||
func (group *NodeGroup) Nodes() []Node {
|
||
if group == nil {
|
||
return nil
|
||
}
|
||
|
||
group.mux.RLock()
|
||
defer group.mux.RUnlock()
|
||
|
||
return group.nodes
|
||
}
|
||
|
||
// GetNode returns the node specified by index in the group.
|
||
func (group *NodeGroup) GetNode(i int) Node {
|
||
group.mux.RLock()
|
||
defer group.mux.RUnlock()
|
||
|
||
if i < 0 || group == nil || len(group.nodes) <= i {
|
||
return Node{}
|
||
}
|
||
return group.nodes[i]
|
||
}
|
||
|
||
// Next selects a node from group.
|
||
// It also selects IP if the IP list exists.
|
||
func (group *NodeGroup) Next() (node Node, err error) {
|
||
if group == nil {
|
||
return
|
||
}
|
||
|
||
group.mux.RLock()
|
||
defer group.mux.RUnlock()
|
||
|
||
selector := group.selector
|
||
if selector == nil {
|
||
selector = &defaultSelector{}
|
||
}
|
||
|
||
// select node from node group
|
||
node, err = selector.Select(group.nodes, group.selectorOptions...)
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
return
|
||
}
|