pax_global_header00006660000000000000000000000064146503425070014520gustar00rootroot0000000000000052 comment=287e90ab238d9eadd14c811a8a2bc40310412e52 golang-github-armon-go-proxyproto-0.1.0/000077500000000000000000000000001465034250700202255ustar00rootroot00000000000000golang-github-armon-go-proxyproto-0.1.0/.gitignore000066400000000000000000000000121465034250700222060ustar00rootroot00000000000000*.test *~ golang-github-armon-go-proxyproto-0.1.0/LICENSE000066400000000000000000000020661465034250700212360ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Armon Dadgar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.golang-github-armon-go-proxyproto-0.1.0/README.md000066400000000000000000000022361465034250700215070ustar00rootroot00000000000000# proxyproto This library provides the `proxyproto` package which can be used for servers listening behind HAProxy of Amazon ELB load balancers. Those load balancers support the use of a proxy protocol (http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt), which provides a simple mechansim for the server to get the address of the client instead of the load balancer. This library provides both a net.Listener and net.Conn implementation that can be used to handle situation in which you may be using the proxy protocol. Only proxy protocol version 1, the human-readable form, is understood. The only caveat is that we check for the "PROXY " prefix to determine if the protocol is being used. If that string may occur as part of your input, then it is ambiguous if the protocol is being used and you may have problems. # Documentation Full documentation can be found [here](http://godoc.org/github.com/armon/go-proxyproto). # Examples Using the library is very simple: ``` // Create a listener list, err := net.Listen("tcp", "...") // Wrap listener in a proxyproto listener proxyList := &proxyproto.Listener{Listener: list} conn, err :=proxyList.Accept() ... ``` golang-github-armon-go-proxyproto-0.1.0/go.mod000066400000000000000000000000571465034250700213350ustar00rootroot00000000000000module github.com/armon/go-proxyproto go 1.20 golang-github-armon-go-proxyproto-0.1.0/protocol.go000066400000000000000000000163751465034250700224310ustar00rootroot00000000000000package proxyproto import ( "bufio" "bytes" "errors" "fmt" "io" "log" "net" "strconv" "strings" "sync" "time" ) var ( // prefix is the string we look for at the start of a connection // to check if this connection is using the proxy protocol prefix = []byte("PROXY ") prefixLen = len(prefix) ErrInvalidUpstream = errors.New("upstream connection address not trusted for PROXY information") ) // SourceChecker can be used to decide whether to trust the PROXY info or pass // the original connection address through. If set, the connecting address is // passed in as an argument. If the function returns an error due to the source // being disallowed, it should return ErrInvalidUpstream. // // If error is not nil, the call to Accept() will fail. If the reason for // triggering this failure is due to a disallowed source, it should return // ErrInvalidUpstream. // // If bool is true, the PROXY-set address is used. // // If bool is false, the connection's remote address is used, rather than the // address claimed in the PROXY info. type SourceChecker func(net.Addr) (bool, error) // Listener is used to wrap an underlying listener, // whose connections may be using the HAProxy Proxy Protocol (version 1). // 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 SourceCheck SourceChecker UnknownOK bool // allow PROXY UNKNOWN } // Conn is used to wrap and underlying connection which // may be speaking the Proxy Protocol. If it is, the RemoteAddr() will // return the address of the client instead of the proxy address. type Conn struct { bufReader *bufio.Reader conn net.Conn dstAddr *net.TCPAddr srcAddr *net.TCPAddr useConnAddr bool once sync.Once proxyHeaderTimeout time.Duration unknownOK bool } // Accept waits for and returns the next connection to the listener. func (p *Listener) Accept() (net.Conn, error) { // Get the underlying connection for { conn, err := p.Listener.Accept() if err != nil { return nil, err } var useConnAddr bool if p.SourceCheck != nil { allowed, err := p.SourceCheck(conn.RemoteAddr()) if err != nil { if err == ErrInvalidUpstream { conn.Close() continue } return nil, err } if !allowed { useConnAddr = true } } newConn := NewConn(conn, p.ProxyHeaderTimeout) newConn.useConnAddr = useConnAddr newConn.unknownOK = p.UnknownOK 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() } // NewConn is used to wrap a net.Conn that may be speaking // the proxy protocol into a proxyproto.Conn func NewConn(conn net.Conn, timeout time.Duration) *Conn { pConn := &Conn{ bufReader: bufio.NewReader(conn), conn: conn, proxyHeaderTimeout: timeout, } return pConn } // Read is check for the proxy protocol header when doing // the initial scan. If there is an error parsing the header, // it is returned and the socket is closed. func (p *Conn) Read(b []byte) (int, error) { var err error p.once.Do(func() { err = p.checkPrefix() }) if err != nil { return 0, err } return p.bufReader.Read(b) } func (p *Conn) ReadFrom(r io.Reader) (int64, error) { if rf, ok := p.conn.(io.ReaderFrom); ok { return rf.ReadFrom(r) } return io.Copy(p.conn, r) } func (p *Conn) WriteTo(w io.Writer) (int64, error) { var err error p.once.Do(func() { err = p.checkPrefix() }) if err != nil { return 0, err } return p.bufReader.WriteTo(w) } func (p *Conn) Write(b []byte) (int, error) { return p.conn.Write(b) } func (p *Conn) Close() error { return p.conn.Close() } func (p *Conn) LocalAddr() net.Addr { p.checkPrefixOnce() if p.dstAddr != nil && !p.useConnAddr { return p.dstAddr } 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. // Once 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.checkPrefixOnce() if p.srcAddr != nil && !p.useConnAddr { return p.srcAddr } return p.conn.RemoteAddr() } func (p *Conn) SetDeadline(t time.Time) error { return p.conn.SetDeadline(t) } func (p *Conn) SetReadDeadline(t time.Time) error { return p.conn.SetReadDeadline(t) } func (p *Conn) SetWriteDeadline(t time.Time) error { return p.conn.SetWriteDeadline(t) } func (p *Conn) checkPrefixOnce() { p.once.Do(func() { if err := p.checkPrefix(); err != nil && err != io.EOF { log.Printf("[ERR] Failed to read proxy prefix: %v", err) p.Close() p.bufReader = bufio.NewReader(p.conn) } }) } func (p *Conn) checkPrefix() error { if p.proxyHeaderTimeout != 0 { readDeadLine := time.Now().Add(p.proxyHeaderTimeout) p.conn.SetReadDeadline(readDeadLine) defer p.conn.SetReadDeadline(time.Time{}) } // Incrementally check each byte of the prefix for i := 1; i <= prefixLen; i++ { inp, err := p.bufReader.Peek(i) if err != nil { if neterr, ok := err.(net.Error); ok && neterr.Timeout() { return nil } else { return err } } // Check for a prefix mis-match, quit early if !bytes.Equal(inp, prefix[:i]) { return nil } } // Read the header line header, err := p.bufReader.ReadString('\n') if err != nil { p.conn.Close() return err } // 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 fmt.Errorf("Invalid header line: %s", header) } // Verify the type is known switch parts[1] { case "UNKNOWN": if !p.unknownOK || len(parts) != 2 { p.conn.Close() return fmt.Errorf("Invalid UNKNOWN header line: %s", header) } p.useConnAddr = true return nil case "TCP4": case "TCP6": default: p.conn.Close() return fmt.Errorf("Unhandled address type: %s", parts[1]) } if len(parts) != 6 { p.conn.Close() return fmt.Errorf("Invalid header line: %s", header) } // Parse out the source address ip := net.ParseIP(parts[2]) if ip == nil { p.conn.Close() return fmt.Errorf("Invalid source ip: %s", parts[2]) } port, err := strconv.Atoi(parts[4]) if err != nil { p.conn.Close() return fmt.Errorf("Invalid source port: %s", parts[4]) } p.srcAddr = &net.TCPAddr{IP: ip, Port: port} // Parse out the destination address ip = net.ParseIP(parts[3]) if ip == nil { p.conn.Close() return fmt.Errorf("Invalid destination ip: %s", parts[3]) } port, err = strconv.Atoi(parts[5]) if err != nil { p.conn.Close() return fmt.Errorf("Invalid destination port: %s", parts[5]) } p.dstAddr = &net.TCPAddr{IP: ip, Port: port} return nil } golang-github-armon-go-proxyproto-0.1.0/protocol_test.go000066400000000000000000000234001465034250700234530ustar00rootroot00000000000000package proxyproto import ( "bytes" "io" "net" "testing" "time" ) const ( goodAddr = "127.0.0.1" badAddr = "127.0.0.2" errAddr = "9999.0.0.2" ) var ( checkAddr string ) func TestPassthrough(t *testing.T) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("err: %v", err) } pl := &Listener{Listener: l} go func() { conn, err := net.Dial("tcp", pl.Addr().String()) if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() conn.Write([]byte("ping")) recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("pong")) { t.Fatalf("bad: %v", recv) } }() conn, err := pl.Accept() if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("ping")) { t.Fatalf("bad: %v", recv) } if _, err := conn.Write([]byte("pong")); err != nil { t.Fatalf("err: %v", err) } } func TestTimeout(t *testing.T) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("err: %v", err) } clientWriteDelay := 200 * time.Millisecond proxyHeaderTimeout := 50 * time.Millisecond pl := &Listener{Listener: l, ProxyHeaderTimeout: proxyHeaderTimeout} go func() { conn, err := net.Dial("tcp", pl.Addr().String()) if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() // Do not send data for a while time.Sleep(clientWriteDelay) conn.Write([]byte("ping")) recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("pong")) { t.Fatalf("bad: %v", recv) } }() conn, err := pl.Accept() if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() // Check the remote addr is the original 127.0.0.1 remoteAddrStartTime := time.Now() addr := conn.RemoteAddr().(*net.TCPAddr) if addr.IP.String() != "127.0.0.1" { t.Fatalf("bad: %v", addr) } remoteAddrDuration := time.Since(remoteAddrStartTime) // Check RemoteAddr() call did timeout if remoteAddrDuration >= clientWriteDelay { t.Fatalf("RemoteAddr() took longer than the specified timeout: %v < %v", proxyHeaderTimeout, remoteAddrDuration) } recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("ping")) { t.Fatalf("bad: %v", recv) } if _, err := conn.Write([]byte("pong")); err != nil { t.Fatalf("err: %v", err) } } func TestParse_ipv4(t *testing.T) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("err: %v", err) } pl := &Listener{Listener: l} go func() { conn, err := net.Dial("tcp", pl.Addr().String()) if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() // Write out the header! header := "PROXY TCP4 10.1.1.1 20.2.2.2 1000 2000\r\n" conn.Write([]byte(header)) conn.Write([]byte("ping")) recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("pong")) { t.Fatalf("bad: %v", recv) } }() conn, err := pl.Accept() if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("ping")) { t.Fatalf("bad: %v", recv) } if _, err := conn.Write([]byte("pong")); err != nil { t.Fatalf("err: %v", err) } // Check the remote addr addr := conn.RemoteAddr().(*net.TCPAddr) if addr.IP.String() != "10.1.1.1" { t.Fatalf("bad: %v", addr) } if addr.Port != 1000 { t.Fatalf("bad: %v", addr) } } func TestParse_ipv6(t *testing.T) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("err: %v", err) } pl := &Listener{Listener: l} go func() { conn, err := net.Dial("tcp", pl.Addr().String()) if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() // Write out the header! header := "PROXY TCP6 ffff::ffff ffff::ffff 1000 2000\r\n" conn.Write([]byte(header)) conn.Write([]byte("ping")) recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("pong")) { t.Fatalf("bad: %v", recv) } }() conn, err := pl.Accept() if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("ping")) { t.Fatalf("bad: %v", recv) } if _, err := conn.Write([]byte("pong")); err != nil { t.Fatalf("err: %v", err) } // Check the remote addr addr := conn.RemoteAddr().(*net.TCPAddr) if addr.IP.String() != "ffff::ffff" { t.Fatalf("bad: %v", addr) } if addr.Port != 1000 { t.Fatalf("bad: %v", addr) } } func TestParse_Unknown(t *testing.T) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("err: %v", err) } pl := &Listener{Listener: l, UnknownOK: true} go func() { conn, err := net.Dial("tcp", pl.Addr().String()) if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() // Write out the header! header := "PROXY UNKNOWN\r\n" conn.Write([]byte(header)) conn.Write([]byte("ping")) recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("pong")) { t.Fatalf("bad: %v", recv) } }() conn, err := pl.Accept() if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("ping")) { t.Fatalf("bad: %v", recv) } if _, err := conn.Write([]byte("pong")); err != nil { t.Fatalf("err: %v", err) } } func TestParse_BadHeader(t *testing.T) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("err: %v", err) } pl := &Listener{Listener: l} go func() { conn, err := net.Dial("tcp", pl.Addr().String()) if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() // Write out the header! header := "PROXY TCP4 what 127.0.0.1 1000 2000\r\n" conn.Write([]byte(header)) conn.Write([]byte("ping")) recv := make([]byte, 4) _, err = conn.Read(recv) if err == nil { t.Fatalf("err: %v", err) } }() conn, err := pl.Accept() if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() // Check the remote addr, should be the local addr addr := conn.RemoteAddr().(*net.TCPAddr) if addr.IP.String() != "127.0.0.1" { t.Fatalf("bad: %v", addr) } // Read should fail recv := make([]byte, 4) _, err = conn.Read(recv) if err == nil { t.Fatalf("err: %v", err) } } func TestParse_ipv4_checkfunc(t *testing.T) { checkAddr = goodAddr testParse_ipv4_checkfunc(t) checkAddr = badAddr testParse_ipv4_checkfunc(t) checkAddr = errAddr testParse_ipv4_checkfunc(t) } func testParse_ipv4_checkfunc(t *testing.T) { l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("err: %v", err) } checkFunc := func(addr net.Addr) (bool, error) { tcpAddr := addr.(*net.TCPAddr) if tcpAddr.IP.String() == checkAddr { return true, nil } return false, nil } pl := &Listener{Listener: l, SourceCheck: checkFunc} go func() { conn, err := net.Dial("tcp", pl.Addr().String()) if err != nil { t.Fatalf("err: %v", err) } defer conn.Close() // Write out the header! header := "PROXY TCP4 10.1.1.1 20.2.2.2 1000 2000\r\n" conn.Write([]byte(header)) conn.Write([]byte("ping")) recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("pong")) { t.Fatalf("bad: %v", recv) } }() conn, err := pl.Accept() if err != nil { if checkAddr == badAddr { return } t.Fatalf("err: %v", err) } defer conn.Close() recv := make([]byte, 4) _, err = conn.Read(recv) if err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(recv, []byte("ping")) { t.Fatalf("bad: %v", recv) } if _, err := conn.Write([]byte("pong")); err != nil { t.Fatalf("err: %v", err) } // Check the remote addr addr := conn.RemoteAddr().(*net.TCPAddr) switch checkAddr { case goodAddr: if addr.IP.String() != "10.1.1.1" { t.Fatalf("bad: %v", addr) } if addr.Port != 1000 { t.Fatalf("bad: %v", addr) } case badAddr: if addr.IP.String() != "127.0.0.1" { t.Fatalf("bad: %v", addr) } if addr.Port == 1000 { t.Fatalf("bad: %v", addr) } } } type testConn struct { readFromCalledWith io.Reader net.Conn // nil; crash on any unexpected use } func (c *testConn) ReadFrom(r io.Reader) (int64, error) { c.readFromCalledWith = r return 0, nil } func (c *testConn) Write(p []byte) (int, error) { return len(p), nil } func (c *testConn) Read(p []byte) (int, error) { return 1, nil } func TestCopyToWrappedConnection(t *testing.T) { innerConn := &testConn{} wrappedConn := NewConn(innerConn, 0) dummySrc := &testConn{} io.Copy(wrappedConn, dummySrc) if innerConn.readFromCalledWith != dummySrc { t.Error("Expected io.Copy to delegate to ReadFrom function of inner destination connection") } } func TestCopyFromWrappedConnection(t *testing.T) { wrappedConn := NewConn(&testConn{}, 0) dummyDst := &testConn{} io.Copy(dummyDst, wrappedConn) if dummyDst.readFromCalledWith != wrappedConn.conn { t.Errorf("Expected io.Copy to pass inner source connection to ReadFrom method of destination") } } func TestCopyFromWrappedConnectionToWrappedConnection(t *testing.T) { innerConn1 := &testConn{} wrappedConn1 := NewConn(innerConn1, 0) innerConn2 := &testConn{} wrappedConn2 := NewConn(innerConn2, 0) io.Copy(wrappedConn1, wrappedConn2) if innerConn1.readFromCalledWith != innerConn2 { t.Errorf("Expected io.Copy to pass inner source connection to ReadFrom of inner destination connection") } }