def _on_bank_ready(self, result): """Called when the bank has finished starting. Alerts the applications to any startup arguments that were passed in, which will likely cause them to actually start.""" if result != True: log_msg("Bank failed to start correctly!", 0) return result #handle the starting arguments: Startup.handle_args(ProgramState.STARTING_DIR, sys.argv[1:]) GlobalEvents.throw_event("startup") #if this is the first run, save all the settings files: if self.bbApp.isFirstRun: for app in [self.bbApp, self.btApp, self.torApp, self.ffApp]: if app: app.settings.save() self.coreSettings.save() #schedule update function: def do_update(): BWHistory.update_all() return True ProgramState.DO_UPDATES = True self.updateEvent = Scheduler.schedule_repeat( Globals.INTERVAL_BETWEEN_UPDATES, do_update) return result
def set_value(self, val): val = unicode(val) if not Files.file_exists(val): try: os.makedirs(val) except Exception, error: log_msg("Could not make a dir: %s" % (error), 0)
def connectionMade(self): log_msg('Sending login message...', 2) signedFingerprint = Globals.PRIVATE_KEY.sign(Globals.FINGERPRINT) publicKey = Basic.long_to_bytes(long(Globals.PRIVATE_KEY.n), 128) protocol = 1 msg = struct.pack('!B128s50s50s128s', protocol, signedFingerprint, self.factory.username, self.factory.password, publicKey) self.sendString(msg)
def handle_request(self, host, port, data, protocol, transport): log_msg("Got request from %s to send %s to %s" % (host, protocol, port)) #make the reply: msg = self.write_reply(host, data) #and send it back self.send_reply(msg, host, port, protocol, transport)
def handle_setup_reply(self, msg): """Handle a setup reply. Send it to the appropriate PaymentStream, then check if they are all done""" log_msg("circ=%d: PAR setup done." % (self.circ.id), 3, "par") #unpack the messages: forwardParVersion, msg = Basic.read_byte(msg) if forwardParVersion < self.parVersion: self.parVersion = forwardParVersion payStream, msg = self.get_payment_stream(msg) payStream.handle_setup_reply(forwardParVersion, msg) if self.all_setup_done(): initialTokensDeferred = self.add_start_tokens() #this usually happens if the circuit is already closed, if not, an exception will already be logged if not initialTokensDeferred: self.circ.on_done() return def initial_tokens_added(result): self.circ.initialTokensAdded = True self._add_tokens_callback(result, PaymentMessageHandler.START_READ_TOKENS, PaymentMessageHandler.START_WRITE_TOKENS) return result initialTokensDeferred.addCallback(initial_tokens_added) initialTokensDeferred.addErrback(self.generic_error_handler) self.setupDone = True #send any payment requests that are waiting on the setup: reads = self.queuedReadTokens writes = self.queuedWriteTokens self.queuedReadTokens = 0 self.queuedWriteTokens = 0 if self.queuedReadTokens or self.queuedWriteTokens: self.send_payment_request(reads, writes) self.circ.on_par_ready()
def _make_bit_twister_display(self): if len(self.torrents['torrents']) == 0: #no .torrents yet, still display the header height = 1 width = 9 else: height = 2 * len(self.torrents['torrents']) + 1 width = self.torrents['maxLength'] + 1 r, c = self._get_screen_size() if r > self.moniesDisplay[0] + height + 1 and \ c > self.serverDisplay[1] + width + 1: win = self.stdscr.derwin(height, width, r - height, c - width) win.addstr(0, width - 9, 'Torrents', curses.A_BOLD) #add the apps to the gui row = 2 rowSep = 2 for formatedString in self.torrents['torrents']: if len(formatedString) < self.torrents['maxLength']: formatedString = ' ' * (self.torrents['maxLength'] - len( formatedString)) + formatedString win.addstr(row, 0, formatedString) row += rowSep win.refresh() else: log_msg( 'There was not enough screen space (%s, %s) for the torrents display (%s, %s)' % (r, c, height, width), 4) return
def _make_bit_twister_display(self): if len(self.torrents['torrents']) == 0: #no .torrents yet, still display the header height = 1 width = 9 else: height = 2 * len(self.torrents['torrents']) + 1 width = self.torrents['maxLength'] + 1 r, c = self._get_screen_size() if r > self.moniesDisplay[0] + height + 1 and \ c > self.serverDisplay[1] + width + 1: win = self.stdscr.derwin(height, width, r - height, c - width) win.addstr(0, width - 9, 'Torrents', curses.A_BOLD) #add the apps to the gui row = 2 rowSep = 2 for formatedString in self.torrents['torrents']: if len(formatedString) < self.torrents['maxLength']: formatedString = ' '*(self.torrents['maxLength'] - len(formatedString)) + formatedString win.addstr(row, 0, formatedString) row += rowSep win.refresh() else: log_msg('There was not enough screen space (%s, %s) for the torrents display (%s, %s)'% (r ,c , height, width), 4) return
def cleanup(self): """Make sure the reactor, threads, etc have been stopped. Also removes the file that indicates we shutdown cleanly.""" #shutdown Twisted if ProgramState.USE_GTK: Globals.reactor.stop() Globals.reactor.runUntilCurrent() #ensure that all threads have closed: remainingThreads = threading.enumerate() for thread in remainingThreads: if threading._MainThread != type(thread): log_msg("Thread has not finished by the end of the program: %s" % (thread), 1) #start the update if necessary: if Updater.get().APPLY_UPDATE: ClientUtil.apply_update() #NOTE: we intentionally leave the log files open so that errors can get written to them... log_msg("Thanks for using BitBlinder", 2) ErrorReporting.destroy_marker_file() #NOTE: this is here so that threads can finish properly. I was getting errors from leftover threads without it. #However, I'm pretty sure that it was just from the IDE time.sleep(0.2)
def connection_lost(self, protocol): c = self.connections[protocol] log_msg('%s connection closed' % (c.ccount), 3, "btprotocol") del self.connections[protocol] if c.download: c.download.disconnected() self.choker.connection_lost(c)
def start_connections(self, list): peers_added = 0 #add to our current list except duplicates: for peer in list: if self.to_connect.count(peer) == 0: if self.prev_connected.count(peer) == 0: self.never_connected.append(peer) peers_added += 1 else: self.to_connect.append(peer) peers_added += 1 log_msg( "Added %s to our list of peers, now %s long." % (peers_added, len(self.to_connect)), 2, "tracker") #without this, the peers would be each get connected to twice on the very first update if they fail if self.lastPeerCycleTime == 0: self.lastPeerCycleTime = time.time() #for testing: sometimes handy to print out peers so I can make sure we can connect to them later #f = open("peer_list.txt", "wb") #for x in list: # dns, id, encrypted = x # #log_msg("%s %s (%s)" % (encrypted, dns, id)) # f.write("%s:%s\n" % (dns[0], dns[1])) #f.close() #make sure we're starting connections from that list: if not self.startConnectionsEvent: self.startConnectionsEvent = Scheduler.schedule_repeat( 1.0, self._start_connection_from_queue) self._start_connection_from_queue()
def send_partial(self, bytes): if self.protocol.closed: return 0 if self.partial_message is None: s = self.upload.get_upload_chunk() if s is None: return 0 index, begin, piece = s self.lastActive = time.time() self.partial_message = ''.join(( tobinary(len(piece) + 9), PIECE, tobinary(index), tobinary(begin), piece.tostring() )) log_msg('%s sending chunk %s %s %s' % (self.ccount,index,begin,begin+len(piece)), 4, "btprotocol") if bytes < len(self.partial_message): self.protocol.send_message_raw(self.partial_message[:bytes]) self.partial_message = self.partial_message[bytes:] return bytes q = [self.partial_message] self.partial_message = None if self.send_choke_queued: self.send_choke_queued = False self.outqueue.append(tobinary(1)+CHOKE) self.upload.choke_sent() self.just_unchoked = 0 q.extend(self.outqueue) self.outqueue = [] q = ''.join(q) self.protocol.send_message_raw(q) return len(q)
def launch_test_upload(self, widget, event=None): test = None #get the selected item (either a Stream or Circuit) circ = self.getSelected() #if there was no selection or the selection was a stream, let BitBlinder pick the circuit if not circ or circ.__class__.__name__ != "Circuit" or not circ.is_open(): log_msg("Cannot upload through that %s" % (circ), 0) return class TestUploader(Int32StringReceiver): def connectionMade(self): self.bytesLeft = 2 * 1024 * 1024 self.transport.write(struct.pack("!I", self.bytesLeft)) self.sendEvent = Scheduler.schedule_repeat(0.1, self.send_more, 1024 * 10) def send_more(self, numBytes): self.transport.write("1"*numBytes) self.bytesLeft -= numBytes if self.bytesLeft <= 0: return False else: return True def stringReceived(self, data): log_msg("Upload hopefully done?") return factory = protocol.ClientFactory() factory.protocol = TestUploader #connect to the bank and send some trash: d = BitBlinder.get().launch_external_factory(Bank.get().host, Bank.get().port, factory, circ.handle_stream, "Test Upload")
def externally_handshaked_connection_made(self, connection, options, already_read, encrypted = None): shouldClose = False if self.done: shouldClose = True log_msg("Refusing connection because we are shutting down", 4) #TODO: move this earlier in the connection process, or ideally, get Twisted to listen only from localhost when socks is enabled elif self.config['use_socks'] and connection.get_ip() != "127.0.0.1": shouldClose = True log_msg("Refusing connection from outside peer because they attempted to connect directly! ip=%s" % (Basic.clean(ip)), 1) elif self.paused: shouldClose = True log_msg("Refusing connection becase we're paused", 4) elif len(self.connections) >= self.max_connections: shouldClose = True log_msg("Refusing connection becase we have too many already", 4) elif self.check_ip(connection=connection): shouldClose = True log_msg("Refusing connection becase check_ip failed", 4) #ok, is there any reason to close? if shouldClose: connection.close() return False #guess not, add this to the connections: self.connect_succeeded(connection) connection.externalHandshakeDone(self, encrypted, options) #TODO: make sure the data is getting handled properly? ##connection.complete = True #if already_read: # #con.data_came_in(con, already_read) # connection.dataReceived(already_read) return True
def create_error_archive(description): logs = ["main.out", "errors.out", "stderr.out", "tor.out", "tor_conn.out", "tor_conn.out.old", "log.out.old"] zipFile = zipfile.ZipFile(Globals.BUG_REPORT_NAME, "w") MAX_SIZE = 2 * 1024L * 1024L for log in logs: #write the file log with name log logName = os.path.join(Globals.LOG_FOLDER, log) if Files.file_exists(logName): fileSize = os.path.getsize(logName) if fileSize > MAX_SIZE: log_msg("There were %s bytes, too many to include :(" % (fileSize), 0) f = open(logName, "rb") initialData = f.read(MAX_SIZE/2) f.seek(-1 * MAX_SIZE/2, 2) finalData = f.read(MAX_SIZE/2) data = initialData + "\n\n...\n\n" + finalData f.close() zipFile.writestr(log, data + "\nStopping becaue there were %s bytes, too many to include :(" % (fileSize)) else: zipFile.write(logName, log) else: log_msg("Could not find log file: %s" % (logName), 0) description = "%s\n%s\n%s\n%s\nDescription: %s" % (Logging.dump_system_info(), Logging.make_platform_status(), Logging.make_application_status(), Logging.make_program_status(), description) zipFile.writestr("description.out", description) zipFile.close()
def _make_monies_display(self): #need to know our width before we can fit maxLen = 0 for key, stat in self.statistics.iteritems(): #note, this implicitly sets the pretty text statLen = len(stat) if statLen > maxLen: maxLen = statLen totalWidth = maxLen + 15 + 1 r, c = self._get_screen_size() #will it fit in the top right corner (width is variable, though the rows shouldn't change) if r > self.moniesDisplay[0] and \ c > totalWidth + self.statusDisplay[1] + 1: startY = 0 startX = c - totalWidth - 1 win = self.stdscr.derwin(self.moniesDisplay[0], totalWidth + 1, startY, startX) win.clear() #win.border() win.addstr(0, 0, "%sCredits"%((totalWidth - 7)*" "), curses.A_BOLD) row = 2 rowSep = 2 col = 0 #dump things into the window! (should we sort them first?) for key, value in self.statistics.iteritems(): win.addstr(row, col, value.justify_right(totalWidth)) row += rowSep win.refresh() else: log_msg('There was not enough screen space (%s, %s) for the credits display (%s, %s)'% (r, c, self.moniesDisplay[0], totalWidth), 4) return
def _reply(self, code): """Send an appropriately encoded reply to the client""" self.reply_func(Basic.write_byte(1) + Basic.write_byte(code)) if code == RESPONSE_CODES["SUCCESS"]: log_msg("Account %s created for %s" % (self.username, self.ipAddress)) else: log_msg("Account creation attempt failed with code = %s" % (code))
def read_rangelist(self, file): l = [] f = open(file, 'r') while True: line = f.readline() if not line: break line = line.strip() if not line or line[0] == '#': continue line = line.split(':')[-1] try: ip1, ip2 = line.split('-') except: ip1 = line ip2 = line try: ip1 = to_long_ipv4(ip1) ip2 = to_long_ipv4(ip2) assert ip1 <= ip2 except: log_msg('*** WARNING *** could not parse IP range: ' + line, 0) l.append((ip1, ip2)) f.close() self._import_ipv4(l)
def on_update(self): """Called every INTERVAL_BETWEEN_UPDATES to update the various information""" if not self.is_running(): return #TODO: this is a really arbitrary way of keeping things up to date... #keep the list of circuits and streams reasonable: #if there are more than 30 streams/circuits, we remove those that have been done for a minute:: toDelete = [] if len(self.streams) > 30: for streamId, obj in self.streams.iteritems(): if obj.is_done(): age = time.time() - obj.endedAt if age > 60: toDelete.append(streamId) log_msg( "Removing stream=%d from the list because it is old and closed" % (streamId), 4, "circuit") for streamId in toDelete: del self.streams[streamId] toDelete = [] if len(self.circuits) > 30: for obj in self.circuits: if obj.is_done(): age = time.time() - obj.endedAt if age > 60: toDelete.append(obj) log_msg( "Removing circuit=%d from the list because it is old and closed" % (obj.id), 4, "stream") for circ in toDelete: self.circuits.remove(circ)
def unpack_and_mint(self, msg): """unpacks the request retreiving the number of coins packed and the total value desired. Verification: values must be positive! """ self.number, msg = Basic.read_short(msg) value, msg = Basic.read_int(msg) log_msg('REQUEST:: %s %s'%(self.number, self.hexId), 0) if not BankUtil.is_positive_integer(self.number): raise ValueError('number of coins must be greater than 0!') if value != ACOIN_VALUE or not BankUtil.is_positive_integer(value): raise ValueError('coins must have a positive, integer value') self.bill = 0 self.signatures = "" for i in range(0, self.number): #TODO: move to a worker pool or something sig = Globals.ACOIN_KEY.decrypt(msg[:Globals.ACOIN_KEY_BYTES], False) self.signatures += struct.pack('!%ss'%(Globals.ACOIN_KEY_BYTES), sig) msg = msg[Globals.ACOIN_KEY_BYTES:] self.bill += value #TODO: move this constraint to postgres to get rid of any potential race conditions sql = "SELECT balance FROM Accounts WHERE Username = %s" inj = (self.user,) d = db.read(sql, inj) return d
def send_partial(self, bytes): if self.protocol.closed: return 0 if self.partial_message is None: s = self.upload.get_upload_chunk() if s is None: return 0 index, begin, piece = s self.lastActive = time.time() self.partial_message = ''.join( (tobinary(len(piece) + 9), PIECE, tobinary(index), tobinary(begin), piece.tostring())) log_msg( '%s sending chunk %s %s %s' % (self.ccount, index, begin, begin + len(piece)), 4, "btprotocol") if bytes < len(self.partial_message): self.protocol.send_message_raw(self.partial_message[:bytes]) self.partial_message = self.partial_message[bytes:] return bytes q = [self.partial_message] self.partial_message = None if self.send_choke_queued: self.send_choke_queued = False self.outqueue.append(tobinary(1) + CHOKE) self.upload.choke_sent() self.just_unchoked = 0 q.extend(self.outqueue) self.outqueue = [] q = ''.join(q) self.protocol.send_message_raw(q) return len(q)
def handle_setup_reply(self, msg): """Handle a setup reply. Send it to the appropriate PaymentStream, then check if they are all done""" log_msg("circ=%d: PAR setup done." % (self.circ.id), 3, "par") #unpack the messages: forwardParVersion, msg = Basic.read_byte(msg) if forwardParVersion < self.parVersion: self.parVersion = forwardParVersion payStream, msg = self.get_payment_stream(msg) payStream.handle_setup_reply(forwardParVersion, msg) if self.all_setup_done(): initialTokensDeferred = self.add_start_tokens() #this usually happens if the circuit is already closed, if not, an exception will already be logged if not initialTokensDeferred: self.circ.on_done() return def initial_tokens_added(result): self.circ.initialTokensAdded = True self._add_tokens_callback( result, PaymentMessageHandler.START_READ_TOKENS, PaymentMessageHandler.START_WRITE_TOKENS) return result initialTokensDeferred.addCallback(initial_tokens_added) initialTokensDeferred.addErrback(self.generic_error_handler) self.setupDone = True #send any payment requests that are waiting on the setup: reads = self.queuedReadTokens writes = self.queuedWriteTokens self.queuedReadTokens = 0 self.queuedWriteTokens = 0 if self.queuedReadTokens or self.queuedWriteTokens: self.send_payment_request(reads, writes) self.circ.on_par_ready()
def response(result): if result: read, write = result log_msg( "%s paid us %s for exit stream, now %d / %d" % (Basic.clean(self.baseCircuit.prevHexId[:4]), creditsEarned, read, write), 3, "par")
def all_receipts_received(self, results, receiptAction, readTokens, writeTokens, event): """Called when all receipts have been received by each PaymentStream Will add tokens locally and update inflight payments""" #this means that we must have timed out: if not results: log_msg("PAR timed out while waiting for receipts :(", 0) #END_CIRC_REASON_TIMEOUT if not self.circ.is_done(): self.circ.close(10) return #validate that all results were successful for resultTuple in results: if not resultTuple[0]: log_msg( "One of the receipts had a problem, closing circuit: %s" % (str(resultTuple)), 0) #END_CIRC_REASON_REQUESTED if not self.circ.is_done(): self.circ.close(3) return #otherwise, cancel the timeout: if event and event.active(): event.cancel() #just add our tokens: addTokensDeferred = self.add_tokens(readTokens, writeTokens) if not addTokensDeferred: return #and we're done! :) def response(result): if result: receiptAction.callback(result) addTokensDeferred.addCallback(response) addTokensDeferred.addErrback(self.generic_error_handler)
def read_rangelist(self, file): l = [] f = open(file, 'r') while True: line = f.readline() if not line: break line = line.strip() if not line or line[0] == '#': continue line = line.split(':')[-1] try: ip1,ip2 = line.split('-') except: ip1 = line ip2 = line try: ip1 = to_long_ipv4(ip1) ip2 = to_long_ipv4(ip2) assert ip1 <= ip2 except: log_msg('*** WARNING *** could not parse IP range: '+line, 0) l.append((ip1,ip2)) f.close() self._import_ipv4(l)
def add_tokens(self, readTokens, writeTokens): if self.circ.is_done(): log_msg("Not adding tokens because circuit (%s) is closed." % (self.circ.id)) return None return PaymentMessageHandler.PaymentMessageHandler.add_tokens( self, readTokens, writeTokens)
def attach(self, stream): """Attach a stream to this circuit. Callers are responsible for handling failure to attach! @param: the Stream to be attached @returns: True if it succeeded, False otherwise.""" if stream.is_done(): log_msg("Stream (%s) is already done, should not try to attach to Circuit (%s)." % (stream.id, self.id), 0) return False if self.is_done(): log_msg("Circuit (%s) is already done, cannot attach Stream (%s)." % (self.id, stream.id), 0) return False if not self.app.is_tor_ready(): log_msg("Cannot attach stream (%s) to circuit (%s) if Tor is not running!" % (stream.id, self.id), 0) return False #streams can only be attached if the circuit is built if self.status == "BUILT": def failure(error): log_ex(error, "Stream (%s) failed to attach to Circuit (%s)" % (stream.id, self.id), [TorCtl.ErrorReply]) stream.ignoreCircuits.add(self) try: #try attaching the stream d = self.app.torApp.conn.attach_stream(stream.id, self.id) d.addErrback(failure) #and what streams are attached to us self.add_stream(stream) #mark this circuit as dirty if it is not already: if not self.dirtiedAt: self.dirtiedAt = time.time() log_msg("Added stream=%d to circ=%d" % (stream.id, self.id), 4, "stream") except TorCtl.ErrorReply, e: log_msg("Stream (%s) failed to attached to Circuit (%s): %s" % (stream.id, self.id, e), 1, "stream") stream.ignoreCircuits.add(self) return False
def dns_succeeded(address, testAddress=testAddress): log_msg( "DNS for %s successfully resolved to %s" % (testAddress, address), 3) global _isDNSWorking _isDNSWorking = True return True
def start_connections(self, list): peers_added = 0 #add to our current list except duplicates: for peer in list: if self.to_connect.count(peer) == 0: if self.prev_connected.count(peer) == 0: self.never_connected.append(peer) peers_added += 1 else: self.to_connect.append(peer) peers_added += 1 log_msg("Added %s to our list of peers, now %s long." % (peers_added, len(self.to_connect)), 2, "tracker") #without this, the peers would be each get connected to twice on the very first update if they fail if self.lastPeerCycleTime == 0: self.lastPeerCycleTime = time.time() #for testing: sometimes handy to print out peers so I can make sure we can connect to them later #f = open("peer_list.txt", "wb") #for x in list: # dns, id, encrypted = x # #log_msg("%s %s (%s)" % (encrypted, dns, id)) # f.write("%s:%s\n" % (dns[0], dns[1])) #f.close() #make sure we're starting connections from that list: if not self.startConnectionsEvent: self.startConnectionsEvent = Scheduler.schedule_repeat(1.0, self._start_connection_from_queue) self._start_connection_from_queue()
def error(failure): if issubclass(type(failure.value), TorCtl.ErrorReply): log_msg( "Failed to get address from Tor, probably asking too early. Oh well." ) else: log_ex(failure, "Unhandled exception in get_external_ip")
def read_fieldlist( self, file ): # reads a list from a file in the format 'ip/len <whatever>' f = open(file, 'r') while True: line = f.readline() if not line: break line = line.strip().expandtabs() if not line or line[0] == '#': continue try: line, garbage = line.split(' ', 1) except: pass try: line, garbage = line.split('#', 1) except: pass try: ip, depth = line.split('/') except: ip = line depth = None try: if depth is not None: depth = int(depth) self._append(ip, depth) except Exception, e: log_msg('*** WARNING *** could not parse IP range: ' + line, 0)
def set_trackers(self, infohash, trackerList): if infohash not in self.downloads: log_msg( "Cannot set trackers for %s--not downloading that torrent" % (str(infohash).encode("hex")), 0) return self.downloads[infohash].rerequest.set_trackers(trackerList)
def set_start_on_boot(self, newVal): """Change the registry key (if necessary) for auto launching BitBlinder at startup on windows, does nothing on *nix. Ends up calling a function made with NSIS that will get the necessary permissions to do the modification @param newVal: whether to start on boot or not @type newVal: bool""" if not System.IS_WINDOWS: return #No need to change if the value is already correct? if self.check_start_on_boot() == newVal: if self.startOnBootDeferred: log_msg("Failed to modify 'start at bootup' value, already editing the registry!", 0) return if self.startOnBootDeferred: return def uac_done(result): self.startOnBootDeferred = None if result != True: log_ex(result, "Bad result while running BitBlinderSettingsUpdate.exe") #launch the program: if newVal: args = " --add-startup=" + ClientUtil.get_launch_command() else: args = " --remove-startup" encodedExe = System.encode_for_filesystem(os.path.join(Globals.WINDOWS_BIN, "BitBlinderSettingsUpdate.exe")) self.startOnBootDeferred = SerialProcessLauncher.get().run_app(encodedExe + " /S" + args) self.startOnBootDeferred.addCallback(uac_done) self.startOnBootDeferred.addErrback(uac_done)
def died(self, hash): if hash in self.torrent_cache: log_msg( 'DIED: "' + Basic.clean(self.torrent_cache[hash]['path']) + '"', 1) else: log_msg('DIED: "' + Basic.clean(hash) + '"', 1)
def _on_bank_ready(self, result): """Called when the bank has finished starting. Alerts the applications to any startup arguments that were passed in, which will likely cause them to actually start.""" if result != True: log_msg("Bank failed to start correctly!", 0) return result #handle the starting arguments: Startup.handle_args(ProgramState.STARTING_DIR, sys.argv[1:]) GlobalEvents.throw_event("startup") #if this is the first run, save all the settings files: if self.bbApp.isFirstRun: for app in [self.bbApp, self.btApp, self.torApp, self.ffApp]: if app: app.settings.save() self.coreSettings.save() #schedule update function: def do_update(): BWHistory.update_all() return True ProgramState.DO_UPDATES = True self.updateEvent = Scheduler.schedule_repeat(Globals.INTERVAL_BETWEEN_UPDATES, do_update) return result
def stringReceived(self, data): self.responseReceived = True self.factory.gotResponse = True data = self.factory.bank.decrypt_message(data) log_msg("ACoin DEPOSIT response received.", 4) (newBalance, interval, expiresCurrent, expiresNext), returnSlip = Basic.read_message('!IIII', data) self.factory.bank.on_new_info(newBalance, interval, expiresCurrent, expiresNext) returnSlip = list(struct.unpack('c'*len(self.factory.coins), returnSlip)) for i in range(0, len(self.factory.coins)): coin = self.factory.coins[i] status = returnSlip[i] gotAcceptableResponse = True badACoin = True if status == "0": badACoin = False elif status == "3": log_msg("ACoin deposit failed. Some node must have double-spent.", 1) elif status == "2": log_msg("ACoin deposit failed. You apparently already sent this acoin.", 1) elif status == "1": log_msg("ACoin deposit failed. ACoin was not valid? %s" % (repr(coin.write_binary())), 0) else: log_msg("Bank returned unknown status message: %s" % (status), 0) gotAcceptableResponse = False if badACoin: #close the circuit, they're trying to cheat us! if coin.originCircuit: coin.originCircuit.close() self.transport.loseConnection()
def read_fieldlist(self, file): # reads a list from a file in the format 'ip/len <whatever>' f = open(file, 'r') while True: line = f.readline() if not line: break line = line.strip().expandtabs() if not line or line[0] == '#': continue try: line, garbage = line.split(' ',1) except: pass try: line, garbage = line.split('#',1) except: pass try: ip, depth = line.split('/') except: ip = line depth = None try: if depth is not None: depth = int(depth) self._append(ip,depth) except Exception, e: log_msg('*** WARNING *** could not parse IP range: '+line, 0)
def stringReceived(self, encryptedMsg): self.responseReceived = True self.transport.loseConnection() blob = self.factory.bank.decrypt_message(encryptedMsg) log_msg("ACoin REQUEST response received.", 4) responseCode, blob = Basic.read_byte(blob) #we had enough credits in our account if responseCode == 0: (newBalance, number), coins = Basic.read_message('!II', blob) #update the balance self.factory.bank.on_new_balance_from_bank(newBalance) acoinStrFormat = "%ss" % (Globals.ACOIN_KEY_BYTES) format = '!' + (acoinStrFormat * number) sigs = list(struct.unpack(format, coins)) while len(self.factory.requests) > 0: request = self.factory.requests.pop(0) sig = sigs.pop(0) coin = BankMessages.parse_acoin_response(self.factory.bank, sig, request, ProgramState.DEBUG) if coin: self.factory.bank.add_acoin(coin) else: log_msg("Got an invalid ACoin from the bank!?", 3) #the bank could not check out the coins because our account doesn't have enough credits! else: (newBalance,), blob = Basic.read_message('!I', blob) self.factory.bank.on_new_balance_from_bank(newBalance)
def all_receipts_received(self, results, receiptAction, readTokens, writeTokens, event): """Called when all receipts have been received by each PaymentStream Will add tokens locally and update inflight payments""" #this means that we must have timed out: if not results: log_msg("PAR timed out while waiting for receipts :(", 0) #END_CIRC_REASON_TIMEOUT if not self.circ.is_done(): self.circ.close(10) return #validate that all results were successful for resultTuple in results: if not resultTuple[0]: log_msg("One of the receipts had a problem, closing circuit: %s" % (str(resultTuple)), 0) #END_CIRC_REASON_REQUESTED if not self.circ.is_done(): self.circ.close(3) return #otherwise, cancel the timeout: if event and event.active(): event.cancel() #just add our tokens: addTokensDeferred = self.add_tokens(readTokens, writeTokens) if not addTokensDeferred: return #and we're done! :) def response(result): if result: receiptAction.callback(result) addTokensDeferred.addCallback(response) addTokensDeferred.addErrback(self.generic_error_handler)
def _update_events(self): earliestTime = get_current_gmtime() curTime = time.time() for logTypeName, remoteFolder in self.serverList.iteritems(): try: log_msg("Updating logs from %s..." % (logTypeName)) dataDir = os.path.join(self.baseDataDir, logTypeName) if not os.path.exists(dataDir): os.makedirs(dataDir) #get the changes from the remote server: os.system('rsync --append -rtz -e "ssh -i %s" %s/ %s' % (IDENTITY, remoteFolder, dataDir)) #for each file in the folder fileNames = glob.glob(dataDir + "/*") for fileName in fileNames: baseFileName = os.path.split(fileName)[1] #ignore really old files: if self._file_is_old(fileName, curTime): self._remove_file(fileName, baseFileName, logTypeName) continue #look up the database row results = self._get_file_row(baseFileName, logTypeName) #if the row existed, figure out if it is old enough to be deleted if len(results) > 0: assert len(results) == 1, "Why are there two rows for %s and %s?" % (baseFileName, logTypeName) numEvents, lastMTimeString = results[0] rowExisted = True if not self._file_was_modified(fileName, lastMTimeString): #don't bother continuing to parse the file if it hasnt been modified continue #otherwise, just note that we've obviously never parsed any events from this file else: numEvents = 0 rowExisted = False #load all lines cur = self.conn.cursor() try: startTime, newNumEvents = EventLogging.parse_events(cur, fileName, numEvents) if startTime < earliestTime: earliestTime = startTime log_msg("Parsed %s events" % (newNumEvents-numEvents)) #if any line fails, abort everything and log the failure except Exception, error: self._log_failure(error, "Failure (%s) while processing line from %s" % (error, fileName)) #otherwise update the file row in the database to note that we've successfully parsed newNumEvents events else: newMTimeString = str(os.path.getmtime(fileName)) if rowExisted: self._update_file_row(cur, baseFileName, logTypeName, newNumEvents, newMTimeString) else: self._insert_file_row(cur, baseFileName, logTypeName, newNumEvents, newMTimeString) finally: cur.close() self.conn.commit()
def datagramReceived(self, datagram, address): if self.finished: return try: self.read_reply(datagram, address[0]) except EchoMixin.BadEchoMessageFormat, e: log_msg("Got a bad UDP message (%s) while testing ports: %s" % (e, repr(datagram)), 4)
def message_arrived(self, msg): """Called when a payment message is received via the controller. Is responsible for piecing it back together into the actual message. @param msg: the data received from Tor @type msg: str""" self.buffer += msg # is the whole message here? msgLen, msgData = Basic.read_short(self.buffer) if len(msgData) >= msgLen: msgData = msgData[:msgLen] # we just discard the rest of the cell, two messages are never packed in the same cell currently self.buffer = "" # what type of message is this? msgType, msgData = Basic.read_byte(msgData) # ok, now handle that message: for msgName in MESSAGE_CODES.keys(): if msgType == MESSAGE_CODES[msgName]: # if we don't know how to handle this message, just close the circuit if msgName not in self.messageHandlers: log_msg("Remote request for %s, which we do not know how to handle" % (msgName), 1) self.close() return # get the handler: handler = self.messageHandlers[msgName] # get the handler function: funcName = "handle_%s" % (msgName) if not hasattr(handler, funcName): raise Exception("%s cannot handle %s payment message?" % (handler, msgName)) f = getattr(handler, funcName) f(msgData) return # uhh, not sure how to handle this message: raise Exception("Unknown message type for payment message: %s" % (msgType))
def _make_monies_display(self): #need to know our width before we can fit maxLen = 0 for key, stat in self.statistics.iteritems(): #note, this implicitly sets the pretty text statLen = len(stat) if statLen > maxLen: maxLen = statLen totalWidth = maxLen + 15 + 1 r, c = self._get_screen_size() #will it fit in the top right corner (width is variable, though the rows shouldn't change) if r > self.moniesDisplay[0] and \ c > totalWidth + self.statusDisplay[1] + 1: startY = 0 startX = c - totalWidth - 1 win = self.stdscr.derwin(self.moniesDisplay[0], totalWidth + 1, startY, startX) win.clear() #win.border() win.addstr(0, 0, "%sCredits" % ((totalWidth - 7) * " "), curses.A_BOLD) row = 2 rowSep = 2 col = 0 #dump things into the window! (should we sort them first?) for key, value in self.statistics.iteritems(): win.addstr(row, col, value.justify_right(totalWidth)) row += rowSep win.refresh() else: log_msg( 'There was not enough screen space (%s, %s) for the credits display (%s, %s)' % (r, c, self.moniesDisplay[0], totalWidth), 4) return
def find_and_bind(self, minport, maxport, bind='', reuse=False, ipv6_socket_style=1, upnp=0, randomizer=False): self.factory = IncomingBTFactory() e = 'maxport less than minport - no ports to check' if maxport - minport < 50 or not randomizer: portrange = range(minport, maxport + 1) if randomizer: shuffle(portrange) portrange = portrange[:20] # check a maximum of 20 ports else: portrange = [] while len(portrange) < 20: listen_port = randrange(minport, maxport + 1) if not listen_port in portrange: portrange.append(listen_port) for listen_port in portrange: try: self.bind(listen_port, bind, ipv6_socket_style=ipv6_socket_style, upnp=upnp) return listen_port except CannotListenError, e: log_msg( "Could not bind %s to listen for BT server" % (listen_port), 3) pass
def failure(error): if error and hasattr(error, "value") and issubclass( type(error.value), TimeoutError): #TODO: this indicates that the bank is down, or something is wrong with their network? log_msg("Relayed payment timed out :(", 0, "par") else: log_ex(error, "Relaying payment message failed!")
def deposit_cb(self, widget, event=None): """Attempts to deposit all coins- for debugging only""" coins = Bank.get().get_acoins(Bank.get().get_wallet_balance()) if not coins: log_msg("No ACoins left!") return Bank.get().deposit_acoins(coins)
def on_update(self): """Called every INTERVAL_BETWEEN_UPDATES to update the various information""" if not self.is_running(): return #TODO: this is a really arbitrary way of keeping things up to date... #keep the list of circuits and streams reasonable: #if there are more than 30 streams/circuits, we remove those that have been done for a minute:: toDelete = [] if len(self.streams) > 30: for streamId, obj in self.streams.iteritems(): if obj.is_done(): age = time.time() - obj.endedAt if age > 60: toDelete.append(streamId) log_msg("Removing stream=%d from the list because it is old and closed" % (streamId), 4, "circuit") for streamId in toDelete: del self.streams[streamId] toDelete = [] if len(self.circuits) > 30: for obj in self.circuits: if obj.is_done(): age = time.time() - obj.endedAt if age > 60: toDelete.append(obj) log_msg("Removing circuit=%d from the list because it is old and closed" % (obj.id), 4, "stream") for circ in toDelete: self.circuits.remove(circ)
def create_circuit(self, path, ignoreLogin=False): """Create a new Circuit for this Application that will travel over path. @param path: the path that the circuit will follow when fully constructed @type path: a list of Routers @return: the newly created circuit, or None if we cannot create a Circuit right now.""" #can only create circuits if we are logged in: if not self.bankApp.is_ready() and not ignoreLogin: log_msg("Not creating the circuit because we arent logged in yet!", 1, "circuit") return None if not self.torApp.is_running( ) or not self.torApp.conn or self.torApp.conn._closed: log_msg("Not creating the circuit because Tor is not ready", 0, "circuit") return None #create the new circuit ids = [] circ = None for relay in path: ids.append(relay.desc.idhex) circ = Circuit.Circuit(None, self, -1, path) #add to list of circuits self.circuits.add(circ) #add to list of live circuits: self.liveCircuits.add(circ) #tell tor to make the circuit torDeferred = self.torApp.conn.extend_circuit(0, ids) torDeferred.addCallback(self._on_circuit_creation_success, circ) torDeferred.addErrback(self._on_circuit_creation_failure, circ) return circ
def get_default_gateway(): #TODO: execing route print is probably a better idea here try: strComputer = "." objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator") objSWbemServices = objWMIService.ConnectServer(strComputer, "root\cimv2") colItems = objSWbemServices.ExecQuery( "Select * from Win32_NetworkAdapterConfiguration") gateways = [] for objItem in colItems: z = objItem.DefaultIPGateway if z: for x in z: gateways.append(x) if len(gateways) > 1: log_msg( "Why are there multiple gateways? :( %s" % (Basic.clean(gateways)), 2) elif len(gateways) == 0: return None return gateways.pop(0) except Exception, e: log_ex(e, "Failed to get default gateway") return "192.168.1.1"
def on_response(self, dialog, response_id): if (response_id == gtk.RESPONSE_YES): self.cb(self.newVersion) else: log_msg("User elected not to update.", 4) GUIController.get().updateDialog = None self.dia.destroy()
def error(failure): if not self.is_done(): if Basic.exception_is_a(failure, [TorCtl.TorCtlClosed, TorCtl.ErrorReply]): log_msg("Failed to create PAR client, closing", 1, "circuit") else: log_ex(failure, "Unexpected failure while starting circuit") self.on_done()
def start_payment_loop(self): coin = Bank.get().get_acoins(1) if not coin: log_msg("No ACoins left!") return coin = coin[0] #generate ACoin request request = BankMessages.make_acoin_request(Bank.get(), Bank.get().currentACoinInterval, 1) #make the message: bankMsg = Basic.write_byte(1) bankMsg += coin.write_binary() + request.msg key = EncryptedDatagram.ClientSymKey(Bank.get().PUBLIC_KEY) bankMsg = Basic.write_byte(1) + key.encrypt(Basic.write_byte(3) + bankMsg) payment = UDPPayment.UDPPayment(Bank.get(), bankMsg) paymentDeferred = payment.get_deferred() def success(result, request=request): log_msg("success") self.payments += 1 # self.start_payment_loop() #validate the ACoin code, sig = Basic.read_byte(result) coin = BankMessages.parse_acoin_response(Bank.get(), sig, request, False) if not coin: log_msg("Invalid ACoin sent for payment!") else: Bank.get().on_earned_coin(coin) paymentDeferred.addCallback(success) def failure(error): self.start_payment_loop() log_ex(error, "Failure during test?") self.failures += 1 paymentDeferred.addErrback(failure)
def _load_private_key(self): """Load (and generate if necessary) the Tor public and private keys""" log_msg("Loading private key...", 3) try: torKey = os.path.join(Globals.USER_DATA_DIR, "tor_data", "keys", "secret_id_key") if not Files.file_exists(torKey): #have to create it ourselves. First make the folders if necessary: keyFolder = os.path.join(Globals.USER_DATA_DIR, "tor_data", "keys") if not Files.file_exists(keyFolder): os.makedirs(keyFolder) #then generate the key Globals.PRIVATE_KEY = PrivateKey.PrivateKey(1024) #and save it in the appropriate format if not Globals.PRIVATE_KEY.key.save_key(torKey, cipher=None): raise Exception("Failed to save key as PEM!") else: Globals.PRIVATE_KEY = PrivateKey.PrivateKey(torKey) Globals.PUBLIC_KEY = Globals.PRIVATE_KEY.publickey() Globals.FINGERPRINT = TorUtils.fingerprint(Globals.PRIVATE_KEY.n, Globals.PRIVATE_KEY.e) log_msg("Globals.FINGERPRINT = %s" % (Globals.FINGERPRINT), 3) except Exception, error: log_ex(error, "Failed while loading private key data")
def cleanup(self): """Make sure the reactor, threads, etc have been stopped. Also removes the file that indicates we shutdown cleanly.""" #shutdown Twisted if ProgramState.USE_GTK: Globals.reactor.stop() Globals.reactor.runUntilCurrent() #ensure that all threads have closed: remainingThreads = threading.enumerate() for thread in remainingThreads: if threading._MainThread != type(thread): log_msg( "Thread has not finished by the end of the program: %s" % (thread), 1) #start the update if necessary: if Updater.get().APPLY_UPDATE: ClientUtil.apply_update() #NOTE: we intentionally leave the log files open so that errors can get written to them... log_msg("Thanks for using BitBlinder", 2) ErrorReporting.destroy_marker_file() #NOTE: this is here so that threads can finish properly. I was getting errors from leftover threads without it. #However, I'm pretty sure that it was just from the IDE time.sleep(0.2)
def reply(self, msg): """returns string msg to client""" msg = str(msg) self.sendString(msg) log_msg('msg returned to client', 3) self.drop_connection() return