mirror of https://github.com/ginuerzh/gost
21 changed files with 912 additions and 36 deletions
@ -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. |
||||
@ -0,0 +1,2 @@ |
|||||
|
# gosocks4 |
||||
|
golang and SOCKS4(a) |
||||
@ -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()) |
||||
|
} |
||||
@ -0,0 +1,152 @@ |
|||||
|
SOCKS: A protocol for TCP proxy across firewalls |
||||
|
|
||||
|
Ying-Da Lee |
||||
|
Principal Member Technical Staff |
||||
|
NEC Systems Laboratory, CSTC |
||||
|
[email protected] |
||||
|
|
||||
|
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. |
||||
@ -0,0 +1,39 @@ |
|||||
|
SOCKS 4A: A Simple Extension to SOCKS 4 Protocol |
||||
|
|
||||
|
Ying-Da Lee |
||||
|
[email protected] or [email protected] |
||||
|
|
||||
|
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. |
||||
Loading…
Reference in new issue