func writeEncoded(c appengine.Context, w http.ResponseWriter, data interface{}) { newEncoder := bencode.NewEncoder(w) if err := newEncoder.Encode(data); err != nil { c.Errorf("Failed to encode data: %s", err) writeInternalError(w) } }
// Bencodes an arbitrary dictionary as a tracker response. func encodeResponseMap(responseMap map[string]interface{}) io.Reader { responseBuf := bytes.NewBuffer(make([]byte, 0)) encoder := bencode.NewEncoder(responseBuf) _ = encoder.Encode(responseMap) return responseBuf }
func (p *peerState) SendExtensions(supportedExtensions map[int]string, metadataSize int64) { handshake := ExtensionHandshake{ M: make(map[string]int, len(supportedExtensions)), V: "Taipei-Torrent dev", MetadataSize: metadataSize, } for i, ext := range supportedExtensions { handshake.M[ext] = i } var buf bytes.Buffer err := bencode.NewEncoder(&buf).Encode(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 ReadTorrent(r io.Reader) (meta MetaInfo, err error) { // 读取文件信息 s, err := ioutil.ReadAll(r) if err != nil { return } err = bencode.DecodeBytes(s, &meta) if err != nil { return } hash := sha1.New() err = bencode.NewEncoder(hash).Encode(meta.Info) if err != nil { return } meta.InfoHash = fmt.Sprintf("%02X", string(hash.Sum(nil))) return /*var m interface{} m, err = bencode.Decode(r) if err != nil { return } topMap, ok := m.(map[string]interface{}) if !ok { return } infoMap, ok := topMap["info"] if !ok { return } var b bytes.Buffer if err = bencode.Marshal(&b, infoMap); err != nil { return } hash := sha1.New() hash.Write(b.Bytes()) err = bencode.Unmarshal(&b, &meta.Info) if err != nil { return } meta.InfoHash = fmt.Sprintf("%X", string(hash.Sum(nil))) meta.Announce = getString(topMap, "announce") meta.Comment = getString(topMap, "comment") meta.CreatedBy = getString(topMap, "created by") meta.CreationDate = getInt64(topMap, "creation date") meta.Encoding = getString(topMap, "encoding") return*/ }
// MD5Params returns the MD5 hash of the provided map[string]string. // This could easily be extended to support hashing any datatype supported // by bencode. func MD5Params(val map[string]string) (string, error) { md5Writer := md5.New() enc := bencode.NewEncoder(md5Writer) if err := enc.Encode(val); err != nil { return "", err } return fmt.Sprintf("%x", md5Writer.Sum(nil)), nil }
// Returns the size of the representation of this metainfo as a bencoded // dictionary func (m *MetaInfo) Size() (sz int) { var buf bytes.Buffer err := bencode.NewEncoder(&buf).Encode(m) if err != nil { log.Fatal("Couldn't bencode this metainfo: ", err) } return buf.Len() }
func (m *MetaInfo) saveToDisk(dir string) (err error) { ihhex := fmt.Sprintf("%x", m.InfoHash) f, err := os.Create(filepath.Join(dir, ihhex)) if err != nil { log.Println("Error when opening file for creation: ", err) return } defer f.Close() return bencode.NewEncoder(f).Encode(m) }
// Returns a bencoded version of the torrent's info dict. func (t *TorrentFile) BencodeInfoDict() ([]byte, error) { infoBuffer := bytes.NewBuffer(make([]byte, 0)) encoder := bencode.NewEncoder(infoBuffer) err := encoder.Encode(t.Info) if err != nil { fmt.Printf("error encoding torrent file: %s", err.Error()) } return infoBuffer.Bytes(), err }
func benc(w http.ResponseWriter, req *http.Request) { enc := bencode.NewEncoder(w) if err := enc.Encode(map[string]interface{}{ "foo": "bar", "baz": 2, "buf": []string{"baz", "bloof"}, }); err != nil { log.Println(err) } log.Printf("Handled request from %s :)", req.RemoteAddr) }
func (query *Query) constructArgs(args interface{}) error { buf := new(bytes.Buffer) bencodeE := bencode.NewEncoder(buf) if err := bencodeE.Encode(args); err != nil { return err } data, err := encodeIntoBigEndian(buf) query.ProcedureData = data return err }
func announceRecover(w http.ResponseWriter) { err := recover() if err == nil { return } w.WriteHeader(http.StatusInternalServerError) enc := bencode.NewEncoder(w) enc.Encode(M{"failure reason": err}) //DEBUG: newline for debugging fmt.Fprintln(w, "") }
// Converts torrent to SUPRA-PRIVATE torrent // // Sets the private flag to 1 and embeds the supplied secret and hash // for authentication purposes. // // This torrent file SHOULD NOT be shared between users or statistics collection // and anti-abuse mechanisms will be skewed for that user. func (t *TorrentFile) WriteFile(secret, hash []byte) ([]byte, error) { fmt.Printf("writing file...") t.Announce = fmt.Sprintf("http://tracker.fatalsyntax.com:4200/%s/%s/announce", hex.EncodeToString(secret), hex.EncodeToString(hash)) t.Encoding = "UTF-8" infoBuffer := bytes.NewBuffer(make([]byte, 0)) encoder := bencode.NewEncoder(infoBuffer) err := encoder.Encode(t) if err != nil { return nil, err } return infoBuffer.Bytes(), nil }
func (cs *ControlSession) DoMetadata(msg []byte, p *peerState) (err error) { var message IHMessage err = bencode.NewDecoder(bytes.NewReader(msg)).Decode(&message) if err != nil { cs.log("Couldn't decode metadata message: ", err) return } if message.Port == 0 { return } // take his IP addr, use the advertised port ip := p.conn.RemoteAddr().(*net.TCPAddr).IP.String() port := strconv.Itoa(int(message.Port)) peer := ip + ":" + port if cs.isNewerThan(message.Info.Rev) { return } var tmpInfoBuf bytes.Buffer err = bencode.NewEncoder(&tmpInfoBuf).Encode(message.Info) if err != nil { cs.log("Couldn't encode ih message, returning now") return err } rawInfo := tmpInfoBuf.Bytes() pub := [ed.PublicKeySize]byte(cs.ID.Pub) var sig [ed.SignatureSize]byte copy(sig[0:ed.SignatureSize], message.Sig) ok := ed.Verify(&pub, rawInfo, &sig) if !ok { return errors.New("Bad Signature") } var test IHMessage err = bencode.NewDecoder(bytes.NewReader(msg)).Decode(&test) cs.session.SaveIHMessage(msg) cs.Torrents <- Announce{ infohash: message.Info.InfoHash, peer: peer, } return }
// Encode's the `info` dictionary into a SHA1 hash; used to uniquely identify a torrent. func (t *TorrentFile) EncodeInfo() []byte { //torrentDict := torrentMetainfo.(map[string]interface{}) infoBytes := make([]byte, 0) //TODO: intelligenty size buffer based on info infoBuffer := bytes.NewBuffer(infoBytes) encoder := bencode.NewEncoder(infoBuffer) err := encoder.Encode(t.Info) if err != nil { fmt.Printf("error encoding torrent file: %s", err.Error()) } hash := sha1.New() io.Copy(hash, infoBuffer) return hash.Sum(nil) }
// Returns the representation of this metainfo's info dict as a // bencoded dictionary func (m *MetaInfo) RawInfo() (b []byte) { if m.rawInfo != nil { return m.rawInfo } if m.Info == nil { return []byte{} } var buf bytes.Buffer err := bencode.NewEncoder(&buf).Encode(m.Info) if err != nil { log.Fatal("Couldn't bencode this metainfo's dict: ", err) } m.rawInfo = buf.Bytes() return m.rawInfo }
func (p *peerState) sendExtensionMessage(typ string, data interface{}) { if _, ok := p.theirExtensions[typ]; !ok { // They don't understand this extension return } var payload bytes.Buffer err := bencode.NewEncoder(&payload).Encode(data) if err != nil { log.Printf("Couldn't marshal extension message: ", err) } msg := make([]byte, 2+payload.Len()) msg[0] = EXTENSION msg[1] = byte(p.theirExtensions[typ]) copy(msg[2:], payload.Bytes()) p.sendMessage(msg) }
func (w *Watcher) torrentify() (ih string, err error) { w.lock.Lock() defer w.lock.Unlock() meta, err := createMeta(w.watchedDir) if err != nil { log.Println(err) return } var buf bytes.Buffer err = bencode.NewEncoder(&buf).Encode(meta) if err != nil { return } w.session.SaveTorrent(buf.Bytes(), meta.InfoHash, time.Now().Format(time.RFC3339)) return meta.InfoHash, err }
func NewMetaInfoFromContent(content []byte) (m *MetaInfo, err error) { var m1 MetaInfo err1 := bencode.DecodeString(string(content), &m1) if err1 != nil { err = errors.New("Couldn't parse torrent file: " + err1.Error()) return } hash := sha1.New() err1 = bencode.NewEncoder(hash).Encode(m1.Info) if err1 != nil { return } m1.InfoHash = string(hash.Sum(nil)) return &m1, nil }
func (cs *ControlSession) SetCurrent(ih string) error { if cs.currentIH == ih { return nil } parts := strings.Split(cs.rev, "-") if len(parts) != 2 { cs.logf("Invalid rev: %s\n", cs.rev) parts = []string{"0", ""} } counter, err := strconv.Atoi(parts[0]) if err != nil { counter = 0 } newCounter := strconv.Itoa(counter + 1) cs.logf("Updating rev with ih %x", ih) newRev := newCounter + "-" + fmt.Sprintf("%x", sha1.Sum([]byte(ih+parts[1]))) mess, err := NewIHMessage(int64(cs.Port), ih, newRev, cs.ID.Priv) if err != nil { return err } var buf bytes.Buffer err = bencode.NewEncoder(&buf).Encode(mess) if err != nil { return err } err = cs.session.SaveIHMessage(buf.Bytes()) if err != nil { return err } cs.currentIH = ih cs.rev = newRev cs.broadcast(mess) return nil }
func NewIHMessage(port int64, ih, rev string, priv id.PrivKey) (mm IHMessage, err error) { info := NewInfo{ InfoHash: ih, Rev: rev, } var buf bytes.Buffer err = bencode.NewEncoder(&buf).Encode(info) if err != nil { log.Println("[CONTROL] Couldn't encode ih message, returning now") return mm, err } var privarg [ed.PrivateKeySize]byte copy(privarg[:], priv[:]) sig := ed.Sign(&privarg, buf.Bytes()) return IHMessage{ Info: info, Port: port, Sig: string(sig[:]), }, nil }
func announce(c *Context) { defer announceRecover(c.w) log.Print(c.r.RawURL) a, err := ParseAnnounce(c.r) if err != nil { log.Panic("ERROR: ", err) } //grab the user by ip co := c.DB.C("users") var user *User co.Find(M{"ips": a.IP.String()}).One(&user) if user == nil { log.Panic("Unauthorized IP: ", a.IP) } //update the user in the database user.LastIP = a.IP.String() user.LastSeen = bson.Now() user.Update(a.InfoHash, a.Event, a.Left) co.Update(M{"_id": user.ID}, user) var peers []StructPeer query := co.Find(M{"info.infohash": a.InfoHash, "_id": M{"$ne": user.ID}}) query.Limit(min(50, max(0, a.Numwant))) //bound between 0 <= n <= 50 selector := M{"lastip": 1, "port": 1} if !a.NoPeerId { selector["peerid"] = 1 } query.Select(selector) query.All(&peers) if query.Iter().Err() != nil { panic(query.Iter().Err()) } enc := bencode.NewEncoder(c.w) //build the response if a.Compact { response := CompactAnnounceResponse{ Interval: 30, Complete: 0, Incomplete: 0, Peers: make([]string, len(peers)), } for i := range peers { response.Peers[i] = peers[i].Compact() } enc.Encode(response) } else { response := AnnounceResponse{ Interval: 30, Complete: 0, Incomplete: 0, Peers: peers, } enc.Encode(response) } fmt.Fprintln(c.w, "") }
func (t *Torrent) Resolve() error { if t.IsMagnet() { t.hasResolved = true return nil } var torrentFile struct { Announce string `bencode:"announce"` AnnounceList [][]string `bencode:"announce-list"` Info map[string]interface{} `bencode:"info"` } // We don't need trackers for public torrents since we'll find them on the // DHT or public trackers if (t.InfoHash != "" && t.Name != "" && t.Peers > 0 && t.Seeds > 0) && (t.IsPrivate == false || len(t.Trackers) > 0) { return nil } parts := strings.Split(t.URI, "|") uri := parts[0] req, err := http.NewRequest("GET", uri, nil) if err != nil { return err } if len(parts) > 1 { for _, part := range parts[1:] { keyVal := strings.SplitN(part, "=", 2) req.Header.Add(keyVal[0], keyVal[1]) } } resp, err := httpClient.Do(req) if err != nil { return err } dec := bencode.NewDecoder(resp.Body) // FIXME!!!! if err := dec.Decode(&torrentFile); err != nil { return err } if t.InfoHash == "" { hasher := sha1.New() bencode.NewEncoder(hasher).Encode(torrentFile.Info) t.InfoHash = hex.EncodeToString(hasher.Sum(nil)) } if t.Name == "" { t.Name = torrentFile.Info["name"].(string) } if len(t.Trackers) == 0 { t.Trackers = append(t.Trackers, torrentFile.Announce) for _, trackers := range torrentFile.AnnounceList { t.Trackers = append(t.Trackers, trackers...) } } t.hasResolved = true t.initialize() return nil }
// Processes received queries func (node *Node) processQuery(query *Query, addr net.Addr) error { procedureName := query.ProcedureName if procedure := node.procedures[procedureName]; procedure != nil { // Procedure exists (it's being served) method := procedure.Method function := method.Func // Initialize value argValue, replyValue := reflect.New(procedure.ArgType.Elem()), reflect.New(procedure.ReplyType.Elem()) // Set value of arg argsReader := bytes.NewReader(query.ProcedureData) argsDecoder := bencode.NewDecoder(argsReader) err := argsDecoder.Decode(argValue.Interface()) if err != nil { fmt.Errorf("qrp:", "Error decoding procedure data into value: %s\n", err.Error()) return err } // Invoke the function function.Call([]reflect.Value{procedure.Receiver, argValue, replyValue}) // Create reply reply := Reply{MessageID: query.MessageID} argsBuf := new(bytes.Buffer) argsEncoder := bencode.NewEncoder(argsBuf) argsEncoder.Encode(replyValue.Interface()) reply.ReturnData, err = encodeIntoBigEndian(argsBuf) if err != nil { fmt.Errorf("qrp:", "Error encoding reply return data: %s\n", err.Error()) return err } // Create message message := Message{Reply: &reply} // Encode message messageBuf := new(bytes.Buffer) messageEncoder := bencode.NewEncoder(messageBuf) err = messageEncoder.Encode(message) if err != nil { fmt.Errorf("qrp:", "Error encoding reply message into BEncode: %s\n", err.Error()) return err } message_bigEndian, err := encodeIntoBigEndian(messageBuf) if err != nil { fmt.Errorf("qrp:", "Error encoding reply message into BigEndian: %s\n", err.Error()) return err } // Send to host node.sendingMutex.Lock() node.connection.WriteTo(message_bigEndian, addr) node.sendingMutex.Unlock() return nil } else { return &BadProcedureError{procedureName} } return nil }
func Share(cliId string, workDir string, cliTarget string, trackers []string, useLPD bool, manualPeers []string) { shareID, err := id.NewFromString(cliId) if err != nil { fmt.Printf("Couldn't generate shareId: %s\n", err) return } sessionName := hex.EncodeToString(shareID.Infohash) + ".sql" session, err := sharesession.New(filepath.Join(workDir, sessionName)) if err != nil { log.Fatal("Couldn't open session file: ", err) } fmt.Printf("WriteReadStore:\t%s\n ReadStore:\t%s\n Store:\t%s\n", shareID.WRS(), shareID.RS(), shareID.S()) target := session.GetTarget() if target == "" { if cliTarget == "" { fmt.Println("Need a folder to share!") return } target = cliTarget session.SaveSession(target, shareID) } else if cliTarget != "" { fmt.Printf("Can't override folder already set to %s\n", target) } _, err = os.Stat(target) if err != nil { if os.IsNotExist(err) { os.MkdirAll(target, 0744) } else { fmt.Printf("%s is an invalid dir: %s\n", target, err) os.Exit(1) } } // Watcher watcher := &Watcher{ PingNewTorrent: make(chan string), } if shareID.CanWrite() { watcher, err = NewWatcher(session, filepath.Clean(target)) if err != nil { log.Fatal("Couldn't start watcher: ", err) } } else { watcher.PingNewTorrent = make(chan string, 1) watcher.PingNewTorrent <- session.GetCurrentInfohash() } // External listener conChan, listenPort, err := listenForPeerConnections([]byte(shareID.Psk[:])) if err != nil { log.Fatal("Couldn't listen for peers connection: ", err) } var currentSession TorrentSessionI = EmptyTorrent{} // quitChan quitChan := listenSigInt() // LPD lpd := &Announcer{announces: make(chan *Announce)} if useLPD { lpd, err = NewAnnouncer(listenPort) if err != nil { log.Fatal("Couldn't listen for Local Peer Discoveries: ", err) } } // Control session controlSession, err := NewControlSession(shareID, listenPort, session, trackers) if err != nil { log.Fatal(err) } if useLPD { lpd.Announce(string(shareID.Infohash)) } for _, peer := range manualPeers { controlSession.backoffHintNewPeer(peer) } peers := session.GetPeers() for _, p := range peers { log.Printf("Feeding with known peer: %s\n", p) controlSession.backoffHintNewPeer(p) } log.Println("Starting.") mainLoop: for { select { case <-quitChan: err := currentSession.Quit() if err != nil { log.Println("Failed: ", err) } else { log.Println("Done") } break mainLoop case c := <-conChan: if currentSession.Matches(c.infohash) { currentSession.AcceptNewPeer(c) } else if controlSession.Matches(c.infohash) { controlSession.AcceptNewPeer(c) } case announce := <-lpd.announces: hexhash, err := hex.DecodeString(announce.infohash) if err != nil { log.Println("Err with hex-decoding:", err) break } if controlSession.Matches(string(hexhash)) { controlSession.backoffHintNewPeer(announce.peer) } case ih := <-watcher.PingNewTorrent: if ih == controlSession.currentIH && !currentSession.IsEmpty() { break } err := controlSession.SetCurrent(ih) if err != nil { log.Fatal("Error setting new current infohash:", err) } currentSession.Quit() torrentFile := session.GetCurrentTorrent() tentativeSession, err := NewTorrentSession(shareID, target, torrentFile, listenPort) if err != nil { if !os.IsNotExist(err) { log.Println("Couldn't start new session from watched dir: ", err) } // Fallback to an emptytorrent, because the previous one is // invalid; hope it will be ok next time ! currentSession = EmptyTorrent{} break } currentSession = tentativeSession go currentSession.DoTorrent() for _, peer := range controlSession.peers.All() { currentSession.hintNewPeer(peer.address) } case announce := <-controlSession.Torrents: if controlSession.currentIH == announce.infohash && !currentSession.IsEmpty() { break } err := controlSession.SetCurrent(announce.infohash) if err != nil { log.Fatal("Error setting new current infohash:", err) } currentSession.Quit() log.Println("Opening new torrent session") magnet := fmt.Sprintf("magnet:?xt=urn:btih:%x", announce.infohash) tentativeSession, err := NewTorrentSession(shareID, target, magnet, listenPort) if err != nil { log.Println("Couldn't start new session from announce: ", err) currentSession = EmptyTorrent{} break } currentSession = tentativeSession go currentSession.DoTorrent() currentSession.hintNewPeer(announce.peer) case peer := <-controlSession.NewPeers: if currentSession.IsEmpty() { magnet := fmt.Sprintf("magnet:?xt=urn:btih:%x", controlSession.currentIH) tentativeSession, err := NewTorrentSession(shareID, target, magnet, listenPort) if err != nil { log.Printf("Couldn't start new session with new peer: %s\n", err) break } currentSession = tentativeSession go currentSession.DoTorrent() } currentSession.hintNewPeer(peer) case meta := <-currentSession.NewMetaInfo(): var buf bytes.Buffer err := bencode.NewEncoder(&buf).Encode(meta) if err != nil { log.Println(err) break } session.SaveTorrent(buf.Bytes(), meta.InfoHash, time.Now().Format(time.RFC3339)) } } }
// Tries to call 'procedure' on remote node, with supplied 'args' and allocated return values 'reply'. // 'timeout' can be used to specify a maximum time to wait for a reply (in seconds). If timeout is 0, we wait forever. // The reliability of this completing successfully is dependent on the network protocol (UDP is unreliable) // Returns an error if there is a timeout func (node *Node) Call(procedure string, addr net.Addr, args interface{}, reply interface{}, timeout int) (err error) { // Get our call, which contains the message ID call := node.nextCall(addr) // Create Query query := Query{ProcedureName: procedure, MessageID: call.MessageID} query.constructArgs(args) // Create Message message := Message{Query: &query} // Encode it into BEncode buf := new(bytes.Buffer) bencodeE := bencode.NewEncoder(buf) if err := bencodeE.Encode(message); err != nil { fmt.Errorf("qrp:", "Error encoding query message into BEncode: %s\n", err.Error()) return err } buf_bigEndian, err := encodeIntoBigEndian(buf) if err != nil { fmt.Errorf("qrp:", "Error encoding query message into BigEndian: %s\n", err.Error()) return err } // Create channel for receiving response responseChan := make(responseChannel) // Allocate channel node.pendingMutex.Lock() node.pending[call] = &responseChan node.pendingMutex.Unlock() // Delete channel after exit defer func() { node.pendingMutex.Lock() delete(node.pending, call) node.pendingMutex.Unlock() }() // Send to host node.sendingMutex.Lock() node.connection.WriteTo(buf_bigEndian, addr) node.sendingMutex.Unlock() // If timeout isn't 0, initate the timeout function concurrently timeoutChan := make(chan bool, 1) if timeout > 0 { go func() { // Timeout function time.Sleep(time.Duration(timeout) * time.Second) timeoutChan <- true }() } // Wait for response on channel select { case replydata := <-responseChan: // We received a reply // Decode args argsReader := bytes.NewReader(replydata) argsDecoder := bencode.NewDecoder(argsReader) err := argsDecoder.Decode(reply) if err != nil { fmt.Errorf("qrp:", "Error decoding reply return data into value: %s\n", err.Error()) return err } case <-timeoutChan: // We timed out return new(TimeoutError) } return nil }
func createMeta(dir string) (meta *MetaInfo, err error) { blockSize := int64(1 << 20) // 1MiB fileDicts := make([]*FileDict, 0) hasher := NewBlockHasher(blockSize) err = torrentWalk(dir, func(path string, info os.FileInfo, perr error) (err error) { if perr != nil { return perr } f, err := os.Open(path) if err != nil { return errors.New(fmt.Sprintf("Couldn't open %s for hashing: %s\n", path, err)) } defer f.Close() _, err = io.Copy(hasher, f) if err != nil { log.Printf("Couldn't hash %s: %s\n", path, err) return err } relPath, err := filepath.Rel(dir, path) if err != nil { return } fileDict := &FileDict{ Length: info.Size(), Path: strings.Split(relPath, string(os.PathSeparator)), } fileDicts = append(fileDicts, fileDict) return }) if err != nil { return } end := hasher.Close() if end != nil { return } meta = &MetaInfo{ Info: &InfoDict{ Pieces: string(hasher.Pieces), PieceLength: blockSize, Private: 0, Name: "rakoshare", Files: fileDicts, }, } hash := sha1.New() err = bencode.NewEncoder(hash).Encode(meta.Info) if err != nil { return } meta.InfoHash = string(hash.Sum(nil)) return }