Skip to content

Commit

Permalink
[7.17](backport #36518) packetbeat/protos/http: don't panic when host…
Browse files Browse the repository at this point in the history
… is empty (#36527)

* packetbeat/protos/http: don't panic when host is empty (#36518)

Previously, extractHostHeader would panic if the host part of header was
empty. Avoid this by using standard library functions to do splits and
clean up IPv6 addresses.

Add tests to confirm old behaviour and test to cover panic case.

(cherry picked from commit 3d0cdb0)

* resolve semantic conflicts

---------

Co-authored-by: Dan Kortschak <[email protected]>
Co-authored-by: Dan Kortschak <[email protected]>
  • Loading branch information
3 people authored Sep 6, 2023
1 parent bf1e585 commit fcd94b3
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d

*Packetbeat*

- Fix panic in HTTP protocol parsing when host header has empty host part. {issue}36497[36497] {issue}36518[36518]

*Winlogbeat*

Expand Down
40 changes: 31 additions & 9 deletions packetbeat/protos/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,20 +741,42 @@ func parseCookieValue(raw string) string {
}

func extractHostHeader(header string) (host string, port int) {
if len(header) == 0 || net.ParseIP(header) != nil {
if header == "" || net.ParseIP(header) != nil {
return header, port
}
// Split :port trailer
if pos := strings.LastIndexByte(header, ':'); pos != -1 {
if num, err := strconv.Atoi(header[pos+1:]); err == nil && num > 0 && num < 65536 {
header, port = header[:pos], num
host, ps, err := net.SplitHostPort(header)
if err != nil {
var addrError *net.AddrError
if errors.As(err, &addrError) && addrError.Err == "missing port in address" {
return trimSquareBracket(header), port
}
}
// Remove square bracket boxing of IPv6 address.
if last := len(header) - 1; header[0] == '[' && header[last] == ']' && net.ParseIP(header[1:last]) != nil {
header = header[1:last]
pi, err := strconv.ParseInt(ps, 10, 16)
if err != nil || pi == 0 {
return header, port
}
return header, port
return trimSquareBracket(host), int(pi)
}

func trimSquareBracket(s string) string {
s, ok := stringsCutPrefix(s, "[")
if !ok {
return s
}
return strings.TrimSuffix(s, "]")
}

// stringsCutPrefix is copied from the standard library strings package as 7.17 is not built
// with go1.20 when strings.CutPrefix was added.
//
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
func stringsCutPrefix(s, prefix string) (after string, found bool) {
if !strings.HasPrefix(s, prefix) {
return s, false
}
return s[len(prefix):], true
}

func (http *httpPlugin) hideHeaders(m *message) {
Expand Down
30 changes: 30 additions & 0 deletions packetbeat/protos/http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1897,6 +1897,36 @@ func TestHttpParser_Extension(t *testing.T) {
}
}

func TestExtractHostHeader(t *testing.T) {
tests := []struct {
header string
wantHost string
wantPort int
}{
{header: "", wantHost: "", wantPort: 0},
{header: "localhost:0", wantHost: "localhost:0", wantPort: 0},
{header: "127.0.0.1:0", wantHost: "127.0.0.1:0", wantPort: 0},
{header: "[::]:0", wantHost: "[::]:0", wantPort: 0},
{header: "localhost", wantHost: "localhost", wantPort: 0},
{header: "localhost:9001", wantHost: "localhost", wantPort: 9001},
{header: "localhost:9000000", wantHost: "localhost:9000000", wantPort: 0},
{header: "127.0.0.1:9001", wantHost: "127.0.0.1", wantPort: 9001},
{header: "127.0.0.1", wantHost: "127.0.0.1", wantPort: 0},
{header: "[::]", wantHost: "::", wantPort: 0},
{header: ":0", wantHost: ":0", wantPort: 0},
{header: ":9001", wantHost: "", wantPort: 9001},
}
for _, test := range tests {
host, port := extractHostHeader(test.header)
if host != test.wantHost {
t.Errorf("unexpected host for %q: got:%q want:%q", test.header, host, test.wantHost)
}
if port != test.wantPort {
t.Errorf("unexpected port for %q: got:%d want:%d", test.header, port, test.wantPort)
}
}
}

func benchmarkHTTPMessage(b *testing.B, data []byte) {
http := httpModForTests(nil)
parser := newParser(&http.parserConfig)
Expand Down

0 comments on commit fcd94b3

Please sign in to comment.