Merge branch 'nghttpx-proxyprotocol'
This commit is contained in:
commit
7d481db248
|
@ -100,6 +100,7 @@ OPTIONS = [
|
||||||
"tls-ticket-key-memcached-max-fail",
|
"tls-ticket-key-memcached-max-fail",
|
||||||
"request-phase-file",
|
"request-phase-file",
|
||||||
"response-phase-file",
|
"response-phase-file",
|
||||||
|
"accept-proxy-protocol",
|
||||||
"conf",
|
"conf",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -787,6 +787,362 @@ func TestH2H1Upgrade(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1TCP4 tests PROXY protocol version 1
|
||||||
|
// containing TCP4 entry is accepted and X-Forwarded-For contains
|
||||||
|
// advertised src address.
|
||||||
|
func TestH2H1ProxyProtocolV1TCP4(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got != want {
|
||||||
|
t.Errorf("X-Forwarded-For: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP4 192.168.0.2 192.168.0.100 12345 8080\r\n"))
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1TCP4",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1TCP6 tests PROXY protocol version 1
|
||||||
|
// containing TCP6 entry is accepted and X-Forwarded-For contains
|
||||||
|
// advertised src address.
|
||||||
|
func TestH2H1ProxyProtocolV1TCP6(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.Header.Get("X-Forwarded-For"), "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; got != want {
|
||||||
|
t.Errorf("X-Forwarded-For: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 ::1 12345 8080\r\n"))
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1TCP6",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1Unknown tests PROXY protocol version 1
|
||||||
|
// containing UNKNOWN entry is accepted.
|
||||||
|
func TestH2H1ProxyProtocolV1Unknown(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, notWant := r.Header.Get("X-Forwarded-For"), "192.168.0.2"; got == notWant {
|
||||||
|
t.Errorf("X-Forwarded-For: %v")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY UNKNOWN 192.168.0.2 192.168.0.100 12345 8080\r\n"))
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1Unknown",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1JustUnknown tests PROXY protocol version 1
|
||||||
|
// containing only "PROXY UNKNOWN" is accepted.
|
||||||
|
func TestH2H1ProxyProtocolV1JustUnknown(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY UNKNOWN\r\n"))
|
||||||
|
|
||||||
|
res, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1JustUnknown",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error st.http2() = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := res.status, 200; got != want {
|
||||||
|
t.Errorf("res.status: %v; want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1TooLongLine tests PROXY protocol version 1
|
||||||
|
// line longer than 107 bytes must be rejected
|
||||||
|
func TestH2H1ProxyProtocolV1TooLongLine(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol", "--add-x-forwarded-for"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 655350\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1TooLongLine",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1BadLineEnd tests that PROXY protocol version
|
||||||
|
// 1 line ending without \r\n should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1BadLineEnd(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 8080\r \n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1BadLineEnd",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1NoEnd tests that PROXY protocol version 1
|
||||||
|
// line containing no \r\n should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1NoEnd(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 8080"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1NoEnd",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1EmbeddedNULL tests that PROXY protocol
|
||||||
|
// version 1 line containing NULL character should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1EmbeddedNULL(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
b := []byte("PROXY TCP6 ::1*foo ::1 12345 8080\r\n")
|
||||||
|
b[14] = 0
|
||||||
|
st.conn.Write(b)
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1EmbeddedNULL",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1MissingSrcPort tests that PROXY protocol
|
||||||
|
// version 1 line without src port should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1MissingSrcPort(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 8080\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1MissingSrcPort",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1MissingDstPort tests that PROXY protocol
|
||||||
|
// version 1 line without dst port should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1MissingDstPort(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 \r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1MissingDstPort",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1InvalidSrcPort tests that PROXY protocol
|
||||||
|
// containing invalid src port should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1InvalidSrcPort(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 123x 8080\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1InvalidSrcPort",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1InvalidDstPort tests that PROXY protocol
|
||||||
|
// containing invalid dst port should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1InvalidDstPort(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 123456 80x\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1InvalidDstPort",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1LeadingZeroPort tests that PROXY protocol
|
||||||
|
// version 1 line with non zero port with leading zero should be
|
||||||
|
// rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1LeadingZeroPort(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 03000 8080\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1LeadingZeroPort",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1TooLargeSrcPort tests that PROXY protocol
|
||||||
|
// containing too large src port should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1TooLargeSrcPort(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 65536 8080\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1TooLargeSrcPort",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1TooLargeDstPort tests that PROXY protocol
|
||||||
|
// containing too large dst port should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1TooLargeDstPort(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 ::1 12345 65536\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1TooLargeDstPort",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1InvalidSrcAddr tests that PROXY protocol
|
||||||
|
// containing invalid src addr should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1InvalidSrcAddr(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 192.168.0.1 ::1 12345 8080\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1InvalidSrcAddr",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1InvalidDstAddr tests that PROXY protocol
|
||||||
|
// containing invalid dst addr should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1InvalidDstAddr(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY TCP6 ::1 192.168.0.1 12345 8080\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1InvalidDstAddr",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1InvalidProtoFamily tests that PROXY protocol
|
||||||
|
// containing invalid protocol family should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1InvalidProtoFamily(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PROXY UNIX ::1 ::1 12345 8080\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1InvalidProtoFamily",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestH2H1ProxyProtocolV1InvalidID tests that PROXY protocol
|
||||||
|
// containing invalid PROXY protocol version 1 ID should be rejected.
|
||||||
|
func TestH2H1ProxyProtocolV1InvalidID(t *testing.T) {
|
||||||
|
st := newServerTester([]string{"--accept-proxy-protocol"}, t, noopHandler)
|
||||||
|
defer st.Close()
|
||||||
|
|
||||||
|
st.conn.Write([]byte("PR0XY TCP6 ::1 ::1 12345 8080\r\n"))
|
||||||
|
|
||||||
|
_, err := st.http2(requestParam{
|
||||||
|
name: "TestH2H1ProxyProtocolV1InvalidID",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("connection was not terminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestH2H1GracefulShutdown tests graceful shutdown.
|
// TestH2H1GracefulShutdown tests graceful shutdown.
|
||||||
func TestH2H1GracefulShutdown(t *testing.T) {
|
func TestH2H1GracefulShutdown(t *testing.T) {
|
||||||
st := newServerTester(nil, t, noopHandler)
|
st := newServerTester(nil, t, noopHandler)
|
||||||
|
|
|
@ -1268,6 +1268,8 @@ Connections:
|
||||||
timeouts when connecting and making CONNECT request can
|
timeouts when connecting and making CONNECT request can
|
||||||
be specified by --backend-read-timeout and
|
be specified by --backend-read-timeout and
|
||||||
--backend-write-timeout options.
|
--backend-write-timeout options.
|
||||||
|
--accept-proxy-protocol
|
||||||
|
Accept PROXY protocol version 1 on frontend connection.
|
||||||
|
|
||||||
Performance:
|
Performance:
|
||||||
-n, --workers=<N>
|
-n, --workers=<N>
|
||||||
|
@ -1915,6 +1917,7 @@ int main(int argc, char **argv) {
|
||||||
90},
|
90},
|
||||||
{SHRPX_OPT_REQUEST_PHASE_FILE, required_argument, &flag, 91},
|
{SHRPX_OPT_REQUEST_PHASE_FILE, required_argument, &flag, 91},
|
||||||
{SHRPX_OPT_RESPONSE_PHASE_FILE, required_argument, &flag, 92},
|
{SHRPX_OPT_RESPONSE_PHASE_FILE, required_argument, &flag, 92},
|
||||||
|
{SHRPX_OPT_ACCEPT_PROXY_PROTOCOL, no_argument, &flag, 93},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
@ -2316,6 +2319,10 @@ int main(int argc, char **argv) {
|
||||||
// --response-phase-file
|
// --response-phase-file
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_RESPONSE_PHASE_FILE, optarg);
|
cmdcfgs.emplace_back(SHRPX_OPT_RESPONSE_PHASE_FILE, optarg);
|
||||||
break;
|
break;
|
||||||
|
case 93:
|
||||||
|
// --accept-proxy-protocol
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_ACCEPT_PROXY_PROTOCOL, "yes");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,8 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
int ClientHandler::noop() { return 0; }
|
||||||
|
|
||||||
int ClientHandler::read_clear() {
|
int ClientHandler::read_clear() {
|
||||||
ev_timer_again(conn_.loop, &conn_.rt);
|
ev_timer_again(conn_.loop, &conn_.rt);
|
||||||
|
|
||||||
|
@ -382,6 +384,17 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
|
||||||
conn_.rlimit.startw();
|
conn_.rlimit.startw();
|
||||||
ev_timer_again(conn_.loop, &conn_.rt);
|
ev_timer_again(conn_.loop, &conn_.rt);
|
||||||
|
|
||||||
|
if (get_config()->accept_proxy_protocol) {
|
||||||
|
read_ = &ClientHandler::read_clear;
|
||||||
|
write_ = &ClientHandler::noop;
|
||||||
|
on_read_ = &ClientHandler::proxy_protocol_read;
|
||||||
|
on_write_ = &ClientHandler::upstream_noop;
|
||||||
|
} else {
|
||||||
|
setup_upstream_io_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientHandler::setup_upstream_io_callback() {
|
||||||
if (conn_.tls.ssl) {
|
if (conn_.tls.ssl) {
|
||||||
conn_.prepare_server_handshake();
|
conn_.prepare_server_handshake();
|
||||||
read_ = write_ = &ClientHandler::tls_handshake;
|
read_ = write_ = &ClientHandler::tls_handshake;
|
||||||
|
@ -829,4 +842,235 @@ ev_io *ClientHandler::get_wev() { return &conn_.wev; }
|
||||||
|
|
||||||
Worker *ClientHandler::get_worker() const { return worker_; }
|
Worker *ClientHandler::get_worker() const { return worker_; }
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
ssize_t parse_proxy_line_port(const uint8_t *first, const uint8_t *last) {
|
||||||
|
auto p = first;
|
||||||
|
int32_t port = 0;
|
||||||
|
|
||||||
|
if (p == last) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '0') {
|
||||||
|
if (p + 1 != last && util::isDigit(*(p + 1))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; p != last && util::isDigit(*p); ++p) {
|
||||||
|
port *= 10;
|
||||||
|
port += *p - '0';
|
||||||
|
|
||||||
|
if (port > 65535) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p - first;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int ClientHandler::on_proxy_protocol_finish() {
|
||||||
|
setup_upstream_io_callback();
|
||||||
|
|
||||||
|
// Run on_read to process data left in buffer since they are not
|
||||||
|
// notified further
|
||||||
|
if (on_read() != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
|
||||||
|
int ClientHandler::proxy_protocol_read() {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol: Started";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto first = rb_.pos;
|
||||||
|
|
||||||
|
// NULL character really destroys functions which expects NULL
|
||||||
|
// terminated string. We won't expect it in PROXY protocol line, so
|
||||||
|
// find it here.
|
||||||
|
auto chrs = std::array<char, 2>{{'\n', '\0'}};
|
||||||
|
|
||||||
|
constexpr size_t MAX_PROXY_LINELEN = 107;
|
||||||
|
|
||||||
|
auto bufend = rb_.pos + std::min(MAX_PROXY_LINELEN, rb_.rleft());
|
||||||
|
|
||||||
|
auto end =
|
||||||
|
std::find_first_of(rb_.pos, bufend, std::begin(chrs), std::end(chrs));
|
||||||
|
|
||||||
|
if (end == bufend) {
|
||||||
|
if (rb_.rleft() >= MAX_PROXY_LINELEN) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: No ending CR LF sequence found";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*end == '\0' || end == rb_.pos || *(end - 1) != '\r') {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: No ending CR LF sequence found";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
--end;
|
||||||
|
|
||||||
|
constexpr const char HEADER[] = "PROXY ";
|
||||||
|
|
||||||
|
if (end - rb_.pos < str_size(HEADER)) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: PROXY version 1 ID not found";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!util::streq_l(HEADER, rb_.pos, str_size(HEADER))) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Bad PROXY protocol version 1 ID";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_.drain(str_size(HEADER));
|
||||||
|
|
||||||
|
int family;
|
||||||
|
|
||||||
|
if (rb_.pos[0] == 'T') {
|
||||||
|
if (end - rb_.pos < 5) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rb_.pos[1] != 'C' || rb_.pos[2] != 'P') {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rb_.pos[3]) {
|
||||||
|
case '4':
|
||||||
|
family = AF_INET;
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
family = AF_INET6;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_.drain(5);
|
||||||
|
} else {
|
||||||
|
if (end - rb_.pos < 7) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: INET protocol family not found";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!util::streq_l("UNKNOWN", rb_.pos, 7)) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Unknown INET protocol family";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_.drain(end + 2 - rb_.pos);
|
||||||
|
|
||||||
|
return on_proxy_protocol_finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// source address
|
||||||
|
auto token_end = std::find(rb_.pos, end, ' ');
|
||||||
|
if (token_end == end) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Source address not found";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*token_end = '\0';
|
||||||
|
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source address";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto src_addr = rb_.pos;
|
||||||
|
auto src_addrlen = token_end - rb_.pos;
|
||||||
|
|
||||||
|
rb_.drain(token_end - rb_.pos + 1);
|
||||||
|
|
||||||
|
// destination address
|
||||||
|
token_end = std::find(rb_.pos, end, ' ');
|
||||||
|
if (token_end == end) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Destination address not found";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*token_end = '\0';
|
||||||
|
if (!util::numeric_host(reinterpret_cast<const char *>(rb_.pos), family)) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination address";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently we don't use destination address
|
||||||
|
|
||||||
|
rb_.drain(token_end - rb_.pos + 1);
|
||||||
|
|
||||||
|
// source port
|
||||||
|
auto n = parse_proxy_line_port(rb_.pos, end);
|
||||||
|
if (n <= 0 || *(rb_.pos + n) != ' ') {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid source port";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_.pos[n] = '\0';
|
||||||
|
auto src_port = rb_.pos;
|
||||||
|
auto src_portlen = n;
|
||||||
|
|
||||||
|
rb_.drain(n + 1);
|
||||||
|
|
||||||
|
// destination port
|
||||||
|
n = parse_proxy_line_port(rb_.pos, end);
|
||||||
|
if (n <= 0 || rb_.pos + n != end) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Invalid destination port";
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently we don't use destination port
|
||||||
|
|
||||||
|
rb_.drain(end + 2 - rb_.pos);
|
||||||
|
|
||||||
|
ipaddr_.assign(src_addr, src_addr + src_addrlen);
|
||||||
|
port_.assign(src_port, src_port + src_portlen);
|
||||||
|
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos - first)
|
||||||
|
<< " bytes read";
|
||||||
|
}
|
||||||
|
|
||||||
|
return on_proxy_protocol_finish();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -56,6 +56,7 @@ public:
|
||||||
const char *port);
|
const char *port);
|
||||||
~ClientHandler();
|
~ClientHandler();
|
||||||
|
|
||||||
|
int noop();
|
||||||
// Performs clear text I/O
|
// Performs clear text I/O
|
||||||
int read_clear();
|
int read_clear();
|
||||||
int write_clear();
|
int write_clear();
|
||||||
|
@ -71,6 +72,9 @@ public:
|
||||||
int upstream_http1_connhd_read();
|
int upstream_http1_connhd_read();
|
||||||
int upstream_write();
|
int upstream_write();
|
||||||
|
|
||||||
|
int proxy_protocol_read();
|
||||||
|
int on_proxy_protocol_finish();
|
||||||
|
|
||||||
// Performs I/O operation. Internally calls on_read()/on_write().
|
// Performs I/O operation. Internally calls on_read()/on_write().
|
||||||
int do_read();
|
int do_read();
|
||||||
int do_write();
|
int do_write();
|
||||||
|
@ -130,6 +134,8 @@ public:
|
||||||
void signal_write();
|
void signal_write();
|
||||||
ev_io *get_wev();
|
ev_io *get_wev();
|
||||||
|
|
||||||
|
void setup_upstream_io_callback();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Connection conn_;
|
Connection conn_;
|
||||||
ev_timer reneg_shutdown_timer_;
|
ev_timer reneg_shutdown_timer_;
|
||||||
|
|
|
@ -625,6 +625,7 @@ void parse_mapping(const DownstreamAddr &addr, const char *src) {
|
||||||
|
|
||||||
// generated by gennghttpxfun.py
|
// generated by gennghttpxfun.py
|
||||||
enum {
|
enum {
|
||||||
|
SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL,
|
||||||
SHRPX_OPTID_ACCESSLOG_FILE,
|
SHRPX_OPTID_ACCESSLOG_FILE,
|
||||||
SHRPX_OPTID_ACCESSLOG_FORMAT,
|
SHRPX_OPTID_ACCESSLOG_FORMAT,
|
||||||
SHRPX_OPTID_ACCESSLOG_SYSLOG,
|
SHRPX_OPTID_ACCESSLOG_SYSLOG,
|
||||||
|
@ -1099,6 +1100,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||||
return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
|
return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (util::strieq_l("accept-proxy-protoco", name, 20)) {
|
||||||
|
return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) {
|
if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) {
|
||||||
return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER;
|
return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER;
|
||||||
|
@ -1968,6 +1974,10 @@ int parse_config(const char *opt, const char *optarg,
|
||||||
LOG(WARN) << opt
|
LOG(WARN) << opt
|
||||||
<< ": ignored because mruby support is disabled at build time.";
|
<< ": ignored because mruby support is disabled at build time.";
|
||||||
#endif // !HAVE_MRUBY
|
#endif // !HAVE_MRUBY
|
||||||
|
return 0;
|
||||||
|
case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL:
|
||||||
|
mod_config()->accept_proxy_protocol = util::strieq(optarg, "yes");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
case SHRPX_OPTID_CONF:
|
case SHRPX_OPTID_CONF:
|
||||||
LOG(WARN) << "conf: ignored";
|
LOG(WARN) << "conf: ignored";
|
||||||
|
|
|
@ -185,6 +185,7 @@ constexpr char SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL[] =
|
||||||
"tls-ticket-key-memcached-max-fail";
|
"tls-ticket-key-memcached-max-fail";
|
||||||
constexpr char SHRPX_OPT_REQUEST_PHASE_FILE[] = "request-phase-file";
|
constexpr char SHRPX_OPT_REQUEST_PHASE_FILE[] = "request-phase-file";
|
||||||
constexpr char SHRPX_OPT_RESPONSE_PHASE_FILE[] = "response-phase-file";
|
constexpr char SHRPX_OPT_RESPONSE_PHASE_FILE[] = "response-phase-file";
|
||||||
|
constexpr char SHRPX_OPT_ACCEPT_PROXY_PROTOCOL[] = "accept-proxy-protocol";
|
||||||
|
|
||||||
union sockaddr_union {
|
union sockaddr_union {
|
||||||
sockaddr_storage storage;
|
sockaddr_storage storage;
|
||||||
|
@ -409,6 +410,7 @@ struct Config {
|
||||||
bool no_ocsp;
|
bool no_ocsp;
|
||||||
// true if --tls-ticket-key-cipher is used
|
// true if --tls-ticket-key-cipher is used
|
||||||
bool tls_ticket_key_cipher_given;
|
bool tls_ticket_key_cipher_given;
|
||||||
|
bool accept_proxy_protocol;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Config *get_config();
|
const Config *get_config();
|
||||||
|
|
17
src/util.cc
17
src/util.cc
|
@ -626,15 +626,16 @@ void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool numeric_host(const char *hostname) {
|
bool numeric_host(const char *hostname) {
|
||||||
struct addrinfo *res;
|
return numeric_host(hostname, AF_INET) || numeric_host(hostname, AF_INET6);
|
||||||
struct addrinfo hints {};
|
|
||||||
hints.ai_family = AF_UNSPEC;
|
|
||||||
hints.ai_flags = AI_NUMERICHOST;
|
|
||||||
if (getaddrinfo(hostname, nullptr, &hints, &res)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
freeaddrinfo(res);
|
|
||||||
return true;
|
bool numeric_host(const char *hostname, int family) {
|
||||||
|
int rv;
|
||||||
|
std::array<uint8_t, sizeof(struct in6_addr)> dst;
|
||||||
|
|
||||||
|
rv = inet_pton(family, hostname, dst.data());
|
||||||
|
|
||||||
|
return rv == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string numeric_name(const struct sockaddr *sa, socklen_t salen) {
|
std::string numeric_name(const struct sockaddr *sa, socklen_t salen) {
|
||||||
|
|
|
@ -516,6 +516,8 @@ void write_uri_field(std::ostream &o, const char *uri, const http_parser_url &u,
|
||||||
|
|
||||||
bool numeric_host(const char *hostname);
|
bool numeric_host(const char *hostname);
|
||||||
|
|
||||||
|
bool numeric_host(const char *hostname, int family);
|
||||||
|
|
||||||
// Returns numeric address string of |addr|. If getnameinfo() is
|
// Returns numeric address string of |addr|. If getnameinfo() is
|
||||||
// failed, "unknown" is returned.
|
// failed, "unknown" is returned.
|
||||||
std::string numeric_name(const struct sockaddr *sa, socklen_t salen);
|
std::string numeric_name(const struct sockaddr *sa, socklen_t salen);
|
||||||
|
|
Loading…
Reference in New Issue