Beispiel #1
0
 def __init__(self, app, conf):
     if not MODULE_DEPENDENCY_MET:
         # reraise the exception if the dependency wasn't met
         raise ImportError('dnspython is required for this module')
     self.app = app
     storage_domain = conf.get('storage_domain', 'example.com')
     self.storage_domain = ['.' + s for s in
                            list_from_csv(storage_domain)
                            if not s.startswith('.')]
     self.storage_domain += [s for s in list_from_csv(storage_domain)
                             if s.startswith('.')]
     self.lookup_depth = int(conf.get('lookup_depth', '1'))
     nameservers = list_from_csv(conf.get('nameservers'))
     try:
         for i, server in enumerate(nameservers):
             ip_or_host, maybe_port = nameservers[i] = \
                 parse_socket_string(server, None)
             if not is_valid_ip(ip_or_host):
                 raise ValueError
             if maybe_port is not None:
                 int(maybe_port)
     except ValueError:
         raise ValueError('Invalid cname_lookup/nameservers configuration '
                          'found. All nameservers must be valid IPv4 or '
                          'IPv6, followed by an optional :<integer> port.')
     self.resolver = dns.resolver.Resolver()
     if nameservers:
         self.resolver.nameservers = [ip for (ip, port) in nameservers]
         self.resolver.nameserver_ports = {
             ip: int(port) for (ip, port) in nameservers
             if port is not None}
     self.memcache = None
     self.logger = get_logger(conf, log_route='cname-lookup')
Beispiel #2
0
def is_local_device(my_ips, my_port, dev_ip, dev_port):
    """
    Return True if the provided dev_ip and dev_port are among the IP
    addresses specified in my_ips and my_port respectively.

    To support accurate locality determination in the server-per-port
    deployment, when my_port is None, only IP addresses are used for
    determining locality (dev_port is ignored).

    If dev_ip is a hostname then it is first translated to an IP
    address before checking it against my_ips.
    """
    candidate_ips = []
    if not is_valid_ip(dev_ip) and is_valid_hostname(dev_ip):
        try:
            # get the ip for this host; use getaddrinfo so that
            # it works for both ipv4 and ipv6 addresses
            addrinfo = socket.getaddrinfo(dev_ip, dev_port)
            for addr in addrinfo:
                family = addr[0]
                dev_ip = addr[4][0]  # get the ip-address
                if family == socket.AF_INET6:
                    dev_ip = expand_ipv6(dev_ip)
                candidate_ips.append(dev_ip)
        except socket.gaierror:
            return False
    else:
        if is_valid_ipv6(dev_ip):
            dev_ip = expand_ipv6(dev_ip)
        candidate_ips = [dev_ip]

    for dev_ip in candidate_ips:
        if dev_ip in my_ips and (my_port is None or dev_port == my_port):
            return True

    return False
Beispiel #3
0
def is_local_device(my_ips, my_port, dev_ip, dev_port):
    """
    Return True if the provided dev_ip and dev_port are among the IP
    addresses specified in my_ips and my_port respectively.

    To support accurate locality determination in the server-per-port
    deployment, when my_port is None, only IP addresses are used for
    determining locality (dev_port is ignored).

    If dev_ip is a hostname then it is first translated to an IP
    address before checking it against my_ips.
    """
    candidate_ips = []
    if not is_valid_ip(dev_ip) and is_valid_hostname(dev_ip):
        try:
            # get the ip for this host; use getaddrinfo so that
            # it works for both ipv4 and ipv6 addresses
            addrinfo = socket.getaddrinfo(dev_ip, dev_port)
            for addr in addrinfo:
                family = addr[0]
                dev_ip = addr[4][0]  # get the ip-address
                if family == socket.AF_INET6:
                    dev_ip = expand_ipv6(dev_ip)
                candidate_ips.append(dev_ip)
        except socket.gaierror:
            return False
    else:
        if is_valid_ipv6(dev_ip):
            dev_ip = expand_ipv6(dev_ip)
        candidate_ips = [dev_ip]

    for dev_ip in candidate_ips:
        if dev_ip in my_ips and (my_port is None or dev_port == my_port):
            return True

    return False
Beispiel #4
0
    def __call__(self, env, start_response):
        if not self.storage_domain:
            return self.app(env, start_response)
        if 'HTTP_HOST' in env:
            requested_host = env['HTTP_HOST']
        else:
            requested_host = env['SERVER_NAME']
        given_domain = wsgi_to_str(requested_host)
        port = ''
        if ':' in given_domain:
            given_domain, port = given_domain.rsplit(':', 1)
        if is_valid_ip(given_domain):
            return self.app(env, start_response)
        a_domain = given_domain
        if not self._domain_endswith_in_storage_domain(a_domain):
            if self.memcache is None:
                self.memcache = cache_from_env(env)
            error = True
            for tries in range(self.lookup_depth):
                found_domain = None
                if self.memcache:
                    memcache_key = ''.join(['cname-', a_domain])
                    found_domain = self.memcache.get(memcache_key)
                    if six.PY2 and found_domain:
                        found_domain = found_domain.encode('utf-8')
                if found_domain is None:
                    ttl, found_domain = lookup_cname(a_domain, self.resolver)
                    if self.memcache and ttl > 0:
                        memcache_key = ''.join(['cname-', given_domain])
                        self.memcache.set(memcache_key, found_domain, time=ttl)
                if not found_domain or found_domain == a_domain:
                    # no CNAME records or we're at the last lookup
                    error = True
                    found_domain = None
                    break
                elif self._domain_endswith_in_storage_domain(found_domain):
                    # Found it!
                    self.logger.info(
                        _('Mapped %(given_domain)s to %(found_domain)s') % {
                            'given_domain': given_domain,
                            'found_domain': found_domain
                        })
                    if port:
                        env['HTTP_HOST'] = ':'.join(
                            [str_to_wsgi(found_domain), port])
                    else:
                        env['HTTP_HOST'] = str_to_wsgi(found_domain)
                    error = False
                    break
                else:
                    # try one more deep in the chain
                    self.logger.debug(
                        _('Following CNAME chain for  '
                          '%(given_domain)s to %(found_domain)s') % {
                              'given_domain': given_domain,
                              'found_domain': found_domain
                          })
                    a_domain = found_domain
            if error:
                if found_domain:
                    msg = 'CNAME lookup failed after %d tries' % \
                        self.lookup_depth
                else:
                    msg = 'CNAME lookup failed to resolve to a valid domain'
                resp = HTTPBadRequest(request=Request(env),
                                      body=msg,
                                      content_type='text/plain')
                return resp(env, start_response)
            else:
                context = _CnameLookupContext(self.app, requested_host,
                                              env['HTTP_HOST'])
                return context.handle_request(env, start_response)

        return self.app(env, start_response)