netsim

package
v0.0.0-...-90ffb0e Latest Latest
Warning

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

Go to latest
Published: Mar 9, 2025 License: GPL-3.0 Imports: 17 Imported by: 0

Documentation

Overview

Package netsim provides a simple network simulation framework that developers can use to write integration tests.

Usage and Features

The NewStack function creates a new, simulated network stack using a given IP address. You can invoke usual functions on the stack, such as:

- DialContext - Listen - ListenPacket

These functions return simulated net.Conn, net.Listener, and net.PacketConn respectively.

When a connection sends data, the data is wrapped inside a [*Packet] emitted on the channel returned by [*Stack.Output]. The *Link type allows connecting two *Stack such that they can send [*Packet] to each other. To send a [*Packet] to a *Stack, you need to post the packet on the channel returned by [*Stack.Input]. You don't need to use a *Link as long as you correctly forward packets. In fact, for simulating complex censorship scenarios, you probably want to write custom code to forward or drop [*Packet]. In the future, there will be subpackages of netsim providing this functionality.

Subpackages of this package contain extensions. For example, the netsim/simpki package code helps to simulate a PKI.

The implementation of net.Conn, net.Listener, and net.PacketConn are [*TCPConn], [*UDPConn], and [*UDPListener]. These types, which can also be created manually, are tiny wrappers around [*Port], which contains most of the common implementation code. These types are public to enable writing more complex tests (e.g., the sending of unexpected TCP flags).

The errors returned by these types are the same syscall.Errno the standard library and the kernel would generate in similar cases (we use the x/sys repository to pull system-dependent error values).

This package contains comprehensive examples showing how to use it.

Design Documents

This package is experimental and has no design documents for now.

Example (BlockpageTransparentProxy)

This example shows how to use netsim to simulate transparent proxying of HTTP requests to serve blockpages.

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"net/netip"

	"github.com/rbmk-project/x/netsim"
	"github.com/rbmk-project/x/netsim/censor"
)

