mirror of
https://github.com/wweir/sower.git
synced 2026-10-19 16:14:33 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dab1a78bbf | ||
|
|
13a6be47a4 | ||
|
|
56b8e35fe7 | ||
|
|
47b8fc601f | ||
|
|
2dc128d1c8 | ||
|
|
2a4776dc28 | ||
|
|
d121427bbb | ||
|
|
91582d6095 | ||
|
|
396b08c0ac | ||
|
|
78b6d4df2e | ||
|
|
1574623507 | ||
|
|
ee6335fa47 |
@@ -1,373 +1,22 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
MIT License
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
Copyright (c) 2020 wweir
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
+100
-58
@@ -1,33 +1,39 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
toml "github.com/pelletier/go-toml"
|
||||
"github.com/wweir/sower/util"
|
||||
"github.com/wweir/utils/log"
|
||||
"github.com/wweir/util-go/log"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
type Client struct {
|
||||
Address string `toml:"address"`
|
||||
HTTPProxy string `toml:"http_proxy"`
|
||||
DNSServeIP string `toml:"dns_serve_ip"`
|
||||
DNSUpstream string `toml:"dns_upstream"`
|
||||
Socks5Proxy string `toml:"socks5"`
|
||||
HTTPProxy string `toml:"http_proxy"`
|
||||
PortForward map[string]string `toml:"port_forward"`
|
||||
|
||||
Router struct {
|
||||
DetectLevel int `toml:"detect_level"`
|
||||
DetectTimeout string `toml:"detect_timeout"`
|
||||
|
||||
DetectLevel int `toml:"detect_level"`
|
||||
ProxyList []string `toml:"proxy_list"`
|
||||
ProxyRefs []string `toml:"proxy_refs"`
|
||||
DirectList []string `toml:"direct_list"`
|
||||
DynamicList []string `toml:"dynamic_list"`
|
||||
DirectRefs []string `toml:"direct_refs"`
|
||||
BlockList []string `toml:"block_list"`
|
||||
BlockRefs []string `toml:"block_refs"`
|
||||
} `toml:"router"`
|
||||
}
|
||||
type server struct {
|
||||
type Server struct {
|
||||
Upstream string `toml:"upstream"`
|
||||
CertFile string `toml:"cert_file"`
|
||||
KeyFile string `toml:"key_file"`
|
||||
@@ -37,82 +43,115 @@ type server struct {
|
||||
var (
|
||||
version, date string
|
||||
|
||||
installCmd string
|
||||
execFile, _ = os.Executable()
|
||||
execDir, _ = filepath.Abs(filepath.Dir(execFile))
|
||||
// Conf full config, include common and server / client
|
||||
Conf = struct {
|
||||
conf = struct {
|
||||
file string
|
||||
Password string `toml:"password"`
|
||||
Server server `toml:"server"`
|
||||
Client client `toml:"client"`
|
||||
Client Client `toml:"client"`
|
||||
Server Server `toml:"server"`
|
||||
}{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&Conf.Password, "password", "", "password")
|
||||
flag.StringVar(&Conf.Server.Upstream, "s", "", "upstream http service, eg: 127.0.0.1:8080")
|
||||
flag.StringVar(&Conf.Server.CertFile, "s_cert", "", "tls cert file, gen cert from letsencrypt if empty")
|
||||
flag.StringVar(&Conf.Server.KeyFile, "s_key", "", "tls key file, gen cert from letsencrypt if empty")
|
||||
flag.StringVar(&Conf.Client.Address, "c", "", "remote server domain, eg: aa.bb.cc, socks5h://127.0.0.1:1080")
|
||||
flag.StringVar(&Conf.Client.HTTPProxy, "http_proxy", ":8080", "http proxy, empty to disable")
|
||||
flag.IntVar(&Conf.Client.Router.DetectLevel, "level", 2, "dynamic rule detect level: 0~4")
|
||||
flag.StringVar(&Conf.Client.Router.DetectTimeout, "timeout", "300ms", "dynamic rule detect timeout")
|
||||
uninstallFlag := flag.Bool("uninstall", false, "uninstall service")
|
||||
_init() // execute platform init logic
|
||||
func Init() (*Client, *Server, string) {
|
||||
beforeInitFlag()
|
||||
defer afterInitFlag()
|
||||
flag.StringVar(&conf.Password, "password", "", "password")
|
||||
flag.StringVar(&conf.Server.Upstream, "s", "", "upstream http service, eg: 127.0.0.1:8080")
|
||||
flag.StringVar(&conf.Server.CertFile, "s_cert", "", "tls cert file, gen cert from letsencrypt if empty")
|
||||
flag.StringVar(&conf.Server.KeyFile, "s_key", "", "tls key file, gen cert from letsencrypt if empty")
|
||||
flag.StringVar(&conf.Client.Address, "c", "", "remote server domain, eg: aa.bb.cc, socks5h://127.0.0.1:1080")
|
||||
flag.StringVar(&conf.Client.HTTPProxy, "http_proxy", ":8080", "http proxy, empty to disable")
|
||||
|
||||
if !flag.Parsed() {
|
||||
flag.Parse()
|
||||
}
|
||||
switch {
|
||||
case *uninstallFlag:
|
||||
uninstall()
|
||||
os.Exit(0)
|
||||
case installCmd != "":
|
||||
install()
|
||||
os.Exit(0)
|
||||
default:
|
||||
runAsService()
|
||||
}
|
||||
|
||||
defer log.Infow("starting", "config", &Conf)
|
||||
if Conf.file == "" {
|
||||
return
|
||||
defer log.Infow("starting", "version", version, "date", date, "config", &conf)
|
||||
if conf.file == "" {
|
||||
return &conf.Client, &conf.Server, conf.Password
|
||||
}
|
||||
|
||||
for i := range loadConfigFns {
|
||||
if err := loadConfigFns[i].fn(); err != nil {
|
||||
log.Fatalw("load config", "config", Conf.file, "step", loadConfigFns[i].step, "err", err)
|
||||
log.Fatalw("load config", "config", conf.file, "step", loadConfigFns[i].step, "err", err)
|
||||
}
|
||||
}
|
||||
return &conf.Client, &conf.Server, conf.Password
|
||||
}
|
||||
|
||||
// refreshFns will be executed while init and write new config
|
||||
var loadConfigFns = []struct {
|
||||
step string
|
||||
fn func() error
|
||||
}{{"load_config", func() error {
|
||||
f, err := os.OpenFile(Conf.file, os.O_RDONLY, 0644)
|
||||
}{{"parse file", func() error {
|
||||
f, err := os.OpenFile(conf.file, os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return toml.NewDecoder(f).Decode(&Conf)
|
||||
return toml.NewDecoder(f).Decode(&conf)
|
||||
}}, {"load referenced rule", func() error {
|
||||
for _, addr := range conf.Client.Router.BlockRefs {
|
||||
lines, err := getRemoteRuleLines(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf.Client.Router.BlockList = append(conf.Client.Router.BlockList, lines...)
|
||||
}
|
||||
for _, addr := range conf.Client.Router.ProxyRefs {
|
||||
lines, err := getRemoteRuleLines(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf.Client.Router.ProxyList = append(conf.Client.Router.ProxyList, lines...)
|
||||
}
|
||||
for _, addr := range conf.Client.Router.DirectRefs {
|
||||
lines, err := getRemoteRuleLines(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf.Client.Router.DirectList = append(conf.Client.Router.DirectList, lines...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}}}
|
||||
|
||||
func getRemoteRuleLines(addr string) ([]string, error) {
|
||||
resp, err := http.Get(addr)
|
||||
if err != nil {
|
||||
return nil, xerrors.New(err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
br := bufio.NewReader(resp.Body)
|
||||
lines := []string{}
|
||||
for {
|
||||
line, _, err := br.ReadLine()
|
||||
if err == io.EOF {
|
||||
return lines, nil
|
||||
} else if err != nil {
|
||||
return nil, xerrors.New(err.Error())
|
||||
}
|
||||
|
||||
lines = append(lines, "**."+strings.TrimSpace(string(line)))
|
||||
}
|
||||
}
|
||||
|
||||
// flushCh to avoid parallel persist
|
||||
var flushCh = make(chan struct{})
|
||||
var flushOnce = sync.Once{}
|
||||
|
||||
// PersistRule persist dynamic rule into config file
|
||||
// PersistRule persist rule into config file
|
||||
func PersistRule(domain string) {
|
||||
flushOnce.Do(func() {
|
||||
go flushConfDaemon()
|
||||
})
|
||||
|
||||
log.Infow("persist dynamic rule into config", "domain", domain)
|
||||
Conf.Client.Router.DynamicList = append(Conf.Client.Router.DynamicList, domain)
|
||||
log.Infow("persist direct rule into config", "domain", domain)
|
||||
conf.Client.Router.DirectList = append(conf.Client.Router.DirectList, domain)
|
||||
select {
|
||||
case flushCh <- struct{}{}:
|
||||
default:
|
||||
@@ -121,34 +160,37 @@ func PersistRule(domain string) {
|
||||
func flushConfDaemon() {
|
||||
for range flushCh {
|
||||
// safe write file
|
||||
if Conf.file != "" {
|
||||
f, err := os.OpenFile(Conf.file+"~", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if conf.file != "" {
|
||||
f, err := os.OpenFile(conf.file+"~", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
log.Errorw("flush config", "step", "flush", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
Conf.Client.Router.DynamicList =
|
||||
util.NewReverseSecSlice(Conf.Client.Router.DynamicList).Sort().Uniq()
|
||||
conf.Client.Router.DirectList =
|
||||
util.NewReverseSecSlice(conf.Client.Router.DirectList).Sort().Uniq()
|
||||
|
||||
if err := toml.NewEncoder(f).ArraysWithOneElementPerLine(true).Encode(&Conf); err != nil {
|
||||
if err := toml.NewEncoder(f).ArraysWithOneElementPerLine(true).Encode(&conf); err != nil {
|
||||
log.Errorw("flush config", "step", "flush", "err", err)
|
||||
f.Close()
|
||||
continue
|
||||
}
|
||||
f.Close()
|
||||
|
||||
if err = os.Rename(Conf.file+"~", Conf.file); err != nil {
|
||||
if stat, err := os.Stat(conf.file); err != nil {
|
||||
log.Warnw("get file stat", "file", conf.file, "err", err)
|
||||
} else {
|
||||
// There is no common way to transfer ownership for a file
|
||||
// cross-platform. Drop the ownership support but file mod.
|
||||
if err = os.Chmod(conf.file+"~", stat.Mode()); err != nil {
|
||||
log.Warnw("set file mod", "file", conf.file+"~", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = os.Rename(conf.file+"~", conf.file); err != nil {
|
||||
log.Errorw("flush config", "step", "flush", "err", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// reload config
|
||||
for i := range loadConfigFns {
|
||||
if err := loadConfigFns[i].fn(); err != nil {
|
||||
log.Errorw("flush config", "step", loadConfigFns[i].step, "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-7
@@ -12,7 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/wweir/utils/log"
|
||||
"github.com/wweir/util-go/log"
|
||||
)
|
||||
|
||||
const svcPath = "/Library/LaunchDaemons/sower.plist"
|
||||
@@ -35,9 +35,13 @@ const svcFile = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
</dict>
|
||||
</plist>`
|
||||
|
||||
var ConfigDir = ""
|
||||
var (
|
||||
ConfigDir = ""
|
||||
installCmd = ""
|
||||
uninstallFlag = false
|
||||
)
|
||||
|
||||
func _init() {
|
||||
func beforeInitFlag() {
|
||||
if _, err := os.Stat(execDir + "/sower.toml"); err == nil {
|
||||
ConfigDir = execDir
|
||||
} else {
|
||||
@@ -46,15 +50,26 @@ func _init() {
|
||||
}
|
||||
|
||||
if _, err := os.Stat(ConfigDir + "/sower.toml"); err != nil {
|
||||
flag.StringVar(&Conf.file, "f", "", "config file, rewrite all other parameters if set")
|
||||
flag.StringVar(&conf.file, "f", "", "config file, rewrite all other parameters if set")
|
||||
} else {
|
||||
flag.StringVar(&Conf.file, "f", ConfigDir+"/sower.toml", "config file, rewrite all other parameters if set")
|
||||
flag.StringVar(&conf.file, "f", ConfigDir+"/sower.toml", "config file, rewrite all other parameters if set")
|
||||
}
|
||||
|
||||
flag.StringVar(&installCmd, "install", "", "install service with cmd, eg: '-f \""+ConfigDir+"/sower.toml\"'")
|
||||
flag.BoolVar(&uninstallFlag, "uninstall", false, "uninstall service")
|
||||
}
|
||||
|
||||
func runAsService() {}
|
||||
func afterInitFlag() {
|
||||
switch {
|
||||
case installCmd != "":
|
||||
install()
|
||||
case uninstallFlag:
|
||||
uninstall()
|
||||
default:
|
||||
return
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func install() {
|
||||
if err := ioutil.WriteFile(svcPath, []byte(fmt.Sprintf(svcFile, execFile, installCmd)), 0644); err != nil {
|
||||
@@ -62,7 +77,7 @@ func install() {
|
||||
}
|
||||
|
||||
execute("launchctl unload " + svcPath)
|
||||
if err := execute("launchctl load -w " + svcPath); err != nil {
|
||||
if err := execute("launchctl load -wF " + svcPath); err != nil {
|
||||
log.Fatalw("install service", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
+21
-6
@@ -12,7 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/wweir/utils/log"
|
||||
"github.com/wweir/util-go/log"
|
||||
)
|
||||
|
||||
const svcPath = "/etc/systemd/system/sower.service"
|
||||
@@ -31,9 +31,13 @@ ExecStart=%s %s
|
||||
RestartSec=3
|
||||
Restart=on-failure`
|
||||
|
||||
var ConfigDir = ""
|
||||
var (
|
||||
ConfigDir = ""
|
||||
installCmd = ""
|
||||
uninstallFlag = false
|
||||
)
|
||||
|
||||
func _init() {
|
||||
func beforeInitFlag() {
|
||||
if _, err := os.Stat(execDir + "/sower.toml"); err == nil {
|
||||
ConfigDir = execDir
|
||||
|
||||
@@ -46,15 +50,26 @@ func _init() {
|
||||
}
|
||||
|
||||
if _, err := os.Stat(ConfigDir + "/sower.toml"); err != nil {
|
||||
flag.StringVar(&Conf.file, "f", "", "config file, rewrite all other parameters if set")
|
||||
flag.StringVar(&conf.file, "f", "", "config file, rewrite all other parameters if set")
|
||||
} else {
|
||||
flag.StringVar(&Conf.file, "f", ConfigDir+"/sower.toml", "config file, rewrite all other parameters if set")
|
||||
flag.StringVar(&conf.file, "f", ConfigDir+"/sower.toml", "config file, rewrite all other parameters if set")
|
||||
}
|
||||
|
||||
flag.StringVar(&installCmd, "install", "", "install service with cmd, eg: '-f "+ConfigDir+"/sower.toml'")
|
||||
flag.BoolVar(&uninstallFlag, "uninstall", false, "uninstall service")
|
||||
}
|
||||
|
||||
func runAsService() {}
|
||||
func afterInitFlag() {
|
||||
switch {
|
||||
case installCmd != "":
|
||||
install()
|
||||
case uninstallFlag:
|
||||
uninstall()
|
||||
default:
|
||||
return
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func install() {
|
||||
if err := ioutil.WriteFile(svcPath, []byte(fmt.Sprintf(svcFile, execFile, installCmd)), 0644); err != nil {
|
||||
|
||||
+24
-6
@@ -13,7 +13,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/wweir/utils/log"
|
||||
"github.com/wweir/util-go/log"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
@@ -23,17 +23,34 @@ import (
|
||||
const name = "sower"
|
||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
|
||||
|
||||
var ConfigDir = execDir
|
||||
var (
|
||||
ConfigDir = ""
|
||||
installCmd = false
|
||||
uninstallFlag = false
|
||||
)
|
||||
|
||||
func _init() {
|
||||
flag.StringVar(&Conf.file, "f", filepath.Join(execDir, "sower.toml"), "config file, rewrite all other parameters if set")
|
||||
flag.StringVar(&installCmd, "install", "", "put any character to install as a service, eg: true")
|
||||
func beforeInitFlag() {
|
||||
flag.StringVar(&conf.file, "f", filepath.Join(execDir, "sower.toml"), "config file, rewrite all other parameters if set")
|
||||
flag.BoolVar(&installCmd, "install", false, "put any character to install as a service, eg: true")
|
||||
}
|
||||
|
||||
func afterInitFlag() {
|
||||
switch {
|
||||
case installCmd:
|
||||
install()
|
||||
case uninstallFlag:
|
||||
uninstall()
|
||||
default:
|
||||
runAsService()
|
||||
return
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func runAsService() {
|
||||
os.Chdir(filepath.Dir(os.Args[0]))
|
||||
if active, err := svc.IsAnInteractiveSession(); err != nil {
|
||||
log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
|
||||
log.Fatalw("failed to determine if we are running in an interactive session", "err", err)
|
||||
} else if !active {
|
||||
go func() {
|
||||
elog, err := eventlog.Open(name)
|
||||
@@ -86,6 +103,7 @@ func uninstall() {
|
||||
return eventlog.Remove(name)
|
||||
})
|
||||
}
|
||||
|
||||
func serviceDo(fn func(*mgr.Service) error) {
|
||||
mgrDo(func(m *mgr.Mgr) error {
|
||||
s, err := m.OpenService(name)
|
||||
|
||||
+59
-49
@@ -1,55 +1,65 @@
|
||||
password="" # sower password
|
||||
password = "" # sower password
|
||||
|
||||
[client]
|
||||
address = "" # aa.bb.cc, socks5h://127.0.0.1:1080
|
||||
http_proxy=":8080"
|
||||
dns_serve_ip="" # eg: 127.0.0.1. keep empty to disable DNS solution
|
||||
dns_upstream="" # keep empty to set via dhcp, not effective in any environment
|
||||
address = "" # aa.bb.cc, socks5h://127.0.0.1:1080
|
||||
dns_upstream = "" # keep empty to set via dhcp, not effective in any environment
|
||||
http_proxy = ""
|
||||
socks5 = ""
|
||||
|
||||
[client.port_forward]
|
||||
# eg: ":2222"="aa.bb.cc:22"
|
||||
|
||||
[client.router]
|
||||
detect_level = 2 # 0~4, the bigger the harder to add
|
||||
detect_timeout = "300ms"
|
||||
direct_list = [
|
||||
"**.in-addr.arpa",
|
||||
"imap.*.*",
|
||||
"imap.*.*.*",
|
||||
"smtp.*.*",
|
||||
"smtp.*.*.*",
|
||||
"pop.*.*",
|
||||
"pop.*.*.*",
|
||||
"**.cn",
|
||||
]
|
||||
proxy_list = [
|
||||
"**.google.*",
|
||||
"**.goo.gl",
|
||||
"**.googleusercontent.com",
|
||||
"**.googleapis.com",
|
||||
"*.googlesource.com",
|
||||
"**.youtube.com",
|
||||
"**.ytimg.com",
|
||||
"**.ggpht.com",
|
||||
"**.googlevideo.com",
|
||||
"**.facebook.com",
|
||||
"**.fbcdn.net",
|
||||
"**.twitter.com",
|
||||
"**.twimg.com",
|
||||
"**.blogspot.com",
|
||||
"**.appspot.com",
|
||||
"**.wikipedia.org",
|
||||
"*.cloudfront.net",
|
||||
"**.amazon.com",
|
||||
"**.amazonaws.com",
|
||||
"*.githubusercontent.com",
|
||||
"*.githubassets.com",
|
||||
"*.github.*",
|
||||
]
|
||||
[client.port_forward]
|
||||
# eg: ":2222"="aa.bb.cc:22"
|
||||
|
||||
[client.router]
|
||||
block_refs = [
|
||||
"https://cdn.jsdelivr.net/gh/pexcn/daily@gh-pages/adlist/adlist.txt",
|
||||
]
|
||||
detect_level = 0 # [-4, 4], the bigger the harder to add
|
||||
direct_cird_refs = [
|
||||
"https://cdn.jsdelivr.net/gh/pexcn/daily@gh-pages/chnroute/chnroute.txt",
|
||||
]
|
||||
direct_list = [
|
||||
"**.in-addr.arpa",
|
||||
"imap.*.*",
|
||||
"imap.*.*.*",
|
||||
"smtp.*.*",
|
||||
"smtp.*.*.*",
|
||||
"pop.*.*",
|
||||
"pop.*.*.*",
|
||||
"**.cn",
|
||||
]
|
||||
direct_refs = [
|
||||
"https://cdn.jsdelivr.net/gh/pexcn/daily@gh-pages/chinalist/chinalist.txt",
|
||||
]
|
||||
proxy_list = [
|
||||
"**.google.*",
|
||||
"**.goo.gl",
|
||||
"**.googleusercontent.com",
|
||||
"**.googleapis.com",
|
||||
"*.googlesource.com",
|
||||
"**.youtube.com",
|
||||
"**.ytimg.com",
|
||||
"**.ggpht.com",
|
||||
"**.googlevideo.com",
|
||||
"**.facebook.com",
|
||||
"**.fbcdn.net",
|
||||
"**.twitter.com",
|
||||
"**.twimg.com",
|
||||
"**.blogspot.com",
|
||||
"**.appspot.com",
|
||||
"**.wikipedia.org",
|
||||
"*.cloudfront.net",
|
||||
"**.amazon.com",
|
||||
"**.amazonaws.com",
|
||||
"*.githubusercontent.com",
|
||||
"*.githubassets.com",
|
||||
"*.github.*",
|
||||
]
|
||||
proxy_refs = [
|
||||
"https://cdn.jsdelivr.net/gh/pexcn/daily@gh-pages/gfwlist/gfwlist.txt",
|
||||
]
|
||||
|
||||
[server]
|
||||
cert_email = "" # eg: user@aa.bb.cc
|
||||
cert_file = "" # eg: /etc/ssl/server.crt
|
||||
key_file = "" # eg: /etc/ssl/server.key
|
||||
upstream = "" # eg: 127.0.0.1:8080
|
||||
cert_email = "" # eg: user@aa.bb.cc
|
||||
cert_file = "" # eg: /etc/ssl/server.crt
|
||||
key_file = "" # eg: /etc/ssl/server.key
|
||||
upstream = "" # eg: 127.0.0.1:8080
|
||||
|
||||
@@ -3,7 +3,7 @@ package dhcp_test
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wweir/sower/proxy/dhcp"
|
||||
"github.com/wweir/sower/dhcp"
|
||||
)
|
||||
|
||||
func Example_dns() {
|
||||
@@ -3,7 +3,7 @@ package dhcp_test
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wweir/sower/proxy/dhcp"
|
||||
"github.com/wweir/sower/dhcp"
|
||||
)
|
||||
|
||||
func Example_iface() {
|
||||
@@ -1,14 +1,16 @@
|
||||
module github.com/wweir/sower
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771
|
||||
github.com/libp2p/go-reuseport v0.0.1
|
||||
github.com/miekg/dns v1.1.27
|
||||
github.com/pelletier/go-toml v1.6.0
|
||||
github.com/libp2p/go-reuseport v0.0.2
|
||||
github.com/miekg/dns v1.1.30
|
||||
github.com/pelletier/go-toml v1.8.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/wweir/utils v0.0.0-20200214114658-f6f356a08736
|
||||
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4
|
||||
github.com/wweir/util-go/log v0.0.0-20200701032032-3cff7b4a46ea
|
||||
github.com/wweir/util-go/mem v0.0.0-20200701032032-3cff7b4a46ea
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
)
|
||||
|
||||
@@ -1,102 +1,75 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/guregu/null v3.4.0+incompatible h1:a4mw37gBO7ypcBlTJeZGuMpSxxFTV9qFfFKgWxQSGaM=
|
||||
github.com/guregu/null v3.4.0+incompatible/go.mod h1:ePGpQaN9cw0tj45IR5E5ehMvsFlLlQZAkkOXZurJ3NM=
|
||||
github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771 h1:t2c2B9g1ZVhMYduqmANSEGVD3/1WlsrEYNPtVoFlENk=
|
||||
github.com/krolaw/dhcp4 v0.0.0-20190909130307-a50d88189771/go.mod h1:0AqAH3ZogsCrvrtUpvc6EtVKbc3w6xwZhkvGLuqyi3o=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw=
|
||||
github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
|
||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
|
||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||
github.com/libp2p/go-reuseport v0.0.2 h1:XSG94b1FJfGA01BUrT82imejHQyTxO4jEWqheyCXYvU=
|
||||
github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ=
|
||||
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
|
||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ulule/deepcopier v0.0.0-20171107155558-ca99b135e50f h1:QatZ4lsJBY3x1+Imst9g95+vUl7m52dqM9Pi4aSMW8w=
|
||||
github.com/ulule/deepcopier v0.0.0-20171107155558-ca99b135e50f/go.mod h1:BNLmYJ8oMJPIPpNx5968jCyUhwEU1XT3YsuOqtbo5qo=
|
||||
github.com/wweir/utils v0.0.0-20200214114658-f6f356a08736 h1:x6LiUrnHR4CCqzRzB0zqiB9hzVFcRhSKrVlZDGW0FcM=
|
||||
github.com/wweir/utils v0.0.0-20200214114658-f6f356a08736/go.mod h1:Nv4eBGkUJiHDPgVowJJlQNcJYgMfst6IkjaDThH2/yI=
|
||||
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 h1:TtyC78WMafNW8QFfv3TeP3yWNDG+uxNkk9vOrnDu6JA=
|
||||
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6/go.mod h1:h8272+G2omSmi30fBXiZDMkmHuOgonplfKIKjQWzlfs=
|
||||
github.com/wweir/util-go v0.0.0-20200701032032-3cff7b4a46ea h1:V/bIKmS5Bv4zMwQ5qE7ugJiHpcAVFnAxvxRbb7OCwug=
|
||||
github.com/wweir/util-go/log v0.0.0-20200701032032-3cff7b4a46ea h1:IlO7d9R0I/l2syxJkx4/PURW9yVf4hy/Qf2IzdR4y38=
|
||||
github.com/wweir/util-go/log v0.0.0-20200701032032-3cff7b4a46ea/go.mod h1:OObmMboiahxgJ1P3twG+AN8EAZ/wz14vstI4E5kaZzI=
|
||||
github.com/wweir/util-go/mem v0.0.0-20200701032032-3cff7b4a46ea h1:nXgUp7CflDJnuR+zd43DHKxRA/hvuMkFtueSRLY7mEc=
|
||||
github.com/wweir/util-go/mem v0.0.0-20200701032032-3cff7b4a46ea/go.mod h1:gp1gaDO1K4uV/B8NfrTjVim99Xq5xAmm1SHVamRsVW0=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
|
||||
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
|
||||
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
|
||||
@@ -7,36 +7,36 @@ import (
|
||||
"github.com/wweir/sower/conf"
|
||||
"github.com/wweir/sower/proxy"
|
||||
"github.com/wweir/sower/router"
|
||||
"github.com/wweir/sower/transport"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client, server, password := conf.Init()
|
||||
|
||||
switch {
|
||||
case conf.Conf.Server.Upstream != "":
|
||||
proxy.StartServer(conf.Conf.Server.Upstream, conf.Conf.Password, conf.ConfigDir,
|
||||
conf.Conf.Server.CertFile, conf.Conf.Server.KeyFile, conf.Conf.Server.CertEmail)
|
||||
case server.Upstream != "":
|
||||
proxy.StartServer(server.Upstream, password, conf.ConfigDir,
|
||||
server.CertFile, server.KeyFile, server.CertEmail)
|
||||
|
||||
case conf.Conf.Client.Address != "":
|
||||
route := &router.Route{
|
||||
ProxyAddress: conf.Conf.Client.Address,
|
||||
ProxyPassword: conf.Conf.Password,
|
||||
DetectLevel: conf.Conf.Client.Router.DetectLevel,
|
||||
DetectTimeout: conf.Conf.Client.Router.DetectTimeout,
|
||||
DirectList: conf.Conf.Client.Router.DirectList,
|
||||
ProxyList: conf.Conf.Client.Router.ProxyList,
|
||||
DynamicList: conf.Conf.Client.Router.DynamicList,
|
||||
PersistFn: conf.PersistRule,
|
||||
case client.Address != "":
|
||||
route := router.NewRoute(client.Address, password, client.Router.DetectLevel,
|
||||
client.Router.BlockList, client.Router.ProxyList, client.Router.DirectList,
|
||||
conf.PersistRule)
|
||||
|
||||
if client.Socks5Proxy != "" {
|
||||
go proxy.StartSocks5Proxy(client.Socks5Proxy, client.Address, []byte(password))
|
||||
}
|
||||
|
||||
go proxy.StartHTTPProxy(conf.Conf.Client.HTTPProxy, conf.Conf.Client.Address,
|
||||
[]byte(conf.Conf.Password), route.ShouldProxy)
|
||||
|
||||
if conf.Conf.Client.DNSServeIP != "" {
|
||||
go proxy.StartDNS(conf.Conf.Client.DNSServeIP, conf.Conf.Client.DNSUpstream,
|
||||
route.ShouldProxy)
|
||||
if client.HTTPProxy != "" {
|
||||
go proxy.StartHTTPProxy(client.HTTPProxy, client.Address,
|
||||
[]byte(password), route.GenProxyCheck(true))
|
||||
}
|
||||
|
||||
proxy.StartClient(conf.Conf.Client.Address, conf.Conf.Password,
|
||||
conf.Conf.Client.DNSServeIP != "", conf.Conf.Client.PortForward)
|
||||
transport.SetDNS(nil, client.DNSUpstream)
|
||||
go proxy.StartDNS(client.DNSUpstream, route.GenProxyCheck(false))
|
||||
|
||||
proxy.StartClient(client.Address, password,
|
||||
client.PortForward, route.GenProxyCheck(true))
|
||||
|
||||
default:
|
||||
fmt.Println()
|
||||
|
||||
+39
-13
@@ -3,24 +3,27 @@ package proxy
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/wweir/sower/proxy/dhcp"
|
||||
"github.com/wweir/utils/log"
|
||||
"github.com/wweir/sower/dhcp"
|
||||
"github.com/wweir/util-go/log"
|
||||
)
|
||||
|
||||
func StartDNS(redirectIP, relayServer string, shouldProxy func(string) bool) {
|
||||
serveIP := net.ParseIP(redirectIP)
|
||||
if redirectIP == "" || serveIP.String() != redirectIP {
|
||||
log.Fatalw("invalid listen ip", "ip", redirectIP)
|
||||
}
|
||||
type msgCache struct {
|
||||
*dns.Msg
|
||||
time.Time
|
||||
}
|
||||
|
||||
var cache sync.Map
|
||||
|
||||
func StartDNS(relayServer string, shouldProxy func(string) (bool, bool)) {
|
||||
var err error
|
||||
if relayServer, err = pickRelayAddr(relayServer); err != nil {
|
||||
log.Fatalw("pick upstream dns server", "err", err)
|
||||
}
|
||||
log.Infow("detect upstream dns", "addr", relayServer)
|
||||
log.Infow("upstream dns", "addr", relayServer)
|
||||
|
||||
dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
// *Msg r has an TSIG record and it was validated
|
||||
@@ -39,12 +42,25 @@ func StartDNS(redirectIP, relayServer string, shouldProxy func(string) bool) {
|
||||
domain = domain[:idx] // trim port
|
||||
}
|
||||
|
||||
if shouldProxy(domain) {
|
||||
w.WriteMsg(localA(r, domain, serveIP))
|
||||
if isblock, isproxy := shouldProxy(domain); isblock {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
w.WriteMsg(m)
|
||||
} else if isproxy {
|
||||
host, _, _ := net.SplitHostPort(w.LocalAddr().String())
|
||||
w.WriteMsg(localA(r, domain, net.ParseIP(host)))
|
||||
|
||||
} else if val, ok := cache.Load(domain); ok && val.(*msgCache).After(time.Now()) {
|
||||
msg := val.(*msgCache)
|
||||
for _, rr := range msg.Answer {
|
||||
rr.Header().Ttl = uint32(time.Until(msg.Time).Seconds())
|
||||
}
|
||||
msg.SetReply(r)
|
||||
w.WriteMsg(msg.Msg)
|
||||
|
||||
} else if msg, err := dns.Exchange(r, relayServer); err != nil || msg == nil {
|
||||
server, err := pickRelayAddr(relayServer)
|
||||
if err != nil {
|
||||
cache.Delete(domain)
|
||||
if server, err := pickRelayAddr(relayServer); err != nil {
|
||||
log.Errorw("detect upstream dns", "err", err)
|
||||
} else if relayServer != server {
|
||||
relayServer = server
|
||||
@@ -52,11 +68,21 @@ func StartDNS(redirectIP, relayServer string, shouldProxy func(string) bool) {
|
||||
}
|
||||
|
||||
} else {
|
||||
cache.Delete(domain)
|
||||
if len(msg.Answer) != 0 {
|
||||
deadline := time.Now().Add(
|
||||
time.Duration(msg.Answer[0].Header().Ttl) * time.Second)
|
||||
cache.Store(domain, &msgCache{
|
||||
Msg: msg,
|
||||
Time: deadline,
|
||||
})
|
||||
}
|
||||
|
||||
w.WriteMsg(msg)
|
||||
}
|
||||
})
|
||||
|
||||
server := &dns.Server{Addr: net.JoinHostPort(redirectIP, "53"), Net: "udp"}
|
||||
server := &dns.Server{Addr: ":53", Net: "udp"}
|
||||
log.Infow("start dns", "addr", server.Addr)
|
||||
log.Fatalw("dns serve fail", "err", server.ListenAndServe())
|
||||
}
|
||||
|
||||
+21
-14
@@ -9,19 +9,27 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/wweir/sower/transport"
|
||||
"github.com/wweir/utils/log"
|
||||
"github.com/wweir/sower/util"
|
||||
"github.com/wweir/util-go/log"
|
||||
)
|
||||
|
||||
// StartHTTPProxy start http reverse proxy.
|
||||
// The httputil.ReverseProxy do not supply enough support for https request.
|
||||
func StartHTTPProxy(httpProxyAddr, serverAddr string, password []byte, shouldProxy func(string) bool) {
|
||||
func StartHTTPProxy(httpProxyAddr, serverAddr string, password []byte,
|
||||
shouldProxy func(string) (bool, bool)) {
|
||||
|
||||
proxy := httputil.ReverseProxy{
|
||||
Director: func(r *http.Request) {},
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
addr, _ = withDefaultPort(addr, "80")
|
||||
return transport.Dial(serverAddr, addr, password)
|
||||
}},
|
||||
return transport.Dial(serverAddr, func(host string) (string, []byte) {
|
||||
if _, ok := shouldProxy(host); ok {
|
||||
return httpProxyAddr, password
|
||||
}
|
||||
return "", nil
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
srv := &http.Server{
|
||||
@@ -43,9 +51,7 @@ func StartHTTPProxy(httpProxyAddr, serverAddr string, password []byte, shouldPro
|
||||
}
|
||||
|
||||
func httpsProxy(w http.ResponseWriter, r *http.Request,
|
||||
serverAddr string, password []byte, shouldProxy func(string) bool) {
|
||||
|
||||
target, host := withDefaultPort(r.Host, "443")
|
||||
serverAddr string, password []byte, shouldProxy func(string) (bool, bool)) {
|
||||
|
||||
conn, _, err := w.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
@@ -60,12 +66,13 @@ func httpsProxy(w http.ResponseWriter, r *http.Request,
|
||||
return
|
||||
}
|
||||
|
||||
var rc net.Conn
|
||||
if shouldProxy(host) {
|
||||
rc, err = transport.Dial(serverAddr, target, password)
|
||||
} else {
|
||||
rc, err = net.Dial("tcp", target)
|
||||
}
|
||||
target, _ := util.WithDefaultPort(r.Host, "443")
|
||||
rc, err := transport.Dial(target, func(host string) (string, []byte) {
|
||||
if _, ok := shouldProxy(host); ok {
|
||||
return serverAddr, password
|
||||
}
|
||||
return "", nil
|
||||
})
|
||||
if err != nil {
|
||||
conn.Write([]byte("sower dial " + serverAddr + " fail: " + err.Error()))
|
||||
conn.Close()
|
||||
|
||||
+26
-25
@@ -4,19 +4,19 @@ import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/wweir/sower/transport"
|
||||
"github.com/wweir/utils/log"
|
||||
"github.com/wweir/util-go/log"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
func StartClient(serverAddr, password string, enableDNS bool, forwards map[string]string) {
|
||||
passwordData := []byte(password)
|
||||
func StartClient(serverAddr, password string,
|
||||
forwards map[string]string, shouldProxy func(string) (bool, bool)) {
|
||||
|
||||
passwordData := []byte(password)
|
||||
relayToRemote := func(lnAddr, target string,
|
||||
parseFn func(net.Conn) (net.Conn, string, error)) {
|
||||
parseFn func(net.Conn) (net.Conn, string, error),
|
||||
shouldProxy func(string) (bool, bool)) {
|
||||
|
||||
ln, err := net.Listen("tcp", lnAddr)
|
||||
if err != nil {
|
||||
@@ -40,9 +40,14 @@ func StartClient(serverAddr, password string, enableDNS bool, forwards map[strin
|
||||
}
|
||||
}
|
||||
|
||||
rc, err := transport.Dial(serverAddr, target, passwordData)
|
||||
rc, err := transport.Dial(target, func(domain string) (string, []byte) {
|
||||
if _, ok := shouldProxy(domain); ok {
|
||||
return serverAddr, passwordData
|
||||
}
|
||||
return "", nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Warnw("dial", "addr", serverAddr, "err", err)
|
||||
log.Warnw("dial", "addr", target, "err", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
@@ -53,28 +58,22 @@ func StartClient(serverAddr, password string, enableDNS bool, forwards map[strin
|
||||
}
|
||||
|
||||
for from, to := range forwards {
|
||||
go relayToRemote(from, to, nil)
|
||||
go relayToRemote(from, to, nil, func(string) (bool, bool) { return false, true })
|
||||
}
|
||||
|
||||
if enableDNS {
|
||||
go relayToRemote(":80", "", ParseHTTP)
|
||||
go relayToRemote(":443", "", ParseHTTPS)
|
||||
}
|
||||
go relayToRemote(":http", "", ParseHTTP, shouldProxy)
|
||||
go relayToRemote(":https", "", ParseHTTPS, shouldProxy)
|
||||
|
||||
log.Infow("start sower client", "dns solution", enableDNS, "forwards", forwards)
|
||||
log.Infow("start sower client", "forwards", forwards)
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
func StartServer(relayTarget, password, cacheDir, certFile, keyFile, email string) {
|
||||
dir, _ := os.UserCacheDir()
|
||||
dir = filepath.Join("/", dir, "sower")
|
||||
log.Infow("certificate cache dir", "dir", dir)
|
||||
|
||||
certManager := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
Email: email,
|
||||
Cache: autocert.DirCache(dir),
|
||||
Cache: autocert.DirCache(cacheDir),
|
||||
}
|
||||
|
||||
tlsConf := &tls.Config{
|
||||
@@ -94,22 +93,22 @@ func StartServer(relayTarget, password, cacheDir, certFile, keyFile, email strin
|
||||
// Try to redirect 80 to 443
|
||||
go http.ListenAndServe(":80", certManager.HTTPHandler(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.Scheme = "https"
|
||||
if host, _, err := net.SplitHostPort(r.Host); err != nil {
|
||||
r.URL.Host = r.Host
|
||||
} else {
|
||||
r.URL.Host = host
|
||||
}
|
||||
r.URL.Scheme = "https"
|
||||
|
||||
http.Redirect(w, r, r.URL.String(), 301)
|
||||
})))
|
||||
|
||||
log.Infow("start sower server", "relay_to", relayTarget)
|
||||
ln, err := tls.Listen("tcp", ":443", tlsConf)
|
||||
if err != nil {
|
||||
log.Fatalw("tcp listen", "err", err)
|
||||
}
|
||||
|
||||
log.Infow("start sower server", "relay_to", relayTarget)
|
||||
|
||||
passwordData := []byte(password)
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
@@ -121,9 +120,11 @@ func StartServer(relayTarget, password, cacheDir, certFile, keyFile, email strin
|
||||
go func(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
conn, target := transport.ToProxyConn(conn, passwordData)
|
||||
if target == "" {
|
||||
target = relayTarget
|
||||
target := relayTarget
|
||||
|
||||
conn, t := transport.ParseTrojanConn(conn, passwordData)
|
||||
if t != nil {
|
||||
target = t.Addr()
|
||||
}
|
||||
|
||||
rc, err := net.Dial("tcp", target)
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/wweir/sower/transport"
|
||||
"github.com/wweir/util-go/log"
|
||||
)
|
||||
|
||||
func StartSocks5Proxy(listenAddr, serverAddr string, password []byte) {
|
||||
ln, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
log.Fatalw("socks5 proxy", "addr", listenAddr, "err", err)
|
||||
}
|
||||
|
||||
log.Infow("start socks5 proxy", "endpoint", listenAddr)
|
||||
serveSocks5(ln, serverAddr, password)
|
||||
}
|
||||
|
||||
func serveSocks5(ln net.Listener, serverAddr string, password []byte) {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
log.Fatalw("socks5 proxy", "err", err)
|
||||
}
|
||||
go serveSocks5(ln, serverAddr, password)
|
||||
|
||||
tgtAddr, err := transport.ParseSocks5(conn)
|
||||
if err != nil {
|
||||
log.Errorw("socks5 proxy", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := transport.Dial(tgtAddr, func(host string) (string, []byte) {
|
||||
return serverAddr, password
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorw("socks5 proxy", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
relay(conn, rc)
|
||||
conn.Close()
|
||||
rc.Close()
|
||||
}
|
||||
+3
-13
@@ -22,10 +22,7 @@ func ParseHTTP(conn net.Conn) (net.Conn, string, error) {
|
||||
return teeConn, "", err
|
||||
}
|
||||
|
||||
if _, _, err := net.SplitHostPort(resp.Host); err != nil {
|
||||
resp.Host = net.JoinHostPort(resp.Host, "80")
|
||||
}
|
||||
|
||||
resp.Host, _ = util.WithDefaultPort(resp.Host, "80")
|
||||
return teeConn, resp.Host, nil
|
||||
}
|
||||
|
||||
@@ -41,15 +38,8 @@ func ParseHTTPS(conn net.Conn) (net.Conn, string, error) {
|
||||
},
|
||||
}).Handshake()
|
||||
|
||||
return teeConn, net.JoinHostPort(domain, "443"), nil
|
||||
}
|
||||
|
||||
func withDefaultPort(addr string, port string) (address, host string) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return net.JoinHostPort(addr, port), addr
|
||||
}
|
||||
return addr, host
|
||||
domain, _ = util.WithDefaultPort(domain, "443")
|
||||
return teeConn, domain, nil
|
||||
}
|
||||
|
||||
func relay(conn1, conn2 net.Conn) {
|
||||
|
||||
+5
-11
@@ -19,19 +19,14 @@ const (
|
||||
)
|
||||
|
||||
// Ping try connect to a http(s) server with domain though the http addr
|
||||
func (p Port) Ping(domain string, timeout time.Duration) error {
|
||||
conn, err := net.DialTimeout("tcp", net.JoinHostPort(domain, p.String()), timeout)
|
||||
func (p Port) Ping(domain string, dial func(string) (net.Conn, error)) error {
|
||||
conn, err := dial(net.JoinHostPort(domain, p.String()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
return p.PingWithConn(domain, conn, timeout)
|
||||
}
|
||||
|
||||
// PingWithConn try connect to a http(s) server with domain though the http addr
|
||||
func (p Port) PingWithConn(domain string, conn net.Conn, timeout time.Duration) error {
|
||||
conn.SetDeadline(time.Now().Add(timeout))
|
||||
conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||||
if _, err := conn.Write(p.PingMsg(domain)); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -39,9 +34,8 @@ func (p Port) PingWithConn(domain string, conn net.Conn, timeout time.Duration)
|
||||
// err -> nil: read something succ
|
||||
// err -> io.EOF: no such domain or connection refused
|
||||
// err -> timeout: tcp package has been dropped
|
||||
_, err := conn.Read(make([]byte, 1))
|
||||
if err == nil || err == io.EOF {
|
||||
return nil
|
||||
if _, err = conn.Read(make([]byte, 10)); err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// +build debug
|
||||
// go test -v -tags debug
|
||||
|
||||
package router_test
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/wweir/sower/router"
|
||||
"github.com/wweir/sower/transport"
|
||||
)
|
||||
|
||||
var (
|
||||
proxyAddr string = "socks5://127.0.0.1:1080"
|
||||
password []byte = nil
|
||||
)
|
||||
|
||||
func TestPort_Ping(t *testing.T) {
|
||||
direct := func(addr string) (net.Conn, error) {
|
||||
return net.DialTimeout("tcp", addr, 5*time.Second)
|
||||
}
|
||||
proxy := func(addr string) (net.Conn, error) {
|
||||
conn, err := tls.Dial("tcp", proxyAddr, &tls.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transport.ToTrojanConn(conn, host, uint16(p), password)
|
||||
}
|
||||
|
||||
type args struct {
|
||||
domain string
|
||||
dial func(string) (net.Conn, error)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
p router.Port
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{"", router.HTTP, args{"baidu.com", direct}, false},
|
||||
{"", router.HTTP, args{"baidu.com", proxy}, false},
|
||||
{"", router.HTTPS, args{"baidu.com", direct}, false},
|
||||
{"", router.HTTPS, args{"baidu.com", proxy}, false},
|
||||
{"", router.HTTP, args{"google.com", direct}, true},
|
||||
{"", router.HTTP, args{"google.com", proxy}, false},
|
||||
{"", router.HTTPS, args{"google.com", direct}, true},
|
||||
{"", router.HTTPS, args{"google.com", proxy}, false},
|
||||
{"", router.HTTP, args{"smtp.163.com", direct}, true},
|
||||
{"", router.HTTP, args{"smtp.163.com", proxy}, true},
|
||||
{"", router.HTTPS, args{"smtp.163.com", direct}, true},
|
||||
{"", router.HTTPS, args{"smtp.163.com", proxy}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := tt.p.Ping(tt.args.domain, tt.args.dial); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Port.Ping() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+83
-81
@@ -9,71 +9,76 @@ import (
|
||||
|
||||
"github.com/wweir/sower/transport"
|
||||
"github.com/wweir/sower/util"
|
||||
"github.com/wweir/utils/log"
|
||||
"github.com/wweir/utils/mem"
|
||||
"github.com/wweir/util-go/log"
|
||||
"github.com/wweir/util-go/mem"
|
||||
)
|
||||
|
||||
// Route implement a router for each request
|
||||
type Route struct {
|
||||
port Port
|
||||
once sync.Once
|
||||
cache *mem.Cache
|
||||
|
||||
ProxyAddress string
|
||||
ProxyPassword string
|
||||
password []byte
|
||||
ProxyAddress string
|
||||
password []byte
|
||||
|
||||
DetectLevel int // dynamic detect proxy level
|
||||
DetectTimeout string // dynamic detect timeout
|
||||
timeout time.Duration
|
||||
|
||||
DirectList []string
|
||||
directRule *util.Node
|
||||
ProxyList []string
|
||||
DetectLevel int // dynamic detect proxy level
|
||||
blockRule *util.Node
|
||||
proxyRule *util.Node
|
||||
DynamicList []string
|
||||
dynamicRule *util.Node
|
||||
directRule *util.Node
|
||||
PersistFn func(string)
|
||||
}
|
||||
|
||||
func NewRoute(address, password string, detectLevel int,
|
||||
blocklist, proxylist, directlist []string, persist func(string)) *Route {
|
||||
return &Route{
|
||||
cache: mem.New(4 * time.Hour),
|
||||
ProxyAddress: address,
|
||||
password: []byte(password),
|
||||
|
||||
DetectLevel: detectLevel,
|
||||
blockRule: util.NewNodeFromRules(blocklist...),
|
||||
proxyRule: util.NewNodeFromRules(proxylist...),
|
||||
directRule: util.NewNodeFromRules(directlist...),
|
||||
PersistFn: persist,
|
||||
}
|
||||
}
|
||||
|
||||
// ShouldProxy check if the domain shoule request though proxy
|
||||
func (r *Route) ShouldProxy(domain string) bool {
|
||||
r.once.Do(func() {
|
||||
r.cache = mem.New(4 * time.Hour)
|
||||
r.password = []byte(r.ProxyPassword)
|
||||
r.directRule = util.NewNodeFromRules(r.DirectList...)
|
||||
r.proxyRule = util.NewNodeFromRules(r.ProxyList...)
|
||||
r.dynamicRule = util.NewNodeFromRules(r.DynamicList...)
|
||||
|
||||
if timeout, err := time.ParseDuration(r.DetectTimeout); err != nil {
|
||||
r.timeout = 300 * time.Millisecond
|
||||
log.Warnw("parse detect timeout", "err", err, "default", r.timeout)
|
||||
} else {
|
||||
r.timeout = timeout
|
||||
func (r *Route) GenProxyCheck(sync bool) func(string) (bool, bool) {
|
||||
detect := func(domain string) bool {
|
||||
go r.cache.Remember(r, domain)
|
||||
return true
|
||||
}
|
||||
if sync {
|
||||
detect = func(domain string) bool {
|
||||
r.cache.Remember(r, domain)
|
||||
if r.proxyRule.Match(domain) {
|
||||
return true
|
||||
}
|
||||
return !r.directRule.Match(domain)
|
||||
}
|
||||
})
|
||||
|
||||
// break deadlook, for wildcard
|
||||
if strings.Count(domain, ".") > 4 {
|
||||
return false
|
||||
}
|
||||
domain = strings.TrimSuffix(domain, ".")
|
||||
|
||||
if domain == r.ProxyAddress {
|
||||
return false
|
||||
}
|
||||
if r.directRule.Match(domain) {
|
||||
return false
|
||||
}
|
||||
if r.proxyRule.Match(domain) {
|
||||
return true
|
||||
}
|
||||
if r.dynamicRule.Match(domain) {
|
||||
return true
|
||||
}
|
||||
|
||||
r.cache.Remember(r, domain)
|
||||
return r.dynamicRule.Match(domain)
|
||||
return func(domain string) (bool, bool) {
|
||||
domain = strings.TrimSuffix(domain, ".")
|
||||
// break deadlook, for wildcard
|
||||
if sepCount := strings.Count(domain, "."); sepCount == 0 || sepCount >= 5 {
|
||||
return false, false
|
||||
}
|
||||
|
||||
if r.blockRule.Match(domain) {
|
||||
return true, false
|
||||
}
|
||||
|
||||
if r.proxyRule.Match(domain) {
|
||||
return false, true
|
||||
}
|
||||
if r.directRule.Match(domain) {
|
||||
return false, false
|
||||
}
|
||||
|
||||
return false, detect(domain)
|
||||
}
|
||||
}
|
||||
|
||||
// Get implement for cache
|
||||
@@ -84,7 +89,7 @@ func (r *Route) Get(key interface{}) (err error) {
|
||||
log.Infow("detect", "domain", domain, "http", httpScore, "https", httpsScore)
|
||||
|
||||
if httpScore+httpsScore >= r.DetectLevel {
|
||||
r.dynamicRule.Add(domain)
|
||||
r.directRule.Add(domain)
|
||||
if r.PersistFn != nil {
|
||||
r.PersistFn(domain)
|
||||
}
|
||||
@@ -96,54 +101,51 @@ func (r *Route) Get(key interface{}) (err error) {
|
||||
func (r *Route) detect(domain string) (http, https int) {
|
||||
wg := sync.WaitGroup{}
|
||||
httpScore, httpsScore := new(int32), new(int32)
|
||||
for _, ping := range [...]*Route{{port: HTTP}, {port: HTTPS}} {
|
||||
for _, ping := range [...]struct {
|
||||
shouldProxy bool
|
||||
port Port
|
||||
}{
|
||||
{shouldProxy: true, port: HTTP},
|
||||
{shouldProxy: true, port: HTTPS},
|
||||
{shouldProxy: false, port: HTTP},
|
||||
{shouldProxy: false, port: HTTPS},
|
||||
} {
|
||||
wg.Add(1)
|
||||
go func(ping *Route) {
|
||||
go func(shouldProxy bool, port Port) {
|
||||
defer wg.Done()
|
||||
|
||||
if err := ping.port.Ping(domain, r.timeout); err != nil {
|
||||
if err := port.Ping(domain, func(domain string) (net.Conn, error) {
|
||||
return transport.Dial(domain,
|
||||
func(domain string) (proxyAddr string, password []byte) {
|
||||
if shouldProxy {
|
||||
return r.ProxyAddress, r.password
|
||||
}
|
||||
return "", nil
|
||||
})
|
||||
}); err != nil {
|
||||
log.Warnw("sower dial", "proxy", shouldProxy, "host", domain, "port", port, "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
switch ping.port {
|
||||
case HTTP:
|
||||
switch {
|
||||
case shouldProxy && port == HTTP:
|
||||
if !atomic.CompareAndSwapInt32(httpScore, 0, -2) {
|
||||
atomic.AddInt32(httpScore, -1)
|
||||
}
|
||||
case HTTPS:
|
||||
case shouldProxy && port == HTTPS:
|
||||
if !atomic.CompareAndSwapInt32(httpsScore, 0, -2) {
|
||||
atomic.AddInt32(httpScore, -1)
|
||||
atomic.AddInt32(httpsScore, -1)
|
||||
}
|
||||
}
|
||||
}(ping)
|
||||
}
|
||||
for _, ping := range [...]*Route{{port: HTTP}, {port: HTTPS}} {
|
||||
wg.Add(1)
|
||||
go func(ping *Route) {
|
||||
defer wg.Done()
|
||||
|
||||
target := net.JoinHostPort(domain, ping.port.String())
|
||||
conn, err := transport.Dial(r.ProxyAddress, target, r.password)
|
||||
if err != nil {
|
||||
log.Errorw("sower dial", "addr", r.ProxyAddress, "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ping.port.PingWithConn(domain, conn, r.timeout); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch ping.port {
|
||||
case HTTP:
|
||||
case !shouldProxy && port == HTTP:
|
||||
if !atomic.CompareAndSwapInt32(httpScore, 0, 2) {
|
||||
atomic.AddInt32(httpScore, 1)
|
||||
}
|
||||
case HTTPS:
|
||||
case !shouldProxy && port == HTTPS:
|
||||
if !atomic.CompareAndSwapInt32(httpsScore, 0, 2) {
|
||||
atomic.AddInt32(httpScore, 1)
|
||||
atomic.AddInt32(httpsScore, 1)
|
||||
}
|
||||
}
|
||||
}(ping)
|
||||
}(ping.shouldProxy, ping.port)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
+5
-10
@@ -2,7 +2,6 @@ package transport
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
@@ -12,14 +11,14 @@ import (
|
||||
)
|
||||
|
||||
// checksum(>=0x80) + port + target_length + target + data
|
||||
// data(HTTP/HTTPS, first byte < 0x7F)
|
||||
// data(HTTP, first byte < 0x7F)
|
||||
type head struct {
|
||||
Checksum byte
|
||||
Port uint16
|
||||
AddrLen uint8
|
||||
}
|
||||
|
||||
func ToProxyConn(conn net.Conn, password []byte) (net.Conn, string) {
|
||||
func ParseProxyConn(conn net.Conn, password []byte) (net.Conn, string) {
|
||||
teeConn := &util.TeeConn{Conn: conn}
|
||||
defer teeConn.Stop()
|
||||
|
||||
@@ -41,12 +40,7 @@ func ToProxyConn(conn net.Conn, password []byte) (net.Conn, string) {
|
||||
return teeConn, net.JoinHostPort(string(buf), strconv.Itoa(int(h.Port)))
|
||||
}
|
||||
|
||||
func DialTlsProxyConn(address, tgtHost string, tgtPort uint16, tlsCfg *tls.Config, password []byte) (net.Conn, error) {
|
||||
conn, err := tls.Dial("tcp", address, tlsCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func ToProxyConn(conn net.Conn, tgtHost string, tgtPort uint16, password []byte) (net.Conn, error) {
|
||||
h := &head{
|
||||
Checksum: sumChecksum([]byte(tgtHost), password),
|
||||
Port: tgtPort,
|
||||
@@ -57,7 +51,8 @@ func DialTlsProxyConn(address, tgtHost string, tgtPort uint16, tlsCfg *tls.Confi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := []byte(tgtHost)
|
||||
var data = []byte(tgtHost)
|
||||
var err error
|
||||
for n, nn := 0, 0; nn < len(data); nn += n {
|
||||
if n, err = conn.Write(data[nn:]); err != nil {
|
||||
conn.Close()
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func IsSocks5Schema(addr string) (string, bool) {
|
||||
if strings.HasPrefix(addr, "socks5://") {
|
||||
return strings.TrimPrefix(addr, "socks5://"), true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(addr, "socks5h://") {
|
||||
return strings.TrimPrefix(addr, "socks5h://"), true
|
||||
}
|
||||
|
||||
return addr, false
|
||||
}
|
||||
|
||||
func ToSocks5(c net.Conn, address string) (net.Conn, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &conn{
|
||||
init: make(chan struct{}),
|
||||
Conn: c,
|
||||
domain: host,
|
||||
port: []byte{byte(p >> 8), byte(p)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
init chan struct{}
|
||||
domain string
|
||||
port []byte
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *conn) Read(b []byte) (n int, err error) {
|
||||
<-c.init
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *conn) Write(b []byte) (n int, err error) {
|
||||
select {
|
||||
case <-c.init:
|
||||
return c.Conn.Write(b)
|
||||
default:
|
||||
}
|
||||
|
||||
{
|
||||
req := &authReq{
|
||||
VER: 5,
|
||||
NMETHODS: 1,
|
||||
METHODS: [1]byte{0}, // NO AUTHENTICATION REQUIRED
|
||||
}
|
||||
if err := binary.Write(c.Conn, binary.BigEndian, req); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
{
|
||||
resp := &authResp{}
|
||||
if err := binary.Read(c.Conn, binary.BigEndian, resp); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
{
|
||||
req := &request{
|
||||
req: req{
|
||||
VER: 5, // socks5
|
||||
CMD: 1, // CONNECT
|
||||
RSV: 0, // RESERVED
|
||||
ATYP: 3, // DOMAINNAME
|
||||
},
|
||||
DST_ADDR: append([]byte{byte(len(c.domain))}, []byte(c.domain)...),
|
||||
DST_PORT: c.port,
|
||||
}
|
||||
|
||||
if _, err := c.Conn.Write(req.Bytes()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
{
|
||||
resp := &response{}
|
||||
if err := binary.Read(c.Conn, binary.BigEndian, &(resp.resp)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch resp.REP {
|
||||
case 0x00:
|
||||
default:
|
||||
return 0, fmt.Errorf("socks5 handshake fail, return code: %d", resp.REP)
|
||||
}
|
||||
|
||||
switch resp.ATYP {
|
||||
case 0x01: // IPv4
|
||||
resp.DST_ADDR = make([]byte, net.IPv4len)
|
||||
if _, err := io.ReadFull(c.Conn, resp.DST_ADDR); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
case 0x03: // domain name
|
||||
if _, err := io.ReadFull(c.Conn, resp.DST_ADDR[:1]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err := io.ReadFull(c.Conn, resp.DST_ADDR[1:1+int(resp.DST_ADDR[0])]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
case 0x04:
|
||||
resp.DST_ADDR = make([]byte, net.IPv6len)
|
||||
if _, err := io.ReadFull(c.Conn, resp.DST_ADDR); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
resp.DST_PORT = make([]byte, 2)
|
||||
if _, err := io.ReadFull(c.Conn, resp.DST_PORT); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
close(c.init)
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func IsSocks5Schema(addr string) (string, bool) {
|
||||
if strings.HasPrefix(addr, "socks5://") {
|
||||
return strings.TrimPrefix(addr, "socks5://"), true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(addr, "socks5h://") {
|
||||
return strings.TrimPrefix(addr, "socks5h://"), true
|
||||
}
|
||||
|
||||
return addr, false
|
||||
}
|
||||
|
||||
func ToSocks5(c net.Conn, host string, port uint16) (net.Conn, error) {
|
||||
return &conn{
|
||||
init: make(chan struct{}),
|
||||
Conn: c,
|
||||
domain: host,
|
||||
port: port,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
init chan struct{}
|
||||
domain string
|
||||
port uint16
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *conn) Read(b []byte) (n int, err error) {
|
||||
select {
|
||||
case <-c.init:
|
||||
return c.Conn.Read(b)
|
||||
default:
|
||||
return 0, c.clientHandshake()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *conn) Write(b []byte) (n int, err error) {
|
||||
select {
|
||||
case <-c.init:
|
||||
return c.Conn.Write(b)
|
||||
default:
|
||||
return 0, c.clientHandshake()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *conn) clientHandshake() error {
|
||||
{
|
||||
req := &authReq{
|
||||
VER: 5,
|
||||
NMETHODS: 1,
|
||||
METHODS: [1]byte{0}, // NO AUTHENTICATION REQUIRED
|
||||
}
|
||||
if err := binary.Write(c.Conn, binary.BigEndian, req); err != nil {
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
}
|
||||
{
|
||||
resp := &authResp{}
|
||||
if err := binary.Read(c.Conn, binary.BigEndian, resp); err != nil {
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
}
|
||||
{
|
||||
reqHead := requestHead{
|
||||
VER: 5, // socks5
|
||||
CMD: 1, // CONNECT
|
||||
RSV: 0, // RESERVED
|
||||
ATYP: 3, // DOMAINNAME
|
||||
}
|
||||
|
||||
if err := binary.Write(c.Conn, binary.BigEndian, reqHead); err != nil {
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
|
||||
buf := make([]byte, 0, 1 /*LEN*/ +len(c.domain)+2 /*PORT*/)
|
||||
buf = append(buf, byte(len(c.domain)))
|
||||
buf = append(buf, []byte(c.domain)...)
|
||||
buf = append(buf, byte(c.port>>8), byte(c.port))
|
||||
if _, err := c.Conn.Write(buf); err != nil {
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
}
|
||||
{
|
||||
head := replyHead{}
|
||||
if err := binary.Read(c.Conn, binary.BigEndian, &head); err != nil {
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
|
||||
switch head.REP {
|
||||
case 0x00:
|
||||
default:
|
||||
return xerrors.Errorf("socks5 handshake fail, return code: %d", head.REP)
|
||||
}
|
||||
|
||||
var bindAddr addrType
|
||||
switch head.ATYP {
|
||||
case 0x01: // IPv4
|
||||
bindAddr = addrTypeIPv4{}
|
||||
case 0x03: // domain name
|
||||
bindAddr = addrTypeDomain{}
|
||||
case 0x04: // IPv6
|
||||
bindAddr = addrTypeIPv6{}
|
||||
default:
|
||||
return xerrors.New("invalid connect type")
|
||||
}
|
||||
if err := bindAddr.Fullfill(c.Conn); err != nil {
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
close(c.init)
|
||||
return nil
|
||||
}
|
||||
+89
-18
@@ -1,44 +1,115 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// https://tools.ietf.org/html/rfc1928
|
||||
|
||||
// 1. client send auth request
|
||||
type authReq struct {
|
||||
VER byte
|
||||
NMETHODS byte
|
||||
METHODS [1]byte // 1 to 255, fix to no authentication
|
||||
}
|
||||
|
||||
// 2. server response auth request
|
||||
type authResp struct {
|
||||
VER byte
|
||||
METHOD byte
|
||||
}
|
||||
|
||||
type request struct {
|
||||
req
|
||||
DST_ADDR []byte // first byte is length
|
||||
DST_PORT []byte // two bytes
|
||||
}
|
||||
type req struct {
|
||||
// 3. client request with target address
|
||||
type requestHead struct {
|
||||
VER byte
|
||||
CMD byte
|
||||
RSV byte
|
||||
ATYP byte
|
||||
}
|
||||
|
||||
func (r *request) Bytes() []byte {
|
||||
out := []byte{r.VER, r.CMD, r.RSV, r.ATYP}
|
||||
out = append(out, r.DST_ADDR...)
|
||||
return append(out, r.DST_PORT...)
|
||||
}
|
||||
|
||||
type response struct {
|
||||
resp
|
||||
DST_ADDR []byte // first byte is length
|
||||
DST_PORT []byte // two bytes
|
||||
}
|
||||
type resp struct {
|
||||
// 4. server response with the address that assigned to connect to target address
|
||||
type replyHead struct {
|
||||
VER byte
|
||||
REP byte
|
||||
RSV byte
|
||||
ATYP byte
|
||||
}
|
||||
|
||||
type addrType interface {
|
||||
Fullfill(r io.Reader) error
|
||||
String() string
|
||||
}
|
||||
|
||||
// ATYP:
|
||||
// 0x01 -> net.IPv4len
|
||||
// 0x03 -> first byte is length
|
||||
// 0x04 -> net.IPv6len
|
||||
type addrTypeIPv4 struct {
|
||||
DST_ADDR [4]byte
|
||||
DST_PORT uint16
|
||||
}
|
||||
|
||||
func (a addrTypeIPv4) Fullfill(r io.Reader) error {
|
||||
return binary.Read(r, binary.BigEndian, &a)
|
||||
}
|
||||
func (a addrTypeIPv4) String() string {
|
||||
return net.JoinHostPort(
|
||||
net.IP(a.DST_ADDR[:]).String(),
|
||||
strconv.FormatUint(uint64(a.DST_PORT), 10),
|
||||
)
|
||||
}
|
||||
|
||||
type addrTypeDomain struct {
|
||||
DST_ADDR_LEN uint8
|
||||
DST_ADDR []byte
|
||||
DST_PORT uint16
|
||||
}
|
||||
|
||||
func (a addrTypeDomain) Fullfill(r io.Reader) error {
|
||||
buf := make([]byte, 256)
|
||||
// domain length
|
||||
if _, err := io.ReadFull(r, buf[:1]); err != nil {
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
a.DST_ADDR_LEN = uint8(buf[0])
|
||||
|
||||
// domain
|
||||
if _, err := io.ReadFull(r, buf[:int(buf[0])]); err != nil {
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
a.DST_ADDR = buf[:int(buf[0])]
|
||||
|
||||
// port
|
||||
if _, err := io.ReadFull(r, buf[:2]); err != nil {
|
||||
return xerrors.New(err.Error())
|
||||
}
|
||||
a.DST_PORT = binary.BigEndian.Uint16(buf[:2])
|
||||
|
||||
return nil
|
||||
}
|
||||
func (a addrTypeDomain) String() string {
|
||||
return net.JoinHostPort(
|
||||
net.IP(a.DST_ADDR[:]).String(),
|
||||
strconv.FormatUint(uint64(a.DST_PORT), 10),
|
||||
)
|
||||
}
|
||||
|
||||
type addrTypeIPv6 struct {
|
||||
DST_ADDR [16]byte
|
||||
DST_PORT uint16
|
||||
}
|
||||
|
||||
func (a addrTypeIPv6) Fullfill(r io.Reader) error {
|
||||
return binary.Read(r, binary.BigEndian, &a)
|
||||
}
|
||||
func (a addrTypeIPv6) String() string {
|
||||
return net.JoinHostPort(
|
||||
net.IP(a.DST_ADDR[:]).String(),
|
||||
strconv.FormatUint(uint64(a.DST_PORT), 10),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func ParseSocks5(conn net.Conn) (tgtaddr string, err error) {
|
||||
{
|
||||
authReq := new(authReq)
|
||||
if err := binary.Read(conn, binary.BigEndian, authReq); err != nil {
|
||||
return "", xerrors.New(err.Error())
|
||||
}
|
||||
|
||||
if authReq.VER != 5 || // socks5
|
||||
authReq.NMETHODS != 1 ||
|
||||
authReq.METHODS[0] != 0 { // NO_AUTH
|
||||
return "", xerrors.New("invalid socks5 auth method")
|
||||
}
|
||||
}
|
||||
{
|
||||
if err := binary.Write(conn, binary.BigEndian, &authResp{
|
||||
VER: 5,
|
||||
METHOD: 1,
|
||||
}); err != nil {
|
||||
return "", xerrors.New(err.Error())
|
||||
}
|
||||
}
|
||||
{
|
||||
head := &requestHead{}
|
||||
if err := binary.Read(conn, binary.BigEndian, head); err != nil {
|
||||
return "", xerrors.New(err.Error())
|
||||
}
|
||||
if head.VER != 5 ||
|
||||
head.CMD != 1 {
|
||||
return "", xerrors.New("invalid socks5 connect request")
|
||||
}
|
||||
|
||||
var addr addrType
|
||||
switch head.ATYP {
|
||||
case 0x01: // IPv4
|
||||
addr = addrTypeIPv4{}
|
||||
case 0x03: // domain name
|
||||
addr = addrTypeDomain{}
|
||||
case 0x04: // IPv6
|
||||
addr = addrTypeIPv6{}
|
||||
default:
|
||||
return "", xerrors.New("invalid connect type")
|
||||
}
|
||||
if err := addr.Fullfill(conn); err != nil {
|
||||
return "", xerrors.New(err.Error())
|
||||
}
|
||||
tgtaddr = addr.String()
|
||||
}
|
||||
{
|
||||
if err := binary.Write(conn, binary.BigEndian, &replyHead{
|
||||
VER: 5,
|
||||
REP: 0,
|
||||
RSV: 0,
|
||||
ATYP: 1,
|
||||
}); err != nil {
|
||||
return "", xerrors.New(err.Error())
|
||||
}
|
||||
|
||||
// FIXME: return the real address
|
||||
if _, err := conn.Write(make([]byte, 6)); err != nil {
|
||||
return "", xerrors.New(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return tgtaddr, nil
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/wweir/sower/util"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// +-----------------------+---------+----------------+---------+----------+
|
||||
// | hex(SHA224(password)) | CRLF | Trojan Request | CRLF | Payload |
|
||||
// +-----------------------+---------+----------------+---------+----------+
|
||||
// | 56 | X'0D0A' | Variable | X'0D0A' | Variable |
|
||||
// +-----------------------+---------+----------------+---------+----------+
|
||||
// +-----+------+----------+----------+
|
||||
// | CMD | ATYP | DST.ADDR | DST.PORT |
|
||||
// +-----+------+----------+----------+
|
||||
// | 1 | 1 | Variable | 2 |
|
||||
// +-----+------+----------+----------+
|
||||
// o CMD
|
||||
// o CONNECT X'01'
|
||||
// o UDP :X'03'
|
||||
// o ATYP
|
||||
// o IP V4 : X'01'
|
||||
// o domain: X'03'
|
||||
// o IP V6 : X'04'
|
||||
|
||||
type Trojan struct {
|
||||
Password [56]byte
|
||||
TrojanRequest
|
||||
}
|
||||
type TrojanRequest struct {
|
||||
CMD uint8
|
||||
ATYP uint8
|
||||
DstAddr []byte
|
||||
DstPort uint16
|
||||
}
|
||||
|
||||
func (t *TrojanRequest) Addr() string {
|
||||
switch t.ATYP {
|
||||
case 0x01:
|
||||
fallthrough
|
||||
case 0x04:
|
||||
return net.JoinHostPort(net.IP(t.DstAddr).String(), strconv.Itoa(int(t.DstPort)))
|
||||
case 0x03:
|
||||
return string(t.DstAddr) + ":" + strconv.Itoa(int(t.DstPort))
|
||||
default:
|
||||
panic("invalid ATYP")
|
||||
}
|
||||
}
|
||||
|
||||
func ParseTrojanConn(conn net.Conn, password []byte) (net.Conn, *Trojan) {
|
||||
passData := sha256.Sum224(password)
|
||||
passHead := hex.EncodeToString(passData[:])
|
||||
|
||||
teeConn := &util.TeeConn{Conn: conn}
|
||||
defer teeConn.Stop()
|
||||
|
||||
buf := make([]byte, 56+2+1+1+1)
|
||||
if _, err := io.ReadFull(teeConn, buf); err != nil {
|
||||
return teeConn, nil
|
||||
}
|
||||
if string(buf[:56]) != passHead {
|
||||
return teeConn, nil
|
||||
}
|
||||
|
||||
t := Trojan{}
|
||||
t.CMD, t.ATYP = buf[58], buf[59]
|
||||
addrLen := buf[60]
|
||||
switch t.ATYP {
|
||||
case 0x01: //ipv4
|
||||
buf = make([]byte, net.IPv4len+2+2)
|
||||
buf[0] = addrLen
|
||||
if _, err := io.ReadFull(teeConn, buf[1:]); err != nil {
|
||||
return teeConn, nil
|
||||
}
|
||||
|
||||
case 0x04: //ipv6
|
||||
buf = make([]byte, net.IPv6len+2+2)
|
||||
buf[0] = addrLen
|
||||
if _, err := io.ReadFull(teeConn, buf[1:]); err != nil {
|
||||
return teeConn, nil
|
||||
}
|
||||
|
||||
case 0x03: // domain
|
||||
buf = make([]byte, addrLen+2+2)
|
||||
buf[0] = addrLen
|
||||
if _, err := io.ReadFull(teeConn, buf); err != nil {
|
||||
return teeConn, nil
|
||||
}
|
||||
default:
|
||||
return teeConn, nil
|
||||
}
|
||||
|
||||
t.DstAddr = buf[:len(buf)-4]
|
||||
t.DstPort = uint16(buf[len(buf)-4])<<8 + uint16(buf[len(buf)-3])
|
||||
|
||||
teeConn.Reset()
|
||||
return teeConn, &t
|
||||
}
|
||||
|
||||
func ToTrojanConn(conn net.Conn, tgtHost string, tgtPort uint16, password []byte) (net.Conn, error) {
|
||||
passData := sha256.Sum224(password)
|
||||
passHead := hex.EncodeToString(passData[:])
|
||||
|
||||
buf := make([]byte, 0, 56+2+1+1+1+len(tgtHost)+2+2)
|
||||
buf = append(buf, []byte(passHead)...)
|
||||
buf = append(buf, '\r', '\n')
|
||||
buf = append(buf, 1, 3, uint8(len(tgtHost)))
|
||||
buf = append(buf, []byte(tgtHost)...)
|
||||
buf = append(buf, byte(tgtPort>>8), byte(tgtPort))
|
||||
buf = append(buf, '\r', '\n')
|
||||
|
||||
if n, err := conn.Write(buf); err != nil || n != len(buf) {
|
||||
return conn, xerrors.Errorf("n: %d, msg: %s", n, err)
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
+66
-11
@@ -1,34 +1,89 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/wweir/sower/dhcp"
|
||||
"github.com/wweir/util-go/log"
|
||||
)
|
||||
|
||||
func Dial(address, target string, password []byte) (net.Conn, error) {
|
||||
var (
|
||||
persistDNS string
|
||||
dnsAddr string
|
||||
resolver = &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return (&net.Dialer{}).DialContext(ctx, network, dnsAddr)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func SetDNS(err error, dnsIP string) {
|
||||
if dnsIP != "" {
|
||||
persistDNS = dnsIP
|
||||
dnsAddr = net.JoinHostPort(dnsIP, "53")
|
||||
return
|
||||
} else if persistDNS != "" {
|
||||
return
|
||||
}
|
||||
|
||||
if e, ok := err.(*net.DNSError); !ok /*nil*/ || !e.IsNotFound {
|
||||
if dnsIP, err = dhcp.GetDefaultDNSServer(); err != nil {
|
||||
dnsIP, err = dhcp.GetDefaultDNSServer() // retry
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorw("get dns via dhcp", "err", err, "current_dns", dnsAddr)
|
||||
} else {
|
||||
dnsAddr = net.JoinHostPort(dnsIP, "53")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dial dial targetAddr with possiable proxy address
|
||||
func Dial(targetAddr string, dialAddr func(domain string) (proxyAddr string, password []byte)) (net.Conn, error) {
|
||||
host, port, err := net.SplitHostPort(targetAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
address, password := dialAddr(host)
|
||||
if address == "" {
|
||||
ips, err := resolver.LookupIPAddr(context.Background(), host)
|
||||
if err != nil { //retry
|
||||
ips, err = resolver.LookupIPAddr(context.Background(), host)
|
||||
}
|
||||
if err != nil {
|
||||
SetDNS(err, "")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return net.Dial("tcp", net.JoinHostPort(ips[0].String(), port))
|
||||
}
|
||||
|
||||
p, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if addr, ok := IsSocks5Schema(address); ok {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if conn, err = ToSocks5(conn, target); err != nil {
|
||||
if conn, err = ToSocks5(conn, host, uint16(p)); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
port = "443"
|
||||
}
|
||||
p, err := strconv.Atoi(port)
|
||||
conn, err := tls.DialWithDialer(&net.Dialer{Resolver: resolver},
|
||||
"tcp", net.JoinHostPort(address, "443"), &tls.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// tls.Config is same as golang http pkg default behavior
|
||||
return DialTlsProxyConn(net.JoinHostPort(address, "443"),
|
||||
host, uint16(p), &tls.Config{}, password)
|
||||
return ToTrojanConn(conn, host, uint16(p), password)
|
||||
}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ func (n *node) add(secs []string) {
|
||||
case 0:
|
||||
return
|
||||
case 1:
|
||||
n.node[secs[length-1]] = &node{node: map[string]*node{"": &node{}}}
|
||||
n.node[secs[length-1]] = &node{node: map[string]*node{"": {}}}
|
||||
default:
|
||||
sec := secs[length-1]
|
||||
if sec == "**" {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package util
|
||||
|
||||
import "net"
|
||||
|
||||
func WithDefaultPort(addr string, port string) (address, host string) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return net.JoinHostPort(addr, port), addr
|
||||
}
|
||||
return addr, host
|
||||
}
|
||||
Reference in New Issue
Block a user