Skip to content

Commit

Permalink
SOCKS auth support, small improvements
Browse files Browse the repository at this point in the history
Auth supports for SOCKS proxies, provide proxy_auth object for HTTP
proxies, set RPC and proxy URLs to instances of URL, remove
parse_api_endpoint, raise a ValueError on invalid proxy schemes.
  • Loading branch information
Noctem committed Apr 20, 2017
1 parent 6dce6dd commit ae316d6
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 23 deletions.
2 changes: 1 addition & 1 deletion aiopogo/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__title__ = 'aiopogo'
__version__ = '1.8.3'
__version__ = '1.9.0'
__author__ = 'David Christenson'
__license__ = 'MIT License'
__copyright__ = 'Copyright (c) 2017 David Christenson <https://github.com/Noctem>'
Expand Down
9 changes: 5 additions & 4 deletions aiopogo/auth_ptc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .exceptions import ActivationRequiredException, AuthConnectionException, AuthException, AuthTimeoutException, InvalidCredentialsException, ProxyException, SocksError, UnexpectedAuthError

class AuthPtc(Auth):
def __init__(self, username=None, password=None, proxy=None, timeout=None, locale=None):
def __init__(self, username=None, password=None, proxy=None, proxy_auth=None, timeout=None, locale=None):
Auth.__init__(self)
self.provider = 'ptc'

Expand All @@ -20,7 +20,8 @@ def __init__(self, username=None, password=None, proxy=None, timeout=None, local
self.timeout = timeout or 10.0

self.proxy = proxy
self.socks = proxy and proxy.startswith('socks')
self.socks = proxy and proxy.scheme in ('socks4', 'socks5')
self.proxy_auth = proxy_auth

async def user_login(self, username=None, password=None):
self._username = username or self._username
Expand All @@ -44,7 +45,7 @@ async def user_login(self, username=None, password=None):
raise_for_status=True,
conn_timeout=5.0,
read_timeout=self.timeout) as session:
async with session.get('https://sso.pokemon.com/sso/oauth2.0/authorize', params={'client_id': 'mobile-app_pokemon-go', 'redirect_uri': 'https://www.nianticlabs.com/pokemongo/error', 'locale': self.locale}, proxy=self.proxy) as resp:
async with session.get('https://sso.pokemon.com/sso/oauth2.0/authorize', params={'client_id': 'mobile-app_pokemon-go', 'redirect_uri': 'https://www.nianticlabs.com/pokemongo/error', 'locale': self.locale}, proxy=self.proxy, proxy_auth=self.proxy_auth) as resp:
data = await resp.json(loads=json_loads, encoding='utf-8', content_type=None)

assert 'lt' in data
Expand All @@ -53,7 +54,7 @@ async def user_login(self, username=None, password=None):
data['password'] = self._password
data['locale'] = self.locale

async with session.post('https://sso.pokemon.com/sso/login', params={'service': 'http://sso.pokemon.com/sso/oauth2.0/callbackAuthorize'}, headers={'Content-Type': 'application/x-www-form-urlencoded'}, data=data, timeout=8.0, proxy=self.proxy, allow_redirects=False) as resp:
async with session.post('https://sso.pokemon.com/sso/login', params={'service': 'http://sso.pokemon.com/sso/oauth2.0/callbackAuthorize'}, headers={'Content-Type': 'application/x-www-form-urlencoded'}, data=data, timeout=8.0, proxy=self.proxy, proxy_auth=self.proxy_auth, allow_redirects=False) as resp:
try:
self._access_token = resp.cookies['CASTGC'].value
except (AttributeError, KeyError, TypeError):
Expand Down
43 changes: 37 additions & 6 deletions aiopogo/pgoapi.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
from logging import getLogger

from yarl import URL
from aiohttp import BasicAuth
try:
from aiosocks import Socks4Auth, Socks5Auth
except ImportError:
class Socks4Auth(Exception):
def __init__(*args, **kwargs):
raise ImportError('You must install aiosocks to use a SOCKS proxy.')
Socks5Auth = Socks4Auth

