Example #1
0
 def prepopulateRings(self):
     # populate all rings (for dumping assignments and testing)
     for filterFn in [filterBridgesByIP4, filterBridgesByIP6]:
         ruleset = frozenset([filterFn])
         key1 = getHMAC(self.splitter.key, "Order-Bridges-In-Ring")
         ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
         self.splitter.addRing(ring, ruleset,
                               filterBridgesByRules([filterFn]),
                               populate_from=self.splitter.bridges)
Example #2
0
    def prepopulateRings(self):
        logging.info("Prepopulating %s distributor hashrings..." % self.name)
        # populate all rings (for dumping assignments and testing)
        for filterFn in [None, filterBridgesByIP4, filterBridgesByIP6]:
            n = self.nClusters
            for category in self.categories:
                g = filterAssignBridgesToRing(self.splitter.hmac,
                                              self.nClusters +
                                              len(self.categories),
                                              n)
                bridgeFilterRules = [g]
                if filterFn:
                    bridgeFilterRules.append(filterFn)
                ruleset = frozenset(bridgeFilterRules)
                key1 = getHMAC(self.splitter.key,
                               "Order-Bridges-In-Ring-%d" % n)
                n += 1
                ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
                ring.setName('{0} Ring'.format(self.name))
                self.splitter.addRing(ring,
                                      ruleset,
                                      filterBridgesByRules(bridgeFilterRules),
                                      populate_from=self.splitter.bridges)


            # populate all ip clusters
            for clusterNum in xrange(self.nClusters):
                g = filterAssignBridgesToRing(self.splitter.hmac,
                                              self.nClusters +
                                              len(self.categories),
                                              clusterNum)
                bridgeFilterRules = [g]
                if filterFn:
                    bridgeFilterRules.append(filterFn)
                ruleset = frozenset(bridgeFilterRules)
                key1 = getHMAC(self.splitter.key,
                               "Order-Bridges-In-Ring-%d" % clusterNum)
                ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
                self.splitter.addRing(ring,
                                      ruleset,
                                      filterBridgesByRules(bridgeFilterRules),
                                      populate_from=self.splitter.bridges)
Example #3
0
    def prepopulateRings(self):
        # populate all rings (for dumping assignments and testing)
        for filterFn in [None, filterBridgesByIP4, filterBridgesByIP6]:
            n = self.nClusters
            for category in self.categories:
                g = filterAssignBridgesToRing(
                    self.splitter.hmac, self.nClusters + len(self.categories),
                    n)
                bridgeFilterRules = [g]
                if filterFn:
                    bridgeFilterRules.append(filterFn)
                ruleset = frozenset(bridgeFilterRules)
                key1 = bridgedb.Bridges.get_hmac(
                    self.splitter.key, "Order-Bridges-In-Ring-%d" % n)
                n += 1
                ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
                self.splitter.addRing(ring,
                                      ruleset,
                                      filterBridgesByRules(bridgeFilterRules),
                                      populate_from=self.splitter.bridges)

            # populate all ip clusters
            for clusterNum in xrange(self.nClusters):
                g = filterAssignBridgesToRing(
                    self.splitter.hmac, self.nClusters + len(self.categories),
                    clusterNum)
                bridgeFilterRules = [g]
                if filterFn:
                    bridgeFilterRules.append(filterFn)
                ruleset = frozenset(bridgeFilterRules)
                key1 = bridgedb.Bridges.get_hmac(
                    self.splitter.key, "Order-Bridges-In-Ring-%d" % clusterNum)
                ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
                self.splitter.addRing(ring,
                                      ruleset,
                                      filterBridgesByRules(bridgeFilterRules),
                                      populate_from=self.splitter.bridges)
Example #4
0
    def prepopulateRings(self):
        # populate all rings (for dumping assignments and testing)
        for filterFn in [None, filterBridgesByIP4, filterBridgesByIP6]:
            n = self.nClusters
            for category in self.categories:
                g = filterAssignBridgesToRing(self.splitter.hmac,
                        self.nClusters +
                        len(self.categories),
                        n)
                bridgeFilterRules = [g]
                if filterFn:
                    bridgeFilterRules.append(filterFn)
                ruleset = frozenset(bridgeFilterRules)
                key1 = bridgedb.Bridges.get_hmac(self.splitter.key,
                        "Order-Bridges-In-Ring-%d"%n) 
                n += 1
                ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
                self.splitter.addRing(ring, ruleset, filterBridgesByRules(bridgeFilterRules),
                                      populate_from=self.splitter.bridges)


            # populate all ip clusters
            for clusterNum in xrange(self.nClusters):
                g = filterAssignBridgesToRing(self.splitter.hmac,
                                              self.nClusters +
                                              len(self.categories),
                                              clusterNum) 
                bridgeFilterRules = [g]
                if filterFn:
                    bridgeFilterRules.append(filterFn)
                ruleset = frozenset(bridgeFilterRules)
                key1 = bridgedb.Bridges.get_hmac(self.splitter.key,
                                                 "Order-Bridges-In-Ring-%d"%clusterNum) 
                ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
                self.splitter.addRing(ring, ruleset, filterBridgesByRules(bridgeFilterRules),
                                      populate_from=self.splitter.bridges)
Example #5
0
    def getBridgesForEmail(self, emailaddress, epoch, N=1, parameters=None,
                           countryCode=None, bridgeFilterRules=None):
        """Return a list of bridges to give to a user.

        :param str emailaddress: The user's email address, as given in a
            :header:`From:` line.
        :param epoch: The time period when we got this request. This can be
            any string, so long as it changes with every period.
        :param int N: The number of bridges to try to give back.
        :param parameters: DOCDOC
        :param countryCode: DOCDOC
        :param bridgeFilterRules: DOCDOC
        """
        if not bridgeFilterRules:
            bridgeFilterRules=[]
        now = time.time()

        # All checks on the email address, such as checks for whitelisting and
        # canonicalization of domain name, are done in
        # :meth:`bridgedb.email.autoresponder.getMailTo` and
        # :meth:`bridgedb.email.autoresponder.SMTPAutoresponder.runChecks`.
        if not emailaddress:
            logging.error(("%s distributor can't get bridges for blank email "
                           "address!") % (self.name, emailaddress))
            return []

        with bridgedb.Storage.getDB() as db:
            wasWarned = db.getWarnedEmail(emailaddress)
            lastSaw = db.getEmailTime(emailaddress)

            logging.info("Attempting to return for %d bridges for %s..."
                         % (N, emailaddress))

            if lastSaw is not None:
                if emailaddress in self.whitelist.keys():
                    logging.info(("Whitelisted email address %s was last seen "
                                  "%d seconds ago.")
                                 % (emailaddress, now - lastSaw))
                elif (lastSaw + MAX_EMAIL_RATE) >= now:
                    wait = (lastSaw + MAX_EMAIL_RATE) - now
                    logging.info("Client %s must wait another %d seconds."
                                 % (emailaddress, wait))
                    if wasWarned:
                        raise IgnoreEmail("Client was warned.", emailaddress)
                    else:
                        logging.info("Sending duplicate request warning.")
                        db.setWarnedEmail(emailaddress, True, now)
                        db.commit()
                        raise TooSoonEmail("Must wait %d seconds" % wait,
                                           emailaddress)

            # warning period is over
            elif wasWarned:
                db.setWarnedEmail(emailaddress, False)

            pos = self.emailHmac("<%s>%s" % (epoch, emailaddress))

            ring = None
            ruleset = frozenset(bridgeFilterRules)
            if ruleset in self.splitter.filterRings.keys():
                logging.debug("Cache hit %s" % ruleset)
                _, ring = self.splitter.filterRings[ruleset]
            else:
                # cache miss, add new ring
                logging.debug("Cache miss %s" % ruleset)

                # add new ring
                key1 = getHMAC(self.splitter.key,
                                                 "Order-Bridges-In-Ring")
                ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
                # debug log: cache miss
                self.splitter.addRing(ring, ruleset,
                                      filterBridgesByRules(bridgeFilterRules),
                                      populate_from=self.splitter.bridges)

            numBridgesToReturn = getNumBridgesPerAnswer(ring,
                                                        max_bridges_per_answer=N)
            result = ring.getBridges(pos, numBridgesToReturn)

            db.setEmailTime(emailaddress, now)
            db.commit()

        return result
