123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- // Copyright 2020 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package proxyprotocol
-
- import (
- "bufio"
- "bytes"
- "encoding/binary"
- "io"
- "net"
- "strconv"
- "strings"
- "sync"
- "time"
-
- "code.gitea.io/gitea/modules/log"
- )
-
- var (
- // v1Prefix is the string we look for at the start of a connection
- // to check if this connection is using the proxy protocol
- v1Prefix = []byte("PROXY ")
- v1PrefixLen = len(v1Prefix)
- v2Prefix = []byte("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A")
- v2PrefixLen = len(v2Prefix)
- )
-
- // Conn is used to wrap and underlying connection which is speaking the
- // Proxy Protocol. RemoteAddr() will return the address of the client
- // instead of the proxy address.
- type Conn struct {
- bufReader *bufio.Reader
- conn net.Conn
- localAddr net.Addr
- remoteAddr net.Addr
- once sync.Once
- proxyHeaderTimeout time.Duration
- acceptUnknown bool
- }
-
- // NewConn is used to wrap a net.Conn speaking the proxy protocol into
- // a proxyprotocol.Conn
- func NewConn(conn net.Conn, timeout time.Duration) *Conn {
- pConn := &Conn{
- bufReader: bufio.NewReader(conn),
- conn: conn,
- proxyHeaderTimeout: timeout,
- }
- return pConn
- }
-
- // Read reads data from the connection.
- // It will initially read the proxy protocol header.
- // If there is an error parsing the header, it is returned and the socket is closed.
- func (p *Conn) Read(b []byte) (int, error) {
- if err := p.readProxyHeaderOnce(); err != nil {
- return 0, err
- }
- return p.bufReader.Read(b)
- }
-
- // ReadFrom reads data from a provided reader and copies it to the connection.
- func (p *Conn) ReadFrom(r io.Reader) (int64, error) {
- if err := p.readProxyHeaderOnce(); err != nil {
- return 0, err
- }
- if rf, ok := p.conn.(io.ReaderFrom); ok {
- return rf.ReadFrom(r)
- }
- return io.Copy(p.conn, r)
- }
-
- // WriteTo reads data from the connection and writes it to the writer.
- // It will initially read the proxy protocol header.
- // If there is an error parsing the header, it is returned and the socket is closed.
- func (p *Conn) WriteTo(w io.Writer) (int64, error) {
- if err := p.readProxyHeaderOnce(); err != nil {
- return 0, err
- }
- return p.bufReader.WriteTo(w)
- }
-
- // Write writes data to the connection.
- // Write can be made to time out and return an error after a fixed
- // time limit; see SetDeadline and SetWriteDeadline.
- func (p *Conn) Write(b []byte) (int, error) {
- if err := p.readProxyHeaderOnce(); err != nil {
- return 0, err
- }
- return p.conn.Write(b)
- }
-
- // Close closes the connection.
- // Any blocked Read or Write operations will be unblocked and return errors.
- func (p *Conn) Close() error {
- return p.conn.Close()
- }
-
- // LocalAddr returns the local network address.
- func (p *Conn) LocalAddr() net.Addr {
- _ = p.readProxyHeaderOnce()
- if p.localAddr != nil {
- return p.localAddr
- }
- return p.conn.LocalAddr()
- }
-
- // RemoteAddr returns the address of the client if the proxy
- // protocol is being used, otherwise just returns the address of
- // the socket peer. If there is an error parsing the header, the
- // address of the client is not returned, and the socket is closed.
- // One implication of this is that the call could block if the
- // client is slow. Using a Deadline is recommended if this is called
- // before Read()
- func (p *Conn) RemoteAddr() net.Addr {
- _ = p.readProxyHeaderOnce()
- if p.remoteAddr != nil {
- return p.remoteAddr
- }
- return p.conn.RemoteAddr()
- }
-
- // SetDeadline sets the read and write deadlines associated
- // with the connection. It is equivalent to calling both
- // SetReadDeadline and SetWriteDeadline.
- //
- // A deadline is an absolute time after which I/O operations
- // fail instead of blocking. The deadline applies to all future
- // and pending I/O, not just the immediately following call to
- // Read or Write. After a deadline has been exceeded, the
- // connection can be refreshed by setting a deadline in the future.
- //
- // If the deadline is exceeded a call to Read or Write or to other
- // I/O methods will return an error that wraps os.ErrDeadlineExceeded.
- // This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
- // The error's Timeout method will return true, but note that there
- // are other possible errors for which the Timeout method will
- // return true even if the deadline has not been exceeded.
- //
- // An idle timeout can be implemented by repeatedly extending
- // the deadline after successful Read or Write calls.
- //
- // A zero value for t means I/O operations will not time out.
- func (p *Conn) SetDeadline(t time.Time) error {
- return p.conn.SetDeadline(t)
- }
-
- // SetReadDeadline sets the deadline for future Read calls
- // and any currently-blocked Read call.
- // A zero value for t means Read will not time out.
- func (p *Conn) SetReadDeadline(t time.Time) error {
- return p.conn.SetReadDeadline(t)
- }
-
- // SetWriteDeadline sets the deadline for future Write calls
- // and any currently-blocked Write call.
- // Even if write times out, it may return n > 0, indicating that
- // some of the data was successfully written.
- // A zero value for t means Write will not time out.
- func (p *Conn) SetWriteDeadline(t time.Time) error {
- return p.conn.SetWriteDeadline(t)
- }
-
- // readProxyHeaderOnce will ensure that the proxy header has been read
- func (p *Conn) readProxyHeaderOnce() (err error) {
- p.once.Do(func() {
- if err = p.readProxyHeader(); err != nil && err != io.EOF {
- log.Error("Failed to read proxy prefix: %v", err)
- p.Close()
- p.bufReader = bufio.NewReader(p.conn)
- }
- })
- return err
- }
-
- func (p *Conn) readProxyHeader() error {
- if p.proxyHeaderTimeout != 0 {
- readDeadLine := time.Now().Add(p.proxyHeaderTimeout)
- _ = p.conn.SetReadDeadline(readDeadLine)
- defer func() {
- _ = p.conn.SetReadDeadline(time.Time{})
- }()
- }
-
- inp, err := p.bufReader.Peek(v1PrefixLen)
- if err != nil {
- return err
- }
-
- if bytes.Equal(inp, v1Prefix) {
- return p.readV1ProxyHeader()
- }
-
- inp, err = p.bufReader.Peek(v2PrefixLen)
- if err != nil {
- return err
- }
- if bytes.Equal(inp, v2Prefix) {
- return p.readV2ProxyHeader()
- }
-
- return &ErrBadHeader{inp}
- }
-
- func (p *Conn) readV2ProxyHeader() error {
- // The binary header format starts with a constant 12 bytes block containing the
- // protocol signature :
- //
- // \x0D \x0A \x0D \x0A \x00 \x0D \x0A \x51 \x55 \x49 \x54 \x0A
- //
- // Note that this block contains a null byte at the 5th position, so it must not
- // be handled as a null-terminated string.
-
- if _, err := p.bufReader.Discard(v2PrefixLen); err != nil {
- // This shouldn't happen as we have already asserted that there should be enough in the buffer
- return err
- }
-
- // The next byte (the 13th one) is the protocol version and command.
- version, err := p.bufReader.ReadByte()
- if err != nil {
- return err
- }
-
- // The 14th byte contains the transport protocol and address family.otocol.
- familyByte, err := p.bufReader.ReadByte()
- if err != nil {
- return err
- }
-
- // The 15th and 16th bytes is the address length in bytes in network endian order.
- var addressLen uint16
- if err := binary.Read(p.bufReader, binary.BigEndian, &addressLen); err != nil {
- return err
- }
-
- // Now handle the version byte: (14th byte).
- // The highest four bits contains the version. As of this specification, it must
- // always be sent as \x2 and the receiver must only accept this value.
- if version>>4 != 0x2 {
- return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
- }
-
- // The lowest four bits represents the command :
- switch version & 0xf {
- case 0x0:
- // - \x0 : LOCAL : the connection was established on purpose by the proxy
- // without being relayed. The connection endpoints are the sender and the
- // receiver. Such connections exist when the proxy sends health-checks to the
- // server. The receiver must accept this connection as valid and must use the
- // real connection endpoints and discard the protocol block including the
- // family which is ignored.
-
- // We therefore ignore the 14th, 15th and 16th bytes
- p.remoteAddr = p.conn.LocalAddr()
- p.localAddr = p.conn.RemoteAddr()
- return nil
- case 0x1:
- // - \x1 : PROXY : the connection was established on behalf of another node,
- // and reflects the original connection endpoints. The receiver must then use
- // the information provided in the protocol block to get original the address.
- default:
- // - other values are unassigned and must not be emitted by senders. Receivers
- // must drop connections presenting unexpected values here.
- return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
- }
-
- // Now handle the familyByte byte: (15th byte).
- // The highest 4 bits contain the address family, the lowest 4 bits contain the protocol
-
- // The address family maps to the original socket family without necessarily
- // matching the values internally used by the system. It may be one of :
- //
- // - 0x0 : AF_UNSPEC : the connection is forwarded for an unknown, unspecified
- // or unsupported protocol. The sender should use this family when sending
- // LOCAL commands or when dealing with unsupported protocol families. The
- // receiver is free to accept the connection anyway and use the real endpoint
- // addresses or to reject it. The receiver should ignore address information.
- //
- // - 0x1 : AF_INET : the forwarded connection uses the AF_INET address family
- // (IPv4). The addresses are exactly 4 bytes each in network byte order,
- // followed by transport protocol information (typically ports).
- //
- // - 0x2 : AF_INET6 : the forwarded connection uses the AF_INET6 address family
- // (IPv6). The addresses are exactly 16 bytes each in network byte order,
- // followed by transport protocol information (typically ports).
- //
- // - 0x3 : AF_UNIX : the forwarded connection uses the AF_UNIX address family
- // (UNIX). The addresses are exactly 108 bytes each.
- //
- // - other values are unspecified and must not be emitted in version 2 of this
- // protocol and must be rejected as invalid by receivers.
-
- // The transport protocol is specified in the lowest 4 bits of the 14th byte :
- //
- // - 0x0 : UNSPEC : the connection is forwarded for an unknown, unspecified
- // or unsupported protocol. The sender should use this family when sending
- // LOCAL commands or when dealing with unsupported protocol families. The
- // receiver is free to accept the connection anyway and use the real endpoint
- // addresses or to reject it. The receiver should ignore address information.
- //
- // - 0x1 : STREAM : the forwarded connection uses a SOCK_STREAM protocol (eg:
- // TCP or UNIX_STREAM). When used with AF_INET/AF_INET6 (TCP), the addresses
- // are followed by the source and destination ports represented on 2 bytes
- // each in network byte order.
- //
- // - 0x2 : DGRAM : the forwarded connection uses a SOCK_DGRAM protocol (eg:
- // UDP or UNIX_DGRAM). When used with AF_INET/AF_INET6 (UDP), the addresses
- // are followed by the source and destination ports represented on 2 bytes
- // each in network byte order.
- //
- // - other values are unspecified and must not be emitted in version 2 of this
- // protocol and must be rejected as invalid by receivers.
-
- if familyByte>>4 == 0x0 || familyByte&0xf == 0x0 {
- // - hi 0x0 : AF_UNSPEC : the connection is forwarded for an unknown address type
- // or
- // - lo 0x0 : UNSPEC : the connection is forwarded for an unspecified protocol
- if !p.acceptUnknown {
- p.conn.Close()
- return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
- }
- p.remoteAddr = p.conn.LocalAddr()
- p.localAddr = p.conn.RemoteAddr()
- _, err = p.bufReader.Discard(int(addressLen))
- return err
- }
-
- // other address or protocol
- if (familyByte>>4) > 0x3 || (familyByte&0xf) > 0x2 {
- return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
- }
-
- // Handle AF_UNIX addresses
- if familyByte>>4 == 0x3 {
- // - \x31 : UNIX stream : the forwarded connection uses SOCK_STREAM over the
- // AF_UNIX protocol family. Address length is 2*108 = 216 bytes.
- // - \x32 : UNIX datagram : the forwarded connection uses SOCK_DGRAM over the
- // AF_UNIX protocol family. Address length is 2*108 = 216 bytes.
- if addressLen != 216 {
- return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
- }
- remoteName := make([]byte, 108)
- localName := make([]byte, 108)
- if _, err := p.bufReader.Read(remoteName); err != nil {
- return err
- }
- if _, err := p.bufReader.Read(localName); err != nil {
- return err
- }
- protocol := "unix"
- if familyByte&0xf == 2 {
- protocol = "unixgram"
- }
-
- p.remoteAddr = &net.UnixAddr{
- Name: string(remoteName),
- Net: protocol,
- }
- p.localAddr = &net.UnixAddr{
- Name: string(localName),
- Net: protocol,
- }
- return nil
- }
-
- var remoteIP []byte
- var localIP []byte
- var remotePort uint16
- var localPort uint16
-
- if familyByte>>4 == 0x1 {
- // AF_INET
- // - \x11 : TCP over IPv4 : the forwarded connection uses TCP over the AF_INET
- // protocol family. Address length is 2*4 + 2*2 = 12 bytes.
- // - \x12 : UDP over IPv4 : the forwarded connection uses UDP over the AF_INET
- // protocol family. Address length is 2*4 + 2*2 = 12 bytes.
- if addressLen != 12 {
- return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
- }
-
- remoteIP = make([]byte, 4)
- localIP = make([]byte, 4)
- } else {
- // AF_INET6
- // - \x21 : TCP over IPv6 : the forwarded connection uses TCP over the AF_INET6
- // protocol family. Address length is 2*16 + 2*2 = 36 bytes.
- // - \x22 : UDP over IPv6 : the forwarded connection uses UDP over the AF_INET6
- // protocol family. Address length is 2*16 + 2*2 = 36 bytes.
- if addressLen != 36 {
- return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
- }
-
- remoteIP = make([]byte, 16)
- localIP = make([]byte, 16)
- }
-
- if _, err := p.bufReader.Read(remoteIP); err != nil {
- return err
- }
- if _, err := p.bufReader.Read(localIP); err != nil {
- return err
- }
- if err := binary.Read(p.bufReader, binary.BigEndian, &remotePort); err != nil {
- return err
- }
- if err := binary.Read(p.bufReader, binary.BigEndian, &localPort); err != nil {
- return err
- }
-
- if familyByte&0xf == 1 {
- p.remoteAddr = &net.TCPAddr{
- IP: remoteIP,
- Port: int(remotePort),
- }
- p.localAddr = &net.TCPAddr{
- IP: localIP,
- Port: int(localPort),
- }
- } else {
- p.remoteAddr = &net.UDPAddr{
- IP: remoteIP,
- Port: int(remotePort),
- }
- p.localAddr = &net.UDPAddr{
- IP: localIP,
- Port: int(localPort),
- }
- }
- return nil
- }
-
- func (p *Conn) readV1ProxyHeader() error {
- // Read until a newline
- header, err := p.bufReader.ReadString('\n')
- if err != nil {
- p.conn.Close()
- return err
- }
-
- if header[len(header)-2] != '\r' {
- return &ErrBadHeader{[]byte(header)}
- }
-
- // Strip the carriage return and new line
- header = header[:len(header)-2]
-
- // Split on spaces, should be (PROXY <type> <remote addr> <local addr> <remote port> <local port>)
- parts := strings.Split(header, " ")
- if len(parts) < 2 {
- p.conn.Close()
- return &ErrBadHeader{[]byte(header)}
- }
-
- // Verify the type is known
- switch parts[1] {
- case "UNKNOWN":
- if !p.acceptUnknown || len(parts) != 2 {
- p.conn.Close()
- return &ErrBadHeader{[]byte(header)}
- }
- p.remoteAddr = p.conn.LocalAddr()
- p.localAddr = p.conn.RemoteAddr()
- return nil
- case "TCP4":
- case "TCP6":
- default:
- p.conn.Close()
- return &ErrBadAddressType{parts[1]}
- }
-
- if len(parts) != 6 {
- p.conn.Close()
- return &ErrBadHeader{[]byte(header)}
- }
-
- // Parse out the remote address
- ip := net.ParseIP(parts[2])
- if ip == nil {
- p.conn.Close()
- return &ErrBadRemote{parts[2], parts[4]}
- }
- port, err := strconv.Atoi(parts[4])
- if err != nil {
- p.conn.Close()
- return &ErrBadRemote{parts[2], parts[4]}
- }
- p.remoteAddr = &net.TCPAddr{IP: ip, Port: port}
-
- // Parse out the destination address
- ip = net.ParseIP(parts[3])
- if ip == nil {
- p.conn.Close()
- return &ErrBadLocal{parts[3], parts[5]}
- }
- port, err = strconv.Atoi(parts[5])
- if err != nil {
- p.conn.Close()
- return &ErrBadLocal{parts[3], parts[5]}
- }
- p.localAddr = &net.TCPAddr{IP: ip, Port: port}
-
- return nil
- }
|