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
|
package hook
import (
"bufio"
"bytes"
"errors"
"fmt"
"regexp"
"tunnel/pkg/http"
"tunnel/pkg/server/env"
"tunnel/pkg/server/opts"
"tunnel/pkg/server/queue"
)
var addrPattern = "^([0-9a-zA-Z-.]+|\\[[0-9a-fA-F:]*\\]):[0-9]+$"
var isGoodAddr = regexp.MustCompile(addrPattern).MatchString
type proxyHook struct {
addr string
auth string
}
type proxy struct {
addr string
auth string
c chan bool
}
func (p *proxy) Send(rq, wq queue.Q) error {
var out bytes.Buffer
fmt.Fprintf(&out, "CONNECT %s HTTP/1.1\r\n", p.addr)
if p.auth != "" {
encoded := http.BasicAuthEncode(p.auth)
fmt.Fprintf(&out, "Proxy-Authorization: %s\r\n", encoded)
}
fmt.Fprintf(&out, "\r\n")
wq <- out.Bytes()
if !<-p.c {
return nil
}
return queue.Copy(rq, wq)
}
func (p *proxy) Recv(rq, wq queue.Q) error {
r := bufio.NewReader(rq.Reader())
resp, err := http.ParseResponse(r)
if err == nil && resp.Code != http.OK {
err = fmt.Errorf("connect failed: %d %s", resp.Code, resp.Desc)
}
if err != nil {
p.c <- false
return err
}
p.c <- true
return queue.IoCopy(r, wq.Writer())
}
func (h *proxyHook) Open(env env.Env) (interface{}, error) {
addr := env.Expand(h.addr)
if !isGoodAddr(addr) {
return nil, fmt.Errorf("invalid addr '%s'", addr)
}
p := &proxy{
addr: addr,
auth: h.auth,
c: make(chan bool),
}
if p.auth == "" {
p.auth = env.Value("proxy.auth")
}
return p, nil
}
func newProxyHook(opts opts.Opts) (hook, error) {
h := &proxyHook{
addr: opts["addr"],
auth: opts["auth"],
}
if h.addr == "" {
return nil, errors.New("expected addr")
}
return h, nil
}
func init() {
register("proxy", newProxyHook)
}
|