func main() {
	// Create scenario
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create blockpage server
	blockpage := scenario.MustNewBlockpageStack()
	scenario.Attach(blockpage)

	// Create target website
	scenario.Attach(scenario.MustNewExampleComStack())

	// Configure DNAT to send blocked traffic to blockpage server
	scenario.Router().AddFilter(censor.NewDNatter(
		netip.MustParseAddr("193.206.158.22"),       // source addr
		netip.MustParseAddrPort("93.184.216.34:80"), // target dest epnt
		netip.MustParseAddrPort("10.10.34.35:80"),   // repl dest epnt
	))

	// Create client stack
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create the HTTP client
	clientTxp := scenario.NewHTTPTransport(clientStack)
	defer clientTxp.CloseIdleConnections()
	clientHTTP := &http.Client{Transport: clientTxp}

	// Get the response body.
	resp, err := clientHTTP.Get("http://93.184.216.34/")
	if err != nil {
		log.Fatal(err)
	}
	if resp.StatusCode != http.StatusForbidden {
		log.Fatalf("HTTP request failed: %d", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// Print the response body
	fmt.Printf("%s", string(body))

}
Output:

Access to this website has been blocked by network policy.
Example (CensorDNS)

This example shows how to use netsim to simulate GFW-style DNS censorship, where poisoned responses are injected before the legitimate response arrives. The example demonstrates:

1. how to configure DNS poisoning using a database 2. how to collect multiple DNS responses using dnscore 3. the expected order of responses (poisoned then legitimate)

This example DOES NOT show how to validate responses using [dnscore] since that is outside its specific scope.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/miekg/dns"
	"github.com/rbmk-project/dnscore"
	"github.com/rbmk-project/x/netsim"
	"github.com/rbmk-project/x/netsim/censor"

	netsimdns "github.com/rbmk-project/x/netsim/dns"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google (8.8.8.8).
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Configure DNS poisoning happening on the scenario router
	// thus modeling the typical behaviour of the GFW.
	censorDB := netsimdns.NewDatabase()
	censorDB.AddAddresses([]string{"dns.google"}, []string{"10.0.0.1"})
	scenario.Router().AddFilter(censor.NewDNSPoisoner(censorDB))

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create a context with a watchdog timeout.
	//
	// In real measurements this would typically be controlled by
	// --wait-duplicates or natural timing of other operations like
	// TCP/TLS handshakes and fetching related web pages.
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	// Create DNS query for dns.google A record
	query, err := dnscore.NewQuery("dns.google.", dns.TypeA)
	if err != nil {
		log.Fatal(err)
	}

	// Configure transport to use our simulated network
	txp := &dnscore.Transport{}
	txp.DialContext = clientStack.DialContext

	// Query 8.8.8.8 over UDP and collect responses
	serverAddr := &dnscore.ServerAddr{
		Protocol: dnscore.ProtocolUDP,
		Address:  "8.8.8.8:53",
	}
	results := txp.QueryWithDuplicates(ctx, serverAddr, query)

	// Print responses as they arrive.
	//
	// We expect:
	//
	// 1. poisoned response (10.0.0.1) from router
	//
	// 2. legitimate response (8.8.8.8) from server
	//
	// After two responses, we cancel the context. In production,
	// we will stop after a timeout or perform other operations and
	// then check whether there are more addresses to measure.
	var count int
	for result := range results {
		if err := result.Err; err != nil {
			// Errors here typically are caused by us closing
			// the connection and, anyway, for this test we only
			// care about seeing the duplicate responses.
			break
		}
		for _, ans := range result.Msg.Answer {
			if a, ok := ans.(*dns.A); ok {
				fmt.Printf("%s\n", a.A.String())
			}
		}
		count++
		if count >= 2 {
			cancel()
		}
	}

}
Output:

10.0.0.1
8.8.8.8
Example (DnsOverHTTPS)

This example shows how to use netsim to simulate a DNS server that listens for incoming requests over HTTPS.

package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/miekg/dns"
	"github.com/rbmk-project/dnscore"
	"github.com/rbmk-project/x/netsim"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google.
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create a context with a watchdog timeout.
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	// Create the dnscore transport and the server address
	txp := &dnscore.Transport{
		HTTPClient: &http.Client{
			Transport: &http.Transport{
				DialContext: clientStack.DialContext,
				TLSClientConfig: &tls.Config{
					RootCAs:    scenario.RootCAs(),
					ServerName: "dns.google",
				},
			},
		},
	}
	serverAddr := dnscore.NewServerAddr(
		dnscore.ProtocolDoH, "https://8.8.8.8/dns-query")

	// Create the query to send
	query, err := dnscore.NewQuery("dns.google", dns.TypeA)
	if err != nil {
		log.Fatal(err)
	}

	// Perform the DNS round trip
	resp, err := txp.Query(ctx, serverAddr, query)
	if err != nil {
		log.Fatal(err)
	}

	// Print the responses
	for _, ans := range resp.Answer {
		if a, ok := ans.(*dns.A); ok {
			fmt.Printf("%s\n", a.A.String())
		}
	}

}
Output:

8.8.8.8
Example (DnsOverTCP)

This example shows how to use netsim to simulate a DNS server that listens for incoming requests over TCP.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/miekg/dns"
	"github.com/rbmk-project/x/netsim"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google.
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create a context with a watchdog timeout.
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	// Create the client connection with the DNS server.
	conn, err := clientStack.DialContext(ctx, "tcp", "8.8.8.8:53")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	// Create the query to send
	query := new(dns.Msg)
	query.Id = dns.Id()
	query.RecursionDesired = true
	query.Question = []dns.Question{{
		Name:   "dns.google.",
		Qtype:  dns.TypeA,
		Qclass: dns.ClassINET,
	}}

	// Perform the DNS round trip
	clientDNS := &dns.Client{Net: "tcp"}
	resp, _, err := clientDNS.ExchangeWithConnContext(ctx, query, &dns.Conn{Conn: conn})
	if err != nil {
		log.Fatal(err)
	}

	// Print the responses
	for _, ans := range resp.Answer {
		if a, ok := ans.(*dns.A); ok {
			fmt.Printf("%s\n", a.A.String())
		}
	}

}
Output:

8.8.8.8
Example (DnsOverTLS)

This example shows how to use netsim to simulate a DNS server that listens for incoming requests over TLS.

package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"time"

	"github.com/miekg/dns"
	"github.com/rbmk-project/x/netsim"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google.
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create a context with a watchdog timeout.
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	// Create the client connection with the DNS server.
	conn, err := clientStack.DialContext(ctx, "tcp", "8.8.8.8:853")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	tconn := tls.Client(conn, &tls.Config{
		RootCAs:    scenario.RootCAs(),
		NextProtos: []string{"dot"},
		ServerName: "dns.google",
	})
	defer tconn.Close()
	if err := tconn.HandshakeContext(ctx); err != nil {
		log.Fatal(err)
	}

	// Create the query to send
	query := new(dns.Msg)
	query.Id = dns.Id()
	query.RecursionDesired = true
	query.Question = []dns.Question{{
		Name:   "dns.google.",
		Qtype:  dns.TypeA,
		Qclass: dns.ClassINET,
	}}

	// Perform the DNS round trip
	clientDNS := &dns.Client{Net: "tcp-tls"}
	resp, _, err := clientDNS.ExchangeWithConnContext(ctx, query, &dns.Conn{Conn: tconn})
	if err != nil {
		log.Fatal(err)
	}

	// Print the responses
	for _, ans := range resp.Answer {
		if a, ok := ans.(*dns.A); ok {
			fmt.Printf("%s\n", a.A.String())
		}
	}

}
Output:

8.8.8.8
Example (DnsOverUDP)

This example shows how to use netsim to simulate a DNS server that listens for incoming requests over UDP.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/miekg/dns"
	"github.com/rbmk-project/x/netsim"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google.
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create a context with a watchdog timeout.
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	// Create the client connection with the DNS server.
	conn, err := clientStack.DialContext(ctx, "udp", "8.8.8.8:53")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	// Create the query to send
	query := new(dns.Msg)
	query.Id = dns.Id()
	query.RecursionDesired = true
	query.Question = []dns.Question{{
		Name:   "dns.google.",
		Qtype:  dns.TypeA,
		Qclass: dns.ClassINET,
	}}

	// Perform the DNS round trip
	clientDNS := &dns.Client{}
	resp, _, err := clientDNS.ExchangeWithConnContext(ctx, query, &dns.Conn{Conn: conn})
	if err != nil {
		log.Fatal(err)
	}

	// Print the responses
	for _, ans := range resp.Answer {
		if a, ok := ans.(*dns.A); ok {
			fmt.Printf("%s\n", a.A.String())
		}
	}

}
Output:

8.8.8.8
Example (Http)