Example #6
0
    def getBridgesForIP(self, ip, epoch, N=1, countryCode=None,
                        bridgeFilterRules=None):
        """Return a list of bridges to give to a user.

        :param str ip: The user's IP address, as a dotted quad.
        :param str epoch: The time period when we got this request.  This can
                          be any string, so long as it changes with every
                          period.
        :param int N: The number of bridges to try to give back. (default: 1)
        :param str countryCode: The two-letter geoip country code of the
            client's IP address. If given, it is assumed that any bridges
            distributed to that client should not be blocked in that
            country. (default: None)
        :param list bridgeFilterRules: A list of callables used filter the
                                       bridges returned in the response to the
                                       client. See :mod:`~bridgedb.Filters`.
        :rtype: list
        :return: A list of :class:`~bridgedb.Bridges.Bridge`s to include in
                 the response. See
                 :meth:`bridgedb.HTTPServer.WebResource.getBridgeRequestAnswer`
                 for an example of how this is used.
        """
        logging.info("Attempting to return %d bridges to client %s..."
                     % (N, ip))

        if not bridgeFilterRules:
            bridgeFilterRules=[]

        if not len(self.splitter):
            logging.warn("Bailing! Splitter has zero bridges!")
            return []

        logging.debug("Bridges in splitter:\t%d" % len(self.splitter))
        logging.debug("Client request epoch:\t%s" % epoch)
        logging.debug("Active bridge filters:\t%s"
                      % ' '.join([x.func_name for x in bridgeFilterRules]))

        area = self.areaMapper(ip)
        logging.debug("IP mapped to area:\t%s" % area)

        key1 = ''
        pos = 0
        n = self.nClusters

        # only one of ip categories or area clustering is active
        # try to match the request to an ip category
        for category in self.categories:
            # IP Categories
            if ip in category:
                # The tag is a tag applied to a proxy IP address when it is
                # added to the bridgedb.proxy.ProxySet. For Tor Exit relays,
                # the default is 'exit_relay'. For other proxies loaded from
                # the PROXY_LIST_FILES config option, the default tag is the
                # full filename that the IP address originally came from.
                tag = category.getTag(ip)
                logging.info("Client was from known proxy (tag: %s): %s" % (tag, ip))
                g = filterAssignBridgesToRing(self.splitter.hmac,
                                              self.nClusters +
                                              len(self.categories),
                                              n)
                bridgeFilterRules.append(g)
                # Cluster Tor/proxy users into four groups.  This means that
                # no matter how many different Tor Exits or proxies a client
                # uses, the most they can ever get is four different sets of
                # bridge lines (per period).
                group = (int(ipaddr.IPAddress(ip)) % 4) + 1
                logging.debug(("Assigning client hashring position based on: "
                               "known-proxy<%s>%s") % (epoch, group))
                pos = self.areaOrderHmac("known-proxy<%s>%s" % (epoch, group))
                key1 = getHMAC(self.splitter.key,
                               "Order-Bridges-In-Ring-%d" % n)
                break
            n += 1

        # if no category matches, use area clustering
        else:
            # IP clustering
            h = int( self.areaClusterHmac(area)[:8], 16)
            # length of numClusters
            clusterNum = h % self.nClusters

            g = filterAssignBridgesToRing(self.splitter.hmac,
                                          self.nClusters +
                                          len(self.categories),
                                          clusterNum)
            bridgeFilterRules.append(g)
            pos = self.areaOrderHmac("<%s>%s" % (epoch, area))
            key1 = getHMAC(self.splitter.key,
                           "Order-Bridges-In-Ring-%d" % clusterNum)

        # try to find a cached copy
        ruleset = frozenset(bridgeFilterRules)

        # See if we have a cached copy of the ring,
        # otherwise, add a new ring and populate it
        if ruleset in self.splitter.filterRings.keys():
            logging.debug("Cache hit %s" % ruleset)
            _, ring = self.splitter.filterRings[ruleset]

        # else create the ring and populate it
        else:
            logging.debug("Cache miss %s" % ruleset)
            ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
            self.splitter.addRing(ring,
                                  ruleset,
                                  filterBridgesByRules(bridgeFilterRules),
                                  populate_from=self.splitter.bridges)

        # get an appropriate number of bridges
        numBridgesToReturn = getNumBridgesPerAnswer(ring,
                                                    max_bridges_per_answer=N)
        answer = ring.getBridges(pos, numBridgesToReturn)
        return answer
