Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

proxy.go 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package utils
  4. import (
  5. "crypto/tls"
  6. "errors"
  7. "net"
  8. "strings"
  9. "sync"
  10. "time"
  11. )
  12. // TODO: handle PROXY protocol v2 (the binary protocol)
  13. const (
  14. // https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
  15. // "a 108-byte buffer is always enough to store all the line and a trailing zero
  16. // for string processing."
  17. maxProxyLineLen = 107
  18. )
  19. // XXX implement net.Error with a Temporary() method that returns true;
  20. // otherwise, ErrBadProxyLine will cause (*http.Server).Serve() to exit
  21. type proxyLineError struct{}
  22. func (p *proxyLineError) Error() string {
  23. return "invalid PROXY line"
  24. }
  25. func (p *proxyLineError) Timeout() bool {
  26. return false
  27. }
  28. func (p *proxyLineError) Temporary() bool {
  29. return true
  30. }
  31. var (
  32. ErrBadProxyLine error = &proxyLineError{}
  33. // TODO(golang/go#4373): replace this with the stdlib ErrNetClosing
  34. ErrNetClosing = errors.New("use of closed network connection")
  35. )
  36. // ListenerConfig is all the information about how to process
  37. // incoming IRC connections on a listener.
  38. type ListenerConfig struct {
  39. TLSConfig *tls.Config
  40. ProxyDeadline time.Duration
  41. RequireProxy bool
  42. // these are just metadata for easier tracking,
  43. // they are not used by ReloadableListener:
  44. Tor bool
  45. STSOnly bool
  46. WebSocket bool
  47. }
  48. // read a PROXY line one byte at a time, to ensure we don't read anything beyond
  49. // that into a buffer, which would break the TLS handshake
  50. func readRawProxyLine(conn net.Conn, deadline time.Duration) (result string) {
  51. // normally this is covered by ping timeouts, but we're doing this outside
  52. // of the normal client goroutine:
  53. conn.SetDeadline(time.Now().Add(deadline))
  54. defer conn.SetDeadline(time.Time{})
  55. var buf [maxProxyLineLen]byte
  56. oneByte := make([]byte, 1)
  57. i := 0
  58. for i < maxProxyLineLen {
  59. n, err := conn.Read(oneByte)
  60. if err != nil {
  61. return
  62. } else if n == 1 {
  63. buf[i] = oneByte[0]
  64. if buf[i] == '\n' {
  65. candidate := string(buf[0 : i+1])
  66. if strings.HasPrefix(candidate, "PROXY") {
  67. return candidate
  68. } else {
  69. return
  70. }
  71. }
  72. i += 1
  73. }
  74. }
  75. // no \r\n, fail out
  76. return
  77. }
  78. // ParseProxyLine parses a PROXY protocol (v1) line and returns the remote IP.
  79. func ParseProxyLine(line string) (ip net.IP, err error) {
  80. params := strings.Fields(line)
  81. if len(params) != 6 || params[0] != "PROXY" {
  82. return nil, ErrBadProxyLine
  83. }
  84. ip = net.ParseIP(params[2])
  85. if ip == nil {
  86. return nil, ErrBadProxyLine
  87. }
  88. return ip.To16(), nil
  89. }
  90. /// WrappedConn is a net.Conn with some additional data stapled to it;
  91. // the proxied IP, if one was read via the PROXY protocol, and the listener
  92. // configuration.
  93. type WrappedConn struct {
  94. net.Conn
  95. ProxiedIP net.IP
  96. Config ListenerConfig
  97. // Secure indicates whether we believe the connection between us and the client
  98. // was secure against interception and modification (including all proxies):
  99. Secure bool
  100. }
  101. // ReloadableListener is a wrapper for net.Listener that allows reloading
  102. // of config data for postprocessing connections (TLS, PROXY protocol, etc.)
  103. type ReloadableListener struct {
  104. // TODO: make this lock-free
  105. sync.Mutex
  106. realListener net.Listener
  107. config ListenerConfig
  108. isClosed bool
  109. }
  110. func NewReloadableListener(realListener net.Listener, config ListenerConfig) *ReloadableListener {
  111. return &ReloadableListener{
  112. realListener: realListener,
  113. config: config,
  114. }
  115. }
  116. func (rl *ReloadableListener) Reload(config ListenerConfig) {
  117. rl.Lock()
  118. rl.config = config
  119. rl.Unlock()
  120. }
  121. func (rl *ReloadableListener) Accept() (conn net.Conn, err error) {
  122. conn, err = rl.realListener.Accept()
  123. rl.Lock()
  124. config := rl.config
  125. isClosed := rl.isClosed
  126. rl.Unlock()
  127. if isClosed {
  128. if err == nil {
  129. conn.Close()
  130. }
  131. err = ErrNetClosing
  132. }
  133. if err != nil {
  134. return nil, err
  135. }
  136. var proxiedIP net.IP
  137. if config.RequireProxy {
  138. // this will occur synchronously on the goroutine calling Accept(),
  139. // but that's OK because this listener *requires* a PROXY line,
  140. // therefore it must be used with proxies that always send the line
  141. // and we won't get slowloris'ed waiting for the client response
  142. proxyLine := readRawProxyLine(conn, config.ProxyDeadline)
  143. proxiedIP, err = ParseProxyLine(proxyLine)
  144. if err != nil {
  145. conn.Close()
  146. return nil, err
  147. }
  148. }
  149. if config.TLSConfig != nil {
  150. conn = tls.Server(conn, config.TLSConfig)
  151. }
  152. return &WrappedConn{
  153. Conn: conn,
  154. ProxiedIP: proxiedIP,
  155. Config: config,
  156. }, nil
  157. }
  158. func (rl *ReloadableListener) Close() error {
  159. rl.Lock()
  160. rl.isClosed = true
  161. rl.Unlock()
  162. return rl.realListener.Close()
  163. }
  164. func (rl *ReloadableListener) Addr() net.Addr {
  165. return rl.realListener.Addr()
  166. }