]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/introducer/server.py
remove introducer's set_encoding_parameters
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / introducer / server.py
index 41f92b23673333845c5ef5da59b681ec655b8132..598325ae624a8dcc010b43c0f8fa888516b8fb84 100644 (file)
@@ -1,23 +1,27 @@
 
-import time, os.path
+import time, os.path, textwrap
 from zope.interface import implements
 from twisted.application import service
 from foolscap.api import Referenceable
 import allmydata
 from allmydata import node
-from allmydata.util import log
+from allmydata.util import log, rrefutil
+from allmydata.util.fileutil import abspath_expanduser_unicode
 from allmydata.introducer.interfaces import \
      RIIntroducerPublisherAndSubscriberService_v2
 from allmydata.introducer.common import convert_announcement_v1_to_v2, \
      convert_announcement_v2_to_v1, unsign_from_foolscap, make_index, \
-     get_tubid_string_from_ann
+     get_tubid_string_from_ann, SubscriberDescriptor, AnnouncementDescriptor
+
+class FurlFileConflictError(Exception):
+    pass
 
 class IntroducerNode(node.Node):
     PORTNUMFILE = "introducer.port"
     NODETYPE = "introducer"
     GENERATED_FILES = ['introducer.furl']
 
-    def __init__(self, basedir="."):
+    def __init__(self, basedir=u"."):
         node.Node.__init__(self, basedir)
         self.read_config()
         self.init_introducer()
@@ -29,13 +33,27 @@ class IntroducerNode(node.Node):
         introducerservice = IntroducerService(self.basedir)
         self.add_service(introducerservice)
 
+        old_public_fn = os.path.join(self.basedir, u"introducer.furl")
+        private_fn = os.path.join(self.basedir, u"private", u"introducer.furl")
+
+        if os.path.exists(old_public_fn):
+            if os.path.exists(private_fn):
+                msg = """This directory (%s) contains both an old public
+                'introducer.furl' file, and a new-style
+                'private/introducer.furl', so I cannot safely remove the old
+                one. Please make sure your desired FURL is in
+                private/introducer.furl, and remove the public file. If this
+                causes your Introducer's FURL to change, you need to inform
+                all grid members so they can update their tahoe.cfg.
+                """
+                raise FurlFileConflictError(textwrap.dedent(msg))
+            os.rename(old_public_fn, private_fn)
         d = self.when_tub_ready()
         def _publish(res):
-            self.introducer_url = self.tub.registerReference(introducerservice,
-                                                             "introducer")
-            self.log(" introducer is at %s" % self.introducer_url,
-                     umid="qF2L9A")
-            self.write_config("introducer.furl", self.introducer_url + "\n")
+            furl = self.tub.registerReference(introducerservice,
+                                              furlFile=private_fn)
+            self.log(" introducer is at %s" % furl, umid="qF2L9A")
+            self.introducer_url = furl # for tests
         d.addCallback(_publish)
         d.addErrback(log.err, facility="tahoe.init",
                      level=log.BAD, umid="UaNs9A")
@@ -44,9 +62,9 @@ class IntroducerNode(node.Node):
         self.log("init_web(webport=%s)", args=(webport,), umid="2bUygA")
 
         from allmydata.webish import IntroducerWebishServer
-        nodeurl_path = os.path.join(self.basedir, "node.url")
-        staticdir = self.get_config("node", "web.static", "public_html")
-        staticdir = os.path.expanduser(staticdir)
+        nodeurl_path = os.path.join(self.basedir, u"node.url")
+        config_staticdir = self.get_config("node", "web.static", "public_html").decode('utf-8')
+        staticdir = abspath_expanduser_unicode(config_staticdir, base=self.basedir)
         ws = IntroducerWebishServer(self, webport, nodeurl_path, staticdir)
         self.add_service(ws)
 
