|
|
@@ -10,23 +10,17 @@ import (
|
|
|
"v2ray.com/core/common"
|
|
|
"v2ray.com/core/common/buf"
|
|
|
"v2ray.com/core/common/bufio"
|
|
|
- "v2ray.com/core/common/crypto"
|
|
|
"v2ray.com/core/common/errors"
|
|
|
"v2ray.com/core/common/log"
|
|
|
v2net "v2ray.com/core/common/net"
|
|
|
+ proto "v2ray.com/core/common/protocol"
|
|
|
"v2ray.com/core/common/serial"
|
|
|
"v2ray.com/core/common/signal"
|
|
|
"v2ray.com/core/proxy"
|
|
|
- "v2ray.com/core/proxy/socks/protocol"
|
|
|
"v2ray.com/core/transport/internet"
|
|
|
"v2ray.com/core/transport/internet/udp"
|
|
|
)
|
|
|
|
|
|
-var (
|
|
|
- ErrUnsupportedSocksCommand = errors.New("Unsupported socks command.")
|
|
|
- ErrUnsupportedAuthMethod = errors.New("Unsupported auth method.")
|
|
|
-)
|
|
|
-
|
|
|
// Server is a SOCKS 5 proxy server
|
|
|
type Server struct {
|
|
|
tcpMutex sync.RWMutex
|
|
|
@@ -79,7 +73,7 @@ func (v *Server) Close() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// Listen implements InboundHandler.Listen().
|
|
|
+// Start implements InboundHandler.Start().
|
|
|
func (v *Server) Start() error {
|
|
|
if v.accepting {
|
|
|
return nil
|
|
|
@@ -111,153 +105,41 @@ func (v *Server) handleConnection(connection internet.Connection) {
|
|
|
reader := bufio.NewReader(timedReader)
|
|
|
defer reader.Release()
|
|
|
|
|
|
- writer := bufio.NewWriter(connection)
|
|
|
- defer writer.Release()
|
|
|
-
|
|
|
- auth, auth4, err := protocol.ReadAuthentication(reader)
|
|
|
- if err != nil && errors.Cause(err) != protocol.Socks4Downgrade {
|
|
|
- if errors.Cause(err) != io.EOF {
|
|
|
- log.Warning("Socks: failed to read authentication: ", err)
|
|
|
- }
|
|
|
- return
|
|
|
+ session := &ServerSession{
|
|
|
+ config: v.config,
|
|
|
+ meta: v.meta,
|
|
|
}
|
|
|
|
|
|
clientAddr := v2net.DestinationFromAddr(connection.RemoteAddr())
|
|
|
- if err != nil && err == protocol.Socks4Downgrade {
|
|
|
- v.handleSocks4(clientAddr, reader, writer, auth4)
|
|
|
- } else {
|
|
|
- v.handleSocks5(clientAddr, reader, writer, auth)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-func (v *Server) handleSocks5(clientAddr v2net.Destination, reader *bufio.BufferedReader, writer *bufio.BufferedWriter, auth protocol.Socks5AuthenticationRequest) error {
|
|
|
- expectedAuthMethod := protocol.AuthNotRequired
|
|
|
- if v.config.AuthType == AuthType_PASSWORD {
|
|
|
- expectedAuthMethod = protocol.AuthUserPass
|
|
|
- }
|
|
|
-
|
|
|
- if !auth.HasAuthMethod(expectedAuthMethod) {
|
|
|
- authResponse := protocol.NewAuthenticationResponse(protocol.AuthNoMatchingMethod)
|
|
|
- err := protocol.WriteAuthentication(writer, authResponse)
|
|
|
- writer.Flush()
|
|
|
- if err != nil {
|
|
|
- log.Warning("Socks: failed to write authentication: ", err)
|
|
|
- return err
|
|
|
- }
|
|
|
- log.Warning("Socks: client doesn't support any allowed auth methods.")
|
|
|
- return ErrUnsupportedAuthMethod
|
|
|
- }
|
|
|
-
|
|
|
- authResponse := protocol.NewAuthenticationResponse(expectedAuthMethod)
|
|
|
- protocol.WriteAuthentication(writer, authResponse)
|
|
|
- err := writer.Flush()
|
|
|
- if err != nil {
|
|
|
- log.Error("Socks: failed to write authentication: ", err)
|
|
|
- return err
|
|
|
- }
|
|
|
- if v.config.AuthType == AuthType_PASSWORD {
|
|
|
- upRequest, err := protocol.ReadUserPassRequest(reader)
|
|
|
- if err != nil {
|
|
|
- log.Warning("Socks: failed to read username and password: ", err)
|
|
|
- return err
|
|
|
- }
|
|
|
- status := byte(0)
|
|
|
- if !v.config.HasAccount(upRequest.Username(), upRequest.Password()) {
|
|
|
- status = byte(0xFF)
|
|
|
- }
|
|
|
- upResponse := protocol.NewSocks5UserPassResponse(status)
|
|
|
- err = protocol.WriteUserPassResponse(writer, upResponse)
|
|
|
- writer.Flush()
|
|
|
- if err != nil {
|
|
|
- log.Error("Socks: failed to write user pass response: ", err)
|
|
|
- return err
|
|
|
- }
|
|
|
- if status != byte(0) {
|
|
|
- log.Warning("Socks: Invalid user account: ", upRequest.AuthDetail())
|
|
|
- log.Access(clientAddr, "", log.AccessRejected, crypto.ErrAuthenticationFailed)
|
|
|
- return crypto.ErrAuthenticationFailed
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- request, err := protocol.ReadRequest(reader)
|
|
|
+ request, err := session.Handshake(reader, connection)
|
|
|
if err != nil {
|
|
|
- log.Warning("Socks: failed to read request: ", err)
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- if request.Command == protocol.CmdUdpAssociate && v.config.UdpEnabled {
|
|
|
- return v.handleUDP(reader, writer)
|
|
|
+ log.Access(clientAddr, "", log.AccessRejected, err)
|
|
|
+ log.Info("Socks|Server: Failed to read request: ", err)
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
- if request.Command == protocol.CmdBind || request.Command == protocol.CmdUdpAssociate {
|
|
|
- response := protocol.NewSocks5Response()
|
|
|
- response.Error = protocol.ErrorCommandNotSupported
|
|
|
- response.Port = v2net.Port(0)
|
|
|
- response.SetIPv4([]byte{0, 0, 0, 0})
|
|
|
-
|
|
|
- response.Write(writer)
|
|
|
- writer.Flush()
|
|
|
- if err != nil {
|
|
|
- log.Error("Socks: failed to write response: ", err)
|
|
|
- return err
|
|
|
+ if request.Command == proto.RequestCommandTCP {
|
|
|
+ dest := request.Destination()
|
|
|
+ session := &proxy.SessionInfo{
|
|
|
+ Source: clientAddr,
|
|
|
+ Destination: dest,
|
|
|
+ Inbound: v.meta,
|
|
|
}
|
|
|
- log.Warning("Socks: Unsupported socks command ", request.Command)
|
|
|
- return ErrUnsupportedSocksCommand
|
|
|
- }
|
|
|
-
|
|
|
- response := protocol.NewSocks5Response()
|
|
|
- response.Error = protocol.ErrorSuccess
|
|
|
+ log.Info("Socks|Server: TCP Connect request to ", dest)
|
|
|
+ log.Access(clientAddr, dest, log.AccessAccepted, "")
|
|
|
|
|
|
- // Some SOCKS software requires a value other than dest. Let's fake one:
|
|
|
- response.Port = v2net.Port(1717)
|
|
|
- response.SetIPv4([]byte{0, 0, 0, 0})
|
|
|
-
|
|
|
- response.Write(writer)
|
|
|
- if err != nil {
|
|
|
- log.Error("Socks: failed to write response: ", err)
|
|
|
- return err
|
|
|
+ v.transport(reader, connection, session)
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
- reader.SetBuffered(false)
|
|
|
- writer.SetBuffered(false)
|
|
|
-
|
|
|
- dest := request.Destination()
|
|
|
- session := &proxy.SessionInfo{
|
|
|
- Source: clientAddr,
|
|
|
- Destination: dest,
|
|
|
- Inbound: v.meta,
|
|
|
+ if request.Command == proto.RequestCommandUDP {
|
|
|
+ v.handleUDP()
|
|
|
+ return
|
|
|
}
|
|
|
- log.Info("Socks: TCP Connect request to ", dest)
|
|
|
- log.Access(clientAddr, dest, log.AccessAccepted, "")
|
|
|
-
|
|
|
- v.transport(reader, writer, session)
|
|
|
- return nil
|
|
|
}
|
|
|
|
|
|
-func (v *Server) handleUDP(reader io.Reader, writer *bufio.BufferedWriter) error {
|
|
|
- response := protocol.NewSocks5Response()
|
|
|
- response.Error = protocol.ErrorSuccess
|
|
|
-
|
|
|
- udpAddr := v.udpAddress
|
|
|
-
|
|
|
- response.Port = udpAddr.Port
|
|
|
- switch udpAddr.Address.Family() {
|
|
|
- case v2net.AddressFamilyIPv4:
|
|
|
- response.SetIPv4(udpAddr.Address.IP())
|
|
|
- case v2net.AddressFamilyIPv6:
|
|
|
- response.SetIPv6(udpAddr.Address.IP())
|
|
|
- case v2net.AddressFamilyDomain:
|
|
|
- response.SetDomain(udpAddr.Address.Domain())
|
|
|
- }
|
|
|
-
|
|
|
- response.Write(writer)
|
|
|
- err := writer.Flush()
|
|
|
-
|
|
|
- if err != nil {
|
|
|
- log.Error("Socks: failed to write response: ", err)
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
+func (v *Server) handleUDP() error {
|
|
|
// The TCP connection closes after v method returns. We need to wait until
|
|
|
// the client closes it.
|
|
|
// TODO: get notified from UDP part
|
|
|
@@ -266,35 +148,6 @@ func (v *Server) handleUDP(reader io.Reader, writer *bufio.BufferedWriter) error
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (v *Server) handleSocks4(clientAddr v2net.Destination, reader *bufio.BufferedReader, writer *bufio.BufferedWriter, auth protocol.Socks4AuthenticationRequest) error {
|
|
|
- result := protocol.Socks4RequestGranted
|
|
|
- if auth.Command == protocol.CmdBind {
|
|
|
- result = protocol.Socks4RequestRejected
|
|
|
- }
|
|
|
- socks4Response := protocol.NewSocks4AuthenticationResponse(result, auth.Port, auth.IP[:])
|
|
|
-
|
|
|
- socks4Response.Write(writer)
|
|
|
-
|
|
|
- if result == protocol.Socks4RequestRejected {
|
|
|
- log.Warning("Socks: Unsupported socks 4 command ", auth.Command)
|
|
|
- log.Access(clientAddr, "", log.AccessRejected, ErrUnsupportedSocksCommand)
|
|
|
- return ErrUnsupportedSocksCommand
|
|
|
- }
|
|
|
-
|
|
|
- reader.SetBuffered(false)
|
|
|
- writer.SetBuffered(false)
|
|
|
-
|
|
|
- dest := v2net.TCPDestination(v2net.IPAddress(auth.IP[:]), auth.Port)
|
|
|
- session := &proxy.SessionInfo{
|
|
|
- Source: clientAddr,
|
|
|
- Destination: dest,
|
|
|
- Inbound: v.meta,
|
|
|
- }
|
|
|
- log.Access(clientAddr, dest, log.AccessAccepted, "")
|
|
|
- v.transport(reader, writer, session)
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
func (v *Server) transport(reader io.Reader, writer io.Writer, session *proxy.SessionInfo) {
|
|
|
ray := v.packetDispatcher.DispatchToOutbound(session)
|
|
|
input := ray.InboundInput()
|