#77 add SOCKS4(A) support
This commit is contained in:
parent
333291e9bc
commit
5efffa7d39
14
README.md
14
README.md
@ -9,7 +9,7 @@ gost - GO Simple Tunnel
|
||||
------
|
||||
* 可同时监听多端口
|
||||
* 可设置转发代理,支持多级转发(代理链)
|
||||
* 支持标准HTTP/HTTPS/SOCKS5代理协议
|
||||
* 支持标准HTTP/HTTPS/SOCKS4(A)/SOCKS5代理协议
|
||||
* SOCKS5代理支持TLS协商加密
|
||||
* Tunnel UDP over TCP
|
||||
* 支持Shadowsocks协议 (OTA: 2.2+,UDP: 2.4+)
|
||||
@ -36,7 +36,7 @@ Google讨论组: https://groups.google.com/d/forum/go-gost
|
||||
```
|
||||
scheme分为两部分: protocol+transport
|
||||
|
||||
protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp), 二者可以任意组合,或单独使用:
|
||||
protocol: 代理协议类型(http, socks4(a), socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp, pht), 二者可以任意组合,或单独使用:
|
||||
|
||||
> http - HTTP代理: http://:8080
|
||||
|
||||
@ -44,11 +44,13 @@ protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输
|
||||
|
||||
> http2 - HTTP2代理并向下兼容HTTPS代理: http2://:443
|
||||
|
||||
> socks - 标准SOCKS5代理(支持tls协商加密): socks://:1080
|
||||
> socks4(a) - 标准SOCKS4(A)代理: socks4://:1080或socks4a://:1080
|
||||
|
||||
> socks - 标准SOCKS5代理(支持TLS协商加密): socks://:1080
|
||||
|
||||
> socks+wss - SOCKS5代理,使用websocket传输数据: socks+wss://:1080
|
||||
|
||||
> tls - HTTPS/SOCKS5代理,使用tls传输数据: tls://:443
|
||||
> tls - HTTPS/SOCKS5代理,使用TLS传输数据: tls://:443
|
||||
|
||||
> ss - Shadowsocks代理,ss://chacha20:123456@:8338
|
||||
|
||||
@ -56,7 +58,9 @@ protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输
|
||||
|
||||
> quic - QUIC代理,quic://:6121
|
||||
|
||||
> kcp - KCP代理,kcp://:8388或kcp://aes:123456@:8388
|
||||
> kcp - KCP通道,kcp://:8388或kcp://aes:123456@:8388
|
||||
|
||||
> pht - 普通HTTP通道,pht://:8080
|
||||
|
||||
> redirect - 透明代理,redirect://:12345
|
||||
|
||||
|
12
README_en.md
12
README_en.md
@ -7,7 +7,7 @@ Features
|
||||
------
|
||||
* Listening on multiple ports
|
||||
* Multi-level forward proxy - proxy chain
|
||||
* Standard HTTP/HTTPS/SOCKS5 proxy protocols support
|
||||
* Standard HTTP/HTTPS/SOCKS4(A)/SOCKS5 proxy protocols support
|
||||
* TLS encryption via negotiation support for SOCKS5 proxy
|
||||
* Tunnel UDP over TCP
|
||||
* Shadowsocks protocol support (OTA: 2.2+, UDP: 2.4+)
|
||||
@ -35,8 +35,8 @@ Effective for the -L and -F parameters
|
||||
```
|
||||
scheme can be divided into two parts: protocol+transport
|
||||
|
||||
protocol: proxy protocol types (http, socks5, shadowsocks),
|
||||
transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used in any combination or individually:
|
||||
protocol: proxy protocol types (http, socks4(a), socks5, shadowsocks),
|
||||
transport: data transmission mode (ws, wss, tls, http2, quic, kcp, pht), may be used in any combination or individually:
|
||||
|
||||
> http - standard HTTP proxy: http://:8080
|
||||
|
||||
@ -44,11 +44,13 @@ transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used
|
||||
|
||||
> http2 - HTTP2 proxy and backwards-compatible with HTTPS proxy: http2://:443
|
||||
|
||||
> socks4(a) - standard SOCKS4(A) proxy: socks4://:1080 or socks4a://:1080
|
||||
|
||||
> socks - standard SOCKS5 proxy: socks://:1080
|
||||
|
||||
> socks+wss - SOCKS5 over websocket: socks+wss://:1080
|
||||
|
||||
> tls - HTTPS/SOCKS5 over tls: tls://:443
|
||||
> tls - HTTPS/SOCKS5 over TLS: tls://:443
|
||||
|
||||
> ss - standard shadowsocks proxy, ss://chacha20:123456@:8338
|
||||
|
||||
@ -58,6 +60,8 @@ transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used
|
||||
|
||||
> kcp - standard KCP tunnel,kcp://:8388 or kcp://aes:123456@:8388
|
||||
|
||||
> pht - plain HTTP tunnel, pht://:8080
|
||||
|
||||
> redirect - transparent proxy,redirect://:12345
|
||||
|
||||
#### Port forwarding
|
||||
|
86
chain.go
86
chain.go
@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"github.com/ginuerzh/pht"
|
||||
"github.com/golang/glog"
|
||||
"github.com/lucas-clemente/quic-go/h2quic"
|
||||
"golang.org/x/net/http2"
|
||||
"io"
|
||||
"net"
|
||||
@ -30,7 +31,8 @@ type ProxyChain struct {
|
||||
kcpConfig *KCPConfig
|
||||
kcpSession *KCPSession
|
||||
kcpMutex sync.Mutex
|
||||
phttpClient *pht.Client
|
||||
phtClient *pht.Client
|
||||
quicClient *http.Client
|
||||
}
|
||||
|
||||
func NewProxyChain(nodes ...ProxyNode) *ProxyChain {
|
||||
@ -121,9 +123,21 @@ func (c *ProxyChain) Init() {
|
||||
return
|
||||
}
|
||||
|
||||
if c.nodes[0].Transport == "quic" {
|
||||
glog.V(LINFO).Infoln("QUIC is enabled")
|
||||
c.quicClient = &http.Client{
|
||||
Transport: &h2quic.QuicRoundTripper{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: c.nodes[0].insecureSkipVerify(),
|
||||
ServerName: c.nodes[0].serverName,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if c.nodes[0].Transport == "pht" {
|
||||
glog.V(LINFO).Infoln("Pure HTTP mode is enabled")
|
||||
c.phttpClient = pht.NewClient(c.nodes[0].Addr, c.nodes[0].Get("key"))
|
||||
c.phtClient = pht.NewClient(c.nodes[0].Addr, c.nodes[0].Get("key"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,6 +263,12 @@ func (c *ProxyChain) dialWithNodes(withHttp2 bool, addr string, nodes ...ProxyNo
|
||||
return c.http2Connect(addr)
|
||||
}
|
||||
}
|
||||
|
||||
if nodes[0].Transport == "quic" {
|
||||
glog.V(LINFO).Infoln("Dial with QUIC")
|
||||
return c.quicConnect(addr)
|
||||
}
|
||||
|
||||
pc, err := c.travelNodes(withHttp2, nodes...)
|
||||
if err != nil {
|
||||
return
|
||||
@ -277,7 +297,7 @@ func (c *ProxyChain) travelNodes(withHttp2 bool, nodes ...ProxyNode) (conn *Prox
|
||||
} else if node.Transport == "kcp" {
|
||||
cc, err = c.getKCPConn()
|
||||
} else if node.Transport == "pht" {
|
||||
cc, err = c.phttpClient.Dial()
|
||||
cc, err = c.phtClient.Dial()
|
||||
} else {
|
||||
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
|
||||
}
|
||||
@ -388,3 +408,63 @@ func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) {
|
||||
}
|
||||
return c.getHttp2Conn(header)
|
||||
}
|
||||
|
||||
func (c *ProxyChain) quicConnect(addr string) (net.Conn, error) {
|
||||
quicNode := c.nodes[0]
|
||||
header := make(http.Header)
|
||||
header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
|
||||
if quicNode.Users != nil {
|
||||
header.Set("Proxy-Authorization",
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(quicNode.Users[0].String())))
|
||||
}
|
||||
return c.getQuicConn(header)
|
||||
}
|
||||
|
||||
func (c *ProxyChain) getQuicConn(header http.Header) (net.Conn, error) {
|
||||
quicNode := c.nodes[0]
|
||||
pr, pw := io.Pipe()
|
||||
|
||||
if header == nil {
|
||||
header = make(http.Header)
|
||||
}
|
||||
|
||||
/*
|
||||
req := http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: &url.URL{Scheme: "https", Host: quicNode.Addr},
|
||||
Header: header,
|
||||
Proto: "HTTP/2.0",
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Body: pr,
|
||||
Host: quicNode.Addr,
|
||||
ContentLength: -1,
|
||||
}
|
||||
*/
|
||||
req, err := http.NewRequest(http.MethodPost, "https://"+quicNode.Addr, pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.ContentLength = -1
|
||||
req.Header = header
|
||||
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
resp, err := c.quicClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
conn := &http2Conn{r: resp.Body, w: pw}
|
||||
conn.remoteAddr, _ = net.ResolveUDPAddr("udp", quicNode.Addr)
|
||||
return conn, nil
|
||||
}
|
||||
|
21
cmd/gost/vendor/github.com/ginuerzh/gosocks4/LICENSE
generated
vendored
Normal file
21
cmd/gost/vendor/github.com/ginuerzh/gosocks4/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 ginuerzh
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2
cmd/gost/vendor/github.com/ginuerzh/gosocks4/README.md
generated
vendored
Normal file
2
cmd/gost/vendor/github.com/ginuerzh/gosocks4/README.md
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# gosocks4
|
||||
golang and SOCKS4(a)
|
252
cmd/gost/vendor/github.com/ginuerzh/gosocks4/socks4.go
generated
vendored
Normal file
252
cmd/gost/vendor/github.com/ginuerzh/gosocks4/socks4.go
generated
vendored
Normal file
@ -0,0 +1,252 @@
|
||||
// SOCKS Protocol Version 4(a)
|
||||
// https://www.openssh.com/txt/socks4.protocol
|
||||
// https://www.openssh.com/txt/socks4a.protocol
|
||||
package gosocks4
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
Ver4 = 4
|
||||
)
|
||||
|
||||
const (
|
||||
CmdConnect uint8 = 1
|
||||
CmdBind = 2
|
||||
)
|
||||
|
||||
const (
|
||||
AddrIPv4 = 0
|
||||
AddrDomain = 1
|
||||
)
|
||||
|
||||
const (
|
||||
Granted = 90
|
||||
Failed = 91
|
||||
Rejected = 92
|
||||
RejectedUserid = 93
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadVersion = errors.New("Bad version")
|
||||
ErrBadFormat = errors.New("Bad format")
|
||||
ErrBadAddrType = errors.New("Bad address type")
|
||||
ErrShortDataLength = errors.New("Short data length")
|
||||
ErrBadCmd = errors.New("Bad Command")
|
||||
)
|
||||
|
||||
type Addr struct {
|
||||
Type int
|
||||
Host string
|
||||
Port uint16
|
||||
}
|
||||
|
||||
func (addr *Addr) Decode(b []byte) error {
|
||||
if len(b) < 6 {
|
||||
return ErrShortDataLength
|
||||
}
|
||||
|
||||
addr.Port = binary.BigEndian.Uint16(b[0:2])
|
||||
addr.Host = net.IP(b[2 : 2+net.IPv4len]).String()
|
||||
|
||||
if b[2]|b[3]|b[4] == 0 {
|
||||
addr.Type = AddrDomain
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (addr *Addr) Encode(b []byte) error {
|
||||
if len(b) < 6 {
|
||||
return ErrShortDataLength
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(b[0:2], addr.Port)
|
||||
|
||||
switch addr.Type {
|
||||
case AddrIPv4:
|
||||
ip4 := net.ParseIP(addr.Host).To4()
|
||||
if ip4 == nil {
|
||||
return ErrBadAddrType
|
||||
}
|
||||
copy(b[2:], ip4)
|
||||
case AddrDomain:
|
||||
ip4 := net.IPv4(0, 0, 0, 1)
|
||||
copy(b[2:], ip4)
|
||||
default:
|
||||
return ErrBadAddrType
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (addr *Addr) String() string {
|
||||
return net.JoinHostPort(addr.Host, strconv.Itoa(int(addr.Port)))
|
||||
}
|
||||
|
||||
/*
|
||||
+----+----+----+----+----+----+----+----+----+----+....+----+
|
||||
| VN | CD | DSTPORT | DSTIP | USERID |NULL|
|
||||
+----+----+----+----+----+----+----+----+----+----+....+----+
|
||||
1 1 2 4 variable 1
|
||||
*/
|
||||
type Request struct {
|
||||
Cmd uint8
|
||||
Addr *Addr
|
||||
Userid []byte
|
||||
}
|
||||
|
||||
func NewRequest(cmd uint8, addr *Addr, userid []byte) *Request {
|
||||
return &Request{
|
||||
Cmd: cmd,
|
||||
Addr: addr,
|
||||
Userid: userid,
|
||||
}
|
||||
}
|
||||
|
||||
func ReadRequest(r io.Reader) (*Request, error) {
|
||||
br := bufio.NewReader(r)
|
||||
b, err := br.Peek(8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b[0] != Ver4 {
|
||||
return nil, ErrBadVersion
|
||||
}
|
||||
|
||||
request := &Request{
|
||||
Cmd: b[1],
|
||||
}
|
||||
|
||||
addr := &Addr{}
|
||||
if err := addr.Decode(b[2:8]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Addr = addr
|
||||
|
||||
if _, err := br.Discard(8); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err = br.ReadBytes(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Userid = b[:len(b)-1]
|
||||
|
||||
if request.Addr.Type == AddrDomain {
|
||||
b, err = br.ReadBytes(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Addr.Host = string(b[:len(b)-1])
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func (r *Request) Write(w io.Writer) (err error) {
|
||||
bw := bufio.NewWriter(w)
|
||||
bw.Write([]byte{Ver4, r.Cmd})
|
||||
|
||||
if r.Addr == nil {
|
||||
return ErrBadAddrType
|
||||
}
|
||||
|
||||
var b [6]byte
|
||||
if err = r.Addr.Encode(b[:]); err != nil {
|
||||
return
|
||||
}
|
||||
bw.Write(b[:])
|
||||
|
||||
if len(r.Userid) > 0 {
|
||||
bw.Write(r.Userid)
|
||||
}
|
||||
bw.WriteByte(0)
|
||||
|
||||
if r.Addr.Type == AddrDomain {
|
||||
bw.WriteString(r.Addr.Host)
|
||||
bw.WriteByte(0)
|
||||
}
|
||||
|
||||
return bw.Flush()
|
||||
}
|
||||
|
||||
func (r *Request) String() string {
|
||||
addr := r.Addr
|
||||
if addr == nil {
|
||||
addr = &Addr{}
|
||||
}
|
||||
return fmt.Sprintf("%d %d %s", Ver4, r.Cmd, addr.String())
|
||||
}
|
||||
|
||||
/*
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| VN | CD | DSTPORT | DSTIP |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
1 1 2 4
|
||||
*/
|
||||
type Reply struct {
|
||||
Code uint8
|
||||
Addr *Addr
|
||||
}
|
||||
|
||||
func NewReply(code uint8, addr *Addr) *Reply {
|
||||
return &Reply{
|
||||
Code: code,
|
||||
Addr: addr,
|
||||
}
|
||||
}
|
||||
|
||||
func ReadReply(r io.Reader) (*Reply, error) {
|
||||
var b [8]byte
|
||||
|
||||
_, err := io.ReadFull(r, b[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b[0] != 0 {
|
||||
return nil, ErrBadVersion
|
||||
}
|
||||
|
||||
reply := &Reply{
|
||||
Code: b[1],
|
||||
}
|
||||
|
||||
reply.Addr = &Addr{}
|
||||
if err := reply.Addr.Decode(b[2:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
func (r *Reply) Write(w io.Writer) (err error) {
|
||||
var b [8]byte
|
||||
|
||||
b[1] = r.Code
|
||||
if r.Addr != nil {
|
||||
if err = r.Addr.Encode(b[2:]); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err = w.Write(b[:])
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Reply) String() string {
|
||||
addr := r.Addr
|
||||
if addr == nil {
|
||||
addr = &Addr{}
|
||||
}
|
||||
return fmt.Sprintf("0 %d %s", r.Code, addr.String())
|
||||
}
|
152
cmd/gost/vendor/github.com/ginuerzh/gosocks4/socks4.protocol.txt
generated
vendored
Normal file
152
cmd/gost/vendor/github.com/ginuerzh/gosocks4/socks4.protocol.txt
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
SOCKS: A protocol for TCP proxy across firewalls
|
||||
|
||||
Ying-Da Lee
|
||||
Principal Member Technical Staff
|
||||
NEC Systems Laboratory, CSTC
|
||||
ylee@syl.dl.nec.com
|
||||
|
||||
SOCKS was originally developed by David Koblas and subsequently modified
|
||||
and extended by me to its current running version -- version 4. It is a
|
||||
protocol that relays TCP sessions at a firewall host to allow application
|
||||
users transparent access across the firewall. Because the protocol is
|
||||
independent of application protocols, it can be (and has been) used for
|
||||
many different services, such as telnet, ftp, finger, whois, gopher, WWW,
|
||||
etc. Access control can be applied at the beginning of each TCP session;
|
||||
thereafter the server simply relays the data between the client and the
|
||||
application server, incurring minimum processing overhead. Since SOCKS
|
||||
never has to know anything about the application protocol, it should also
|
||||
be easy for it to accommodate applications which use encryption to protect
|
||||
their traffic from nosey snoopers.
|
||||
|
||||
Two operations are defined: CONNECT and BIND.
|
||||
|
||||
1) CONNECT
|
||||
|
||||
The client connects to the SOCKS server and sends a CONNECT request when
|
||||
it wants to establish a connection to an application server. The client
|
||||
includes in the request packet the IP address and the port number of the
|
||||
destination host, and userid, in the following format.
|
||||
|
||||
+----+----+----+----+----+----+----+----+----+----+....+----+
|
||||
| VN | CD | DSTPORT | DSTIP | USERID |NULL|
|
||||
+----+----+----+----+----+----+----+----+----+----+....+----+
|
||||
# of bytes: 1 1 2 4 variable 1
|
||||
|
||||
VN is the SOCKS protocol version number and should be 4. CD is the
|
||||
SOCKS command code and should be 1 for CONNECT request. NULL is a byte
|
||||
of all zero bits.
|
||||
|
||||
The SOCKS server checks to see whether such a request should be granted
|
||||
based on any combination of source IP address, destination IP address,
|
||||
destination port number, the userid, and information it may obtain by
|
||||
consulting IDENT, cf. RFC 1413. If the request is granted, the SOCKS
|
||||
server makes a connection to the specified port of the destination host.
|
||||
A reply packet is sent to the client when this connection is established,
|
||||
or when the request is rejected or the operation fails.
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| VN | CD | DSTPORT | DSTIP |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
# of bytes: 1 1 2 4
|
||||
|
||||
VN is the version of the reply code and should be 0. CD is the result
|
||||
code with one of the following values:
|
||||
|
||||
90: request granted
|
||||
91: request rejected or failed
|
||||
92: request rejected becasue SOCKS server cannot connect to
|
||||
identd on the client
|
||||
93: request rejected because the client program and identd
|
||||
report different user-ids
|
||||
|
||||
The remaining fields are ignored.
|
||||
|
||||
The SOCKS server closes its connection immediately after notifying
|
||||
the client of a failed or rejected request. For a successful request,
|
||||
the SOCKS server gets ready to relay traffic on both directions. This
|
||||
enables the client to do I/O on its connection as if it were directly
|
||||
connected to the application server.
|
||||
|
||||
|
||||
2) BIND
|
||||
|
||||
The client connects to the SOCKS server and sends a BIND request when
|
||||
it wants to prepare for an inbound connection from an application server.
|
||||
This should only happen after a primary connection to the application
|
||||
server has been established with a CONNECT. Typically, this is part of
|
||||
the sequence of actions:
|
||||
|
||||
-bind(): obtain a socket
|
||||
-getsockname(): get the IP address and port number of the socket
|
||||
-listen(): ready to accept call from the application server
|
||||
-use the primary connection to inform the application server of
|
||||
the IP address and the port number that it should connect to.
|
||||
-accept(): accept a connection from the application server
|
||||
|
||||
The purpose of SOCKS BIND operation is to support such a sequence
|
||||
but using a socket on the SOCKS server rather than on the client.
|
||||
|
||||
The client includes in the request packet the IP address of the
|
||||
application server, the destination port used in the primary connection,
|
||||
and the userid.
|
||||
|
||||
+----+----+----+----+----+----+----+----+----+----+....+----+
|
||||
| VN | CD | DSTPORT | DSTIP | USERID |NULL|
|
||||
+----+----+----+----+----+----+----+----+----+----+....+----+
|
||||
# of bytes: 1 1 2 4 variable 1
|
||||
|
||||
VN is again 4 for the SOCKS protocol version number. CD must be 2 to
|
||||
indicate BIND request.
|
||||
|
||||
The SOCKS server uses the client information to decide whether the
|
||||
request is to be granted. The reply it sends back to the client has
|
||||
the same format as the reply for CONNECT request, i.e.,
|
||||
|
||||
+----+----+----+----+----+----+----+----+
|
||||
| VN | CD | DSTPORT | DSTIP |
|
||||
+----+----+----+----+----+----+----+----+
|
||||
# of bytes: 1 1 2 4
|
||||
|
||||
VN is the version of the reply code and should be 0. CD is the result
|
||||
code with one of the following values:
|
||||
|
||||
90: request granted
|
||||
91: request rejected or failed
|
||||
92: request rejected becasue SOCKS server cannot connect to
|
||||
identd on the client
|
||||
93: request rejected because the client program and identd
|
||||
report different user-ids.
|
||||
|
||||
However, for a granted request (CD is 90), the DSTPORT and DSTIP fields
|
||||
are meaningful. In that case, the SOCKS server obtains a socket to wait
|
||||
for an incoming connection and sends the port number and the IP address
|
||||
of that socket to the client in DSTPORT and DSTIP, respectively. If the
|
||||
DSTIP in the reply is 0 (the value of constant INADDR_ANY), then the
|
||||
client should replace it by the IP address of the SOCKS server to which
|
||||
the cleint is connected. (This happens if the SOCKS server is not a
|
||||
multi-homed host.) In the typical scenario, these two numbers are
|
||||
made available to the application client prgram via the result of the
|
||||
subsequent getsockname() call. The application protocol must provide a
|
||||
way for these two pieces of information to be sent from the client to
|
||||
the application server so that it can initiate the connection, which
|
||||
connects it to the SOCKS server rather than directly to the application
|
||||
client as it normally would.
|
||||
|
||||
The SOCKS server sends a second reply packet to the client when the
|
||||
anticipated connection from the application server is established.
|
||||
The SOCKS server checks the IP address of the originating host against
|
||||
the value of DSTIP specified in the client's BIND request. If a mismatch
|
||||
is found, the CD field in the second reply is set to 91 and the SOCKS
|
||||
server closes both connections. If the two match, CD in the second
|
||||
reply is set to 90 and the SOCKS server gets ready to relay the traffic
|
||||
on its two connections. From then on the client does I/O on its connection
|
||||
to the SOCKS server as if it were directly connected to the application
|
||||
server.
|
||||
|
||||
|
||||
|
||||
For both CONNECT and BIND operations, the server sets a time limit
|
||||
(2 minutes in current CSTC implementation) for the establishment of its
|
||||
connection with the application server. If the connection is still not
|
||||
establiched when the time limit expires, the server closes its connection
|
||||
to the client and gives up.
|
39
cmd/gost/vendor/github.com/ginuerzh/gosocks4/socks4a.protocol.txt
generated
vendored
Normal file
39
cmd/gost/vendor/github.com/ginuerzh/gosocks4/socks4a.protocol.txt
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
SOCKS 4A: A Simple Extension to SOCKS 4 Protocol
|
||||
|
||||
Ying-Da Lee
|
||||
yingda@best.com or yingda@esd.sgi.com
|
||||
|
||||
Please read SOCKS4.protocol first for an description of the version 4
|
||||
protocol. This extension is intended to allow the use of SOCKS on hosts
|
||||
which are not capable of resolving all domain names.
|
||||
|
||||
In version 4, the client sends the following packet to the SOCKS server
|
||||
to request a CONNECT or a BIND operation:
|
||||
|
||||
+----+----+----+----+----+----+----+----+----+----+....+----+
|
||||
| VN | CD | DSTPORT | DSTIP | USERID |NULL|
|
||||
+----+----+----+----+----+----+----+----+----+----+....+----+
|
||||
# of bytes: 1 1 2 4 variable 1
|
||||
|
||||
VN is the SOCKS protocol version number and should be 4. CD is the
|
||||
SOCKS command code and should be 1 for CONNECT or 2 for BIND. NULL
|
||||
is a byte of all zero bits.
|
||||
|
||||
For version 4A, if the client cannot resolve the destination host's
|
||||
domain name to find its IP address, it should set the first three bytes
|
||||
of DSTIP to NULL and the last byte to a non-zero value. (This corresponds
|
||||
to IP address 0.0.0.x, with x nonzero. As decreed by IANA -- The
|
||||
Internet Assigned Numbers Authority -- such an address is inadmissible
|
||||
as a destination IP address and thus should never occur if the client
|
||||
can resolve the domain name.) Following the NULL byte terminating
|
||||
USERID, the client must sends the destination domain name and termiantes
|
||||
it with another NULL byte. This is used for both CONNECT and BIND requests.
|
||||
|
||||
A server using protocol 4A must check the DSTIP in the request packet.
|
||||
If it represent address 0.0.0.x with nonzero x, the server must read
|
||||
in the domain name that the client sends in the packet. The server
|
||||
should resolve the domain name and make connection to the destination
|
||||
host if it can.
|
||||
|
||||
SOCKSified sockd may pass domain names that it cannot resolve to
|
||||
the next-hop SOCKS server.
|
14
cmd/gost/vendor/github.com/ginuerzh/gost/README.md
generated
vendored
14
cmd/gost/vendor/github.com/ginuerzh/gost/README.md
generated
vendored
@ -9,7 +9,7 @@ gost - GO Simple Tunnel
|
||||
------
|
||||
* 可同时监听多端口
|
||||
* 可设置转发代理,支持多级转发(代理链)
|
||||
* 支持标准HTTP/HTTPS/SOCKS5代理协议
|
||||
* 支持标准HTTP/HTTPS/SOCKS4(A)/SOCKS5代理协议
|
||||
* SOCKS5代理支持TLS协商加密
|
||||
* Tunnel UDP over TCP
|
||||
* 支持Shadowsocks协议 (OTA: 2.2+,UDP: 2.4+)
|
||||
@ -36,7 +36,7 @@ Google讨论组: https://groups.google.com/d/forum/go-gost
|
||||
```
|
||||
scheme分为两部分: protocol+transport
|
||||
|
||||
protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp), 二者可以任意组合,或单独使用:
|
||||
protocol: 代理协议类型(http, socks4(a), socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp, pht), 二者可以任意组合,或单独使用:
|
||||
|
||||
> http - HTTP代理: http://:8080
|
||||
|
||||
@ -44,11 +44,13 @@ protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输
|
||||
|
||||
> http2 - HTTP2代理并向下兼容HTTPS代理: http2://:443
|
||||
|
||||
> socks - 标准SOCKS5代理(支持tls协商加密): socks://:1080
|
||||
> socks4(a) - 标准SOCKS4(A)代理: socks4://:1080或socks4a://:1080
|
||||
|
||||
> socks - 标准SOCKS5代理(支持TLS协商加密): socks://:1080
|
||||
|
||||
> socks+wss - SOCKS5代理,使用websocket传输数据: socks+wss://:1080
|
||||
|
||||
> tls - HTTPS/SOCKS5代理,使用tls传输数据: tls://:443
|
||||
> tls - HTTPS/SOCKS5代理,使用TLS传输数据: tls://:443
|
||||
|
||||
> ss - Shadowsocks代理,ss://chacha20:123456@:8338
|
||||
|
||||
@ -56,7 +58,9 @@ protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输
|
||||
|
||||
> quic - QUIC代理,quic://:6121
|
||||
|
||||
> kcp - KCP代理,kcp://:8388或kcp://aes:123456@:8388
|
||||
> kcp - KCP通道,kcp://:8388或kcp://aes:123456@:8388
|
||||
|
||||
> pht - 普通HTTP通道,pht://:8080
|
||||
|
||||
> redirect - 透明代理,redirect://:12345
|
||||
|
||||
|
12
cmd/gost/vendor/github.com/ginuerzh/gost/README_en.md
generated
vendored
12
cmd/gost/vendor/github.com/ginuerzh/gost/README_en.md
generated
vendored
@ -7,7 +7,7 @@ Features
|
||||
------
|
||||
* Listening on multiple ports
|
||||
* Multi-level forward proxy - proxy chain
|
||||
* Standard HTTP/HTTPS/SOCKS5 proxy protocols support
|
||||
* Standard HTTP/HTTPS/SOCKS4(A)/SOCKS5 proxy protocols support
|
||||
* TLS encryption via negotiation support for SOCKS5 proxy
|
||||
* Tunnel UDP over TCP
|
||||
* Shadowsocks protocol support (OTA: 2.2+, UDP: 2.4+)
|
||||
@ -35,8 +35,8 @@ Effective for the -L and -F parameters
|
||||
```
|
||||
scheme can be divided into two parts: protocol+transport
|
||||
|
||||
protocol: proxy protocol types (http, socks5, shadowsocks),
|
||||
transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used in any combination or individually:
|
||||
protocol: proxy protocol types (http, socks4(a), socks5, shadowsocks),
|
||||
transport: data transmission mode (ws, wss, tls, http2, quic, kcp, pht), may be used in any combination or individually:
|
||||
|
||||
> http - standard HTTP proxy: http://:8080
|
||||
|
||||
@ -44,11 +44,13 @@ transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used
|
||||
|
||||
> http2 - HTTP2 proxy and backwards-compatible with HTTPS proxy: http2://:443
|
||||
|
||||
> socks4(a) - standard SOCKS4(A) proxy: socks4://:1080 or socks4a://:1080
|
||||
|
||||
> socks - standard SOCKS5 proxy: socks://:1080
|
||||
|
||||
> socks+wss - SOCKS5 over websocket: socks+wss://:1080
|
||||
|
||||
> tls - HTTPS/SOCKS5 over tls: tls://:443
|
||||
> tls - HTTPS/SOCKS5 over TLS: tls://:443
|
||||
|
||||
> ss - standard shadowsocks proxy, ss://chacha20:123456@:8338
|
||||
|
||||
@ -58,6 +60,8 @@ transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used
|
||||
|
||||
> kcp - standard KCP tunnel,kcp://:8388 or kcp://aes:123456@:8388
|
||||
|
||||
> pht - plain HTTP tunnel, pht://:8080
|
||||
|
||||
> redirect - transparent proxy,redirect://:12345
|
||||
|
||||
#### Port forwarding
|
||||
|
86
cmd/gost/vendor/github.com/ginuerzh/gost/chain.go
generated
vendored
86
cmd/gost/vendor/github.com/ginuerzh/gost/chain.go
generated
vendored
@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"github.com/ginuerzh/pht"
|
||||
"github.com/golang/glog"
|
||||
"github.com/lucas-clemente/quic-go/h2quic"
|
||||
"golang.org/x/net/http2"
|
||||
"io"
|
||||
"net"
|
||||
@ -30,7 +31,8 @@ type ProxyChain struct {
|
||||
kcpConfig *KCPConfig
|
||||
kcpSession *KCPSession
|
||||
kcpMutex sync.Mutex
|
||||
phttpClient *pht.Client
|
||||
phtClient *pht.Client
|
||||
quicClient *http.Client
|
||||
}
|
||||
|
||||
func NewProxyChain(nodes ...ProxyNode) *ProxyChain {
|
||||
@ -121,9 +123,21 @@ func (c *ProxyChain) Init() {
|
||||
return
|
||||
}
|
||||
|
||||
if c.nodes[0].Transport == "quic" {
|
||||
glog.V(LINFO).Infoln("QUIC is enabled")
|
||||
c.quicClient = &http.Client{
|
||||
Transport: &h2quic.QuicRoundTripper{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: c.nodes[0].insecureSkipVerify(),
|
||||
ServerName: c.nodes[0].serverName,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if c.nodes[0].Transport == "pht" {
|
||||
glog.V(LINFO).Infoln("Pure HTTP mode is enabled")
|
||||
c.phttpClient = pht.NewClient(c.nodes[0].Addr, c.nodes[0].Get("key"))
|
||||
c.phtClient = pht.NewClient(c.nodes[0].Addr, c.nodes[0].Get("key"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,6 +263,12 @@ func (c *ProxyChain) dialWithNodes(withHttp2 bool, addr string, nodes ...ProxyNo
|
||||
return c.http2Connect(addr)
|
||||
}
|
||||
}
|
||||
|
||||
if nodes[0].Transport == "quic" {
|
||||
glog.V(LINFO).Infoln("Dial with QUIC")
|
||||
return c.quicConnect(addr)
|
||||
}
|
||||
|
||||
pc, err := c.travelNodes(withHttp2, nodes...)
|
||||
if err != nil {
|
||||
return
|
||||
@ -277,7 +297,7 @@ func (c *ProxyChain) travelNodes(withHttp2 bool, nodes ...ProxyNode) (conn *Prox
|
||||
} else if node.Transport == "kcp" {
|
||||
cc, err = c.getKCPConn()
|
||||
} else if node.Transport == "pht" {
|
||||
cc, err = c.phttpClient.Dial()
|
||||
cc, err = c.phtClient.Dial()
|
||||
} else {
|
||||
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
|
||||
}
|
||||
@ -388,3 +408,63 @@ func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) {
|
||||
}
|
||||
return c.getHttp2Conn(header)
|
||||
}
|
||||
|
||||
func (c *ProxyChain) quicConnect(addr string) (net.Conn, error) {
|
||||
quicNode := c.nodes[0]
|
||||
header := make(http.Header)
|
||||
header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
|
||||
if quicNode.Users != nil {
|
||||
header.Set("Proxy-Authorization",
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(quicNode.Users[0].String())))
|
||||
}
|
||||
return c.getQuicConn(header)
|
||||
}
|
||||
|
||||
func (c *ProxyChain) getQuicConn(header http.Header) (net.Conn, error) {
|
||||
quicNode := c.nodes[0]
|
||||
pr, pw := io.Pipe()
|
||||
|
||||
if header == nil {
|
||||
header = make(http.Header)
|
||||
}
|
||||
|
||||
/*
|
||||
req := http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: &url.URL{Scheme: "https", Host: quicNode.Addr},
|
||||
Header: header,
|
||||
Proto: "HTTP/2.0",
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Body: pr,
|
||||
Host: quicNode.Addr,
|
||||
ContentLength: -1,
|
||||
}
|
||||
*/
|
||||
req, err := http.NewRequest(http.MethodPost, "https://"+quicNode.Addr, pr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.ContentLength = -1
|
||||
req.Header = header
|
||||
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpRequest(req, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
resp, err := c.quicClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if glog.V(LDEBUG) {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
glog.Infoln(string(dump))
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, errors.New(resp.Status)
|
||||
}
|
||||
conn := &http2Conn{r: resp.Body, w: pw}
|
||||
conn.remoteAddr, _ = net.ResolveUDPAddr("udp", quicNode.Addr)
|
||||
return conn, nil
|
||||
}
|
||||
|
35
cmd/gost/vendor/github.com/ginuerzh/gost/conn.go
generated
vendored
35
cmd/gost/vendor/github.com/ginuerzh/gost/conn.go
generated
vendored
@ -5,6 +5,8 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ginuerzh/gosocks4"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
@ -179,6 +181,39 @@ func (c *ProxyConn) Connect(addr string) error {
|
||||
if reply.Rep != gosocks5.Succeeded {
|
||||
return errors.New("Service unavailable")
|
||||
}
|
||||
case "socks4", "socks4a":
|
||||
atype := gosocks4.AddrDomain
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p, _ := strconv.Atoi(port)
|
||||
|
||||
if c.Node.Protocol == "socks4" {
|
||||
taddr, err := net.ResolveTCPAddr("tcp4", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
host = taddr.IP.String()
|
||||
p = taddr.Port
|
||||
atype = gosocks4.AddrIPv4
|
||||
}
|
||||
req := gosocks4.NewRequest(gosocks4.CmdConnect,
|
||||
&gosocks4.Addr{Type: atype, Host: host, Port: uint16(p)}, nil)
|
||||
if err := req.Write(c); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[%s] %s", c.Node.Protocol, req)
|
||||
|
||||
reply, err := gosocks4.ReadReply(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[%s] %s", c.Node.Protocol, reply)
|
||||
|
||||
if reply.Code != gosocks4.Granted {
|
||||
return errors.New(fmt.Sprintf("%s: code=%d", c.Node.Protocol, reply.Code))
|
||||
}
|
||||
case "http":
|
||||
fallthrough
|
||||
default:
|
||||
|
2
cmd/gost/vendor/github.com/ginuerzh/gost/node.go
generated
vendored
2
cmd/gost/vendor/github.com/ginuerzh/gost/node.go
generated
vendored
@ -84,7 +84,7 @@ func ParseProxyNode(s string) (node ProxyNode, err error) {
|
||||
}
|
||||
|
||||
switch node.Protocol {
|
||||
case "http", "http2", "socks", "socks5", "ss":
|
||||
case "http", "http2", "socks", "socks4", "socks4a", "socks5", "ss":
|
||||
default:
|
||||
node.Protocol = ""
|
||||
}
|
||||
|
3
cmd/gost/vendor/github.com/ginuerzh/gost/quic.go
generated
vendored
3
cmd/gost/vendor/github.com/ginuerzh/gost/quic.go
generated
vendored
@ -29,7 +29,8 @@ func (s *QuicServer) ListenAndServeTLS(config *tls.Config) error {
|
||||
},
|
||||
}
|
||||
if server.Handler == nil {
|
||||
server.Handler = http.HandlerFunc(s.HandleRequest)
|
||||
// server.Handler = http.HandlerFunc(s.HandleRequest)
|
||||
server.Handler = http.HandlerFunc(NewHttp2Server(s.Base).HandleRequest)
|
||||
}
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
9
cmd/gost/vendor/github.com/ginuerzh/gost/server.go
generated
vendored
9
cmd/gost/vendor/github.com/ginuerzh/gost/server.go
generated
vendored
@ -3,6 +3,7 @@ package gost
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"github.com/ginuerzh/gosocks4"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
@ -173,6 +174,14 @@ func (s *ProxyServer) handleConn(conn net.Conn) {
|
||||
}
|
||||
NewSocks5Server(conn, s).HandleRequest(req)
|
||||
return
|
||||
case "socks4", "socks4a":
|
||||
req, err := gosocks4.ReadRequest(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks4]", err)
|
||||
return
|
||||
}
|
||||
NewSocks4Server(conn, s).HandleRequest(req)
|
||||
return
|
||||
}
|
||||
|
||||
// http or socks5
|
||||
|
80
cmd/gost/vendor/github.com/ginuerzh/gost/socks.go
generated
vendored
80
cmd/gost/vendor/github.com/ginuerzh/gost/socks.go
generated
vendored
@ -3,12 +3,9 @@ package gost
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
//"errors"
|
||||
"github.com/ginuerzh/gosocks4"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
//"os/exec"
|
||||
//"io"
|
||||
//"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@ -672,3 +669,78 @@ func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
|
||||
Port: uint16(port),
|
||||
}
|
||||
}
|
||||
|
||||
type Socks4Server struct {
|
||||
conn net.Conn
|
||||
Base *ProxyServer
|
||||
}
|
||||
|
||||
func NewSocks4Server(conn net.Conn, base *ProxyServer) *Socks4Server {
|
||||
return &Socks4Server{conn: conn, Base: base}
|
||||
}
|
||||
|
||||
func (s *Socks4Server) HandleRequest(req *gosocks4.Request) {
|
||||
glog.V(LDEBUG).Infof("[socks4] %s -> %s\n%s", s.conn.RemoteAddr(), req.Addr, req)
|
||||
|
||||
switch req.Cmd {
|
||||
case gosocks4.CmdConnect:
|
||||
glog.V(LINFO).Infof("[socks4-connect] %s -> %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.handleConnect(req)
|
||||
|
||||
case gosocks4.CmdBind:
|
||||
glog.V(LINFO).Infof("[socks4-bind] %s - %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.handleBind(req)
|
||||
|
||||
default:
|
||||
glog.V(LWARNING).Infoln("[socks4] Unrecognized request:", req.Cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Socks4Server) handleConnect(req *gosocks4.Request) {
|
||||
cc, err := s.Base.Chain.Dial(req.Addr.String())
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[socks4-connect] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
rep := gosocks4.NewReply(gosocks4.Failed, nil)
|
||||
rep.Write(s.conn)
|
||||
glog.V(LDEBUG).Infof("[socks4-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
rep := gosocks4.NewReply(gosocks4.Granted, nil)
|
||||
if err := rep.Write(s.conn); err != nil {
|
||||
glog.V(LWARNING).Infof("[socks4-connect] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks4-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
|
||||
|
||||
glog.V(LINFO).Infof("[socks4-connect] %s <-> %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.Base.transport(s.conn, cc)
|
||||
glog.V(LINFO).Infof("[socks4-connect] %s >-< %s", s.conn.RemoteAddr(), req.Addr)
|
||||
}
|
||||
|
||||
func (s *Socks4Server) handleBind(req *gosocks4.Request) {
|
||||
cc, err := s.Base.Chain.GetConn()
|
||||
|
||||
// connection error
|
||||
if err != nil && err != ErrEmptyChain {
|
||||
glog.V(LWARNING).Infof("[socks4-bind] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
reply := gosocks4.NewReply(gosocks4.Failed, nil)
|
||||
reply.Write(s.conn)
|
||||
glog.V(LDEBUG).Infof("[socks4-bind] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
|
||||
return
|
||||
}
|
||||
// TODO: serve socks4 bind
|
||||
if err == ErrEmptyChain {
|
||||
//s.bindOn(req.Addr.String())
|
||||
return
|
||||
}
|
||||
|
||||
defer cc.Close()
|
||||
// forward request
|
||||
req.Write(cc)
|
||||
|
||||
glog.V(LINFO).Infof("[socks4-bind] %s <-> %s", s.conn.RemoteAddr(), cc.RemoteAddr())
|
||||
s.Base.transport(s.conn, cc)
|
||||
glog.V(LINFO).Infof("[socks4-bind] %s >-< %s", s.conn.RemoteAddr(), cc.RemoteAddr())
|
||||
}
|
||||
|
35
conn.go
35
conn.go
@ -5,6 +5,8 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ginuerzh/gosocks4"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
@ -179,6 +181,39 @@ func (c *ProxyConn) Connect(addr string) error {
|
||||
if reply.Rep != gosocks5.Succeeded {
|
||||
return errors.New("Service unavailable")
|
||||
}
|
||||
case "socks4", "socks4a":
|
||||
atype := gosocks4.AddrDomain
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p, _ := strconv.Atoi(port)
|
||||
|
||||
if c.Node.Protocol == "socks4" {
|
||||
taddr, err := net.ResolveTCPAddr("tcp4", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
host = taddr.IP.String()
|
||||
p = taddr.Port
|
||||
atype = gosocks4.AddrIPv4
|
||||
}
|
||||
req := gosocks4.NewRequest(gosocks4.CmdConnect,
|
||||
&gosocks4.Addr{Type: atype, Host: host, Port: uint16(p)}, nil)
|
||||
if err := req.Write(c); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[%s] %s", c.Node.Protocol, req)
|
||||
|
||||
reply, err := gosocks4.ReadReply(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[%s] %s", c.Node.Protocol, reply)
|
||||
|
||||
if reply.Code != gosocks4.Granted {
|
||||
return errors.New(fmt.Sprintf("%s: code=%d", c.Node.Protocol, reply.Code))
|
||||
}
|
||||
case "http":
|
||||
fallthrough
|
||||
default:
|
||||
|
2
node.go
2
node.go
@ -84,7 +84,7 @@ func ParseProxyNode(s string) (node ProxyNode, err error) {
|
||||
}
|
||||
|
||||
switch node.Protocol {
|
||||
case "http", "http2", "socks", "socks5", "ss":
|
||||
case "http", "http2", "socks", "socks4", "socks4a", "socks5", "ss":
|
||||
default:
|
||||
node.Protocol = ""
|
||||
}
|
||||
|
3
quic.go
3
quic.go
@ -29,7 +29,8 @@ func (s *QuicServer) ListenAndServeTLS(config *tls.Config) error {
|
||||
},
|
||||
}
|
||||
if server.Handler == nil {
|
||||
server.Handler = http.HandlerFunc(s.HandleRequest)
|
||||
// server.Handler = http.HandlerFunc(s.HandleRequest)
|
||||
server.Handler = http.HandlerFunc(NewHttp2Server(s.Base).HandleRequest)
|
||||
}
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package gost
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"github.com/ginuerzh/gosocks4"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
@ -173,6 +174,14 @@ func (s *ProxyServer) handleConn(conn net.Conn) {
|
||||
}
|
||||
NewSocks5Server(conn, s).HandleRequest(req)
|
||||
return
|
||||
case "socks4", "socks4a":
|
||||
req, err := gosocks4.ReadRequest(conn)
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infoln("[socks4]", err)
|
||||
return
|
||||
}
|
||||
NewSocks4Server(conn, s).HandleRequest(req)
|
||||
return
|
||||
}
|
||||
|
||||
// http or socks5
|
||||
|
80
socks.go
80
socks.go
@ -3,12 +3,9 @@ package gost
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
//"errors"
|
||||
"github.com/ginuerzh/gosocks4"
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"github.com/golang/glog"
|
||||
//"os/exec"
|
||||
//"io"
|
||||
//"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@ -672,3 +669,78 @@ func ToSocksAddr(addr net.Addr) *gosocks5.Addr {
|
||||
Port: uint16(port),
|
||||
}
|
||||
}
|
||||
|
||||
type Socks4Server struct {
|
||||
conn net.Conn
|
||||
Base *ProxyServer
|
||||
}
|
||||
|
||||
func NewSocks4Server(conn net.Conn, base *ProxyServer) *Socks4Server {
|
||||
return &Socks4Server{conn: conn, Base: base}
|
||||
}
|
||||
|
||||
func (s *Socks4Server) HandleRequest(req *gosocks4.Request) {
|
||||
glog.V(LDEBUG).Infof("[socks4] %s -> %s\n%s", s.conn.RemoteAddr(), req.Addr, req)
|
||||
|
||||
switch req.Cmd {
|
||||
case gosocks4.CmdConnect:
|
||||
glog.V(LINFO).Infof("[socks4-connect] %s -> %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.handleConnect(req)
|
||||
|
||||
case gosocks4.CmdBind:
|
||||
glog.V(LINFO).Infof("[socks4-bind] %s - %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.handleBind(req)
|
||||
|
||||
default:
|
||||
glog.V(LWARNING).Infoln("[socks4] Unrecognized request:", req.Cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Socks4Server) handleConnect(req *gosocks4.Request) {
|
||||
cc, err := s.Base.Chain.Dial(req.Addr.String())
|
||||
if err != nil {
|
||||
glog.V(LWARNING).Infof("[socks4-connect] %s -> %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
rep := gosocks4.NewReply(gosocks4.Failed, nil)
|
||||
rep.Write(s.conn)
|
||||
glog.V(LDEBUG).Infof("[socks4-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
rep := gosocks4.NewReply(gosocks4.Granted, nil)
|
||||
if err := rep.Write(s.conn); err != nil {
|
||||
glog.V(LWARNING).Infof("[socks4-connect] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
return
|
||||
}
|
||||
glog.V(LDEBUG).Infof("[socks4-connect] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, rep)
|
||||
|
||||
glog.V(LINFO).Infof("[socks4-connect] %s <-> %s", s.conn.RemoteAddr(), req.Addr)
|
||||
s.Base.transport(s.conn, cc)
|
||||
glog.V(LINFO).Infof("[socks4-connect] %s >-< %s", s.conn.RemoteAddr(), req.Addr)
|
||||
}
|
||||
|
||||
func (s *Socks4Server) handleBind(req *gosocks4.Request) {
|
||||
cc, err := s.Base.Chain.GetConn()
|
||||
|
||||
// connection error
|
||||
if err != nil && err != ErrEmptyChain {
|
||||
glog.V(LWARNING).Infof("[socks4-bind] %s <- %s : %s", s.conn.RemoteAddr(), req.Addr, err)
|
||||
reply := gosocks4.NewReply(gosocks4.Failed, nil)
|
||||
reply.Write(s.conn)
|
||||
glog.V(LDEBUG).Infof("[socks4-bind] %s <- %s\n%s", s.conn.RemoteAddr(), req.Addr, reply)
|
||||
return
|
||||
}
|
||||
// TODO: serve socks4 bind
|
||||
if err == ErrEmptyChain {
|
||||
//s.bindOn(req.Addr.String())
|
||||
return
|
||||
}
|
||||
|
||||
defer cc.Close()
|
||||
// forward request
|
||||
req.Write(cc)
|
||||
|
||||
glog.V(LINFO).Infof("[socks4-bind] %s <-> %s", s.conn.RemoteAddr(), cc.RemoteAddr())
|
||||
s.Base.transport(s.conn, cc)
|
||||
glog.V(LINFO).Infof("[socks4-bind] %s >-< %s", s.conn.RemoteAddr(), cc.RemoteAddr())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user