Changes from v2ray-core (#93)

This commit is contained in:
Arthur Morgan 2020-12-25 03:45:35 +08:00 committed by GitHub
parent 85619b5a29
commit 6f25191822
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 85 additions and 39 deletions

View file

@ -36,19 +36,23 @@ func (m *AccessMessage) String() string {
builder.WriteString(string(m.Status)) builder.WriteString(string(m.Status))
builder.WriteByte(' ') builder.WriteByte(' ')
builder.WriteString(serial.ToString(m.To)) builder.WriteString(serial.ToString(m.To))
builder.WriteByte(' ')
if len(m.Detour) > 0 { if len(m.Detour) > 0 {
builder.WriteByte('[') builder.WriteString(" [")
builder.WriteString(m.Detour) builder.WriteString(m.Detour)
builder.WriteString("] ") builder.WriteByte(']')
}
if reason := serial.ToString(m.Reason); len(reason) > 0 {
builder.WriteString(" ")
builder.WriteString(reason)
} }
builder.WriteString(serial.ToString(m.Reason))
if len(m.Email) > 0 { if len(m.Email) > 0 {
builder.WriteString("email:") builder.WriteString(" email: ")
builder.WriteString(m.Email) builder.WriteString(m.Email)
builder.WriteByte(' ')
} }
return builder.String() return builder.String()
} }

View file

