def test_empty_location(self): # bug #129: a FURL with no location hints causes a synchronous # exception in Tub.getReference(), instead of an errback'ed Deferred. tubA = Tub() tubA.setServiceParent(self.s) tubB = Tub() tubB.setServiceParent(self.s) # This is a hack to get a FURL with empty location hints. The correct # way to make a Tub unreachable is to not call .setLocation() at all. tubB.setLocation("") r = Receiver(tubB) furl = tubB.registerReference(r) # the buggy behavior is that the following call raises an exception d = tubA.getReference(furl) # whereas it ought to return a Deferred self.assertTrue(isinstance(d, defer.Deferred)) def _check(f): self.assertTrue(isinstance(f, failure.Failure), f) self.assertTrue(f.check(NoLocationHintsError), f) d.addBoth(_check) return d
def test_bad_hints(self): self.tubA = Tub() self.tubA.startService() self.services.append(self.tubA) self.tubB = Tub() self.tubB.startService() self.services.append(self.tubB) portnum = allocate_tcp_port() self.tubB.listenOn("tcp:%d:interface=127.0.0.1" % portnum) bad1 = "no-colon" bad2 = "unknown:foo" bad3 = "tcp:300.300.300.300:333" self.tubB.setLocation(bad1, bad2, bad3) target = HelperTarget("bob") url = self.tubB.registerReference(target) rc = self.tubA.connectTo(url, None) ri = rc.getReconnectionInfo() self.assertEqual(ri.state, "connecting") yield self.poll(lambda: rc.getReconnectionInfo().state != "connecting") # now look at the details ri = rc.getReconnectionInfo() self.assertEqual(ri.state, "waiting") ci = ri.connectionInfo self.assertEqual(ci.connected, False) self.assertEqual(ci.winningHint, None) s = ci.connectorStatuses self.assertEqual(set(s.keys()), set([bad1, bad2, bad3])) self.assertEqual(s[bad1], "bad hint: no colon") self.assertEqual(s[bad2], "bad hint: no handler registered") self.assertIn("DNS lookup failed", s[bad3]) ch = ci.connectionHandlers self.assertEqual(ch, {bad2: None, bad3: "tcp"})
def test_wait_for_brokers(self): """ The L{Deferred} returned by L{Tub.stopService} fires only after the L{Broker} connections belonging to the L{Tub} have disconnected. """ tub = Tub() tub.startService() another_tub = Tub() another_tub.startService() brokers = list(tub.brokerClass(None) for i in range(3)) for n, b in enumerate(brokers): b.makeConnection(StringTransport()) ref = SturdyRef(encode_furl(another_tub.tubID, [], str(n))) tub.brokerAttached(ref, b, isClient=(n % 2) == 1) stopping = tub.stopService() d = flushEventualQueue() def event(ignored): self.assertNoResult(stopping) for b in brokers: b.connectionLost(failure.Failure(Exception("Connection lost"))) return flushEventualQueue() d.addCallback(event) def connectionsLost(ignored): self.successResultOf(stopping) d.addCallback(connectionsLost) return d
def test_unreachable_client(self): # A "client-only" Tub has no location set. It should still be # possible to connect to objects in other (location-bearing) server # Tubs, and objects in the client Tub can still be sent to (and used # by) the server Tub. client_tub = Tub() client_tub.setServiceParent(self.s) server_tub = Tub() server_tub.setServiceParent(self.s) portnum = allocate_tcp_port() server_tub.listenOn("tcp:%d:interface=127.0.0.1" % portnum) server_tub.setLocation("tcp:127.0.0.1:%d" % portnum) s = Receiver() # no FURL, not directly reachable r = Receiver() furl = server_tub.registerReference(r) d = client_tub.getReference(furl) d.addCallback(lambda rref: rref.callRemote("call", s)) d.addCallback(lambda res: self.assertEqual(res, 1)) d.addCallback(lambda _: self.assertNotEqual(r.obj, None)) def _inspect_obj(_): self.assertEqual(r.obj.getSturdyRef().getURL(), None) d.addCallback(_inspect_obj) d.addCallback(lambda _: r.obj.callRemote("call", 2)) d.addCallback(lambda _: self.assertEqual(s.obj, 2)) return d
def test_unreachable_gift(self): client_tub = Tub() client_tub.setServiceParent(self.s) server_tub = Tub() server_tub.setServiceParent(self.s) recipient_tub = Tub() recipient_tub.setServiceParent(self.s) portnum = allocate_tcp_port() server_tub.listenOn("tcp:%d:interface=127.0.0.1" % portnum) server_tub.setLocation("tcp:127.0.0.1:%d" % portnum) s = Receiver() # no FURL, not directly reachable r = Receiver() furl = server_tub.registerReference(r) d = client_tub.getReference(furl) d.addCallback(lambda rref: rref.callRemote("call", s)) d.addCallback(lambda res: self.assertEqual(res, 1)) d.addCallback(lambda _: recipient_tub.getReference(furl)) # when server_tub tries to send the lame 's' rref to recipient_tub, # the RemoteReferenceTracker won't have a FURL, so it will be # serialized as a (their-reference furl="") sequence. Then # recipient_tub will try to resolve it, and will throw a # NoLocationHintsError. It might be more natural to send # (their-reference furl=None), but the constraint schema on # their-references forbids non-strings. It might also seem # appropriate to raise a Violation (i.e. server_tub is bad for trying # to send it, rather than foisting the problem off to recipient_tub), # but that causes the connection explode and fall out of sync. d.addCallback(lambda rref: self.shouldFail( NoLocationHintsError, "gift_me", None, rref.callRemote, "gift_me")) return d
def test_cancel_pending_deliveries(self): # when a Tub is stopped, any deliveries that were pending should be # discarded. TubA sends remote_one+remote_two (and we hope they # arrive in the same chunk). TubB responds to remote_one by shutting # down. remote_two should be discarded. The bug was that remote_two # would cause an unhandled error on the TubB side. self.tubA = Tub() self.tubB = Tub() self.tubA.startService() self.tubB.startService() self.tubB.listenOn("tcp:0") d = self.tubB.setLocationAutomatically() r = Receiver(self.tubB) d.addCallback(lambda res: self.tubB.registerReference(r)) d.addCallback(lambda furl: self.tubA.getReference(furl)) def _go(rref): # we want these two to get sent and received in the same hunk rref.callRemoteOnly("one") rref.callRemoteOnly("two") return r.done_d d.addCallback(_go) # let remote_two do its log.err before we move on to the next test d.addCallback(self.stall, 1.0) return d
def setUp(self): self.services = [Tub(), Tub()] self.tubA, self.tubB = self.services for s in self.services: s.startService() l = s.listenOn("tcp:0:interface=127.0.0.1") s.setLocation("127.0.0.1:%d" % l.getPortnum())
def test_referenceable(self): t1 = Tub() t1.setServiceParent(self.s) l = t1.listenOn("tcp:0:interface=127.0.0.1") t1.setLocation("127.0.0.1:%d" % l.getPortnum()) r1 = Referenceable() # the serialized blob can't keep the reference alive, so you must # arrange for that separately t1.registerReference(r1) t2 = Tub() t2.setServiceParent(self.s) obj = ("graph tangly", r1) d = t1.serialize(obj) del r1 del obj def _done(data): self.failUnless("their-reference" in data) return data d.addCallback(_done) d.addCallback(lambda data: t2.unserialize(data)) def _check(obj2): self.failUnlessEqual(obj2[0], "graph tangly") self.failUnless(isinstance(obj2[1], RemoteReference)) d.addCallback(_check) return d
def setUp(self): self.s = service.MultiService() self.s.startService() self.target_tub = Tub() self.target_tub.setServiceParent(self.s) l = self.target_tub.listenOn("tcp:0:interface=127.0.0.1") self.target_tub.setLocation("127.0.0.1:%d" % l.getPortnum()) self.source_tub = Tub() self.source_tub.setServiceParent(self.s)
def test_certfile(self): fn = "test_tub.TestCertFile.certfile" t1 = Tub(certFile=fn) self.failUnless(os.path.exists(fn)) data1 = t1.getCertData() t2 = Tub(certFile=fn) data2 = t2.getCertData() self.failUnless(data1 == data2)
def setUp(self): self.services = [Tub(), Tub()] self.tubA, self.tubB = self.services self.tub_ports = [] for s in self.services: s.startService() l = s.listenOn("tcp:0:interface=127.0.0.1") s.setLocation("127.0.0.1:%d" % l.getPortnum()) self.tub_ports.append(l.getPortnum()) self._log_observers_to_remove = []
def makeTub(self, hint_type): tubA = Tub(certData=certData_low) tubA.setServiceParent(self.s) tubB = Tub(certData=certData_high) tubB.setServiceParent(self.s) portnum = util.allocate_tcp_port() tubA.listenOn("tcp:%d:interface=127.0.0.1" % portnum) tubA.setLocation("%s:127.0.0.1:%d" % (hint_type, portnum)) furl = tubA.registerReference(Target()) return furl, tubB
def new_tahoe_configuration(deploy_config, bucketname, key_prefix, publichost, privatehost, introducer_port, storageserver_port): """ Create brand new secrets and configuration for use by an introducer/storage pair. """ base_name = dict( organizationName=b"Least Authority Enterprises", organizationalUnitName=b"S4", emailAddress=bucketname, ) keypair = KeyPair.generate(size=2048) introducer_certificate = keypair.selfSignedCert( serialNumber=1, commonName=b"introducer", **base_name ) storage_certificate = keypair.selfSignedCert( serialNumber=1, commonName=b"storage", **base_name ) def pem(key, cert): return b"\n".join((key.dump(FILETYPE_PEM), cert.dump(FILETYPE_PEM))) introducer_tub = Tub(certData=pem(keypair, introducer_certificate)) introducer_tub.setLocation("{}:{}".format(publichost, introducer_port)) storage_tub = Tub(certData=pem(keypair, storage_certificate)) return marshal_tahoe_configuration( introducer_pem=introducer_tub.getCertData().strip(), storage_pem=storage_tub.getCertData().strip(), storage_privkey=keyutil.make_keypair()[0] + b"\n", introducer_port=introducer_port, storageserver_port=storageserver_port, bucket_name=bucketname, key_prefix=key_prefix, publichost=publichost, privatehost=privatehost, # The object of the reference is irrelevant. The furl will # get hooked up to something else when Tahoe really runs. # Just need to pass something _weak referenceable_! Which # rules out a lot of things... introducer_furl=introducer_tub.registerReference(introducer_tub), s3_access_key_id=deploy_config.s3_access_key_id, s3_secret_key=deploy_config.s3_secret_key, log_gatherer_furl=deploy_config.log_gatherer_furl, stats_gatherer_furl=deploy_config.stats_gatherer_furl, )
def setUp(self): s0, s1 = self.services = [Tub(), Tub()] s0.brokerClass = PingCountingBroker s1.brokerClass = PingCountingBroker s0.startService() s1.startService() l = s0.listenOn("tcp:0:interface=127.0.0.1") s0.setLocation("127.0.0.1:%d" % l.getPortnum()) self.target = TargetWithoutInterfaces() public_url = s0.registerReference(self.target, "target") self.public_url = public_url
def makeServers(self, t1opts={}, t2opts={}, lo1={}, lo2={}, tubAauthenticated=True, tubBauthenticated=True): if tubAauthenticated or tubBauthenticated: self.requireCrypto() # first we create two Tubs if tubAauthenticated: a = Tub(options=t1opts) else: a = UnauthenticatedTub(options=t1opts) if tubBauthenticated: b = Tub(options=t1opts) else: b = UnauthenticatedTub(options=t1opts) # then we figure out which one will be the master, and call it tub1 if a.tubID > b.tubID: # a is the master tub1, tub2 = a, b else: tub1, tub2 = b, a if not self.tub1IsMaster: tub1, tub2 = tub2, tub1 self.tub1 = tub1 self.tub2 = tub2 # now fix up the options and everything else self.tub1phases = [] t1opts['debug_gatherPhases'] = self.tub1phases tub1.options = t1opts self.tub2phases = [] t2opts['debug_gatherPhases'] = self.tub2phases tub2.options = t2opts # connection[0], the winning connection, will be from tub1 to tub2 tub1.startService() self.services.append(tub1) l1 = tub1.listenOn("tcp:0", lo1) tub1.setLocation("127.0.0.1:%d" % l1.getPortnum()) self.target1 = Target() self.url1 = tub1.registerReference(self.target1) # connection[1], the abandoned connection, will be from tub2 to tub1 tub2.startService() self.services.append(tub2) l2 = tub2.listenOn("tcp:0", lo2) tub2.setLocation("127.0.0.1:%d" % l2.getPortnum()) self.target2 = Target() self.url2 = tub2.registerReference(self.target2)
def setUp(self): TargetMixin.setUp(self) self.tubA, self.tubB = [Tub(), Tub()] self.services = [self.tubA, self.tubB] self.tubA.startService() self.tubB.startService() l = self.tubB.listenOn("tcp:0:interface=127.0.0.1") self.tubB.setLocation("127.0.0.1:%d" % l.getPortnum()) self.url_on_b = self.tubB.registerReference(Referenceable()) self.lookups = [] self.lookups2 = [] self.names = {} self.names2 = {}
def create_tub(tub_options, default_connection_handlers, foolscap_connection_handlers, handler_overrides={}, **kwargs): """ Create a Tub with the right options and handlers. It will be ephemeral unless the caller provides certFile= in kwargs :param handler_overrides: anything in this will override anything in `default_connection_handlers` for just this call. :param dict tub_options: every key-value pair in here will be set in the new Tub via `Tub.setOption` """ tub = Tub(**kwargs) for (name, value) in list(tub_options.items()): tub.setOption(name, value) handlers = default_connection_handlers.copy() handlers.update(handler_overrides) tub.removeAllConnectionHintHandlers() for hint_type, handler_name in list(handlers.items()): handler = foolscap_connection_handlers.get(handler_name) if handler: tub.addConnectionHintHandler(hint_type, handler) return tub
def test_set_location(self): t = Tub() t.listenOn("tcp:0") t.setServiceParent(self.s) t.setLocation("127.0.0.1:12345") # setLocation may only be called once self.failUnlessRaises(PBError, t.setLocation, "127.0.0.1:12345")
def test_retry(self): tubC = Tub(certData=self.tubB.getCertData()) connects = [] target = HelperTarget("bob") url = self.tubB.registerReference(target, "target") portb = self.tub_ports[1] d1 = defer.Deferred() notifiers = [d1] self.services.remove(self.tubB) # This will fail, since tubB is not listening anymore. Wait until it's # moved to the "waiting" state. yield self.tubB.stopService() rc = self.tubA.connectTo(url, self._connected, notifiers, connects) yield self.poll(lambda: rc.getReconnectionInfo().state == "waiting") self.failUnlessEqual(len(connects), 0) # now start tubC listening on the same port that tubB used to, which # should allow the connection to complete (since they both use the same # certData) self.services.append(tubC) tubC.startService() tubC.listenOn("tcp:%d:interface=127.0.0.1" % portb) tubC.setLocation("tcp:127.0.0.1:%d" % portb) url2 = tubC.registerReference(target, "target") assert url2 == url yield d1 self.failUnlessEqual(len(connects), 1) rc.stopConnecting()
def do_remote_command(command, *args, **kwargs): client = AnyConsensoProcess() client.start() furl = client.furl() tub = Tub() tub.startService() def got_error(err): print "Error while calling command remotely", err reactor.stop() def got_result(res): print(res) def got_remote(remote): d = remote.callRemote(command, *args, **kwargs) d.addCallback(got_result) d.addCallback(lambda res: reactor.stop()) d.addErrback(got_error) return d d = tub.getReference(furl) d.addCallback(got_remote) d.addErrback(got_error) reactor.run()
def startService(self): service.MultiService.startService(self) certFile = os.path.join(self.basedir, "gatherer.pem") self._tub = Tub(certFile=certFile) self._tub.setServiceParent(self) local_addresses = ["127.0.0.1"] local_address = get_local_ip_for() if self.use_local_addresses and local_address: local_addresses.insert(0, local_address) portnumfile = os.path.join(self.basedir, "portnum") try: desired_portnum = int(open(portnumfile, "r").read()) except (EnvironmentError, ValueError): desired_portnum = 0 l = self._tub.listenOn("tcp:%d" % desired_portnum) got_portnum = l.getPortnum() f = open(portnumfile, "w") f.write("%d\n" % got_portnum) f.close() # we can't do setLocation until we have two things: # portnum: this requires listenOn, if portnum=0 also startService # localip: this just requires a call to get_local_ip_for # so it is safe to do everything in startService, after the upcall local_addresses = [ "%s:%d" % (addr, got_portnum,) for addr in local_addresses ] assert len(local_addresses) >= 1 location = ",".join(local_addresses) self._tub.setLocation(location) self.tub_ready()
def test_failure(self): self.basedir = "introducer/NonV1Server/failure" os.makedirs(self.basedir) self.create_tub() i = TooNewServer() i.setServiceParent(self.parent) self.introducer_furl = self.central_tub.registerReference(i) tub = Tub() tub.setOption("expose-remote-exception-types", False) tub.setServiceParent(self.parent) listenOnUnused(tub) c = IntroducerClient(tub, self.introducer_furl, u"nickname-client", "version", "oldest", fakeseq, FilePath(self.mktemp())) announcements = {} def got(key_s, ann): announcements[key_s] = ann c.subscribe_to("storage", got) c.setServiceParent(self.parent) # now we wait for it to connect and notice the bad version def _got_bad(): return bool(c._introducer_error) or bool(c._publisher) d = self.poll(_got_bad) def _done(res): self.failUnless(c._introducer_error) self.failUnless(c._introducer_error.check(InsufficientVersionError), c._introducer_error) d.addCallback(_done) return d
def test_tubid_check(self): t1 = Tub() # gets a new key t1.setServiceParent(self.s) l = t1.listenOn("tcp:0:interface=127.0.0.1") t1.setLocation("127.0.0.1:%d" % l.getPortnum()) port1 = "tcp:%d:interface=127.0.0.1" % l.getPortnum() r1 = Referenceable() ffn = "test_tub.FurlFile.test_tubid_check.furlfile" furl1 = t1.registerReference(r1, furlFile=ffn) d = defer.maybeDeferred(t1.disownServiceParent) self.failUnless(os.path.exists(ffn)) self.failUnlessEqual(furl1, open(ffn, "r").read().strip()) def _take2(res): t2 = Tub() # gets a different key t2.setServiceParent(self.s) l = t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % l.getPortnum()) r2 = Referenceable() self.failUnlessRaises(WrongTubIdError, t2.registerReference, r2, furlFile=ffn) return t2.disownServiceParent() d.addCallback(_take2) return d
def test_furlfile(self): cfn = "test_tub.FurlFile.test_furlfile.certfile" t1 = Tub(certFile=cfn) t1.setServiceParent(self.s) l = t1.listenOn("tcp:0:interface=127.0.0.1") t1.setLocation("127.0.0.1:%d" % l.getPortnum()) port1 = "tcp:%d:interface=127.0.0.1" % l.getPortnum() r1 = Referenceable() ffn = "test_tub.FurlFile.test_furlfile.furlfile" furl1 = t1.registerReference(r1, furlFile=ffn) d = defer.maybeDeferred(t1.disownServiceParent) self.failUnless(os.path.exists(ffn)) self.failUnlessEqual(furl1, open(ffn, "r").read().strip()) def _take2(res): t2 = Tub(certFile=cfn) t2.setServiceParent(self.s) l = t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % l.getPortnum()) r2 = Referenceable() furl2 = t2.registerReference(r2, furlFile=ffn) self.failUnlessEqual(furl1, furl2) return t2.disownServiceParent() d.addCallback(_take2) return d
def __init__(self, basedir=".", verbose=False): service.MultiService.__init__(self) self.basedir = basedir self.tub = Tub( certFile=os.path.join(self.basedir, "stats_gatherer.pem")) self.tub.setServiceParent(self) self.tub.setOption("logLocalFailures", True) self.tub.setOption("logRemoteFailures", True) self.tub.setOption("expose-remote-exception-types", False) self.stats_gatherer = JSONStatsGatherer(self.basedir, verbose) self.stats_gatherer.setServiceParent(self) try: with open(os.path.join(self.basedir, "location")) as f: location = f.read().strip() except EnvironmentError: raise ValueError( "Unable to find 'location' in BASEDIR, please rebuild your stats-gatherer" ) try: with open(os.path.join(self.basedir, "port")) as f: port = f.read().strip() except EnvironmentError: raise ValueError( "Unable to find 'port' in BASEDIR, please rebuild your stats-gatherer" ) self.tub.listenOn(port) self.tub.setLocation(location) ff = os.path.join(self.basedir, self.furl_file) self.gatherer_furl = self.tub.registerReference(self.stats_gatherer, furlFile=ff)
def run_command(config): c = dispatch_table[config.subCommand]() tub = Tub() try: from twisted.internet import reactor from twisted.internet.endpoints import clientFromString from foolscap.connections import tor CONTROL = os.environ.get("FOOLSCAP_TOR_CONTROL_PORT", "") SOCKS = os.environ.get("FOOLSCAP_TOR_SOCKS_PORT", "") if CONTROL: h = tor.control_endpoint(clientFromString(reactor, CONTROL)) tub.addConnectionHintHandler("tor", h) elif SOCKS: h = tor.socks_endpoint(clientFromString(reactor, SOCKS)) tub.addConnectionHintHandler("tor", h) #else: # h = tor.default_socks() # tub.addConnectionHintHandler("tor", h) except ImportError: pass d = defer.succeed(None) d.addCallback(lambda _ign: tub.startService()) d.addCallback(lambda _ign: tub.getReference(config.furl)) d.addCallback(c.run, config.subOptions) # might provide tub here d.addBoth(lambda res: tub.stopService().addCallback(lambda _ign: res)) return d
def test_unreachable(self): t = Tub() t.setServiceParent(self.s) # we call neither .listenOn nor .setLocation self.assertEqual(t.locationHints, []) self.assertRaises(NoLocationError, t.registerReference, Referenceable())
def testAuthenticated(self): url, portnum = self.makeServer() client = Tub() client.startService() self.services.append(client) d = client.getReference(url) return d
def create_tub(self): certfile = os.path.join(self.basedir, "private", self.CERTFILE) self.tub = Tub(certFile=certfile) self.tub.setOption("logLocalFailures", True) self.tub.setOption("logRemoteFailures", True) self.tub.setOption("expose-remote-exception-types", False) # see #521 for a discussion of how to pick these timeout values. keepalive_timeout_s = self.get_config("node", "timeout.keepalive", "") if keepalive_timeout_s: self.tub.setOption("keepaliveTimeout", int(keepalive_timeout_s)) disconnect_timeout_s = self.get_config("node", "timeout.disconnect", "") if disconnect_timeout_s: # N.B.: this is in seconds, so use "1800" to get 30min self.tub.setOption("disconnectTimeout", int(disconnect_timeout_s)) self.nodeid = b32decode(self.tub.tubID.upper()) # binary format self.write_config("my_nodeid", b32encode(self.nodeid).lower() + "\n") self.short_nodeid = b32encode(self.nodeid).lower()[:8] # ready for printing tubport = self.get_config("node", "tub.port", "tcp:0") self.tub.listenOn(tubport) # we must wait until our service has started before we can find out # our IP address and thus do tub.setLocation, and we can't register # any services with the Tub until after that point self.tub.setServiceParent(self)
def makeTub(self, hint_type, listener_test_options={}, extra_hint=None): tubA = Tub(certData=certData_low) tubA.setServiceParent(self.s) tubB = Tub(certData=certData_high) tubB.setServiceParent(self.s) self._tubA, self._tubB = tubA, tubB portnum = util.allocate_tcp_port() self._portnum = portnum port = "tcp:%d:interface=127.0.0.1" % portnum hint = "%s:127.0.0.1:%d" % (hint_type, portnum) if extra_hint: hint = hint + "," + extra_hint tubA.listenOn(port, _test_options=listener_test_options) tubA.setLocation(hint) self._target = Target() furl = tubA.registerReference(self._target) return furl, tubB, hint