quic

package module
v0.0.8 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2021 License: BSD-2-Clause Imports: 15 Imported by: 0

README

Quiwi 🥝

Go Reference

QUIC transport protocol (https://quicwg.org/) implementation in Go. The goal is to provide low level APIs for applications or protocols using QUIC as a transport.

TLS 1.3 support is based on standard Go TLS package (https://github.com/golang/go/tree/master/src/crypto/tls), licensed under the 3-clause BSD license.

Features

  • Handshake with TLS 1.3
  • Version negotiation
  • Address validation
  • Loss detection
  • Congestion control
  • Streams
  • Flow control
  • ChaCha20 header protection
  • TLS session resumption
  • Anti-amplification
  • Unreliable datagram
  • qlog
  • Key update
  • Connection migration
  • Path MTU discovery
  • Zero RTT
  • HTTP/3

Development

Run tests:

go test ./...

Build command:

cd cmd/quiwi
go build

# To enable tracing
go build -tags quicdebug

# Check heap allocations
go build -gcflags '-m' 2>&1 | sort -V > debug.txt
go test -bench BenchmarkStream -run NONE -benchmem -memprofile mem.out -cpuprofile cpu.out
go tool pprof -http=:8080 mem.out

# Raspberry Pi Zero
GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build
APIs

Package transport provides low-level APIs to control QUIC connections. Applications write input data to the connection and read output data for sending to peer.

config := transport.NewConfig()
server, err := transport.Accept(scid, odcid, config)
config := transport.NewConfig()
client, err := transport.Connect(scid, dcid, config)
for !conn.IsClosed() { // Loop until the connection is closed
	timeout := conn.Timeout()
	// (A negative timeout means that the timer should be disarmed)
	select {
		case data := <-dataChanel:  // Got data from peer
			n, err := conn.Write(data)
		case <-time.After(timeout): // Got receiving timeout
			n, err := conn.Write(nil)
	}
	// Get and process connection events
	events = conn.Events(events)
	for { // Loop until err != nil or n == 0
		n, err := conn.Read(buf)
		// Send buf[:n] to peer
	}
}

The root package quic instead provides high-level APIs where QUIC data are transferred over UDP. It also handles version negotiation, address validation and logging.

server := quic.NewServer(config)
server.SetHandler(handler)
err := server.ListenAndServe(address)
client := quic.NewClient(config)
client.SetHandler(handler)
err := client.ListenAndServe(address)
err = client.Connect(serverAddress)
// wait
client.Close()

Applications get connection events in the handler to control QUIC connections:

func (handler) Serve(conn *quic.Conn, events []transport.Event) {
	for _, e := range events {
		switch e.Type {
		case transport.EventConnOpen:
		case transport.EventConnClosed:
		}
	}
}
Server

See cmd/quiwi/server.go

Usage: quiwi server [arguments]
  -cache string
    	certificate cache directory when using ACME (default ".")
  -cert string
    	TLS certificate path
  -domains string
    	allowed host names for ACME (separated by a comma)
  -key string
    	TLS certificate key path
  -listen string
    	listen on the given IP:port (default ":4433")
  -qlog string
    	write logs to qlog file
  -retry
    	enable address validation using Retry packet
  -root string
    	root directory (default "www")
  -v int
    	log verbose: 0=off 1=error 2=info 3=debug 4=trace (default 2)

Examples:

# Listen on port 4433:
./quiwi server -cert ../../testdata/cert.pem -key ../../testdata/key.pem

# Automatically get certificate from Let's Encrypt:
# (This will also listen on TCP port 443 to handle "tls-alpn-01" challenge)
./quiwi server -domains example.com

Add SSLKEYLOGFILE=key.log to have TLS keys logged to file.

Client

See cmd/quiwi/client.go

Usage: quiwi client [arguments] <url>
  -cipher string
    	TLS 1.3 cipher suite, e.g. TLS_CHACHA20_POLY1305_SHA256
  -insecure
    	skip verifying server certificate
  -listen string
    	listen on the given IP:port (default "0.0.0.0:0")
  -qlog string
    	write logs to qlog file
  -root string
    	root download directory
  -v int
    	log verbose: 0=off 1=error 2=info 3=debug 4=trace (default 2)

Examples

./quiwi client https://quic.tech:4433/

./quiwi client -insecure https://localhost:4433/file.txt
Datagram

See cmd/quiwi/datagram.go

Usage: quiwi datagram [arguments] [url]
  -cert string
    	TLS certificate path (server only) (default "cert.pem")
  -data string
    	Datagram for sending (or from stdin if empty)
  -insecure
    	skip verifying server certificate (client only)
  -key string
    	TLS certificate key path (server only) (default "key.pem")
  -listen string
    	listen on the given IP:port (default "0.0.0.0:0")
  -v int
    	log verbose: 0=off 1=error 2=info 3=debug 4=trace (default 2)

Examples:

# Server
./quiwi datagram -listen 127.0.0.1:4433

# Client
./quiwi datagram -insecure -data hello https://127.0.0.1:4433

Testing

See interop/README.md

Fuzzing

See https://github.com/goburrow/quic-fuzz

Documentation

Overview

Package quic provides client and server functions for QUIC connections.

The process of creating a basic client and server looks like this:

config := transport.NewConfig()

client := quic.NewClient(config)
client.SetHandler(handler)
err := client.ListenAndServe(address)
err = client.Connect(serverAddress)
// wait
client.Close()

server := quic.NewServer(config)
server.SetHandler(handler)
err := server.ListenAndServe(address)

The handler is where applications interact with QUIC connections:

func (handler) Serve(conn *quic.Conn, events []transport.Event) {
	for _, e := range events {
		switch e.Type {
		case transport.EventConnOpen:
		case transport.EventConnClosed:
		}
	}
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AddressVerifier added in v0.0.8

type AddressVerifier interface {
	IsActive(addr net.Addr) bool
	// NewToken creates a new token from given address, retry source connection ID
	// and original destination connection ID.
	NewToken(addr net.Addr, rscid, odcid []byte) []byte
	// VerifyToken returns odcid when the address and token pair is valid,
	// empty slice otherwise.
	VerifyToken(addr net.Addr, dcid, token []byte) []byte
}

AddressVerifier generates and validates server retry token. https://www.rfc-editor.org/rfc/rfc9000.html#token-integrity

func NewAddressVerifier added in v0.0.8

func NewAddressVerifier() AddressVerifier

NewAddressVerifier returns a simple implementation of AddressValidator. It encrypts client original CID into token which is valid for 10 seconds.

type CIDIssuer added in v0.0.8

type CIDIssuer interface {
	// NewCID generates a new connection ID.
	NewCID() ([]byte, error)
	// CIDLength returns the length of generated connection id which is needed
	// to decode short-header packets.
	// Currently, only constant length is supported.
	CIDLength() int
}

CIDIssuer generates connection ID.

func NewServerCIDIssuer added in v0.0.8

func NewServerCIDIssuer(id uint64) CIDIssuer

NewServerCIDIssuer returns a new CIDIssuer that creates CID using Plaintext Algorithm. Server ID is encoded in CID using QUIC varint encoding. https://quicwg.org/load-balancers/draft-ietf-quic-load-balancers.html#section-5.1

type Client

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

Client is a client-side QUIC connection. All setters must only be invoked before calling Serve.

func NewClient

func NewClient(config *transport.Config) *Client

NewClient creates a new QUIC client.

func (*Client) Close added in v0.0.3

func (s *Client) Close() error

Close closes all current established connections and listening socket.

func (*Client) Connect

func (s *Client) Connect(addr string) error

Connect establishes a new connection to UDP network address addr.

func (*Client) ListenAndServe added in v0.0.3

func (s *Client) ListenAndServe(addr string) error

ListenAndServe starts listening on UDP network address addr and serves incoming packets. Unlike Server.ListenAndServe, this function does not block as Serve is invoked in a goroutine.

func (*Client) LocalAddr added in v0.0.6

func (s *Client) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Client) Serve added in v0.0.3

func (s *Client) Serve() error

Serve handles requests from given socket.

func (*Client) SetCIDIssuer added in v0.0.8

func (s *Client) SetCIDIssuer(cidIss CIDIssuer)

SetCIDIssuer sets generator for connection ids. By default, it generates random IDs from Reader in crypto/rand. If transport.Config.TLS.Rand is available, it will use that source instead.

func (*Client) SetHandler added in v0.0.2

func (s *Client) SetHandler(v Handler)

SetHandler sets QUIC connection callbacks.

func (*Client) SetListener added in v0.0.4

func (s *Client) SetListener(conn net.PacketConn)

SetListener sets listening socket connection.

func (*Client) SetLogger added in v0.0.2

func (s *Client) SetLogger(level int, w io.Writer)

SetLogger sets transaction logger. It is safe to change connection logger at any time.

type Conn

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

Conn is a QUIC connection presenting a peer connected to this client or server. Conn is not safe for concurrent use. Its methods would only be called inside the Handler callback.

func (*Conn) Close

func (s *Conn) Close() error

Close sets the connection status to closing state. If peer has already initiated closing with an error, this function will return that error, which is either transport.Error or transport.AppError

func (*Conn) CloseWithError added in v0.0.4

func (s *Conn) CloseWithError(code uint64, reason string) error

CloseWithError sets the connection to closing state with an application code code and reason sending to peer. The function returns error if peer has already initiated closing.

func (*Conn) ConnectionState added in v0.0.6

func (s *Conn) ConnectionState() transport.ConnectionState

ConnectionState returns details about the connection.

func (*Conn) Datagram added in v0.0.4

func (s *Conn) Datagram() *Datagram

Datagram returns a Datagram associated with the connection. NOTE: Unlike other Conn.Datagram* functions, the returned Datagram must only be used in a different goroutine (i.e. not in the connection handler). See Datagram struct for details.

func (*Conn) DatagramRead added in v0.0.6

func (s *Conn) DatagramRead(b []byte) (int, error)

DatagramRead pulls received datagram directly from the connection buffer. It returns nil when there is no data to read.

func (*Conn) DatagramWrite added in v0.0.6

func (s *Conn) DatagramWrite(b []byte) (int, error)

DatagramWrite queues data to the connection buffer for sending via datagram.

func (*Conn) LocalAddr added in v0.0.4

func (s *Conn) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Conn) NewStream added in v0.0.4

func (s *Conn) NewStream(bidi bool) (uint64, bool)

NewStream creates and returns a new local stream id. If number of streams exceeds peer limits, the function will return false.

func (*Conn) RemoteAddr

func (s *Conn) RemoteAddr() net.Addr

RemoteAddr returns the remote network address.

func (*Conn) SetUserData added in v0.0.4

func (s *Conn) SetUserData(data interface{})

SetUserData attaches (or removes) user data to the connection.

func (*Conn) Stream

func (s *Conn) Stream(streamID uint64) (*Stream, error)

Stream creates or returns an existing QUIC stream given the ID. NOTE: The returned stream would only be used in a different goroutine. See Stream struct for details.

func (*Conn) StreamClose added in v0.0.6

func (s *Conn) StreamClose(streamID uint64) error

StreamClose gracefully closes sending part of the stream.

func (*Conn) StreamCloseRead added in v0.0.6

func (s *Conn) StreamCloseRead(streamID uint64, errorCode uint64) error

StreamCloseRead terminates reading part of the stream.

func (*Conn) StreamCloseWrite added in v0.0.6

func (s *Conn) StreamCloseWrite(streamID uint64, errorCode uint64) error

StreamCloseWrite terminates sending part of the stream.

func (*Conn) StreamRead added in v0.0.6

func (s *Conn) StreamRead(streamID uint64, b []byte) (int, error)

StreamRead gets data received from the stream buffer.

func (*Conn) StreamWrite added in v0.0.6

func (s *Conn) StreamWrite(streamID uint64, b []byte) (int, error)

StreamWrite adds data to the stream buffer for sending.

func (*Conn) StreamWriteTo added in v0.0.8

func (s *Conn) StreamWriteTo(streamID uint64, w io.Writer) (int64, error)

StreamWriteTo writes stream data to w until there is no more data or when an error occurs.

func (*Conn) UserData added in v0.0.4

func (s *Conn) UserData() interface{}

UserData returns attached data.

type Datagram added in v0.0.6

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

Datagram provides asynchronous APIs to interact which QUIC datagram. All Datagram functions must be used in a separated goroutine that is different to the connection callback. For example:

func (handler) Serve(conn *quic.Conn, events []transport.Event) {
	for _, e := range events {
		switch e.Type {
		case transport.EventDatagramWritable:
			go func(datagram *quic.Datagram) {
				// Working on the datagram.
			}(conn.Datagram())
		}
	}
}

Datagram implements net.Conn.

func (*Datagram) Close added in v0.0.6

func (s *Datagram) Close() error

Close on Datagram does not do anything.

func (*Datagram) LocalAddr added in v0.0.6

func (s *Datagram) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Datagram) Read added in v0.0.6

