Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
G
gost
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
gost
Commits
b3068a7d
Commit
b3068a7d
authored
Nov 16, 2017
by
rui.zheng
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev' of github.com:ginuerzh/gost into dev
parents
6974e9a8
6f935fc3
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
431 additions
and
204 deletions
+431
-204
chain.go
chain.go
+73
-66
client.go
client.go
+8
-0
cmd/gost/gost.json
cmd/gost/gost.json
+20
-0
cmd/gost/main.go
cmd/gost/main.go
+127
-47
cmd/gost/peer.json
cmd/gost/peer.json
+10
-0
gost.go
gost.go
+1
-0
handler.go
handler.go
+2
-1
http.go
http.go
+7
-0
node.go
node.go
+92
-8
obfs.go
obfs.go
+3
-2
selector.go
selector.go
+59
-60
sni.go
sni.go
+12
-4
ss.go
ss.go
+10
-9
ssh.go
ssh.go
+3
-3
ws.go
ws.go
+4
-4
No files found.
chain.go
View file @
b3068a7d
package
gost
import
(
"bytes"
"errors"
"fmt"
"net"
"strings"
"github.com/go-log/log"
)
...
...
@@ -15,6 +16,8 @@ var (
// Chain is a proxy chain that holds a list of proxy nodes.
type
Chain
struct
{
isRoute
bool
Retries
int
nodeGroups
[]
*
NodeGroup
}
...
...
@@ -27,6 +30,12 @@ func NewChain(nodes ...Node) *Chain {
return
chain
}
func
newRoute
(
nodes
...
Node
)
*
Chain
{
chain
:=
NewChain
(
nodes
...
)
chain
.
isRoute
=
true
return
chain
}
// Nodes returns the proxy nodes that the chain holds.
// If a node is a node group, the first node in the group will be returned.
func
(
c
*
Chain
)
Nodes
()
(
nodes
[]
Node
)
{
...
...
@@ -50,8 +59,8 @@ func (c *Chain) LastNode() Node {
if
c
.
IsEmpty
()
{
return
Node
{}
}
last
:=
c
.
nodeGroups
[
len
(
c
.
nodeGroups
)
-
1
]
return
last
.
nodes
[
0
]
group
:=
c
.
nodeGroups
[
len
(
c
.
nodeGroups
)
-
1
]
return
group
.
nodes
[
0
]
.
Clone
()
}
// LastNodeGroup returns the last group of the group list.
...
...
@@ -90,17 +99,27 @@ func (c *Chain) IsEmpty() bool {
// Dial connects to the target address addr through the chain.
// If the chain is empty, it will use the net.Dial directly.
func
(
c
*
Chain
)
Dial
(
addr
string
)
(
net
.
Conn
,
error
)
{
func
(
c
*
Chain
)
Dial
(
addr
string
)
(
conn
net
.
Conn
,
err
error
)
{
if
c
.
IsEmpty
()
{
return
net
.
Dial
(
"tcp"
,
addr
)
return
net
.
Dial
Timeout
(
"tcp"
,
addr
,
DialTimeout
)
}
for
i
:=
0
;
i
<
c
.
Retries
+
1
;
i
++
{
conn
,
err
=
c
.
dial
(
addr
)
if
err
==
nil
{
break
}
}
return
}
func
(
c
*
Chain
)
dial
(
addr
string
)
(
net
.
Conn
,
error
)
{
route
,
err
:=
c
.
selectRoute
()
if
err
!=
nil
{
return
nil
,
err
}
conn
,
err
:=
c
.
getConn
(
route
)
conn
,
err
:=
route
.
getConn
(
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -116,77 +135,60 @@ func (c *Chain) Dial(addr string) (net.Conn, error) {
// Conn obtains a handshaked connection to the last node of the chain.
// If the chain is empty, it returns an ErrEmptyChain error.
func
(
c
*
Chain
)
Conn
()
(
conn
net
.
Conn
,
err
error
)
{
route
,
err
:=
c
.
selectRoute
()
if
err
!=
nil
{
return
nil
,
err
}
conn
,
err
=
c
.
getConn
(
route
)
return
}
func
(
c
*
Chain
)
selectRoute
()
(
route
*
Chain
,
err
error
)
{
route
=
NewChain
()
for
_
,
group
:=
range
c
.
nodeGroups
{
selector
:=
group
.
Selector
if
selector
==
nil
{
selector
=
&
defaultSelector
{}
}
// select node from node group
node
,
err
:=
selector
.
Select
(
group
.
Nodes
(),
group
.
Options
...
)
for
i
:=
0
;
i
<
c
.
Retries
+
1
;
i
++
{
var
route
*
Chain
route
,
err
=
c
.
selectRoute
()
if
err
!=
nil
{
return
nil
,
err
continue
}
if
node
.
Client
.
Transporter
.
Multiplex
()
{
node
.
DialOptions
=
append
(
node
.
DialOptions
,
ChainDialOption
(
route
),
)
route
=
NewChain
()
// cutoff the chain for multiplex
conn
,
err
=
route
.
getConn
()
if
err
!=
nil
{
continue
}
route
.
AddNode
(
node
)
break
}
return
}
func
(
c
*
Chain
)
getConn
(
route
*
Chain
)
(
conn
net
.
Conn
,
err
error
)
{
if
route
.
IsEmpty
()
{
func
(
c
*
Chain
)
getConn
()
(
conn
net
.
Conn
,
err
error
)
{
if
c
.
IsEmpty
()
{
err
=
ErrEmptyChain
return
}
nodes
:=
route
.
Nodes
()
nodes
:=
c
.
Nodes
()
node
:=
nodes
[
0
]
addr
,
err
:=
selectIP
(
&
node
)
if
err
!=
nil
{
return
}
cn
,
err
:=
node
.
Client
.
Dial
(
addr
,
node
.
DialOptions
...
)
cn
,
err
:=
node
.
Client
.
Dial
(
node
.
Addr
,
node
.
DialOptions
...
)
if
err
!=
nil
{
node
.
MarkDead
()
return
}
cn
,
err
=
node
.
Client
.
Handshake
(
cn
,
node
.
HandshakeOptions
...
)
if
err
!=
nil
{
node
.
MarkDead
()
return
}
node
.
ResetDead
()
preNode
:=
node
for
_
,
node
:=
range
nodes
[
1
:
]
{
addr
,
err
=
selectIP
(
&
node
)
if
err
!=
nil
{
return
}
var
cc
net
.
Conn
cc
,
err
=
preNode
.
Client
.
Connect
(
cn
,
a
ddr
)
cc
,
err
=
preNode
.
Client
.
Connect
(
cn
,
node
.
A
ddr
)
if
err
!=
nil
{
cn
.
Close
()
node
.
MarkDead
()
return
}
cc
,
err
=
node
.
Client
.
Handshake
(
cc
,
node
.
HandshakeOptions
...
)
if
err
!=
nil
{
cn
.
Close
()
node
.
MarkDead
()
return
}
node
.
ResetDead
()
cn
=
cc
preNode
=
node
}
...
...
@@ -195,29 +197,34 @@ func (c *Chain) getConn(route *Chain) (conn net.Conn, err error) {
return
}
func
selectIP
(
node
*
Node
)
(
string
,
error
)
{
addr
:=
node
.
Addr
s
:=
node
.
IPSelector
if
s
==
nil
{
s
=
&
RandomIPSelector
{}
func
(
c
*
Chain
)
selectRoute
()
(
route
*
Chain
,
err
error
)
{
if
c
.
isRoute
{
return
c
,
nil
}
// select IP from IP list
ip
,
err
:=
s
.
Select
(
node
.
IPs
)
if
err
!=
nil
{
return
""
,
err
}
if
ip
!=
""
{
if
!
strings
.
Contains
(
ip
,
":"
)
{
_
,
sport
,
err
:=
net
.
SplitHostPort
(
addr
)
if
err
!=
nil
{
return
""
,
err
}
ip
=
ip
+
":"
+
sport
buf
:=
bytes
.
Buffer
{}
route
=
newRoute
()
route
.
Retries
=
c
.
Retries
for
_
,
group
:=
range
c
.
nodeGroups
{
node
,
err
:=
group
.
Next
()
if
err
!=
nil
{
return
nil
,
err
}
addr
=
ip
// override the original address
node
.
HandshakeOptions
=
append
(
node
.
HandshakeOptions
,
AddrHandshakeOption
(
addr
))
buf
.
WriteString
(
fmt
.
Sprintf
(
"%s -> "
,
node
.
String
()))
if
node
.
Client
.
Transporter
.
Multiplex
()
{
node
.
DialOptions
=
append
(
node
.
DialOptions
,
ChainDialOption
(
route
),
)
route
=
newRoute
()
// cutoff the chain for multiplex.
route
.
Retries
=
c
.
Retries
}
route
.
AddNode
(
node
)
}
if
Debug
{
log
.
Log
(
"select route:"
,
buf
.
String
())
}
log
.
Log
(
"select IP:"
,
node
.
Addr
,
node
.
IPs
,
addr
)
return
addr
,
nil
return
}
client.go
View file @
b3068a7d
...
...
@@ -116,6 +116,7 @@ func ChainDialOption(chain *Chain) DialOption {
// HandshakeOptions describes the options for handshake.
type
HandshakeOptions
struct
{
Addr
string
Host
string
User
*
url
.
Userinfo
Timeout
time
.
Duration
Interval
time
.
Duration
...
...
@@ -136,6 +137,13 @@ func AddrHandshakeOption(addr string) HandshakeOption {
}
}
// HostHandshakeOption specifies the hostname
func
HostHandshakeOption
(
host
string
)
HandshakeOption
{
return
func
(
opts
*
HandshakeOptions
)
{
opts
.
Host
=
host
}
}
// UserHandshakeOption specifies the user used by Transporter.Handshake
func
UserHandshakeOption
(
user
*
url
.
Userinfo
)
HandshakeOption
{
return
func
(
opts
*
HandshakeOptions
)
{
...
...
cmd/gost/gost.json
View file @
b3068a7d
{
"Debug"
:
false
,
"Retries"
:
1
,
"ServeNodes"
:
[
":8080"
,
"ss://chacha20:12345678@:8338"
...
...
@@ -6,5 +8,23 @@
"ChainNodes"
:
[
"http://192.168.1.1:8080"
,
"https://10.0.2.1:443"
],
"Routes"
:
[
{
"Retries"
:
1
,
"ServeNodes"
:
[
"ws://:1443"
],
"ChainNodes"
:
[
"socks://:192.168.1.1:1080"
]
},
{
"Retries"
:
3
,
"ServeNodes"
:
[
"quic://:443"
]
}
]
}
\ No newline at end of file
cmd/gost/main.go
View file @
b3068a7d
...
...
@@ -22,10 +22,8 @@ import (
)
var
(
options
struct
{
ChainNodes
,
ServeNodes
stringList
Debug
bool
}
options
route
routes
[]
route
)
func
init
()
{
...
...
@@ -43,12 +41,17 @@ func init() {
flag
.
BoolVar
(
&
printVersion
,
"V"
,
false
,
"print version"
)
flag
.
Parse
()
if
len
(
options
.
ServeNodes
)
>
0
{
routes
=
append
(
routes
,
options
)
}
gost
.
Debug
=
options
.
Debug
if
err
:=
loadConfigureFile
(
configureFile
);
err
!=
nil
{
log
.
Log
(
err
)
os
.
Exit
(
1
)
}
if
flag
.
NFlag
()
==
0
{
if
flag
.
NFlag
()
==
0
||
len
(
routes
)
==
0
{
flag
.
PrintDefaults
()
os
.
Exit
(
0
)
}
...
...
@@ -57,8 +60,6 @@ func init() {
fmt
.
Fprintf
(
os
.
Stderr
,
"gost %s (%s)
\n
"
,
gost
.
Version
,
runtime
.
Version
())
os
.
Exit
(
0
)
}
gost
.
Debug
=
options
.
Debug
}
func
main
()
{
...
...
@@ -72,45 +73,73 @@ func main() {
Certificates
:
[]
tls
.
Certificate
{
cert
},
}
chain
,
err
:=
initChain
()
if
err
!=
nil
{
log
.
Log
(
err
)
os
.
Exit
(
1
)
}
if
err
:=
serve
(
chain
);
err
!=
nil
{
log
.
Log
(
err
)
os
.
Exit
(
1
)
for
_
,
route
:=
range
routes
{
if
err
:=
route
.
serve
();
err
!=
nil
{
log
.
Log
(
err
)
os
.
Exit
(
1
)
}
}
select
{}
}
func
initChain
()
(
*
gost
.
Chain
,
error
)
{
type
route
struct
{
ChainNodes
,
ServeNodes
stringList
Retries
int
Debug
bool
}
func
(
r
*
route
)
initChain
()
(
*
gost
.
Chain
,
error
)
{
chain
:=
gost
.
NewChain
()
for
_
,
ns
:=
range
options
.
ChainNodes
{
chain
.
Retries
=
r
.
Retries
gid
:=
1
// group ID
for
_
,
ns
:=
range
r
.
ChainNodes
{
ngroup
:=
gost
.
NewNodeGroup
()
ngroup
.
ID
=
gid
gid
++
// parse the base node
node
,
err
:=
parseChainNode
(
ns
)
node
s
,
err
:=
parseChainNode
(
ns
)
if
err
!=
nil
{
return
nil
,
err
}
n
group
:=
gost
.
NewNodeGroup
(
node
)
n
id
:=
1
// node ID
// parse node peers if exists
peerCfg
,
err
:=
loadPeerConfig
(
node
.
Values
.
Get
(
"peer"
))
for
i
:=
range
nodes
{
nodes
[
i
]
.
ID
=
nid
nid
++
}
ngroup
.
AddNode
(
nodes
...
)
// parse peer nodes if exists
peerCfg
,
err
:=
loadPeerConfig
(
nodes
[
0
]
.
Values
.
Get
(
"peer"
))
if
err
!=
nil
{
log
.
Log
(
err
)
}
peerCfg
.
Validate
()
ngroup
.
Options
=
append
(
ngroup
.
Options
,
// gost.WithFilter(),
gost
.
WithFilter
(
&
gost
.
FailFilter
{
MaxFails
:
peerCfg
.
MaxFails
,
FailTimeout
:
time
.
Duration
(
peerCfg
.
FailTimeout
)
*
time
.
Second
,
}),
gost
.
WithStrategy
(
parseStrategy
(
peerCfg
.
Strategy
)),
)
for
_
,
s
:=
range
peerCfg
.
Nodes
{
node
,
err
=
parseChainNode
(
s
)
node
s
,
err
=
parseChainNode
(
s
)
if
err
!=
nil
{
return
nil
,
err
}
ngroup
.
AddNode
(
node
)
for
i
:=
range
nodes
{
nodes
[
i
]
.
ID
=
nid
nid
++
}
ngroup
.
AddNode
(
nodes
...
)
}
chain
.
AddNodeGroup
(
ngroup
)
...
...
@@ -119,15 +148,12 @@ func initChain() (*gost.Chain, error) {
return
chain
,
nil
}
func
parseChainNode
(
ns
string
)
(
node
gost
.
Node
,
err
error
)
{
node
,
err
=
gost
.
ParseNode
(
ns
)
func
parseChainNode
(
ns
string
)
(
node
s
[]
gost
.
Node
,
err
error
)
{
node
,
err
:
=
gost
.
ParseNode
(
ns
)
if
err
!=
nil
{
return
}
node
.
IPs
=
parseIP
(
node
.
Values
.
Get
(
"ip"
))
node
.
IPSelector
=
&
gost
.
RoundRobinIPSelector
{}
users
,
err
:=
parseUsers
(
node
.
Values
.
Get
(
"secrets"
))
if
err
!=
nil
{
return
...
...
@@ -135,7 +161,7 @@ func parseChainNode(ns string) (node gost.Node, err error) {
if
node
.
User
==
nil
&&
len
(
users
)
>
0
{
node
.
User
=
users
[
0
]
}
serverName
,
_
,
_
:=
net
.
SplitHostPort
(
node
.
Addr
)
serverName
,
sport
,
_
:=
net
.
SplitHostPort
(
node
.
Addr
)
if
serverName
==
""
{
serverName
=
"localhost"
// default server name
}
...
...
@@ -177,7 +203,7 @@ func parseChainNode(ns string) (node gost.Node, err error) {
*/
config
,
err
:=
parseKCPConfig
(
node
.
Values
.
Get
(
"c"
))
if
err
!=
nil
{
return
n
ode
,
err
return
n
il
,
err
}
tr
=
gost
.
KCPTransporter
(
config
)
case
"ssh"
:
...
...
@@ -206,7 +232,7 @@ func parseChainNode(ns string) (node gost.Node, err error) {
case
"obfs4"
:
if
err
:=
gost
.
Obfs4Init
(
node
,
false
);
err
!=
nil
{
return
n
ode
,
err
return
n
il
,
err
}
tr
=
gost
.
Obfs4Transporter
()
case
"ohttp"
:
...
...
@@ -249,24 +275,41 @@ func parseChainNode(ns string) (node gost.Node, err error) {
interval
,
_
:=
strconv
.
Atoi
(
node
.
Values
.
Get
(
"ping"
))
retry
,
_
:=
strconv
.
Atoi
(
node
.
Values
.
Get
(
"retry"
))
node
.
HandshakeOptions
=
append
(
node
.
HandshakeOptions
,
handshakeOptions
:=
[]
gost
.
HandshakeOption
{
gost
.
AddrHandshakeOption
(
node
.
Addr
),
gost
.
HostHandshakeOption
(
node
.
Host
),
gost
.
UserHandshakeOption
(
node
.
User
),
gost
.
TLSConfigHandshakeOption
(
tlsCfg
),
gost
.
IntervalHandshakeOption
(
time
.
Duration
(
interval
)
*
time
.
Second
),
gost
.
TimeoutHandshakeOption
(
time
.
Duration
(
timeout
)
*
time
.
Second
),
gost
.
IntervalHandshakeOption
(
time
.
Duration
(
interval
)
*
time
.
Second
),
gost
.
TimeoutHandshakeOption
(
time
.
Duration
(
timeout
)
*
time
.
Second
),
gost
.
RetryHandshakeOption
(
retry
),
)
}
node
.
Client
=
&
gost
.
Client
{
Connector
:
connector
,
Transporter
:
tr
,
}
ips
:=
parseIP
(
node
.
Values
.
Get
(
"ip"
),
sport
)
for
_
,
ip
:=
range
ips
{
node
.
Addr
=
ip
node
.
HandshakeOptions
=
append
(
handshakeOptions
,
gost
.
AddrHandshakeOption
(
ip
))
nodes
=
append
(
nodes
,
node
)
}
if
len
(
ips
)
==
0
{
node
.
HandshakeOptions
=
handshakeOptions
nodes
=
[]
gost
.
Node
{
node
}
}
return
}
func
serve
(
chain
*
gost
.
Chain
)
error
{
for
_
,
ns
:=
range
options
.
ServeNodes
{
func
(
r
*
route
)
serve
()
error
{
chain
,
err
:=
r
.
initChain
()
if
err
!=
nil
{
return
err
}
for
_
,
ns
:=
range
r
.
ServeNodes
{
node
,
err
:=
gost
.
ParseNode
(
ns
)
if
err
!=
nil
{
return
err
...
...
@@ -475,9 +518,24 @@ func loadConfigureFile(configureFile string) error {
if
err
!=
nil
{
return
err
}
if
err
:=
json
.
Unmarshal
(
content
,
&
options
);
err
!=
nil
{
var
cfg
struct
{
route
Routes
[]
route
}
if
err
:=
json
.
Unmarshal
(
content
,
&
cfg
);
err
!=
nil
{
return
err
}
if
len
(
cfg
.
route
.
ServeNodes
)
>
0
{
routes
=
append
(
routes
,
cfg
.
route
)
}
for
_
,
route
:=
range
cfg
.
Routes
{
if
len
(
cfg
.
route
.
ServeNodes
)
>
0
{
routes
=
append
(
routes
,
route
)
}
}
gost
.
Debug
=
cfg
.
Debug
return
nil
}
...
...
@@ -544,16 +602,23 @@ func parseUsers(authFile string) (users []*url.Userinfo, err error) {
return
}
func
parseIP
(
s
string
)
(
ips
[]
string
)
{
func
parseIP
(
s
string
,
port
string
)
(
ips
[]
string
)
{
if
s
==
""
{
return
nil
return
}
if
port
==
""
{
port
=
"8080"
// default port
}
file
,
err
:=
os
.
Open
(
s
)
if
err
!=
nil
{
ss
:=
strings
.
Split
(
s
,
","
)
for
_
,
s
:=
range
ss
{
s
=
strings
.
TrimSpace
(
s
)
if
s
!=
""
{
if
!
strings
.
Contains
(
s
,
":"
)
{
s
=
s
+
":"
+
port
}
ips
=
append
(
ips
,
s
)
}
...
...
@@ -567,15 +632,20 @@ func parseIP(s string) (ips []string) {
if
line
==
""
||
strings
.
HasPrefix
(
line
,
"#"
)
{
continue
}
if
!
strings
.
Contains
(
line
,
":"
)
{
line
=
line
+
":"
+
port
}
ips
=
append
(
ips
,
line
)
}
return
}
type
peerConfig
struct
{
Strategy
string
`json:"strategy"`
Filters
[]
string
`json:"filters"`
Nodes
[]
string
`json:"nodes"`
Strategy
string
`json:"strategy"`
Filters
[]
string
`json:"filters"`
MaxFails
int
`json:"max_fails"`
FailTimeout
int
`json:"fail_timeout"`
Nodes
[]
string
`json:"nodes"`
}
func
loadPeerConfig
(
peer
string
)
(
config
peerConfig
,
err
error
)
{
...
...
@@ -590,13 +660,23 @@ func loadPeerConfig(peer string) (config peerConfig, err error) {
return
}
func
(
cfg
*
peerConfig
)
Validate
()
{
if
cfg
.
MaxFails
<=
0
{
cfg
.
MaxFails
=
3
}
if
cfg
.
FailTimeout
<=
0
{
cfg
.
FailTimeout
=
30
// seconds
}
}
func
parseStrategy
(
s
string
)
gost
.
Strategy
{
switch
s
{
case
"round"
:
return
&
gost
.
RoundStrategy
{}
case
"random"
:
return
&
gost
.
RandomStrategy
{}
case
"round"
:
fallthrough
default
:
return
&
gost
.
RandomStrategy
{}
return
&
gost
.
RoundStrategy
{}
}
}
cmd/gost/peer.json
0 → 100644
View file @
b3068a7d
{
"strategy"
:
"round"
,
"max_fails"
:
3
,
"fail_timeout"
:
30
,
"nodes"
:[
"socks5://:1081"
,
"socks://:1082"
,
"socks4a://:1083"
]
}
\ No newline at end of file
gost.go
View file @
b3068a7d
...
...
@@ -56,6 +56,7 @@ func SetLogger(logger log.Logger) {
log
.
DefaultLogger
=
logger
}
// GenCertificate generates a random TLS certificate
func
GenCertificate
()
(
cert
tls
.
Certificate
,
err
error
)
{
rawCert
,
rawKey
,
err
:=
generateKeyPair
()
if
err
!=
nil
{
...
...
handler.go
View file @
b3068a7d
...
...
@@ -95,7 +95,8 @@ func (h *autoHandler) Handle(conn net.Conn) {
cc
:=
&
bufferdConn
{
Conn
:
conn
,
br
:
br
}
switch
b
[
0
]
{
case
gosocks4
.
Ver4
:
return
// SOCKS4(a) does not suppport authentication method, so we ignore it.
cc
.
Close
()
return
// SOCKS4(a) does not suppport authentication method, so we ignore it for security reason.
case
gosocks5
.
Ver5
:
SOCKS5Handler
(
h
.
options
...
)
.
Handle
(
cc
)
default
:
// http
...
...
http.go
View file @
b3068a7d
...
...
@@ -93,6 +93,13 @@ func (h *httpHandler) Handle(conn net.Conn) {
return
}
h
.
handleRequest
(
conn
,
req
)
}
func
(
h
*
httpHandler
)
handleRequest
(
conn
net
.
Conn
,
req
*
http
.
Request
)
{
if
req
==
nil
{
return
}
if
Debug
{
dump
,
_
:=
httputil
.
DumpRequest
(
req
,
false
)
log
.
Logf
(
"[http] %s -> %s
\n
%s"
,
conn
.
RemoteAddr
(),
req
.
Host
,
string
(
dump
))
...
...
node.go
View file @
b3068a7d
package
gost
import
(
"fmt"
"net/url"
"strings"
"sync/atomic"
"time"
)
// Node is a proxy node, mainly used to construct a proxy chain.
type
Node
struct
{
ID
int
Addr
string
IPs
[]
string
Host
string
Protocol
string
Transport
string
Remote
string
// remote address, used by tcp/udp port forwarding
...
...
@@ -17,7 +21,9 @@ type Node struct {
DialOptions
[]
DialOption
HandshakeOptions
[]
HandshakeOption
Client
*
Client
IPSelector
IPSelector
group
*
NodeGroup
failCount
uint32
failTime
int64
}
// ParseNode parses the node info.
...
...
@@ -38,6 +44,7 @@ func ParseNode(s string) (node Node, err error) {
node
=
Node
{
Addr
:
u
.
Host
,
Host
:
u
.
Host
,
Remote
:
strings
.
Trim
(
u
.
EscapedPath
(),
"/"
),
Values
:
u
.
Query
(),
User
:
u
.
User
,
...
...
@@ -79,8 +86,68 @@ func ParseNode(s string) (node Node, err error) {
return
}
// MarkDead marks the node fail status.
func
(
node
*
Node
)
MarkDead
()
{
atomic
.
AddUint32
(
&
node
.
failCount
,
1
)
atomic
.
StoreInt64
(
&
node
.
failTime
,
time
.
Now
()
.
Unix
())
if
node
.
group
==
nil
{
return
}
for
i
:=
range
node
.
group
.
nodes
{
if
node
.
group
.
nodes
[
i
]
.
ID
==
node
.
ID
{
atomic
.
AddUint32
(
&
node
.
group
.
nodes
[
i
]
.
failCount
,
1
)
atomic
.
StoreInt64
(
&
node
.
group
.
nodes
[
i
]
.
failTime
,
time
.
Now
()
.
Unix
())
break
}
}
}
// ResetDead resets the node fail status.
func
(
node
*
Node
)
ResetDead
()
{
atomic
.
StoreUint32
(
&
node
.
failCount
,
0
)
atomic
.
StoreInt64
(
&
node
.
failTime
,
0
)
if
node
.
group
==
nil
{
return
}
for
i
:=
range
node
.
group
.
nodes
{
if
node
.
group
.
nodes
[
i
]
.
ID
==
node
.
ID
{
atomic
.
StoreUint32
(
&
node
.
group
.
nodes
[
i
]
.
failCount
,
0
)
atomic
.
StoreInt64
(
&
node
.
group
.
nodes
[
i
]
.
failTime
,
0
)
break
}
}
}
// Clone clones the node, it will prevent data race.
func
(
node
*
Node
)
Clone
()
Node
{
return
Node
{
ID
:
node
.
ID
,
Addr
:
node
.
Addr
,
Host
:
node
.
Host
,
Protocol
:
node
.
Protocol
,
Transport
:
node
.
Transport
,
Remote
:
node
.
Remote
,
User
:
node
.
User
,
Values
:
node
.
Values
,
DialOptions
:
node
.
DialOptions
,
HandshakeOptions
:
node
.
HandshakeOptions
,
Client
:
node
.
Client
,
group
:
node
.
group
,
failCount
:
atomic
.
LoadUint32
(
&
node
.
failCount
),
failTime
:
atomic
.
LoadInt64
(
&
node
.
failTime
),
}
}
func
(
node
*
Node
)
String
()
string
{
return
fmt
.
Sprintf
(
"%d@%s"
,
node
.
ID
,
node
.
Addr
)
}
// NodeGroup is a group of nodes.
type
NodeGroup
struct
{
ID
int
nodes
[]
Node
Options
[]
SelectOption
Selector
NodeSelector
...
...
@@ -94,17 +161,34 @@ func NewNodeGroup(nodes ...Node) *NodeGroup {
}
// AddNode adds node or node list into group
func
(
ng
*
NodeGroup
)
AddNode
(
node
...
Node
)
{
if
ng
==
nil
{
func
(
group
*
NodeGroup
)
AddNode
(
node
...
Node
)
{
if
group
==
nil
{
return
}
ng
.
nodes
=
append
(
ng
.
nodes
,
node
...
)
group
.
nodes
=
append
(
group
.
nodes
,
node
...
)
}
// Nodes returns node list in the group
func
(
ng
*
NodeGroup
)
Nodes
()
[]
Node
{
if
ng
==
nil
{
func
(
group
*
NodeGroup
)
Nodes
()
[]
Node
{
if
group
==
nil
{
return
nil
}
return
ng
.
nodes
return
group
.
nodes
}
// Next selects the next node from group.
// It also selects IP if the IP list exists.
func
(
group
*
NodeGroup
)
Next
()
(
node
Node
,
err
error
)
{
selector
:=
group
.
Selector
if
selector
==
nil
{
selector
=
&
defaultSelector
{}
}
// select node from node group
node
,
err
=
selector
.
Select
(
group
.
Nodes
(),
group
.
Options
...
)
if
err
!=
nil
{
return
}
node
.
group
=
group
return
}
obfs.go
View file @
b3068a7d
...
...
@@ -35,7 +35,7 @@ func (tr *obfsHTTPTransporter) Handshake(conn net.Conn, options ...HandshakeOpti
for
_
,
option
:=
range
options
{
option
(
opts
)
}
return
&
obfsHTTPConn
{
Conn
:
conn
},
nil
return
&
obfsHTTPConn
{
Conn
:
conn
,
host
:
opts
.
Host
},
nil
}
type
obfsHTTPListener
struct
{
...
...
@@ -66,6 +66,7 @@ func (l *obfsHTTPListener) Accept() (net.Conn, error) {
type
obfsHTTPConn
struct
{
net
.
Conn
host
string
request
*
http
.
Request
response
*
http
.
Response
rbuf
[]
byte
...
...
@@ -151,7 +152,7 @@ func (c *obfsHTTPConn) clientHandshake() (err error) {
Method
:
http
.
MethodGet
,
ProtoMajor
:
1
,
ProtoMinor
:
1
,
URL
:
&
url
.
URL
{
Scheme
:
"http"
,
Host
:
"www.baidu.com"
},
URL
:
&
url
.
URL
{
Scheme
:
"http"
,
Host
:
c
.
host
},
Header
:
make
(
http
.
Header
),
}
r
.
Header
.
Set
(
"Connection"
,
"keep-alive"
)
...
...
selector.go
View file @
b3068a7d
...
...
@@ -2,6 +2,8 @@ package gost
import
(
"errors"
"math/rand"
"sync"
"sync/atomic"
"time"
)
...
...
@@ -37,9 +39,28 @@ func (s *defaultSelector) Select(nodes []Node, opts ...SelectOption) (Node, erro
return
sopts
.
Strategy
.
Apply
(
nodes
),
nil
}
// Filter is used to filter a node during the selection process
type
Filter
interface
{
Filter
([]
Node
)
[]
Node
// SelectOption used when making a select call
type
SelectOption
func
(
*
SelectOptions
)
// SelectOptions is the options for node selection
type
SelectOptions
struct
{
Filters
[]
Filter
Strategy
Strategy
}
// WithFilter adds a filter function to the list of filters
// used during the Select call.
func
WithFilter
(
f
...
Filter
)
SelectOption
{
return
func
(
o
*
SelectOptions
)
{
o
.
Filters
=
append
(
o
.
Filters
,
f
...
)
}
}
// WithStrategy sets the selector strategy
func
WithStrategy
(
s
Strategy
)
SelectOption
{
return
func
(
o
*
SelectOptions
)
{
o
.
Strategy
=
s
}
}
// Strategy is a selection strategy e.g random, round robin
...
...
@@ -58,7 +79,7 @@ func (s *RoundStrategy) Apply(nodes []Node) Node {
if
len
(
nodes
)
==
0
{
return
Node
{}
}
old
:=
s
.
count
old
:=
atomic
.
LoadUint64
(
&
s
.
count
)
atomic
.
AddUint64
(
&
s
.
count
,
1
)
return
nodes
[
int
(
old
%
uint64
(
len
(
nodes
)))]
}
...
...
@@ -68,82 +89,60 @@ func (s *RoundStrategy) String() string {
}
// RandomStrategy is a strategy for node selector
type
RandomStrategy
struct
{}
type
RandomStrategy
struct
{
Seed
int64
rand
*
rand
.
Rand
once
sync
.
Once
}
// Apply applies the random strategy for the nodes
func
(
s
*
RandomStrategy
)
Apply
(
nodes
[]
Node
)
Node
{
s
.
once
.
Do
(
func
()
{
seed
:=
s
.
Seed
if
seed
==
0
{
seed
=
time
.
Now
()
.
UnixNano
()
}
s
.
rand
=
rand
.
New
(
rand
.
NewSource
(
seed
))
})
if
len
(
nodes
)
==
0
{
return
Node
{}
}
return
nodes
[
time
.
Now
()
.
Nanosecond
()
%
len
(
nodes
)]
return
nodes
[
s
.
rand
.
Int
()
%
len
(
nodes
)]
}
func
(
s
*
RandomStrategy
)
String
()
string
{
return
"random"
}
// SelectOption used when making a select call
type
SelectOption
func
(
*
SelectOptions
)
// SelectOptions is the options for node selection
type
SelectOptions
struct
{
Filters
[]
Filter
Strategy
Strategy
}
// WithFilter adds a filter function to the list of filters
// used during the Select call.
func
WithFilter
(
f
...
Filter
)
SelectOption
{
return
func
(
o
*
SelectOptions
)
{
o
.
Filters
=
append
(
o
.
Filters
,
f
...
)
}
}
// WithStrategy sets the selector strategy
func
WithStrategy
(
s
Strategy
)
SelectOption
{
return
func
(
o
*
SelectOptions
)
{
o
.
Strategy
=
s
}
}
// IPSelector as a mechanism to pick IPs and mark their status.
type
IPSelector
interface
{
Select
(
ips
[]
string
)
(
string
,
error
)
// Filter is used to filter a node during the selection process
type
Filter
interface
{
Filter
([]
Node
)
[]
Node
String
()
string
}
// RandomIPSelector is an IP Selector that selects an IP with random strategy.
type
RandomIPSelector
struct
{
// FailFilter filters the dead node.
// A node is marked as dead if its failed count is greater than MaxFails.
type
FailFilter
struct
{
MaxFails
int
FailTimeout
time
.
Duration
}
//
Select selects an IP from ips list
.
func
(
s
*
RandomIPSelector
)
Select
(
ips
[]
string
)
(
string
,
error
)
{
if
len
(
ips
)
=
=
0
{
return
""
,
nil
//
Filter filters nodes
.
func
(
f
*
FailFilter
)
Filter
(
nodes
[]
Node
)
[]
Node
{
if
f
.
MaxFails
<
=
0
{
return
nodes
}
return
ips
[
time
.
Now
()
.
Nanosecond
()
%
len
(
ips
)],
nil
}
func
(
s
*
RandomIPSelector
)
String
()
string
{
return
"random"
}
// RoundRobinIPSelector is an IP Selector that selects an IP with round-robin strategy.
type
RoundRobinIPSelector
struct
{
count
uint64
}
// Select selects an IP from ips list.
func
(
s
*
RoundRobinIPSelector
)
Select
(
ips
[]
string
)
(
string
,
error
)
{
if
len
(
ips
)
==
0
{
return
""
,
nil
nl
:=
[]
Node
{}
for
i
:=
range
nodes
{
if
atomic
.
LoadUint32
(
&
nodes
[
i
]
.
failCount
)
<
uint32
(
f
.
MaxFails
)
||
time
.
Since
(
time
.
Unix
(
atomic
.
LoadInt64
(
&
nodes
[
i
]
.
failTime
),
0
))
>=
f
.
FailTimeout
{
nl
=
append
(
nl
,
nodes
[
i
]
.
Clone
())
}
}
old
:=
s
.
count
atomic
.
AddUint64
(
&
s
.
count
,
1
)
return
ips
[
int
(
old
%
uint64
(
len
(
ips
)))],
nil
return
nl
}
func
(
s
*
RoundRobinIPSelecto
r
)
String
()
string
{
return
"
round
"
func
(
f
*
FailFilte
r
)
String
()
string
{
return
"
fail
"
}
sni.go
View file @
b3068a7d
...
...
@@ -11,6 +11,7 @@ import (
"hash/crc32"
"io"
"net"
"net/http"
"strings"
"sync"
...
...
@@ -53,15 +54,22 @@ func (h *sniHandler) Handle(conn net.Conn) {
return
}
conn
=
&
bufferdConn
{
br
:
br
,
Conn
:
conn
}
defer
conn
.
Close
()
if
hdr
[
0
]
!=
dissector
.
Handshake
{
// We assume that it is HTTP request
HTTPHandler
(
h
.
options
...
)
.
Handle
(
conn
)
// We assume it is an HTTP request
req
,
err
:=
http
.
ReadRequest
(
bufio
.
NewReader
(
conn
))
if
!
req
.
URL
.
IsAbs
()
{
req
.
URL
.
Scheme
=
"http"
// make sure that the URL is absolute
}
if
err
!=
nil
{
log
.
Logf
(
"[sni] %s - %s : %s"
,
conn
.
RemoteAddr
(),
conn
.
LocalAddr
(),
err
)
return
}
HTTPHandler
(
h
.
options
...
)
.
(
*
httpHandler
)
.
handleRequest
(
conn
,
req
)
return
}
defer
conn
.
Close
()
b
,
host
,
err
:=
readClientHelloRecord
(
conn
,
""
,
false
)
if
err
!=
nil
{
log
.
Log
(
"[sni]"
,
err
)
...
...
ss.go
View file @
b3068a7d
...
...
@@ -124,11 +124,15 @@ func (h *shadowHandler) Handle(conn net.Conn) {
log
.
Logf
(
"[ss] %s - %s"
,
conn
.
RemoteAddr
(),
conn
.
LocalAddr
())
conn
.
SetReadDeadline
(
time
.
Now
()
.
Add
(
ReadTimeout
))
addr
,
err
:=
h
.
getRequest
(
conn
)
if
err
!=
nil
{
log
.
Logf
(
"[ss] %s - %s : %s"
,
conn
.
RemoteAddr
(),
conn
.
LocalAddr
(),
err
)
return
}
// clear timer
conn
.
SetReadDeadline
(
time
.
Time
{})
log
.
Logf
(
"[ss] %s -> %s"
,
conn
.
RemoteAddr
(),
addr
)
if
!
Can
(
"tcp"
,
addr
,
h
.
options
.
Whitelist
,
h
.
options
.
Blacklist
)
{
...
...
@@ -165,19 +169,16 @@ const (
)
// This function is copied from shadowsocks library with some modification.
func
(
h
*
shadowHandler
)
getRequest
(
conn
net
.
Conn
)
(
host
string
,
err
error
)
{
func
(
h
*
shadowHandler
)
getRequest
(
r
io
.
Reader
)
(
host
string
,
err
error
)
{
// buf size should at least have the same size with the largest possible
// request size (when addrType is 3, domain name has at most 256 bytes)
// 1(addrType) + 1(lenByte) + 256(max length address) + 2(port)
buf
:=
make
([]
byte
,
smallBufferSize
)
// read till we get possible domain length field
conn
.
SetReadDeadline
(
time
.
Now
()
.
Add
(
ReadTimeout
))
if
_
,
err
=
io
.
ReadFull
(
conn
,
buf
[
:
idType
+
1
]);
err
!=
nil
{
if
_
,
err
=
io
.
ReadFull
(
r
,
buf
[
:
idType
+
1
]);
err
!=
nil
{
return
}
// clear timer
conn
.
SetReadDeadline
(
time
.
Time
{})
var
reqStart
,
reqEnd
int
addrType
:=
buf
[
idType
]
...
...
@@ -187,16 +188,16 @@ func (h *shadowHandler) getRequest(conn net.Conn) (host string, err error) {
case
typeIPv6
:
reqStart
,
reqEnd
=
idIP0
,
idIP0
+
lenIPv6
case
typeDm
:
if
_
,
err
=
io
.
ReadFull
(
conn
,
buf
[
idType
+
1
:
idDmLen
+
1
]);
err
!=
nil
{
if
_
,
err
=
io
.
ReadFull
(
r
,
buf
[
idType
+
1
:
idDmLen
+
1
]);
err
!=
nil
{
return
}
reqStart
,
reqEnd
=
idDm0
,
i
nt
(
idDm0
+
buf
[
idDmLen
]
+
lenDmBase
)
reqStart
,
reqEnd
=
idDm0
,
i
dDm0
+
int
(
buf
[
idDmLen
])
+
lenDmBase
default
:
err
=
fmt
.
Errorf
(
"addr type %d not supported"
,
addrType
&
ss
.
AddrMask
)
return
}
if
_
,
err
=
io
.
ReadFull
(
conn
,
buf
[
reqStart
:
reqEnd
]);
err
!=
nil
{
if
_
,
err
=
io
.
ReadFull
(
r
,
buf
[
reqStart
:
reqEnd
]);
err
!=
nil
{
return
}
...
...
@@ -209,7 +210,7 @@ func (h *shadowHandler) getRequest(conn net.Conn) (host string, err error) {
case
typeIPv6
:
host
=
net
.
IP
(
buf
[
idIP0
:
idIP0
+
net
.
IPv6len
])
.
String
()
case
typeDm
:
host
=
string
(
buf
[
idDm0
:
idDm0
+
buf
[
idDmLen
]
])
host
=
string
(
buf
[
idDm0
:
idDm0
+
int
(
buf
[
idDmLen
])
])
}
// parse port
port
:=
binary
.
BigEndian
.
Uint16
(
buf
[
reqEnd
-
2
:
reqEnd
])
...
...
ssh.go
View file @
b3068a7d
...
...
@@ -830,13 +830,13 @@ func (c *sshConn) RemoteAddr() net.Addr {
}
func
(
c
*
sshConn
)
SetDeadline
(
t
time
.
Time
)
error
{
return
&
net
.
OpError
{
Op
:
"set"
,
Net
:
"ssh"
,
Source
:
nil
,
Addr
:
nil
,
Err
:
errors
.
New
(
"deadline not supported"
)}
return
nil
}
func
(
c
*
sshConn
)
SetReadDeadline
(
t
time
.
Time
)
error
{
return
&
net
.
OpError
{
Op
:
"set"
,
Net
:
"ssh"
,
Source
:
nil
,
Addr
:
nil
,
Err
:
errors
.
New
(
"deadline not supported"
)}
return
nil
}
func
(
c
*
sshConn
)
SetWriteDeadline
(
t
time
.
Time
)
error
{
return
&
net
.
OpError
{
Op
:
"set"
,
Net
:
"ssh"
,
Source
:
nil
,
Addr
:
nil
,
Err
:
errors
.
New
(
"deadline not supported"
)}
return
nil
}
ws.go
View file @
b3068a7d
...
...
@@ -129,7 +129,7 @@ func (tr *wsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (n
if
opts
.
WSOptions
!=
nil
{
wsOptions
=
opts
.
WSOptions
}
url
:=
url
.
URL
{
Scheme
:
"ws"
,
Host
:
opts
.
Addr
,
Path
:
"/ws"
}
url
:=
url
.
URL
{
Scheme
:
"ws"
,
Host
:
opts
.
Host
,
Path
:
"/ws"
}
return
websocketClientConn
(
url
.
String
(),
conn
,
nil
,
wsOptions
)
}
...
...
@@ -210,7 +210,7 @@ func (tr *mwsTransporter) initSession(addr string, conn net.Conn, opts *Handshak
if
opts
.
WSOptions
!=
nil
{
wsOptions
=
opts
.
WSOptions
}
url
:=
url
.
URL
{
Scheme
:
"ws"
,
Host
:
opts
.
Addr
,
Path
:
"/ws"
}
url
:=
url
.
URL
{
Scheme
:
"ws"
,
Host
:
opts
.
Host
,
Path
:
"/ws"
}
conn
,
err
:=
websocketClientConn
(
url
.
String
(),
conn
,
nil
,
wsOptions
)
if
err
!=
nil
{
return
nil
,
err
...
...
@@ -252,7 +252,7 @@ func (tr *wssTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (
if
opts
.
TLSConfig
==
nil
{
opts
.
TLSConfig
=
&
tls
.
Config
{
InsecureSkipVerify
:
true
}
}
url
:=
url
.
URL
{
Scheme
:
"wss"
,
Host
:
opts
.
Addr
,
Path
:
"/ws"
}
url
:=
url
.
URL
{
Scheme
:
"wss"
,
Host
:
opts
.
Host
,
Path
:
"/ws"
}
return
websocketClientConn
(
url
.
String
(),
conn
,
opts
.
TLSConfig
,
wsOptions
)
}
...
...
@@ -337,7 +337,7 @@ func (tr *mwssTransporter) initSession(addr string, conn net.Conn, opts *Handsha
if
tlsConfig
==
nil
{
tlsConfig
=
&
tls
.
Config
{
InsecureSkipVerify
:
true
}
}
url
:=
url
.
URL
{
Scheme
:
"wss"
,
Host
:
opts
.
Addr
,
Path
:
"/ws"
}
url
:=
url
.
URL
{
Scheme
:
"wss"
,
Host
:
opts
.
Host
,
Path
:
"/ws"
}
conn
,
err
:=
websocketClientConn
(
url
.
String
(),
conn
,
tlsConfig
,
wsOptions
)
if
err
!=
nil
{
return
nil
,
err
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment