fix(web_connectivity@v0.5): limit number of redirects (#965)

Part of https://github.com/ooni/probe/issues/2237
This commit is contained in:
Simone Basso 2022-09-15 08:46:53 +02:00 committed by GitHub
parent 700f94b62e
commit ad01856beb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 44 deletions

View File

@ -38,6 +38,9 @@ type CleartextFlow struct {
// Logger is the MANDATORY logger to use. // Logger is the MANDATORY logger to use.
Logger model.Logger Logger model.Logger
// NumRedirects it the MANDATORY counter of the number of redirects.
NumRedirects *NumRedirects
// TestKeys is MANDATORY and contains the TestKeys. // TestKeys is MANDATORY and contains the TestKeys.
TestKeys *TestKeys TestKeys *TestKeys
@ -259,8 +262,8 @@ func (t *CleartextFlow) httpTransaction(ctx context.Context, network, address, a
// maybeFollowRedirects follows redirects if configured and needed // maybeFollowRedirects follows redirects if configured and needed
func (t *CleartextFlow) maybeFollowRedirects(ctx context.Context, resp *http.Response) { func (t *CleartextFlow) maybeFollowRedirects(ctx context.Context, resp *http.Response) {
if !t.FollowRedirects { if !t.FollowRedirects || !t.NumRedirects.CanFollowOneMoreRedirect() {
return // not configured return // not configured or too many redirects
} }
switch resp.StatusCode { switch resp.StatusCode {
case 301, 302, 307, 308: case 301, 302, 307, 308:
@ -270,19 +273,20 @@ func (t *CleartextFlow) maybeFollowRedirects(ctx context.Context, resp *http.Res
} }
t.Logger.Infof("redirect to: %s", location.String()) t.Logger.Infof("redirect to: %s", location.String())
resolvers := &DNSResolvers{ resolvers := &DNSResolvers{
CookieJar: t.CookieJar, CookieJar: t.CookieJar,
DNSCache: t.DNSCache, DNSCache: t.DNSCache,
Domain: location.Hostname(), Domain: location.Hostname(),
IDGenerator: t.IDGenerator, IDGenerator: t.IDGenerator,
Logger: t.Logger, Logger: t.Logger,
TestKeys: t.TestKeys, NumRedirects: t.NumRedirects,
URL: location, TestKeys: t.TestKeys,
ZeroTime: t.ZeroTime, URL: location,
WaitGroup: t.WaitGroup, ZeroTime: t.ZeroTime,
Referer: resp.Request.URL.String(), WaitGroup: t.WaitGroup,
Session: nil, // no need to issue another control request Referer: resp.Request.URL.String(),
THAddr: "", // ditto Session: nil, // no need to issue another control request
UDPAddress: t.UDPAddress, THAddr: "", // ditto
UDPAddress: t.UDPAddress,
} }
resolvers.Start(ctx) resolvers.Start(ctx)
default: default:

View File

@ -39,6 +39,9 @@ type DNSResolvers struct {
// Logger is the MANDATORY logger to use. // Logger is the MANDATORY logger to use.
Logger model.Logger Logger model.Logger
// NumRedirects it the MANDATORY counter of the number of redirects.
NumRedirects *NumRedirects
// TestKeys is MANDATORY and contains the TestKeys. // TestKeys is MANDATORY and contains the TestKeys.
TestKeys *TestKeys TestKeys *TestKeys
@ -434,6 +437,7 @@ func (t *DNSResolvers) startCleartextFlows(
DNSCache: t.DNSCache, DNSCache: t.DNSCache,
IDGenerator: t.IDGenerator, IDGenerator: t.IDGenerator,
Logger: t.Logger, Logger: t.Logger,
NumRedirects: t.NumRedirects,
TestKeys: t.TestKeys, TestKeys: t.TestKeys,
ZeroTime: t.ZeroTime, ZeroTime: t.ZeroTime,
WaitGroup: t.WaitGroup, WaitGroup: t.WaitGroup,
@ -475,6 +479,7 @@ func (t *DNSResolvers) startSecureFlows(
DNSCache: t.DNSCache, DNSCache: t.DNSCache,
IDGenerator: t.IDGenerator, IDGenerator: t.IDGenerator,
Logger: t.Logger, Logger: t.Logger,
NumRedirects: t.NumRedirects,
TestKeys: t.TestKeys, TestKeys: t.TestKeys,
ZeroTime: t.ZeroTime, ZeroTime: t.ZeroTime,
WaitGroup: t.WaitGroup, WaitGroup: t.WaitGroup,

View File

@ -36,7 +36,7 @@ func (m *Measurer) ExperimentName() string {
// ExperimentVersion implements model.ExperimentMeasurer. // ExperimentVersion implements model.ExperimentMeasurer.
func (m *Measurer) ExperimentVersion() string { func (m *Measurer) ExperimentVersion() string {
return "0.5.17" return "0.5.18"
} }
// Run implements model.ExperimentMeasurer. // Run implements model.ExperimentMeasurer.
@ -108,19 +108,20 @@ func (m *Measurer) Run(ctx context.Context, sess model.ExperimentSession,
// start background tasks // start background tasks
resos := &DNSResolvers{ resos := &DNSResolvers{
DNSCache: NewDNSCache(), DNSCache: NewDNSCache(),
Domain: URL.Hostname(), Domain: URL.Hostname(),
IDGenerator: idGenerator, IDGenerator: idGenerator,
Logger: sess.Logger(), Logger: sess.Logger(),
TestKeys: tk, NumRedirects: NewNumRedirects(5),
URL: URL, TestKeys: tk,
ZeroTime: measurement.MeasurementStartTimeSaved, URL: URL,
WaitGroup: wg, ZeroTime: measurement.MeasurementStartTimeSaved,
CookieJar: jar, WaitGroup: wg,
Referer: "", CookieJar: jar,
Session: sess, Referer: "",
THAddr: thAddr, Session: sess,
UDPAddress: "", THAddr: thAddr,
UDPAddress: "",
} }
resos.Start(ctx) resos.Start(ctx)

View File

@ -0,0 +1,23 @@
package webconnectivity
import "github.com/ooni/probe-cli/v3/internal/atomicx"
// NumRedirects counts the number of redirects left.
type NumRedirects struct {
count *atomicx.Int64
}
// NewNumRedirects creates a new NumRedirects instance.
func NewNumRedirects(n int64) *NumRedirects {
count := &atomicx.Int64{}
count.Add(n)
return &NumRedirects{
count: count,
}
}
// CanFollowOneMoreRedirect returns true if we are
// allowed to follow one more redirect.
func (nr *NumRedirects) CanFollowOneMoreRedirect() bool {
return nr.count.Add(-1) > 0
}

View File

@ -39,6 +39,9 @@ type SecureFlow struct {
// Logger is the MANDATORY logger to use. // Logger is the MANDATORY logger to use.
Logger model.Logger Logger model.Logger
// NumRedirects it the MANDATORY counter of the number of redirects.
NumRedirects *NumRedirects
// TestKeys is MANDATORY and contains the TestKeys. // TestKeys is MANDATORY and contains the TestKeys.
TestKeys *TestKeys TestKeys *TestKeys
@ -311,8 +314,8 @@ func (t *SecureFlow) httpTransaction(ctx context.Context, network, address, alpn
// maybeFollowRedirects follows redirects if configured and needed // maybeFollowRedirects follows redirects if configured and needed
func (t *SecureFlow) maybeFollowRedirects(ctx context.Context, resp *http.Response) { func (t *SecureFlow) maybeFollowRedirects(ctx context.Context, resp *http.Response) {
if !t.FollowRedirects { if !t.FollowRedirects || !t.NumRedirects.CanFollowOneMoreRedirect() {
return // not configured return // not configured or too many redirects
} }
switch resp.StatusCode { switch resp.StatusCode {
case 301, 302, 307, 308: case 301, 302, 307, 308:
@ -322,19 +325,20 @@ func (t *SecureFlow) maybeFollowRedirects(ctx context.Context, resp *http.Respon
} }
t.Logger.Infof("redirect to: %s", location.String()) t.Logger.Infof("redirect to: %s", location.String())
resolvers := &DNSResolvers{ resolvers := &DNSResolvers{
CookieJar: t.CookieJar, CookieJar: t.CookieJar,
DNSCache: t.DNSCache, DNSCache: t.DNSCache,
Domain: location.Hostname(), Domain: location.Hostname(),
IDGenerator: t.IDGenerator, IDGenerator: t.IDGenerator,
Logger: t.Logger, Logger: t.Logger,
TestKeys: t.TestKeys, NumRedirects: t.NumRedirects,
URL: location, TestKeys: t.TestKeys,
ZeroTime: t.ZeroTime, URL: location,
WaitGroup: t.WaitGroup, ZeroTime: t.ZeroTime,
Referer: resp.Request.URL.String(), WaitGroup: t.WaitGroup,
Session: nil, // no need to issue another control request Referer: resp.Request.URL.String(),
THAddr: "", // ditto Session: nil, // no need to issue another control request
UDPAddress: t.UDPAddress, THAddr: "", // ditto
UDPAddress: t.UDPAddress,
} }
resolvers.Start(ctx) resolvers.Start(ctx)
default: default: