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 }