You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

proxy.go 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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. "encoding/binary"
  7. "errors"
  8. "io"
  9. "net"
  10. "strings"
  11. "sync"
  12. "time"
  13. )
  14. const (
  15. // https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
  16. // "a 108-byte buffer is always enough to store all the line and a trailing zero
  17. // for string processing."
  18. maxProxyLineLenV1 = 107
  19. )
  20. // XXX implement net.Error with a Temporary() method that returns true;
  21. // otherwise, ErrBadProxyLine will cause (*http.Server).Serve() to exit
  22. type proxyLineError struct{}
  23. func (p *proxyLineError) Error() string {
  24. return "invalid PROXY line"
  25. }
  26. func (p *proxyLineError) Timeout() bool {
  27. return false
  28. }
  29. func (p *proxyLineError) Temporary() bool {
  30. return true
  31. }
  32. var (
  33. ErrBadProxyLine error = &proxyLineError{}
  34. // TODO(golang/go#4373): replace this with the stdlib ErrNetClosing
  35. ErrNetClosing = errors.New("use of closed network connection")
  36. )
  37. // ListenerConfig is all the information about how to process
  38. // incoming IRC connections on a listener.
  39. type ListenerConfig struct {
  40. TLSConfig *tls.Config
  41. ProxyDeadline time.Duration
  42. RequireProxy bool
  43. // these are just metadata for easier tracking,
  44. // they are not used by ReloadableListener:
  45. Tor bool
  46. STSOnly bool
  47. WebSocket bool
  48. HideSTS bool
  49. }
  50. // read a PROXY header (either v1 or v2), ensuring we don't read anything beyond
  51. // the header into a buffer (this would break the TLS handshake)
  52. func readRawProxyLine(conn net.Conn, deadline time.Duration) (result []byte, err error) {
  53. // normally this is covered by ping timeouts, but we're doing this outside
  54. // of the normal client goroutine:
  55. conn.SetDeadline(time.Now().Add(deadline))
  56. defer conn.SetDeadline(time.Time{})
  57. // read the first 16 bytes of the proxy header
  58. buf := make([]byte, 16, maxProxyLineLenV1)
  59. _, err = io.ReadFull(conn, buf)
  60. if err != nil {
  61. return
  62. }
  63. switch buf[0] {
  64. case 'P':
  65. // PROXY v1: starts with "PROXY"
  66. return readRawProxyLineV1(conn, buf)
  67. case '\r':
  68. // PROXY v2: starts with "\r\n\r\n"
  69. return readRawProxyLineV2(conn, buf)
  70. default:
  71. return nil, ErrBadProxyLine
  72. }
  73. }
  74. func readRawProxyLineV1(conn net.Conn, buf []byte) (result []byte, err error) {
  75. for {
  76. i := len(buf)
  77. if i >= maxProxyLineLenV1 {
  78. return nil, ErrBadProxyLine // did not find \r\n, fail
  79. }
  80. // prepare a single byte of free space, then read into it
  81. buf = buf[0 : i+1]
  82. _, err = io.ReadFull(conn, buf[i:])
  83. if err != nil {
  84. return nil, err
  85. }
  86. if buf[i] == '\n' {
  87. return buf, nil
  88. }
  89. }
  90. }
  91. func readRawProxyLineV2(conn net.Conn, buf []byte) (result []byte, err error) {
  92. // "The 15th and 16th bytes is the address length in bytes in network endian order."
  93. addrLen := int(binary.BigEndian.Uint16(buf[14:16]))
  94. if addrLen == 0 {
  95. return buf[0:16], nil
  96. } else if addrLen <= cap(buf)-16 {
  97. buf = buf[0 : 16+addrLen]
  98. } else {
  99. // proxy source is unix domain, we don't really handle this
  100. buf2 := make([]byte, 16+addrLen)
  101. copy(buf2[0:16], buf[0:16])
  102. buf = buf2
  103. }
  104. _, err = io.ReadFull(conn, buf[16:16+addrLen])
  105. if err != nil {
  106. return
  107. }
  108. return buf[0 : 16+addrLen], nil
  109. }
  110. // ParseProxyLine parses a PROXY protocol (v1 or v2) line and returns the remote IP.
  111. func ParseProxyLine(line []byte) (ip net.IP, err error) {
  112. if len(line) == 0 {
  113. return nil, ErrBadProxyLine
  114. }
  115. switch line[0] {
  116. case 'P':
  117. return ParseProxyLineV1(string(line))
  118. case '\r':
  119. return parseProxyLineV2(line)
  120. default:
  121. return nil, ErrBadProxyLine
  122. }
  123. }
  124. // ParseProxyLineV1 parses a PROXY protocol (v1) line and returns the remote IP.
  125. func ParseProxyLineV1(line string) (ip net.IP, err error) {
  126. params := strings.Fields(line)
  127. if len(params) != 6 || params[0] != "PROXY" {
  128. return nil, ErrBadProxyLine
  129. }
  130. ip = net.ParseIP(params[2])
  131. if ip == nil {
  132. return nil, ErrBadProxyLine
  133. }
  134. return ip.To16(), nil
  135. }
  136. func parseProxyLineV2(line []byte) (ip net.IP, err error) {
  137. if len(line) < 16 {
  138. return nil, ErrBadProxyLine
  139. }
  140. // this doesn't allocate
  141. if string(line[:12]) != "\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a" {
  142. return nil, ErrBadProxyLine
  143. }
  144. // "The next byte (the 13th one) is the protocol version and command."
  145. versionCmd := line[12]
  146. // "The highest four bits contains the version [....] it must always be sent as \x2"
  147. if (versionCmd >> 4) != 2 {
  148. return nil, ErrBadProxyLine
  149. }
  150. // "The lowest four bits represents the command"
  151. switch versionCmd & 0x0f {
  152. case 0:
  153. return nil, nil // LOCAL command
  154. case 1:
  155. // PROXY command, continue below
  156. default:
  157. // "Receivers must drop connections presenting unexpected values here"
  158. return nil, ErrBadProxyLine
  159. }
  160. var addrLen int
  161. // "The 14th byte contains the transport protocol and address family."
  162. protoAddr := line[13]
  163. // "The highest 4 bits contain the address family"
  164. switch protoAddr >> 4 {
  165. case 1:
  166. addrLen = 4 // AF_INET
  167. case 2:
  168. addrLen = 16 // AF_INET6
  169. default:
  170. return nil, nil // AF_UNSPEC or AF_UNIX, either way there's no IP address
  171. }
  172. // header, source and destination address, two 16-bit port numbers:
  173. expectedLen := 16 + 2*addrLen + 4
  174. if len(line) < expectedLen {
  175. return nil, ErrBadProxyLine
  176. }
  177. // "Starting from the 17th byte, addresses are presented in network byte order.
  178. // The address order is always the same :
  179. // - source layer 3 address in network byte order [...]"
  180. if addrLen == 4 {
  181. ip = net.IP(line[16 : 16+addrLen]).To16()
  182. } else {
  183. ip = make(net.IP, addrLen)
  184. copy(ip, line[16:16+addrLen])
  185. }
  186. return ip, nil
  187. }
  188. /// WrappedConn is a net.Conn with some additional data stapled to it;
  189. // the proxied IP, if one was read via the PROXY protocol, and the listener
  190. // configuration.
  191. type WrappedConn struct {
  192. net.Conn
  193. ProxiedIP net.IP
  194. Config ListenerConfig
  195. // Secure indicates whether we believe the connection between us and the client
  196. // was secure against interception and modification (including all proxies):
  197. Secure bool
  198. }
  199. // ReloadableListener is a wrapper for net.Listener that allows reloading
  200. // of config data for postprocessing connections (TLS, PROXY protocol, etc.)
  201. type ReloadableListener struct {
  202. // TODO: make this lock-free
  203. sync.Mutex
  204. realListener net.Listener
  205. config ListenerConfig
  206. isClosed bool
  207. }
  208. func NewReloadableListener(realListener net.Listener, config ListenerConfig) *ReloadableListener {
  209. return &ReloadableListener{
  210. realListener: realListener,
  211. config: config,
  212. }
  213. }
  214. func (rl *ReloadableListener) Reload(config ListenerConfig) {
  215. rl.Lock()
  216. rl.config = config
  217. rl.Unlock()
  218. }
  219. func (rl *ReloadableListener) Accept() (conn net.Conn, err error) {
  220. conn, err = rl.realListener.Accept()
  221. rl.Lock()
  222. config := rl.config
  223. isClosed := rl.isClosed
  224. rl.Unlock()
  225. if isClosed {
  226. if err == nil {
  227. conn.Close()
  228. }
  229. err = ErrNetClosing
  230. }
  231. if err != nil {
  232. return nil, err
  233. }
  234. var proxiedIP net.IP
  235. if config.RequireProxy {
  236. // this will occur synchronously on the goroutine calling Accept(),
  237. // but that's OK because this listener *requires* a PROXY line,
  238. // therefore it must be used with proxies that always send the line
  239. // and we won't get slowloris'ed waiting for the client response
  240. proxyLine, err := readRawProxyLine(conn, config.ProxyDeadline)
  241. if err == nil {
  242. proxiedIP, err = ParseProxyLine(proxyLine)
  243. }
  244. if err != nil {
  245. conn.Close()
  246. return nil, err
  247. }
  248. }
  249. if config.TLSConfig != nil {
  250. conn = tls.Server(conn, config.TLSConfig)
  251. }
  252. return &WrappedConn{
  253. Conn: conn,
  254. ProxiedIP: proxiedIP,
  255. Config: config,
  256. }, nil
  257. }
  258. func (rl *ReloadableListener) Close() error {
  259. rl.Lock()
  260. rl.isClosed = true
  261. rl.Unlock()
  262. return rl.realListener.Close()
  263. }
  264. func (rl *ReloadableListener) Addr() net.Addr {
  265. return rl.realListener.Addr()
  266. }