Documentation
¶
Overview ¶
Package utpnet presents a uTP socket through the standard library's net interfaces.
A Socket satisfies
net.PacketConn Accept() (net.Conn, error) Addr() net.Addr DialContext(ctx context.Context, network, addr string) (net.Conn, error)
and its connections are net.Conn values with CloseRead and CloseWrite, so it can be used wherever a BitTorrent client or other application expects a uTP socket in net terms.
Sharing a UDP port ¶
A BitTorrent client typically runs uTP and the DHT on one UDP port. A Socket therefore inspects each datagram: uTP packets go to the uTP connections, and everything else is returned by ReadFrom. WriteTo sends raw datagrams on the same port.
Example ¶
package main
import (
"context"
"fmt"
"io"
"log"
"github.com/arxodeus/utp/utpnet"
)
func main() {
ctx := context.Background()
server, err := utpnet.Listen(ctx, "udp", "127.0.0.1:0", nil)
if err != nil {
log.Fatal(err)
}
defer server.Close()
go func() {
conn, err := server.Accept()
if err != nil {
return
}
defer conn.Close()
io.Copy(conn, conn) // echo
}()
client, err := utpnet.Listen(ctx, "udp", "127.0.0.1:0", nil)
if err != nil {
log.Fatal(err)
}
defer client.Close()
conn, err := client.DialContext(ctx, "udp", server.Addr().String())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
if _, err := conn.Write([]byte("hello")); err != nil {
log.Fatal(err)
}
buf := make([]byte, 5)
if _, err := io.ReadFull(conn, buf); err != nil {
log.Fatal(err)
}
fmt.Println(string(buf))
}
Output: hello
Index ¶
- Variables
- type Conn
- func (c *Conn) Close() error
- func (c *Conn) CloseRead() error
- func (c *Conn) CloseWrite() error
- func (c *Conn) LocalAddr() net.Addr
- func (c *Conn) Read(b []byte) (int, error)
- func (c *Conn) RemoteAddr() net.Addr
- func (c *Conn) SetDeadline(t time.Time) error
- func (c *Conn) SetReadDeadline(t time.Time) error
- func (c *Conn) SetWriteDeadline(t time.Time) error
- func (c *Conn) Stream() *utp.Stream
- func (c *Conn) Write(b []byte) (int, error)
- func (c *Conn) WriteBuffers(bufs net.Buffers) (int64, error)
- type Options
- type Socket
- func (s *Socket) Accept() (net.Conn, error)
- func (s *Socket) AcceptBacklog() int
- func (s *Socket) Addr() net.Addr
- func (s *Socket) Close() error
- func (s *Socket) ConnectionConfig() *utp.ConnectionConfig
- func (s *Socket) Dial(network, addr string) (net.Conn, error)
- func (s *Socket) DialAddr(ctx context.Context, addr net.Addr) (net.Conn, error)
- func (s *Socket) DialContext(ctx context.Context, network, addr string) (net.Conn, error)
- func (s *Socket) LocalAddr() net.Addr
- func (s *Socket) ProcessICMPError(quoted []byte, addr *net.UDPAddr) bool
- func (s *Socket) ProcessICMPFragmentation(quoted []byte, addr *net.UDPAddr, nextHopMTU uint16) bool
- func (s *Socket) ReadFrom(p []byte) (int, net.Addr, error)
- func (s *Socket) SetConnectionConfig(config *utp.ConnectionConfig)
- func (s *Socket) SetDeadline(t time.Time) error
- func (s *Socket) SetReadDeadline(t time.Time) error
- func (s *Socket) SetWriteDeadline(t time.Time) error
- func (s *Socket) WriteTo(p []byte, addr net.Addr) (int, error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNilPeer = errors.New("utpnet: nil peer")
ErrNilPeer is returned when a write names no destination.
var ErrSocketClosed = errors.New("utpnet: socket is closed")
ErrSocketClosed is returned by operations on a closed Socket.
Functions ¶
This section is empty.
Types ¶
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is a uTP stream presented as a net.Conn.
func (*Conn) Close ¶
Close closes the connection and returns at once; what has been written is still delivered in the background.
func (*Conn) CloseRead ¶
CloseRead shuts down the receiving side and leaves the sending side open, like net.TCPConn.CloseRead. Data the peer sends afterwards is acknowledged, so the peer is not stalled and can finish its close, and then discarded.
func (*Conn) CloseWrite ¶
CloseWrite shuts down the sending side and leaves the receiving side open, like net.TCPConn.CloseWrite. Callers can detect it with a type assertion to interface{ CloseWrite() error }.
func (*Conn) RemoteAddr ¶
RemoteAddr returns the peer's address.
func (*Conn) SetDeadline ¶
SetDeadline sets the read and write deadlines. Moving a deadline affects calls already waiting.
func (*Conn) SetReadDeadline ¶
SetReadDeadline sets the read deadline.
func (*Conn) SetWriteDeadline ¶
SetWriteDeadline sets the write deadline.
func (*Conn) Stream ¶
Stream returns the underlying uTP stream, for callers that need the context-taking API or the connection id.
func (*Conn) Write ¶
Write writes all of b, or reports how much was written. If the write deadline moves while it waits, it continues with the remainder.
func (*Conn) WriteBuffers ¶
WriteBuffers writes several buffers as one stream write without joining them first, saving a copy. net.Buffers.WriteTo does not use it, so call it directly. If the write deadline moves while it waits, it continues with the remainder, as Write does.
type Options ¶
type Options struct {
// Logger receives the uTP socket's logging. A nil Logger discards it.
Logger *slog.Logger
// ConnectionConfig is applied to every stream this socket accepts or
// dials. A nil value uses utp.NewConnectionConfig().
ConnectionConfig *utp.ConnectionConfig
// Firewall, when set, is consulted for every incoming connection before
// any state is created for it. Returning true refuses it: the packet is
// dropped without an answer, so a refused peer learns nothing. A typical
// use is an IP blocklist.
//
// It runs on the socket's receive path: it must not block or call back
// into the socket.
Firewall func(net.Addr) bool
// OnWriteError, when set, is called for every datagram the uTP
// connections failed to send, with its destination. The connections
// carry on regardless, since a lost datagram is retransmitted; this lets
// a caller give up sooner on an error it knows is permanent, or count
// them.
//
// It is called on the sending goroutine: it must not block or call back
// into the socket.
OnWriteError func(to net.Addr, err error)
// MaxConnections limits the connections this socket holds before it
// silently refuses new incoming ones. Zero means
// utp.DefaultMaxConnections; a negative value means no limit. See
// utp.WithMaxConnections.
MaxConnections int
}
Options configures a Socket.
type Socket ¶
type Socket struct {
// contains filtered or unexported fields
}
Socket is a uTP endpoint on a UDP port, presented as net types.
func Listen ¶
Listen binds a UDP port and returns a uTP socket on it.
network is "udp", "udp4" or "udp6"; addr is a host:port as accepted by net.ResolveUDPAddr, and an empty port lets the kernel choose one.
func NewSocket ¶
NewSocket returns a Socket on an already bound UDP connection. The Socket takes ownership, and closing it closes conn. Use it to set socket options or bind with a particular control function.
func NewSocketFromPacketConn ¶
func NewSocketFromPacketConn(ctx context.Context, pc net.PacketConn, opts *Options) (*Socket, error)
NewSocketFromPacketConn returns a Socket on any net.PacketConn, not only a UDP socket: for example one wrapped for rate limiting or metrics, one shared with other code, or an in-memory network.
The Socket takes ownership of pc, and closing the Socket closes it. Unlike a Socket on a UDP socket, Close does not wait for the reader to return, since pc may not honor read deadlines. The reader stops when pc's ReadFrom fails. A non-timeout error on an open Socket closes it, and Accept, ReadFrom and the dial methods then return ErrSocketClosed wrapping that error.
Addresses must name a host and port. A datagram from an address that is not a *net.UDPAddr is handled under its UDP form, and replies are sent to the original address; DialAddr dials an address of pc's own type. A PacketConn that uses its own address type must accept, in WriteTo, any address it has returned from ReadFrom.
func (*Socket) AcceptBacklog ¶
AcceptBacklog reports how many incoming connections are waiting for an Accept to take them.
func (*Socket) Close ¶
Close shuts down the socket, every connection on it, and the underlying UDP port. It is safe to call more than once.
Connections already closed are given time to deliver what they hold, as utp.Socket.Close gives them, and the port keeps being read meanwhile, since their acknowledgements arrive on it.
func (*Socket) ConnectionConfig ¶
func (s *Socket) ConnectionConfig() *utp.ConnectionConfig
ConnectionConfig returns the configuration given to streams accepted or dialed now.
func (*Socket) DialAddr ¶
DialAddr opens a uTP connection to an address already resolved. It takes any address with a host and port. On a Socket created with NewSocketFromPacketConn, use it to dial an address of the PacketConn's own type, which DialContext cannot resolve.
func (*Socket) DialContext ¶
DialContext opens a uTP connection to addr, a host:port. network is a UDP network name; "utp", "utp4" and "utp6" are accepted as aliases.
func (*Socket) ProcessICMPError ¶
ProcessICMPError reports an ICMP message that should end a connection: destination unreachable other than fragmentation needed, time exceeded, or parameter problem. Which messages are fatal is for the caller to decide.
quoted, addr and the result are as for ProcessICMPFragmentation.
func (*Socket) ProcessICMPFragmentation ¶
ProcessICMPFragmentation reports an ICMP "fragmentation needed" message (IPv4 type 3 code 4, or ICMPv6 "packet too big") for a datagram this socket sent, so the path-MTU search can use the router's figure.
quoted is the uTP datagram the ICMP message quoted, the original UDP payload without the ICMP packet around it, and addr is where it was sent. nextHopMTU is the router's figure, which may be zero. It reports whether quoted matched a live connection.
This package does not collect ICMP itself. On Linux the usual source is IP_RECVERR on the UDP socket the Socket was built from.
func (*Socket) ReadFrom ¶
ReadFrom returns the next datagram that was not a uTP packet, such as a BitTorrent client's DHT traffic on a shared port.
func (*Socket) SetConnectionConfig ¶
func (s *Socket) SetConnectionConfig(config *utp.ConnectionConfig)
SetConnectionConfig replaces the configuration given to streams accepted or dialed from now on; open streams keep theirs. A nil config means utp.NewConnectionConfig(). The config is not copied and must not be modified afterwards.
func (*Socket) SetDeadline ¶
SetDeadline sets both the read and write deadlines for the packet-oriented side of this socket. It does not affect established uTP connections, which carry their own deadlines.
func (*Socket) SetReadDeadline ¶
SetReadDeadline sets how long ReadFrom will wait.
func (*Socket) SetWriteDeadline ¶
SetWriteDeadline sets the deadline for WriteTo, including a WriteTo already waiting. It does not apply to the uTP connections' own writes, which share the port. See WriteTo.
func (*Socket) WriteTo ¶
WriteTo sends a raw datagram to addr on the port the uTP connections use, for example a DHT reply.
The write deadline applies while the write waits, and moving it moves the wait. The datagram is handed to a single writer goroutine (see passthroughWriter), so the wait can end without the write; a deadline set on the port itself would end the uTP connections' writes too. A datagram whose deadline passes before the writer reaches it is not sent; one the writer is already sending may still be. p is copied, so the caller may reuse it on return.
On Unix a datagram is first offered to the kernel once without waiting (sendNow); only one the kernel cannot take at once goes to the writer, so a blocked write can always be ended by the deadline. While no deadline is set, handing over uses no timer or goroutine.