Files
httptunnel/httptunnel.go

400 lines
9.6 KiB
Go
Raw Normal View History

2019-02-19 21:54:12 -05:00
package i2phttpproxy
import (
2019-02-27 22:01:47 -05:00
"crypto/tls"
"fmt"
"golang.org/x/time/rate"
2019-02-19 21:54:12 -05:00
"io"
2019-03-16 23:40:59 -04:00
"io/ioutil"
2019-02-19 21:54:12 -05:00
"log"
"net"
2019-02-19 21:54:12 -05:00
"net/http"
2019-03-16 23:40:59 -04:00
"os"
2019-08-14 15:03:48 -04:00
"strconv"
2019-02-19 21:54:12 -05:00
"strings"
"time"
)
import (
"github.com/eyedeekay/goSam"
2019-06-09 23:09:52 -04:00
"github.com/eyedeekay/goSam/compat"
2019-02-27 22:01:47 -05:00
"github.com/eyedeekay/httptunnel/common"
2019-08-14 15:03:48 -04:00
"github.com/eyedeekay/sam-forwarder/hashhash"
2019-04-25 23:55:04 -04:00
"github.com/eyedeekay/sam-forwarder/i2pkeys"
2019-05-17 20:07:18 -04:00
"github.com/eyedeekay/sam-forwarder/interface"
2019-06-09 23:09:52 -04:00
"github.com/eyedeekay/sam3/i2pkeys"
2019-02-19 21:54:12 -05:00
)
2019-02-21 17:46:33 -05:00
type SAMHTTPProxy struct {
2019-03-16 23:40:59 -04:00
goSam *goSam.Client
2019-08-14 15:03:48 -04:00
Hasher *hashhash.Hasher
2019-02-27 22:01:47 -05:00
client *http.Client
transport *http.Transport
rateLimiter *rate.Limiter
2019-04-25 23:55:04 -04:00
tunName string
sigType string
proxyHost string
proxyPort string
2019-02-21 17:46:33 -05:00
SamHost string
SamPort string
2019-02-28 13:04:27 -05:00
controlHost string
controlPort string
2019-03-16 23:40:59 -04:00
destination string
keyspath string
2019-02-21 17:46:33 -05:00
inLength uint
outLength uint
inVariance int
outVariance int
inQuantity uint
outQuantity uint
inBackups uint
outBackups uint
dontPublishLease bool
encryptLease bool
reduceIdle bool
reduceIdleTime uint
reduceIdleQuantity uint
2019-02-26 22:36:26 -05:00
closeIdle bool
closeIdleTime uint
2019-02-21 17:46:33 -05:00
compression bool
2019-02-26 22:36:26 -05:00
useOutProxy bool
2019-02-21 17:46:33 -05:00
2019-02-27 22:01:47 -05:00
dialed bool
debug bool
2019-05-18 11:23:44 -04:00
up bool
2019-02-27 22:01:47 -05:00
}
2019-04-07 21:58:26 -04:00
var Quiet bool
2019-04-07 21:58:36 -04:00
func plog(in ...interface{}) {
if !Quiet {
2019-05-11 22:35:04 -04:00
log.Println(in...)
2019-04-07 21:58:36 -04:00
}
2019-04-07 21:58:26 -04:00
}
2019-04-25 23:51:14 -04:00
func (f *SAMHTTPProxy) print() []string {
2019-04-25 23:55:04 -04:00
return strings.Split(f.Print(), " ")
2019-04-25 23:51:14 -04:00
}
2019-05-11 22:35:04 -04:00
func (f *SAMHTTPProxy) GetType() string {
2019-05-11 22:36:34 -04:00
return "httpclient"
2019-05-11 22:35:04 -04:00
}
func (f *SAMHTTPProxy) ID() string {
2019-05-11 22:36:34 -04:00
return f.tunName
2019-05-11 22:35:04 -04:00
}
2019-06-09 23:09:52 -04:00
func (f *SAMHTTPProxy) Keys() i2pkeys.I2PKeys {
k, _ := samkeys.DestToKeys(f.goSam.Destination())
return k
}
2019-04-25 23:51:14 -04:00
func (f *SAMHTTPProxy) Props() map[string]string {
2019-05-11 22:35:04 -04:00
r := make(map[string]string)
2019-04-25 23:51:14 -04:00
for _, prop := range f.print() {
k, v := sfi2pkeys.Prop(prop)
r[k] = v
}
return r
}
func (p *SAMHTTPProxy) Cleanup() {
p.Close()
}
func (p *SAMHTTPProxy) Print() string {
return p.goSam.Print()
}
func (p *SAMHTTPProxy) Search(search string) string {
terms := strings.Split(search, ",")
if search == "" {
return p.Print()
}
for _, value := range terms {
if !strings.Contains(p.Print(), value) {
return ""
}
}
return p.Print()
}
func (p *SAMHTTPProxy) Target() string {
return p.proxyHost + ":" + p.proxyPort
}
func (p *SAMHTTPProxy) Base32() string {
return p.goSam.Base32()
}
2019-08-14 15:03:48 -04:00
// Base32Readable returns the base32 address where the local service is being
// forwarded, but as a list of English words(More languages later if it works)
// instead of as a hash for person-to-person transmission.
func (f *SAMHTTPProxy) Base32Readable() string {
b32 := strings.Replace(f.Base32(), ".b32.i2p", "", 1)
rhash, _ := f.Hasher.Friendly(b32)
return rhash + " " + strconv.Itoa(len(b32))
}
func (p *SAMHTTPProxy) Base64() string {
return p.goSam.Base64()
}
func (p *SAMHTTPProxy) Serve() error {
ln, err := net.Listen("tcp", p.proxyHost+":"+p.proxyPort)
if err != nil {
return err
}
srv := &http.Server{
ReadTimeout: 600 * time.Second,
WriteTimeout: 10 * time.Second,
Addr: ln.Addr().String(),
}
srv.Handler = p
if err != nil {
return err
}
log.Println("Starting proxy server on", ln.Addr())
if err := srv.Serve(ln); err != nil {
if err == http.ErrServerClosed {
return err
}
}
log.Println("Stopping proxy server on", ln.Addr())
return nil
}
func (p *SAMHTTPProxy) Close() error {
2019-05-18 11:23:44 -04:00
p.up = false
return p.goSam.Close()
}
2019-02-27 22:01:47 -05:00
func (p *SAMHTTPProxy) freshTransport() *http.Transport {
t := http.Transport{
2019-03-16 23:40:59 -04:00
DialContext: p.goSam.DialContext,
2019-02-27 22:01:47 -05:00
MaxConnsPerHost: 1,
MaxIdleConns: 0,
MaxIdleConnsPerHost: 1,
DisableKeepAlives: false,
ResponseHeaderTimeout: time.Second * 600,
IdleConnTimeout: time.Second * 300,
TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
return &t
2019-02-19 21:54:12 -05:00
}
2019-02-21 17:46:33 -05:00
func (p *SAMHTTPProxy) freshClient() *http.Client {
return &http.Client{
2019-02-27 22:01:47 -05:00
Transport: p.transport,
Timeout: time.Second * 300,
2019-02-26 22:36:26 -05:00
CheckRedirect: nil,
}
}
2019-02-27 22:01:47 -05:00
func (p *SAMHTTPProxy) freshSAMClient() (*goSam.Client, error) {
2019-03-16 23:40:59 -04:00
return p.goSam.NewClient()
2019-02-27 22:01:47 -05:00
}
//return the combined host:port of the SAM bridge
func (p *SAMHTTPProxy) samaddr() string {
return fmt.Sprintf("%s:%s", p.SamHost, p.SamPort)
}
2019-02-21 17:46:33 -05:00
func (p *SAMHTTPProxy) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
2019-04-07 21:58:26 -04:00
plog(req.RemoteAddr, " ", req.Method, " ", req.URL)
2019-03-16 23:40:59 -04:00
p.Save()
2019-02-26 22:36:26 -05:00
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
if !(req.Method == http.MethodConnect) {
msg := "Unsupported protocol scheme " + req.URL.Scheme
http.Error(wr, msg, http.StatusBadRequest)
2019-04-07 21:58:26 -04:00
plog(msg)
2019-02-26 22:36:26 -05:00
return
}
}
if !strings.HasSuffix(req.URL.Host, ".i2p") {
2019-02-28 13:04:27 -05:00
if req.URL.Host == p.controlHost+":"+p.controlPort {
p.reset(wr, req)
return
}
2019-02-26 22:36:26 -05:00
msg := "Unsupported host " + req.URL.Host
2019-04-07 21:58:36 -04:00
if !Quiet {
http.Error(wr, msg, http.StatusBadRequest)
2019-04-07 21:58:26 -04:00
}
2019-04-07 21:58:36 -04:00
plog(msg)
2019-02-19 21:54:12 -05:00
return
}
2019-02-27 22:01:47 -05:00
if req.Method != http.MethodConnect {
p.get(wr, req)
2019-02-26 22:36:26 -05:00
return
2019-02-21 17:46:33 -05:00
} else {
2019-02-27 22:01:47 -05:00
p.connect(wr, req)
2019-02-26 22:36:26 -05:00
return
}
}
2019-02-28 13:04:27 -05:00
func (p *SAMHTTPProxy) reset(wr http.ResponseWriter, req *http.Request) {
2019-04-07 21:58:26 -04:00
plog("Validating control access from", req.RemoteAddr, p.controlHost+":"+p.controlPort)
2019-02-28 13:04:27 -05:00
if strings.SplitN(req.RemoteAddr, ":", 2)[0] == p.controlHost {
2019-04-07 21:58:26 -04:00
plog("Validated control access from", req.RemoteAddr, p.controlHost+":"+p.controlPort)
2019-06-28 23:41:18 -04:00
resp, err := http.Get("http://" + p.controlHost + ":" + p.controlPort)
2019-02-28 13:04:27 -05:00
if err == nil {
wr.Header().Set("Content-Type", "text/html; charset=utf-8")
wr.Header().Set("Access-Control-Allow-Origin", "*")
wr.WriteHeader(resp.StatusCode)
io.Copy(wr, resp.Body)
return
}
}
}
2019-02-26 22:36:26 -05:00
func (p *SAMHTTPProxy) get(wr http.ResponseWriter, req *http.Request) {
req.RequestURI = ""
proxycommon.DelHopHeaders(req.Header)
2019-02-27 22:01:47 -05:00
p.client = p.freshClient()
resp, err := p.client.Do(req)
2019-02-26 22:36:26 -05:00
if err != nil {
msg := "Proxy Error " + err.Error()
if !Quiet {
http.Error(wr, msg, http.StatusBadRequest)
}
plog(msg)
2019-02-26 22:36:26 -05:00
return
2019-02-21 17:46:33 -05:00
}
2019-02-26 22:36:26 -05:00
defer resp.Body.Close()
2019-02-19 21:54:12 -05:00
2019-02-26 22:36:26 -05:00
proxycommon.CopyHeader(wr.Header(), resp.Header)
wr.WriteHeader(resp.StatusCode)
io.Copy(wr, resp.Body)
2019-02-21 17:46:33 -05:00
}
2019-02-19 21:54:12 -05:00
2019-02-21 17:46:33 -05:00
func (p *SAMHTTPProxy) connect(wr http.ResponseWriter, req *http.Request) {
2019-04-07 21:58:26 -04:00
plog("CONNECT via i2p to", req.URL.Host)
2019-03-16 23:40:59 -04:00
dest_conn, err := p.goSam.Dial("tcp", req.URL.Host)
2019-02-21 17:46:33 -05:00
if err != nil {
if !Quiet {
http.Error(wr, err.Error(), http.StatusServiceUnavailable)
}
return
2019-02-21 17:46:33 -05:00
}
wr.WriteHeader(http.StatusOK)
hijacker, ok := wr.(http.Hijacker)
if !ok {
if !Quiet {
http.Error(wr, "Hijacking not supported", http.StatusInternalServerError)
}
return
2019-02-21 17:46:33 -05:00
}
client_conn, _, err := hijacker.Hijack()
if err != nil {
if !Quiet {
http.Error(wr, err.Error(), http.StatusServiceUnavailable)
}
return
2019-02-19 21:54:12 -05:00
}
2019-02-26 22:36:26 -05:00
go proxycommon.Transfer(dest_conn, client_conn)
go proxycommon.Transfer(client_conn, dest_conn)
2019-02-21 17:46:33 -05:00
}
2019-05-17 20:07:18 -04:00
func (f *SAMHTTPProxy) Up() bool {
2019-05-18 11:23:44 -04:00
return f.up
2019-05-17 20:07:18 -04:00
}
2019-03-16 23:40:59 -04:00
func (p *SAMHTTPProxy) Save() string {
if p.keyspath != "invalid.tunkey" {
if _, err := os.Stat(p.keyspath); os.IsNotExist(err) {
if p.goSam != nil {
if p.goSam.Destination() != "" {
ioutil.WriteFile(p.keyspath, []byte(p.goSam.Destination()), 0644)
p.destination = p.goSam.Destination()
return p.goSam.Destination()
}
}
} else {
if keys, err := ioutil.ReadFile(p.keyspath); err == nil {
p.destination = string(keys)
return string(keys)
}
}
}
return ""
2019-02-27 22:01:47 -05:00
}
2019-05-17 20:07:18 -04:00
func (handler *SAMHTTPProxy) Load() (samtunnel.SAMTunnel, error) {
var err error
handler.destination = handler.Save()
handler.goSam, err = goSam.NewClientFromOptions(
goSam.SetHost(handler.SamHost),
goSam.SetPort(handler.SamPort),
goSam.SetUnpublished(handler.dontPublishLease),
goSam.SetInLength(handler.inLength),
goSam.SetOutLength(handler.outLength),
goSam.SetInQuantity(handler.inQuantity),
goSam.SetOutQuantity(handler.outQuantity),
goSam.SetInBackups(handler.inBackups),
goSam.SetOutBackups(handler.outBackups),
goSam.SetReduceIdle(handler.reduceIdle),
goSam.SetReduceIdleTime(handler.reduceIdleTime),
goSam.SetReduceIdleQuantity(handler.reduceIdleQuantity),
goSam.SetCloseIdle(handler.closeIdle),
goSam.SetCloseIdleTime(handler.closeIdleTime),
goSam.SetCompression(handler.compression),
goSam.SetDebug(handler.debug),
goSam.SetLocalDestination(handler.destination),
)
if err != nil {
return nil, err
}
handler.transport = handler.freshTransport()
handler.client = handler.freshClient()
2019-08-14 15:03:48 -04:00
handler.Hasher, err = hashhash.NewHasher(len(strings.Replace(handler.Base32(), ".b32.i2p", "", 1)))
if err != nil {
return nil, err
}
2019-05-18 11:23:44 -04:00
handler.up = true
2019-05-17 20:07:18 -04:00
return handler, nil
}
2019-02-21 17:46:33 -05:00
func NewHttpProxy(opts ...func(*SAMHTTPProxy) error) (*SAMHTTPProxy, error) {
var handler SAMHTTPProxy
handler.SamHost = "127.0.0.1"
handler.SamPort = "7656"
2019-02-28 13:04:27 -05:00
handler.controlHost = "127.0.0.1"
handler.controlPort = "7951"
handler.proxyHost = "127.0.0.1"
handler.proxyPort = "7950"
2019-02-21 17:46:33 -05:00
handler.inLength = 2
handler.outLength = 2
handler.inVariance = 0
handler.outVariance = 0
2019-02-27 22:01:47 -05:00
handler.inQuantity = 1
handler.outQuantity = 1
2019-02-21 17:46:33 -05:00
handler.inBackups = 1
handler.outBackups = 1
handler.dontPublishLease = true
handler.encryptLease = false
handler.reduceIdle = false
handler.reduceIdleTime = 2000000
2019-02-26 22:36:26 -05:00
handler.closeIdleTime = 3000000
2019-02-21 17:46:33 -05:00
handler.reduceIdleQuantity = 1
2019-02-26 22:36:26 -05:00
handler.useOutProxy = false
handler.compression = true
2019-04-25 23:51:14 -04:00
handler.tunName = "0"
2019-03-16 23:40:59 -04:00
handler.keyspath = "invalid.tunkey"
handler.destination = ""
2019-02-21 17:46:33 -05:00
for _, o := range opts {
if err := o(&handler); err != nil {
return nil, err
}
}
2019-05-17 20:07:18 -04:00
l, e := handler.Load()
if e != nil {
return nil, e
2019-02-21 17:46:33 -05:00
}
2019-05-17 20:07:18 -04:00
return l.(*SAMHTTPProxy), nil
2019-02-20 23:12:38 -05:00
}