This example shows how to use netsim to simulate an HTTP server that listens for incoming cleartext requests.

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"

	"github.com/rbmk-project/x/netsim"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating www.example.com.
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewExampleComStack())

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create the HTTP client
	clientTxp := scenario.NewHTTPTransport(clientStack)
	defer clientTxp.CloseIdleConnections()
	clientHTTP := &http.Client{Transport: clientTxp}

	// Get the response body.
	resp, err := clientHTTP.Get("http://93.184.216.34/")
	if err != nil {
		log.Fatal(err)
	}
	if resp.StatusCode != http.StatusOK {
		log.Fatalf("HTTP request failed: %d", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// Print the response body
	fmt.Printf("%s", string(body))

}
Output:

Example Web Server.
Example (Https)

This example shows how to use netsim to simulate an HTTPS server that listens for incoming encrypted requests.

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"

	"github.com/rbmk-project/x/netsim"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google.
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create the HTTP client
	clientTxp := scenario.NewHTTPTransport(clientStack)
	defer clientTxp.CloseIdleConnections()
	clientHTTP := &http.Client{Transport: clientTxp}

	// Get the response body.
	resp, err := clientHTTP.Get("https://8.8.8.8/")
	if err != nil {
		log.Fatal(err)
	}
	if resp.StatusCode != http.StatusOK {
		log.Fatalf("HTTP request failed: %d", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// Print the response body
	fmt.Printf("%s", string(body))

}
Output:

Google Public DNS server.
Example (Router)

This example shows how to use a router to simulate a network topology consisting of a client and multiple servers.

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"time"

	"github.com/rbmk-project/x/netsim"
	"github.com/rbmk-project/x/netsim/geolink"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google.
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Create server stack emulating www.example.com.
	scenario.Attach(scenario.MustNewExampleComStack())

	// Create the client stack, build a geographic point-to-point link
	// and attach the scenario router to the other end of the link.
	clientStack := scenario.MustNewClientStack()
	linkDev := geolink.Extend(clientStack, &geolink.Config{
		Delay: 10 * time.Millisecond,
		Log:   true,
	})
	scenario.Attach(linkDev)

	// Create the HTTP client
	clientTxp := scenario.NewHTTPTransport(clientStack)
	defer clientTxp.CloseIdleConnections()
	clientHTTP := &http.Client{Transport: clientTxp}

	// Get the response body.
	resp, err := clientHTTP.Get("https://www.example.com/")
	if err != nil {
		log.Fatal(err)
	}
	if resp.StatusCode != http.StatusOK {
		log.Fatalf("HTTP request failed: %d", resp.StatusCode)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// Print the response body
	fmt.Printf("%s", string(body))

}
Output:

Example Web Server.
Example (Tls)

This example shows how to use netsim to simulate a TLS server that listens for incoming encrypted requests.

package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"
	"time"

	"github.com/rbmk-project/x/netsim"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google.
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create a context with a watchdog timeout.
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	// Connect to the server
	conn, err := clientStack.DialContext(ctx, "tcp", "8.8.8.8:443")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	// Perform the TLS handshake
	tconn := tls.Client(conn, &tls.Config{
		RootCAs:    scenario.RootCAs(),
		ServerName: "dns.google",
	})
	defer tconn.Close()
	if err := tconn.HandshakeContext(ctx); err != nil {
		log.Fatal(err)
	}

	// Print the handshake result
	fmt.Printf("%v", err)

}
Output:

<nil>
Example (TlsBlackholing)

This example shows how to use netsim to simulate SNI-based TLS blocking using connection blackholing.

package main

import (
	"fmt"
	"net/http"
	"net/netip"
	"time"

	"github.com/rbmk-project/x/netsim"
	"github.com/rbmk-project/x/netsim/censor"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google.
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Configure blackholing on the scenario router targeting
	// connections where the SNI matches "dns.google"
	scenario.Router().AddFilter(censor.NewBlackholer(
		300*time.Second,      // residual censorship duration
		netip.AddrPort{},     // match any endpoint
		[]byte("dns.google"), // match SNI
	))

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create the HTTP client with a short timeout
	clientTxp := scenario.NewHTTPTransport(clientStack)
	defer clientTxp.CloseIdleConnections()
	clientHTTP := &http.Client{
		Transport: clientTxp,
		Timeout:   200 * time.Millisecond, // short timeout for testing
	}

	// Attempt the HTTPS request, which should time out
	_, err := clientHTTP.Get("https://dns.google/")
	fmt.Printf("err: %v\n", err)

}
Output:

err: Get "https://dns.google/": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
Example (TlsRSTInjection)

This example shows how to use netsim to simulate SNI-based TLS blocking using RST injection.

package main

import (
	"fmt"
	"net/http"
	"net/netip"

	"github.com/rbmk-project/x/netsim"
	"github.com/rbmk-project/x/netsim/censor"
)

func main() {
	// Create a new scenario using the given directory to cache
	// the certificates used by the simulated PKI
	scenario := netsim.NewScenario("testdata")
	defer scenario.Close()

	// Create server stack emulating dns.google.
	//
	// This includes:
	//
	// 1. creating, attaching, and enabling routing for a server stack
	//
	// 2. registering the proper domain names and addresses
	//
	// 3. updating the PKI database to include the server's certificate
	scenario.Attach(scenario.MustNewGoogleDNSStack())

	// Configure RST injection on the scenario router targeting
	// connections where the SNI matches "dns.google"
	scenario.Router().AddFilter(censor.NewTCPResetter(
		netip.AddrPort{},     // match any endpoint
		[]byte("dns.google"), // match SNI
	))

	// Create and attach the client stack.
	clientStack := scenario.MustNewClientStack()
	scenario.Attach(clientStack)

	// Create the HTTP client
	clientTxp := scenario.NewHTTPTransport(clientStack)
	defer clientTxp.CloseIdleConnections()
	clientHTTP := &http.Client{Transport: clientTxp}

	// Attempt the HTTPS request, which should fail due to RST
	_, err := clientHTTP.Get("https://dns.google/")
	fmt.Printf("err: %v\n", err)

}
Output:

err: Get "https://dns.google/": connection reset by peer

Index

Examples

Constants

This section is empty.

Variables

View Source
var NewLink = link.New

NewLink is an alias for link.New.

View Source
var NewStack = netstack.New

NewStack is an alias for netstack.New.

Functions

func NewDNSHTTPHandler

func NewDNSHTTPHandler(dd dns.Database) http.Handler

NewDNSHTTPHandler returns an http.Handler handling DNS-over-HTTPS.

Types

type DNSHandler

type DNSHandler = dns.Handler

DNSHandler is an alias for dns.Handler.

type Link = link.Link

Link is an alias for link.Link.

type Scenario

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

Scenario manages network simulation components using a star topology, where all stacks are connected through a central router.

This means:

1. Each stack is connected only to the central router;

2. The router forwards packets between stacks.

func NewScenario

func NewScenario(cacheDir string) *Scenario

NewScenario creates a new network simulation scenario.

The cacheDir caches simulated-PKI-related data.

func (*Scenario) Attach

func (s *Scenario) Attach(dev packet.NetworkDevice)

Attach connects a device to the scenario's central router to read packets from the device, and sets up the route to that return packets correctly reach the device.

The common case is to attach a *Stack but other cases are also possible. Suppose a *Stack is linked to a firewall through a link, then you can also attach the firewall to the router.

All network traffic to/from this device will flow through the router.

func (*Scenario) Close

func (s *Scenario) Close() error

Close releases all resources associated with the scenario.

func (*Scenario) DNSHandler

func (s *Scenario) DNSHandler() DNSHandler

DNSHandler returns the DNSHandler for the scenario. The returned handler will serve queries based on the scenario's DNS database.

func (*Scenario) MustNewBlockpageStack

func (s *Scenario) MustNewBlockpageStack() *Stack

MustNewBlockpageStack creates a new stack simulating a censorship blockpage server.

It serves a simple warning page on HTTP/HTTPS indicating that the content has been blocked.

func (*Scenario) MustNewClientStack

func (s *Scenario) MustNewClientStack() *Stack

MustNewClientStack creates a new client stack with standard testing configuration.

We use GARR's (Italian Research & Education Network) public addresses (193.206.158.22 and 2001:760:0:158::22) as default client addresses. These are chosen over documentation ranges (like 192.0.2.0/24) to avoid triggering bogon filters in network simulation scenarios, while still being associated with a public research institution.

The stack uses Google's public DNS addresses as the default resolvers.

func (*Scenario) MustNewExampleComStack

func (s *Scenario) MustNewExampleComStack() *Stack

MustNewExampleComStack creates a new stack simulating www.example.com.

func (*Scenario) MustNewGoogleDNSStack

func (s *Scenario) MustNewGoogleDNSStack() *Stack

MustNewGoogleDNSStack creates a new stack simulating dns.google.

func (*Scenario) MustNewStack

func (s *Scenario) MustNewStack(config *StackConfig) *Stack

MustNewStack creates a new network stack using the given configuration.

This method panics on error.

This method IS NOT goroutine safe.

func (*Scenario) NewHTTPTransport

func (s *Scenario) NewHTTPTransport(stack *Stack) *http.Transport

NewHTTPTransport creates an *http.Transport configured to use the given stack and the scenario's root CAs.

func (*Scenario) RootCAs

func (s *Scenario) RootCAs() *x509.CertPool

RootCAs returns the *x509.CertPool that clients should use.

func (*Scenario) Router

func (s *Scenario) Router() *router.Router

Router returns the *router.Router for the scenario.

type Stack

type Stack = netstack.Stack

Stack is an alias for netstack.Stack.

type StackConfig

type StackConfig struct {
	// Addresses contains the IP addresses for this stack.
	//
	// The config is invalid if there is not at least one address.
	Addresses []string

	// ClientResolvers optionally specifies resolvers for client stacks.
	ClientResolvers []string

	// DNSOverUDPHandler optionally specifies a handler for DNS-over-UDP.
	DNSOverUDPHandler DNSHandler

	// DNSOverTCPHandler optionally specifies a handler for DNS-over-TCP.
	DNSOverTCPHandler DNSHandler

	// DNSOverTLSHandler optionally specifies a handler for DNS-over-TLS.
	DNSOverTLSHandler DNSHandler

	// DomainNames contains the optional domain names associated with this stack.
	//
	// If there are associated domain names, we will configure the DNS and
	// register related certificates for emulating the PKI.
	DomainNames []string

	// HTTPHandler optionally specifies a handle to use on port 80/tcp.
	HTTPHandler http.Handler

	// HTTPSHandler optionally specifies a handle to use on port 443/tcp.
	HTTPSHandler http.Handler
}

StackConfig contains configuration for creating a new network stack.

Directories

Path Synopsis
Package censor implements common internet censorship techniques for testing.
Package censor implements common internet censorship techniques for testing.
Package models the distributed DNS database.
Package models the distributed DNS database.
Package geolink models a geographic point-to-point link.
Package geolink models a geographic point-to-point link.
Package link models a point-to-point network link.
Package link models a point-to-point network link.
Package packet contains *Packet and the related definitions.
Package packet contains *Packet and the related definitions.
Package router provides network routing capabilities for testing.
Package router provides network routing capabilities for testing.
Package simpki allows to simulate a PKI for testing purposes.
Package simpki allows to simulate a PKI for testing purposes.

Jump to

Keyboard shortcuts

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