func (s *Datagram) Read(b []byte) (int, error)

Read reads datagram from the connection.

func (*Datagram) RemoteAddr added in v0.0.6

func (s *Datagram) RemoteAddr() net.Addr

RemoteAddr returns the remote network address.

func (*Datagram) SetDeadline added in v0.0.6

func (s *Datagram) SetDeadline(t time.Time) error

SetDeadline sets the read and write deadlines associated with the stream.

func (*Datagram) SetReadDeadline added in v0.0.6

func (s *Datagram) SetReadDeadline(t time.Time) error

SetReadDeadline sets the read deadline associated with the stream.

func (*Datagram) SetWriteDeadline added in v0.0.6

func (s *Datagram) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets the write deadline associated with the stream.

func (*Datagram) Write added in v0.0.6

func (s *Datagram) Write(b []byte) (int, error)

Write writes data to the stream.

type Handler

type Handler interface {
	// Serve handles connection events.
	// Applications should keep execution time in the callback to a minimum.
	// If extra works needed, applications should consider using asynchronous APIs,
	// i.e Stream functions instead of Conn.Stream* functions.
	// Currently, each connection has its own goroutine for this callback.
	Serve(conn *Conn, events []transport.Event)
}

Handler defines interface to handle QUIC connection states. Multiple goroutines may invoke methods on a Handler simultaneously.

