2018-08-29 05:06:07 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2019-01-19 05:30:28 -08:00
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
2018-08-29 05:06:07 -07:00
|
|
|
"golang.org/x/net/proxy"
|
2019-01-19 05:34:32 -08:00
|
|
|
"io"
|
2018-08-29 05:06:07 -07:00
|
|
|
"net"
|
2019-01-19 05:30:28 -08:00
|
|
|
"net/http"
|
2018-08-29 05:06:07 -07:00
|
|
|
"os"
|
2019-01-19 05:30:28 -08:00
|
|
|
"socks5"
|
2018-08-29 05:06:07 -07:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2019-01-19 05:30:28 -08:00
|
|
|
type httpProxyHandler struct {
|
|
|
|
upstream proxy.Dialer
|
|
|
|
}
|
|
|
|
|
|
|
|
func transfer(dst io.WriteCloser, src io.ReadCloser) {
|
|
|
|
defer dst.Close()
|
|
|
|
defer src.Close()
|
|
|
|
io.Copy(dst, src)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *httpProxyHandler) dialOut(addr string) (net.Conn, error) {
|
|
|
|
host, _, err := net.SplitHostPort(addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if strings.HasSuffix(host, ".onion") {
|
|
|
|
return h.upstream.Dial("tcp", addr)
|
|
|
|
}
|
|
|
|
return net.Dial("tcp", addr)
|
|
|
|
}
|
|
|
|
|
2019-01-19 05:34:32 -08:00
|
|
|
func (h *httpProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
2019-01-19 05:30:28 -08:00
|
|
|
if r.Method == http.MethodConnect {
|
|
|
|
outConn, err := h.dialOut(r.Host)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusServiceUnavailable)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
hijacker, ok := w.(http.Hijacker)
|
|
|
|
if !ok {
|
|
|
|
http.Error(w, "hijack disallowed", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
conn, _, err := hijacker.Hijack()
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, err.Error(), http.StatusServiceUnavailable)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-29 05:06:07 -07:00
|
|
|
func main() {
|
|
|
|
args := os.Args[1:]
|
2019-01-19 05:32:54 -08:00
|
|
|
usehttp := args[0] == "http"
|
2018-08-29 05:06:07 -07:00
|
|
|
if len(args) < 2 {
|
2019-01-19 05:32:54 -08:00
|
|
|
fmt.Printf("usage: %s proto bindaddr onionsocksaddr\n", os.Args[0])
|
2018-08-29 05:06:07 -07:00
|
|
|
return
|
|
|
|
}
|
2019-01-19 05:30:28 -08:00
|
|
|
|
2019-01-19 05:32:54 -08:00
|
|
|
upstream, err := proxy.SOCKS5("tcp", args[2], nil, nil)
|
2018-08-29 05:06:07 -07:00
|
|
|
if err != nil {
|
2019-01-19 05:32:54 -08:00
|
|
|
fmt.Printf("failed to create upstream proxy to %s, %s", args[2], err.Error())
|
2019-01-19 05:30:28 -08:00
|
|
|
return
|
2018-08-29 05:06:07 -07:00
|
|
|
}
|
2019-01-19 05:30:28 -08:00
|
|
|
if usehttp {
|
|
|
|
serv := &http.Server{
|
2019-01-19 05:32:54 -08:00
|
|
|
Addr: args[1],
|
2019-01-19 05:34:32 -08:00
|
|
|
Handler: &httpProxyHandler{
|
2019-01-19 05:30:28 -08:00
|
|
|
upstream: upstream,
|
|
|
|
},
|
|
|
|
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
|
|
|
}
|
2019-01-19 05:34:32 -08:00
|
|
|
serv.ListenAndServe()
|
2019-01-19 05:30:28 -08:00
|
|
|
} else {
|
|
|
|
serv, err := socks5.New(&socks5.Config{
|
|
|
|
Dial: func(addr string) (net.Conn, error) {
|
|
|
|
host, _, err := net.SplitHostPort(addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if strings.HasSuffix(host, ".onion") {
|
|
|
|
return upstream.Dial("tcp", addr)
|
|
|
|
}
|
|
|
|
return net.Dial("tcp", addr)
|
|
|
|
},
|
|
|
|
})
|
2018-08-29 05:06:07 -07:00
|
|
|
|
2019-01-19 05:30:28 -08:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("failed to create socks proxy %s", err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-19 05:32:54 -08:00
|
|
|
l, err := net.Listen("tcp", args[1])
|
2019-01-19 05:30:28 -08:00
|
|
|
if err != nil {
|
2019-01-19 05:32:54 -08:00
|
|
|
fmt.Printf("failed to listen on %s, %s", args[1], err.Error())
|
2019-01-19 05:30:28 -08:00
|
|
|
return
|
|
|
|
|
|
|
|
serv.Serve(l)
|
|
|
|
}
|
2018-08-29 05:06:07 -07:00
|
|
|
}
|
|
|
|
}
|