Example #7
0
    def getBridgesForIP(self, ip, epoch, N=1, countryCode=None,
                        bridgeFilterRules=None):
        """Return a list of bridges to give to a user.

        :param str ip: The user's IP address, as a dotted quad.
        :param str epoch: The time period when we got this request.  This can
                          be any string, so long as it changes with every
                          period.
        :param int N: The number of bridges to try to give back. (default: 1)
        :param str countryCode: DOCDOC (default: None)
        :param list bridgeFilterRules: A list of callables used filter the
                                       bridges returned in the response to the
                                       client. See :mod:`~bridgedb.Filters`.
        :rtype: list
        :return: A list of :class:`~bridgedb.Bridges.Bridge`s to include in
                 the response. See
                 :meth:`bridgedb.HTTPServer.WebResource.getBridgeRequestAnswer`
                 for an example of how this is used.
        """
        logging.info("Attempting to return %d bridges to client %s..."
                     % (N, ip))

        if not bridgeFilterRules:
            bridgeFilterRules=[]

        if not len(self.splitter):
            logging.warn("Bailing! Splitter has zero bridges!")
            return []

        logging.debug("Bridges in splitter:\t%d" % len(self.splitter))
        logging.debug("Client request epoch:\t%s" % epoch)
        logging.debug("Active bridge filters:\t%s"
                      % ' '.join([x.func_name for x in bridgeFilterRules]))

        area = self.areaMapper(ip)
        logging.debug("IP mapped to area:\t%s"
                      % logSafely("{0}.0/24".format(area)))

        key1 = ''
        pos = 0
        n = self.nClusters

        # only one of ip categories or area clustering is active
        # try to match the request to an ip category
        for category in self.categories:
            # IP Categories
            if category.contains(ip):
                g = filterAssignBridgesToRing(self.splitter.hmac,
                                              self.nClusters +
                                              len(self.categories),
                                              n)
                bridgeFilterRules.append(g)
                logging.info("category<%s>%s", epoch, logSafely(area))
                pos = self.areaOrderHmac("category<%s>%s" % (epoch, area))
                key1 = getHMAC(self.splitter.key,
                               "Order-Bridges-In-Ring-%d" % n)
                break
            n += 1

        # if no category matches, use area clustering
        else:
            # IP clustering
            h = int( self.areaClusterHmac(area)[:8], 16)
            # length of numClusters
            clusterNum = h % self.nClusters

            g = filterAssignBridgesToRing(self.splitter.hmac,
                                          self.nClusters +
                                          len(self.categories),
                                          clusterNum)
            bridgeFilterRules.append(g)
            pos = self.areaOrderHmac("<%s>%s" % (epoch, area))
            key1 = getHMAC(self.splitter.key,
                           "Order-Bridges-In-Ring-%d" % clusterNum)

        # try to find a cached copy
        ruleset = frozenset(bridgeFilterRules)

        # See if we have a cached copy of the ring,
        # otherwise, add a new ring and populate it
        if ruleset in self.splitter.filterRings.keys():
            logging.debug("Cache hit %s" % ruleset)
            _,ring = self.splitter.filterRings[ruleset]

        # else create the ring and populate it
        else:
            logging.debug("Cache miss %s" % ruleset)
            ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
            self.splitter.addRing(ring,
                                  ruleset,
                                  filterBridgesByRules(bridgeFilterRules),
                                  populate_from=self.splitter.bridges)

        # get an appropriate number of bridges
        numBridgesToReturn = getNumBridgesPerAnswer(ring,
                                                    max_bridges_per_answer=N)
        answer = ring.getBridges(pos, numBridgesToReturn)
        return answer
