Esempio n. 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
}