// 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 } } }
// 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 } } }
// 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 }
// 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 }
// 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() }() }
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()) } }
// 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() }
// 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()) }
// 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 }
// 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 }
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, ¶ms) } if err == nil { now := time.Now() t.m.Lock() err = t.t.handleAnnounce(now, peerListenAddress, ¶ms, 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()) } }
// 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 }
// 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)) } }
// 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 }
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) } }
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) }
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) } }
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) }
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) } }
// 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) } }
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 }