Example #8
0
    def getBridgesForEmail(self, emailaddress, epoch, N=1,
            parameters=None, countryCode=None, bridgeFilterRules=None):
        """Return a list of bridges to give to a user.
           emailaddress -- the user's email address, as given in a from line.
           epoch -- the time period when we got this request.  This can
               be any string, so long as it changes with every period.
           N -- the number of bridges to try to give back.
        """
        if not bridgeFilterRules:
            bridgeFilterRules=[]
        now = time.time()
        try:
            emailaddress = normalizeEmail(emailaddress, self.domainmap,
                                          self.domainrules)
        except BadEmail:
            return [] #XXXX log the exception
        if emailaddress is None:
            return [] #XXXX raise an exception.

        db = bridgedb.Storage.getDB()
        wasWarned = db.getWarnedEmail(emailaddress)

        lastSaw = db.getEmailTime(emailaddress)
        if lastSaw is not None and lastSaw + MAX_EMAIL_RATE >= now:
            if wasWarned:
                logging.info("Got a request for bridges from %r; we already "
                             "sent a warning. Ignoring.", Util.logSafely(emailaddress))
                raise IgnoreEmail("Client was warned", Util.logSafely(emailaddress))
            else:
                db.setWarnedEmail(emailaddress, True, now)
                db.commit() 

            logging.info("Got a request for bridges from %r; we already "
                         "answered one within the last %d seconds. Warning.",
                         Util.logSafely(emailaddress), MAX_EMAIL_RATE)
            raise TooSoonEmail("Too many emails; wait till later", emailaddress)

        # warning period is over
        elif wasWarned:
            db.setWarnedEmail(emailaddress, False) 

        pos = self.emailHmac("<%s>%s" % (epoch, emailaddress))

        ring = None
        ruleset = frozenset(bridgeFilterRules)
        if ruleset in self.splitter.filterRings.keys():
            logging.debug("Cache hit %s" % ruleset)
            _,ring = self.splitter.filterRings[ruleset]
        else:
            # cache miss, add new ring
            logging.debug("Cache miss %s" % ruleset)

            # add new ring 
            key1 = bridgedb.Bridges.get_hmac(self.splitter.key,
                    "Order-Bridges-In-Ring")
            ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
            # debug log: cache miss 
            self.splitter.addRing(ring, ruleset,
                                  filterBridgesByRules(bridgeFilterRules),
                                  populate_from=self.splitter.bridges)

        result = ring.getBridges(pos, getNumBridgesPerAnswer(ring, max_bridges_per_answer=N))

        db.setEmailTime(emailaddress, now)
        db.commit()
        return result
