// Parse a UDP byte buffer, return response from udpTracker func parseUDP(buf []byte, addr *net.UDPAddr) ([]byte, error) { // Attempt to grab generic UDP connection fields packet := new(udp.Packet) err := packet.UnmarshalBinary(buf) if err != nil { // Because no transaction ID is present on failure, we must return nil return nil, errUDPHandshake } // Create a udpTracker to handle this client udpTracker := tracker.UDPTracker{TransID: packet.TransID} // Check for maintenance mode if common.Static.Maintenance { // Return tracker error with maintenance message return udpTracker.Error("Maintenance: " + common.Static.StatusMessage), nil } // Action switch // Action 0: Connect if packet.Action == 0 { // Validate UDP udpTracker handshake if packet.ConnID != udpInitID { return udpTracker.Error("Invalid UDP udpTracker handshake"), errUDPHandshake } // Generate a connection ID, which will be expected for this client next call expID := uint64(common.RandRange(1, 1000000000)) // Store this client's address and ID in map udpAddrToID[addr.String()] = expID // Generate connect response connect := udp.ConnectResponse{ Action: 0, TransID: packet.TransID, ConnID: expID, } // Grab bytes from connect response connectBuf, err := connect.MarshalBinary() if err != nil { log.Println(err.Error()) return udpTracker.Error("Could not generate UDP connect response"), errUDPWrite } return connectBuf, nil } // For all udpTracker actions other than connect, we must validate the connection ID for this // address, ensuring it matches the previously set value // Ensure connection ID map contains this IP address expID, ok := udpAddrToID[addr.String()] if !ok { return udpTracker.Error("Client must properly handshake before announce"), errUDPHandshake } // Validate expected connection ID using map if packet.ConnID != expID { return udpTracker.Error("Invalid UDP connection ID"), errUDPHandshake } // Clear this IP from the connection map after 2 minutes // note: this is done to conserve memory and prevent session fixation go func(addr *net.UDPAddr) { <-time.After(2 * time.Minute) delete(udpAddrToID, addr.String()) }(addr) // Action 1: Announce if packet.Action == 1 { // Retrieve UDP announce request from byte buffer announce := new(udp.AnnounceRequest) err := announce.UnmarshalBinary(buf) log.Println(announce) if err != nil { return udpTracker.Error("Malformed UDP announce"), errUDPInteger } // Convert UDP announce to query map query := announce.ToValues() // Check if a proper IP was set, and if not, use the UDP connection address if query.Get("ip") == "0" { query.Set("ip", strings.Split(addr.String(), ":")[0]) } // Trigger an anonymous announce return tracker.Announce(udpTracker, data.UserRecord{}, query), nil } // Action 2: Scrape if packet.Action == 2 { // Generate UDP scrape packet from byte buffer scrape := new(udp.ScrapeRequest) err := scrape.UnmarshalBinary(buf) if err != nil { return udpTracker.Error("Malformed UDP scrape"), errUDPHandshake } // Convert UDP scrape to query map query := scrape.ToValues() // Store IP in query map query.Set("ip", strings.Split(addr.String(), ":")[0]) // Trigger a scrape return tracker.Scrape(udpTracker, query), nil } // No action matched return udpTracker.Error("Invalid action"), errUDPAction }
// TestUDPRouter verifies that the main UDP router is working properly func TestUDPRouter(t *testing.T) { log.Println("TestUDPRouter()") // Load config config, err := common.LoadConfig() if err != nil { t.Fatalf("Could not load configuration: %s", err.Error()) } common.Static.Config = config // Generate mock data.FileRecord file := data.FileRecord{ InfoHash: "6465616462656566303030303030303030303030", Verified: true, } // Save mock file if err := file.Save(); err != nil { t.Fatalf("Failed to save mock file: %s", err.Error()) } // Fake UDP address addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") if err != nil { t.Fatalf("Failed to create fake UDP address") } // Connect packet with handshake connect := udp.Packet{udpInitID, 0, 1234} connectBuf, err := connect.MarshalBinary() if err != nil { t.Fatalf("Failed to create UDP connect packet") } // Perform connection handshake res, err := parseUDP(connectBuf, addr) if err != nil { errRes := new(udp.ErrorResponse) err2 := errRes.UnmarshalBinary(res) if err2 != nil { t.Fatalf(err.Error()) } log.Println("ERROR:", errRes.Error) t.Fatalf(err.Error()) } // Retrieve response, get new connection ID, which will be expected by router connRes := new(udp.ConnectResponse) err = connRes.UnmarshalBinary(res) if err != nil { t.Fatalf(err.Error()) } // Create announce request announce := udp.AnnounceRequest{ ConnID: connRes.ConnID, Action: 1, TransID: connRes.TransID, InfoHash: []byte("deadbeef000000000000"), PeerID: []byte("00001111222233334444"), Downloaded: 0, Left: 0, Uploaded: 0, IP: 0, Key: 1234, Port: 5000, } // Get announce bytes announceBuf, err := announce.MarshalBinary() if err != nil { t.Fatalf(err.Error()) } // Send announce to UDP router res, err = parseUDP(announceBuf, addr) if err != nil { errRes := new(udp.ErrorResponse) err2 := errRes.UnmarshalBinary(res) if err2 != nil { t.Fatalf(err.Error()) } log.Println("ERROR:", errRes.Error) t.Fatalf(err.Error()) } // Get UDP announce response announceRes := new(udp.AnnounceResponse) err = announceRes.UnmarshalBinary(res) if err != nil { errRes := new(udp.ErrorResponse) err2 := errRes.UnmarshalBinary(res) if err2 != nil { t.Fatalf(err.Error()) } log.Println("ERROR:", errRes.Error) t.Fatalf(err.Error()) } log.Println(announceRes) // Create scrape request scrape := udp.ScrapeRequest{ ConnID: connRes.ConnID, Action: 2, TransID: connRes.TransID, InfoHashes: [][]byte{[]byte("deadbeef000000000000")}, } // Get scrape bytes scrapeBuf, err := scrape.MarshalBinary() if err != nil { t.Fatalf(err.Error()) } // Send scrape to UDP router res, err = parseUDP(scrapeBuf, addr) if err != nil { errRes := new(udp.ErrorResponse) err2 := errRes.UnmarshalBinary(res) if err2 != nil { t.Fatalf(err.Error()) } log.Println("ERROR:", errRes.Error) t.Fatalf(err.Error()) } // Get UDP scrape response scrapeRes := new(udp.ScrapeResponse) err = scrapeRes.UnmarshalBinary(res) if err != nil { errRes := new(udp.ErrorResponse) err2 := errRes.UnmarshalBinary(res) if err2 != nil { t.Fatalf(err.Error()) } log.Println("ERROR:", errRes.Error) t.Fatalf(err.Error()) } log.Println(scrapeRes) // Delete mock file if err := file.Delete(); err != nil { t.Fatalf("Failed to delete mock file: %s", err.Error()) } }