299 lines
6.9 KiB
Go
299 lines
6.9 KiB
Go
package socks5
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
)
|
|
|
|
var (
|
|
// ErrVersion is version error
|
|
ErrVersion = errors.New("Invalid Version")
|
|
// ErrUserPassVersion is username/password auth version error
|
|
ErrUserPassVersion = errors.New("Invalid Version of Username Password Auth")
|
|
// ErrBadRequest is bad request error
|
|
ErrBadRequest = errors.New("Bad Request")
|
|
)
|
|
|
|
// NewNegotiationRequestFrom read negotiation requst packet from client
|
|
func NewNegotiationRequestFrom(r io.Reader) (*NegotiationRequest, error) {
|
|
// memory strict
|
|
bb := make([]byte, 2)
|
|
if _, err := io.ReadFull(r, bb); err != nil {
|
|
return nil, err
|
|
}
|
|
if bb[0] != Ver {
|
|
return nil, ErrVersion
|
|
}
|
|
if bb[1] == 0 {
|
|
return nil, ErrBadRequest
|
|
}
|
|
ms := make([]byte, int(bb[1]))
|
|
if _, err := io.ReadFull(r, ms); err != nil {
|
|
return nil, err
|
|
}
|
|
if Debug {
|
|
log.Printf("Got NegotiationRequest: %#v %#v %#v\n", bb[0], bb[1], ms)
|
|
}
|
|
return &NegotiationRequest{
|
|
Ver: bb[0],
|
|
NMethods: bb[1],
|
|
Methods: ms,
|
|
}, nil
|
|
}
|
|
|
|
// NewNegotiationReply return negotiation reply packet can be writed into client
|
|
func NewNegotiationReply(method byte) *NegotiationReply {
|
|
return &NegotiationReply{
|
|
Ver: Ver,
|
|
Method: method,
|
|
}
|
|
}
|
|
|
|
// WriteTo write negotiation reply packet into client
|
|
func (r *NegotiationReply) WriteTo(w io.Writer) (int64, error) {
|
|
var n int
|
|
i, err := w.Write([]byte{r.Ver, r.Method})
|
|
n = n + i
|
|
if err != nil {
|
|
return int64(n), err
|
|
}
|
|
if Debug {
|
|
log.Printf("Sent NegotiationReply: %#v %#v\n", r.Ver, r.Method)
|
|
}
|
|
return int64(n), nil
|
|
}
|
|
|
|
// NewUserPassNegotiationRequestFrom read user password negotiation request packet from client
|
|
func NewUserPassNegotiationRequestFrom(r io.Reader) (*UserPassNegotiationRequest, error) {
|
|
bb := make([]byte, 2)
|
|
if _, err := io.ReadFull(r, bb); err != nil {
|
|
return nil, err
|
|
}
|
|
if bb[0] != UserPassVer {
|
|
return nil, ErrUserPassVersion
|
|
}
|
|
if bb[1] == 0 {
|
|
return nil, ErrBadRequest
|
|
}
|
|
ub := make([]byte, int(bb[1])+1)
|
|
if _, err := io.ReadFull(r, ub); err != nil {
|
|
return nil, err
|
|
}
|
|
if ub[int(bb[1])] == 0 {
|
|
return nil, ErrBadRequest
|
|
}
|
|
p := make([]byte, int(ub[int(bb[1])]))
|
|
if _, err := io.ReadFull(r, p); err != nil {
|
|
return nil, err
|
|
}
|
|
if Debug {
|
|
log.Printf("Got UserPassNegotiationRequest: %#v %#v %#v %#v %#v\n", bb[0], bb[1], ub[:int(bb[1])], ub[int(bb[1])], p)
|
|
}
|
|
return &UserPassNegotiationRequest{
|
|
Ver: bb[0],
|
|
Ulen: bb[1],
|
|
Uname: ub[:int(bb[1])],
|
|
Plen: ub[int(bb[1])],
|
|
Passwd: p,
|
|
}, nil
|
|
}
|
|
|
|
// NewUserPassNegotiationReply return negotiation username password reply packet can be writed into client
|
|
func NewUserPassNegotiationReply(status byte) *UserPassNegotiationReply {
|
|
return &UserPassNegotiationReply{
|
|
Ver: UserPassVer,
|
|
Status: status,
|
|
}
|
|
}
|
|
|
|
// WriteTo write negotiation username password reply packet into client
|
|
func (r *UserPassNegotiationReply) WriteTo(w io.Writer) (int64, error) {
|
|
var n int
|
|
i, err := w.Write([]byte{r.Ver, r.Status})
|
|
n = n + i
|
|
if err != nil {
|
|
return int64(n), err
|
|
}
|
|
if Debug {
|
|
log.Printf("Sent UserPassNegotiationReply: %#v %#v \n", r.Ver, r.Status)
|
|
}
|
|
return int64(n), nil
|
|
}
|
|
|
|
// NewRequestFrom read requst packet from client
|
|
func NewRequestFrom(r io.Reader) (*Request, error) {
|
|
bb := make([]byte, 4)
|
|
if _, err := io.ReadFull(r, bb); err != nil {
|
|
return nil, err
|
|
}
|
|
if bb[0] != Ver {
|
|
return nil, ErrVersion
|
|
}
|
|
var addr []byte
|
|
if bb[3] == ATYPIPv4 {
|
|
addr = make([]byte, 4)
|
|
if _, err := io.ReadFull(r, addr); err != nil {
|
|
return nil, err
|
|
}
|
|
} else if bb[3] == ATYPIPv6 {
|
|
addr = make([]byte, 16)
|
|
if _, err := io.ReadFull(r, addr); err != nil {
|
|
return nil, err
|
|
}
|
|
} else if bb[3] == ATYPDomain {
|
|
dal := make([]byte, 1)
|
|
if _, err := io.ReadFull(r, dal); err != nil {
|
|
return nil, err
|
|
}
|
|
if dal[0] == 0 {
|
|
return nil, ErrBadRequest
|
|
}
|
|
addr = make([]byte, int(dal[0]))
|
|
if _, err := io.ReadFull(r, addr); err != nil {
|
|
return nil, err
|
|
}
|
|
addr = append(dal, addr...)
|
|
} else {
|
|
return nil, ErrBadRequest
|
|
}
|
|
port := make([]byte, 2)
|
|
if _, err := io.ReadFull(r, port); err != nil {
|
|
return nil, err
|
|
}
|
|
if Debug {
|
|
log.Printf("Got Request: %#v %#v %#v %#v %#v %#v\n", bb[0], bb[1], bb[2], bb[3], addr, port)
|
|
}
|
|
return &Request{
|
|
Ver: bb[0],
|
|
Cmd: bb[1],
|
|
Rsv: bb[2],
|
|
Atyp: bb[3],
|
|
DstAddr: addr,
|
|
DstPort: port,
|
|
}, nil
|
|
}
|
|
|
|
// NewReply return reply packet can be writed into client, bndaddr should not have domain length
|
|
func NewReply(rep byte, atyp byte, bndaddr []byte, bndport []byte) *Reply {
|
|
if atyp == ATYPDomain {
|
|
bndaddr = append([]byte{byte(len(bndaddr))}, bndaddr...)
|
|
}
|
|
return &Reply{
|
|
Ver: Ver,
|
|
Rep: rep,
|
|
Rsv: 0x00,
|
|
Atyp: atyp,
|
|
BndAddr: bndaddr,
|
|
BndPort: bndport,
|
|
}
|
|
}
|
|
|
|
// WriteTo write reply packet into client
|
|
func (r *Reply) WriteTo(w io.Writer) (int64, error) {
|
|
var n int
|
|
i, err := w.Write([]byte{r.Ver, r.Rep, r.Rsv, r.Atyp})
|
|
n = n + i
|
|
if err != nil {
|
|
return int64(n), err
|
|
}
|
|
i, err = w.Write(r.BndAddr)
|
|
n = n + i
|
|
if err != nil {
|
|
return int64(n), err
|
|
}
|
|
i, err = w.Write(r.BndPort)
|
|
n = n + i
|
|
if err != nil {
|
|
return int64(n), err
|
|
}
|
|
if Debug {
|
|
log.Printf("Sent Reply: %#v %#v %#v %#v %#v %#v\n", r.Ver, r.Rep, r.Rsv, r.Atyp, r.BndAddr, r.BndPort)
|
|
}
|
|
return int64(n), nil
|
|
}
|
|
|
|
func NewDatagramFromBytes(bb []byte) (*Datagram, error) {
|
|
n := len(bb)
|
|
minl := 4
|
|
if n < minl {
|
|
return nil, ErrBadRequest
|
|
}
|
|
var addr []byte
|
|
if bb[3] == ATYPIPv4 {
|
|
minl += 4
|
|
if n < minl {
|
|
return nil, ErrBadRequest
|
|
}
|
|
addr = bb[minl-4 : minl]
|
|
} else if bb[3] == ATYPIPv6 {
|
|
minl += 16
|
|
if n < minl {
|
|
return nil, ErrBadRequest
|
|
}
|
|
addr = bb[minl-16 : minl]
|
|
} else if bb[3] == ATYPDomain {
|
|
minl += 1
|
|
if n < minl {
|
|
return nil, ErrBadRequest
|
|
}
|
|
l := bb[4]
|
|
if l == 0 {
|
|
return nil, ErrBadRequest
|
|
}
|
|
minl += int(l)
|
|
if n < minl {
|
|
return nil, ErrBadRequest
|
|
}
|
|
addr = bb[minl-int(l) : minl]
|
|
addr = append([]byte{l}, addr...)
|
|
} else {
|
|
return nil, ErrBadRequest
|
|
}
|
|
minl += 2
|
|
if n <= minl {
|
|
return nil, ErrBadRequest
|
|
}
|
|
port := bb[minl-2 : minl]
|
|
data := bb[minl:]
|
|
d := &Datagram{
|
|
Rsv: bb[0:2],
|
|
Frag: bb[2],
|
|
Atyp: bb[3],
|
|
DstAddr: addr,
|
|
DstPort: port,
|
|
Data: data,
|
|
}
|
|
if Debug {
|
|
log.Printf("Got Datagram. data: %#v %#v %#v %#v %#v %#v datagram address: %#v\n", d.Rsv, d.Frag, d.Atyp, d.DstAddr, d.DstPort, d.Data, d.Address())
|
|
}
|
|
return d, nil
|
|
}
|
|
|
|
// NewDatagram return datagram packet can be writed into client, dstaddr should not have domain length
|
|
func NewDatagram(atyp byte, dstaddr []byte, dstport []byte, data []byte) *Datagram {
|
|
if atyp == ATYPDomain {
|
|
dstaddr = append([]byte{byte(len(dstaddr))}, dstaddr...)
|
|
}
|
|
return &Datagram{
|
|
Rsv: []byte{0x00, 0x00},
|
|
Frag: 0x00,
|
|
Atyp: atyp,
|
|
DstAddr: dstaddr,
|
|
DstPort: dstport,
|
|
Data: data,
|
|
}
|
|
}
|
|
|
|
// Bytes return []byte
|
|
func (d *Datagram) Bytes() []byte {
|
|
b := make([]byte, 0)
|
|
b = append(b, d.Rsv...)
|
|
b = append(b, d.Frag)
|
|
b = append(b, d.Atyp)
|
|
b = append(b, d.DstAddr...)
|
|
b = append(b, d.DstPort...)
|
|
b = append(b, d.Data...)
|
|
return b
|
|
}
|