// handleOptionalParameters parses the optional parameters as described in BEP // 41 and updates an announce with the values parsed. func handleOptionalParameters(packet []byte) (bittorrent.Params, error) { if len(packet) == 0 { return bittorrent.ParseURLData("") } var buf = newBuffer() defer buf.free() for i := 0; i < len(packet); { option := packet[i] switch option { case optionEndOfOptions: return bittorrent.ParseURLData(buf.String()) case optionNOP: i++ case optionURLData: if i+1 >= len(packet) { return nil, errMalformedPacket } length := int(packet[i+1]) if i+2+length > len(packet) { return nil, errMalformedPacket } n, err := buf.Write(packet[i+2 : i+2+length]) if err != nil { return nil, err } if n != length { return nil, fmt.Errorf("expected to write %d bytes, wrote %d", length, n) } i += 2 + length default: return nil, errUnknownOptionType } } return bittorrent.ParseURLData(buf.String()) }
// ParseScrape parses an bittorrent.ScrapeRequest from an http.Request. func ParseScrape(r *http.Request) (*bittorrent.ScrapeRequest, error) { qp, err := bittorrent.ParseURLData(r.RequestURI) if err != nil { return nil, err } infoHashes := qp.InfoHashes() if len(infoHashes) < 1 { return nil, bittorrent.ClientError("no info_hash parameter supplied") } request := &bittorrent.ScrapeRequest{ InfoHashes: infoHashes, Params: qp, } return request, nil }
// ParseAnnounce parses an bittorrent.AnnounceRequest from an http.Request. // // If allowIPSpoofing is true, IPs provided via params will be used. // If realIPHeader is not empty string, the first value of the HTTP Header with // that name will be used. func ParseAnnounce(r *http.Request, realIPHeader string, allowIPSpoofing bool) (*bittorrent.AnnounceRequest, error) { qp, err := bittorrent.ParseURLData(r.RequestURI) if err != nil { return nil, err } request := &bittorrent.AnnounceRequest{Params: qp} eventStr, _ := qp.String("event") request.Event, err = bittorrent.NewEvent(eventStr) if err != nil { return nil, bittorrent.ClientError("failed to provide valid client event") } compactStr, _ := qp.String("compact") request.Compact = compactStr != "" && compactStr != "0" infoHashes := qp.InfoHashes() if len(infoHashes) < 1 { return nil, bittorrent.ClientError("no info_hash parameter supplied") } if len(infoHashes) > 1 { return nil, bittorrent.ClientError("multiple info_hash parameters supplied") } request.InfoHash = infoHashes[0] peerID, ok := qp.String("peer_id") if !ok { return nil, bittorrent.ClientError("failed to parse parameter: peer_id") } if len(peerID) != 20 { return nil, bittorrent.ClientError("failed to provide valid peer_id") } request.Peer.ID = bittorrent.PeerIDFromString(peerID) request.Left, err = qp.Uint64("left") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: left") } request.Downloaded, err = qp.Uint64("downloaded") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: downloaded") } request.Uploaded, err = qp.Uint64("uploaded") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: uploaded") } numwant, err := qp.Uint64("numwant") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: numwant") } request.NumWant = uint32(numwant) port, err := qp.Uint64("port") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: port") } request.Peer.Port = uint16(port) request.Peer.IP = requestedIP(r, qp, realIPHeader, allowIPSpoofing) if request.Peer.IP == nil { return nil, bittorrent.ClientError("failed to parse peer IP address") } // Sanitize IPv4 addresses to 4 bytes. if ip := request.Peer.IP.To4(); ip != nil { request.Peer.IP = ip } return request, nil }