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