Add pac support for mixed server

This commit is contained in:
世界 2022-04-12 22:50:23 +08:00
parent 869224c157
commit 04917323e6
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
14 changed files with 277 additions and 55 deletions

View file

@ -11,6 +11,7 @@ import (
"os"
"os/signal"
"runtime/debug"
"strconv"
"strings"
"syscall"
"time"
@ -30,6 +31,7 @@ import (
"github.com/sagernet/sing/protocol/shadowsocks/shadowaead"
"github.com/sagernet/sing/protocol/shadowsocks/shadowaead_2022"
"github.com/sagernet/sing/protocol/socks"
"github.com/sagernet/sing/protocol/wapd"
"github.com/sagernet/sing/transport/mixed"
"github.com/sagernet/sing/transport/system"
"github.com/sirupsen/logrus"
@ -52,6 +54,7 @@ type flags struct {
UseSystemRNG bool `json:"use_system_rng"`
ReducedSaltEntropy bool `json:"reduced_salt_entropy"`
ConfigFile string
WAPD bool
}
func main() {
@ -88,6 +91,7 @@ Only available with Linux kernel > 3.7.0.`)
command.Flags().BoolVarP(&f.Verbose, "verbose", "v", true, "Enable verbose mode.")
command.Flags().BoolVar(&f.UseSystemRNG, "use-system-rng", false, "Use system random number generator.")
command.Flags().BoolVar(&f.ReducedSaltEntropy, "reduced-salt-entropy", false, "Remapping salt to printable chars.")
command.Flags().BoolVar(&f.WAPD, "wapd", false, "Listen WADP server.")
err := command.Execute()
if err != nil {
@ -102,6 +106,7 @@ type LocalClient struct {
method shadowsocks.Method
dialer net.Dialer
bypass string
wapd *wapd.Listener
}
func NewLocalClient(f *flags) (*LocalClient, error) {
@ -247,6 +252,10 @@ func NewLocalClient(f *flags) (*LocalClient, error) {
client.Listener = mixed.NewListener(netip.AddrPortFrom(bind, f.LocalPort), nil, transproxyMode, client)
if f.WAPD {
client.wapd = wapd.NewListener(netip.MustParseAddr("255.255.255.255"), "http://127.0.0.1:"+strconv.Itoa(int(f.LocalPort))+"/proxy.pac", client)
}
if f.Bypass != "" {
err := geoip.LoadMMDB("Country.mmdb")
if err != nil {
@ -373,6 +382,14 @@ func Run(cmd *cobra.Command, flags *flags) {
logrus.Fatal(err)
}
if client.wapd != nil {
err = client.wapd.Start()
}
if err != nil {
logrus.Fatal(err)
}
logrus.Info("mixed server started at ", client.Listener.TCPListener.Addr())
osSignals := make(chan os.Signal, 1)
@ -383,6 +400,7 @@ func Run(cmd *cobra.Command, flags *flags) {
}
func (c *LocalClient) HandleError(err error) {
common.Close(err)
if E.IsClosed(err) {
return
}

View file

@ -9,6 +9,7 @@ import (
"syscall"
"github.com/sagernet/sing"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/redir"
@ -133,6 +134,7 @@ func (c *localClient) NewPacketConnection(conn socks.PacketConn, _ M.Metadata) e
}
func (c *localClient) OnError(err error) {
common.Close(err)
if E.IsClosed(err) {
return
}

View file

@ -96,20 +96,33 @@ func Must1(_ any, err error) {
}
}
func Close(closers ...any) {
func Close(closers ...any) error {
var retErr error
for _, closer := range closers {
if closer == nil {
continue
}
var err error
switch c := closer.(type) {
case io.Closer:
c.Close()
err = c.Close()
}
switch c := closer.(type) {
case ReaderWithUpstream:
Close(c.Upstream())
err = Close(c.Upstream())
case WriterWithUpstream:
Close(c.Upstream())
err = Close(c.Upstream())
}
if err != nil {
retErr = err
}
}
return retErr
}
func CloseError(closer any, err error) error {
if err == nil {
return nil
}
return Close(closer)
}

View file

@ -0,0 +1,10 @@
package metadata
import "net/netip"
func NetworkFromNetAddr(network string, addr netip.Addr) string {
if addr.Is4() && (addr.IsUnspecified() || addr.IsGlobalUnicast() || addr.IsLinkLocalUnicast()) {
return network + "4"
}
return network
}

2
go.mod
View file

@ -3,6 +3,7 @@ module github.com/sagernet/sing
go 1.18
require (
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41
github.com/klauspost/compress v1.15.1
github.com/openacid/low v0.1.21
github.com/oschwald/geoip2-golang v1.7.0
@ -33,6 +34,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

55
go.sum
View file

@ -5,19 +5,41 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 h1:BS21ZUJ/B5X2UVUbczfmdWH7GapPWAhxcMsDnjJTU1E=
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41 h1:Yg3n3AI7GoHnWt7dyjsLPU+TEuZfPAg0OdiA3MJUV6I=
github.com/insomniacslk/dhcp v0.0.0-20220405050111-12fbdcb11b41/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A=
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=
github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=
@ -42,10 +64,13 @@ github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c h1:pqy40B3M
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@ -54,21 +79,51 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
github.com/v2fly/v2ray-core/v5 v5.0.3 h1:2rnJ9vZbBQ7V4upWsoUVYGoqZl4grrx8SxOReKx+jjc=
github.com/v2fly/v2ray-core/v5 v5.0.3/go.mod h1:zhDdsUJcNE8LcLRA3l7fEQ6QLuveD4/OLbQM2CceSHM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c=
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.zx2c4.com/wireguard v0.0.0-20220407013110-ef5c587f782d h1:q4JksJ2n0fmbXC0Aj0eOs6E0AcPqnKglxWXWFqGD6x0=

View file

@ -12,7 +12,6 @@ import (
_ "unsafe"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/transport/tcp"
@ -22,14 +21,9 @@ type Handler interface {
tcp.Handler
}
func HandleConnection(conn *buf.BufferedConn, authenticator auth.Authenticator, handler Handler) error {
func HandleRequest(request *http.Request, conn net.Conn, authenticator auth.Authenticator, handler Handler, metadata M.Metadata) error {
var httpClient *http.Client
for {
request, err := readRequest(conn.Reader())
if err != nil {
return E.Cause(err, "read http request")
}
if authenticator != nil {
var authOk bool
authorization := request.Header.Get("Proxy-Authorization")
@ -39,7 +33,7 @@ func HandleConnection(conn *buf.BufferedConn, authenticator auth.Authenticator,
authOk = authenticator.Verify(userPswdArr[0], userPswdArr[1])
}
if !authOk {
err = responseWith(request, http.StatusProxyAuthRequired).Write(conn)
err := responseWith(request, http.StatusProxyAuthRequired).Write(conn)
if err != nil {
return err
}
@ -61,9 +55,8 @@ func HandleConnection(conn *buf.BufferedConn, authenticator auth.Authenticator,
if err != nil {
return E.Cause(err, "write http response")
}
return handler.NewConnection(conn, M.Metadata{
Destination: destination,
})
metadata.Destination = destination
return handler.NewConnection(conn, metadata)
}
keepAlive := strings.TrimSpace(strings.ToLower(request.Header.Get("Proxy-Connection"))) == "keep-alive"
@ -101,11 +94,10 @@ func HandleConnection(conn *buf.BufferedConn, authenticator auth.Authenticator,
left, right := net.Pipe()
go func() {
err = handler.NewConnection(right, M.Metadata{
Destination: destination,
})
metadata.Destination = destination
err = handler.NewConnection(right, metadata)
if err != nil {
handler.HandleError(err)
handler.HandleError(&tcp.Error{Conn: right, Cause: err})
}
}()
return left, nil
@ -137,11 +129,15 @@ func HandleConnection(conn *buf.BufferedConn, authenticator auth.Authenticator,
if err != nil {
return err
}
if !keepAlive {
return conn.Close()
}
}
}
//go:linkname readRequest net/http.ReadRequest
func readRequest(b *bufio.Reader) (req *http.Request, err error)
//go:linkname ReadRequest net/http.ReadRequest
func ReadRequest(b *bufio.Reader) (req *http.Request, err error)
func removeHopByHopHeaders(header http.Header) {
// Strip hop-by-hop header based on RFC:

View file

@ -35,7 +35,7 @@ func NewListener(bind netip.AddrPort, authenticator auth.Authenticator, handler
}
func (l *Listener) NewConnection(conn net.Conn, metadata M.Metadata) error {
return HandleConnection(conn, l.bindAddr, l.authenticator, l.handler)
return HandleConnection(conn, l.authenticator, l.bindAddr, l.handler, metadata)
}
func (l *Listener) Start() error {
@ -50,7 +50,7 @@ func (l *Listener) HandleError(err error) {
l.handler.HandleError(err)
}
func HandleConnection(conn net.Conn, bind netip.Addr, authenticator auth.Authenticator, handler Handler) error {
func HandleConnection(conn net.Conn, authenticator auth.Authenticator, bind netip.Addr, handler Handler, metadata M.Metadata) error {
authRequest, err := ReadAuthRequest(conn)
if err != nil {
return E.Cause(err, "read socks auth request")
@ -72,7 +72,7 @@ func HandleConnection(conn net.Conn, bind netip.Addr, authenticator auth.Authent
}
err = WriteAuthResponse(conn, &AuthResponse{
Version: authRequest.Version,
Method: AuthTypeNotRequired,
Method: authMethod,
})
if err != nil {
return E.Cause(err, "write socks auth response")
@ -109,9 +109,8 @@ func HandleConnection(conn net.Conn, bind netip.Addr, authenticator auth.Authent
if err != nil {
return E.Cause(err, "write socks response")
}
return handler.NewConnection(conn, M.Metadata{
Destination: request.Destination,
})
metadata.Destination = request.Destination
return handler.NewConnection(conn, metadata)
case CommandUDPAssociate:
network := "udp"
if bind.Is4() {
@ -130,15 +129,12 @@ func HandleConnection(conn net.Conn, bind netip.Addr, authenticator auth.Authent
if err != nil {
return E.Cause(err, "write socks response")
}
metadata.Destination = request.Destination
go func() {
err := handler.NewPacketConnection(NewPacketConn(conn, udpConn), M.Metadata{
Source: M.AddrPortFromNetAddr(conn.RemoteAddr()),
Destination: request.Destination,
})
err := handler.NewPacketConnection(NewPacketConn(conn, udpConn), metadata)
if err != nil {
handler.HandleError(err)
}
conn.Close()
}()
return common.Error(io.Copy(io.Discard, conn))
default:

52
protocol/wapd/listener.go Normal file
View file

@ -0,0 +1,52 @@
package wapd
import (
"fmt"
"net/netip"
"github.com/insomniacslk/dhcp/dhcpv4"
_ "github.com/insomniacslk/dhcp/dhcpv6"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/transport/udp"
"github.com/sirupsen/logrus"
)
type Listener struct {
*udp.Listener
E.Handler
option dhcpv4.Option
}
func NewListener(bind netip.Addr, proxyURL string, errorHandler E.Handler) *Listener {
l := &Listener{
Handler: errorHandler,
option: dhcpv4.Option{
Code: OptionCode,
Value: dhcpv4.String(fmt.Sprint(proxyURL)),
},
}
l.Listener = udp.NewUDPListener(netip.AddrPortFrom(bind, dhcpv4.ServerPort), l)
return l
}
func (l *Listener) NewPacket(packet *buf.Buffer, metadata M.Metadata) error {
request, err := dhcpv4.FromBytes(packet.Bytes())
if err != nil {
return E.Cause(err, "bad dhcpv4 packet")
}
logrus.Trace("DHCPv4 request ", request)
if !request.IsOptionRequested(OptionCode) {
return nil
}
reply, err := dhcpv4.NewReplyFromRequest(request, dhcpv4.WithOption(l.option))
if err != nil {
return E.Cause(err, "create response")
}
_, err = l.WriteTo(reply.ToBytes(), metadata.Source.UDPAddr())
if err != nil {
return E.Cause(err, "write response")
}
return nil
}

13
protocol/wapd/protocol.go Normal file
View file

@ -0,0 +1,13 @@
package wapd
var OptionCode = optionCode{}
type optionCode struct{}
func (C optionCode) Code() uint8 {
return 252
}
func (C optionCode) String() string {
return "Web Proxy Auto-Discovery Protocol"
}

View file

@ -1,11 +1,17 @@
package mixed
import (
"io"
"net"
netHttp "net/http"
"net/netip"
"strings"
"github.com/sagernet/sing"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/redir"
"github.com/sagernet/sing/common/udpnat"
@ -53,11 +59,40 @@ func (l *Listener) NewConnection(conn net.Conn, metadata M.Metadata) error {
return err
}
switch header[0] {
case socks.Version4, socks.Version5:
return socks.HandleConnection(bufConn, l.bindAddr, l.authenticator, l.handler)
default:
return http.HandleConnection(bufConn, l.authenticator, l.handler)
case socks.Version4:
return E.New("socks4 request dropped (TODO)")
case socks.Version5:
return socks.HandleConnection(bufConn, l.authenticator, l.bindAddr, l.handler, metadata)
}
request, err := http.ReadRequest(bufConn.Reader())
if err != nil {
return E.Cause(err, "read http request")
}
if request.Method == "GET" && request.URL.Path == "/proxy.pac" {
content := newPAC(M.AddrPortFromNetAddr(conn.LocalAddr()))
response := &netHttp.Response{
StatusCode: 200,
Status: netHttp.StatusText(200),
Proto: request.Proto,
ProtoMajor: request.ProtoMajor,
ProtoMinor: request.ProtoMinor,
Header: netHttp.Header{
"Content-Type": {"application/x-ns-proxy-autoconfig"},
"Server": {sing.VersionStr},
},
ContentLength: int64(len(content)),
Body: io.NopCloser(strings.NewReader(content)),
}
err = response.Write(bufConn)
if err != nil {
return E.Cause(err, "write pac response")
}
return nil
}
return http.HandleRequest(request, bufConn, l.authenticator, l.handler, metadata)
}
func (l *Listener) NewPacket(packet *buf.Buffer, metadata M.Metadata) error {
@ -80,9 +115,8 @@ func (l *Listener) Start() error {
}
func (l *Listener) Close() error {
l.TCPListener.Close()
if l.UDPListener != nil {
l.UDPListener.Close()
}
return nil
return common.Close(
l.TCPListener,
l.UDPListener,
)
}

19
transport/mixed/pac.go Normal file
View file

@ -0,0 +1,19 @@
package mixed
import M "github.com/sagernet/sing/common/metadata"
/*func newPAC(proxyAddr *M.AddrPort) string {
return `
function FindProxyForURL(url, host) {
return "SOCKS5 ` + proxyAddr.String() + `;SOCKS ` + proxyAddr.String() + `; PROXY ` + proxyAddr.String() + `";
}`
}
*/
func newPAC(proxyAddr *M.AddrPort) string {
// TODO: socks4 not supported
return `
function FindProxyForURL(url, host) {
return "SOCKS5 ` + proxyAddr.String() + `; PROXY ` + proxyAddr.String() + `";
}`
}

View file

@ -23,6 +23,23 @@ type Listener struct {
*net.TCPListener
}
type Error struct {
Conn net.Conn
Cause error
}
func (e *Error) Error() string {
return e.Cause.Error()
}
func (e *Error) Unwrap() error {
return e.Cause
}
func (e *Error) Close() error {
return common.Close(e.Conn)
}
func NewTCPListener(listen netip.AddrPort, handler Handler, options ...Option) *Listener {
listener := &Listener{
bind: listen,
@ -35,11 +52,7 @@ func NewTCPListener(listen netip.AddrPort, handler Handler, options ...Option) *
}
func (l *Listener) Start() error {
network := "tcp"
if l.bind.Addr() == netip.IPv4Unspecified() {
network = "tcp4"
}
tcpListener, err := net.ListenTCP(network, net.TCPAddrFromAddrPort(l.bind))
tcpListener, err := net.ListenTCP(M.NetworkFromNetAddr("tcp", l.bind.Addr()), net.TCPAddrFromAddrPort(l.bind))
if err != nil {
return err
}
@ -73,7 +86,9 @@ func (l *Listener) loop() {
l.Close()
return
}
var metadata M.Metadata
metadata := M.Metadata{
Source: M.AddrPortFromNetAddr(tcpConn.RemoteAddr()),
}
switch l.trans {
case redir.ModeRedirect:
metadata.Destination, _ = redir.GetOriginalDestination(tcpConn)
@ -86,9 +101,9 @@ func (l *Listener) loop() {
}
}
go func() {
err := l.handler.NewConnection(tcpConn, metadata)
if err != nil {
l.handler.HandleError(err)
hErr := l.handler.NewConnection(tcpConn, metadata)
if hErr != nil {
l.handler.HandleError(&Error{Conn: tcpConn, Cause: hErr})
}
}()
}

View file

@ -19,6 +19,7 @@ type Handler interface {
type Listener struct {
*net.UDPConn
handler Handler
network string
bind netip.AddrPort
tproxy bool
}
@ -35,11 +36,7 @@ func NewUDPListener(listen netip.AddrPort, handler Handler, options ...Option) *
}
func (l *Listener) Start() error {
network := "udp"
if l.bind.Addr() == netip.IPv4Unspecified() {
network = "udp4"
}
udpConn, err := net.ListenUDP(network, net.UDPAddrFromAddrPort(l.bind))
udpConn, err := net.ListenUDP(M.NetworkFromNetAddr("udp", l.bind.Addr()), net.UDPAddrFromAddrPort(l.bind))
if err != nil {
return err
}