type Server

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

Server is a server-side QUIC connection. All setters must only be invoked before calling Serve.

func NewServer

func NewServer(config *transport.Config) *Server

NewServer creates a new QUIC server.

func (*Server) Close

func (s *Server) Close() error

Close sends Close frame to all connected clients and closes the socket given in Serve. Note: if Close is called before Serve, the socket may not be set so it will not be close. In that case Serve will hang until it gets socket read error.

func (*Server) ListenAndServe added in v0.0.3

func (s *Server) ListenAndServe(addr string) error

ListenAndServe starts listening on UDP network address addr and serves incoming packets.

func (*Server) LocalAddr added in v0.0.6

func (s *Server) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Server) Serve

func (s *Server) Serve() error

Serve handles incoming requests from a socket connection. XXX: Since net.PacketConn methods can be called simultaneously, users should be able to run Serve in multiple goroutines. For example:

s.SetListen(socket)
for i := 1; i < num; i++ {
	go s.Serve()
}
s.Serve() // main one blocking

func (*Server) SetAddressVerifier added in v0.0.8

func (s *Server) SetAddressVerifier(v AddressVerifier)

SetAddressVerifier sets validation for QUIC connections address.

func (*Server) SetCIDIssuer added in v0.0.8

func (s *Server) SetCIDIssuer(cidIss CIDIssuer)

