def add_to_tor(self, protocol): ''' Returns a Deferred which fires with 'self' after at least one descriptor has been uploaded. Errback if no descriptor upload succeeds. ''' upload_d = _await_descriptor_upload(protocol, self, progress=None, await_all_uploads=False) # _add_ephemeral_service takes a TorConfig but we don't have # that here .. and also we're just keeping this for # backwards-compatability anyway so instead of trying to # re-use that helper I'm leaving this original code here. So # this is what it supports and that's that: ports = ' '.join(map(lambda x: 'Port=' + x.strip(), self._ports)) cmd = 'ADD_ONION %s %s' % (self._key_blob, ports) ans = yield protocol.queue_command(cmd) ans = find_keywords(ans.split('\n')) self.hostname = ans['ServiceID'] + '.onion' if self._key_blob.startswith('NEW:'): self.private_key = ans['PrivateKey'] else: self.private_key = self._key_blob log.msg('Created hidden-service at', self.hostname) log.msg("Created '{}', waiting for descriptor uploads.".format( self.hostname)) yield upload_d
def update(self, args): # print "Circuit.update:",args if self.id is None: self.id = int(args[0]) for x in self.listeners: x.circuit_new(self) else: if int(args[0]) != self.id: raise RuntimeError("Update for wrong circuit.") self.state = args[1] kw = find_keywords(args) self.flags = kw if 'PURPOSE' in kw: self.purpose = kw['PURPOSE'] if 'BUILD_FLAGS' in kw: self.build_flags = kw['BUILD_FLAGS'].split(',') if self.state == 'LAUNCHED': self.path = [] for x in self.listeners: x.circuit_launched(self) else: if self.state != 'FAILED' and self.state != 'CLOSED': if len(args) > 2: self.update_path(args[2].split(',')) if self.state == 'BUILT': for x in self.listeners: x.circuit_built(self) self._when_built.fire(self) elif self.state == 'CLOSED': if len(self.streams) > 0: # it seems this can/does happen if a remote router # crashes or otherwise shuts down a circuit with # streams on it still .. also if e.g. you "carml circ # --delete " the circuit while the stream is # in-progress...can we do better than logging? # *should* we do anything else (the stream should get # closed still by Tor). log.msg( "Circuit is {} but still has {} streams".format( self.state, len(self.streams) ) ) flags = self._create_flags(kw) self.maybe_call_closing_deferred() for x in self.listeners: x.circuit_closed(self, **flags) elif self.state == 'FAILED': if len(self.streams) > 0: log.err(RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) flags = self._create_flags(kw) self.maybe_call_closing_deferred() for x in self.listeners: x.circuit_failed(self, **flags)
def add_to_tor(self, protocol): ''' Returns a Deferred which fires with 'self' after at least one descriptor has been uploaded. Errback if no descriptor upload succeeds. ''' upload_d = _await_descriptor_upload(protocol, self, progress=None, await_all_uploads=False) # _add_ephemeral_service takes a TorConfig but we don't have # that here .. and also we're just keeping this for # backwards-compatability anyway so instead of trying to # re-use that helper I'm leaving this original code here. So # this is what it supports and that's that: ports = ' '.join(map(lambda x: 'Port=' + x.strip(), self._ports)) cmd = 'ADD_ONION %s %s' % (self._key_blob, ports) ans = yield protocol.queue_command(cmd) ans = find_keywords(ans.split('\n')) self.hostname = ans['ServiceID'] + '.onion' if self._key_blob.startswith('NEW:'): self.private_key = ans['PrivateKey'] else: self.private_key = self._key_blob log.msg('Created hidden-service at', self.hostname) log.msg("Created '{}', waiting for descriptor uploads.".format(self.hostname)) yield upload_d
def test_filter(self): "make sure we filter out keys that look like router IDs" self.assertEqual( find_keywords("foo=bar $1234567890=routername baz=quux".split()), { 'foo': 'bar', 'baz': 'quux' })
def update(self, args): # print "Circuit.update:",args if self.id is None: self.id = int(args[0]) for x in self.listeners: x.circuit_new(self) else: if int(args[0]) != self.id: raise RuntimeError("Update for wrong circuit.") self.state = args[1] kw = find_keywords(args) self.flags = kw if 'PURPOSE' in kw: self.purpose = kw['PURPOSE'] if 'BUILD_FLAGS' in kw: self.build_flags = kw['BUILD_FLAGS'].split(',') if self.state == 'LAUNCHED': self.path = [] for x in self.listeners: x.circuit_launched(self) else: if self.state != 'FAILED' and self.state != 'CLOSED': if len(args) > 2: self.update_path(args[2].split(',')) if self.state == 'BUILT': for x in self.listeners: x.circuit_built(self) self._when_built.fire(self) elif self.state == 'CLOSED': if len(self.streams) > 0: # it seems this can/does happen if a remote router # crashes or otherwise shuts down a circuit with # streams on it still .. also if e.g. you "carml circ # --delete " the circuit while the stream is # in-progress...can we do better than logging? # *should* we do anything else (the stream should get # closed still by Tor). log.msg("Circuit is {} but still has {} streams".format( self.state, len(self.streams))) flags = self._create_flags(kw) self.maybe_call_closing_deferred() for x in self.listeners: x.circuit_closed(self, **flags) elif self.state == 'FAILED': if len(self.streams) > 0: log.err( RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) flags = self._create_flags(kw) self.maybe_call_closing_deferred() for x in self.listeners: x.circuit_failed(self, **flags)
def update(self, args): # print "Circuit.update:",args if self.id is None: self.id = int(args[0]) [x.circuit_new(self) for x in self.listeners] else: if int(args[0]) != self.id: raise RuntimeError("Update for wrong circuit.") self.state = args[1] kw = find_keywords(args) self.flags = kw if 'PURPOSE' in kw: self.purpose = kw['PURPOSE'] if 'BUILD_FLAGS' in kw: self.build_flags = kw['BUILD_FLAGS'].split(',') if self.state == 'LAUNCHED': self.path = [] [x.circuit_launched(self) for x in self.listeners] else: if self.state != 'FAILED' and self.state != 'CLOSED': if len(args) > 2: self.update_path(args[2].split(',')) if self.state == 'BUILT': [x.circuit_built(self) for x in self.listeners] for d in self._when_built: d.callback(self) self._when_built = [] elif self.state == 'CLOSED': if len(self.streams) > 0: # FIXME it seems this can/does happen if a remote # router crashes or otherwise shuts down a circuit # with streams on it still log.err( RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) flags = self._create_flags(kw) self.maybe_call_closing_deferred() [x.circuit_closed(self, **flags) for x in self.listeners] elif self.state == 'FAILED': if len(self.streams) > 0: log.err( RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) flags = self._create_flags(kw) self.maybe_call_closing_deferred() [x.circuit_failed(self, **flags) for x in self.listeners]
def status_client(self, arg): args = shlex.split(arg) if args[1] != 'BOOTSTRAP': return kw = find_keywords(args) prog = int(kw['PROGRESS']) tag = kw['TAG'] summary = kw['SUMMARY'] self.progress(prog, tag, summary) if prog == 100: self.connected_cb.callback(self)
def update(self, args): # print "Circuit.update:",args if self.id is None: self.id = int(args[0]) [x.circuit_new(self) for x in self.listeners] else: if int(args[0]) != self.id: raise RuntimeError("Update for wrong circuit.") self.state = args[1] kw = find_keywords(args) self.flags = kw if 'PURPOSE' in kw: self.purpose = kw['PURPOSE'] if 'BUILD_FLAGS' in kw: self.build_flags = kw['BUILD_FLAGS'].split(',') if self.state == 'LAUNCHED': self.path = [] [x.circuit_launched(self) for x in self.listeners] else: if self.state != 'FAILED' and self.state != 'CLOSED': if len(args) > 2: self.update_path(args[2].split(',')) if self.state == 'BUILT': [x.circuit_built(self) for x in self.listeners] for d in self._when_built: d.callback(self) self._when_built = [] elif self.state == 'CLOSED': if len(self.streams) > 0: # FIXME it seems this can/does happen if a remote # router crashes or otherwise shuts down a circuit # with streams on it still log.err(RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) flags = self._create_flags(kw) self.maybe_call_closing_deferred() [x.circuit_closed(self, **flags) for x in self.listeners] elif self.state == 'FAILED': if len(self.streams) > 0: log.err(RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) flags = self._create_flags(kw) self.maybe_call_closing_deferred() [x.circuit_failed(self, **flags) for x in self.listeners]
def _status_client(self, arg): args = shlex.split(arg) if args[1] != 'BOOTSTRAP': return kw = find_keywords(args) prog = int(kw['PROGRESS']) tag = kw['TAG'] summary = kw['SUMMARY'] self.progress(prog, tag, summary) if prog == 100: if self._timeout_delayed_call: self._timeout_delayed_call.cancel() self._timeout_delayed_call = None self._maybe_notify_connected(self)
def status_client(self, arg): args = shlex.split(arg) if args[1] != "BOOTSTRAP": return kw = find_keywords(args) prog = int(kw["PROGRESS"]) tag = kw["TAG"] summary = kw["SUMMARY"] self.progress(prog, tag, summary) if prog == 100: if self._timeout_delayed_call: self._timeout_delayed_call.cancel() self._timeout_delayed_call = None self.connected_cb.callback(self)
def update(self, args): ##print "Circuit.update:",args if self.id is None: self.id = int(args[0]) [x.circuit_new(self) for x in self.listeners] else: if int(args[0]) != self.id: raise RuntimeError("Update for wrong circuit.") self.state = args[1] kw = find_keywords(args) if 'PURPOSE' in kw: self.purpose = kw['PURPOSE'] if 'BUILD_FLAGS' in kw: self.build_flags = kw['BUILD_FLAGS'].split(',') if self.state == 'LAUNCHED': self.path = [] [x.circuit_launched(self) for x in self.listeners] else: if self.state != 'FAILED' and self.state != 'CLOSED' and len(args) > 2: self.update_path(args[2].split(',')) if self.state == 'BUILT': [x.circuit_built(self) for x in self.listeners] elif self.state == 'CLOSED': if len(self.streams) > 0: log.err(RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) flags = self._create_flags(kw) self.maybe_call_closing_deferred() [x.circuit_closed(self, **flags) for x in self.listeners] elif self.state == 'FAILED': if len(self.streams) > 0: log.err(RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) flags = self._create_flags(kw) self.maybe_call_closing_deferred() [x.circuit_failed(self, **flags) for x in self.listeners]
def update(self, args): ##print "Circuit.update:",args if self.id is None: self.id = int(args[0]) [x.circuit_new(self) for x in self.listeners] else: if int(args[0]) != self.id: raise RuntimeError("Update for wrong circuit.") self.state = args[1] kw = find_keywords(args) if 'PURPOSE' in kw: self.purpose = kw['PURPOSE'] if self.state == 'LAUNCHED': self.path = [] [x.circuit_launched(self) for x in self.listeners] else: if self.state != 'FAILED' and self.state != 'CLOSED' and len(args) > 2: self.update_path(args[2].split(',')) if self.state == 'BUILT': [x.circuit_built(self) for x in self.listeners] elif self.state == 'CLOSED': if len(self.streams) > 0: log.err(RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) [x.circuit_closed(self) for x in self.listeners] elif self.state == 'FAILED': if len(self.streams) > 0: log.err(RuntimeError("Circuit is %s but still has %d streams" % (self.state, len(self.streams)))) reason = 'unknown' if 'REASON' in kw: reason = kw['REASON'] [x.circuit_failed(self, reason) for x in self.listeners]
def test_filter(self): "make sure we filter out keys that look like router IDs" self.assertEqual( find_keywords("foo=bar $1234567890=routername baz=quux".split()), {'foo': 'bar', 'baz': 'quux'} )
def update(self, args): # print "update",self.id,args if self.id is None: self.id = int(args[0]) else: if self.id != int(args[0]): raise RuntimeError("Update for wrong stream.") kw = find_keywords(args) self.flags = kw if 'SOURCE_ADDR' in kw: last_colon = kw['SOURCE_ADDR'].rfind(':') self.source_addr = kw['SOURCE_ADDR'][:last_colon] if self.source_addr != '(Tor_internal)': self.source_addr = maybe_ip_addr(self.source_addr) self.source_port = int(kw['SOURCE_ADDR'][last_colon + 1:]) self.state = args[1] # XXX why not using the state-machine stuff? ;) if self.state in ['NEW', 'NEWRESOLVE', 'SUCCEEDED']: if self.target_host is None: last_colon = args[3].rfind(':') self.target_host = args[3][:last_colon] self.target_port = int(args[3][last_colon + 1:]) self.target_port = int(self.target_port) if self.state == 'NEW': if self.circuit is not None: log.err(RuntimeError("Weird: circuit valid in NEW")) [x.stream_new(self) for x in self.listeners] else: [x.stream_succeeded(self) for x in self.listeners] elif self.state == 'REMAP': self.target_addr = maybe_ip_addr(args[3][:args[3].rfind(':')]) elif self.state == 'CLOSED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() flags = self._create_flags(kw) [x.stream_closed(self, **flags) for x in self.listeners] elif self.state == 'FAILED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() # build lower-case version of all flags flags = self._create_flags(kw) [x.stream_failed(self, **flags) for x in self.listeners] elif self.state == 'SENTCONNECT': pass # print 'SENTCONNECT',self,args elif self.state == 'DETACHED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None # FIXME does this count as closed? # self.maybe_call_closing_deferred() flags = self._create_flags(kw) [x.stream_detach(self, **flags) for x in self.listeners] elif self.state in ['NEWRESOLVE', 'SENTRESOLVE']: pass # print self.state, self, args else: raise RuntimeError("Unknown state: %s" % self.state) # see if we attached to a circuit. I believe this only happens # on a SENTCONNECT or REMAP. DETACHED is excluded so we don't # immediately re-add the circuit we just detached from if self.state not in ['CLOSED', 'FAILED', 'DETACHED']: cid = int(args[2]) if cid == 0: if self.circuit and self in self.circuit.streams: self.circuit.streams.remove(self) self.circuit = None else: if self.circuit is None: self.circuit = self.circuit_container.find_circuit(cid) if self not in self.circuit.streams: self.circuit.streams.append(self) for x in self.listeners: x.stream_attach(self, self.circuit) else: if self.circuit.id != cid: log.err( RuntimeError( 'Circuit ID changed from %d to %d.' % (self.circuit.id, cid) ) )
def add_to_tor(self, protocol): """ Returns a Deferred which fires with 'self' after at least one descriptor has been uploaded. Errback if no descriptor upload succeeds. """ ports = ' '.join(map(lambda x: 'Port=' + x.strip(), self._ports)) cmd = 'ADD_ONION %s %s' % (self._key_blob, ports) ans = yield protocol.queue_command(cmd) ans = find_keywords(ans.split('\n')) self.hostname = ans['ServiceID'] + '.onion' if self._key_blob == 'NEW:BEST': self.private_key = ans['PrivateKey'] # NOTE line changed to give indication when using mocks log.info('Created onion-service at', self.hostname) # pylint: disable=no-member # Now we want to wait for the descriptor uploads. This doesn't # quite work, as the UPLOADED events always say "UNKNOWN" for # the HSAddress so we can't correlate it to *this* onion for # sure :/ "yet", though. Yawning says on IRC this is coming. # XXX Hmm, still UPLOADED always says UNKNOWN, but the UPLOAD # events do say the address -- so we save all those, and # correlate to the target nodes. Not sure if this will really # even work, but better than nothing. uploaded = defer.Deferred() attempted_uploads = set() confirmed_uploads = set() failed_uploads = set() def hs_desc(evt): """ From control-spec: "650" SP "HS_DESC" SP Action SP HSAddress SP AuthType SP HsDir [SP DescriptorID] [SP "REASON=" Reason] [SP "REPLICA=" Replica] """ args = evt.split() subtype = args[0] if subtype == 'UPLOAD': if args[1] == self.hostname[:-6]: attempted_uploads.add(args[3]) elif subtype == 'UPLOADED': # we only need ONE successful upload to happen for the # HS to be reachable. (addr is args[1]) if args[3] in attempted_uploads: confirmed_uploads.add(args[3]) log.info("Uploaded '{}' to '{}'".format(self.hostname, args[3])) # pylint: disable=no-member uploaded.callback(self) elif subtype == 'FAILED': if args[1] == self.hostname[:-6]: failed_uploads.add(args[3]) if failed_uploads == attempted_uploads: msg = "Failed to upload '{}' to: {}".format( self.hostname, ', '.join(failed_uploads), ) uploaded.errback(RuntimeError(msg)) log.info("Created '{}', waiting for descriptor uploads.".format(self.hostname)) # pylint: disable=no-member yield protocol.add_event_listener('HS_DESC', hs_desc) yield uploaded yield protocol.remove_event_listener('HS_DESC', hs_desc)
def update(self, args): # print "update",self.id,args if self.id is None: self.id = int(args[0]) else: if self.id != int(args[0]): raise RuntimeError("Update for wrong stream.") kw = find_keywords(args) self.flags = kw if "SOURCE_ADDR" in kw: last_colon = kw["SOURCE_ADDR"].rfind(":") self.source_addr = kw["SOURCE_ADDR"][:last_colon] if self.source_addr != "(Tor_internal)": self.source_addr = maybe_ip_addr(self.source_addr) self.source_port = int(kw["SOURCE_ADDR"][last_colon + 1 :]) self.state = args[1] # XXX why not using the state-machine stuff? ;) if self.state in ["NEW", "NEWRESOLVE", "SUCCEEDED"]: if self.target_host is None: last_colon = args[3].rfind(":") self.target_host = args[3][:last_colon] self.target_port = int(args[3][last_colon + 1 :]) # target_host is often an IP address (newer tors? did # this change?) so we attempt to look it up in our # AddrMap and make it a name no matter what. if self._addrmap: try: h = self._addrmap.find(self.target_host) self.target_host = h.name except KeyError: pass self.target_port = int(self.target_port) if self.state == "NEW": if self.circuit is not None: log.err(RuntimeError("Weird: circuit valid in NEW")) self._notify("stream_new", self) else: self._notify("stream_succeeded", self) elif self.state == "REMAP": self.target_addr = maybe_ip_addr(args[3][: args[3].rfind(":")]) elif self.state == "CLOSED": if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() flags = self._create_flags(kw) self._notify("stream_closed", self, **flags) elif self.state == "FAILED": if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() # build lower-case version of all flags flags = self._create_flags(kw) self._notify("stream_failed", self, **flags) elif self.state == "SENTCONNECT": pass # print 'SENTCONNECT',self,args elif self.state == "DETACHED": if self.circuit: self.circuit.streams.remove(self) self.circuit = None # FIXME does this count as closed? # self.maybe_call_closing_deferred() flags = self._create_flags(kw) self._notify("stream_detach", self, **flags) elif self.state in ["NEWRESOLVE", "SENTRESOLVE"]: pass # print self.state, self, args else: raise RuntimeError("Unknown state: %s" % self.state) # see if we attached to a circuit. I believe this only happens # on a SENTCONNECT or REMAP. DETACHED is excluded so we don't # immediately re-add the circuit we just detached from if self.state not in ["CLOSED", "FAILED", "DETACHED"]: cid = int(args[2]) if cid == 0: if self.circuit and self in self.circuit.streams: self.circuit.streams.remove(self) self.circuit = None else: if self.circuit is None: self.circuit = self.circuit_container.find_circuit(cid) if self not in self.circuit.streams: self.circuit.streams.append(self) self._notify("stream_attach", self, self.circuit) else: if self.circuit.id != cid: log.err(RuntimeError("Circuit ID changed from %d to %d." % (self.circuit.id, cid)))
def test_filter(self): self.assertEqual(find_keywords("foo=bar $1234567890=routername baz=quux".split()), {'foo': 'bar', 'baz': 'quux'})
def _add_ephemeral_service(config, onion, progress, version, auth=None): """ Internal Helper. This uses ADD_ONION to add the given service to Tor. The Deferred this returns will callback when the ADD_ONION call has succeed, *and* when at least one descriptor has been uploaded to a Hidden Service Directory. :param config: a TorConfig instance :param onion: an EphemeralOnionService instance :param progress: a callable taking 3 arguments (percent, tag, description) that is called some number of times to tell you of progress. :param version: 2 or 3, which kind of service to create :param auth: if not None, create an authenticated service ("basic" is the only kind supported currently so a AuthBasic instance should be passed) """ if onion not in config.EphemeralOnionServices: config.EphemeralOnionServices.append(onion) # we have to keep this as a Deferred for now so that HS_DESC # listener gets added before we issue ADD_ONION assert version in (2, 3) uploaded_d = _await_descriptor_upload(config.tor_protocol, onion, progress) # we allow a key to be passed that *doestn'* start with # "RSA1024:" because having to escape the ":" for endpoint # string syntax (which uses ":" as delimeters) is annoying # XXX rethink ^^? what do we do when the type is upgraded? # maybe just a magic-character that's different from ":", or # force people to escape them? if onion.private_key: if onion.private_key is not DISCARD and ':' not in onion.private_key: if version == 2: if not onion.private_key.startswith("RSA1024:"): onion._private_key = "RSA1024:" + onion.private_key elif version == 3: if not onion.private_key.startswith("ED25519-V3:"): onion._private_key = "ED25519-V3:" + onion.private_key # okay, we're set up to listen, and now we issue the ADD_ONION # command. this will set ._hostname and ._private_key properly keystring = 'NEW:BEST' if onion.private_key not in (None, DISCARD): keystring = onion.private_key elif version == 3: keystring = 'NEW:ED25519-V3' if version == 3: if 'V3' not in keystring: raise ValueError("version=3 but private key isn't 'ED25519-V3'") cmd = 'ADD_ONION {}'.format(keystring) for port in onion._ports: cmd += ' Port={},{}'.format(*port.split(' ', 1)) flags = [] if onion._detach: flags.append('Detach') if onion.private_key is DISCARD: flags.append('DiscardPK') if auth is not None: assert isinstance(auth, AuthBasic) # don't support AuthStealth yet if isinstance(auth, AuthBasic): flags.append('BasicAuth') if flags: cmd += ' Flags={}'.format(','.join(flags)) if auth is not None: for client_name in auth.client_names(): keyblob = auth.keyblob_for(client_name) if keyblob is None: cmd += ' ClientAuth={}'.format(client_name) else: cmd += ' ClientAuth={}:{}'.format(client_name, keyblob) onion._add_client(client_name, keyblob) raw_res = yield config.tor_protocol.queue_command(cmd) res = find_keywords(raw_res.split('\n')) try: onion._hostname = res['ServiceID'] + '.onion' if onion.private_key is DISCARD: onion._private_key = None else: # if we specified a private key, it's not echoed back if not onion.private_key: onion._private_key = res['PrivateKey'].strip() except KeyError: raise RuntimeError( "Expected ADD_ONION to return ServiceID= and PrivateKey= args." "Got: {}".format(res)) if auth is not None: for line in raw_res.split('\n'): if line.startswith("ClientAuth="): name, blob = line[11:].split(':', 1) onion._add_client(name, blob) log.msg("{}: waiting for descriptor uploads.".format(onion.hostname)) yield uploaded_d
def update(self, args): if self.id is None: self.id = int(args[0]) else: if self.id != int(args[0]): raise RuntimeError("Update for wrong stream.") kw = find_keywords(args) self.flags = kw if 'SOURCE_ADDR' in kw: last_colon = kw['SOURCE_ADDR'].rfind(':') self.source_addr = kw['SOURCE_ADDR'][:last_colon] if self.source_addr != '(Tor_internal)': self.source_addr = maybe_ip_addr(self.source_addr) self.source_port = int(kw['SOURCE_ADDR'][last_colon + 1:]) self.state = args[1] # XXX why not using the state-machine stuff? ;) if self.state in ['NEW', 'NEWRESOLVE', 'SUCCEEDED']: if self.target_host is None: last_colon = args[3].rfind(':') self.target_host = args[3][:last_colon] self.target_port = int(args[3][last_colon + 1:]) # target_host is often an IP address (newer tors? did # this change?) so we attempt to look it up in our # AddrMap and make it a name no matter what. if self._addrmap: try: h = self._addrmap.find(self.target_host) self.target_host = h.name except KeyError: pass self.target_port = int(self.target_port) if self.state == 'NEW': if self.circuit is not None: log.err(RuntimeError("Weird: circuit valid in NEW")) self._notify('stream_new', self) else: self._notify('stream_succeeded', self) elif self.state == 'REMAP': self.target_addr = maybe_ip_addr(args[3][:args[3].rfind(':')]) elif self.state == 'CLOSED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() flags = self._create_flags(kw) self._notify('stream_closed', self, **flags) elif self.state == 'FAILED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() # build lower-case version of all flags flags = self._create_flags(kw) self._notify('stream_failed', self, **flags) elif self.state == 'SENTCONNECT': pass # print 'SENTCONNECT',self,args elif self.state == 'DETACHED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None # FIXME does this count as closed? # self.maybe_call_closing_deferred() flags = self._create_flags(kw) self._notify('stream_detach', self, **flags) elif self.state in ['NEWRESOLVE', 'SENTRESOLVE']: pass # print self.state, self, args else: raise RuntimeError("Unknown state: %s" % self.state) # see if we attached to a circuit. I believe this only happens # on a SENTCONNECT or REMAP. DETACHED is excluded so we don't # immediately re-add the circuit we just detached from if self.state not in ['CLOSED', 'FAILED', 'DETACHED']: cid = int(args[2]) if cid == 0: if self.circuit and self in self.circuit.streams: self.circuit.streams.remove(self) self.circuit = None else: if self.circuit is None: self.circuit = self.circuit_container.find_circuit(cid) if self not in self.circuit.streams: self.circuit.streams.append(self) self._notify('stream_attach', self, self.circuit) else: if self.circuit.id != cid: log.err( RuntimeError('Circuit ID changed from %d to %d.' % (self.circuit.id, cid)))
def update(self, args): if self.id is None: self.id = int(args[0]) else: if self.id != int(args[0]): raise RuntimeError("Update for wrong stream.") kw = find_keywords(args) self.flags = kw if 'SOURCE_ADDR' in kw: last_colon = kw['SOURCE_ADDR'].rfind(':') self.source_addr = kw['SOURCE_ADDR'][:last_colon] if self.source_addr != '(Tor_internal)': self.source_addr = maybe_ip_addr(self.source_addr) self.source_port = int(kw['SOURCE_ADDR'][last_colon + 1:]) self.state = args[1] # XXX why not using the state-machine stuff? ;) if self.state in ['NEW', 'NEWRESOLVE', 'SUCCEEDED']: if self.target_host is None: last_colon = args[3].rfind(':') self.target_host = args[3][:last_colon] self.target_port = int(args[3][last_colon + 1:]) # target_host is often an IP address (newer tors? did # this change?) so we attempt to look it up in our # AddrMap and make it a name no matter what. if self._addrmap: try: h = self._addrmap.find(self.target_host) self.target_host = h.name except KeyError: pass self.target_port = int(self.target_port) if self.state == 'NEW': if self.circuit is not None: log.err(RuntimeError("Weird: circuit valid in NEW")) self._notify('stream_new', self) else: self._notify('stream_succeeded', self) elif self.state == 'REMAP': self.target_addr = maybe_ip_addr(args[3][:args[3].rfind(':')]) elif self.state == 'CLOSED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() flags = self._create_flags(kw) self._notify('stream_closed', self, **flags) elif self.state == 'FAILED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() # build lower-case version of all flags flags = self._create_flags(kw) self._notify('stream_failed', self, **flags) elif self.state == 'SENTCONNECT': pass # print 'SENTCONNECT',self,args elif self.state == 'DETACHED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None # FIXME does this count as closed? # self.maybe_call_closing_deferred() flags = self._create_flags(kw) self._notify('stream_detach', self, **flags) elif self.state in ['NEWRESOLVE', 'SENTRESOLVE']: pass # print self.state, self, args else: raise RuntimeError("Unknown state: %s" % self.state) # see if we attached to a circuit. I believe this only happens # on a SENTCONNECT or REMAP. DETACHED is excluded so we don't # immediately re-add the circuit we just detached from if self.state not in ['CLOSED', 'FAILED', 'DETACHED']: cid = int(args[2]) if cid == 0: if self.circuit and self in self.circuit.streams: self.circuit.streams.remove(self) self.circuit = None else: if self.circuit is None: self.circuit = self.circuit_container.find_circuit(cid) if self not in self.circuit.streams: self.circuit.streams.append(self) self._notify('stream_attach', self, self.circuit) else: # XXX I am seeing this from torexitscan (*and* # from my TorNS thing, so I think it's some kind # of 'consistent' behavior out of Tor) so ... this # is probably when we're doing stream-attachment # stuff? maybe the stream gets assigned a circuit # 'provisionally' and then it's changed? # ...yup, looks like it! if self.circuit.id != cid: # you can get SENTCONNECT twice in a row (for example) with different circuit-ids if there is something (e.g. another txtorcon) doing R log.err( RuntimeError( 'Circuit ID changed from %d to %d state=%s.' % (self.circuit.id, cid, self.state) ) )
def update(self, args): ## print "update",self.id,args if self.id is None: self.id = int(args[0]) else: if self.id != int(args[0]): raise RuntimeError("Update for wrong stream.") kw = find_keywords(args) self.flags = kw if 'SOURCE_ADDR' in kw: last_colon = kw['SOURCE_ADDR'].rfind(':') self.source_addr = kw['SOURCE_ADDR'][:last_colon] if self.source_addr != '(Tor_internal)': self.source_addr = maybe_ip_addr(self.source_addr) self.source_port = int(kw['SOURCE_ADDR'][last_colon + 1:]) self.state = args[1] if self.state in ['NEW', 'NEWRESOLVE', 'SUCCEEDED']: if self.target_host is None: last_colon = args[3].rfind(':') self.target_host = args[3][:last_colon] self.target_port = int(args[3][last_colon + 1:]) self.target_port = int(self.target_port) if self.state == 'NEW': if self.circuit is not None: log.err(RuntimeError("Weird: circuit valid in NEW")) [x.stream_new(self) for x in self.listeners] else: [x.stream_succeeded(self) for x in self.listeners] elif self.state == 'REMAP': self.target_addr = maybe_ip_addr(args[3][:args[3].rfind(':')]) elif self.state == 'CLOSED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() flags = self._create_flags(kw) [x.stream_closed(self, **flags) for x in self.listeners] elif self.state == 'FAILED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() # build lower-case version of all flags flags = self._create_flags(kw) [x.stream_failed(self, **flags) for x in self.listeners] elif self.state == 'SENTCONNECT': pass # print 'SENTCONNECT',self,args elif self.state == 'DETACHED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None ## FIXME does this count as closed? ##self.maybe_call_closing_deferred() flags = self._create_flags(kw) [x.stream_detach(self, **flags) for x in self.listeners] elif self.state in ['NEWRESOLVE', 'SENTRESOLVE']: pass # print self.state, self, args else: raise RuntimeError("Unknown state: %s" % self.state) ## see if we attached to a circuit. I believe this only ## happens on a SENTCONNECT or REMAP. DETACHED is excluded so ## we don't immediately re-add the circuit we just detached ## from if self.state not in ['CLOSED', 'FAILED', 'DETACHED']: cid = int(args[2]) if cid == 0: if self.circuit and self in self.circuit.streams: self.circuit.streams.remove(self) self.circuit = None else: if self.circuit is None: self.circuit = self.circuit_container.find_circuit(cid) if self not in self.circuit.streams: self.circuit.streams.append(self) [x.stream_attach(self, self.circuit) for x in self.listeners] else: if self.circuit.id != cid: log.err(RuntimeError('Circuit ID changed from %d to %d.' % (self.circuit.id, cid)))
def update(self, args): ##print "update",self.id,args if self.id is None: self.id = int(args[0]) else: if self.id != int(args[0]): raise RuntimeError("Update for wrong stream.") kw = find_keywords(args) if 'SOURCE_ADDR' in kw: last_colon = kw['SOURCE_ADDR'].rfind(':') self.source_addr = kw['SOURCE_ADDR'][:last_colon] if self.source_addr != '(Tor_internal)': self.source_addr = maybe_ip_addr(self.source_addr) self.source_port = int(kw['SOURCE_ADDR'][last_colon + 1:]) self.state = args[1] if self.state in ['NEW', 'SUCCEEDED']: if self.target_host is None: last_colon = args[3].rfind(':') self.target_host = args[3][:last_colon] self.target_port = int(args[3][last_colon + 1:]) self.target_port = int(self.target_port) if self.state == 'NEW': if self.circuit is not None: log.err(RuntimeError("Weird: circuit valid in NEW")) [x.stream_new(self) for x in self.listeners] else: [x.stream_succeeded(self) for x in self.listeners] elif self.state == 'REMAP': self.target_addr = maybe_ip_addr(args[3][:args[3].rfind(':')]) elif self.state == 'CLOSED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None [x.stream_closed(self) for x in self.listeners] elif self.state == 'FAILED': reason = '' remote_reason = '' if 'REMOTE_REASON' in kw: remote_reason = kw['REMOTE_REASON'] if 'REASON' in kw: reason = kw['REASON'] if self.circuit: self.circuit.streams.remove(self) self.circuit = None [x.stream_failed(self, reason, remote_reason) for x in self.listeners] elif self.state == 'SENTCONNECT': pass #print 'SENTCONNECT',self,args elif self.state == 'DETACHED': reason = '' if len(args) >= 4 and args[4][:7] == 'REASON=': reason = args[4][7:] if self.circuit: self.circuit.streams.remove(self) self.circuit = None [x.stream_detach(self, reason) for x in self.listeners] elif self.state == 'NEWRESOLVE': pass #print 'NEWRESOLVE',self,args elif self.state == 'SENTRESOLVE': pass #print 'SENTRESOLVE',self,args else: raise RuntimeError("Unknown state: %s" % self.state) ## see if we attached to a circuit. I believe this only ## happens on a SENTCONNECT or REMAP. DETACHED is excluded so ## we don't immediately re-add the circuit we just detached ## from if self.state not in ['CLOSED', 'FAILED', 'DETACHED']: cid = int(args[2]) if cid == 0: if self.circuit and self in self.circuit.streams: self.circuit.streams.remove(self) self.circuit = None else: if self.circuit is None: self.circuit = self.circuit_container.find_circuit(cid) if self not in self.circuit.streams: self.circuit.streams.append(self) [x.stream_attach(self, self.circuit) for x in self.listeners] else: if self.circuit.id != cid: log.err(RuntimeError('Circuit ID changed from %d to %d.' % (self.circuit.id, cid)))
def test_filter(self): self.assertEqual( find_keywords("foo=bar $1234567890=routername baz=quux".split()), { 'foo': 'bar', 'baz': 'quux' })
def update(self, args): if self.id is None: self.id = int(args[0]) else: if self.id != int(args[0]): raise RuntimeError("Update for wrong stream.") kw = find_keywords(args) self.flags = kw if 'SOURCE_ADDR' in kw: last_colon = kw['SOURCE_ADDR'].rfind(':') self.source_addr = kw['SOURCE_ADDR'][:last_colon] if self.source_addr != '(Tor_internal)': self.source_addr = maybe_ip_addr(self.source_addr) self.source_port = int(kw['SOURCE_ADDR'][last_colon + 1:]) self.state = args[1] # XXX why not using the state-machine stuff? ;) if self.state in ['NEW', 'NEWRESOLVE', 'SUCCEEDED']: if self.target_host is None: last_colon = args[3].rfind(':') self.target_host = args[3][:last_colon] self.target_port = int(args[3][last_colon + 1:]) # target_host is often an IP address (newer tors? did # this change?) so we attempt to look it up in our # AddrMap and make it a name no matter what. if self._addrmap: try: h = self._addrmap.find(self.target_host) self.target_host = h.name except KeyError: pass self.target_port = int(self.target_port) if self.state == 'NEW': if self.circuit is not None: log.err(RuntimeError("Weird: circuit valid in NEW")) self._notify('stream_new', self) else: self._notify('stream_succeeded', self) elif self.state == 'REMAP': self.target_addr = maybe_ip_addr(args[3][:args[3].rfind(':')]) elif self.state == 'CLOSED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() flags = self._create_flags(kw) self._notify('stream_closed', self, **flags) elif self.state == 'FAILED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None self.maybe_call_closing_deferred() # build lower-case version of all flags flags = self._create_flags(kw) self._notify('stream_failed', self, **flags) elif self.state == 'SENTCONNECT': pass # print 'SENTCONNECT',self,args elif self.state == 'DETACHED': if self.circuit: self.circuit.streams.remove(self) self.circuit = None # FIXME does this count as closed? # self.maybe_call_closing_deferred() flags = self._create_flags(kw) self._notify('stream_detach', self, **flags) elif self.state in ['NEWRESOLVE', 'SENTRESOLVE']: pass # print self.state, self, args else: raise RuntimeError("Unknown state: %s" % self.state) # see if we attached to a circuit. I believe this only happens # on a SENTCONNECT or REMAP. DETACHED is excluded so we don't # immediately re-add the circuit we just detached from if self.state not in ['CLOSED', 'FAILED', 'DETACHED']: cid = int(args[2]) if cid == 0: if self.circuit and self in self.circuit.streams: self.circuit.streams.remove(self) self.circuit = None else: if self.circuit is None: self.circuit = self.circuit_container.find_circuit(cid) if self not in self.circuit.streams: self.circuit.streams.append(self) self._notify('stream_attach', self, self.circuit) else: # XXX I am seeing this from torexitscan (*and* # from my TorNS thing, so I think it's some kind # of 'consistent' behavior out of Tor) so ... this # is probably when we're doing stream-attachment # stuff? maybe the stream gets assigned a circuit # 'provisionally' and then it's changed? # ...yup, looks like it! if self.circuit.id != cid: # you can get SENTCONNECT twice in a row (for example) with different circuit-ids if there is something (e.g. another txtorcon) doing R log.err( RuntimeError( 'Circuit ID changed from %d to %d state=%s.' % (self.circuit.id, cid, self.state)))