package bittorrent import ( "net/netip" "sort" "time" "github.com/rs/zerolog" ) // RequestAddress wrapper for netip.Addr with Provided flag. // Used in RequestAddresses to determine addresses priority type RequestAddress struct { netip.Addr Provided bool } // Note: there is no IPv6 broadcast address var globalBroadcastIPv4 = netip.AddrFrom4([4]byte{255, 255, 255, 255}) // IsValid checks if netip.Addr is valid, not unspecified and not multicast func (a RequestAddress) IsValid() bool { return a.Addr.IsValid() && !(a.IsUnspecified() || a.IsMulticast() || a.Addr == globalBroadcastIPv4) } // MarshalZerologObject writes fields into zerolog event func (a RequestAddress) MarshalZerologObject(e *zerolog.Event) { e.Stringer("addr", a.Addr).Bool("provided", a.Provided) } // RequestAddresses is an array of RequestAddress used mainly for // sort.Interface implementation. // Frontends may determine peer's address from connections info // or from provided values or combine these addresses to fetch maximum // connection information about peer type RequestAddresses []RequestAddress func (aa *RequestAddresses) Len() int { return len(*aa) } // Less returns true only if i-th RequestAddress is marked as // RequestAddress.Provided and j-th is not (provided address has // higher priority) func (aa *RequestAddresses) Less(i, j int) bool { return (*aa)[i].Provided && !(*aa)[j].Provided } func (aa *RequestAddresses) Swap(i, j int) { (*aa)[i], (*aa)[j] = (*aa)[j], (*aa)[i] } // Add checks if provided RequestAddress is valid and adds unmapped // netip.Addr to array func (aa *RequestAddresses) Add(a RequestAddress) { if a.IsValid() { a.Addr = a.Unmap() *aa = append(*aa, a) } } // Sanitize checks if array is not empty and at least one RequestAddress is valid, // then make them unique and sorts with RequestAddresses.Less rule. // If ignorePrivate set to true, function will preserve only global unicast and // non-private (see netip.IsGlobalUnicast and netip.IsPrivate) addresses. // If there are no valid and global (if ignorePrivate checked) addresses in array, // function returns false and empty receiver. func (aa *RequestAddresses) Sanitize(ignorePrivate bool) bool { if len(*aa) == 0 { return false } uniqueAddresses := make(map[netip.Addr]bool, len(*aa)) for _, a := range *aa { if a.IsValid() && (!ignorePrivate || a.IsGlobalUnicast() && !a.IsPrivate()) { if provided, found := uniqueAddresses[a.Addr]; !found || !provided && a.Provided { uniqueAddresses[a.Addr] = a.Provided } } } *aa = make(RequestAddresses, 0, len(uniqueAddresses)) for a, p := range uniqueAddresses { *aa = append(*aa, RequestAddress{a, p}) } if len(*aa) > 1 { sort.Sort(aa) } return len(uniqueAddresses) > 0 } // GetFirst returns first address from array // or empty netip.Addr if array is empty func (aa *RequestAddresses) GetFirst() netip.Addr { var a netip.Addr if len(*aa) > 0 { a = (*aa)[0].Addr } return a } // MarshalZerologArray writes array elements to zerolog event func (aa *RequestAddresses) MarshalZerologArray(a *zerolog.Array) { if aa != nil { for _, addr := range *aa { a.Object(addr) } } } // Peers wrapper of array of Peer-s type Peers []Peer // MarshalZerologArray writes array elements to zerolog event func (p Peers) MarshalZerologArray(a *zerolog.Array) { for _, peer := range p { a.Object(peer) } } // RequestPeer is bundle of peer ID, provided or // determined addresses and net port type RequestPeer struct { ID PeerID Port uint16 RequestAddresses } // Peers constructs array of Peer-s with the same ID and Port // for every RequestAddress array. func (rp RequestPeer) Peers() (peers Peers) { for _, a := range rp.RequestAddresses { peers = append(peers, Peer{ ID: rp.ID, AddrPort: netip.AddrPortFrom(a.Addr, rp.Port), }) } return } // MarshalZerologObject writes fields into zerolog event func (rp RequestPeer) MarshalZerologObject(e *zerolog.Event) { e.Stringer("id", rp.ID). Array("addresses", &rp.RequestAddresses). Uint16("port", rp.Port) } // AnnounceRequest represents the parsed parameters from an announce request. type AnnounceRequest struct { Event Event InfoHash InfoHash EventProvided bool NumWantProvided bool NumWant uint32 Left uint64 Downloaded uint64 Uploaded uint64 RequestPeer Params } // MarshalZerologObject writes fields into zerolog event func (r AnnounceRequest) MarshalZerologObject(e *zerolog.Event) { e.Stringer("event", r.Event). Stringer("infoHash", r.InfoHash). Bool("eventProvided", r.EventProvided). Bool("numWantProvided", r.NumWantProvided). Uint32("numWant", r.NumWant). Uint64("left", r.Left). Uint64("downloaded", r.Downloaded). Uint64("uploaded", r.Uploaded). Object("source", r.RequestPeer). Object("params", r.Params) } // AnnounceResponse represents the parameters used to create an announce // response. type AnnounceResponse struct { Complete uint32 Incomplete uint32 Interval time.Duration MinInterval time.Duration IPv4Peers Peers IPv6Peers Peers } // MarshalZerologObject writes fields into zerolog event func (r AnnounceResponse) MarshalZerologObject(e *zerolog.Event) { e.Uint32("complete", r.Complete). Uint32("incomplete", r.Incomplete). Dur("interval", r.Interval). Dur("minInterval", r.MinInterval). Array("ipv4Peers", r.IPv4Peers). Array("ipv6Peers", r.IPv6Peers) } // InfoHashes wrapper of array of InfoHash-es type InfoHashes []InfoHash // MarshalZerologArray writes array elements to zerolog event func (i InfoHashes) MarshalZerologArray(a *zerolog.Array) { for _, ih := range i { a.Str(ih.String()) } } // ScrapeRequest represents the parsed parameters from a scrape request. type ScrapeRequest struct { // RequestAddresses not used in internal logic, // but MAY be used in middleware (per-ip block etc.) RequestAddresses InfoHashes InfoHashes Params Params } // MarshalZerologObject writes fields into zerolog event func (r ScrapeRequest) MarshalZerologObject(e *zerolog.Event) { e.Array("addresses", &r.RequestAddresses). Array("infoHashes", r.InfoHashes). Object("params", r.Params) } // Scrape represents the state of a swarm that is returned in a scrape response. type Scrape struct { InfoHash InfoHash Snatches uint32 Complete uint32 Incomplete uint32 } // MarshalZerologObject writes fields into zerolog event func (s Scrape) MarshalZerologObject(e *zerolog.Event) { e.Stringer("infoHash", s.InfoHash). Uint32("snatches", s.Snatches). Uint32("complete", s.Complete). Uint32("incomplete", s.Incomplete) } // Scrapes wrapper of array of Scrape-s type Scrapes []Scrape // MarshalZerologArray writes array elements to zerolog event func (s Scrapes) MarshalZerologArray(a *zerolog.Array) { for _, scrape := range s { a.Object(scrape) } } // ScrapeResponse represents the parameters used to create a scrape response. // // The Scrapes must be in the same order as the InfoHashes in the corresponding // ScrapeRequest. type ScrapeResponse struct { Data Scrapes } // MarshalZerologObject writes fields into zerolog event func (sr ScrapeResponse) MarshalZerologObject(e *zerolog.Event) { e.Array("scrapes", sr.Data) }