package hook import ( "bytes" "errors" "fmt" "regexp" "tunnel/pkg/http" "tunnel/pkg/server/env" "tunnel/pkg/server/opts" "tunnel/pkg/server/queue" ) var addrRe = regexp.MustCompile("^[0-9a-zA-Z-.]+:[0-9]+$") 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 { resp, err := http.ParseResponse(rq.Reader()) 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.Copy(rq, wq) } func (h *proxyHook) Open(env env.Env) (interface{}, error) { p := &proxy{ addr: h.addr, auth: h.auth, c: make(chan bool), } if p.auth == "" { p.auth = env.GetLocal("proxy.auth") } return p, nil } func newProxyHook(opts opts.Opts, env env.Env) (hook, error) { h := &proxyHook{} if addr, ok := opts["addr"]; !ok { return nil, errors.New("proxy: missing addr") } else if !addrRe.MatchString(addr) { return nil, errors.New("proxy: invalid addr") } else { h.addr = addr } h.auth = opts["auth"] return h, nil } func init() { register("proxy", newProxyHook) }