Example #9
0
    def getBridgesForIP(self, ip, epoch, N=1, countryCode=None,
                        bridgeFilterRules=None):
        """Return a list of bridges to give to a user.
           ip -- the user's IP address, as a dotted quad.
           epoch -- the time period when we got this request.  This can
               be any string, so long as it changes with every period.
           N -- the number of bridges to try to give back.
        """
        if not bridgeFilterRules: bridgeFilterRules=[]
        logging.debug("getBridgesForIP(%s, %s, %s, %s",
                Util.logSafely(ip), epoch, N, bridgeFilterRules)
        if not len(self.splitter):
            logging.debug("bailing without splitter")
            return []

        area = self.areaMapper(ip)

        logging.info("area is %s", Util.logSafely(area))
        
        key1 = ''
        pos = 0
        n = self.nClusters

        # only one of ip categories or area clustering is active
        # try to match the request to an ip category
        for category in self.categories:
            # IP Categories
            if category.contains(ip):
                g = filterAssignBridgesToRing(self.splitter.hmac,
                                                      self.nClusters +
                                                      len(self.categories),
                                                      n)
                bridgeFilterRules.append(g)
                logging.info("category<%s>%s", epoch, Util.logSafely(area))
                pos = self.areaOrderHmac("category<%s>%s" % (epoch, area))
                key1 = bridgedb.Bridges.get_hmac(self.splitter.key,
                                             "Order-Bridges-In-Ring-%d"%n) 
                break;
            n += 1

        # if no category matches, use area clustering
        else:
            # IP clustering
            h = int( self.areaClusterHmac(area)[:8], 16)
            # length of numClusters
            clusterNum = h % self.nClusters
 
            g = filterAssignBridgesToRing(self.splitter.hmac,
                                          self.nClusters +
                                          len(self.categories),
                                          clusterNum) 
            bridgeFilterRules.append(g)
            pos = self.areaOrderHmac("<%s>%s" % (epoch, area))
            key1 = bridgedb.Bridges.get_hmac(self.splitter.key,
                                             "Order-Bridges-In-Ring-%d"%clusterNum) 

        logging.debug("bridgeFilterRules: %s" % bridgeFilterRules)

        # try to find a cached copy
        ruleset = frozenset(bridgeFilterRules)

        # See if we have a cached copy of the ring,
        # otherwise, add a new ring and populate it
        if ruleset in self.splitter.filterRings.keys():
            logging.debug("Cache hit %s" % ruleset)
            _,ring = self.splitter.filterRings[ruleset]

        # else create the ring and populate it
        else:
            logging.debug("Cache miss %s" % ruleset)
            ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
            self.splitter.addRing(ring, ruleset, filterBridgesByRules(bridgeFilterRules),
                                  populate_from=self.splitter.bridges)

        # get an appropriate number of bridges
        return ring.getBridges(pos, getNumBridgesPerAnswer(ring, max_bridges_per_answer=N))
Example #10
0
    def getBridgesForEmail(self,
                           emailaddress,
                           epoch,
                           N=1,
                           parameters=None,
                           countryCode=None,
                           bridgeFilterRules=None):
        """Return a list of bridges to give to a user.
           emailaddress -- the user's email address, as given in a from line.
           epoch -- the time period when we got this request.  This can
               be any string, so long as it changes with every period.
           N -- the number of bridges to try to give back.
        """
        if not bridgeFilterRules:
            bridgeFilterRules = []
        now = time.time()
        try:
            emailaddress = normalizeEmail(emailaddress, self.domainmap,
                                          self.domainrules)
        except BadEmail:
            return []  #XXXX log the exception
        if emailaddress is None:
            return []  #XXXX raise an exception.

        db = bridgedb.Storage.getDB()
        wasWarned = db.getWarnedEmail(emailaddress)

        lastSaw = db.getEmailTime(emailaddress)
        if lastSaw is not None and lastSaw + MAX_EMAIL_RATE >= now:
            if wasWarned:
                logging.info(
                    "Got a request for bridges from %r; we already "
                    "sent a warning. Ignoring.", Util.logSafely(emailaddress))
                raise IgnoreEmail("Client was warned",
                                  Util.logSafely(emailaddress))
            else:
                db.setWarnedEmail(emailaddress, True, now)
                db.commit()

            logging.info(
                "Got a request for bridges from %r; we already "
                "answered one within the last %d seconds. Warning.",
                Util.logSafely(emailaddress), MAX_EMAIL_RATE)
            raise TooSoonEmail("Too many emails; wait till later",
                               emailaddress)

        # warning period is over
        elif wasWarned:
            db.setWarnedEmail(emailaddress, False)

        pos = self.emailHmac("<%s>%s" % (epoch, emailaddress))

        ring = None
        ruleset = frozenset(bridgeFilterRules)
        if ruleset in self.splitter.filterRings.keys():
            logging.debug("Cache hit %s" % ruleset)
            _, ring = self.splitter.filterRings[ruleset]
        else:
            # cache miss, add new ring
            logging.debug("Cache miss %s" % ruleset)

            # add new ring
            key1 = bridgedb.Bridges.get_hmac(self.splitter.key,
                                             "Order-Bridges-In-Ring")
            ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
            # debug log: cache miss
            self.splitter.addRing(ring,
                                  ruleset,
                                  filterBridgesByRules(bridgeFilterRules),
                                  populate_from=self.splitter.bridges)

        result = ring.getBridges(
            pos, getNumBridgesPerAnswer(ring, max_bridges_per_answer=N))

        db.setEmailTime(emailaddress, now)
        db.commit()
        return result
