controlhttpserver.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !ios
  4. // Package controlhttpserver contains the HTTP server side of the ts2021 control protocol.
  5. package controlhttpserver
  6. import (
  7. "context"
  8. "encoding/base64"
  9. "errors"
  10. "fmt"
  11. "io"
  12. "net"
  13. "net/http"
  14. "strings"
  15. "time"
  16. "github.com/coder/websocket"
  17. "tailscale.com/control/controlbase"
  18. "tailscale.com/control/controlhttp/controlhttpcommon"
  19. "tailscale.com/net/netutil"
  20. "tailscale.com/net/wsconn"
  21. "tailscale.com/types/key"
  22. )
  23. // AcceptHTTP upgrades the HTTP request given by w and r into a Tailscale
  24. // control protocol base transport connection.
  25. //
  26. // AcceptHTTP always writes an HTTP response to w. The caller must not attempt
  27. // their own response after calling AcceptHTTP.
  28. //
  29. // earlyWrite optionally specifies a func to write to the noise connection
  30. // (encrypted). It receives the negotiated version and a writer to write to, if
  31. // desired.
  32. func AcceptHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request, private key.MachinePrivate, earlyWrite func(protocolVersion int, w io.Writer) error) (*controlbase.Conn, error) {
  33. return acceptHTTP(ctx, w, r, private, earlyWrite)
  34. }
  35. func acceptHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request, private key.MachinePrivate, earlyWrite func(protocolVersion int, w io.Writer) error) (_ *controlbase.Conn, retErr error) {
  36. next := strings.ToLower(r.Header.Get("Upgrade"))
  37. if next == "" {
  38. http.Error(w, "missing next protocol", http.StatusBadRequest)
  39. return nil, errors.New("no next protocol in HTTP request")
  40. }
  41. if next == "websocket" {
  42. return acceptWebsocket(ctx, w, r, private)
  43. }
  44. if next != controlhttpcommon.UpgradeHeaderValue {
  45. http.Error(w, "unknown next protocol", http.StatusBadRequest)
  46. return nil, fmt.Errorf("client requested unhandled next protocol %q", next)
  47. }
  48. initB64 := r.Header.Get(controlhttpcommon.HandshakeHeaderName)
  49. if initB64 == "" {
  50. http.Error(w, "missing Tailscale handshake header", http.StatusBadRequest)
  51. return nil, errors.New("no tailscale handshake header in HTTP request")
  52. }
  53. init, err := base64.StdEncoding.DecodeString(initB64)
  54. if err != nil {
  55. http.Error(w, "invalid tailscale handshake header", http.StatusBadRequest)
  56. return nil, fmt.Errorf("decoding base64 handshake header: %v", err)
  57. }
  58. hijacker, ok := w.(http.Hijacker)
  59. if !ok {
  60. http.Error(w, "make request over HTTP/1", http.StatusBadRequest)
  61. return nil, errors.New("can't hijack client connection")
  62. }
  63. w.Header().Set("Upgrade", controlhttpcommon.UpgradeHeaderValue)
  64. w.Header().Set("Connection", "upgrade")
  65. w.WriteHeader(http.StatusSwitchingProtocols)
  66. conn, brw, err := hijacker.Hijack()
  67. if err != nil {
  68. return nil, fmt.Errorf("hijacking client connection: %w", err)
  69. }
  70. defer func() {
  71. if retErr != nil {
  72. conn.Close()
  73. }
  74. }()
  75. if err := brw.Flush(); err != nil {
  76. return nil, fmt.Errorf("flushing hijacked HTTP buffer: %w", err)
  77. }
  78. conn = netutil.NewDrainBufConn(conn, brw.Reader)
  79. cwc := newWriteCorkingConn(conn)
  80. nc, err := controlbase.Server(ctx, cwc, private, init)
  81. if err != nil {
  82. return nil, fmt.Errorf("noise handshake failed: %w", err)
  83. }
  84. if earlyWrite != nil {
  85. if deadline, ok := ctx.Deadline(); ok {
  86. if err := conn.SetDeadline(deadline); err != nil {
  87. return nil, fmt.Errorf("setting conn deadline: %w", err)
  88. }
  89. defer conn.SetDeadline(time.Time{})
  90. }
  91. if err := earlyWrite(nc.ProtocolVersion(), nc); err != nil {
  92. return nil, err
  93. }
  94. }
  95. if err := cwc.uncork(); err != nil {
  96. return nil, err
  97. }
  98. return nc, nil
  99. }
  100. // acceptWebsocket upgrades a WebSocket connection (from a client that cannot
  101. // speak HTTP) to a Tailscale control protocol base transport connection.
  102. func acceptWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, private key.MachinePrivate) (*controlbase.Conn, error) {
  103. c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
  104. Subprotocols: []string{controlhttpcommon.UpgradeHeaderValue},
  105. OriginPatterns: []string{"*"},
  106. // Disable compression because we transmit Noise messages that are not
  107. // compressible.
  108. // Additionally, Safari has a broken implementation of compression
  109. // (see https://github.com/nhooyr/websocket/issues/218) that makes
  110. // enabling it actively harmful.
  111. CompressionMode: websocket.CompressionDisabled,
  112. })
  113. if err != nil {
  114. return nil, fmt.Errorf("Could not accept WebSocket connection %v", err)
  115. }
  116. if c.Subprotocol() != controlhttpcommon.UpgradeHeaderValue {
  117. c.Close(websocket.StatusPolicyViolation, "client must speak the control subprotocol")
  118. return nil, fmt.Errorf("Unexpected subprotocol %q", c.Subprotocol())
  119. }
  120. if err := r.ParseForm(); err != nil {
  121. c.Close(websocket.StatusPolicyViolation, "Could not parse parameters")
  122. return nil, fmt.Errorf("parse query parameters: %v", err)
  123. }
  124. initB64 := r.Form.Get(controlhttpcommon.HandshakeHeaderName)
  125. if initB64 == "" {
  126. c.Close(websocket.StatusPolicyViolation, "missing Tailscale handshake parameter")
  127. return nil, errors.New("no tailscale handshake parameter in HTTP request")
  128. }
  129. init, err := base64.StdEncoding.DecodeString(initB64)
  130. if err != nil {
  131. c.Close(websocket.StatusPolicyViolation, "invalid tailscale handshake parameter")
  132. return nil, fmt.Errorf("decoding base64 handshake parameter: %v", err)
  133. }
  134. conn := wsconn.NetConn(ctx, c, websocket.MessageBinary, r.RemoteAddr)
  135. nc, err := controlbase.Server(ctx, conn, private, init)
  136. if err != nil {
  137. conn.Close()
  138. return nil, fmt.Errorf("noise handshake failed: %w", err)
  139. }
  140. return nc, nil
  141. }
  142. // corkConn is a net.Conn wrapper that initially buffers all writes until uncork
  143. // is called. If the conn is corked and a Read occurs, the Read will flush any
  144. // buffered (corked) write.
  145. //
  146. // Until uncorked, Read/Write/uncork may be not called concurrently.
  147. //
  148. // Deadlines still work, but a corked write ignores deadlines until a Read or
  149. // uncork goes to do that Write.
  150. //
  151. // Use newWriteCorkingConn to create one.
  152. type corkConn struct {
  153. net.Conn
  154. corked bool
  155. buf []byte // corked data
  156. }
  157. func newWriteCorkingConn(c net.Conn) *corkConn {
  158. return &corkConn{Conn: c, corked: true}
  159. }
  160. func (c *corkConn) Write(b []byte) (int, error) {
  161. if c.corked {
  162. c.buf = append(c.buf, b...)
  163. return len(b), nil
  164. }
  165. return c.Conn.Write(b)
  166. }
  167. func (c *corkConn) Read(b []byte) (int, error) {
  168. if c.corked {
  169. if err := c.flush(); err != nil {
  170. return 0, err
  171. }
  172. }
  173. return c.Conn.Read(b)
  174. }
  175. // uncork flushes any buffered data and uncorks the connection so future Writes
  176. // don't buffer. It may not be called concurrently with reads or writes and
  177. // may only be called once.
  178. func (c *corkConn) uncork() error {
  179. if !c.corked {
  180. panic("usage error; uncork called twice") // worth panicking to catch misuse
  181. }
  182. err := c.flush()
  183. c.corked = false
  184. return err
  185. }
  186. func (c *corkConn) flush() error {
  187. if len(c.buf) == 0 {
  188. return nil
  189. }
  190. _, err := c.Conn.Write(c.buf)
  191. c.buf = nil
  192. return err
  193. }