// 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 ) 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 }