mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-03 20:07:38 +03:00
281 lines
6.4 KiB
Go
281 lines
6.4 KiB
Go
package socks5
|
|
|
|
import (
|
|
"io"
|
|
"net/netip"
|
|
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
M "github.com/sagernet/sing/common/metadata"
|
|
"github.com/sagernet/sing/common/rw"
|
|
)
|
|
|
|
const (
|
|
Version byte = 5
|
|
|
|
AuthTypeNotRequired byte = 0x00
|
|
AuthTypeGSSAPI byte = 0x01
|
|
AuthTypeUsernamePassword byte = 0x02
|
|
AuthTypeNoAcceptedMethods byte = 0xFF
|
|
|
|
UsernamePasswordStatusSuccess byte = 0x00
|
|
UsernamePasswordStatusFailure byte = 0x01
|
|
|
|
CommandConnect byte = 0x01
|
|
CommandBind byte = 0x02
|
|
CommandUDPAssociate byte = 0x03
|
|
|
|
ReplyCodeSuccess byte = 0
|
|
ReplyCodeFailure byte = 1
|
|
ReplyCodeNotAllowed byte = 2
|
|
ReplyCodeNetworkUnreachable byte = 3
|
|
ReplyCodeHostUnreachable byte = 4
|
|
ReplyCodeConnectionRefused byte = 5
|
|
ReplyCodeTTLExpired byte = 6
|
|
ReplyCodeUnsupported byte = 7
|
|
ReplyCodeAddressTypeUnsupported byte = 8
|
|
)
|
|
|
|
// +----+----------+----------+
|
|
// |VER | NMETHODS | METHODS |
|
|
// +----+----------+----------+
|
|
// | 1 | 1 | 1 to 255 |
|
|
// +----+----------+----------+
|
|
|
|
type AuthRequest struct {
|
|
Methods []byte
|
|
}
|
|
|
|
func WriteAuthRequest(writer io.Writer, request AuthRequest) error {
|
|
err := rw.WriteByte(writer, Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = rw.WriteByte(writer, byte(len(request.Methods)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rw.WriteBytes(writer, request.Methods)
|
|
}
|
|
|
|
func ReadAuthRequest(reader io.Reader) (request AuthRequest, err error) {
|
|
version, err := rw.ReadByte(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if version != Version {
|
|
err = E.New("expected socks version 5, got ", version)
|
|
return
|
|
}
|
|
return ReadAuthRequest0(reader)
|
|
}
|
|
|
|
func ReadAuthRequest0(reader io.Reader) (request AuthRequest, err error) {
|
|
methodLen, err := rw.ReadByte(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
request.Methods, err = rw.ReadBytes(reader, int(methodLen))
|
|
return
|
|
}
|
|
|
|
// +----+--------+
|
|
// |VER | METHOD |
|
|
// +----+--------+
|
|
// | 1 | 1 |
|
|
// +----+--------+
|
|
|
|
type AuthResponse struct {
|
|
Method byte
|
|
}
|
|
|
|
func WriteAuthResponse(writer io.Writer, response AuthResponse) error {
|
|
return rw.WriteBytes(writer, []byte{Version, response.Method})
|
|
}
|
|
|
|
func ReadAuthResponse(reader io.Reader) (response AuthResponse, err error) {
|
|
version, err := rw.ReadByte(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if version != Version {
|
|
err = E.New("expected socks version 5, got ", version)
|
|
return
|
|
}
|
|
response.Method, err = rw.ReadByte(reader)
|
|
return
|
|
}
|
|
|
|
// +----+------+----------+------+----------+
|
|
// |VER | ULEN | UNAME | PLEN | PASSWD |
|
|
// +----+------+----------+------+----------+
|
|
// | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
|
|
// +----+------+----------+------+----------+
|
|
|
|
type UsernamePasswordAuthRequest struct {
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func WriteUsernamePasswordAuthRequest(writer io.Writer, request UsernamePasswordAuthRequest) error {
|
|
err := rw.WriteByte(writer, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = M.WriteSocksString(writer, request.Username)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return M.WriteSocksString(writer, request.Password)
|
|
}
|
|
|
|
func ReadUsernamePasswordAuthRequest(reader io.Reader) (request UsernamePasswordAuthRequest, err error) {
|
|
version, err := rw.ReadByte(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if version != 1 {
|
|
err = E.New("excepted password request version 1, got ", version)
|
|
return
|
|
}
|
|
request.Username, err = M.ReadSockString(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
request.Password, err = M.ReadSockString(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// +----+--------+
|
|
// |VER | STATUS |
|
|
// +----+--------+
|
|
// | 1 | 1 |
|
|
// +----+--------+
|
|
|
|
type UsernamePasswordAuthResponse struct {
|
|
Status byte
|
|
}
|
|
|
|
func WriteUsernamePasswordAuthResponse(writer io.Writer, response UsernamePasswordAuthResponse) error {
|
|
err := rw.WriteByte(writer, 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return rw.WriteByte(writer, response.Status)
|
|
}
|
|
|
|
func ReadUsernamePasswordAuthResponse(reader io.Reader) (response UsernamePasswordAuthResponse, err error) {
|
|
version, err := rw.ReadByte(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if version != 1 {
|
|
err = E.New("excepted password request version 1, got ", version)
|
|
return
|
|
}
|
|
response.Status, err = rw.ReadByte(reader)
|
|
return
|
|
}
|
|
|
|
// +----+-----+-------+------+----------+----------+
|
|
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
|
|
// +----+-----+-------+------+----------+----------+
|
|
// | 1 | 1 | X'00' | 1 | Variable | 2 |
|
|
// +----+-----+-------+------+----------+----------+
|
|
|
|
type Request struct {
|
|
Command byte
|
|
Destination M.Socksaddr
|
|
}
|
|
|
|
func WriteRequest(writer io.Writer, request Request) error {
|
|
err := rw.WriteByte(writer, Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = rw.WriteByte(writer, request.Command)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = rw.WriteZero(writer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return M.SocksaddrSerializer.WriteAddrPort(writer, request.Destination)
|
|
}
|
|
|
|
func ReadRequest(reader io.Reader) (request Request, err error) {
|
|
version, err := rw.ReadByte(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if version != Version {
|
|
err = E.New("expected socks version 5, got ", version)
|
|
return
|
|
}
|
|
request.Command, err = rw.ReadByte(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = rw.Skip(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
request.Destination, err = M.SocksaddrSerializer.ReadAddrPort(reader)
|
|
return
|
|
}
|
|
|
|
// +----+-----+-------+------+----------+----------+
|
|
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
|
|
// +----+-----+-------+------+----------+----------+
|
|
// | 1 | 1 | X'00' | 1 | Variable | 2 |
|
|
// +----+-----+-------+------+----------+----------+
|
|
|
|
type Response struct {
|
|
ReplyCode byte
|
|
Bind M.Socksaddr
|
|
}
|
|
|
|
func WriteResponse(writer io.Writer, response Response) error {
|
|
err := rw.WriteByte(writer, Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = rw.WriteByte(writer, response.ReplyCode)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = rw.WriteZero(writer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !response.Bind.IsValid() {
|
|
return M.SocksaddrSerializer.WriteAddrPort(writer, M.Socksaddr{
|
|
Addr: netip.IPv4Unspecified(),
|
|
})
|
|
}
|
|
return M.SocksaddrSerializer.WriteAddrPort(writer, response.Bind)
|
|
}
|
|
|
|
func ReadResponse(reader io.Reader) (response Response, err error) {
|
|
version, err := rw.ReadByte(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if version != Version {
|
|
err = E.New("expected socks version 5, got ", version)
|
|
return
|
|
}
|
|
response.ReplyCode, err = rw.ReadByte(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = rw.Skip(reader)
|
|
if err != nil {
|
|
return
|
|
}
|
|
response.Bind, err = M.SocksaddrSerializer.ReadAddrPort(reader)
|
|
return
|
|
}
|