from . import __title__, __version__
from .rpc_api import RpcApi, RpcState
from .auth_ptc import AuthPtc
from .auth_google import AuthGoogle
from .utilities import parse_api_endpoint
from .hash_server import HashServer
from .exceptions import AuthTokenExpiredException, InvalidCredentialsException, NoPlayerPositionSetException, ServerApiEndpointRedirectException
from .protos.pogoprotos.networking.requests.request_type_pb2 import RequestType
Expand All @@ -25,11 +34,12 @@ def __init__(self, provider=None, lat=None, lon=None, alt=None, proxy=None, devi
self.altitude = alt

self.proxy = proxy
self.proxy_auth = None
self.device_info = device_info

async def set_authentication(self, provider='ptc', username=None, password=None, proxy=None, timeout=10, locale='en_US', refresh_token=None):
async def set_authentication(self, provider='ptc', username=None, password=None, timeout=10, locale='en_US', refresh_token=None):
if provider == 'ptc':
self.auth_provider = AuthPtc(username, password, proxy=proxy or self.proxy, timeout=timeout)
self.auth_provider = AuthPtc(username, password, proxy=self._proxy, proxy_auth=self.proxy_auth, timeout=timeout)
elif provider == 'google':
self.auth_provider = AuthGoogle(proxy=proxy, refresh_token=refresh_token)
if refresh_token:
Expand Down Expand Up @@ -63,9 +73,30 @@ def api_endpoint(self):
@api_endpoint.setter
def api_endpoint(self, api_url):
if api_url.startswith("https"):
self._api_endpoint = api_url
self._api_endpoint = URL(api_url)
else:
self._api_endpoint = URL('https://' + api_url + '/rpc')

@property
def proxy(self):
return self._proxy

@proxy.setter
def proxy(self, proxy):
if proxy is None:
self._proxy = proxy
else:
self._api_endpoint = parse_api_endpoint(api_url)
self._proxy = URL(proxy)
if self._proxy.user:
scheme = self._proxy.scheme
if scheme == 'http':
self.proxy_auth = BasicAuth(self._proxy.user, self._proxy.password)
elif scheme == 'socks5':
self.proxy_auth = Socks5Auth(self._proxy.user, self._proxy.password)
elif scheme == 'socks4':
self.proxy_auth = Socks4Auth(self._proxy.user)
else:
raise ValueError('Proxy protocol must be http, socks5, or socks4.')

@property
def start_time(self):
Expand Down Expand Up @@ -102,7 +133,7 @@ async def call(self):
request = RpcApi(auth_provider, parent.state)
while True:
try:
response = await request.request(parent.api_endpoint, self._req_method_list, position, parent.device_info, parent.proxy)
response = await request.request(parent.api_endpoint, self._req_method_list, position, parent.device_info, parent._proxy, parent.proxy_auth)
break
except AuthTokenExpiredException:
self.log.info('Access token rejected! Requesting new one...')
Expand Down
8 changes: 4 additions & 4 deletions aiopogo/rpc_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ def get_class(self, class_):
module_, class_ = class_.rsplit('.', 1)
return getattr(import_module(module_), to_camel_case(class_))

async def _make_rpc(self, endpoint, request_proto_plain, proxy):
async def _make_rpc(self, endpoint, request_proto_plain, proxy, proxy_auth):
self.log.debug('Execution of RPC')

session = SESSIONS.get(proxy)

request_proto_serialized = request_proto_plain.SerializeToString()
try:
async with session.post(endpoint, data=request_proto_serialized, proxy=proxy) as resp:
async with session.post(endpoint, data=request_proto_serialized, proxy=proxy, proxy_auth=proxy_auth) as resp:
return await resp.read()
except (ClientHttpProxyError, ClientProxyConnectionError, SocksError) as e:
raise ProxyException('Proxy connection error during RPC request.') from e
Expand Down Expand Up @@ -77,10 +77,10 @@ def get_request_name(subrequests):
except (ValueError, TypeError):
return 'unknown'

async def request(self, endpoint, subrequests, player_position, device_info=None, proxy=None):
async def request(self, endpoint, subrequests, player_position, device_info=None, proxy=None, proxy_auth=None):
request_proto = await self._build_main_request(subrequests, player_position, device_info)

response = await self._make_rpc(endpoint, request_proto, proxy)
response = await self._make_rpc(endpoint, request_proto, proxy, proxy_auth)
response_dict = self._parse_main_response(response, subrequests)

if 'auth_ticket' in response_dict:
Expand Down
2 changes: 1 addition & 1 deletion aiopogo/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(self):
self.loop = get_event_loop()

def get(self, proxy=None):
socks = proxy and proxy.startswith('socks')
socks = proxy and proxy.scheme in ('socks4', 'socks5')
try:
return self.socks_session if socks else self.session
except AttributeError:
Expand Down
6 changes: 0 additions & 6 deletions aiopogo/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ def get_time_ms():
return int(time() * 1000)


def parse_api_endpoint(api_url):
if not api_url.startswith("https"):
api_url = 'https://{}/rpc'.format(api_url)
return api_url


class IdGenerator:
'''Lehmer random number generator'''
M = 0x7fffffff # 2^31 - 1 (A large prime number)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
author='David Christenson',
author_email='mail@noctem.xyz',
description='Asynchronous Pokemon API lib',
version='1.8.3',
version='1.9.0',
url='https://github.com/Noctem/aiopogo',
packages=find_packages(),
install_requires=[
Expand Down

0 comments on commit ae316d6

Please sign in to comment.