From f4967a86513ad60618d2b51174e93ebe0a57bf01 Mon Sep 17 00:00:00 2001 From: bddjr Date: Sat, 17 Feb 2024 13:52:50 +0800 Subject: [PATCH 1/4] net/http: configurable error message for Client sent an HTTP request to an HTTPS server. --- src/crypto/tls/common.go | 4 ++++ src/crypto/tls/conn.go | 6 +++++- src/net/http/server.go | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index 849e8b0a209d33..d408797204597f 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -785,6 +785,10 @@ type Config struct { // autoSessionTicketKeys is like sessionTicketKeys but is owned by the // auto-rotation logic. See Config.ticketKeys. autoSessionTicketKeys []ticketKey + + // If tlsRecordHeaderLooksLikeHTTP, call this function, + // then get return string to io.WriteString + LooksLikeHttpResponseHandler func(RecondBytes []byte) string } const ( diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index 0e4669866e5e98..c84c02b57d8db5 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -568,6 +568,9 @@ type RecordHeaderError struct { // RecordHeader contains the five bytes of TLS record header that // triggered the error. RecordHeader [5]byte + // RecondBytes contains all the bytes of the TLS record header that + // triggered the error. + RecondBytes []byte // Conn provides the underlying net.Conn in the case that a client // sent an initial handshake that didn't look like TLS. // It is nil if there's already been a handshake or a TLS alert has @@ -580,7 +583,8 @@ func (e RecordHeaderError) Error() string { return "tls: " + e.Msg } func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) { err.Msg = msg err.Conn = conn - copy(err.RecordHeader[:], c.rawInput.Bytes()) + err.RecondBytes = c.rawInput.Bytes() + copy(err.RecordHeader[:], err.RecondBytes) return err } diff --git a/src/net/http/server.go b/src/net/http/server.go index 0ba88d1119e4f9..41c85ac26c0424 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -1923,7 +1923,12 @@ func (c *conn) serve(ctx context.Context) { // TLS, assume they're speaking plaintext HTTP and write a // 400 response on the TLS conn's underlying net.Conn. if re, ok := err.(tls.RecordHeaderError); ok && re.Conn != nil && tlsRecordHeaderLooksLikeHTTP(re.RecordHeader) { - io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n") + if handler := c.server.TLSConfig.LooksLikeHttpResponseHandler; handler != nil { + // configurable error message for Client sent an HTTP request to an HTTPS server. + io.WriteString(re.Conn, handler(re.RecondBytes)) + } else { + io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n") + } re.Conn.Close() return } From 8764a6641a3cfd0121f39a062b1100d8d6cc0b5a Mon Sep 17 00:00:00 2001 From: bddjr Date: Thu, 29 Feb 2024 18:01:21 +0800 Subject: [PATCH 2/4] optimize --- src/crypto/tls/common.go | 20 +++++++++-- src/net/http/server.go | 77 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index d408797204597f..422f6190432f99 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -786,9 +786,23 @@ type Config struct { // auto-rotation logic. See Config.ticketKeys. autoSessionTicketKeys []ticketKey - // If tlsRecordHeaderLooksLikeHTTP, call this function, - // then get return string to io.WriteString - LooksLikeHttpResponseHandler func(RecondBytes []byte) string + // HttpOnHttpsPortErrorResponseString is sent on an HTTPS connection + // which receives what looks like an HTTP request. + // "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\nClient sent an HTTP request to an HTTPS server.\n" + HttpOnHttpsPortErrorResponse string + + // HttpOnHttpsPortErrorRedirect is sent on an HTTPS connection + // which receives what looks like an HTTP request. + // If true, 307 redirect will be sent + HttpOnHttpsPortErrorRedirect bool + + // HttpOnHttpsPortErrorHandler handles HTTP requests sent to an HTTPS port. + // + // WriteString use: + // io.WriteString(conn, "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\nClient sent an HTTP request to an HTTPS server.\n") + // Parse the request header use: + // req, err := http.ReadRequestForHttpOnHttpsPortErrorHandler(conn, recondBytes) + HttpOnHttpsPortErrorHandler func(conn net.Conn, recondBytes []byte, badRequestResponse string) } const ( diff --git a/src/net/http/server.go b/src/net/http/server.go index 41c85ac26c0424..7f7fa94d4e3e1d 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -1923,12 +1923,7 @@ func (c *conn) serve(ctx context.Context) { // TLS, assume they're speaking plaintext HTTP and write a // 400 response on the TLS conn's underlying net.Conn. if re, ok := err.(tls.RecordHeaderError); ok && re.Conn != nil && tlsRecordHeaderLooksLikeHTTP(re.RecordHeader) { - if handler := c.server.TLSConfig.LooksLikeHttpResponseHandler; handler != nil { - // configurable error message for Client sent an HTTP request to an HTTPS server. - io.WriteString(re.Conn, handler(re.RecondBytes)) - } else { - io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n") - } + c.httpOnHttpsPortErrorHandler(re.Conn, re.RecondBytes) re.Conn.Close() return } @@ -3846,3 +3841,73 @@ func MaxBytesHandler(h Handler, n int64) Handler { h.ServeHTTP(w, &r2) }) } + +// net/http: configurable error message for Client sent an HTTP request to an HTTPS server. +// https://go.dev/issue/49310 +func (c *conn) httpOnHttpsPortErrorHandler(conn net.Conn, recondBytes []byte) { + // Read Response string + badRequestResponse := c.server.TLSConfig.HttpOnHttpsPortErrorResponse + if badRequestResponse == "" { + badRequestResponse = "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\nClient sent an HTTP request to an HTTPS server.\n" + } + + // Handler + if handler := c.server.TLSConfig.HttpOnHttpsPortErrorHandler; handler != nil { + handler(conn, recondBytes, badRequestResponse) + return + } + + // Redirect + if c.server.TLSConfig.HttpOnHttpsPortErrorRedirect { + // Read Header + req, err := ReadRequestForHttpOnHttpsPortErrorHandler(conn, recondBytes) + if err != nil { + io.WriteString(conn, badRequestResponse) + return + } + + // Send Redirect + io.WriteString(conn, fmt.Sprintf( + "HTTP/1.1 307 Temporary Redirect\r\nLocation: https://%s%s\r\nConnection: close\r\n\r\nClient sent an HTTP request to an HTTPS server.\n", + req.Host, req.URL.Path, + )) + return + } + + // Send Response string + io.WriteString(conn, badRequestResponse) +} + +func ReadRequestForHttpOnHttpsPortErrorHandler(conn net.Conn, recondBytes []byte) (*Request, error) { + req, err := ReadRequest(bufio.NewReader(bytes.NewReader(recondBytes))) + if err != nil || req.Host == "" { + // Content length may be insufficient, continue reading. + // Max 4KB + if len(recondBytes) >= 4096 { + if err != nil { + return nil, err + } + return nil, errors.New("missing required Host header") + } + + // Set Timeout 1s + conn.SetReadDeadline(time.Now().Add(time.Duration(1 * time.Second))) + // Read bytes + b := make([]byte, 4096-len(recondBytes)) + n, err := conn.Read(b) + if err != nil { + return nil, err + } + recondBytes = append(recondBytes, b[:n]...) + + // Read Request + req, err = ReadRequest(bufio.NewReader(bytes.NewReader(recondBytes))) + if err != nil { + return nil, err + } + if req.Host == "" { + return nil, errors.New("missing required Host header") + } + } + return req, nil +} From 14e97b106bd292914c875107ba208cee2074571e Mon Sep 17 00:00:00 2001 From: bddjr Date: Thu, 29 Feb 2024 22:00:19 +0800 Subject: [PATCH 3/4] optimize --- src/crypto/tls/common.go | 2 +- src/net/http/server.go | 43 ++++++++++++++++++---------------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index 422f6190432f99..cc6e5b9e80e9b3 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -801,7 +801,7 @@ type Config struct { // WriteString use: // io.WriteString(conn, "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\nClient sent an HTTP request to an HTTPS server.\n") // Parse the request header use: - // req, err := http.ReadRequestForHttpOnHttpsPortErrorHandler(conn, recondBytes) + // http.ReadRequestForHttpOnHttpsPortErrorHandler(conn, recondBytes) HttpOnHttpsPortErrorHandler func(conn net.Conn, recondBytes []byte, badRequestResponse string) } diff --git a/src/net/http/server.go b/src/net/http/server.go index 7f7fa94d4e3e1d..e3f2dbb23dbcc9 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -3860,7 +3860,7 @@ func (c *conn) httpOnHttpsPortErrorHandler(conn net.Conn, recondBytes []byte) { // Redirect if c.server.TLSConfig.HttpOnHttpsPortErrorRedirect { // Read Header - req, err := ReadRequestForHttpOnHttpsPortErrorHandler(conn, recondBytes) + req, _, err := ReadRequestForHttpOnHttpsPortErrorHandler(conn, recondBytes) if err != nil { io.WriteString(conn, badRequestResponse) return @@ -3878,36 +3878,31 @@ func (c *conn) httpOnHttpsPortErrorHandler(conn net.Conn, recondBytes []byte) { io.WriteString(conn, badRequestResponse) } -func ReadRequestForHttpOnHttpsPortErrorHandler(conn net.Conn, recondBytes []byte) (*Request, error) { - req, err := ReadRequest(bufio.NewReader(bytes.NewReader(recondBytes))) - if err != nil || req.Host == "" { - // Content length may be insufficient, continue reading. - // Max 4KB - if len(recondBytes) >= 4096 { - if err != nil { - return nil, err - } - return nil, errors.New("missing required Host header") - } +func ReadRequestForHttpOnHttpsPortErrorHandler(conn net.Conn, recondBytes []byte) (req *Request, reqBytes []byte, err error) { + // Max 4KB + if len(recondBytes) > 4096 { + return nil, recondBytes, errors.New("recondBytes too long") + } + // Content length may be insufficient, continue reading. + if !bytes.Contains(recondBytes, []byte("\r\n\r\n")) { + b := make([]byte, 4096-len(recondBytes)) // Set Timeout 1s conn.SetReadDeadline(time.Now().Add(time.Duration(1 * time.Second))) - // Read bytes - b := make([]byte, 4096-len(recondBytes)) n, err := conn.Read(b) if err != nil { - return nil, err + return nil, recondBytes, err } recondBytes = append(recondBytes, b[:n]...) + } - // Read Request - req, err = ReadRequest(bufio.NewReader(bytes.NewReader(recondBytes))) - if err != nil { - return nil, err - } - if req.Host == "" { - return nil, errors.New("missing required Host header") - } + // Read Request + req, err = ReadRequest(bufio.NewReader(bytes.NewReader(recondBytes))) + if err != nil { + return nil, recondBytes, err + } + if req.Host == "" { + return nil, recondBytes, errors.New("missing required Host header") } - return req, nil + return req, recondBytes, nil } From 59daad1a0e14923e6ca9ffcc9c7750321af9e614 Mon Sep 17 00:00:00 2001 From: bddjr Date: Fri, 1 Mar 2024 10:58:20 +0800 Subject: [PATCH 4/4] optimize --- src/net/http/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/http/server.go b/src/net/http/server.go index e3f2dbb23dbcc9..06a43e78f14c86 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -3885,7 +3885,7 @@ func ReadRequestForHttpOnHttpsPortErrorHandler(conn net.Conn, recondBytes []byte } // Content length may be insufficient, continue reading. - if !bytes.Contains(recondBytes, []byte("\r\n\r\n")) { + if len(recondBytes) < 4096 && !bytes.Contains(recondBytes, []byte("\r\n\r\n")) { b := make([]byte, 4096-len(recondBytes)) // Set Timeout 1s conn.SetReadDeadline(time.Now().Add(time.Duration(1 * time.Second)))