summaryrefslogtreecommitdiff
path: root/pkg/server/socket/proxy.go
diff options
context:
space:
mode:
authorMikhail Osipov <mike.osipov@gmail.com>2020-05-05 20:10:08 +0300
committerMikhail Osipov <mike.osipov@gmail.com>2020-05-05 20:10:08 +0300
commitb089b35f35a14e85d89df69254cc61495d59d3dd (patch)
treeda07e5b015e02f7fa05cf0be181697670af787ed /pkg/server/socket/proxy.go
parentf44d6e1a111154b70aaeac9ffe38beaee2cc5dd7 (diff)
add http connect proxy server
Diffstat (limited to 'pkg/server/socket/proxy.go')
-rw-r--r--pkg/server/socket/proxy.go134
1 files changed, 134 insertions, 0 deletions
diff --git a/pkg/server/socket/proxy.go b/pkg/server/socket/proxy.go
new file mode 100644
index 0000000..ef14f48
--- /dev/null
+++ b/pkg/server/socket/proxy.go
@@ -0,0 +1,134 @@
+package socket
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "tunnel/pkg/http"
+ "tunnel/pkg/server/env"
+ "tunnel/pkg/server/queue"
+)
+
+type status struct {
+ code int
+ desc string
+}
+
+type proxySocket struct {
+ proto string
+}
+
+type proxyServer struct {
+ sock *proxySocket
+ addr string
+ auth string
+ wait chan status
+ env env.Env
+ conn Conn
+}
+
+func newProxySocket(proto string) (S, error) {
+ return &proxySocket{proto}, nil
+}
+
+func (sock *proxySocket) Open(env env.Env) (Conn, error) {
+ s := &proxyServer{
+ sock: sock,
+ auth: env.GetLocal("proxy.auth"),
+ wait: make(chan status),
+ env: env,
+ }
+
+ return s, nil
+}
+
+func (sock *proxySocket) Close() {
+}
+
+func (s *proxyServer) String() string {
+ return "proxy"
+}
+
+func (s *proxyServer) Send(wq queue.Q) error {
+ var out bytes.Buffer
+
+ status := <-s.wait
+
+ fmt.Fprintf(&out, "HTTP/1.0 %d %s\r\n", status.code, status.desc)
+ switch status.code {
+ case http.OK:
+ fmt.Fprintf(&out, "Proxy-Agent: tunnel\r\n")
+ case 407:
+ fmt.Fprintf(&out, "Proxy-Authenticate: Basic realm=\"tunnel\"\r\n")
+ fallthrough
+ default:
+ fmt.Fprintf(&out, "Server: tunnel\r\n")
+ fmt.Fprintf(&out, "Connection: close\r\n")
+ }
+ fmt.Fprintf(&out, "\r\n")
+
+ wq <- out.Bytes()
+
+ if status.code != http.OK {
+ return nil
+ }
+
+ return s.conn.Send(wq)
+}
+
+func (s *proxyServer) initConn(addr string) error {
+ dial, err := newDialSocket(s.sock.proto, addr)
+ if err != nil {
+ return err
+ }
+
+ conn, err := dial.Open(s.env)
+ if err != nil {
+ dial.Close()
+ return err
+ }
+
+ s.conn = conn
+
+ return nil
+}
+
+func (s *proxyServer) Recv(rq queue.Q) error {
+ req, err := http.ParseRequest(rq.Reader())
+ if err != nil {
+ s.wait <- status{400, "Bad Request"}
+ return err
+ }
+
+ if req.Method != "CONNECT" {
+ s.wait <- status{400, "Bad Request"}
+ return errors.New("bad method")
+ }
+
+ if s.auth != "" {
+ if auth, ok := req.Header["Proxy-Authorization"]; !ok {
+ s.wait <- status{407, "Proxy Authentication Required"}
+ return errors.New("auth required")
+ } else if !http.BasicAuthCheck(s.auth, auth) {
+ s.wait <- status{401, "Unauthorized"}
+ return errors.New("auth failed")
+ }
+ }
+
+ if err := s.initConn(req.URI); err != nil {
+ s.wait <- status{500, "Unable to connect"}
+ return err
+ }
+
+ s.wait <- status{200, "Connection established"}
+
+ return s.conn.Recv(rq)
+}
+
+func (s *proxyServer) Close() (err error) {
+ if s.conn != nil {
+ err = s.conn.Close()
+ }
+
+ return
+}