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
135
136
137
138
139
140
141
|
package socket
import (
"bufio"
"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 `opts:"default:tcp"`
}
type proxyServer struct {
sock *proxySocket
addr string
auth string
wait chan status
env env.Env
conn Conn
}
func (sock *proxySocket) New(env env.Env) (Conn, error) {
s := &proxyServer{
sock: sock,
auth: env.Value("proxy.auth"),
wait: make(chan status),
env: env,
}
return s, nil
}
func (sock *proxySocket) Close() {
}
func (s *proxyServer) String() string {
return "proxy(" + s.addr + ")"
}
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 := dialSocket{
Proto: s.sock.Proto,
Addr: addr,
}
conn, err := dial.New(s.env)
if err != nil {
dial.Close()
return err
}
s.addr = addr
s.conn = conn
return nil
}
func (s *proxyServer) Recv(rq queue.Q) error {
r := bufio.NewReader(rq.Reader())
req, err := http.ParseRequest(r)
if err != nil {
s.wait <- status{400, "Bad Request"}
return err
}
// TODO check if extra data is available in reader
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 queue.IoCopy(r, s.conn.(*conn))
}
func (s *proxyServer) Close() (err error) {
if s.conn != nil {
err = s.conn.Close()
}
return
}
func init() {
register("proxy", proxySocket{})
}
|