forked from tkluck/pac4cli
/
main.py
executable file
·121 lines (102 loc) · 3.94 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!@PYTHON@
from twisted.internet import reactor
from twisted.web import proxy
from twisted.web.http import HTTPFactory
from twisted.web.client import Agent, readBody
from twisted.internet import defer
from twisted.internet.defer import inlineCallbacks
from twisted.web.client import Agent
from wpad import WPAD
import servicemanager
from argparse import ArgumentParser
import platform
import pacparser
import signal
import configparser
parser= ArgumentParser(description="""
Run a simple HTTP proxy on localhost that uses a wpad.dat to decide
how to connect to the actual server.
""")
parser.add_argument("-c", "--config", type=str)
parser.add_argument("-p", "--port", type=int, metavar="PORT")
parser.add_argument("-F", "--force-proxy", type=str, metavar="PROXY STRING")
parser.add_argument("--loglevel", type=str, default="info", metavar="LEVEL")
parser.add_argument("--systemd", action='store_true')
args= parser.parse_args()
from pac4cli import WPADProxyRequest
import logging
logger = logging.getLogger('pac4cli')
@inlineCallbacks
def start_server(port, reactor):
factory = HTTPFactory()
factory.protocol = proxy.Proxy
factory.protocol.requestFactory = WPADProxyRequest
yield reactor.listenTCP(port, factory, interface="127.0.0.1")
servicemanager.notify_ready();
@inlineCallbacks
def get_possible_configuration_locations():
try:
wpad = WPAD( reactor, args.config )
urls = yield wpad.getUrls()
return urls
except Exception as e:
logger.warning("Issue getting wpad configuration", exc_info=True)
return []
@inlineCallbacks
def updateWPAD(signum=None, stackframe=None):
if args.force_proxy:
return
logger.info("Updating WPAD configuration...")
wpad_urls = yield get_possible_configuration_locations()
# use DIRECT temporarily; who knows what state the below gets pacparser
# in
WPADProxyRequest.force_direct = 'DIRECT'
for wpad_url in wpad_urls:
logger.info("Trying %s...", wpad_url)
try:
agent = Agent(reactor)
# TODO: need to ensure this doesn't go through any http_proxy, such as
# ourselves :)
response = yield agent.request(b'GET', wpad_url.encode('ascii'))
body = yield readBody(response)
logger.info("...found. Parsing configuration...")
pacparser.parse_pac_string(body.decode('ascii'))
logger.info("Updated configuration")
WPADProxyRequest.force_direct = None
break
except Exception as e:
logger.info("...didn't work")
pass
else:
logger.info("None of the tried urls seem to have worked; falling back to direct")
WPADProxyRequest.force_direct = 'DIRECT'
@inlineCallbacks
def main(args):
try:
pacparser.init()
WPADProxyRequest.force_direct = 'DIRECT' # direct, until we have a configuration
if args.force_proxy:
WPADProxyRequest.force_proxy = args.force_proxy
else:
yield updateWPAD()
signal.signal(signal.SIGHUP, updateWPAD)
force_proxy_message = ", sending all traffic through %s"%args.force_proxy if args.force_proxy else ""
logger.info("Starting proxy server on port %s%s", args.port, force_proxy_message)
yield start_server(args.port, reactor)
logger.info("Successfully started.")
except Exception as e:
logger.error("Problem starting the server", exc_info=True)
if __name__ == "__main__":
import os
log_level_name = os.environ.get('LOG_LEVEL', args.loglevel)
log_level = getattr(logging, log_level_name.upper(), logging.INFO)
if args.systemd:
log_handler = servicemanager.LogHandler()
else:
log_handler = logging.StreamHandler()
logger.setLevel(log_level)
logger.addHandler(log_handler)
log_handler.setFormatter(logging.Formatter(fmt="%(levelname)s [%(process)d]: %(name)s: %(message)s"))
main(args)
reactor.run()
logger.info("Shutdown")