def run(self): SIZE = 65000 while True: #TODO: what if not all the data comes at once? data, address = self.sock.recvfrom(SIZE) if address[0] == self.ip and address[1] == self.ignore_port: continue query = ZapTorrentProtocolParser(data) zap_debug_print("in zap_broadcast and got some data! ", data) zap_debug_print("address is", address) zap_debug_print("my ignoring stuff is", self.ip, self.ignore_port) query.parse() if query.message_type == 'error': self.sock.sendto(query.response, address) elif query.message_type == 'files?': #BUILD LIST OF FILES AND SEND BACK response = ZapTorrentProtocolResponse(response_type='files', name=ZapConfig.name, ip=self.ip, port=ZapConfig.tcp_port) for filename in self.local_files.get_files(): f = self.local_files.get_files()[filename][0] zap_debug_print("Adding a local file, and it is", f) response.add(f) zap_debug_print("response is ", response.as_response()) self.sock.sendto(response.as_response(), address)
def download_block(self, filename, id, ip, port): query = ZapTorrentProtocolResponse(response_type='download?', filename=filename, id=id, name=ZapConfig.name, ip=ZapConfig.ip, port=ZapConfig.tcp_port) msg = query.as_response() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip, int(port))) self.send_to_socket(sock, msg) resp = sock.recv(54000) zap_debug_print("download_blocks got %s back" % resp) #TODO: what if we don't receive the # entire first chunk, or we get an error? if len(resp) == 0: raise RuntimeError("socket closed remotely!") parser = ZapTorrentProtocolParser(resp) parser.parse() if parser.message_type == 'download': #make sure we have the whole block while len(parser.fields['data']) < int(parser.fields['bytes']): parser.fields['data'] += sock.recv(54000) sock.close() return parser.fields['data'] else: zap_debug_print("Error downloading a block: got ", resp) sock.close() return None
def get_blocks(self, **kwargs): if 'status' in kwargs: for block in self.blocks: zap_debug_print(dir(block)) return filter(lambda x: x.status == kwargs['status'], self.blocks) else: return self.blocks
def run(self): self.print_welcome() self.discoverer.start() #Send something to the discover sock so we can bind it to a port self.discoverer.sock.sendto("HURP DURP", ("<broadcast>", self.broadcast_port)) own_address = self.discoverer.sock.getsockname() zap_debug_print("my udp discovery port is", own_address[1]) ignore_port = own_address[1] b = ZapBroadcast(self.broadcast_port, self.local_files, self.remote_files, self.ip, ignore_port) b.start() tcp_server = ZapTCPServer(port=self.tcp_port, host=self.ip, local_files=self.local_files) tcp_server.start() while True: line = raw_input(self.prompt + " ") if re.match('^quit$', line): self.quit() elif re.match('^name (\w+)$', line): ZapConfig.name = re.match(r"^name (\w+)$", line).group(1) print("Name is now %s" % ZapConfig.name) elif re.match('^list$', line): self.remote_files.clear() query = ZapTorrentProtocolResponse(response_type="files?").as_response() s = self.discoverer.sock length = 0 while length < len(query): sent_length = s.sendto(query, ("<broadcast>", self.broadcast_port)) length += sent_length #now wait for the filesLister to get more info print("Waiting for response from peers. . .") time.sleep(3) for k in self.remote_files.get_files(): print("File: %s" % k) elif re.match('^load ([\w\._\-/]+)$', line): path = re.match('^load ([\w\._\-/]+)$', line).group(1) f = ZapFile() if f.set_path(path): zap_debug_print("Loaded a local file and it is ", f) self.local_files.add(f) print("File at %s loaded for sharing." % path) else: print("File at %s doesn't exist, or it is a directory. Try a different path." % path) elif re.match('^get ([\w\.\-]+)$', line): filename = re.match('^get ([\w\.\-]+)$', line).group(1) remote_files = self.remote_files.get(filename) if remote_files is not None: downloader = ZapDownloader(remote_files, self.local_files) downloader.start() print("Starting to download %s." % filename) else: print("No files by that name. Sorry. Try 'list' to update your list of files.") else: self.print_usage() continue
def __init__(self, port, verbose): self.prompt = "[Zap Torrent]" self.port = port self.local_files = ZapFiles() self.remote_files = ZapFiles() ZapConfig.verbose = verbose self.ip = self.get_ip() zap_debug_print("my ip is %s, port is %d" % (self.ip, self.port)) self.broadcast_port = port ZapConfig.ip = self.get_ip() self.discoverer = FilesLister(port=self.broadcast_port, remote_files=self.remote_files) ZapConfig.tcp_port = random.randint(1300, 40000) self.tcp_port = ZapConfig.tcp_port
def run(self): """Spawn off threads to download each block. When all threads return, check if the file is completely downloaded. If so, determine its digest and make sure it matches, and save it to disk.""" start_time = time.time() file_info = self.remote_files[0] remote_file = ZapFile() remote_file.filename = file_info.filename remote_file.number_of_blocks = file_info.number_of_blocks remote_file.mark_as_remote() self.local_files.add(remote_file) child_threads = [] for f in self.remote_files: remote_location = {} remote_location['ip'] = f.ip remote_location['port'] = f.port remote_location['name'] = f.name child_thread = self.remote_file_downloader(remote_file, f) child_thread.start() child_threads.append(child_thread) # How do we wait for them to finish? # TODO: what if I can't download the whole file? while not remote_file.is_downloaded(): time.sleep(4) # Now all child threads are gone, I hope. remote_file.save_to_disk() zap_debug_print("remote file digest is ", remote_file.digest, "file_info.digest is ", file_info.digest) if remote_file.digest != file_info.digest: # Our file does not match. Quit this thread and return an error zap_debug_print("Digest does not match! I should delete downloaded file!") self.local_files.remove(remote_file) os.remove(remote_file.path) return False else: stop_time = time.time() log_string = "file %s %s %s" % (remote_file.filename, os.path.getsize(remote_file.path), stop_time - start_time) zap_log(log_string) print("Finished downloading %s. Find it at %s." % (remote_file.filename, remote_file.path)) return True
def get_ip(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ip = "" try: sock.connect(("google.com", 80)) ip = sock.getsockname()[0] except socket.gaierror: # if there is no connection to the internet, try getting it by just # broadcasting sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: sock.sendto("HURP DURP", ("<broadcast>", self.port)) zap_debug_print("self.port is %d" % self.port) ip = sock.getsockname()[0] except socket.error: print >> sys.stderr, ("Sorry, but I can't connect to the network for some reason.") print >> sys.stderr, ("Check your network connection and try running ZapTorrent again.") sys.exit(2) return ip
def as_response(self): """Format the response as a ZapTorrent protocol response and return it as a string.""" # <b>Response</b> # ZT 1.0 files [name] [IP] [port] [number]\n # [filename] [digest] [blocks]\n # [filename] [digest] [blocks]\n # ... response_string = self.string if self.response_type == "files": # add the four fields response_string += " %s %s %d %d\n" % (self.fields['name'], self.fields['ip'], self.fields['port'], len(self.stuff_to_add)) zap_debug_print("in as_response before for loop, and constructed following response: %s" % response_string) for f in self.stuff_to_add: response_string += "%s %s %d\n" % (f.filename, f.digest, f.number_of_blocks) zap_debug_print("in as_response, and constructed following response: %s" % response_string) elif self.response_type == "files?": response_string += "\n" # <b>Message</b> # ZT 1.0 inventory? [filename]\n # <b>Response</b> # ZT 1.0 inventory [filename] [blocks]\n # [id] [bytes]\n # [id] [bytes]\n # ... elif self.response_type == 'inventory?': response_string += " %s\n" % self.fields['filename'] elif self.response_type == 'inventory': response_string += " %s %d\n" % (self.fields['filename'], self.fields['blocks']) for f in self.stuff_to_add: response_string += "%d %d\n" % (f.id, f.size) # <b>Message</b> # ZT 1.0 download? [filename] [id] [name] [IP] [port]\n # <b>Response</b> # ZT 1.0 download [filename] [id] [bytes]\n # ... elif self.response_type == 'download?': response_string += " %s %s %s %s %s\n" % (self.fields['filename'], self.fields['id'], self.fields['name'], self.fields['ip'], self.fields['port']) elif self.response_type == 'download': number_of_bytes = len(self.fields['bytes']) zap_debug_print("Fields are", self.fields['filename'], self.fields['id'], number_of_bytes, self.fields['bytes']) response_string += " %s %d %d\n" % (self.fields['filename'], int(self.fields['id']), number_of_bytes) response_string += self.fields['bytes'] return response_string
def run(self): #TODO: what if we don't receive it all at once? msg = self.sock.recv(54000) zap_debug_print("Got a message on the ZAPTCPResponseThread and it is", msg) response = "" query = ZapTorrentProtocolParser(msg) query.parse() if query.message_type == 'error': response = query.response elif query.message_type == 'inventory?': #Look for the file in local_files f = self.local_files.get(query.fields['filename']) if f is None: response = "ZT 1.0 error No file named %s" % query.fields['filename'] else: f = f[0] r = ZapTorrentProtocolResponse(response_type="inventory", filename=f.filename, blocks=f.number_of_blocks) r.stuff_to_add = f.get_blocks(status='present') zap_debug_print("got back some blocks and they looks like this:", r.stuff_to_add) response = r.as_response() elif query.message_type == 'download?': #make sure we have the file f = self.local_files.get(query.fields['filename']) if f is None: response = "ZT 1.0 error No file named %s" % query.fields['filename'] else: f = f[0] if f.block_is_present(int(query.fields['id'])): r = ZapTorrentProtocolResponse(response_type="download", filename=f.filename, id=query.fields['id'], bytes=f.get_block(int(query.fields['id'])).get_bytes()) response = r.as_response() log_string = "upload %s %s %s %s %s %s" % (f.filename, ZapConfig.name, query.fields['ip'], query.fields['port'], query.fields['id'], len(f.get_block(int(query.fields['id'])).get_bytes())) zap_log(log_string) else: response = "ZT 1.0 error No block for %s at %s\n" % (f.filename, query.fields['id']) else: response = "ZT 1.0 error unknown TCP query type.\n" sent_length = 0 zap_debug_print("sending %s as response" % response) while sent_length < len(response): message_remaining = response[sent_length:] length = self.sock.send(message_remaining) sent_length += length #TODO: do I close it on my end? self.sock.close()
def run(self): # In a loop - aquire the lock # Check if the peer info has any stuff that I don't have # If so, mark it as downloading and release the lock # download it # Aquire the lock again # Mark the downloaded block as present # Release the lock while True: # We get the blocks each time in case the peer # has since received new blocks blocks_available = self.get_available_blocks(self.peer_info.ip, self.peer_info.port, self.peer_info.filename) zap_debug_print("Blocks available are ", blocks_available) self.remote_file.sem.acquire() block_to_download = None for block_id in blocks_available: if self.remote_file.does_block_needs_downloading(int(block_id)): block_to_download = int(block_id) self.remote_file.mark_block_as('downloading', int(block_to_download)) break self.remote_file.sem.release() if block_to_download is None: zap_debug_print("No more blocks to download from" + " this peer: ", self.peer_info) break data = self.download_block(self.peer_info.filename, block_to_download, self.peer_info.ip, self.peer_info.port) zap_debug_print("received %s back from the download query" % data) if data is not None: self.remote_file.set_block_data(block_to_download, data) self.remote_file.mark_block_as('present', block_to_download) log_string = "download %s %s %s %s %s %s" % (self.peer_info.filename, self.peer_info.name, self.peer_info.ip, self.peer_info.port, block_to_download, len(data)) zap_log(log_string) else: # Mark the block to be downloaded again. print(("error downloading block %s from" % block_to_download), self.peer_info) self.remote_file.mark_block_as('not-present', int(block_to_download))
def run(self): size = 55000 while True: data, address = self.sock.recvfrom(size) #ignore stuff sent from our own socket if address[0] == ZapConfig.ip and address[1] == self.ignore_port: continue query = ZapTorrentProtocolParser(data) zap_debug_print("Got some data! ", data) zap_debug_print("my address is", (ZapConfig.ip, self.port)) zap_debug_print("other address is", address) zap_debug_print("about to parse the query in FilesLister") query.parse() if query.message_type == "files": # Parse the files out of the query, and store them in the remote files zap_debug_print("got a files reponse: ", data) ip = query.get_field('ip') port = query.get_field('port') name = query.get_field('name') for f in query.get_files(): zf = ZapFile(status="not-present") zf.ip = ip zf.port = port zf.name = name zf.number_of_blocks = f['blocks'] zf.digest = f['digest'] zf.filename = f['filename'] zap_debug_print("Someone told me about a file named", zf.filename) self.remote_files.add(zf)
def parse(self): if self.protocol_matchers['files?'].match(self.data): self.message_type = "files?" elif self.protocol_matchers['files'].match(self.data): match = self.protocol_matchers['files'].match(self.data) self.message_type = 'files' self.fields['ip'] = match.group('ip') self.fields['name'] = match.group('name') self.fields['port'] = match.group('port') self.files_list = [] num_files = match.group('num_files') file_list = match.group('rest') filename_re = re.compile(r"^(?P<filename>[\w\.\-]+) (?P<digest>[\w]+) (?P<blocks>\d+)$") # splitting on new lines when the string ends with a newline results in # an empty string. Check for empty string when looking. for line_number, line in enumerate(file_list.split("\n")): # parse the file fields file_line_match = filename_re.match(line) if file_line_match is None or line_number + 1 > num_files: if line == "": continue else: self.message_type = 'error' self.response = "ZT 1.0 error File lines not correctly formatted.\n" self.files_list.append({ 'filename': file_line_match.group('filename'), 'digest': file_line_match.group('digest'), 'blocks': file_line_match.group('blocks')}) elif self.protocol_matchers['inventory?'].match(self.data): match = self.protocol_matchers['inventory?'].match(self.data) self.message_type = 'inventory?' self.fields['filename'] = match.group('filename') elif self.protocol_matchers['inventory'].match(self.data): match = self.protocol_matchers['inventory'].match(self.data) self.message_type = 'inventory' self.fields['filename'] = match.group('filename') self.fields['blocks'] = match.group('blocks') self.block_list = [] block_list = match.group('rest') zap_debug_print("protocol blocks look like this: %s" % block_list) block_line_re = re.compile(r"^(?P<id>\d+) (?P<bytes>\d+)$") for line_number, line in enumerate(block_list.split('\n')): block_line_match = block_line_re.match(line) if block_line_match is None or line_number + 1 > self.fields['blocks']: if block_line_match == "": continue else: self.message_type = "error" self.response = "ZT 1.0 error Block lines not correctly formatted.\n" break else: self.block_list.append({ 'id': block_line_match.group('id'), 'bytes': block_line_match.group('bytes')}) elif self.protocol_matchers['download?'].match(self.data): match = self.protocol_matchers['download?'].match(self.data) self.message_type = 'download?' self.fields['filename'] = match.group('filename') self.fields['id'] = match.group('id') self.fields['name'] = match.group('name') self.fields['ip'] = match.group('ip') self.fields['port'] = match.group('port') elif self.protocol_matchers['download'].match(self.data): match = self.protocol_matchers['download'].match(self.data) self.message_type = 'download' self.fields['filename'] = match.group('filename') self.fields['id'] = match.group('id') self.fields['bytes'] = match.group('bytes') self.fields['data'] = match.group('data') else: zap_debug_print("Nothing matched", self.data) self.message_type = "error" self.response = "ZT 1.0 error Could not recognize protocol.\n"