version 1.2

master
wheever 2015-01-07 11:57:51 +08:00
parent 01454177bd
commit aab5dd91bf
10 changed files with 6307 additions and 0 deletions

52
CA.crt Normal file
View File

@ -0,0 +1,52 @@
-----BEGIN CERTIFICATE-----
MIID+DCCAuCgAwIBAgIBADANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJDTjEX
MBUGA1UEChMOUHJveEhUVFBTUHJveHkxEjAQBgNVBAsTCXB5T3BlblNTTDEaMBgG
A1UEAxMRUHJveEhUVFBTUHJveHkgQ0EwHhcNMTQwNzI1MDI0NjM1WhcNMjQwNzI0
MDI0NjM1WjBWMQswCQYDVQQGEwJDTjEXMBUGA1UEChMOUHJveEhUVFBTUHJveHkx
EjAQBgNVBAsTCXB5T3BlblNTTDEaMBgGA1UEAxMRUHJveEhUVFBTUHJveHkgQ0Ew
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDu5tWlBhw7kJ4aqv+Uc/Cy
TDNYvE5SV54vjJp7VwU1mVq/VUrcmSsNySrput4fc2jIYs98XH0IWRnEVSqbbtVg
tYKbMODRF2DTtdtfGrxjvPW1CJZTGPV8rln1dSTp1ZPOp9n3fHXRRjRyHTOFw+gs
0PorxQSEHZNtLQfpXD+ou5PL14hE3f/nO227eFsDR9QJGo14U610U+0T1bMX/7mc
0gbLiXoPqQKVFqBCD1Zsq/ZKbHf2jDkg5wjaK6vfGSUEyFaeYWnTVeUDdGy74XRO
ZDVLW1lDJM2glZ7I0s6WcgRrrYoDKWkAl/X+SeFkTQuikoUXU8JXGeWuul6p5RjT
AgMBAAGjgdAwgc0wDwYDVR0TAQH/BAUwAwEB/zARBglghkgBhvhCAQEEBAMCAgQw
ewYDVR0lAQH/BHEwbwYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYB
BQUHAwgGCisGAQQBgjcCARUGCisGAQQBgjcCARYGCisGAQQBgjcKAwEGCisGAQQB
gjcKAwMGCisGAQQBgjcKAwQGCWCGSAGG+EIEATALBgNVHQ8EBAMCAQYwHQYDVR0O
BBYEFCs59meqNHm2h0jDlW5BvWu6gnkuMA0GCSqGSIb3DQEBBQUAA4IBAQAib4M1
QIJoRs0cLQRn8HGVXB3OVYTRp5e+GIdEkxRb69/mOJT6e1rqlk86OkVakK083E2B
a3Vhl4XIW7HhnIPhH/ZjZcwZ/MDiSvpkJVcmWjEeA6PxSpjqgl1bxMlJhbY1e3Bo
ps3rE40vH9+Hq3ps3FCUGAhTnXGA42L//JgU3N2XTWQXfdkoi4eVsIaf4obQT5wD
ThmuTZNLTkJOzFxFqHpuxUHO1BMgKgHirul8Fy9ydj75MJfUCCQh7Prjqf0ch5Ou
LlwRF70nULYJ6KXIaPM9icT/Wo1jWsTT+FJlXj27esuLpth7DjunWZQRjKsg3p0/
DiaGIm0JYJEoRH+u
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDu5tWlBhw7kJ4a
qv+Uc/CyTDNYvE5SV54vjJp7VwU1mVq/VUrcmSsNySrput4fc2jIYs98XH0IWRnE
VSqbbtVgtYKbMODRF2DTtdtfGrxjvPW1CJZTGPV8rln1dSTp1ZPOp9n3fHXRRjRy
HTOFw+gs0PorxQSEHZNtLQfpXD+ou5PL14hE3f/nO227eFsDR9QJGo14U610U+0T
1bMX/7mc0gbLiXoPqQKVFqBCD1Zsq/ZKbHf2jDkg5wjaK6vfGSUEyFaeYWnTVeUD
dGy74XROZDVLW1lDJM2glZ7I0s6WcgRrrYoDKWkAl/X+SeFkTQuikoUXU8JXGeWu
ul6p5RjTAgMBAAECggEBAIzAi5cSpoehiMUFglcgh+tEYewh5bM11AkHuex5NKSD
maeoxNZ7l5Yjs5jJdBzrjgyhysTmaPQGKtw1oUi6YBT+W9i7T3lhYLbbKkg6JExP
BCSIzqheyCORRYcMYZczMkZLNmxNc8y8pCGnereQ2LWu2fX3udVs53MnWMXwh8EM
v3b6Nm16Z/YMAJ5tVF3x4q43D7tjUUUabATQ7iiZA3MZia0RP/WFooZVsNLR6wym
Th2JN6t2Lde06wdEQAjDqSzKSRy7UazynzY8VkDUhQ6c7voVL1DYdjwPdpGdELdE
nqoobg1b4PRLlNFsQVtRN6Xzm5dvlHZt8hH7xviFRiECgYEA+bxkyrLyafCeuLvZ
S8DXBi3nE2BaVUPxFuVqcTGvtZVQus7vJE119wJU7clx9gE8QRujVCw7vOJxAbuM
eDVD1fQOXyWhmXsAXnTyxgwRx5Sskfh3EhPUraOJ8bUkEUZChG0mXWP0JE1IwguD
yGx5yioZyeY+VWfRXAQjwKQc3g0CgYEA9OTfQIdwcVX6rVg2ay78gjhGjwB7vX5b
kApRkOt9T+Bhks4X7Zhdy6jfgvGHZ81CtghPKkiDE8tzb+0qmM4P2ksuPtFS9XHz
X4Ne9ZpxrKbSXB+ZuGmvf3U5YSs1XgzeSDryeU5+gICgKXCNYPkv7KNHvPLLCXGk
FRVz5Vwu+l8CgYEApTtZsKc/LorOaUmYVr+rROJgDfjlK579R3nYuDX4nHrDZCYA
IZPCpgB78qwi43IjzKwdn1Z429syi8PsB6iuTkPgsmEyj1rLfrN/4TshFFyw5Q9w
sJxATfZzEWIZD/jdj8Kr7IRyhwY/SmvV8GidF/5M+8GDonxDD8+JXC75Do0CgYB4
Sf97E8vgb6odAUphpYe0cu39ifA96NYSz263L2SzGnTzBjZspz3TLpTzHa40B3ew
MC6qh0mTLIj8PlOBER5qTClgA7OrChjQHphDLWSOuLZ1N7RrB8aDZdIUb4PcOZfp
hMLbYPn0JroFANsA+gVXR/CR1/RxJBfDn3D5BSmyIwKBgHv/LTB10VbBxd9IfXS4
E9lyBm5ZLX263bZDFdMmL1Dsa9fb8ZhbU2UTroVccMcoYBLmAjUh5o0hVw9gXTi7
H/cjK5ckO6R2iFc1wtsJ4GZkNj+TdOy7l+G4dZq5dECon41SdhKD3hvjYtuZdADS
NgbKfrz5Q13KcUB7jrOFevXD
-----END PRIVATE KEY-----

110
CertTool.py Normal file
View File

@ -0,0 +1,110 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"Cert Tools, pyOpenSSL version"
__author__ = 'phoenix'
__version__ = '0.1'
CA = "CA.crt"
CERTDIR = "Certs"
# Temp list for generating certs
workingList = set()
import os
import time
import OpenSSL
def create_CA(capath):
key = OpenSSL.crypto.PKey()
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
ca = OpenSSL.crypto.X509()
ca.set_serial_number(0)
ca.set_version(2)
subj = ca.get_subject()
subj.countryName = 'CN'
subj.organizationName = 'ProxHTTPSProxy'
subj.organizationalUnitName = 'pyOpenSSL'
subj.commonName = 'ProxHTTPSProxy CA'
ca.gmtime_adj_notBefore(0)
ca.gmtime_adj_notAfter(24 * 60 * 60 * 3652)
ca.set_issuer(ca.get_subject())
ca.set_pubkey(key)
ca.add_extensions(
[OpenSSL.crypto.X509Extension(b"basicConstraints", True, b"CA:TRUE"),
# mozilla::pkix doesn't handle the Netscape Cert Type extension (which is problematic when it's marked critical)
# https://bugzilla.mozilla.org/show_bug.cgi?id=1009161
OpenSSL.crypto.X509Extension(b"nsCertType", False, b"sslCA"),
OpenSSL.crypto.X509Extension(b"extendedKeyUsage", True, b"serverAuth,clientAuth,emailProtection,timeStamping,msCodeInd,msCodeCom,msCTLSign,msSGC,msEFS,nsSGC"),
OpenSSL.crypto.X509Extension(b"keyUsage", False, b"keyCertSign, cRLSign"),
OpenSSL.crypto.X509Extension(b"subjectKeyIdentifier", False, b"hash", subject=ca)])
ca.sign(key, 'sha1')
with open(capath, 'wb') as fp:
fp.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, ca))
fp.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key))
def get_cert(name, cafile=CA, certdir=CERTDIR):
"""Return cert file path. Create it if it doesn't exist.
cafile: the CA file to create dummpy cert files
certdir: the path where cert files are looked for or created
"""
certfile = os.path.join(certdir, name + '.crt')
if not os.path.exists(certfile):
dummy_cert(cafile, certfile, name)
return certfile
def dummy_cert(cafile, certfile, commonname):
"""Generates and writes a certificate to certfile
commonname: Common name for the generated certificate
Ref: https://github.com/mitmproxy/netlib/blob/master/netlib/certutils.py
"""
if certfile in workingList:
# Another thread is working on it, wait until it finish
while True:
time.sleep(0.2)
if certfile not in workingList: break
else:
workingList.add(certfile)
with open(cafile, "rb") as file:
content = file.read()
ca = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, content)
key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, content)
cert = OpenSSL.crypto.X509()
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(60 * 60 * 24 * 3652)
cert.set_issuer(ca.get_subject())
cert.get_subject().CN = '*' + commonname if commonname.startswith('.') else commonname
cert.set_serial_number(int(time.time()*10000))
cert.set_pubkey(ca.get_pubkey())
cert.sign(key, "sha1")
with open(certfile, 'wb') as fp:
fp.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))
fp.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key))
workingList.remove(certfile)
def startup_check():
if not os.path.exists(CERTDIR):
os.mkdir(CERTDIR)
if not os.path.exists(CERTDIR):
print("%s directory does not exist!")
print("Please create it and restart the program!")
input()
raise SystemExit
if not os.path.exists(CA):
print("Creating CA ...")
create_CA(CA)
if not os.path.exists(CA):
print("Failed to create CA :(")
else:
print("* Please import created %s to your client's store of trusted certificate authorities." % CA)
print("* Please delete all files under %s directory!" % CERTDIR)
print("* Then restart the program!")
input()
raise SystemExit
startup_check()
if __name__ == "__main__":
print("All Good!")

BIN
Docs/HowItWorks.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

22
Docs/changelog.txt Normal file
View File

@ -0,0 +1,22 @@
ProxHTTPSProxyMII
=================
Version 1.2 (20141221)
--------------
+ Content is streamed to client, while not cached before sending
* Fix config auto reload
* Less exception traceback dumped
* Tagged header changed from "Tagged:Proxomitron FrontProxy/*" to "Tagged:ProxHTTPSProxyMII FrontProxy/*"
Version 1.1 (20141024)
--------------
+ Support URL bypass
+ Handle both HTTP and HTTPS
+ Auto reload config upon chagnes
Version 1.0 (20140729)
--------------
Initial release

88
Docs/readme.html Normal file
View File

@ -0,0 +1,88 @@
<!DOCTYPE html><html><head><meta charset="utf-8"><title>ProxHTTPSProxyMII</title><style></style></head><body>
<h1 id="proxhttpsproxymii">ProxHTTPSProxyMII</h1>
<p>Created to provide modern nag-free HTTPS connections for an HTTP proxy.</p>
<h2 id="how-it-works">How it works</h2>
<p><img src="http://www.proxfilter.net/proxhttpsproxy/HowItWorks.gif" alt="how it works"></p>
<h2 id="eligible-http-proxies">Eligible HTTP Proxies</h2>
<ul>
<li>The <a href="http://www.proxomitron.info">Proxomitron</a>, for which ProxHTTPSProxy was created :)</li>
<li>Any that have the ability to forward all requests with a &quot;Tagged:ProxHTTPSProxyMII FrontProxy/*&quot; header to the ProxHTTPSProxyMII rear server.</li>
<li>Any that can be ran as two instances, one for true http and another for &quot;tagged&quot; http</li>
<li>Any that will only be used to monitor https traffic </li>
</ul>
<h2 id="install">Install</h2>
<ul>
<li>ProxHTTPSProxy&#39;s &quot;CA.crt&quot; to the Client&#39;s store of trusted certificate authorities.</li>
</ul>
<h2 id="configure">Configure</h2>
<ul>
<li>The Client to use the ProxHTTPSProxy front server at 127.0.0.1 on port 8079 for secure connections.</li>
<li>The HTTP proxy to receive requests at 127.0.0.1 on port 8080.</li>
<li>The HTTP proxy to forward requests to the ProxHTTPSProxy rear server at 127.0.0.1 on port 8081.</li>
<li>Edit &quot;Config.ini&quot; to change these requirements.</li>
</ul>
<h2 id="execute">Execute</h2>
<p>ProxHTTPSProxy.exe to start.</p>
<h2 id="remember">Remember</h2>
<p>Be aware and careful! Use a direct connection when you don&#39;t want any mistakes made.</p>
<p>Use at your own risk!</p>
<p>Have fun!</p>
<h2 id="discuss">Discuss</h2>
<p><a href="http://prxbx.com/forums/showthread.php?tid=2172">http://prxbx.com/forums/showthread.php?tid=2172</a></p>
<h2 id="author">Author</h2>
<ul>
<li>phoenix (aka whenever)</li>
<li>JJoe (test and doc)</li>
</ul>
<h1 id="proxomitron-tips">Proxomitron Tips</h1>
<h2 id="to-use">To use</h2>
<ul>
<li><p>Add the ProxHTTPSProxy rear server to the Proxomitron&#39;s list of external proxies</p>
<p><code>127.0.0.1:8081 ProxHTTPSProxy</code></p>
</li>
<li><p>Add to Proxomitron&#39;s &quot;Bypass URLs that match this expression&quot; field if it is empty</p>
<p><code>$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$SETPROXY(127.0.0.1:8081)(^)</code></p>
</li>
<li><p>Add to the beginning of the entry in Proxomitron&#39;s &quot;Bypass URLs that match this expression&quot; field if it is <strong>not</strong> empty</p>
<p><code>$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$SETPROXY(127.0.0.1:8081)(^)|</code> </p>
</li>
</ul>
<h2 id="tips">Tips</h2>
<ul>
<li><p>Proxomitron always executes some commands in &quot;Bypass URLs that match this expression&quot; field. Adding the entry there allows the Proxomitron to use the rear server when in Bypass mode.</p>
<p>This undocumented feature brings many possibilities but remember, an actual match triggers bypass of filtering!</p>
<ul>
<li><code>$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)</code> checks for the header that indicates an https request.</li>
<li><code>$SETPROXY(127.0.0.1:8081)</code> is executed when found.</li>
<li><code>(^)</code> expression never matches. </li>
</ul>
</li>
<li><p>Identify https connections by testing for the &quot;Tagged&quot; request header that the ProxHTTPSProxy front server adds to the request. </p>
<p><code>$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)</code></p>
</li>
<li><p>For local file requests, use an expression like </p>
<p><code>$USEPROXY(false)$RDIR(http://local.ptron/killed.gif)</code></p>
</li>
<li><p>Before redirecting &quot;Tagged&quot; connections to external resources consider removing the &quot;Tagged&quot; header. </p>
</li>
<li><p>If needed, the Proxomitron can still do https. After adding the ssl files to the Proxomitron, use a header filter like</p>
<pre><code>[HTTP headers]
In = FALSE
Out = TRUE
Key = &quot;Tagged: Use Proxomitron for https://badcert.com&quot;
URL = &quot;badcert.com$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$USEPROXY(false)$RDIR(https://badcert.com)&quot;
</code></pre><p>This filter also removes the &quot;Tagged&quot; header. </p>
</li>
</ul>
<h2 id="for-the-current-sidki-set">For the current sidki set</h2>
<ol>
<li><p>Add the following two lines to Exceptions-U</p>
<pre><code>$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$SET(keyword=$GET(keyword)i_proxy:3.)(^)
~(^$TST(keyword=i_proxy:[03].))$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$SET(keyword=$GET(keyword)i_proxy:3.)(^)
</code></pre></li>
<li><p>Redirect connections to http resources with an expression like</p>
<p><code>$USEPROXY(false)$SET(keyword=i_proxy:0.)$RDIR(http://local.ptron/killed.gif)</code></p>
</li>
</ol>
</body></html>

119
Docs/readme.txt Normal file
View File

@ -0,0 +1,119 @@
ProxHTTPSProxyMII
=================
Created to provide modern nag-free HTTPS connections for an HTTP proxy.
How it works
----
![how it works](http://www.proxfilter.net/proxhttpsproxy/HowItWorks.gif)
Eligible HTTP Proxies
----
* The [Proxomitron](http://www.proxomitron.info), for which ProxHTTPSProxy was created :)
* Any that have the ability to forward all requests with a "Tagged:ProxHTTPSProxyMII FrontProxy/*" header to the ProxHTTPSProxyMII rear server.
* Any that can be ran as two instances, one for true http and another for "tagged" http
* Any that will only be used to monitor https traffic
Install
----
* ProxHTTPSProxy's "CA.crt" to the Client's store of trusted certificate authorities.
Configure
----
* The Client to use the ProxHTTPSProxy front server at 127.0.0.1 on port 8079 for secure connections.
* The HTTP proxy to receive requests at 127.0.0.1 on port 8080.
* The HTTP proxy to forward requests to the ProxHTTPSProxy rear server at 127.0.0.1 on port 8081.
* Edit "Config.ini" to change these requirements.
Execute
----
ProxHTTPSProxy.exe to start.
Remember
----
Be aware and careful! Use a direct connection when you don't want any mistakes made.
Use at your own risk!
Have fun!
Discuss
----
<http://prxbx.com/forums/showthread.php?tid=2172>
Author
----
* phoenix (aka whenever)
* JJoe (test and doc)
Proxomitron Tips
================
To use
----
* Add the ProxHTTPSProxy rear server to the Proxomitron's list of external proxies
`127.0.0.1:8081 ProxHTTPSProxyMII`
* Add to Proxomitron's "Bypass URLs that match this expression" field if it is empty
`$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$SETPROXY(127.0.0.1:8081)(^)`
* Add to the beginning of the entry in Proxomitron's "Bypass URLs that match this expression" field if it is **not** empty
`$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$SETPROXY(127.0.0.1:8081)(^)|`
Tips
----
* Proxomitron always executes some commands in "Bypass URLs that match this expression" field. Adding the entry there allows the Proxomitron to use the rear server when in Bypass mode.
This undocumented feature brings many possibilities but remember, an actual match triggers bypass of filtering!
- `$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)` checks for the header that indicates an https request.
- `$SETPROXY(127.0.0.1:8081)` is executed when found.
- `(^)` expression never matches.
* Identify https connections by testing for the "Tagged" request header that the ProxHTTPSProxy front server adds to the request.
`$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)`
* For local file requests, use an expression like
`$USEPROXY(false)$RDIR(http://local.ptron/killed.gif)`
* Before redirecting "Tagged" connections to external resources consider removing the "Tagged" header.
* If needed, the Proxomitron can still do https. After adding the ssl files to the Proxomitron, use a header filter like
```
[HTTP headers]
In = FALSE
Out = TRUE
Key = "Tagged: Use Proxomitron for https://badcert.com"
URL = "badcert.com$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$USEPROXY(false)$RDIR(https://badcert.com)"
```
This filter also removes the "Tagged" header.
For the current sidki set
----
1. Add the following two lines to Exceptions-U
```
$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$SET(keyword=$GET(keyword)i_proxy:3.)(^)
~(^$TST(keyword=i_proxy:[03].))$OHDR(Tagged:ProxHTTPSProxyMII FrontProxy/*)$SET(keyword=$GET(keyword)i_proxy:3.)(^)
```
2. Redirect connections to http resources with an expression like
`$USEPROXY(false)$SET(keyword=i_proxy:0.)$RDIR(http://local.ptron/killed.gif)`

347
ProxHTTPSProxy.py Normal file
View File

@ -0,0 +1,347 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"A Proxomitron Helper Program"
_name = 'ProxHTTPSProxyMII'
__author__ = 'phoenix'
__version__ = 'v1.2'
CONFIG = "config.ini"
CA_CERTS = "cacert.pem"
import os
import time
import configparser
import fnmatch
import logging
import threading
import ssl
import urllib3
#https://urllib3.readthedocs.org/en/latest/security.html#insecurerequestwarning
urllib3.disable_warnings()
from socketserver import ThreadingMixIn
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse
from ProxyTool import ProxyRequestHandler, get_cert
from colorama import init, Fore, Back, Style
init(autoreset=True)
class LoadConfig:
def __init__(self, configfile):
self.config = configparser.ConfigParser(allow_no_value=True,
inline_comment_prefixes=('#',))
self.config.read(configfile)
self.PROXADDR = self.config['GENERAL'].get('ProxAddr')
self.FRONTPORT = int(self.config['GENERAL'].get('FrontPort'))
self.REARPORT = int(self.config['GENERAL'].get('RearPort'))
self.DEFAULTPROXY = self.config['GENERAL'].get('DefaultProxy')
self.LOGLEVEL = self.config['GENERAL'].get('LogLevel')
class ConnectionPools:
"""
self.pools is a list of {'proxy': 'http://127.0.0.1:8080',
'pool': urllib3.ProxyManager() object,
'patterns': ['ab.com', 'bc.net', ...]}
self.getpool() is a method that returns pool based on host matching
"""
# Windows default CA certificates are incomplete
# See: http://bugs.python.org/issue20916
# cacert.pem sources:
# - http://curl.haxx.se/docs/caextract.html
# - http://certifi.io/en/latest/
# ssl_version="TLSv1" to specific version
sslparams = dict(cert_reqs="REQUIRED", ca_certs=CA_CERTS)
# IE: http://support2.microsoft.com/kb/181050/en-us
# Firefox about:config
# network.http.connection-timeout 90
# network.http.response.timeout 300
timeout = urllib3.util.timeout.Timeout(connect=90.0, read=300.0)
def __init__(self, config):
self.file = config
self.file_timestamp = os.path.getmtime(config)
self.loadConfig()
def loadConfig(self):
# self.conf has to be inited each time for reloading
self.conf = configparser.ConfigParser(allow_no_value=True, delimiters=('=',),
inline_comment_prefixes=('#',))
self.conf.read(self.file)
self.pools = []
proxy_sections = [section for section in self.conf.sections()
if section.startswith('PROXY')]
for section in proxy_sections:
proxy = section.split()[1]
self.pools.append(dict(proxy=proxy,
# maxsize is the max. number of connections to the same server
pool=[urllib3.ProxyManager(proxy, 10, maxsize=8, timeout=self.timeout, **self.sslparams),
urllib3.ProxyManager(proxy, 10, maxsize=8, timeout=self.timeout)],
patterns=list(self.conf[section].keys())))
default_proxy = self.conf['GENERAL'].get('DefaultProxy')
default_pool = ([urllib3.ProxyManager(default_proxy, 10, maxsize=8, timeout=self.timeout, **self.sslparams),
urllib3.ProxyManager(default_proxy, 10, maxsize=8, timeout=self.timeout)]
if default_proxy else
[urllib3.PoolManager(10, maxsize=8, timeout=self.timeout, **self.sslparams),
urllib3.PoolManager(10, maxsize=8, timeout=self.timeout)])
self.pools.append({'proxy': default_proxy, 'pool': default_pool, 'patterns': '*'})
self.noverifylist = list(self.conf['SSL No-Verify'].keys())
self.blacklist = list(self.conf['BLACKLIST'].keys())
self.sslpasslist = list(self.conf['SSL Pass-Thru'].keys())
self.bypasslist = list(self.conf['BYPASS URL'].keys())
def reloadConfig(self):
while True:
mtime = os.path.getmtime(self.file)
if mtime > self.file_timestamp:
self.file_timestamp = mtime
self.loadConfig()
logger.info(Fore.RED + Style.BRIGHT
+ "*" * 20 + " CONFIG RELOADED " + "*" * 20)
time.sleep(1)
def getpool(self, host, httpmode=False):
noverify = True if httpmode or any((fnmatch.fnmatch(host, pattern) for pattern in self.noverifylist)) else False
for pool in self.pools:
if any((fnmatch.fnmatch(host, pattern) for pattern in pool['patterns'])):
return pool['proxy'], pool['pool'][noverify], noverify
class FrontServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
pass
class RearServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
pass
class FrontRequestHandler(ProxyRequestHandler):
"""
Sit between the client and Proxomitron
Convert https request to http
"""
server_version = "%s FrontProxy/%s" % (_name, __version__)
def do_CONNECT(self):
"Descrypt https request and dispatch to http handler"
# request line: CONNECT www.example.com:443 HTTP/1.1
self.host, self.port = self.path.split(":")
self.proxy, self.pool, self.noverify = pools.getpool(self.host)
if any((fnmatch.fnmatch(self.host, pattern) for pattern in pools.blacklist)):
# BLACK LIST
self.deny_request()
logger.info(Fore.CYAN + 'Denied by blacklist: %s' % self.host)
elif any((fnmatch.fnmatch(self.host, pattern) for pattern in pools.sslpasslist)):
# SSL Pass-Thru
if self.proxy and self.proxy.startswith('https'):
self.forward_to_https_proxy()
elif self.proxy and self.proxy.startswith('socks5'):
self.forward_to_socks5_proxy()
else:
self.tunnel_traffic()
# Upstream server or proxy of the tunnel is closed explictly, so we close the local connection too
self.close_connection = 1
else:
# SSL MITM
self.wfile.write(("HTTP/1.1 200 Connection established\r\n" +
"Proxy-agent: %s\r\n" % self.version_string() +
"\r\n").encode('ascii'))
commonname = '.' + self.host.partition('.')[-1] if self.host.count('.') >= 2 else self.host
dummycert = get_cert(commonname)
# set a flag for do_METHOD
self.ssltunnel = True
ssl_sock = ssl.wrap_socket(self.connection, keyfile=dummycert, certfile=dummycert, server_side=True)
# Ref: Lib/socketserver.py#StreamRequestHandler.setup()
self.connection = ssl_sock
self.rfile = self.connection.makefile('rb', self.rbufsize)
self.wfile = self.connection.makefile('wb', self.wbufsize)
# dispatch to do_METHOD()
self.handle_one_request()
def do_METHOD(self):
"Forward request to Proxomitron"
if self.ssltunnel:
# https request
host = self.host if self.port == '443' else "%s:%s" % (self.host, self.port)
url = "https://%s%s" % (host, self.path)
self.bypass = any((fnmatch.fnmatch(url, pattern) for pattern in pools.bypasslist))
if not self.bypass:
url = "http://%s%s" % (host, self.path)
# Tag the request so Proxomitron can recognize it
self.headers["Tagged"] = self.version_string()
else:
# http request
self.host = urlparse(self.path).hostname
if any((fnmatch.fnmatch(self.host, pattern) for pattern in pools.blacklist)):
# BLACK LIST
self.deny_request()
logger.info(Fore.CYAN + 'Denied by blacklist: %s' % self.host)
return
host = urlparse(self.path).netloc
self.proxy, self.pool, self.noverify = pools.getpool(self.host, httpmode=True)
self.bypass = any((fnmatch.fnmatch('http://' + host + urlparse(self.path).path, pattern) for pattern in pools.bypasslist))
url = self.path
self.url = url
prefix = '[P]' if self.proxy else '[D]'
if self.bypass:
prefix += '[B]'
pool = self.pool if self.bypass else proxpool
data_length = self.headers.get("Content-Length")
self.postdata = self.rfile.read(int(data_length)) if data_length else None
# Remove hop-by-hop headers
self.purge_headers(self.headers)
# pool.urlopen() expects a dict like headers container for http request
headers = urllib3._collections.HTTPHeaderDict()
[headers.add(key, value) for (key, value) in self.headers.items()]
r = None
try:
# Sometimes 302 redirect would fail with "BadStatusLine" exception, and IE11 doesn't restart the request.
# retries=1 instead of retries=False fixes it.
r = pool.urlopen(self.command, url, body=self.postdata, headers=headers,
retries=1, redirect=False, preload_content=False, decode_content=False)
if not self.ssltunnel:
logger.info(Fore.GREEN + '%s "%s %s %s" %s %s' %
(prefix, self.command, url, self.request_version, r.status, r.getheader('Content-Length', '-')))
self.send_response_only(r.status, r.reason)
# HTTPResponse.getheader() combines multiple same name headers into one
# https://login.yahoo.com would fail to login
# Use HTTPResponse.msg instead
r.headers = r._original_response.msg
self.write_headers(r.headers)
if self.command == 'HEAD' or r.status in (100, 101, 204, 304):
written = None
else:
written = self.stream_to_client(r)
if "Content-Length" not in r.headers and 'Transfer-Encoding' not in r.headers:
self.close_connection = 1
# Intend to catch regular http and bypass http/https requests exceptions
# Regular https request exceptions should be handled by rear server
except urllib3.exceptions.TimeoutError as e:
self.sendout_error(url, 504, message="Timeout", explain=e)
logger.warning(Fore.YELLOW + '[F] %s on "%s %s"', e, self.command, url)
except (urllib3.exceptions.HTTPError,) as e:
self.sendout_error(url, 502, message="HTTPError", explain=e)
logger.warning(Fore.YELLOW + '[F] %s on "%s %s"', e, self.command, url)
finally:
if r:
# Release the connection back into the pool
r.release_conn()
do_GET = do_POST = do_HEAD = do_PUT = do_DELETE = do_OPTIONS = do_METHOD
class RearRequestHandler(ProxyRequestHandler):
"""
Supposed to be the parent proxy for Proxomitron for tagged requests
Convert http request to https
"""
server_version = "%s RearProxy/%s" % (_name, __version__)
def do_METHOD(self):
"Convert http request to https"
# request line: GET http://somehost.com/path?attr=value HTTP/1.1
url = "https" + self.path[4:]
self.host = urlparse(self.path).hostname
proxy, pool, noverify = pools.getpool(self.host)
prefix = '[P]' if proxy else '[D]'
data_length = self.headers.get("Content-Length")
self.postdata = self.rfile.read(int(data_length)) if data_length else None
self.purge_headers(self.headers)
# Remove the tag
del self.headers["Tagged"]
# pool.urlopen() expects a dict like headers container for http request
headers = urllib3._collections.HTTPHeaderDict()
[headers.add(key, value) for (key, value) in self.headers.items()]
r = None
try:
r = pool.urlopen(self.command, url, body=self.postdata, headers=headers,
retries=1, redirect=False, preload_content=False, decode_content=False)
if proxy:
logger.debug('Using Proxy - %s' % proxy)
color = Fore.RED if noverify else Fore.GREEN
logger.info(color + '%s "%s %s" %s %s' %
(prefix, self.command, url, r.status, r.getheader('Content-Length', '-')))
self.send_response_only(r.status, r.reason)
# HTTPResponse.getheader() combines multiple same name headers into one
# https://login.yahoo.com would fail to login
# Use HTTPResponse.msg instead
r.headers = r._original_response.msg
self.write_headers(r.headers)
if self.command == 'HEAD' or r.status in (100, 101, 204, 304):
written = None
else:
written = self.stream_to_client(r)
if "Content-Length" not in r.headers and 'Transfer-Encoding' not in r.headers:
self.close_connection = 1
except urllib3.exceptions.SSLError as e:
self.sendout_error(url, 417, message="SSL Certificate Failed", explain=e)
logger.error(Fore.RED + Style.BRIGHT + "[SSL Certificate Error] " + url)
except urllib3.exceptions.TimeoutError as e:
self.sendout_error(url, 504, message="Timeout", explain=e)
logger.warning(Fore.YELLOW + '[R] %s on "%s %s"', e, self.command, url)
except (urllib3.exceptions.HTTPError,) as e:
self.sendout_error(url, 502, message="HTTPError", explain=e)
logger.warning(Fore.YELLOW + '[R] %s on "%s %s"', e, self.command, url)
finally:
if r:
# Release the connection back into the pool
r.release_conn()
do_GET = do_POST = do_HEAD = do_PUT = do_DELETE = do_OPTIONS = do_METHOD
"""
#Information#
* Python default ciphers: http://bugs.python.org/issue20995
* SSL Cipher Suite Details of Your Browser: https://cc.dcsec.uni-hannover.de/
* https://wiki.mozilla.org/Security/Server_Side_TLS
"""
try:
if os.name == 'nt':
import ctypes
ctypes.windll.kernel32.SetConsoleTitleW('%s %s' % (_name, __version__))
config = LoadConfig(CONFIG)
logger = logging.getLogger(__name__)
logger.setLevel(getattr(logging, config.LOGLEVEL, logging.INFO))
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(message)s', datefmt='[%H:%M:%S]')
handler.setFormatter(formatter)
logger.addHandler(handler)
pools = ConnectionPools(CONFIG)
proxpool = urllib3.ProxyManager(config.PROXADDR, 10, maxsize=8,
# A little longer than timeout of rear pool
# to avoid trigger front server exception handler
timeout=urllib3.util.timeout.Timeout(connect=90.0, read=310.0))
frontserver = FrontServer(('', config.FRONTPORT), FrontRequestHandler)
rearserver = RearServer(('', config.REARPORT), RearRequestHandler)
for worker in (frontserver.serve_forever, rearserver.serve_forever,
pools.reloadConfig):
thread = threading.Thread(target=worker)
thread.dameon = True
thread.start()
print("=" * 76)
print('%s %s (urllib3/%s)' % (_name, __version__, urllib3.__version__))
print()
print(' FrontServer : localhost:%s' % config.FRONTPORT)
print(' RearServer : localhost:%s' % config.REARPORT)
print(' ParentServer : %s' % config.DEFAULTPROXY)
print(' Proxomitron : ' + config.PROXADDR)
print("=" * 76)
except KeyboardInterrupt:
print("Quitting...")

346
ProxyTool.py Normal file
View File

@ -0,0 +1,346 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"HTTP Proxy Tools, pyOpenSSL version"
_name = "ProxyTool"
__author__ = 'phoenix'
__version__ = '1.0'
import time
from datetime import datetime
import logging
import cgi
import socket
import select
import selectors
import ssl
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
from CertTool import get_cert
from colorama import init, Fore, Back, Style
init(autoreset=True)
logger = logging.getLogger('__main__')
message_format = """\
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Proxy Error: %(code)d</title>
</head>
<body>
<h1>%(code)d: %(message)s</h1>
<p>The following error occurred while trying to access <strong>%(url)s</strong></p>
<p><strong>%(explain)s</strong></p>
<hr>Generated on %(now)s by %(server)s.
</body>
</html>
"""
def read_write(socket1, socket2):
"Read and Write contents between 2 sockets, wait 5s for no data before return"
start = time.time()
with selectors.DefaultSelector() as selector:
socket1.setblocking(False)
socket2.setblocking(False)
selector.register(socket1, selectors.EVENT_READ)
selector.register(socket2, selectors.EVENT_READ)
while True:
tasks = selector.select(5)
if not tasks: break
for key, events in tasks:
if events & selectors.EVENT_READ:
reader = key.fileobj
writer = socket2 if reader is socket1 else socket1
try:
data = reader.recv(1024)
if data:
writer.sendall(data)
else:
# EOF
selector.unregister(reader)
selector.unregister(writer)
except (ConnectionAbortedError, ConnectionResetError, BrokenPipeError):
pass
logger.debug("took %.2Fs" % (time.time()-start))
def read_write(socket1, socket2, max_idling=10):
"Read and Write contents between 2 sockets"
iw = [socket1, socket2]
ow = []
count = 0
while True:
count += 1
(ins, _, exs) = select.select(iw, ow, iw, 1)
if exs: break
if ins:
for reader in ins:
writer = socket2 if reader is socket1 else socket1
try:
data = reader.recv(1024)
if data:
writer.send(data)
count = 0
except (ConnectionAbortedError, ConnectionResetError, BrokenPipeError):
pass
if count == max_idling: break
class ProxyRequestHandler(BaseHTTPRequestHandler):
"""RequestHandler with do_CONNECT method defined
"""
server_version = "%s/%s" % (_name, __version__)
# do_CONNECT() will set self.ssltunnel to override this
ssltunnel = False
# Override default value 'HTTP/1.0'
protocol_version = 'HTTP/1.1'
def do_CONNECT(self):
"Descrypt https request and dispatch to http handler"
# request line: CONNECT www.example.com:443 HTTP/1.1
self.host, self.port = self.path.split(":")
# SSL MITM
self.wfile.write(("HTTP/1.1 200 Connection established\r\n" +
"Proxy-agent: %s\r\n" % self.version_string() +
"\r\n").encode('ascii'))
commonname = '.' + self.host.partition('.')[-1] if self.host.count('.') >= 2 else self.host
dummycert = get_cert(commonname)
# set a flag for do_METHOD
self.ssltunnel = True
ssl_sock = ssl.wrap_socket(self.connection, keyfile=dummycert, certfile=dummycert, server_side=True)
# Ref: Lib/socketserver.py#StreamRequestHandler.setup()
self.connection = ssl_sock
self.rfile = self.connection.makefile('rb', self.rbufsize)
self.wfile = self.connection.makefile('wb', self.wbufsize)
# dispatch to do_METHOD()
self.handle_one_request()
def handle_one_request(self):
"""Catch more exceptions than default
Intend to catch exceptions on local side
Exceptions on remote side should be handled in do_*()
"""
try:
BaseHTTPRequestHandler.handle_one_request(self)
return
except (ConnectionError, FileNotFoundError) as e:
logger.warning(Fore.RED + "%s", e)
except (ssl.SSLEOFError, ssl.SSLError) as e:
if hasattr(self, 'url'):
# Happens after the tunnel is established
logger.warning(Fore.YELLOW + '"%s" while operating on established local SSL tunnel for [%s]' % (e, self.url))
else:
logger.warning(Fore.YELLOW + '"%s" while trying to establish local SSL tunnel for [%s]' % (e, self.path))
self.close_connection = 1
def sendout_error(self, url, code, message=None, explain=None):
"Modified from http.server.send_error() for customized display"
try:
shortmsg, longmsg = self.responses[code]
except KeyError:
shortmsg, longmsg = '???', '???'
if message is None:
message = shortmsg
if explain is None:
explain = longmsg
content = (message_format %
{'code': code, 'message': message, 'explain': explain,
'url': url, 'now': datetime.today(), 'server': self.server_version})
body = content.encode('UTF-8', 'replace')
self.send_response_only(code, message)
self.send_header("Content-Type", self.error_content_type)
self.send_header('Content-Length', int(len(body)))
self.end_headers()
if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
self.wfile.write(body)
def deny_request(self):
self.send_response_only(403)
self.send_header('Content-Length', 0)
self.end_headers()
def redirect(self, url):
self.send_response_only(302)
self.send_header('Content-Length', 0)
self.send_header('Location', url)
self.end_headers()
def forward_to_https_proxy(self):
"Forward https request to upstream https proxy"
logger.debug('Using Proxy - %s' % self.proxy)
proxy_host, proxy_port = self.proxy.split('//')[1].split(':')
server_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
server_conn.connect((proxy_host, int(proxy_port)))
server_conn.send(('CONNECT %s HTTP/1.1\r\n\r\n' % self.path).encode('ascii'))
server_conn.settimeout(0.1)
datas = b''
while True:
try:
data = server_conn.recv(4096)
except socket.timeout:
break
if data:
datas += data
else:
break
server_conn.setblocking(True)
if b'200' in datas and b'established' in datas.lower():
logger.info(Fore.CYAN + '[P] SSL Pass-Thru: https://%s/' % self.path)
self.wfile.write(("HTTP/1.1 200 Connection established\r\n" +
"Proxy-agent: %s\r\n\r\n" % self.version_string()).encode('ascii'))
read_write(self.connection, server_conn)
else:
logger.warning(Fore.YELLOW + 'Proxy %s failed.', self.proxy)
if datas:
logger.debug(datas)
self.wfile.write(datas)
finally:
# We don't maintain a connection reuse pool, so close the connection anyway
server_conn.close()
def forward_to_socks5_proxy(self):
"Forward https request to upstream socks5 proxy"
logger.warning(Fore.YELLOW + 'Socks5 proxy not implemented yet, please use https proxy')
def tunnel_traffic(self):
"Tunnel traffic to remote host:port"
logger.info(Fore.CYAN + '[D] SSL Pass-Thru: https://%s/' % self.path)
server_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
server_conn.connect((self.host, int(self.port)))
self.wfile.write(("HTTP/1.1 200 Connection established\r\n" +
"Proxy-agent: %s\r\n" % self.version_string() +
"\r\n").encode('ascii'))
read_write(self.connection, server_conn)
except TimeoutError:
self.wfile.write(b"HTTP/1.1 504 Gateway Timeout\r\n\r\n")
logger.warning(Fore.YELLOW + 'Timed Out: https://%s:%s/' % (self.host, self.port))
except socket.gaierror as e:
self.wfile.write(b"HTTP/1.1 503 Service Unavailable\r\n\r\n")
logger.warning(Fore.YELLOW + '%s: https://%s:%s/' % (e, self.host, self.port))
finally:
# We don't maintain a connection reuse pool, so close the connection anyway
server_conn.close()
def ssl_get_response(self, conn):
try:
server_conn = ssl.wrap_socket(conn, cert_reqs=ssl.CERT_REQUIRED, ca_certs="cacert.pem", ssl_version=ssl.PROTOCOL_TLSv1)
server_conn.sendall(('%s %s HTTP/1.1\r\n' % (self.command, self.path)).encode('ascii'))
server_conn.sendall(self.headers.as_bytes())
if self.postdata:
server_conn.sendall(self.postdata)
while True:
data = server_conn.recv(4096)
if data:
self.wfile.write(data)
else: break
except (ssl.SSLEOFError, ssl.SSLError) as e:
logger.error(Fore.RED + Style.BRIGHT + "[SSLError]")
self.send_error(417, message="Exception %s" % str(e.__class__), explain=str(e))
def purge_headers(self, headers):
"Remove hop-by-hop headers that shouldn't pass through a Proxy"
for name in ["Connection", "Keep-Alive", "Upgrade",
"Proxy-Connection", "Proxy-Authenticate"]:
del headers[name]
def write_headers(self, headers):
self.purge_headers(headers)
for key, value in headers.items():
self.send_header(key, value)
self.end_headers()
def stream_to_client(self, response):
bufsize = 1024 * 64
need_chunked = 'Transfer-Encoding' in response.headers
written = 0
while True:
data = response.read(bufsize)
if not data:
if need_chunked:
self.wfile.write(b'0\r\n\r\n')
break
if need_chunked:
self.wfile.write(('%x\r\n' % len(data)).encode('ascii'))
self.wfile.write(data)
if need_chunked:
self.wfile.write(b'\r\n')
written += len(data)
return written
def http_request_info(self):
"""Return HTTP request information in bytes
"""
context = ["CLIENT VALUES:",
"client_address = %s" % str(self.client_address),
"requestline = %s" % self.requestline,
"command = %s" % self.command,
"path = %s" % self.path,
"request_version = %s" % self.request_version,
"",
"SERVER VALUES:",
"server_version = %s" % self.server_version,
"sys_version = %s" % self.sys_version,
"protocol_version = %s" % self.protocol_version,
"",
"HEADER RECEIVED:"]
for name, value in sorted(self.headers.items()):
context.append("%s = %s" % (name, value.rstrip()))
if self.command == "POST":
context.append("\r\nPOST VALUES:")
form = cgi.FieldStorage(fp=self.rfile,
headers=self.headers,
environ={'REQUEST_METHOD': 'POST'})
for field in form.keys():
fielditem = form[field]
if fielditem.filename:
# The field contains an uploaded file
file_data = fielditem.file.read()
file_len = len(file_data)
context.append('Uploaded %s as "%s" (%d bytes)'
% (field, fielditem.filename, file_len))
else:
# Regular form value
context.append("%s = %s" % (field, fielditem.value))
return("\r\n".join(context).encode('ascii'))
def demo():
PORT = 8000
class ProxyServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
pass
class RequestHandler(ProxyRequestHandler):
"Displaying HTTP request information"
server_version = "DemoProxy/0.1"
def do_METHOD(self):
"Universal method for GET, POST, HEAD, PUT and DELETE"
message = self.http_request_info()
self.send_response(200)
# 'Content-Length' is important for HTTP/1.1
self.send_header('Content-Length', len(message))
self.end_headers()
self.wfile.write(message)
do_GET = do_POST = do_HEAD = do_PUT = do_DELETE = do_OPTIONS = do_METHOD
print('%s serving now, <Ctrl-C> to stop ...' % RequestHandler.server_version)
print('Listen Addr : localhost:%s' % PORT)
print("-" * 10)
server = ProxyServer(('', PORT), RequestHandler)
server.serve_forever()
if __name__ == '__main__':
try:
demo()
except KeyboardInterrupt:
print("Quitting...")

5160
cacert.pem Normal file

File diff suppressed because it is too large Load Diff

63
config.ini Normal file
View File

@ -0,0 +1,63 @@
### The parent proxy has to support CONNECT method, if you want to proxy HTTPS requests
###
### Proxy setting applies to HTTPS requests only, as it is applied by the Rear Server
### HTTP requests are passed to and handled by Proxomitron, please set up Proxomitron for proxy
###
### Socks proxy support
### https://github.com/shazow/urllib3/pull/284
[GENERAL]
ProxAddr = http://localhost:8080
FrontPort = 8079
RearPort = 8081
# DefaultProxy = http://127.0.0.1:8118
# Proper values for LogLevel are ERROR, WARNING, INFO, DEBUG
# Default is INFO if unset
LogLevel =
# * matches everything
# ? matches any single character
# [seq] matches any character in seq
# [!seq] matches any character not in seq
[PROXY http://192.168.178.8:8123]
duckduckgo.com
*.s3.amazonaws.com
[PROXY http://192.168.178.8:8124]
test.com
### Ignore SSL certificate verify, Use at your own risk!!!
### Proxy setting still effective
[SSL No-Verify]
*.12306.cn
[BLACKLIST]
*.doubleclick.net
*.google-analytics.com
### Bypass Proxomitron and the Rear Server, Proxy setting still effective
### SSL certificate verify will be done by the browser
[SSL Pass-Thru]
pypi.python.org
www.gstatic.com
watson.telemetry.microsoft.com
*.sync.services.mozilla.com
*.mega.co.nz
# Microsoft SmartScreen Filter Service
*.smartscreen.microsoft.com
urs.microsoft.com
# NoScript uses https://secure.informaction.com/ipecho to detect the WAN IP
# https://addons.mozilla.org/en-US/firefox/addon/noscript/privacy/
secure.informaction.com
### Bypass Proxomitron and the Rear Server, Proxy setting still effective
### This section supports URL matching
[BYPASS URL]
*.zip
*.rar
*.exe
*.pdf