Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@scturtle
Last active June 22, 2020 21:51
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save scturtle/4f2d95507b2d357f1b17a538a1d36d82 to your computer and use it in GitHub Desktop.
Save scturtle/4f2d95507b2d357f1b17a538a1d36d82 to your computer and use it in GitHub Desktop.
use opera's built-in VPN as proxy
#!/usr/bin/env python3
import asyncio
from vpn import get_proxy
proxy = port = auth = None
pool = asyncio.Queue(5)
psize = 0
async def process_client(client_reader, client_writer, *, CHUNK=4096):
global psize
client_name = client_writer.get_extra_info('peername')
print('Client connected:', client_name)
try:
remote_reader, remote_writer = pool.get_nowait()
except asyncio.QueueEmpty:
if psize < pool.maxsize:
psize += 1
print('new remote connection:', psize)
remote_reader, remote_writer = await asyncio.open_connection(
host=proxy, port=port, ssl=True, server_hostname='us.opera-proxy.net')
else:
remote_reader, remote_writer = await pool.get()
headers = []
content_length = 0
while True:
line = await client_reader.readline()
if line.startswith(b'Content-Length'):
content_length = int(line.split(b' ')[1])
if line == b'\r\n':
break
headers.append(line)
headers = b''.join(headers) + auth + b'\r\n'
# print(headers)
remote_writer.write(headers)
# HTTPS tunnel
if headers.startswith(b'CONNECT'):
async def forward():
while True:
req = await client_reader.read(CHUNK)
# print('> ', req)
if not req:
break
remote_writer.write(req)
async def backward():
while True:
res = await remote_reader.read(CHUNK)
# print('< ', res)
if not res:
break
client_writer.write(res)
await asyncio.wait([asyncio.ensure_future(forward()),
asyncio.ensure_future(backward())])
print('tunnel done:', client_name)
else: # plain HTTP data
sent = 0
while sent < content_length:
req = await client_reader.read(CHUNK)
sent += len(req)
# print('> ', req)
remote_writer.write(req)
await remote_writer.drain()
while True:
res = await remote_reader.read(CHUNK)
# print('< ', res)
client_writer.write(res)
if len(res) < CHUNK:
break
await client_writer.drain()
client_writer.close()
await pool.put((remote_reader, remote_writer))
print('Client finished:', client_name)
def client_handler(client_reader, client_writer):
asyncio.ensure_future(process_client(client_reader, client_writer))
if __name__ == '__main__':
auth, proxy, port = get_proxy()
auth = 'Proxy-Authorization: BASIC {}\r\n'.format(auth).encode('ascii')
loop = asyncio.get_event_loop()
server = loop.run_until_complete(asyncio.start_server(client_handler, port=8888))
print('Started HTTP proxy at', server.sockets[0].getsockname())
loop.run_forever()
import os
import ssl
import socket
import base64
import hashlib
import requests
import http.client
def get_proxy():
# custom
email = '09D3AE09-837D-42B2-BB80-1552248CBFF7@se0304.surfeasy.vpn'
# must be sha1
password = '427D88E5AA4DCF2F6BEFE53B7575A704798D095B'
device_hash = '4BE7D6F1BD040DE45A371FD831167BC108554111'
apikey = '3690AC1CE5B39E6DC67D9C2B46D3C79923C43F05527D4FFADCC860740E9E2B25'
SEheaders = {'SE-Client-Type': 'se0304', 'SE-Client-API-Key': apikey}
s = requests.session()
if os.path.exists('secret'):
device_id, device_password = open('secret').read().split()
else: # register
j = s.post('https://api.surfeasy.com/v2/register_subscriber',
data=dict(email=email, password=password),
headers=SEheaders).json()
assert '0' in j['return_code'], j
j = s.post('https://api.surfeasy.com/v2/register_device',
data=dict(client_type='se0304', device_hash=device_hash,
device_name='Opera-Browser-Client'),
headers=SEheaders).json()
assert '0' in j['return_code'], j
d = j['data']
device_id = hashlib.sha1(d['device_id'].encode('ascii')).hexdigest()
device_password = d['device_password']
with open('secret', 'w') as f:
f.write(device_id + ' ' + device_password)
auth = device_id + ":" + device_password
basic_auth = base64.b64encode(auth.encode('ascii')).decode('ascii')
# j = s.post('https://api.surfeasy.com/v2/subscriber_login',
# data={'login': email, 'password': password,
# 'client_type': 'se0304'},
# headers=SEheaders).json()
# assert '0' in j['return_code'], j
# j = s.post('https://api.surfeasy.com/v2/geo_list',
# data={'device_id': device_id}).json()
# assert '0' in j['return_code'], j
# geo = j['data']
# print(geo)
j = s.post('https://api.surfeasy.com/v2/discover',
data={'serial_no': device_id, 'requested_geo': '"US"'}).json()
assert j['return_code']['0'] == 'OK', j
ips = j['data']['ips']
# print(ips)
proxy = ips[0]['ip']
port = ips[0]['ports'][0]
print('Proxy:', proxy, port)
return basic_auth, proxy, port
class PatchedContext:
def __init__(self, conn):
conn._check_hostname = False
conn._context.check_hostname = False
self.ctx = conn._context
def __getattr__(self, attr):
if attr == 'wrap_socket':
return lambda sock, server_hostname: self.ctx.wrap_socket(sock)
return getattr(self.ctx, attr)
if __name__ == '__main__':
basic_auth, proxy, port = get_proxy()
req = 'GET {url} HTTP/1.0\nProxy-Authorization: BASIC {basic_auth}\n\n'.format(
url='http://httpbin.org/ip', basic_auth=basic_auth)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock = ssl.wrap_socket(sock)
sock.connect((proxy, port))
sock.send(req.encode('ascii'))
print(sock.recv(1024))
sock.close()
# or with ugly monkey patch
conn = http.client.HTTPSConnection(proxy, port)
conn._context = PatchedContext(conn)
conn.request("GET", "http://httpbin.org/ip",
headers={"Proxy-Authorization": "BASIC "+basic_auth})
print(conn.getresponse().read())
@JackDandy
Copy link

This no longer works.

Output from vpn.py is {u'data': {}, u'return_code': {u'19': u'Invalid client.'}}

at https://gist.github.com/scturtle/4f2d95507b2d357f1b17a538a1d36d82#file-vpn-py-L29

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment