2022-07-01 12:22:22 +02:00
|
|
|
package measurexlite
|
|
|
|
|
|
|
|
//
|
|
|
|
// Conn tracing
|
|
|
|
//
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/tracex"
|
|
|
|
)
|
|
|
|
|
|
|
|
// MaybeClose is a convenience function for closing a conn only when such a conn isn't nil.
|
|
|
|
func MaybeClose(conn net.Conn) (err error) {
|
|
|
|
if conn != nil {
|
|
|
|
err = conn.Close()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-08-17 20:58:06 +02:00
|
|
|
// MaybeWrapNetConn implements model.Trace.MaybeWrapNetConn.
|
|
|
|
func (tx *Trace) MaybeWrapNetConn(conn net.Conn) net.Conn {
|
2022-07-01 12:22:22 +02:00
|
|
|
return &connTrace{
|
|
|
|
Conn: conn,
|
|
|
|
tx: tx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// connTrace is a trace-aware net.Conn.
|
|
|
|
type connTrace struct {
|
|
|
|
// Implementation note: it seems safe to use embedding here because net.Conn
|
|
|
|
// is an interface from the standard library that we don't control
|
|
|
|
net.Conn
|
|
|
|
tx *Trace
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ net.Conn = &connTrace{}
|
|
|
|
|
|
|
|
// Read implements net.Conn.Read and saves network events.
|
|
|
|
func (c *connTrace) Read(b []byte) (int, error) {
|
|
|
|
network := c.RemoteAddr().Network()
|
|
|
|
addr := c.RemoteAddr().String()
|
|
|
|
started := c.tx.TimeSince(c.tx.ZeroTime)
|
|
|
|
count, err := c.Conn.Read(b)
|
|
|
|
finished := c.tx.TimeSince(c.tx.ZeroTime)
|
|
|
|
select {
|
2022-08-17 20:10:48 +02:00
|
|
|
case c.tx.networkEvent <- NewArchivalNetworkEvent(
|
2022-07-01 12:22:22 +02:00
|
|
|
c.tx.Index, started, netxlite.ReadOperation, network, addr, count, err, finished):
|
|
|
|
default: // buffer is full
|
|
|
|
}
|
|
|
|
return count, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write implements net.Conn.Write and saves network events.
|
|
|
|
func (c *connTrace) Write(b []byte) (int, error) {
|
|
|
|
network := c.RemoteAddr().Network()
|
|
|
|
addr := c.RemoteAddr().String()
|
|
|
|
started := c.tx.TimeSince(c.tx.ZeroTime)
|
|
|
|
count, err := c.Conn.Write(b)
|
|
|
|
finished := c.tx.TimeSince(c.tx.ZeroTime)
|
|
|
|
select {
|
2022-08-17 20:10:48 +02:00
|
|
|
case c.tx.networkEvent <- NewArchivalNetworkEvent(
|
2022-07-01 12:22:22 +02:00
|
|
|
c.tx.Index, started, netxlite.WriteOperation, network, addr, count, err, finished):
|
|
|
|
default: // buffer is full
|
|
|
|
}
|
|
|
|
return count, err
|
|
|
|
}
|
|
|
|
|
2022-08-17 09:19:11 +02:00
|
|
|
// MaybeUDPLikeClose is a convenience function for closing a conn only when such a conn isn't nil.
|
|
|
|
func MaybeCloseUDPLikeConn(conn model.UDPLikeConn) (err error) {
|
|
|
|
if conn != nil {
|
|
|
|
err = conn.Close()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-08-17 20:58:06 +02:00
|
|
|
// MaybeWrapUDPLikeConn implements model.Trace.MaybeWrapUDPLikeConn.
|
|
|
|
func (tx *Trace) MaybeWrapUDPLikeConn(conn model.UDPLikeConn) model.UDPLikeConn {
|
2022-08-17 09:19:11 +02:00
|
|
|
return &udpLikeConnTrace{
|
|
|
|
UDPLikeConn: conn,
|
|
|
|
tx: tx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// udpLikeConnTrace is a trace-aware model.UDPLikeConn.
|
|
|
|
type udpLikeConnTrace struct {
|
|
|
|
// Implementation note: it seems ~safe to use embedding here because model.UDPLikeConn
|
|
|
|
// contains fields deriving from how lucas-clemente/quic-go uses the standard library
|
|
|
|
model.UDPLikeConn
|
|
|
|
tx *Trace
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read implements model.UDPLikeConn.ReadFrom and saves network events.
|
|
|
|
func (c *udpLikeConnTrace) ReadFrom(b []byte) (int, net.Addr, error) {
|
|
|
|
started := c.tx.TimeSince(c.tx.ZeroTime)
|
|
|
|
count, addr, err := c.UDPLikeConn.ReadFrom(b)
|
|
|
|
finished := c.tx.TimeSince(c.tx.ZeroTime)
|
|
|
|
address := addrStringIfNotNil(addr)
|
|
|
|
select {
|
2022-08-17 20:10:48 +02:00
|
|
|
case c.tx.networkEvent <- NewArchivalNetworkEvent(
|
2022-08-17 09:19:11 +02:00
|
|
|
c.tx.Index, started, netxlite.ReadFromOperation, "udp", address, count, err, finished):
|
|
|
|
default: // buffer is full
|
|
|
|
}
|
|
|
|
return count, addr, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write implements model.UDPLikeConn.WriteTo and saves network events.
|
|
|
|
func (c *udpLikeConnTrace) WriteTo(b []byte, addr net.Addr) (int, error) {
|
|
|
|
started := c.tx.TimeSince(c.tx.ZeroTime)
|
|
|
|
address := addr.String()
|
|
|
|
count, err := c.UDPLikeConn.WriteTo(b, addr)
|
|
|
|
finished := c.tx.TimeSince(c.tx.ZeroTime)
|
|
|
|
select {
|
2022-08-17 20:10:48 +02:00
|
|
|
case c.tx.networkEvent <- NewArchivalNetworkEvent(
|
2022-08-17 09:19:11 +02:00
|
|
|
c.tx.Index, started, netxlite.WriteToOperation, "udp", address, count, err, finished):
|
|
|
|
default: // buffer is full
|
|
|
|
}
|
|
|
|
return count, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// addrStringIfNotNil returns the string of the given addr
|
|
|
|
// unless the addr is nil, in which case it returns an empty string.
|
|
|
|
func addrStringIfNotNil(addr net.Addr) (out string) {
|
|
|
|
if addr != nil {
|
|
|
|
out = addr.String()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-07-01 12:22:22 +02:00
|
|
|
// NewArchivalNetworkEvent creates a new model.ArchivalNetworkEvent.
|
|
|
|
func NewArchivalNetworkEvent(index int64, started time.Duration, operation string, network string,
|
|
|
|
address string, count int, err error, finished time.Duration) *model.ArchivalNetworkEvent {
|
|
|
|
return &model.ArchivalNetworkEvent{
|
2022-08-25 13:41:26 +02:00
|
|
|
Address: address,
|
|
|
|
Failure: tracex.NewFailure(err),
|
|
|
|
NumBytes: int64(count),
|
|
|
|
Operation: operation,
|
|
|
|
Proto: network,
|
|
|
|
T0: started.Seconds(),
|
|
|
|
T: finished.Seconds(),
|
|
|
|
TransactionID: index,
|
|
|
|
Tags: []string{},
|
2022-07-01 12:22:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewAnnotationArchivalNetworkEvent is a simplified NewArchivalNetworkEvent
|
|
|
|
// where we create a simple annotation without attached I/O info.
|
|
|
|
func NewAnnotationArchivalNetworkEvent(
|
|
|
|
index int64, time time.Duration, operation string) *model.ArchivalNetworkEvent {
|
|
|
|
return NewArchivalNetworkEvent(index, time, operation, "", "", 0, nil, time)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NetworkEvents drains the network events buffered inside the NetworkEvent channel.
|
|
|
|
func (tx *Trace) NetworkEvents() (out []*model.ArchivalNetworkEvent) {
|
|
|
|
for {
|
|
|
|
select {
|
2022-08-17 20:10:48 +02:00
|
|
|
case ev := <-tx.networkEvent:
|
2022-07-01 12:22:22 +02:00
|
|
|
out = append(out, ev)
|
|
|
|
default:
|
|
|
|
return // done
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-17 20:10:48 +02:00
|
|
|
|
|
|
|
// FirstNetworkEventOrNil drains the network events buffered inside the NetworkEvents channel
|
|
|
|
// and returns the first NetworkEvent, if any. Otherwise, it returns nil.
|
|
|
|
func (tx *Trace) FirstNetworkEventOrNil() *model.ArchivalNetworkEvent {
|
|
|
|
ev := tx.NetworkEvents()
|
|
|
|
if len(ev) < 1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return ev[0]
|
|
|
|
}
|