proxyconnect.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. // Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package ipnserver
  5. import (
  6. "bufio"
  7. "context"
  8. "io"
  9. "net"
  10. "net/http"
  11. "time"
  12. "tailscale.com/logpolicy"
  13. "tailscale.com/types/logger"
  14. )
  15. // handleProxyConnectConn handles a CONNECT request to
  16. // log.tailscale.io (or whatever the configured log server is). This
  17. // is intended for use by the Windows GUI client to log via when an
  18. // exit node is in use, so the logs don't go out via the exit node and
  19. // instead go directly, like tailscaled's. The dialer tried to do that
  20. // in the unprivileged GUI by binding to a specific interface, but the
  21. // "Internet Kill Switch" installed by tailscaled for exit nodes
  22. // precludes that from working and instead the GUI fails to dial out.
  23. // So, go through tailscaled (with a CONNECT request) instead.
  24. func (s *Server) handleProxyConnectConn(ctx context.Context, br *bufio.Reader, c net.Conn, logf logger.Logf) {
  25. defer c.Close()
  26. c.SetReadDeadline(time.Now().Add(5 * time.Second)) // should be long enough to send the HTTP headers
  27. req, err := http.ReadRequest(br)
  28. if err != nil {
  29. logf("ReadRequest: %v", err)
  30. return
  31. }
  32. c.SetReadDeadline(time.Time{})
  33. if req.Method != "CONNECT" {
  34. logf("ReadRequest: unexpected method %q, not CONNECT", req.Method)
  35. return
  36. }
  37. hostPort := req.RequestURI
  38. logHost := logpolicy.LogHost()
  39. allowed := net.JoinHostPort(logHost, "443")
  40. if hostPort != allowed {
  41. logf("invalid CONNECT target %q; want %q", hostPort, allowed)
  42. io.WriteString(c, "HTTP/1.1 403 Forbidden\r\n\r\nBad CONNECT target.\n")
  43. return
  44. }
  45. tr := logpolicy.NewLogtailTransport(logHost)
  46. back, err := tr.DialContext(ctx, "tcp", hostPort)
  47. if err != nil {
  48. logf("error CONNECT dialing %v: %v", hostPort, err)
  49. io.WriteString(c, "HTTP/1.1 502 Fail\r\n\r\nConnect failure.\n")
  50. return
  51. }
  52. defer back.Close()
  53. io.WriteString(c, "HTTP/1.1 200 OK\r\n\r\n")
  54. errc := make(chan error, 2)
  55. go func() {
  56. _, err := io.Copy(c, back)
  57. errc <- err
  58. }()
  59. go func() {
  60. _, err := io.Copy(back, br)
  61. errc <- err
  62. }()
  63. <-errc
  64. }