refactor: move third_party code to separate dir
parent
0d5db0bf08
commit
fc191cb14e
16
go.mod
16
go.mod
|
|
@ -1,9 +1,19 @@
|
|||
module github.com/zhsj/wghttp
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/jessevdk/go-flags v1.5.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211025155311-828a885a7140
|
||||
golang.zx2c4.com/wireguard/tun/netstack v0.0.0-20211025155311-828a885a7140
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211116201604-de7c702ace45
|
||||
golang.zx2c4.com/wireguard/tun/netstack v0.0.0-20211116201604-de7c702ace45
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 // indirect
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20211020211948-f76a604701b6 // indirect
|
||||
)
|
||||
|
|
|
|||
18
go.sum
18
go.sum
|
|
@ -642,8 +642,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI=
|
||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 h1:VrJZAjbekhoRn7n5FBujY31gboH+iB3pdLxn3gE9FjU=
|
||||
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
|
@ -736,8 +736,8 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985 h1:LOlKVhfDyahgmqa97awczplwkjzNaELFg3zRIJ13RYo=
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
@ -812,11 +812,13 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
|
||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210424170727-c9db4b7aaa22/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211025155311-828a885a7140 h1:2b5DXA/r6bViC2AwUO66SKfdGIus8ffcwR4+BLF0KTk=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211025155311-828a885a7140/go.mod h1:RTjaYEQboNk7+2qfPGBotaMEh/5HIvmPZ6DIe10lTqI=
|
||||
golang.zx2c4.com/wireguard/tun/netstack v0.0.0-20211025155311-828a885a7140 h1:dCphgMdSHXtrMgj5BmSqDXmc68at6MLgUg4EPbVYVb4=
|
||||
golang.zx2c4.com/wireguard/tun/netstack v0.0.0-20211025155311-828a885a7140/go.mod h1:U6gm2r5g3zEe4OlrGL+W3rptmOo4Sj6mIFSIZHMFo4s=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211116201604-de7c702ace45 h1:mEVhdMPTuebD9IUXOUB5Q2sjZpcmzkahHWd6DrGpLHA=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20211116201604-de7c702ace45/go.mod h1:evxZIqfCetExY5piKXGAxJYwvXWkps9zTCkWpkoGFxw=
|
||||
golang.zx2c4.com/wireguard/tun/netstack v0.0.0-20211116201604-de7c702ace45 h1:td2v30AWwaU5sKKbgqC3vh6jywoKOG76jJuZV/JnGzA=
|
||||
golang.zx2c4.com/wireguard/tun/netstack v0.0.0-20211116201604-de7c702ace45/go.mod h1:U6gm2r5g3zEe4OlrGL+W3rptmOo4Sj6mIFSIZHMFo4s=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2020 Tailscale & AUTHORS.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// HTTP proxy code
|
||||
|
||||
package httpproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Handler returns an HTTP proxy http.Handler using the
|
||||
// provided backend dialer.
|
||||
func Handler(dialer func(ctx context.Context, netw, addr string) (net.Conn, error)) http.Handler {
|
||||
rp := &httputil.ReverseProxy{
|
||||
Director: func(r *http.Request) {}, // no change
|
||||
Transport: &http.Transport{
|
||||
DialContext: dialer,
|
||||
},
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "CONNECT" {
|
||||
backURL := r.RequestURI
|
||||
if strings.HasPrefix(backURL, "/") || backURL == "*" {
|
||||
http.Error(w, "bogus RequestURI; must be absolute URL or CONNECT", 400)
|
||||
return
|
||||
}
|
||||
rp.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// CONNECT support:
|
||||
|
||||
dst := r.RequestURI
|
||||
c, err := dialer(r.Context(), "tcp", dst)
|
||||
if err != nil {
|
||||
w.Header().Set("Connect-Error", err.Error())
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
cc, ccbuf, err := w.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
io.WriteString(cc, "HTTP/1.1 200 OK\r\n\r\n")
|
||||
|
||||
var clientSrc io.Reader = ccbuf
|
||||
if ccbuf.Reader.Buffered() == 0 {
|
||||
// In the common case (with no
|
||||
// buffered data), read directly from
|
||||
// the underlying client connection to
|
||||
// save some memory, letting the
|
||||
// bufio.Reader/Writer get GC'ed.
|
||||
clientSrc = cc
|
||||
}
|
||||
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := io.Copy(cc, c)
|
||||
errc <- err
|
||||
}()
|
||||
go func() {
|
||||
_, err := io.Copy(c, clientSrc)
|
||||
errc <- err
|
||||
}()
|
||||
<-errc
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package proxymux splits a net.Listener in two, routing SOCKS5
|
||||
// connections to one and HTTP requests to the other.
|
||||
//
|
||||
// It allows for hosting both a SOCKS5 proxy and an HTTP proxy on the
|
||||
// same listener.
|
||||
package proxymux
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SplitSOCKSAndHTTP accepts connections on ln and passes connections
|
||||
// through to either socksListener or httpListener, depending the
|
||||
// first byte sent by the client.
|
||||
func SplitSOCKSAndHTTP(ln net.Listener) (socksListener, httpListener net.Listener) {
|
||||
sl := &listener{
|
||||
addr: ln.Addr(),
|
||||
c: make(chan net.Conn),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
hl := &listener{
|
||||
addr: ln.Addr(),
|
||||
c: make(chan net.Conn),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
|
||||
go splitSOCKSAndHTTPListener(ln, sl, hl)
|
||||
|
||||
return sl, hl
|
||||
}
|
||||
|
||||
func splitSOCKSAndHTTPListener(ln net.Listener, sl, hl *listener) {
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
sl.Close()
|
||||
hl.Close()
|
||||
return
|
||||
}
|
||||
go routeConn(conn, sl, hl)
|
||||
}
|
||||
}
|
||||
|
||||
func routeConn(c net.Conn, socksListener, httpListener *listener) {
|
||||
if err := c.SetReadDeadline(time.Now().Add(15 * time.Second)); err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
var b [1]byte
|
||||
if _, err := io.ReadFull(c, b[:]); err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.SetReadDeadline(time.Time{}); err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
conn := &connWithOneByte{
|
||||
Conn: c,
|
||||
b: b[0],
|
||||
}
|
||||
|
||||
// First byte of a SOCKS5 session is a version byte set to 5.
|
||||
var ln *listener
|
||||
if b[0] == 5 {
|
||||
ln = socksListener
|
||||
} else {
|
||||
ln = httpListener
|
||||
}
|
||||
select {
|
||||
case ln.c <- conn:
|
||||
case <-ln.closed:
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
|
||||
type listener struct {
|
||||
addr net.Addr
|
||||
c chan net.Conn
|
||||
mu sync.Mutex // serializes close() on closed. It's okay to receive on closed without locking.
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
func (ln *listener) Accept() (net.Conn, error) {
|
||||
// Once closed, reliably stay closed, don't race with attempts at
|
||||
// further connections.
|
||||
select {
|
||||
case <-ln.closed:
|
||||
return nil, net.ErrClosed
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case ret := <-ln.c:
|
||||
return ret, nil
|
||||
case <-ln.closed:
|
||||
return nil, net.ErrClosed
|
||||
}
|
||||
}
|
||||
|
||||
func (ln *listener) Close() error {
|
||||
ln.mu.Lock()
|
||||
defer ln.mu.Unlock()
|
||||
select {
|
||||
case <-ln.closed:
|
||||
// Already closed
|
||||
default:
|
||||
close(ln.closed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ln *listener) Addr() net.Addr {
|
||||
return ln.addr
|
||||
}
|
||||
|
||||
// connWithOneByte is a net.Conn that returns b for the first read
|
||||
// request, then forwards everything else to Conn.
|
||||
type connWithOneByte struct {
|
||||
net.Conn
|
||||
|
||||
b byte
|
||||
bRead bool
|
||||
}
|
||||
|
||||
func (c *connWithOneByte) Read(bs []byte) (int, error) {
|
||||
if c.bRead {
|
||||
return c.Conn.Read(bs)
|
||||
}
|
||||
if len(bs) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
c.bRead = true
|
||||
bs[0] = c.b
|
||||
return 1, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package socks5 is a SOCKS5 server implementation.
|
||||
//
|
||||
// This is used for userspace networking in Tailscale. Specifically,
|
||||
// this is used for dialing out of the machine to other nodes, without
|
||||
// the host kernel's involvement, so it doesn't proper routing tables,
|
||||
// TUN, IPv6, etc. This package is meant to only handle the SOCKS5 protocol
|
||||
// details and not any integration with Tailscale internals itself.
|
||||
//
|
||||
// The glue between this package and Tailscale is in net/socks5/tssocks.
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
noAuthRequired byte = 0
|
||||
noAcceptableAuth byte = 255
|
||||
|
||||
// socks5Version is the byte that represents the SOCKS version
|
||||
// in requests.
|
||||
socks5Version byte = 5
|
||||
)
|
||||
|
||||
// commandType are the bytes sent in SOCKS5 packets
|
||||
// that represent the kind of connection the client needs.
|
||||
type commandType byte
|
||||
|
||||
// The set of valid SOCKS5 commands as described in RFC 1928.
|
||||
const (
|
||||
connect commandType = 1
|
||||
bind commandType = 2
|
||||
udpAssociate commandType = 3
|
||||
)
|
||||
|
||||
// addrType are the bytes sent in SOCKS5 packets
|
||||
// that represent particular address types.
|
||||
type addrType byte
|
||||
|
||||
// The set of valid SOCKS5 address types as defined in RFC 1928.
|
||||
const (
|
||||
ipv4 addrType = 1
|
||||
domainName addrType = 3
|
||||
ipv6 addrType = 4
|
||||
)
|
||||
|
||||
// replyCode are the bytes sent in SOCKS5 packets
|
||||
// that represent replies from the server to a client
|
||||
// request.
|
||||
type replyCode byte
|
||||
|
||||
// The set of valid SOCKS5 reply types as per the RFC 1928.
|
||||
const (
|
||||
success replyCode = 0
|
||||
generalFailure replyCode = 1
|
||||
connectionNotAllowed replyCode = 2
|
||||
networkUnreachable replyCode = 3
|
||||
hostUnreachable replyCode = 4
|
||||
connectionRefused replyCode = 5
|
||||
ttlExpired replyCode = 6
|
||||
commandNotSupported replyCode = 7
|
||||
addrTypeNotSupported replyCode = 8
|
||||
)
|
||||
|
||||
// Server is a SOCKS5 proxy server.
|
||||
type Server struct {
|
||||
// Logf optionally specifies the logger to use.
|
||||
// If nil, the standard logger is used.
|
||||
Logf func(format string, args ...interface{})
|
||||
|
||||
// Dialer optionally specifies the dialer to use for outgoing connections.
|
||||
// If nil, the net package's standard dialer is used.
|
||||
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
func (s *Server) dial(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
dial := s.Dialer
|
||||
if dial == nil {
|
||||
dialer := &net.Dialer{}
|
||||
dial = dialer.DialContext
|
||||
}
|
||||
return dial(ctx, network, addr)
|
||||
}
|
||||
|
||||
func (s *Server) logf(format string, args ...interface{}) {
|
||||
logf := s.Logf
|
||||
if logf == nil {
|
||||
logf = log.Printf
|
||||
}
|
||||
logf(format, args...)
|
||||
}
|
||||
|
||||
// Serve accepts and handles incoming connections on the given listener.
|
||||
func (s *Server) Serve(l net.Listener) error {
|
||||
defer l.Close()
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
conn := &Conn{clientConn: c, srv: s}
|
||||
err := conn.Run()
|
||||
if err != nil {
|
||||
s.logf("client connection failed: %v", err)
|
||||
conn.clientConn.Close()
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Conn is a SOCKS5 connection for client to reach
|
||||
// server.
|
||||
type Conn struct {
|
||||
// The struct is filled by each of the internal
|
||||
// methods in turn as the transaction progresses.
|
||||
|
||||
srv *Server
|
||||
clientConn net.Conn
|
||||
request *request
|
||||
}
|
||||
|
||||
// Run starts the new connection.
|
||||
func (c *Conn) Run() error {
|
||||
err := parseClientGreeting(c.clientConn)
|
||||
if err != nil {
|
||||
c.clientConn.Write([]byte{socks5Version, noAcceptableAuth})
|
||||
return err
|
||||
}
|
||||
c.clientConn.Write([]byte{socks5Version, noAuthRequired})
|
||||
return c.handleRequest()
|
||||
}
|
||||
|
||||
func (c *Conn) handleRequest() error {
|
||||
req, err := parseClientRequest(c.clientConn)
|
||||
if err != nil {
|
||||
res := &response{reply: generalFailure}
|
||||
buf, _ := res.marshal()
|
||||
c.clientConn.Write(buf)
|
||||
return err
|
||||
}
|
||||
if req.command != connect {
|
||||
res := &response{reply: commandNotSupported}
|
||||
buf, _ := res.marshal()
|
||||
c.clientConn.Write(buf)
|
||||
return fmt.Errorf("unsupported command %v", req.command)
|
||||
}
|
||||
c.request = req
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
srv, err := c.srv.dial(
|
||||
ctx,
|
||||
"tcp",
|
||||
net.JoinHostPort(c.request.destination, strconv.Itoa(int(c.request.port))),
|
||||
)
|
||||
if err != nil {
|
||||
res := &response{reply: generalFailure}
|
||||
buf, _ := res.marshal()
|
||||
c.clientConn.Write(buf)
|
||||
return err
|
||||
}
|
||||
defer srv.Close()
|
||||
serverAddr, serverPortStr, err := net.SplitHostPort(srv.LocalAddr().String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serverPort, _ := strconv.Atoi(serverPortStr)
|
||||
|
||||
var bindAddrType addrType
|
||||
if ip := net.ParseIP(serverAddr); ip != nil {
|
||||
if ip.To4() != nil {
|
||||
bindAddrType = ipv4
|
||||
} else {
|
||||
bindAddrType = ipv6
|
||||
}
|
||||
} else {
|
||||
bindAddrType = domainName
|
||||
}
|
||||
res := &response{
|
||||
reply: success,
|
||||
bindAddrType: bindAddrType,
|
||||
bindAddr: serverAddr,
|
||||
bindPort: uint16(serverPort),
|
||||
}
|
||||
buf, err := res.marshal()
|
||||
if err != nil {
|
||||
res = &response{reply: generalFailure}
|
||||
buf, _ = res.marshal()
|
||||
}
|
||||
c.clientConn.Write(buf)
|
||||
|
||||
errc := make(chan error, 2)
|
||||
go func() {
|
||||
_, err := io.Copy(c.clientConn, srv)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("from backend to client: %w", err)
|
||||
}
|
||||
errc <- err
|
||||
}()
|
||||
go func() {
|
||||
_, err := io.Copy(srv, c.clientConn)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("from client to backend: %w", err)
|
||||
}
|
||||
errc <- err
|
||||
}()
|
||||
return <-errc
|
||||
}
|
||||
|
||||
// parseClientGreeting parses a request initiation packet
|
||||
// and returns a slice that contains the acceptable auth methods
|
||||
// for the client.
|
||||
func parseClientGreeting(r io.Reader) error {
|
||||
var hdr [2]byte
|
||||
_, err := io.ReadFull(r, hdr[:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read packet header")
|
||||
}
|
||||
if hdr[0] != socks5Version {
|
||||
return fmt.Errorf("incompatible SOCKS version")
|
||||
}
|
||||
count := int(hdr[1])
|
||||
methods := make([]byte, count)
|
||||
_, err = io.ReadFull(r, methods)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read methods")
|
||||
}
|
||||
for _, m := range methods {
|
||||
if m == noAuthRequired {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("no acceptable auth methods")
|
||||
}
|
||||
|
||||
// request represents data contained within a SOCKS5
|
||||
// connection request packet.
|
||||
type request struct {
|
||||
command commandType
|
||||
destination string
|
||||
port uint16
|
||||
destAddrType addrType
|
||||
}
|
||||
|
||||
// parseClientRequest converts raw packet bytes into a
|
||||
// SOCKS5Request struct.
|
||||
func parseClientRequest(r io.Reader) (*request, error) {
|
||||
var hdr [4]byte
|
||||
_, err := io.ReadFull(r, hdr[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read packet header")
|
||||
}
|
||||
cmd := hdr[1]
|
||||
destAddrType := addrType(hdr[3])
|
||||
|
||||
var destination string
|
||||
var port uint16
|
||||
|
||||
if destAddrType == ipv4 {
|
||||
var ip [4]byte
|
||||
_, err = io.ReadFull(r, ip[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read IPv4 address")
|
||||
}
|
||||
destination = net.IP(ip[:]).String()
|
||||
} else if destAddrType == domainName {
|
||||
var dstSizeByte [1]byte
|
||||
_, err = io.ReadFull(r, dstSizeByte[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read domain name size")
|
||||
}
|
||||
dstSize := int(dstSizeByte[0])
|
||||
domainName := make([]byte, dstSize)
|
||||
_, err = io.ReadFull(r, domainName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read domain name")
|
||||
}
|
||||
destination = string(domainName)
|
||||
} else if destAddrType == ipv6 {
|
||||
var ip [16]byte
|
||||
_, err = io.ReadFull(r, ip[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read IPv6 address")
|
||||
}
|
||||
destination = net.IP(ip[:]).String()
|
||||
} else {
|
||||
return nil, fmt.Errorf("unsupported address type")
|
||||
}
|
||||
var portBytes [2]byte
|
||||
_, err = io.ReadFull(r, portBytes[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read port")
|
||||
}
|
||||
port = binary.BigEndian.Uint16(portBytes[:])
|
||||
|
||||
return &request{
|
||||
command: commandType(cmd),
|
||||
destination: destination,
|
||||
port: port,
|
||||
destAddrType: destAddrType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// response contains the contents of
|
||||
// a response packet sent from the proxy
|
||||
// to the client.
|
||||
type response struct {
|
||||
reply replyCode
|
||||
bindAddrType addrType
|
||||
bindAddr string
|
||||
bindPort uint16
|
||||
}
|
||||
|
||||
// marshal converts a SOCKS5Response struct into
|
||||
// a packet. If res.reply == Success, it may throw an error on
|
||||
// receiving an invalid bind address. Otherwise, it will not throw.
|
||||
func (res *response) marshal() ([]byte, error) {
|
||||
pkt := make([]byte, 4)
|
||||
pkt[0] = socks5Version
|
||||
pkt[1] = byte(res.reply)
|
||||
pkt[2] = 0 // null reserved byte
|
||||
pkt[3] = byte(res.bindAddrType)
|
||||
|
||||
if res.reply != success {
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
var addr []byte
|
||||
switch res.bindAddrType {
|
||||
case ipv4:
|
||||
addr = net.ParseIP(res.bindAddr).To4()
|
||||
if addr == nil {
|
||||
return nil, fmt.Errorf("invalid IPv4 address for binding")
|
||||
}
|
||||
case domainName:
|
||||
if len(res.bindAddr) > 255 {
|
||||
return nil, fmt.Errorf("invalid domain name for binding")
|
||||
}
|
||||
addr = make([]byte, 0, len(res.bindAddr)+1)
|
||||
addr = append(addr, byte(len(res.bindAddr)))
|
||||
addr = append(addr, []byte(res.bindAddr)...)
|
||||
case ipv6:
|
||||
addr = net.ParseIP(res.bindAddr).To16()
|
||||
if addr == nil {
|
||||
return nil, fmt.Errorf("invalid IPv6 address for binding")
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported address type")
|
||||
}
|
||||
|
||||
pkt = append(pkt, addr...)
|
||||
port := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(port, uint16(res.bindPort))
|
||||
pkt = append(pkt, port...)
|
||||
|
||||
return pkt, nil
|
||||
}
|
||||
26
main.go
26
main.go
|
|
@ -12,6 +12,10 @@ import (
|
|||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||
|
||||
"github.com/zhsj/wghttp/internal/third_party/tailscale/httpproxy"
|
||||
"github.com/zhsj/wghttp/internal/third_party/tailscale/proxymux"
|
||||
"github.com/zhsj/wghttp/internal/third_party/tailscale/socks5"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -47,10 +51,24 @@ func main() {
|
|||
}
|
||||
log.Printf("Listening on %s", ln.Addr())
|
||||
|
||||
s := &http.Server{Handler: httpProxyHandler(tnet.DialContext)}
|
||||
if err := s.Serve(ln); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
socksListener, httpListener := proxymux.SplitSOCKSAndHTTP(ln)
|
||||
|
||||
httpProxy := &http.Server{Handler: httpproxy.Handler(tnet.DialContext)}
|
||||
socksProxy := &socks5.Server{Dialer: tnet.DialContext}
|
||||
|
||||
errc := make(chan error, 2)
|
||||
go func() {
|
||||
if err := httpProxy.Serve(httpListener); err != nil {
|
||||
errc <- err
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
if err := socksProxy.Serve(socksListener); err != nil {
|
||||
errc <- err
|
||||
}
|
||||
}()
|
||||
|
||||
log.Fatal(<-errc)
|
||||
}
|
||||
|
||||
func setupNet(opts options) *netstack.Net {
|
||||
|
|
|
|||
102
proxy.go
102
proxy.go
|
|
@ -1,102 +0,0 @@
|
|||
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// HTTP proxy code
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// httpProxyHandler returns an HTTP proxy http.Handler using the
|
||||
// provided backend dialer.
|
||||
func httpProxyHandler(dialer func(ctx context.Context, netw, addr string) (net.Conn, error)) http.Handler {
|
||||
rp := &httputil.ReverseProxy{
|
||||
Director: func(r *http.Request) {}, // no change
|
||||
Transport: &http.Transport{
|
||||
DialContext: dialer,
|
||||
},
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "CONNECT" {
|
||||
backURL := r.RequestURI
|
||||
if strings.HasPrefix(backURL, "/") || backURL == "*" {
|
||||
http.Error(w, "bogus RequestURI; must be absolute URL or CONNECT", 400)
|
||||
return
|
||||
}
|
||||
rp.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// CONNECT support:
|
||||
|
||||
dst := r.RequestURI
|
||||
c, err := dialer(r.Context(), "tcp", dst)
|
||||
if err != nil {
|
||||
w.Header().Set("Connect-Error", err.Error())
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
cc, ccbuf, err := w.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
io.WriteString(cc, "HTTP/1.1 200 OK\r\n\r\n")
|
||||
|
||||
var clientSrc io.Reader = ccbuf
|
||||
if ccbuf.Reader.Buffered() == 0 {
|
||||
// In the common case (with no
|
||||
// buffered data), read directly from
|
||||
// the underlying client connection to
|
||||
// save some memory, letting the
|
||||
// bufio.Reader/Writer get GC'ed.
|
||||
clientSrc = cc
|
||||
}
|
||||
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := io.Copy(cc, c)
|
||||
errc <- err
|
||||
}()
|
||||
go func() {
|
||||
_, err := io.Copy(c, clientSrc)
|
||||
errc <- err
|
||||
}()
|
||||
<-errc
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue