doppel

package
v2.2.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 7, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DoppelGangerMaxDurations  = 4096
	DoppelGangerScoutRaidEach = 6 * time.Hour
	DoppelGangerScoutRepeats  = 10

	MinCertSizesToCalculate = 3
)
View Source
const (
	// Please see Stats description
	// https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency/
	// https://github.com/cloudflare/sslconfig/blob/master/patches/nginx__dynamic_tls_records.patch
	TLSRecordSizeStart = 1450
	TLSRecordSizeAccel = 4096
	TLSRecordSizeMax   = 16384 - tls.SizeHeader

	TLSCounterAccelAfter = 40
	TLSCounterMaxAfter   = TLSCounterAccelAfter + 20

	TLSRecordSizeResetAfter = time.Second
)
View Source
const (
	StatsBisectTimes = 70
	StatsLowK        = 0.01
	StatsHighK       = 10.0

	// do not calculate statistics if we have < than this number of durations
	MinDurationsToCalculate = 100

	// these values are taken from ok.ru. measured from moscow site.
	StatsDefaultK      = 0.37846373895785335
	StatsDefaultLambda = 1.73177086015485

	// how many bytes should we drift
	DRSNoise = 100
)
View Source
const (
	ScoutConnCollectedPreallocSize = 100
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Conn

type Conn struct {
	essentials.Conn
	// contains filtered or unexported fields
}

func NewConn

func NewConn(ctx context.Context, conn essentials.Conn, stats Stats) Conn

func (Conn) Stop

func (c Conn) Stop()

func (Conn) Write

func (c Conn) Write(p []byte) (int, error)

type Ganger

type Ganger struct {
	// contains filtered or unexported fields
}

func NewGanger

func NewGanger(
	ctx context.Context,
	network Network,
	logger Logger,
	scoutEach time.Duration,
	scoutRepeats int,
	urls []string,
	drs bool,
) *Ganger

func (*Ganger) NewConn

func (g *Ganger) NewConn(conn essentials.Conn) (Conn, error)

func (*Ganger) NoiseParams added in v2.2.5

func (g *Ganger) NoiseParams() NoiseParams

NoiseParams returns the current cert-size-based noise parameters. Returns zero-value NoiseParams if not yet measured (caller should use fallback).

func (*Ganger) Run

func (g *Ganger) Run()

func (*Ganger) Shutdown

func (g *Ganger) Shutdown()

type Logger

type Logger interface {
	Info(msg string)
	WarningError(msg string, err error)
}

type Network

type Network interface {
	// Dial establishes context-free TCP connections.
	Dial(network, address string) (essentials.Conn, error)

	// DialContext dials using a context. This is a preferable way of
	// establishing TCP connections.
	DialContext(ctx context.Context, network, address string) (essentials.Conn, error)

	// MakeHTTPClient build an HTTP client with given dial function. If nothing is
	// provided, then DialContext of this interface is going to be used.
	MakeHTTPClient(func(ctx context.Context, network, address string) (essentials.Conn, error)) *http.Client

	// NativeDialer returns a configured instance of native dialer that
	// skips proxy connections or any other irrelevant settings.
	NativeDialer() *net.Dialer
}

copypasted from mtglib

type NoiseParams added in v2.2.5

type NoiseParams struct {
	Mean   int
	Jitter int
}

NoiseParams holds the measured cert chain size for FakeTLS noise calibration. If Mean is 0, the caller should use a legacy fallback.

type Scout

type Scout struct {
	// contains filtered or unexported fields
}

func NewScout

func NewScout(network Network, urls []string) Scout

func (Scout) Learn

func (s Scout) Learn(ctx context.Context) (ScoutResult, error)

type ScoutConn

type ScoutConn struct {
	tls.Conn
	// contains filtered or unexported fields
}

func NewScoutConn

func NewScoutConn(conn essentials.Conn, results *ScoutConnCollected) *ScoutConn

func (*ScoutConn) Read

func (s *ScoutConn) Read(p []byte) (int, error)

func (*ScoutConn) Write added in v2.2.5

func (s *ScoutConn) Write(p []byte) (int, error)

type ScoutConnCollected

type ScoutConnCollected struct {
	// contains filtered or unexported fields
}

func NewScoutConnCollected

func NewScoutConnCollected() *ScoutConnCollected

func (*ScoutConnCollected) Add

func (s *ScoutConnCollected) Add(record byte, payloadLen int)

func (*ScoutConnCollected) MarkWrite added in v2.2.5

func (s *ScoutConnCollected) MarkWrite()

MarkWrite records the current data length as the handshake boundary.

func (*ScoutConnCollected) Snapshot added in v2.2.7

func (s *ScoutConnCollected) Snapshot() ([]ScoutConnResult, int)

Snapshot returns a copy of the collected data and the write index.

type ScoutConnResult

type ScoutConnResult struct {
	// contains filtered or unexported fields
}

type ScoutResult added in v2.2.5

type ScoutResult struct {
	Durations []time.Duration
	CertSize  int // total ApplicationData bytes during TLS handshake; 0 if unknown
}

ScoutResult holds measurements from a single scout HTTP request.

type Stats

type Stats struct {
	// contains filtered or unexported fields
}

Stats is responsible for generating values that are distributed according to some statistical distribution.

It follows several ideas:

  1. Based on nginx and Cloudflare behaviour, even if server is eager to send a lot, they all start with small TLS packets that are approximately MTU-sized. After
  2. After ~40 TLS records, server considers TCP session as somewhat solid and reliable and ramps up to 4096.
  3. After ~20 TLS records more it jumps to the max 16384 bytes and keep this size as long as it can
  4. If there is no any byte within a connection for a longer time period, this counter resets.

This is called Dynamic TLS Record Sizing

And this optimized for the very first byte, so web browsers could start to render as early as possible, showing user some preliminary results, optimizing for perceived latency.

Since this is very typical for the website, we also aim for that.

Another important idea is how delays between TLS packets are distributed. In case of sending huge heavy content with max sized record, delays have lognormal distribution. But a nature of a typical website shows that it eagers to deliver as fast as it can in a few very first records and could possibly slow down later.

This is perfectly described by Weibull distribution:

In other word, a combination of Dynamic TLS Record Sizing hints us for Weibull distribution.

But we also have to keep in mind that DRS is not well spread yet. In most cases users still rely on OpenSSL or webserver defaults. OpenSSL chunks with biggest packet sizes, nginx relies on static setting that is 16k by default. Thus, dynamic sizing has to be present but we cannot oblige users to use that.

func NewStats

func NewStats(durations []time.Duration, drs bool) Stats

func (*Stats) Delay

func (d *Stats) Delay() time.Duration

func (*Stats) Size

func (d *Stats) Size() int

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL