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 env env.Env } 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 } p.env.Push("info", "-> "+p.addr) 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), env: env, } 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) }