Esempio n. 1
0
    def handleQuery(self, message, protocol, address):
        _super = super()
        def _handleQuery(mpa):
            message, protocol, address = mpa
            return _super.handleQuery(message, protocol, address)

        name = message.queries[0].name.name.decode("UTF-8")
        return apply_middlewares(self.middlewares.get_descriptors(name), _handleQuery)((message, protocol, address))
Esempio n. 2
0
    def pick_certificate(self, connection):
        def _pick_certificate(connection):
            return self.tls_ctx

        # Apply middlewares
        server_name_indication = (connection.get_servername() or b'').decode("UTF-8")
        ctx = apply_middlewares(self.middlewares.get_descriptors(server_name_indication), _pick_certificate)(connection)
        if ctx is not None:
            connection.set_context(self.tls_ctx)
Esempio n. 3
0
    def pick_certificate(self, connection):
        def _pick_certificate(server_name_indication, connection):
            return self.tls_ctx

        # Apply middlewares
        server_name_indication = (connection.get_servername()
                                  or b'').decode("UTF-8")
        middlewares = self.routes.get_descriptors(
            server_name_indication,
            rfilter=lambda x: x.get("protocol") == "ssl_middleware")
        ctx = utils.apply_middlewares(middlewares, _pick_certificate)(
            server_name_indication, connection)
        if ctx is not None:
            connection.set_context(ctx)
        else:
            connection.set_context(self.tls_ctx)
Esempio n. 4
0
    def handleQuery(self, message, protocol, address):
        def _handleQuery(route_descriptor, qname, lookup_cls, qtype, message, protocol, address):
            return self._lookup(route_descriptor, qname, lookup_cls, qtype, 1000)

        query = message.queries[0]

        # Standardise the query name
        qname = query.name.name
        if not isinstance(qname, str):
            qname = qname.decode('idna')
        qname = qname.lower()

        # At this point we only know of dns.IN lookup classes
        lookup_cls = dns.IN

        # Access route_descriptors directly to perform complex filtering
        route_descriptor = {}
        for _route_descriptor, _ in self.routes.get_descriptors(qname, rfilter=lambda x: x.get("protocol") == "dns"):
            _action_des = _route_descriptor.get("action")
            if _action_des is None:
                continue

            if lookup_cls == _action_des.get("class", dns.IN):

                # Convert the route_descriptor type to an integer
                rd_type = _action_des.get("type", query.type)
                if not isinstance(rd_type, int):
                    rd_type = dns.REV_TYPES.get(rd_type, 0)

                # If the lookup type matches the route_descriptor type
                if query.type == rd_type:
                    route_descriptor = _route_descriptor
                    break

        middlewares = self.routes.get_descriptors(qname, rfilter=lambda x: x.get("protocol") == "dns_middleware")
        response = utils.apply_middlewares(middlewares, _handleQuery)(route_descriptor, qname, lookup_cls, query.type, message, protocol, address)
        self.gotResolverResponse(response, protocol, message, address)
Esempio n. 5
0
    def getChild(self, name, request):
        # Rebuild the request parts for route matching
        scheme = "https" if request.isSecure() else "http"
        host = request.getRequestHostname().decode("UTF-8") or "-"
        port = request.getHost().port
        path = request.path.decode("UTF-8")
        args = (request.uri.decode("UTF-8").split("?", 1) + [""])[1]

        # Match on any of scheme://host(:port)/path?args, /path?args, /path in that order
        request_path = path
        request_parts = []
        
        request_parts.append("{scheme}://{host}{port}{path}{args}".format(
            scheme=scheme,
            host=host,
            port=(":%d" % port) if port != {"http": 80, "https": 443}[scheme] else "",
            path=request_path,
            args= "?" + args if args else ""
        ))

        if args:
            request_parts.append(request_path + "?" + args)

        request_parts.append(request_path)

        def _getChild(route_descriptor, route_match, request):
            response = SimpleResource(404, body=b'Not Found')

            if route_descriptor is not None:
                action_des = route_descriptor.get("action")
                if action_des is not None:
                    response_handler = action_des.get("handler", "serve")

                    if response_handler == "serve":
                        # Replace regex groups in the route path
                        resource_path = action_des["path"]
                        for i, group in enumerate(re.search(route_descriptor["route"], route_match).groups()):
                            if group is not None:
                                resource_path = resource_path.replace("${}".format(i + 1), group)

                        # Security: Ensure the absolute resource_path is within the `self.filesroot` directory!
                        # Prepend the resource_path with the self.filesroot and canonicalize
                        resource_path = os.path.abspath(os.path.join(self.filesroot, resource_path.lstrip("/")))
                        if resource_path.startswith(self.filesroot):
                            # If the resource_path does not exist, or is a directory, search for an index.py in each url path directory
                            if not os.path.isfile(resource_path):
                                search_path = ""
                                search_dirs = [""] + resource_path.replace(self.filesroot, "").strip("/").split("/")
                                for search_dir in search_dirs:
                                    search_path = os.path.join(search_path, search_dir)
                                    if os.path.isfile(os.path.join(self.filesroot, search_path, "index.py")):
                                        resource_path = os.path.join(self.filesroot, search_path, "index.py")
                                        break
                            
                            if os.path.isfile(resource_path):
                                # Execute python scripts
                                if os.path.splitext(resource_path)[1].lower() == ".py":
                                    # Fixup the request path
                                    request.postpath.insert(0, request.prepath.pop(0))
                                    response = get_script_response(resource_path, request)
                                else:
                                    data = b''
                                    with open(resource_path, "rb") as f:
                                        data = f.read()
                                    response = SimpleResource(200, body=data)

                    elif response_handler == "script":
                        # Fixup the request path
                        request.postpath.insert(0, request.prepath.pop(0))

                        # Relocate to `base`
                        if "base" in action_des:
                            base = action_des["base"]
                            request.prepath = base.strip("/").encode().split(b'/')
                            if request_path.startswith(base):
                                request.postpath = request_path.split(base, 1)[1].strip("/").encode().split(b'/')

                        # Apply rewrite
                        if "rewrite" in action_des:
                            match = re.search(action_des["rewrite"], request_path)
                            if match:
                                try:
                                    rewrite = match.group(1)
                                except IndexError:
                                    rewrite = match.group(0)
                                finally:
                                    request.postpath = rewrite.encode().split(b'/')
                        response = get_script_response(action_des["path"], request)
                    elif response_handler == "raw":
                        code = action_des.get("code", 200)
                        body = action_des.get("body", "").encode("UTF-8")
                        response = SimpleResource(code, body=body)
                    elif response_handler == "forward":
                        url = action_des["destination"]
                        if action_des.get("recreate_url", True):
                            fscheme, fnetloc, _, _, _, _ = urllib.parse.urlparse(url)
                            url = urllib.parse.urlunparse((fscheme, fnetloc, request.uri.decode("UTF-8"), "", "", ""))
                        response = ForwardResource(url, headers=action_des.get("request_headers", {}))
                    
                    
                    headers = action_des.get("headers", {})
                    # Take a copy of the `replace` array as we will modify it afterwards
                    replace = copy.deepcopy(action_des.get("replace", []))
                    if len(headers) or len(replace):
                        if len(replace):
                            # Prepare the replacements
                            for replace_descriptor in replace:
                                replacement = replace_descriptor["replacement"]
                                replacement = replacement.replace("{hostname}", host)
                                replacement = replacement.replace("{port}", str(port))
                                replacement = replacement.replace("{path}", path)
                                replacement = replacement.replace("{scheme}", scheme)
                                replace_descriptor["pattern"] = replace_descriptor["pattern"].encode()
                                replace_descriptor["replacement"] = replacement.encode()
                        
                        # Patch request.write to replace headers after rendering and perform replacements
                        request.write = lambda data: _write(request, headers, replace, data)
            
            return response

        route_descriptor, route_match = self.routes.get_descriptor(*request_parts, rfilter=lambda x: x.get("protocol") == "http")
        middlewares = self.routes.get_descriptors(*request_parts, rfilter=lambda x: x.get("protocol") == "http_middleware")
        return utils.apply_middlewares(middlewares, _getChild)(route_descriptor, route_match, request)
Esempio n. 6
0
    def getChild(self, name, request):
        # Rebuild the request parts for route matching
        scheme = "https" if request.isSecure() else "http"
        host = request.getRequestHostname().decode("UTF-8") or "-"
        port = request.getHost().port
        path = request.path.decode("UTF-8")
        args = (request.uri.decode("UTF-8").split("?", 1) + [""])[1]

        request_path = path
        request_parts = [request_path]
        if args:
            request_parts.append(request_path + "?" + args)

        request_parts.append("{}://{}{}{}".format(
            scheme,
            host,
            (":%d" % port) if port != {"http": 80, "https": 443}[scheme] else "",
            request_parts[-1]
        ))

        def _getChild(request):
            headers = {}
            resource_path = request_path

            route_descriptor, route_match = self.routes.get_descriptor(*request_parts)
            if route_descriptor is not None:
                headers = route_descriptor.get("headers", {})

                # Forward case
                if "forward" in route_descriptor:
                    # Recreate the URL
                    if route_descriptor.get("recreate_url", True):
                        fscheme, fnetloc, _, _, _, _ = urllib.parse.urlparse(route_descriptor["forward"])
                        url = urllib.parse.urlunparse((fscheme, fnetloc, request.uri.decode("UTF-8"), "", "", ""))
                    else:
                        url = route_descriptor["forward"]

                    replace = route_descriptor.get("replace", [])
                    return ForwardResource(url, headers=headers, replace=replace)
                # Code case
                elif "code" in route_descriptor:
                    code = route_descriptor.get("code", 200)
                    body = route_descriptor.get("body", "")
                    return SimpleResource(request_path, code, headers=headers, body=body)
                # Path case
                elif "path" in route_descriptor:
                    resource_path = route_descriptor["path"]
                    # Replace regex groups in the route path
                    for i, group in enumerate(re.search(route_descriptor["route"], route_match).groups()):
                        if group is not None:
                            resource_path = resource_path.replace("${}".format(i + 1), group)

                # Security: Ensure the absolute resource_path is within the wwwroot!
                # Prepend the resource_path with the wwwroot and canonicalize
                resource_path = os.path.abspath(os.path.join(self.filesroot, resource_path.lstrip("/")))
                if resource_path.startswith(self.filesroot):
                    if os.path.exists(resource_path):
                        # Security: Don't show the soruce of python files!
                        if os.path.splitext(resource_path)[1].lower() == ".py":
                            try:
                                res = exec_cached_script(resource_path)
                                return resource.IResource(res["get_resource"](request))
                            except Exception:
                                logger.exception("Unahandled exception in exec'd file '{}'".format(resource_path))
                        else:
                            with open(resource_path, "r") as f:
                                data = f.read()
                            return SimpleResource(request_path, 200, headers=headers, body=data)

            # Default handling, 404 here
            return super().getChild(name, request)

        return apply_middlewares(self.middlewares.get_descriptors(*request_parts), _getChild)(request)