SetCIDIssuer sets generator for connection ids. By default, it generates random IDs from Reader in crypto/rand. If transport.Config.TLS.Rand is available, it will use that source instead.

func (*Server) SetHandler added in v0.0.2

func (s *Server) SetHandler(v Handler)

SetHandler sets QUIC connection callbacks.

func (*Server) SetListener added in v0.0.4

func (s *Server) SetListener(conn net.PacketConn)

SetListener sets listening socket connection.

func (*Server) SetLogger added in v0.0.2

func (s *Server) SetLogger(level int, w io.Writer)

SetLogger sets transaction logger. It is safe to change connection logger at any time.

type Stream added in v0.0.6

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

Stream provides asynchronous APIs to interact which a QUIC stream. All Stream functions must be used in a separated goroutine that is different to the connection callback. For example:

func (handler) Serve(conn *quic.Conn, events []transport.Event) {
	for _, e := range events {
		switch e.Type {
		case transport.EventStreamOpen:
			st, err := conn.Stream(e.ID)
			...
			go func(stream *quic.Stream) {
				// Working on the stream.
			}(st)
		}
	}
}

Stream implements net.Conn interface.

func (*Stream) Close added in v0.0.6

func (s *Stream) Close() error

Close closes the sending part of the stream.

func (*Stream) CloseRead added in v0.0.6

func (s *Stream) CloseRead(errorCode uint64) error

CloseRead terminates reading part of the stream.

func (*Stream) CloseWrite added in v0.0.6

func (s *Stream) CloseWrite(errorCode uint64) error

CloseWrite terminates sending part of the stream.

func (*Stream) LocalAddr added in v0.0.6

func (s *Stream) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Stream) Read added in v0.0.6

func (s *Stream) Read(b []byte) (int, error)

Read reads data from the stream. The function is blocked until any stream data is available or timeout.

func (*Stream) RemoteAddr added in v0.0.6

func (s *Stream) RemoteAddr() net.Addr

RemoteAddr returns the remote network address.

func (*Stream) SetDeadline added in v0.0.6

func (s *Stream) SetDeadline(t time.Time) error

SetDeadline sets the read and write deadlines associated with the stream.

func (*Stream) SetReadDeadline added in v0.0.6

func (s *Stream) SetReadDeadline(t time.Time) error

SetReadDeadline sets the read deadline associated with the stream.

func (*Stream) SetWriteDeadline added in v0.0.6

func (s *Stream) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets the write deadline associated with the stream.

func (*Stream) Write added in v0.0.6

func (s *Stream) Write(b []byte) (int, error)

Write writes data to the connection stream. The function is blocked until all data are put into stream buffer or timeout.

Directories

Path Synopsis
cmd
Package qlog transforms quiwi logs to qlog
Package qlog transforms quiwi logs to qlog
Package transport provides implementation of QUIC transport protocol.
Package transport provides implementation of QUIC transport protocol.

Jump to

Keyboard shortcuts

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