@@ -56,7 +74,7 @@ class WrapV1SubscriberInV2Interface: # for_v1
     """
 
     def __init__(self, original):
-        self.original = original
+        self.original = original # also used for tests
     def __eq__(self, them):
         return self.original == them
     def __ne__(self, them):
@@ -69,15 +87,14 @@ class WrapV1SubscriberInV2Interface: # for_v1
         return self.original.getSturdyRef()
     def getPeer(self):
         return self.original.getPeer()
+    def getLocationHints(self):
+        return self.original.getLocationHints()
     def callRemote(self, methname, *args, **kwargs):
         m = getattr(self, "wrap_" + methname)
         return m(*args, **kwargs)
     def wrap_announce_v2(self, announcements):
         anns_v1 = [convert_announcement_v2_to_v1(ann) for ann in announcements]
         return self.original.callRemote("announce", set(anns_v1))
-    def wrap_set_encoding_parameters(self, parameters):
-        # note: unused
-        return self.original.callRemote("set_encoding_parameters", parameters)
     def notifyOnDisconnect(self, *args, **kwargs):
         return self.original.notifyOnDisconnect(*args, **kwargs)
 
@@ -118,6 +135,8 @@ class IntroducerService(service.MultiService, Referenceable):
 
         self._debug_counts = {"inbound_message": 0,
                               "inbound_duplicate": 0,
+                              "inbound_no_seqnum": 0,
+                              "inbound_old_replay": 0,
                               "inbound_update": 0,
                               "outbound_message": 0,
                               "outbound_announcements": 0,
@@ -133,16 +152,40 @@ class IntroducerService(service.MultiService, Referenceable):
             kwargs["facility"] = "tahoe.introducer.server"
         return log.msg(*args, **kwargs)
 
-    def get_announcements(self):
-        return self._announcements
+    def get_announcements(self, include_stub_clients=True):
+        """Return a list of AnnouncementDescriptor for all announcements"""
+        announcements = []
+        for (index, (_, canary, ann, when)) in self._announcements.items():
+            if ann["service-name"] == "stub_client":
+                if not include_stub_clients:
+                    continue
+            ad = AnnouncementDescriptor(when, index, canary, ann)
+            announcements.append(ad)
+        return announcements
+
     def get_subscribers(self):
-        """Return a list of (service_name, when, subscriber_info, rref) for
-        all subscribers. subscriber_info is a dict with the following keys:
-        version, nickname, app-versions, my-version, oldest-supported"""
+        """Return a list of SubscriberDescriptor objects for all subscribers"""
         s = []
         for service_name, subscriptions in self._subscribers.items():
             for rref,(subscriber_info,when) in subscriptions.items():
-                s.append( (service_name, when, subscriber_info, rref) )
+                # note that if the subscriber didn't do Tub.setLocation,
+                # tubid will be None. Also, subscribers do not tell us which
+                # pubkey they use; only publishers do that.
+                tubid = rref.getRemoteTubID() or "?"
+                remote_address = rrefutil.stringify_remote_address(rref)
+                # these three assume subscriber_info["version"]==0, but
+                # should tolerate other versions
+                if not subscriber_info:
+                     # V1 clients that haven't yet sent their stub_info data
+                    subscriber_info = {}
+                nickname = subscriber_info.get("nickname", u"?")
+                version = subscriber_info.get("my-version", u"?")
+                app_versions = subscriber_info.get("app-versions", {})
+                # 'when' is the time they subscribed
+                sd = SubscriberDescriptor(service_name, when,
+                                          nickname, version, app_versions,
+                                          remote_address, tubid)
+                s.append(sd)
         return s
 
     def remote_get_version(self):
@@ -188,6 +231,22 @@ class IntroducerService(service.MultiService, Referenceable):
                 self._debug_counts["inbound_duplicate"] += 1
                 return
             else:
+                if "seqnum" in old_ann:
+                    # must beat previous sequence number to replace
+                    if ("seqnum" not in ann
+                        or not isinstance(ann["seqnum"], (int,long))):
+                        self.log("not replacing old ann, no valid seqnum",
+                                 level=log.NOISY, umid="ySbaVw")
+                        self._debug_counts["inbound_no_seqnum"] += 1
+                        return
+                    if ann["seqnum"] <= old_ann["seqnum"]:
+                        self.log("not replacing old ann, new seqnum is too old"
+                                 " (%s <= %s) (replay attack?)"
+                                 % (ann["seqnum"], old_ann["seqnum"]),
+                                 level=log.UNUSUAL, umid="sX7yqQ")
+                        self._debug_counts["inbound_old_replay"] += 1
+                        return
+                    # ok, seqnum is newer, allow replacement
                 self.log("old announcement being updated", level=log.NOISY,
                          umid="304r9g")
                 self._debug_counts["inbound_update"] += 1