Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
packetbeat/protos/http: don't panic when host is empty (#36518)
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.

(cherry picked from commit 3d0cdb0)
efd6 authored and mergify[bot] committed Sep 6, 2023
1 parent bf1e585 commit 8e88ea4
Showing 3 changed files with 50 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
@@ -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*

28 changes: 19 additions & 9 deletions packetbeat/protos/http/http.go
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ package http
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"net"
"net/url"
@@ -741,20 +742,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) {
30 changes: 30 additions & 0 deletions packetbeat/protos/http/http_test.go
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 8e88ea4

Please sign in to comment.