summaryrefslogtreecommitdiff
path: root/pkg/server/socket/proxy.go
blob: ef14f48681f0118f671f8daa8ee9bcdc08775e81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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
}