From cf2adad4c9c0f9ff9bcb4d161a8774ecedcfb174 Mon Sep 17 00:00:00 2001 From: "Lawrence, Rendall" Date: Mon, 2 May 2022 23:12:13 +0300 Subject: [PATCH] (tested) fix invalid parsing of multivalued and comma-separated addresses from HTTP header --- bittorrent/request.go | 28 +++++++++++++++++++--------- bittorrent/request_test.go | 31 +++++++++++++++++++++++++++++++ frontend/http/parser.go | 11 +++++++++-- 3 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 bittorrent/request_test.go diff --git a/bittorrent/request.go b/bittorrent/request.go index f84a49e..58c91c0 100644 --- a/bittorrent/request.go +++ b/bittorrent/request.go @@ -56,19 +56,29 @@ func (aa *RequestAddresses) Add(a RequestAddress) { } } -// Validate checks if array is not empty and every RequestAddress is valid, -// then sorts addresses with Sort -func (aa RequestAddresses) Validate() bool { - if len(aa) == 0 { +// Validate checks if array is not empty and at least one RequestAddress is valid, +// then make them unique and sorts with Less rule +func (aa *RequestAddresses) Validate() bool { + if len(*aa) == 0 { return false } - for _, a := range aa { - if !a.Validate() { - return false + uniqueAddresses := make(map[netip.Addr]bool, len(*aa)) + for _, a := range *aa { + if a.Validate() { + if provided, found := uniqueAddresses[a.Addr]; !found || !provided && a.Provided { + uniqueAddresses[a.Addr] = a.Provided + } } } - if len(aa) > 1 { - sort.Sort(aa) + if len(uniqueAddresses) == 0 { + return false + } + *aa = make(RequestAddresses, 0, len(uniqueAddresses)) + for a, p := range uniqueAddresses { + *aa = append(*aa, RequestAddress{a, p}) + } + if len(*aa) > 1 { + sort.Sort(*aa) } return true } diff --git a/bittorrent/request_test.go b/bittorrent/request_test.go new file mode 100644 index 0000000..eb407ae --- /dev/null +++ b/bittorrent/request_test.go @@ -0,0 +1,31 @@ +package bittorrent + +import ( + "net/netip" + "testing" + + "github.com/stretchr/testify/require" +) + +// TestRequestAddresses_Validate test fix issue RM#5617 +func TestRequestAddresses_Validate(t *testing.T) { + ra := make(RequestAddresses, 0, 3) + ra = append(ra, RequestAddress{ + Addr: netip.MustParseAddr("1.2.3.4"), + Provided: false, + }) + ra = append(ra, RequestAddress{ + Addr: netip.MustParseAddr("1.2.3.4"), + Provided: true, + }) + ra = append(ra, RequestAddress{ + Addr: netip.MustParseAddr("4.3.2.1"), + Provided: false, + }) + ra = append(ra, RequestAddress{ + Addr: netip.MustParseAddr("4.3.2.1"), + Provided: false, + }) + require.True(t, ra.Validate()) + require.Equal(t, 2, len(ra)) +} diff --git a/frontend/http/parser.go b/frontend/http/parser.go index 16e5f26..b104c87 100644 --- a/frontend/http/parser.go +++ b/frontend/http/parser.go @@ -4,6 +4,7 @@ import ( "errors" "net/http" "net/netip" + "strings" "github.com/sot-tech/mochi/bittorrent" "github.com/sot-tech/mochi/frontend" @@ -149,8 +150,14 @@ func requestedIPs(r *http.Request, p bittorrent.Params, opts ParseOptions) (addr } } - if ipStr := r.Header.Get(opts.RealIPHeader); ipStr != "" && opts.RealIPHeader != "" { - addresses.Add(parseRequestAddress(ipStr, false)) + if ipValues := r.Header.Values(opts.RealIPHeader); len(ipValues) > 0 && opts.RealIPHeader != "" { + for _, ipStr := range ipValues { + for _, ipStr := range strings.Split(ipStr, ",") { + if ipStr = strings.TrimSpace(ipStr); len(ipStr) > 0 { + addresses.Add(parseRequestAddress(ipStr, false)) + } + } + } } else { addrPort, _ := netip.ParseAddrPort(r.RemoteAddr) addresses.Add(bittorrent.RequestAddress{