Example #1
0
// 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
}
Example #2
0
// 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())
	}
}