diff options
Diffstat (limited to 'libpathod/pathod.py')
-rw-r--r-- | libpathod/pathod.py | 146 |
1 files changed, 117 insertions, 29 deletions
diff --git a/libpathod/pathod.py b/libpathod/pathod.py index 79fe7ed1..173773cf 100644 --- a/libpathod/pathod.py +++ b/libpathod/pathod.py @@ -1,7 +1,12 @@ -import urllib, threading, re, logging, os +import urllib +import threading +import logging +import os +import sys from netlib import tcp, http, wsgi, certutils import netlib.utils -import version, app, language, utils + +from . import version, app, language, utils DEFAULT_CERT_DOMAIN = "pathod.net" @@ -12,7 +17,8 @@ CA_CERT_NAME = "mitmproxy-ca.pem" logger = logging.getLogger('pathod') -class PathodError(Exception): pass +class PathodError(Exception): + pass class SSLOptions: @@ -64,7 +70,12 @@ class PathodHandler(tcp.BaseHandler): if self.server.explain and not isinstance(crafted, language.PathodErrorResponse): crafted = crafted.freeze(self.server.request_settings, None) self.info(">> Spec: %s" % crafted.spec()) - response_log = language.serve(crafted, self.wfile, self.server.request_settings, None) + response_log = language.serve( + crafted, + self.wfile, + self.server.request_settings, + None + ) if response_log["disconnect"]: return False, response_log return True, response_log @@ -162,15 +173,14 @@ class PathodHandler(tcp.BaseHandler): for i in self.server.anchors: if i[0].match(path): self.info("crafting anchor: %s" % path) - aresp = language.parse_response(self.server.request_settings, i[1]) - again, retlog["response"] = self.serve_crafted(aresp) + again, retlog["response"] = self.serve_crafted(i[1]) return again, retlog if not self.server.nocraft and path.startswith(self.server.craftanchor): spec = urllib.unquote(path)[len(self.server.craftanchor):] self.info("crafting spec: %s" % spec) try: - crafted = language.parse_response(self.server.request_settings, spec) + crafted = language.parse_response(spec) except language.ParseException, v: self.info("Parse error: %s" % v.msg) crafted = language.make_error_response( @@ -182,7 +192,10 @@ class PathodHandler(tcp.BaseHandler): elif self.server.noweb: crafted = language.make_error_response("Access Denied") language.serve(crafted, self.wfile, self.server.request_settings) - return False, dict(type="error", msg="Access denied: web interface disabled") + return False, dict( + type="error", + msg="Access denied: web interface disabled" + ) else: self.info("app: %s %s" % (method, path)) req = wsgi.Request("http", method, path, headers, content) @@ -252,19 +265,34 @@ class Pathod(tcp.TCPServer): LOGBUF = 500 def __init__( - self, addr, confdir=CONFDIR, ssl=False, ssloptions=None, - craftanchor="/p/", staticdir=None, anchors=None, - sizelimit=None, noweb=False, nocraft=False, noapi=False, - nohang=False, timeout=None, logreq=False, logresp=False, - explain=False, hexdump=False + self, + addr, + confdir=CONFDIR, + ssl=False, + ssloptions=None, + craftanchor="/p/", + staticdir=None, + anchors=(), + sizelimit=None, + noweb=False, + nocraft=False, + noapi=False, + nohang=False, + timeout=None, + logreq=False, + logresp=False, + explain=False, + hexdump=False ): """ addr: (address, port) tuple. If port is 0, a free port will be automatically chosen. ssloptions: an SSLOptions object. - craftanchor: string specifying the path under which to anchor response generation. + craftanchor: string specifying the path under which to anchor + response generation. staticdir: path to a directory of static resources, or None. - anchors: A list of (regex, spec) tuples, or None. + anchors: List of (regex object, language.Request object) tuples, or + None. sizelimit: Limit size of served data. nocraft: Disable response crafting. noapi: Disable the API. @@ -276,26 +304,17 @@ class Pathod(tcp.TCPServer): self.staticdir = staticdir self.craftanchor = craftanchor self.sizelimit = sizelimit - self.noweb, self.nocraft, self.noapi, self.nohang = noweb, nocraft, noapi, nohang - self.timeout, self.logreq, self.logresp, self.hexdump = timeout, logreq, logresp, hexdump + self.noweb, self.nocraft = noweb, nocraft + self.noapi, self.nohang = noapi, nohang + self.timeout, self.logreq = timeout, logreq + self.logresp, self.hexdump = logresp, hexdump self.explain = explain self.app = app.make_app(noapi) self.app.config["pathod"] = self self.log = [] self.logid = 0 - self.anchors = [] - if anchors: - for i in anchors: - try: - arex = re.compile(i[0]) - except re.error: - raise PathodError("Invalid regex in anchor: %s" % i[0]) - try: - language.parse_response(self.request_settings, i[1]) - except language.ParseException, v: - raise PathodError("Invalid page spec in anchor: '%s', %s" % (i[1], str(v))) - self.anchors.append((arex, i[1])) + self.anchors = anchors def check_policy(self, req, settings): """ @@ -363,3 +382,72 @@ class Pathod(tcp.TCPServer): def get_log(self): return self.log + + +def main(args): + ssloptions = SSLOptions( + cn = args.cn, + confdir = args.confdir, + not_after_connect = args.ssl_not_after_connect, + ciphers = args.ciphers, + sslversion = utils.SSLVERSIONS[args.sslversion], + certs = args.ssl_certs + ) + + root = logging.getLogger() + if root.handlers: + for handler in root.handlers: + root.removeHandler(handler) + + log = logging.getLogger('pathod') + log.setLevel(logging.DEBUG) + fmt = logging.Formatter( + '%(asctime)s: %(message)s', + datefmt='%d-%m-%y %H:%M:%S', + ) + if args.logfile: + fh = logging.handlers.WatchedFileHandler(args.logfile) + fh.setFormatter(fmt) + log.addHandler(fh) + if not args.daemonize: + sh = logging.StreamHandler() + sh.setFormatter(fmt) + log.addHandler(sh) + + try: + pd = Pathod( + (args.address, args.port), + craftanchor = args.craftanchor, + ssl = args.ssl, + ssloptions = ssloptions, + staticdir = args.staticdir, + anchors = args.anchors, + sizelimit = args.sizelimit, + noweb = args.noweb, + nocraft = args.nocraft, + noapi = args.noapi, + nohang = args.nohang, + timeout = args.timeout, + logreq = args.logreq, + logresp = args.logresp, + hexdump = args.hexdump, + explain = args.explain, + ) + except PathodError, v: + print >> sys.stderr, "Error: %s"%v + sys.exit(1) + except language.FileAccessDenied, v: + print >> sys.stderr, "Error: %s"%v + + if args.daemonize: + utils.daemonize() + + try: + print "%s listening on %s:%s"%( + version.NAMEVERSION, + pd.address.host, + pd.address.port + ) + pd.serve_forever() + except KeyboardInterrupt: + pass |