Example #11
0
    def getBridgesForIP(self,
                        ip,
                        epoch,
                        N=1,
                        countryCode=None,
                        bridgeFilterRules=None):
        """Return a list of bridges to give to a user.
           ip -- the user's IP address, as a dotted quad.
           epoch -- the time period when we got this request.  This can
               be any string, so long as it changes with every period.
           N -- the number of bridges to try to give back.
        """
        if not bridgeFilterRules: bridgeFilterRules = []
        logging.debug("getBridgesForIP(%s, %s, %s, %s", Util.logSafely(ip),
                      epoch, N, bridgeFilterRules)
        if not len(self.splitter):
            logging.debug("bailing without splitter")
            return []

        area = self.areaMapper(ip)

        logging.info("area is %s", Util.logSafely(area))

        key1 = ''
        pos = 0
        n = self.nClusters

        # only one of ip categories or area clustering is active
        # try to match the request to an ip category
        for category in self.categories:
            # IP Categories
            if category.contains(ip):
                g = filterAssignBridgesToRing(
                    self.splitter.hmac, self.nClusters + len(self.categories),
                    n)
                bridgeFilterRules.append(g)
                logging.info("category<%s>%s", epoch, Util.logSafely(area))
                pos = self.areaOrderHmac("category<%s>%s" % (epoch, area))
                key1 = bridgedb.Bridges.get_hmac(
                    self.splitter.key, "Order-Bridges-In-Ring-%d" % n)
                break
            n += 1

        # if no category matches, use area clustering
        else:
            # IP clustering
            h = int(self.areaClusterHmac(area)[:8], 16)
            # length of numClusters
            clusterNum = h % self.nClusters

            g = filterAssignBridgesToRing(
                self.splitter.hmac, self.nClusters + len(self.categories),
                clusterNum)
            bridgeFilterRules.append(g)
            pos = self.areaOrderHmac("<%s>%s" % (epoch, area))
            key1 = bridgedb.Bridges.get_hmac(
                self.splitter.key, "Order-Bridges-In-Ring-%d" % clusterNum)

        logging.debug("bridgeFilterRules: %s" % bridgeFilterRules)

        # try to find a cached copy
        ruleset = frozenset(bridgeFilterRules)

        # See if we have a cached copy of the ring,
        # otherwise, add a new ring and populate it
        if ruleset in self.splitter.filterRings.keys():
            logging.debug("Cache hit %s" % ruleset)
            _, ring = self.splitter.filterRings[ruleset]

        # else create the ring and populate it
        else:
            logging.debug("Cache miss %s" % ruleset)
            ring = bridgedb.Bridges.BridgeRing(key1, self.answerParameters)
            self.splitter.addRing(ring,
                                  ruleset,
                                  filterBridgesByRules(bridgeFilterRules),
                                  populate_from=self.splitter.bridges)

        # get an appropriate number of bridges
        return ring.getBridges(
            pos, getNumBridgesPerAnswer(ring, max_bridges_per_answer=N))