@ -8,7 +8,7 @@ import (
// ToString serialize an arbitrary value into string. // ToString serialize an arbitrary value into string.
func ToString(v interface{}) string { func ToString(v interface{}) string {
if v == nil { if v == nil {
return " " return ""
} }
switch value := v.(type) { switch value := v.(type) {

View file

@ -84,7 +84,7 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
geoipList, err := toCidrList(c.ExpectIPs) geoipList, err := toCidrList(c.ExpectIPs)
if err != nil { if err != nil {
return nil, newError("invalid ip rule: ", c.ExpectIPs).Base(err) return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
} }
return &dns.NameServer{ return &dns.NameServer{
@ -142,7 +142,7 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
for _, server := range c.Servers { for _, server := range c.Servers {
ns, err := server.Build() ns, err := server.Build()
if err != nil { if err != nil {
return nil, newError("failed to build name server").Base(err) return nil, newError("failed to build nameserver").Base(err)
} }
config.NameServer = append(config.NameServer, ns) config.NameServer = append(config.NameServer, ns)
} }
@ -159,15 +159,23 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
var mappings []*dns.Config_HostMapping var mappings []*dns.Config_HostMapping
switch { switch {
case strings.HasPrefix(domain, "domain:"): case strings.HasPrefix(domain, "domain:"):
domainName := domain[7:]
if len(domainName) == 0 {
return nil, newError("empty domain type of rule: ", domain)
}
mapping := getHostMapping(addr) mapping := getHostMapping(addr)
mapping.Type = dns.DomainMatchingType_Subdomain mapping.Type = dns.DomainMatchingType_Subdomain
mapping.Domain = domain[7:] mapping.Domain = domainName
mappings = append(mappings, mapping) mappings = append(mappings, mapping)
case strings.HasPrefix(domain, "geosite:"): case strings.HasPrefix(domain, "geosite:"):
domains, err := loadGeositeWithAttr("geosite.dat", strings.ToUpper(domain[8:])) listName := domain[8:]
if len(listName) == 0 {
return nil, newError("empty geosite rule: ", domain)
}
domains, err := loadGeositeWithAttr("geosite.dat", listName)
if err != nil { if err != nil {
return nil, newError("invalid geosite settings: ", domain).Base(err) return nil, newError("failed to load geosite: ", listName).Base(err)
} }
for _, d := range domains { for _, d := range domains {
mapping := getHostMapping(addr) mapping := getHostMapping(addr)
@ -177,21 +185,33 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
} }
case strings.HasPrefix(domain, "regexp:"): case strings.HasPrefix(domain, "regexp:"):
regexpVal := domain[7:]
if len(regexpVal) == 0 {
return nil, newError("empty regexp type of rule: ", domain)
}
mapping := getHostMapping(addr) mapping := getHostMapping(addr)
mapping.Type = dns.DomainMatchingType_Regex mapping.Type = dns.DomainMatchingType_Regex
mapping.Domain = domain[7:] mapping.Domain = regexpVal
mappings = append(mappings, mapping) mappings = append(mappings, mapping)
case strings.HasPrefix(domain, "keyword:"): case strings.HasPrefix(domain, "keyword:"):
keywordVal := domain[8:]
if len(keywordVal) == 0 {
return nil, newError("empty keyword type of rule: ", domain)
}
mapping := getHostMapping(addr) mapping := getHostMapping(addr)
mapping.Type = dns.DomainMatchingType_Keyword mapping.Type = dns.DomainMatchingType_Keyword
mapping.Domain = domain[8:] mapping.Domain = keywordVal
mappings = append(mappings, mapping) mappings = append(mappings, mapping)
case strings.HasPrefix(domain, "full:"): case strings.HasPrefix(domain, "full:"):
fullVal := domain[5:]
if len(fullVal) == 0 {
return nil, newError("empty full domain type of rule: ", domain)
}
mapping := getHostMapping(addr) mapping := getHostMapping(addr)
mapping.Type = dns.DomainMatchingType_Full mapping.Type = dns.DomainMatchingType_Full
mapping.Domain = domain[5:] mapping.Domain = fullVal
mappings = append(mappings, mapping) mappings = append(mappings, mapping)
case strings.HasPrefix(domain, "dotless:"): case strings.HasPrefix(domain, "dotless:"):
@ -213,10 +233,10 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
return nil, newError("invalid external resource: ", domain) return nil, newError("invalid external resource: ", domain)
} }
filename := kv[0] filename := kv[0]
country := kv[1] list := kv[1]
domains, err := loadGeositeWithAttr(filename, country) domains, err := loadGeositeWithAttr(filename, list)
if err != nil { if err != nil {
return nil, newError("failed to load domains: ", country, " from ", filename).Base(err) return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err)
} }
for _, d := range domains { for _, d := range domains {
mapping := getHostMapping(addr) mapping := getHostMapping(addr)

View file

@ -167,7 +167,7 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) {
switch fb.Dest[0] { switch fb.Dest[0] {
case '@', '/': case '@', '/':
fb.Type = "unix" fb.Type = "unix"
if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && runtime.GOOS == "linux" { if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") {
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy
copy(fullAddr, fb.Dest[1:]) copy(fullAddr, fb.Dest[1:])
fb.Dest = string(fullAddr) fb.Dest = string(fullAddr)

View file

@ -101,7 +101,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
switch fb.Dest[0] { switch fb.Dest[0] {
case '@', '/': case '@', '/':
fb.Type = "unix" fb.Type = "unix"
if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && runtime.GOOS == "linux" { if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && (runtime.GOOS == "linux" || runtime.GOOS == "android") {
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy
copy(fullAddr, fb.Dest[1:]) copy(fullAddr, fb.Dest[1:])
fb.Dest = string(fullAddr) fb.Dest = string(fullAddr)

View file

@ -168,6 +168,7 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u
rawConn.Close() rawConn.Close()
return nil, err return nil, err
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
rawConn.Close() rawConn.Close()

View file

@ -293,6 +293,7 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri
response.Close = true response.Close = true
result = nil result = nil
} }
defer response.Body.Close()
} else { } else {
newError("failed to read response from ", request.Host).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx)) newError("failed to read response from ", request.Host).Base(err).AtWarning().WriteToLog(session.ExportIDToError(ctx))
response = &http.Response{ response = &http.Response{

View file

@ -51,14 +51,19 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
if outbound == nil || !outbound.Target.IsValid() { if outbound == nil || !outbound.Target.IsValid() {
return newError("target not specified.") return newError("target not specified.")
} }
// Destination of the inner request.
destination := outbound.Target destination := outbound.Target
// Outbound server.
var server *protocol.ServerSpec var server *protocol.ServerSpec
// Outbound server's destination.
var dest net.Destination
// Connection to the outbound server.
var conn internet.Connection var conn internet.Connection
if err := retry.ExponentialBackoff(5, 100).On(func() error { if err := retry.ExponentialBackoff(5, 100).On(func() error {
server = c.serverPicker.PickServer() server = c.serverPicker.PickServer()
dest := server.Destination() dest = server.Destination()
rawConn, err := dialer.Dial(ctx, dest) rawConn, err := dialer.Dial(ctx, dest)
if err != nil { if err != nil {
return err return err
@ -101,6 +106,11 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
if err != nil { if err != nil {
return newError("failed to establish connection to server").AtWarning().Base(err) return newError("failed to establish connection to server").AtWarning().Base(err)
} }
if udpRequest != nil {
if udpRequest.Address == net.AnyIP || udpRequest.Address == net.AnyIPv6 {
udpRequest.Address = dest.Address
}
}
if err := conn.SetDeadline(time.Time{}); err != nil { if err := conn.SetDeadline(time.Time{}); err != nil {
newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx)) newError("failed to clear deadline after handshake").Base(err).WriteToLog(session.ExportIDToError(ctx))

View file

@ -16,7 +16,7 @@ const (
cmdTCPConnect = 0x01 cmdTCPConnect = 0x01
cmdTCPBind = 0x02 cmdTCPBind = 0x02
cmdUDPPort = 0x03 cmdUDPAssociate = 0x03
cmdTorResolve = 0xF0 cmdTorResolve = 0xF0
cmdTorResolvePTR = 0xF1 cmdTorResolvePTR = 0xF1
@ -39,8 +39,10 @@ var addrParser = protocol.NewAddressParser(
) )
type ServerSession struct { type ServerSession struct {
config *ServerConfig config *ServerConfig
port net.Port address net.Address
port net.Port
clientAddress net.Address
} }
func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
@ -162,7 +164,7 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri
case cmdTCPConnect, cmdTorResolve, cmdTorResolvePTR: case cmdTCPConnect, cmdTorResolve, cmdTorResolvePTR:
// We don't have a solution for Tor case now. Simply treat it as connect command. // We don't have a solution for Tor case now. Simply treat it as connect command.
request.Command = protocol.RequestCommandTCP request.Command = protocol.RequestCommandTCP
case cmdUDPPort: case cmdUDPAssociate:
if !s.config.UdpEnabled { if !s.config.UdpEnabled {
writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0))
return nil, newError("UDP is not enabled.") return nil, newError("UDP is not enabled.")
@ -185,15 +187,20 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri
request.Address = addr request.Address = addr
request.Port = port request.Port = port
responseAddress := net.AnyIP responseAddress := s.address
responsePort := net.Port(1717) responsePort := s.port
//nolint:gocritic // Use if else chain for clarity
if request.Command == protocol.RequestCommandUDP { if request.Command == protocol.RequestCommandUDP {
addr := s.config.Address.AsAddress() if s.config.Address != nil {
if addr == nil { // Use configured IP as remote address in the response to UdpAssociate
addr = net.LocalHostIP responseAddress = s.config.Address.AsAddress()
} else if s.clientAddress == net.LocalHostIP || s.clientAddress == net.LocalHostIPv6 {
// For localhost clients use loopback IP
responseAddress = s.clientAddress
} else {
// For non-localhost clients use inbound listening address
responseAddress = s.address
} }
responseAddress = addr
responsePort = s.port
} }
if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil { if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil {
return nil, err return nil, err
@ -446,7 +453,7 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i
command := byte(cmdTCPConnect) command := byte(cmdTCPConnect)
if request.Command == protocol.RequestCommandUDP { if request.Command == protocol.RequestCommandUDP {
command = byte(cmdUDPPort) command = byte(cmdUDPAssociate)
} }
common.Must2(b.Write([]byte{socks5Version, command, 0x00 /* reserved */})) common.Must2(b.Write([]byte{socks5Version, command, 0x00 /* reserved */}))
if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil { if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil {

View file

@ -89,8 +89,10 @@ func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispa
} }
svrSession := &ServerSession{ svrSession := &ServerSession{
config: s.config, config: s.config,
port: inbound.Gateway.Port, address: inbound.Gateway.Address,
port: inbound.Gateway.Port,
clientAddress: inbound.Source.Address,
} }
reader := &buf.BufferedReader{Reader: buf.NewReader(conn)} reader := &buf.BufferedReader{Reader: buf.NewReader(conn)}

View file

@ -136,8 +136,8 @@ func TestHTTPConnectionHeader(t *testing.T) {
} }
func TestDomainSocket(t *testing.T) { func TestDomainSocket(t *testing.T) {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" || runtime.GOOS == "android" {
t.Skip("Not supported on windows") t.Skip("Not supported on windows and android")
return return
} }
tcpServer := tcp.Server{ tcpServer := tcp.Server{

View file

@ -1,4 +1,5 @@
// +build !windows // +build !windows
// +build !android
package domainsocket_test package domainsocket_test

View file

@ -54,7 +54,7 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S
lc.Control = nil lc.Control = nil
network = addr.Network() network = addr.Network()
address = addr.Name address = addr.Name
if runtime.GOOS == "linux" && address[0] == '@' { if (runtime.GOOS == "linux" || runtime.GOOS == "android") && address[0] == '@' {
// linux abstract unix domain socket is lockfree // linux abstract unix domain socket is lockfree
if len(address) > 1 && address[1] == '@' { if len(address) > 1 && address[1] == '@' {
// but may need padding to work with haproxy // but may need padding to work with haproxy

View file

@ -48,7 +48,7 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSe
Net: "unix", Net: "unix",
}, streamSettings.SocketSettings) }, streamSettings.SocketSettings)
if err != nil { if err != nil {
return nil, newError("failed to listen Unix Doman Socket on ", address).Base(err) return nil, newError("failed to listen Unix Domain Socket on ", address).Base(err)
} }
newError("listening Unix Domain Socket on ", address).WriteToLog(session.ExportIDToError(ctx)) newError("listening Unix Domain Socket on ", address).WriteToLog(session.ExportIDToError(ctx))
locker := ctx.Value(address.Domain()) locker := ctx.Value(address.Domain())