Skip to content

Commit

Permalink
packetbeat/protos/http: don't panic when host is empty
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
efd6 committed Sep 6, 2023
1 parent ffae391 commit e4c6d2e
Show file tree
Hide file tree
Showing 3 changed files with 48 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 @@ -136,6 +136,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff]

*Packetbeat*

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

*Winlogbeat*

Expand Down
28 changes: 19 additions & 9 deletions packetbeat/protos/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package http
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"net"
"net/url"
Expand Down Expand Up @@ -735,20 +736,29 @@ 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 trimSquareBracket(host), int(pi)
}

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

func (http *httpPlugin) hideHeaders(m *message) {
Expand Down
28 changes: 28 additions & 0 deletions packetbeat/protos/http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1901,6 +1901,34 @@ 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: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: ":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 e4c6d2e

Please sign in to comment.