Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion cmd/wl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,13 @@ func runGet(opts getOpts) error {
clientFiles[i] = client.FileEntry{Path: f.Path, Length: f.Length}
}

v1Hash, _ := hex.DecodeString(mag.InfoHashV1)
// A v2-only magnet has no v1 hash, which is fine; but if one is present it
// must be valid hex — reject a malformed value at the boundary rather than
// handing a truncated hash to the downloader.
v1Hash, err := hex.DecodeString(mag.InfoHashV1)
if err != nil {
return fmt.Errorf("invalid v1 info hash %q: %w", mag.InfoHashV1, err)
}

ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
Expand Down
23 changes: 17 additions & 6 deletions internal/client/tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func Announce(ctx context.Context, trackerURL, infoHash, peerID string, port int
var trackerResponse struct {
FailureReason string `bencode:"failure reason"`
Peers string `bencode:"peers"`
Peers6 string `bencode:"peers6"`
}

if err := bencode.DecodeBytes(data, &trackerResponse); err != nil {
Expand All @@ -71,12 +72,22 @@ func Announce(ctx context.Context, trackerURL, infoHash, peerID string, port int
return nil, fmt.Errorf("tracker failure: %s", trackerResponse.FailureReason)
}

return unpackPeers([]byte(trackerResponse.Peers))
// BEP 3 compact IPv4 (6 bytes/peer) plus BEP 7 compact IPv6 (18 bytes/peer).
addrs, err := unpackPeers([]byte(trackerResponse.Peers), net.IPv4len)
if err != nil {
return nil, err
}
addrs6, err := unpackPeers([]byte(trackerResponse.Peers6), net.IPv6len)
if err != nil {
return nil, err
}
return append(addrs, addrs6...), nil
}

// unpackPeers parses the compact BEP 3 peer list (6 bytes per peer: 4 for IPv4, 2 for port).
func unpackPeers(peers []byte) ([]string, error) {
const peerSize = 6
// unpackPeers parses a compact peer list: ipLen address bytes (4 for IPv4,
// 16 for IPv6) followed by a 2-byte big-endian port, per peer.
func unpackPeers(peers []byte, ipLen int) ([]string, error) {
peerSize := ipLen + 2
if len(peers)%peerSize != 0 {
return nil, fmt.Errorf("invalid peers string length: %d", len(peers))
}
Expand All @@ -86,8 +97,8 @@ func unpackPeers(peers []byte) ([]string, error) {

for i := 0; i < numPeers; i++ {
offset := i * peerSize
ip := net.IPv4(peers[offset], peers[offset+1], peers[offset+2], peers[offset+3])
port := binary.BigEndian.Uint16(peers[offset+4 : offset+6])
ip := net.IP(peers[offset : offset+ipLen])
port := binary.BigEndian.Uint16(peers[offset+ipLen : offset+peerSize])
addrs[i] = net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))
}

Expand Down
23 changes: 21 additions & 2 deletions internal/client/tracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func TestUnpackPeers(t *testing.T) {
5, 6, 7, 8, 0x1f, 0x90, // 8080 = 0x1f90
}

addrs, err := unpackPeers(peers)
addrs, err := unpackPeers(peers, net.IPv4len)
if err != nil {
t.Fatalf("unpackPeers failed: %v", err)
}
Expand All @@ -35,10 +35,29 @@ func TestUnpackPeers(t *testing.T) {
}
}

func TestUnpackPeersIPv6(t *testing.T) {
t.Parallel()
// One IPv6 peer: [::1]:6881 — 16 address bytes + 2 port bytes.
peers := make([]byte, 0, 18)
peers = append(peers, net.ParseIP("::1").To16()...)
peers = append(peers, 0x1a, 0xe1) // 6881

addrs, err := unpackPeers(peers, net.IPv6len)
if err != nil {
t.Fatalf("unpackPeers (v6) failed: %v", err)
}
if len(addrs) != 1 {
t.Fatalf("expected 1 peer, got %d", len(addrs))
}
if addrs[0] != "[::1]:6881" {
t.Errorf("expected [::1]:6881, got %s", addrs[0])
}
}

func TestUnpackPeersInvalidLength(t *testing.T) {
t.Parallel()
peers := []byte{1, 2, 3, 4, 5} // 5 bytes, not a multiple of 6
_, err := unpackPeers(peers)
_, err := unpackPeers(peers, net.IPv4len)
if err == nil {
t.Error("expected error for invalid length")
}
Expand Down
5 changes: 3 additions & 2 deletions internal/tracker/registry.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tracker

import (
"crypto/subtle"
"database/sql"
"encoding/json"
"errors"
Expand Down Expand Up @@ -89,7 +90,7 @@ func HandleAPI(w http.ResponseWriter, r *http.Request) {

case http.MethodPost:
if registryKey != "" {
if r.Header.Get("X-Weightless-Key") != registryKey {
if subtle.ConstantTimeCompare([]byte(r.Header.Get("X-Weightless-Key")), []byte(registryKey)) != 1 {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
Expand Down Expand Up @@ -144,7 +145,7 @@ func HandleAPI(w http.ResponseWriter, r *http.Request) {

case http.MethodDelete:
if registryKey != "" {
if r.Header.Get("X-Weightless-Key") != registryKey {
if subtle.ConstantTimeCompare([]byte(r.Header.Get("X-Weightless-Key")), []byte(registryKey)) != 1 {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
Expand Down
Loading