def handle_bank_relay(self, msg): """Send a message to the bank on behalf of someone else, then send the reply onward @param msg: the message to relay @type msg: str""" version, msg = Basic.read_byte(msg) assert version == 1, "only accepts version 1 of PAR protocol" responseHop, msg = Basic.read_byte(msg) bankMsg, msg = Basic.read_lenstr(msg) responseMsg, msg = Basic.read_lenstr(msg) payment = UDPPayment.UDPPayment(self.bank, bankMsg) paymentDeferred = payment.get_deferred() def success(response, responseMsg=responseMsg, responseHop=responseHop): responseMsg += response if responseHop != 0: self.send_direct_tor_message(responseMsg, "payment", True, responseHop) else: self.handle_payment(responseMsg) paymentDeferred.addCallback(success) 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!") paymentDeferred.addErrback(failure)
def stringReceived(self, data): """Called when the login response arrives""" self.factory.done = True self.responseReceived = True text = None try: protocol, data = Basic.read_byte(data) if protocol == 1: returnCode, data = Basic.read_byte(data) if returnCode == 1: format = '!IIII4sI' (balance, currentACoinInterval, currentACoinIntervalExpiration, nextAcoinIntervalExpiration, host, port), blob = Basic.read_message(format, data) size = struct.calcsize(SymmetricKey.MESSAGE_FORMAT) authBlob = blob[:size] text = blob[size:] host = socket.inet_ntoa(host) self.factory.bank.on_new_info( balance, currentACoinInterval, currentACoinIntervalExpiration, nextAcoinIntervalExpiration) self.factory.bank.on_login_success(balance, authBlob, host, port, text) else: size = struct.calcsize('!I') timeout = struct.unpack('!I', data[:size])[0] text = data[size:] raise BadLoginPasswordError(timeout) else: raise Exception('unknown protocol: %s' % (protocol)) except Exception, error: self.factory.bank.on_login_failure(error, text)
def datagramReceived(self, datagram, address): try: self.address = address log_msg('Datagram received from %s:%s!'%address, 3) msgType, msg = Basic.read_byte(datagram) #for compatability really if msgType == 1: #is it a replay? if not cache.has_key(msg): self.request = msg self.symKey = EncryptedDatagram.ServerSymKey(Globals.GENERIC_KEY) msg = self.symKey.decrypt(msg) #for compatability really request_type, msg = Basic.read_byte(msg) if request_type == 3: self.handler = ACoinMessages.Payment(self.reply, self.address) self.handler.on_message(msg) else: raise Exception("Unknown request_type: %s" % (request_type)) else: self.reply(cache[msg], putInQueue=False) return else: raise Exception("Unknown msgType: %s" % (msgType)) except Exception, e: self.err(e)
def stringReceived(self, data): """Called when the login response arrives""" self.factory.done = True self.responseReceived = True text = None try: protocol, data = Basic.read_byte(data) if protocol == 1: returnCode, data = Basic.read_byte(data) if returnCode == 1: format = '!IIII4sI' (balance, currentACoinInterval, currentACoinIntervalExpiration, nextAcoinIntervalExpiration, host, port), blob = Basic.read_message(format, data) size = struct.calcsize(SymmetricKey.MESSAGE_FORMAT) authBlob = blob[:size] text = blob[size:] host = socket.inet_ntoa(host) self.factory.bank.on_new_info(balance, currentACoinInterval, currentACoinIntervalExpiration, nextAcoinIntervalExpiration) self.factory.bank.on_login_success(balance, authBlob, host, port, text) else: size = struct.calcsize('!I') timeout = struct.unpack('!I', data[:size])[0] text = data[size:] raise BadLoginPasswordError(timeout) else: raise Exception('unknown protocol: %s'%(protocol)) except Exception, error: self.factory.bank.on_login_failure(error, text)
def handle_bank_relay(self, msg): """Send a message to the bank on behalf of someone else, then send the reply onward @param msg: the message to relay @type msg: str""" version, msg = Basic.read_byte(msg) assert version == 1, "only accepts version 1 of PAR protocol" responseHop, msg = Basic.read_byte(msg) bankMsg, msg = Basic.read_lenstr(msg) responseMsg, msg = Basic.read_lenstr(msg) payment = UDPPayment.UDPPayment(self.bank, bankMsg) paymentDeferred = payment.get_deferred() def success(response, responseMsg=responseMsg, responseHop=responseHop): responseMsg += response if responseHop != 0: self.send_direct_tor_message(responseMsg, "payment", True, responseHop) else: self.handle_payment(responseMsg) paymentDeferred.addCallback(success) 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!") paymentDeferred.addErrback(failure)
def datagramReceived(self, datagram, address): try: self.address = address log_msg('Datagram received from %s:%s!' % address, 3) msgType, msg = Basic.read_byte(datagram) #for compatability really if msgType == 1: #is it a replay? if not cache.has_key(msg): self.request = msg self.symKey = EncryptedDatagram.ServerSymKey( Globals.GENERIC_KEY) msg = self.symKey.decrypt(msg) #for compatability really request_type, msg = Basic.read_byte(msg) if request_type == 3: self.handler = ACoinMessages.Payment( self.reply, self.address) self.handler.on_message(msg) else: raise Exception("Unknown request_type: %s" % (request_type)) else: self.reply(cache[msg], putInQueue=False) return else: raise Exception("Unknown msgType: %s" % (msgType)) except Exception, e: self.err(e)
def _read_header(self, data, msgName): #read the version version, data = Basic.read_byte(data) assert version == self.VERSION, "Bad version number: %s" % (version) #read the message type: msgType, data = Basic.read_byte(data) assert msgType == self.MESSAGES[msgName], "Bad message type: %s" % (msgType) return data
def _read_header(self, data, msgName): #read the version version, data = Basic.read_byte(data) assert version == self.VERSION, "Bad version number: %s" % (version) #read the message type: msgType, data = Basic.read_byte(data) assert msgType == self.MESSAGES[msgName], "Bad message type: %s" % ( msgType) return data
def handle_dht_request(self, data): log_msg("Got remote DHT request", 4, "dht") #unpack and validate the message: version, data = Basic.read_byte(data) assert version == Node.VERSION #read the infohash: vals, data = Basic.read_message("20s", data) infohash = vals[0] #read each peer: peers = set() while len(data) > 0: #what type of peer? (ip or url) peerType, data = Basic.read_byte(data) #IP peer: if peerType == 0: vals, data = Basic.read_message("!4sH", data) host = socket.inet_ntoa(vals[0]) port = vals[1] #URL peer: elif peerType == 1: host, data = Basic.read_lenstr(data) port, data = Basic.read_short(data) #bad peer type: else: raise Exception("Unknown peer address type: %s" % (peerType)) peers.add((host, port)) #note that there is a new transaction: transactionId = self.currentTransactionId self.responses[transactionId] = "" self.currentTransactionId += 1 #now add each peer: for host, port in peers: #make sure it's not one of our defaults #TODO: in the future, make sure we don't already know about it anyway? Eh, maybe that will break DHT somehow? if (host, port) not in Node.BOOTSTRAP_NODES: log_msg("Neat, someone told us about a new DHT node", 2) self.dhtNode.add_contact(host, port) #and then send out the request: def response(data, transactionId=transactionId): #is this the last message? if len(data) <= 0: #then send the response for this transaction: self._send_peers(transactionId) #otherwise, just accumulate the data for later: else: self.responses[transactionId] += "".join(data[0]) self.dhtNode.get_peers(infohash, response)
def on_login_failure(self, bankApp, err, optionalServerResponse=None): text = None self.dia.window.raise_() eType = type(err) if eType is BadLoginPasswordError: text = str(err) elif eType is Failure: eType = type(err.value) if issubclass(eType, ConnectionDone) or issubclass( eType, ConnectionLost): text = "The login server is temporarily offline. We are sorry for the inconvenience. Please try again later." if not text: text = "Login failed for an unknown reason. Please try again." text += "\nNOTE: You must restart the program to change users." if optionalServerResponse: force, optionalServerResponse = Basic.read_byte( optionalServerResponse) #concatenate if force is 0: text += '\n' + optionalServerResponse #nuke it else: text = optionalServerResponse self.label.set_text(text) self.nameEntry.set_sensitive(True) self.pwEntry.set_sensitive(True) self.loginButton.set_sensitive(True) self.quitButton.set_sensitive(True)
def sym_decrypt(self, msg): """this is disgusting because we use two different systems for symmetric encryption type 0 uses SmmetricKey while type 1 uses EncryptedDatagram quirks: 0 must also update the nonce stored in the db 1 performs heavy crypto and has non symmetric client/server side encryption/decryption""" if not self.symKey: msgType, msg = Basic.read_byte(msg) if msgType is 0: #get the tor fingerprint so we know how to decrypt the message (binId,), msg = Basic.read_message('!20s', msg) #convert the tor fingerprint back into hex self.hexId = binascii.hexlify(binId).upper() #get the symmetric key out of the database: sql = "SELECT Owner, Public_Key, Msgnum, auth_blob FROM Relays WHERE Tor_Id = %s" inj = (self.hexId,) d = db.read(sql, inj) #get the sym key from the db and decrypt the msg d.addCallback(self.fetch_sym_key, msg) #update the message number in the database d.addCallback(self.update_db) elif msgType is 1: self.symKey = EncryptedDatagram.ServerSymKey(Globals.GENERIC_KEY) d = threads.deferToThread(self.get_sym_key_value, msg) else: raise Exception("Unknown msgType: %s" % (msgType)) else: raise Exception('Passing more than one message per tcp connection is currently not supported') return d
def on_login_failure(self, bankApp, err, optionalServerResponse=None): text = None self.dia.window.raise_() eType = type(err) if eType is BadLoginPasswordError: text = str(err) elif eType is Failure: eType = type(err.value) if issubclass(eType, ConnectionDone) or issubclass(eType, ConnectionLost): text = "The login server is temporarily offline. We are sorry for the inconvenience. Please try again later." if not text: text = "Login failed for an unknown reason. Please try again." text += "\nNOTE: You must restart the program to change users." if optionalServerResponse: force, optionalServerResponse = Basic.read_byte(optionalServerResponse) #concatenate if force is 0: text += '\n'+optionalServerResponse #nuke it else: text = optionalServerResponse self.label.set_text(text) self.nameEntry.set_sensitive(True) self.pwEntry.set_sensitive(True) self.loginButton.set_sensitive(True) self.quitButton.set_sensitive(True)
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 sym_decrypt(self, msg): """this is disgusting because we use two different systems for symmetric encryption type 0 uses SmmetricKey while type 1 uses EncryptedDatagram quirks: 0 must also update the nonce stored in the db 1 performs heavy crypto and has non symmetric client/server side encryption/decryption""" if not self.symKey: msgType, msg = Basic.read_byte(msg) if msgType is 0: #get the tor fingerprint so we know how to decrypt the message (binId, ), msg = Basic.read_message('!20s', msg) #convert the tor fingerprint back into hex self.hexId = binascii.hexlify(binId).upper() #get the symmetric key out of the database: sql = "SELECT Owner, Public_Key, Msgnum, auth_blob FROM Relays WHERE Tor_Id = %s" inj = (self.hexId, ) d = db.read(sql, inj) #get the sym key from the db and decrypt the msg d.addCallback(self.fetch_sym_key, msg) #update the message number in the database d.addCallback(self.update_db) elif msgType is 1: self.symKey = EncryptedDatagram.ServerSymKey( Globals.GENERIC_KEY) d = threads.deferToThread(self.get_sym_key_value, msg) else: raise Exception("Unknown msgType: %s" % (msgType)) else: raise Exception( 'Passing more than one message per tcp connection is currently not supported' ) return d
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 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 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 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 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)
def read_request(self, data, host, transport): try: #read the header: data = self._read_header(data, "request") #read the protocol type: protocolType, data = Basic.read_byte(data) assert protocolType in self.TEST_TYPES, "Unknown echo protocol: %s" % (protocolType) protocol = self.TEST_TYPES[protocolType] #read the port: port, data = Basic.read_short(data) except AssertionError, error: raise BadEchoMessageFormat(str(error))
def read_request(self, data, host, transport): try: #read the header: data = self._read_header(data, "request") #read the protocol type: protocolType, data = Basic.read_byte(data) assert protocolType in self.TEST_TYPES, "Unknown echo protocol: %s" % ( protocolType) protocol = self.TEST_TYPES[protocolType] #read the port: port, data = Basic.read_short(data) except AssertionError, error: raise BadEchoMessageFormat(str(error))
def on_login_success(self, balance, authBlob, host, port, text): """Called when we successfully log in to the bank server, passed the response message @param balance: how many credits we have at the bank @type balance: int @param authBlob: the symmetric key to use for subsequent messages @type authBlob: str @param host: where to send subsequent messages @type host: str (ip addr) @param port: where to send subsequent messages @type port: int (port) @param text: a status code and message from the bank to possibly show the user. @type text: str""" if not self.startupDeferred: return log_msg("login was successful: \nServer says: %s" % (text), 4) if text: #If the code is non-zero, display the text code, text = Basic.read_byte(text) if code == 0: text = None self.loginInProgress = False #figure out the shared symmetric key from the bank (for use encrypting all later messages) self.secretKey = SymmetricKey.SymmetricKey(authBlob) self.host = host self.port = port if not self.isLoggedIn: self.isLoggedIn = True #determine some file names based on the username: self.ACOIN_FILE_NAME = os.path.join(Globals.USER_DATA_DIR, "acoins.txt") self.DEPOSITED_FILE_NAME = os.path.join(Globals.USER_DATA_DIR, "acoins_in_deposit.txt") #inform the gui that we've logged in: self._trigger_event("login_success", text) #load any coins we stored when we shut down previously: self.load_coins() log_msg("Bank login successful!", 3) #make sure we have enough ACoins: self.check_wallet_balance() #let other parts of the program react to the fact that we just logged in GlobalEvents.throw_event("login") #notify anyone waiting on the startup deferred: self.startupDeferred.callback(True) self.startupDeferred = None self._trigger_event("started") else: log_ex("already logged in", "The bank should not be started more than once")
def handle_message(self, msg): request_type, msg = Basic.read_byte(msg) #attach the correct handler if request_type == 1: self.handler = ACoinMessages.Request(self.encrypted_reply, self.owner, self.hexId) elif request_type == 2: self.handler = ACoinMessages.Deposit(self.encrypted_reply, self.owner, self.hexId) elif request_type == 3: self.owner = self.transport.getPeer() self.handler = ACoinMessages.Payment(self.reply, self.owner) else: log_msg('invalid request: %s, %s' % (request_type, msg), 1) self.reply('invalid request: %s' % (request_type)) #self.drop_connection() return self.handler.on_message(msg)
def stringReceived(self, data): self.successful = True #we don't have any errbacks attached yet- an error will just make the client hang indefinately try: protocol, blob = Basic.read_byte(data) if protocol == 1: self.hexIdSig, self.username, self.password, self.n = self.unpack(blob) d = self.check_for_timeout() d.addCallback(self.login_timeout_known) d.addErrback(self.err) elif protocol == 2: ipAddress = self.transport.getPeer().host self.currentAction = AccountCreation.AccountCreation(blob, ipAddress, db, self.reply) else: raise Exception('unknown login protocol: %s' % (protocol)) except Exception, e: self.err(e)
def update_db(self, blob): """utility function that updates verifies the nonce in the msg and then updates the nonce in the db""" protocol, blob = Basic.read_byte(blob) if protocol is not 1: raise Exception('change protocol') msgNum, blob = Basic.read_short(blob) #the msgNum is a nonce to prevent replay attacks- #the client always increases it by one, we just check that it is bigger if msgNum > self.previousMsgnum: #update the msgnum in the db to be this msgnum of course - #not generally threadsafe sql = "UPDATE Relays SET Msgnum = %s WHERE tor_id = %s" inj = (msgNum, self.hexId) d = db.write(sql, inj) else: raise Exception('replay attack or something') return blob
def read_payment_tokens(self, msg): """Read merchant payment tokens from a message""" #read the number of payment requests: numTokens, msg = Basic.read_byte(msg) #determine the size of the tokens: assert len(msg) % numTokens == 0, "bad payment token message" tokenSize = len(msg) / numTokens #read each of the payment requests: while msg: token, msg = msg[:tokenSize], msg[tokenSize:] requestId, token, tokenSig = struct.unpack("!L%ss%ss" % (Globals.ACOIN_KEY_BYTES, Globals.TOR_ID_KEY_BYTES), token) if not self.key.verify(token, tokenSig): #END_CIRC_REASON_TORPROTOCOL if not self.parClient.circ.is_done(): log_ex("Signature invalid", "Error unpacking payment tokens") self.parClient.circ.close(1) return False self.paymentTokens[requestId] = token return True
def on_message(self, msg): log_msg('PAYMENT', 0) current = Globals.CURRENT_ACOIN_INTERVAL[0] #read the number of payments: numPayments, msg = Basic.read_byte(msg) reply = "" for i in range(0, numPayments): #read the payment: result, coin, msg = deposit_acoin(msg, current) reply += struct.pack('!s', result) token, msg = msg[:Globals.ACOIN_KEY_BYTES], msg[Globals.ACOIN_KEY_BYTES:] #if the coin is valid... if result == '0': sig = Globals.ACOIN_KEY.decrypt(token, False) #~ print "token: " + repr(token) #~ print "sig: " + repr(sig) reply += struct.pack('!%ss' % (Globals.ACOIN_KEY_BYTES), sig) #and finally, send it back to the client: self.send_func(reply) #log the event: eventLogger.aggregate_event("PAYMENTS", self.address[0], 1)
def on_message(self, msg): log_msg('PAYMENT', 0) current = Globals.CURRENT_ACOIN_INTERVAL[0] #read the number of payments: numPayments, msg = Basic.read_byte(msg) reply = "" for i in range(0, numPayments): #read the payment: result, coin, msg = deposit_acoin(msg, current) reply += struct.pack('!s', result) token, msg = msg[:Globals.ACOIN_KEY_BYTES], msg[Globals. ACOIN_KEY_BYTES:] #if the coin is valid... if result == '0': sig = Globals.ACOIN_KEY.decrypt(token, False) #~ print "token: " + repr(token) #~ print "sig: " + repr(sig) reply += struct.pack('!%ss' % (Globals.ACOIN_KEY_BYTES), sig) #and finally, send it back to the client: self.send_func(reply) #log the event: eventLogger.aggregate_event("PAYMENTS", self.address[0], 1)
def handle_payment(self, msg): """Unpack, process, and respond to a payment message. @param msg: the payment message from the origin. @type msg: str""" #if there are any failures, log them, and close the circuit: try: #read the PAR protocol version: version, msg = Basic.read_byte(msg) assert version == 1, "currently only accept PAR version 1" readTokens, msg = Basic.read_int(msg) writeTokens, msg = Basic.read_int(msg) #read their request ID too theirId, msg = Basic.read_long(msg) #read the number of coins: numCoins, msg = Basic.read_byte(msg) #read each coin: creditsEarned = 0 requests = [] for i in range(0, numCoins): #what type of coin is this? coinType, msg = Basic.read_byte(msg) #we only accept acoins for now: assert coinType == PaymentStream.COIN_TYPES[ 'A'], "bad coin type" #get the matching request: requestId, msg = Basic.read_long(msg) requests.append(requestId) assert len(msg) % numCoins == 0, "bad payment message length" coinLen = len(msg) / numCoins for requestId in requests: #if this is not true, there wont even be another part to the response assert Basic.read_byte(msg)[0] == ord( '0'), "bad leading byte in payment message" blob, msg = msg[:coinLen], msg[coinLen:] request = self.requests[requestId] del self.requests[requestId] code, sig = Basic.read_byte(blob) #validate the ACoin coin = BankMessages.parse_acoin_response( self.bank, sig, request) if not coin: raise Exception("Invalid ACoin sent for payment!") #success! creditsEarned += coin.get_expected_value() coin.originCircuit = self self.bank.on_earned_coin(coin) receiptMessageDeferred = self.send_receipt_message( theirId, numCoins) if not receiptMessageDeferred: return #check that they paid enough: requestedTokens = readTokens + writeTokens paidTokens = creditsEarned * Globals.CELLS_PER_PAYMENT if paidTokens < requestedTokens: raise Exception("Relays asked for %s, but only paid for %s" % (requestedTokens, paidTokens)) #inform Tor that we got a payment message: addTokensDeferred = self.add_tokens(readTokens, writeTokens) if not addTokensDeferred: return 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") addTokensDeferred.addCallback(response) except Exception, error: log_ex(error, "Got bad PAR message") self.close()
def handle_payment(self, msg): """Unpack, process, and respond to a payment message. @param msg: the payment message from the origin. @type msg: str""" #if there are any failures, log them, and close the circuit: try: #read the PAR protocol version: version, msg = Basic.read_byte(msg) assert version == 1, "currently only accept PAR version 1" readTokens, msg = Basic.read_int(msg) writeTokens, msg = Basic.read_int(msg) #read their request ID too theirId, msg = Basic.read_long(msg) #read the number of coins: numCoins, msg = Basic.read_byte(msg) #read each coin: creditsEarned = 0 requests = [] for i in range(0, numCoins): #what type of coin is this? coinType, msg = Basic.read_byte(msg) #we only accept acoins for now: assert coinType == PaymentStream.COIN_TYPES['A'], "bad coin type" #get the matching request: requestId, msg = Basic.read_long(msg) requests.append(requestId) assert len(msg) % numCoins == 0, "bad payment message length" coinLen = len(msg) / numCoins for requestId in requests: #if this is not true, there wont even be another part to the response assert Basic.read_byte(msg)[0] == ord('0'), "bad leading byte in payment message" blob, msg = msg[:coinLen], msg[coinLen:] request = self.requests[requestId] del self.requests[requestId] code, sig = Basic.read_byte(blob) #validate the ACoin coin = BankMessages.parse_acoin_response(self.bank, sig, request) if not coin: raise Exception("Invalid ACoin sent for payment!") #success! creditsEarned += coin.get_expected_value() coin.originCircuit = self self.bank.on_earned_coin(coin) receiptMessageDeferred = self.send_receipt_message(theirId, numCoins) if not receiptMessageDeferred: return #check that they paid enough: requestedTokens = readTokens + writeTokens paidTokens = creditsEarned * Globals.CELLS_PER_PAYMENT if paidTokens < requestedTokens: raise Exception("Relays asked for %s, but only paid for %s" % (requestedTokens, paidTokens)) #inform Tor that we got a payment message: addTokensDeferred = self.add_tokens(readTokens, writeTokens) if not addTokensDeferred: return 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") addTokensDeferred.addCallback(response) except Exception, error: log_ex(error, "Got bad PAR message") self.close()
def handle_receipt(self, msg): """Handle a receipt message from Tor control""" version, msg = Basic.read_byte(msg) payStream, msg = self.get_payment_stream(msg) payStream.handle_receipt(msg)
def handle_dht_response(self, data): log_msg("Got remote DHT response", 4, "dht") version, data = Basic.read_byte(data) assert version == Node.VERSION peerList = {'peers': data} self.callback(peerList)