Beispiel #1
0
// RegisterControlPeer - This method register control peer on a Bootstrap node
func (dht *DHTClient) RegisterControlPeer() {
	for len(dht.ID) != 36 {
		time.Sleep(1 * time.Second)
	}
	var req DHTMessage
	var err error
	req.ID = dht.ID
	req.Query = "0"
	req.Command = DhtCmdRegProxy
	req.Arguments = fmt.Sprintf("%d", dht.P2PPort)
	var b bytes.Buffer
	if err := bencode.Marshal(&b, req); err != nil {
		Log(Error, "Failed to Marshal bencode %v", err)
		return
	}
	// TODO: Optimize types here
	msg := b.String()
	for _, conn := range dht.Connection {
		if dht.Shutdown {
			continue
		}
		_, err = conn.Write([]byte(msg))
		if err != nil {
			Log(Error, "Failed to send packet: %v", err)
			conn.Close()
			return
		}
	}
}
Beispiel #2
0
// RequestControlPeer - This method request a new control peer for particular host
func (dht *DHTClient) RequestControlPeer(id string, omit []*net.UDPAddr) {
	var req DHTMessage
	var err error
	req.ID = dht.ID
	req.Query = ""
	// Collect list of failed forwarders
	for _, fwd := range omit {
		req.Query += fwd.String() + "|"
	}
	req.Command = DhtCmdProxy
	req.Arguments = id
	var b bytes.Buffer
	if err := bencode.Marshal(&b, req); err != nil {
		Log(Error, "Failed to Marshal bencode %v", err)
		return
	}
	msg := b.String()
	// TODO: Move sending to a separate method
	for _, conn := range dht.Connection {
		if dht.Shutdown {
			continue
		}
		_, err = conn.Write([]byte(msg))
		if err != nil {
			Log(Error, "Failed to send packet: %v", err)
			conn.Close()
			return
		}
	}
}
Beispiel #3
0
// Handshake performs data exchange between DHT client and server
func (dht *DHTClient) Handshake(conn *net.UDPConn) error {
	// Handshake
	var req DHTMessage
	req.ID = "0"
	req.Query = PacketVersion
	req.Command = DhtCmdConn
	// TODO: rename Port to something more clear
	req.Arguments = fmt.Sprintf("%d", dht.P2PPort)
	req.Payload = dht.NetworkHash
	for _, ip := range dht.IPList {
		req.Arguments = req.Arguments + "|" + ip.String()
	}
	var b bytes.Buffer
	if err := bencode.Marshal(&b, req); err != nil {
		Log(Error, "Failed to Marshal bencode %v", err)
		conn.Close()
		return err
	}
	// TODO: Optimize types here
	msg := b.String()
	if dht.Shutdown {
		return nil
	}
	_, err := conn.Write([]byte(msg))
	if err != nil {
		Log(Error, "Failed to send packet: %v", err)
		conn.Close()
		return err
	}
	return nil
}
Beispiel #4
0
// Encode to Bencode, but only encode non-default values.
func (m *MetaInfo) Bencode(w io.Writer) (err error) {
	var mi map[string]interface{} = map[string]interface{}{}
	id := m.Info.toMap()
	if len(id) > 0 {
		mi["info"] = id
	}
	// Do not encode InfoHash. Clients are supposed to calculate it themselves.
	if m.Announce != "" {
		mi["announce"] = m.Announce
	}
	if len(m.AnnounceList) > 0 {
		mi["announce-list"] = m.AnnounceList
	}
	if m.CreationDate != "" {
		mi["creation date"] = m.CreationDate
	}
	if m.Comment != "" {
		mi["comment"] = m.Comment
	}
	if m.CreatedBy != "" {
		mi["created by"] = m.CreatedBy
	}
	if m.Encoding != "" {
		mi["encoding"] = m.Encoding
	}
	bencode.Marshal(w, mi)
	return
}
Beispiel #5
0
// startSeed attempts to start up a seeding process for a given torrent file.
func (self *Tracker) startSeed(file *File, metadata *Metadata) {
	self.seedStartLock.Lock()

	if file.SeedCommand != nil {
		self.seedStartLock.Unlock()
		return
	}

	tmp, err := ioutil.TempFile("", "distributor.")
	if err != nil {
		LogFatal("TempFile failed: %s", err)
	}
	LogDebug("Temporary file for %s: %s", file.Name, tmp.Name())

	err = bencode.Marshal(tmp, *metadata)
	if err != nil {
		self.seedStartLock.Unlock()
		LogError("Failed to bencode %s: %s", file.Name, err)
		return
	}

	err = tmp.Sync()
	if err != nil {
		self.seedStartLock.Unlock()
		LogError("Failed to fsync: %s", err)
		return
	}

	file.SeedCommand = exec.Command(
		self.ctorrent,
		"-s",
		file.FQFN,
		"-e",
		"4",
		"-p",
		"8999",
		tmp.Name())
	self.seedStartLock.Unlock()

	// TODO: Read from output pipes, because they could fill up?

	go func() {
		LogDebug("Seed starting: %s", file.Name)
		file.SeedCommand.Run()
		LogDebug("Seed exited: %s", file.Name)

		// Try to clean up temporary file.
		tmp.Close()
		os.Remove(tmp.Name())

		// Seeds exit after 4 hours. Then they get restarted if someone requests them.
		self.seedStartLock.Lock()
		file.SeedCommand = nil
		self.seedStartLock.Unlock()
	}()
}
Beispiel #6
0
func (t *Tracker) handleScrape(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	infoHashes := r.URL.Query()["info_hash"]
	response := make(bmap)
	response["files"] = t.t.scrape(infoHashes)
	var b bytes.Buffer
	err := bencode.Marshal(&b, response)
	if err == nil {
		w.Write(b.Bytes())
	}
}
Beispiel #7
0
// EncodeRequest - Marshals message onto Bencode format
func (dht *DHTClient) EncodeRequest(req DHTMessage) string {
	if req.Command == "" {
		return ""
	}
	var b bytes.Buffer
	if err := bencode.Marshal(&b, req); err != nil {
		Log(Error, "Failed to Marshal bencode %v", err)
		return ""
	}
	return b.String()
}
Beispiel #8
0
// ReportControlPeerLoad - sends current amount of clients on this proxy
func (dht *DHTClient) ReportControlPeerLoad(amount int) {
	var req DHTMessage
	req.ID = dht.ID
	req.Command = DhtCmdLoad
	req.Arguments = fmt.Sprintf("%d", amount)
	var b bytes.Buffer
	if err := bencode.Marshal(&b, req); err != nil {
		Log(Error, "Failed to Marshal bencode %v", err)
		return
	}
	dht.Send(b.String())
}
Beispiel #9
0
// sendMsg bencodes the data in 'query' and sends it to the remote node.
func sendMsg(conn *net.UDPConn, raddr net.UDPAddr, query interface{}) {
	totalSent.Add(1)
	var b bytes.Buffer
	if err := bencode.Marshal(&b, query); err != nil {
		return
	}
	if n, err := conn.WriteToUDP(b.Bytes(), &raddr); err != nil {
		// debug.Println("DHT: node write failed:", err)
	} else {
		totalWrittenBytes.Add(int64(n))
	}
	return
}
Beispiel #10
0
// sendMsg bencodes the data in 'query' and sends it to the remote node.
func sendMsg(conn *net.UDPConn, raddr net.UDPAddr, query interface{}) {
	totalSent.Add(1)
	var b bytes.Buffer
	if err := bencode.Marshal(&b, query); err != nil {
		return
	}
	if n, err := conn.WriteToUDP(b.Bytes(), &raddr); err != nil {
		log.V(3).Infof("DHT: node write failed to %+v, error=%s", raddr, err)
	} else {
		totalWrittenBytes.Add(int64(n))
	}
	return
}
Beispiel #11
0
func (t *Tracker) handleAnnounce(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	response := make(bmap)
	var params announceParams
	var peerListenAddress *net.TCPAddr
	err := params.parse(r.URL)
	if err == nil {
		if params.trackerID != "" && params.trackerID != t.ID {
			err = fmt.Errorf("Incorrect tracker ID: %#v", params.trackerID)
		}
	}
	if err == nil {
		peerListenAddress, err = newTrackerPeerListenAddress(r.RemoteAddr, &params)
	}
	if err == nil {
		now := time.Now()
		t.m.Lock()
		err = t.t.handleAnnounce(now, peerListenAddress, &params, response)
		t.m.Unlock()
		if err == nil {
			response["interval"] = int64(30 * 60)
			response["tracker id"] = t.ID
		}
	}
	var b bytes.Buffer
	if err != nil {
		log.Printf("announce from %v failed: %#v", r.RemoteAddr, err.Error())
		errorResponse := make(bmap)
		errorResponse["failure reason"] = err.Error()
		err = bencode.Marshal(&b, errorResponse)
	} else {
		err = bencode.Marshal(&b, response)
	}
	if err == nil {
		w.Write(b.Bytes())
	}
}
Beispiel #12
0
// Updates the InfoHash field. Call this after manually changing the Info data.
func (m *MetaInfo) UpdateInfoHash(metaInfo *MetaInfo) (err error) {
	var b bytes.Buffer
	infoMap := m.Info.toMap()
	if len(infoMap) > 0 {
		err = bencode.Marshal(&b, infoMap)
		if err != nil {
			return
		}
	}
	hash := sha1.New()
	hash.Write(b.Bytes())

	m.InfoHash = string(hash.Sum(nil))
	return
}
Beispiel #13
0
// Stop - sends a STOP message about current peer
func (dht *DHTClient) Stop() {
	dht.Shutdown = true
	var req DHTMessage
	req.ID = dht.ID
	req.Command = DhtCmdStop
	req.Arguments = "0"
	var b bytes.Buffer
	if err := bencode.Marshal(&b, req); err != nil {
		Log(Error, "Failed to Marshal bencode %v", err)
		return
	}
	msg := b.String()
	for _, conn := range dht.Connection {
		conn.Write([]byte(msg))
	}
}
Beispiel #14
0
// sendMsg bencodes the data in 'query' and sends it to the remote node.
func sendMsg(conn *net.UDPConn, raddr net.UDPAddr, query interface{}) error {
	totalSent.Add(1)
	var b bytes.Buffer
	if err := bencode.Marshal(&b, query); err != nil {
		return err
	}
	if n, err := conn.WriteToUDP(b.Bytes(), &raddr); err != nil {
		if denet.ErrorIsPortUnreachable(err) {
			fmt.Println("DHT: port unreachable: ", err)
			return err
		}

		// all other errors are quashed
		//fmt.Println("DHT: node write failed:", err)
	} else {
		totalWrittenBytes.Add(int64(n))
	}
	return nil
}
Beispiel #15
0
func trackerHandler(w http.ResponseWriter, r *http.Request) {
	if r.FormValue("compact") != "1" {
		http.Error(w, "Only compact protocol supported.", 400)
		return
	}

	info_hash := dht.InfoHash(r.FormValue("info_hash"))
	if len(info_hash) != 20 {
		http.Error(w, "Bad info_hash.", 400)
		return
	}

	response := TrackerResponse{
		Interval:    300,
		MinInterval: 60,
	}

	peers, ok := peerCache.Get(info_hash)

	dhtNode.Find(info_hash)

	if !ok || len(peers) == 0 {
		response.Interval = 30
		response.MinInterval = 10

		time.Sleep(5 * time.Second)

		peers, ok = peerCache.Get(info_hash)
	}

	if ok && len(peers) > 0 {
		response.Incomplete = len(peers)
		response.Peers = strings.Join(peers, "")
	}

	w.Header().Set("Content-Type", "application/octet-stream")

	if err := bencode.Marshal(w, response); err != nil {
		http.Error(w, err.Error(), 500)
	}
}
Beispiel #16
0
func (p *peerState) sendMetadataRequest(piece int) {
	log.Printf("Sending metadata request for piece %d to %s\n", piece, p.address)

	m := map[string]int{
		"msg_type": METADATA_REQUEST,
		"piece":    piece,
	}

	var raw bytes.Buffer
	err := bencode.Marshal(&raw, m)
	if err != nil {
		return
	}

	msg := make([]byte, raw.Len()+2)
	msg[0] = EXTENSION
	msg[1] = byte(p.theirExtensions["ut_metadata"])
	copy(msg[2:], raw.Bytes())

	p.sendMessage(msg)
}
Beispiel #17
0
func (self *Tracker) serveFile(w http.ResponseWriter, r *http.Request, file *File) {
	if file == nil {
		http.Error(w, "File not found", 404)
		return
	}

	for {
		// TODO: This could run infinitely in a case where the file is requested and deleted or
		// replaced, so we keep checking a structure that never will get filled in since it's no
		// longer active.
		file.Lock.Lock()
		if file.MetadataInfo == nil {
			file.Lock.Unlock()
			LogDebug("Request for missing metadata on %v. Sleeping.", file.Name)
			time.Sleep(1 * time.Second)
			continue
		}
		file.Lock.Unlock()
		break
	}

	file.Lock.Lock()
	md := Metadata{
		// Using Host like this is probably safe, but is potentially a hack.
		Announce: fmt.Sprintf("http://%s/announce", r.Host),
		Info:     *file.MetadataInfo,
	}
	file.Lock.Unlock()

	if file.SeedCommand == nil {
		self.startSeed(file, &md)
	}

	err := bencode.Marshal(w, md)
	if err != nil {
		LogError("Failed to bencode %s: %s", file.Name, err)
	}
}
Beispiel #18
0
func (p *peerState) SendExtensions(port uint16) {

	handshake := map[string]interface{}{
		"m": map[string]int{
			"ut_metadata": 1,
		},
		"v": "Taipei-Torrent dev",
	}

	var buf bytes.Buffer
	err := bencode.Marshal(&buf, handshake)
	if err != nil {
		//log.Println("Error when marshalling extension message")
		return
	}

	msg := make([]byte, 2+buf.Len())
	msg[0] = EXTENSION
	msg[1] = EXTENSION_HANDSHAKE
	copy(msg[2:], buf.Bytes())

	p.sendMessage(msg)
}
Beispiel #19
0
func (t *TorrentSession) DoMetadata(msg []byte, p *peerState) {
	var message MetadataMessage
	err := bencode.Unmarshal(bytes.NewReader(msg), &message)
	if err != nil {
		log.Println("Error when parsing metadata: ", err)
		return
	}

	mt := message.MsgType
	switch mt {
	case METADATA_REQUEST:
		if !t.si.HaveTorrent {
			break
		}

		rawInfo := t.m.RawInfo()

		from := message.Piece * METADATA_PIECE_SIZE

		// Piece asked must be between the first one and the last one.
		// Note that the last one will most of the time be smaller than
		// METADATA_PIECE_SIZE
		if from >= len(rawInfo) {
			log.Printf("%d is out of range. Not sending this\n", message.Piece)
			break
		}

		to := from + METADATA_PIECE_SIZE
		if to > len(rawInfo) {
			to = len(rawInfo)
		}

		if _, ok := p.theirExtensions["ut_metadata"]; !ok {
			log.Println("%s doesn't understand ut_metadata\n", p.address)
			break
		}

		respHeader := MetadataMessage{
			MsgType:   METADATA_DATA,
			Piece:     message.Piece,
			TotalSize: len(rawInfo),
		}

		var resp bytes.Buffer
		resp.WriteByte(EXTENSION)
		resp.WriteByte(byte(p.theirExtensions["ut_metadata"]))

		err = bencode.Marshal(&resp, respHeader)
		if err != nil {
			log.Println("Couldn't header metadata response: ", err)
			break
		}

		resp.Write(rawInfo[from:to])
		p.sendMessage(resp.Bytes())

	case METADATA_DATA:

		if t.si.HaveTorrent {
			break
		}

		if message.TotalSize == 0 {
			log.Println("No metadata size, bailing out")
			return
		}

		if message.Piece >= len(t.si.ME.Pieces) {
			log.Printf("Rejecting invalid metadata piece %d, max is %d\n",
				message.Piece, len(t.si.ME.Pieces)-1)
			break
		}

		pieceSize := METADATA_PIECE_SIZE
		if message.Piece == len(t.si.ME.Pieces)-1 {
			pieceSize = message.TotalSize - (message.TotalSize/METADATA_PIECE_SIZE)*METADATA_PIECE_SIZE
		}

		t.si.ME.Pieces[message.Piece] = msg[len(msg)-pieceSize:]

		finished := true
		for idx, data := range t.si.ME.Pieces {
			if len(data) == 0 {
				p.sendMetadataRequest(idx)
				finished = false
				break
			}
		}

		if !finished {
			break
		}

		log.Println("Finished downloading metadata!")
		var full bytes.Buffer
		for _, piece := range t.si.ME.Pieces {
			full.Write(piece)
		}
		info := full.Bytes()

		// Verify sha
		sha := sha1.New()
		sha.Write(info)
		actual := string(sha.Sum(nil))
		if actual != t.m.InfoHash {
			log.Println("Invalid metadata")
			log.Printf("Expected %x, got %x\n", t.m.InfoHash, actual)
			break
		}

		err = t.reload(info)
		if err != nil {
			return
		}

		if p.have == nil {
			if p.temporaryBitfield != nil {
				p.have = bitset.NewFromBytes(t.totalPieces, p.temporaryBitfield)
				p.temporaryBitfield = nil
			} else {
				p.have = bitset.New(t.totalPieces)
			}
		}
		if p.have == nil {
			log.Panic("Invalid bitfield data")
		}

		p.SendBitfield(t.pieceSet)
	case METADATA_REJECT:
		log.Printf("%d didn't want to send piece %d\n", p.address, message.Piece)
	default:
		log.Println("Didn't understand metadata extension type: ", mt)
	}
}
Beispiel #20
0
// handleAnnounce is the endpoint for torrent clients to announce themselves and request
// other peers.
func (self *Tracker) handleAnnounce(w http.ResponseWriter, r *http.Request) {
	values := r.URL.Query()

	peer, err := parsePeer(r, values)
	if err != nil {
		io.WriteString(w, err.Error())
		return
	}
	LogDebug("Request from peer at %s:%d.", peer.Ip, peer.Port)

	// Get other arguments and validate them.
	var info_hash string
	if info_hash_list, ok := values["info_hash"]; ok && len(info_hash_list) == 1 {
		info_hash = info_hash_list[0]
	}

	var event string
	if event_list, ok := values["event"]; ok && len(event_list) == 1 {
		event = event_list[0]
	}

	var numwant uint64
	if numwant_list, ok := values["numwant"]; ok && len(numwant_list) == 1 {
		numwant, err = strconv.ParseUint(numwant_list[0], 10, 8)
		if err != nil || numwant > 100 {
			numwant = 100
		}
	} else {
		numwant = 50
	}

	// Lock this now since we're validated our inputs.
	self.peerListLock.Lock()
	defer self.peerListLock.Unlock()

	peers, ok := self.PeerList[info_hash]
	if !ok {
		peers = make(map[string]Peer)
		self.PeerList[info_hash] = peers
	}

	peerseen, ok := self.PeerSeen[info_hash]
	if !ok {
		peerseen = make(map[string]time.Time)
		self.PeerSeen[info_hash] = peerseen
	}

	var peerage time.Duration
	peerlastseen, pok := peerseen[peer.Id]
	if pok {
		peerage = time.Since(peerlastseen)
	}

	// Add this peer to the set if they don't exist, plus possibly purge other peers on this IP.
	if _, ok := peers[peer.Id]; !ok {
		// Remove any other peers on this IP address. This is kind of a hack since we don't have
		// "last reported time" at the moment. If a new peer starts up on a host, then we remove
		// the other one.
		toRemove := make([]string, 0, 10)
		for id, tmpPeer := range peers {
			if tmpPeer.Ip == peer.Ip || (pok && peerage > 300*time.Second) {
				toRemove = append(toRemove, id)
			}
		}
		for _, id := range toRemove {
			delete(peers, id)
			delete(peerseen, id)
		}

		// Finally insert this new peer.
		peers[peer.Id] = *peer
	}

	// Always update the timestamp so we know when people report.
	peerseen[peer.Id] = time.Now()

	// If they're stopping, then remove this peer from the valid list.
	if event == "stopped" {
		LogInfo("Peer %s:%d is leaving the swarm.", peer.Ip, peer.Port)
		delete(peers, peer.Id)
		delete(peerseen, peer.Id)
	}

	// We give the user back N random peers by just picking a window into our peer list.
	ct := 0
	outPeers := make([]Peer, 0, numwant)
	for _, tmpPeer := range peers {
		if ct++; ct > cap(outPeers) {
			break
		}

		if tmpPeer.Ip == peer.Ip {
			// This helps avoid giving peers connections to their own machine, which seems
			// to confuse ctorrent. It seems to mostly affect small clusters.
			continue
		}
		outPeers = append(outPeers, tmpPeer)
		LogDebug("[%s:%d] peer %s:%d", peer.Ip, peer.Port, tmpPeer.Ip, tmpPeer.Port)
	}
	LogInfo("Giving peer %s:%d a list of %d peers (out of %d).",
		peer.Ip, peer.Port, len(outPeers), len(peers))

	// Build the output dictionary and return it.
	err = bencode.Marshal(w, PeerResponse{Interval: rand.Intn(120) + 300, Peers: outPeers})
	if err != nil {
		LogError("Failed to bencode: %s", err)
	}
}
Beispiel #21
0
func GetMetaInfo(dialer proxy.Dialer, torrent string) (metaInfo *MetaInfo, err error) {
	var input io.ReadCloser
	if strings.HasPrefix(torrent, "http:") {
		r, err := proxyHttpGet(dialer, torrent)
		if err != nil {
			return nil, err
		}
		input = r.Body
	} else if strings.HasPrefix(torrent, "magnet:") {
		magnet, err := parseMagnet(torrent)
		if err != nil {
			log.Println("Couldn't parse magnet: ", err)
			return nil, err
		}

		ih, err := dht.DecodeInfoHash(magnet.InfoHashes[0])
		if err != nil {
			return nil, err
		}

		metaInfo = &MetaInfo{InfoHash: string(ih), AnnounceList: magnet.Trackers}

		//Gives us something to call the torrent until metadata can be procurred
		metaInfo.Info.Name = hex.EncodeToString([]byte(ih))

		return metaInfo, err

	} else {
		if input, err = os.Open(torrent); err != nil {
			return
		}
	}

	// We need to calcuate the sha1 of the Info map, including every value in the
	// map. The easiest way to do this is to read the data using the Decode
	// API, and then pick through it manually.
	var m interface{}
	m, err = bencode.Decode(input)
	input.Close()
	if err != nil {
		err = errors.New("Couldn't parse torrent file phase 1: " + err.Error())
		return
	}

	topMap, ok := m.(map[string]interface{})
	if !ok {
		err = errors.New("Couldn't parse torrent file phase 2.")
		return
	}

	infoMap, ok := topMap["info"]
	if !ok {
		err = errors.New("Couldn't parse torrent file. info")
		return
	}
	var b bytes.Buffer
	if err = bencode.Marshal(&b, infoMap); err != nil {
		return
	}
	hash := sha1.New()
	hash.Write(b.Bytes())

	var m2 MetaInfo
	err = bencode.Unmarshal(&b, &m2.Info)
	if err != nil {
		return
	}

	m2.InfoHash = string(hash.Sum(nil))
	m2.Announce = getString(topMap, "announce")
	m2.AnnounceList = getSliceSliceString(topMap, "announce-list")
	m2.CreationDate = getString(topMap, "creation date")
	m2.Comment = getString(topMap, "comment")
	m2.CreatedBy = getString(topMap, "created by")
	m2.Encoding = strings.ToUpper(getString(topMap, "encoding"))

	metaInfo = &m2
	return
}