Support Proxy protocol (#12527)
	
		
	
				
					
				
			This PR adds functionality to allow Gitea to sit behind an HAProxy and HAProxy protocolled connections directly. Fix #7508 Signed-off-by: Andrew Thornton <art27@cantab.net>tokarchuk/v1.18
							parent
							
								
									0b4c166e8a
								
							
						
					
					
						commit
						943753f560
					
				@ -0,0 +1,506 @@ | 
				
			||||
// Copyright 2020 The Gitea Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a MIT-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
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 | 
				
			||||
} | 
				
			||||
@ -0,0 +1,45 @@ | 
				
			||||
// Copyright 2020 The Gitea Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a MIT-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
package proxyprotocol | 
				
			||||
 | 
				
			||||
import "fmt" | 
				
			||||
 | 
				
			||||
// ErrBadHeader is an error demonstrating a bad proxy header
 | 
				
			||||
type ErrBadHeader struct { | 
				
			||||
	Header []byte | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (e *ErrBadHeader) Error() string { | 
				
			||||
	return fmt.Sprintf("Unexpected proxy header: %v", e.Header) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ErrBadAddressType is an error demonstrating a bad proxy header with bad Address type
 | 
				
			||||
type ErrBadAddressType struct { | 
				
			||||
	Address string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (e *ErrBadAddressType) Error() string { | 
				
			||||
	return fmt.Sprintf("Unexpected proxy header address type: %s", e.Address) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ErrBadRemote is an error demonstrating a bad proxy header with bad Remote
 | 
				
			||||
type ErrBadRemote struct { | 
				
			||||
	IP   string | 
				
			||||
	Port string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (e *ErrBadRemote) Error() string { | 
				
			||||
	return fmt.Sprintf("Unexpected proxy header remote IP and port: %s %s", e.IP, e.Port) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// ErrBadLocal is an error demonstrating a bad proxy header with bad Local
 | 
				
			||||
type ErrBadLocal struct { | 
				
			||||
	IP   string | 
				
			||||
	Port string | 
				
			||||
} | 
				
			||||
 | 
				
			||||
func (e *ErrBadLocal) Error() string { | 
				
			||||
	return fmt.Sprintf("Unexpected proxy header local IP and port: %s %s", e.IP, e.Port) | 
				
			||||
} | 
				
			||||
@ -0,0 +1,47 @@ | 
				
			||||
// Copyright 2020 The Gitea Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a MIT-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
package proxyprotocol | 
				
			||||
 | 
				
			||||
import ( | 
				
			||||
	"net" | 
				
			||||
	"time" | 
				
			||||
) | 
				
			||||
 | 
				
			||||
// Listener is used to wrap an underlying listener,
 | 
				
			||||
// whose connections may be using the HAProxy Proxy Protocol (version 1 or 2).
 | 
				
			||||
// If the connection is using the protocol, the RemoteAddr() will return
 | 
				
			||||
// the correct client address.
 | 
				
			||||
//
 | 
				
			||||
// Optionally define ProxyHeaderTimeout to set a maximum time to
 | 
				
			||||
// receive the Proxy Protocol Header. Zero means no timeout.
 | 
				
			||||
type Listener struct { | 
				
			||||
	Listener           net.Listener | 
				
			||||
	ProxyHeaderTimeout time.Duration | 
				
			||||
	AcceptUnknown      bool // allow PROXY UNKNOWN
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Accept implements the Accept method in the Listener interface
 | 
				
			||||
// it waits for the next call and returns a wrapped Conn.
 | 
				
			||||
func (p *Listener) Accept() (net.Conn, error) { | 
				
			||||
	// Get the underlying connection
 | 
				
			||||
	conn, err := p.Listener.Accept() | 
				
			||||
	if err != nil { | 
				
			||||
		return nil, err | 
				
			||||
	} | 
				
			||||
 | 
				
			||||
	newConn := NewConn(conn, p.ProxyHeaderTimeout) | 
				
			||||
	newConn.acceptUnknown = p.AcceptUnknown | 
				
			||||
	return newConn, nil | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Close closes the underlying listener.
 | 
				
			||||
func (p *Listener) Close() error { | 
				
			||||
	return p.Listener.Close() | 
				
			||||
} | 
				
			||||
 | 
				
			||||
// Addr returns the underlying listener's network address.
 | 
				
			||||
func (p *Listener) Addr() net.Addr { | 
				
			||||
	return p.Listener.Addr() | 
				
			||||
} | 
				
			||||
@ -0,0 +1,15 @@ | 
				
			||||
// Copyright 2020 The Gitea Authors. All rights reserved.
 | 
				
			||||
// Use of this source code is governed by a MIT-style
 | 
				
			||||
// license that can be found in the LICENSE file.
 | 
				
			||||
 | 
				
			||||
package proxyprotocol | 
				
			||||
 | 
				
			||||
import "io" | 
				
			||||
 | 
				
			||||
var localHeader = append(v2Prefix, '\x20', '\x00', '\x00', '\x00', '\x00') | 
				
			||||
 | 
				
			||||
// WriteLocalHeader will write the ProxyProtocol Header for a local connection to the provided writer
 | 
				
			||||
func WriteLocalHeader(w io.Writer) error { | 
				
			||||
	_, err := w.Write(localHeader) | 
				
			||||
	return err | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue