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 }