parent
ee2f462ee9
commit
dc4f0ac2d3
8 changed files with 23 additions and 844 deletions
@ -1,258 +0,0 @@ |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
import os |
||||
import json |
||||
import socks |
||||
import time |
||||
import urllib |
||||
import hashlib |
||||
from xmlrpc.client import ( |
||||
Fault, |
||||
Transport, |
||||
SafeTransport, |
||||
) |
||||
from sockshandler import SocksiPyConnection |
||||
from .util import jsonDecimal |
||||
|
||||
|
||||
class SocksTransport(Transport): |
||||
|
||||
def set_proxy(self, proxy_host, proxy_port): |
||||
self.proxy_host = proxy_host |
||||
self.proxy_port = proxy_port |
||||
|
||||
self.proxy_type = socks.PROXY_TYPE_SOCKS5 |
||||
self.proxy_rdns = True |
||||
self.proxy_username = None |
||||
self.proxy_password = None |
||||
|
||||
def make_connection(self, host): |
||||
# return an existing connection if possible. This allows |
||||
# HTTP/1.1 keep-alive. |
||||
if self._connection and host == self._connection[0]: |
||||
return self._connection[1] |
||||
# create a HTTP connection object from a host descriptor |
||||
chost, self._extra_headers, x509 = self.get_host_info(host) |
||||
self._connection = host, SocksiPyConnection(self.proxy_type, self.proxy_host, self.proxy_port, self.proxy_rdns, self.proxy_username, self.proxy_password, chost) |
||||
return self._connection[1] |
||||
|
||||
|
||||
class JsonrpcDigest(): |
||||
# __getattr__ complicates extending ServerProxy |
||||
def __init__(self, uri, transport=None, encoding=None, verbose=False, |
||||
allow_none=False, use_datetime=False, use_builtin_types=False, |
||||
*, context=None): |
||||
|
||||
parsed = urllib.parse.urlparse(uri) |
||||
if parsed.scheme not in ('http', 'https'): |
||||
raise OSError('unsupported XML-RPC protocol') |
||||
self.__host = parsed.netloc |
||||
self.__handler = parsed.path |
||||
|
||||
if transport is None: |
||||
handler = SafeTransport if parsed.scheme == 'https' else Transport |
||||
extra_kwargs = {} |
||||
transport = handler(use_datetime=use_datetime, |
||||
use_builtin_types=use_builtin_types, |
||||
**extra_kwargs) |
||||
self.__transport = transport |
||||
|
||||
self.__encoding = encoding or 'utf-8' |
||||
self.__verbose = verbose |
||||
self.__allow_none = allow_none |
||||
|
||||
self.__request_id = 0 |
||||
|
||||
def close(self): |
||||
if self.__transport is not None: |
||||
self.__transport.close() |
||||
|
||||
def request_id(self): |
||||
return self.__request_id |
||||
|
||||
def post_request(self, method, params, timeout=None): |
||||
try: |
||||
connection = self.__transport.make_connection(self.__host) |
||||
if timeout: |
||||
connection.timeout = timeout |
||||
headers = self.__transport._extra_headers[:] |
||||
|
||||
connection.putrequest('POST', self.__handler) |
||||
headers.append(('Content-Type', 'application/json')) |
||||
headers.append(('User-Agent', 'jsonrpc')) |
||||
self.__transport.send_headers(connection, headers) |
||||
self.__transport.send_content(connection, '' if params is None else json.dumps(params, default=jsonDecimal).encode('utf-8')) |
||||
self.__request_id += 1 |
||||
|
||||
resp = connection.getresponse() |
||||
return resp.read() |
||||
|
||||
except Fault: |
||||
raise |
||||
except Exception: |
||||
self.__transport.close() |
||||
raise |
||||
|
||||
def json_request(self, request_body, username='', password='', timeout=None): |
||||
try: |
||||
connection = self.__transport.make_connection(self.__host) |
||||
if timeout: |
||||
connection.timeout = timeout |
||||
|
||||
headers = self.__transport._extra_headers[:] |
||||
|
||||
connection.putrequest('POST', self.__handler) |
||||
headers.append(('Content-Type', 'application/json')) |
||||
headers.append(('Connection', 'keep-alive')) |
||||
self.__transport.send_headers(connection, headers) |
||||
self.__transport.send_content(connection, json.dumps(request_body, default=jsonDecimal).encode('utf-8') if request_body else '') |
||||
resp = connection.getresponse() |
||||
|
||||
if resp.status == 401: |
||||
resp_headers = resp.getheaders() |
||||
v = resp.read() |
||||
|
||||
algorithm = '' |
||||
realm = '' |
||||
nonce = '' |
||||
for h in resp_headers: |
||||
if h[0] != 'WWW-authenticate': |
||||
continue |
||||
fields = h[1].split(',') |
||||
for f in fields: |
||||
key, value = f.split('=', 1) |
||||
if key == 'algorithm' and value != 'MD5': |
||||
break |
||||
if key == 'realm': |
||||
realm = value.strip('"') |
||||
if key == 'nonce': |
||||
nonce = value.strip('"') |
||||
if realm != '' and nonce != '': |
||||
break |
||||
|
||||
if realm == '' or nonce == '': |
||||
raise ValueError('Authenticate header not found.') |
||||
|
||||
path = self.__handler |
||||
HA1 = hashlib.md5(f'{username}:{realm}:{password}'.encode('utf-8')).hexdigest() |
||||
|
||||
http_method = 'POST' |
||||
HA2 = hashlib.md5(f'{http_method}:{path}'.encode('utf-8')).hexdigest() |
||||
|
||||
ncvalue = '{:08x}'.format(1) |
||||
s = ncvalue.encode('utf-8') |
||||
s += nonce.encode('utf-8') |
||||
s += time.ctime().encode('utf-8') |
||||
s += os.urandom(8) |
||||
cnonce = (hashlib.sha1(s).hexdigest()[:16]) |
||||
|
||||
# MD5-SESS |
||||
HA1 = hashlib.md5(f'{HA1}:{nonce}:{cnonce}'.encode('utf-8')).hexdigest() |
||||
|
||||
respdig = hashlib.md5(f'{HA1}:{nonce}:{ncvalue}:{cnonce}:auth:{HA2}'.encode('utf-8')).hexdigest() |
||||
|
||||
header_value = f'Digest username="{username}", realm="{realm}", nonce="{nonce}", uri="{path}", response="{respdig}", algorithm="MD5-sess", qop="auth", nc={ncvalue}, cnonce="{cnonce}"' |
||||
headers = self.__transport._extra_headers[:] |
||||
headers.append(('Authorization', header_value)) |
||||
|
||||
connection.putrequest('POST', self.__handler) |
||||
headers.append(('Content-Type', 'application/json')) |
||||
headers.append(('Connection', 'keep-alive')) |
||||
self.__transport.send_headers(connection, headers) |
||||
self.__transport.send_content(connection, json.dumps(request_body, default=jsonDecimal).encode('utf-8') if request_body else '') |
||||
resp = connection.getresponse() |
||||
|
||||
self.__request_id += 1 |
||||
return resp.read() |
||||
|
||||
except Fault: |
||||
raise |
||||
except Exception: |
||||
self.__transport.close() |
||||
raise |
||||
|
||||
|
||||
def callrpc_wow(rpc_port, method, params=[], rpc_host='127.0.0.1', path='json_rpc', auth=None, timeout=120, transport=None, tag=''): |
||||
# auth is a tuple: (username, password) |
||||
try: |
||||
if rpc_host.count('://') > 0: |
||||
url = '{}:{}/{}'.format(rpc_host, rpc_port, path) |
||||
else: |
||||
url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, path) |
||||
|
||||
x = JsonrpcDigest(url, transport=transport) |
||||
request_body = { |
||||
'method': method, |
||||
'params': params, |
||||
'jsonrpc': '2.0', |
||||
'id': x.request_id() |
||||
} |
||||
if auth: |
||||
v = x.json_request(request_body, username=auth[0], password=auth[1], timeout=timeout) |
||||
else: |
||||
v = x.json_request(request_body, timeout=timeout) |
||||
x.close() |
||||
r = json.loads(v.decode('utf-8')) |
||||
except Exception as ex: |
||||
raise ValueError('{}RPC Server Error: {}'.format(tag, str(ex))) |
||||
|
||||
if 'error' in r and r['error'] is not None: |
||||
raise ValueError(tag + 'RPC error ' + str(r['error'])) |
||||
|
||||
return r['result'] |
||||
|
||||
|
||||
def callrpc_wow2(rpc_port: int, method: str, params=None, auth=None, rpc_host='127.0.0.1', timeout=120, transport=None, tag=''): |
||||
try: |
||||
if rpc_host.count('://') > 0: |
||||
url = '{}:{}/{}'.format(rpc_host, rpc_port, method) |
||||
else: |
||||
url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, method) |
||||
|
||||
x = JsonrpcDigest(url, transport=transport) |
||||
if auth: |
||||
v = x.json_request(params, username=auth[0], password=auth[1], timeout=timeout) |
||||
else: |
||||
v = x.json_request(params, timeout=timeout) |
||||
x.close() |
||||
r = json.loads(v.decode('utf-8')) |
||||
except Exception as ex: |
||||
raise ValueError('{}RPC Server Error: {}'.format(tag, str(ex))) |
||||
|
||||
return r |
||||
|
||||
|
||||
def make_wow_rpc2_func(port, auth, host='127.0.0.1', proxy_host=None, proxy_port=None, default_timeout=120, tag=''): |
||||
port = port |
||||
auth = auth |
||||
host = host |
||||
transport = None |
||||
default_timeout = default_timeout |
||||
tag = tag |
||||
|
||||
if proxy_host: |
||||
transport = SocksTransport() |
||||
transport.set_proxy(proxy_host, proxy_port) |
||||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=default_timeout): |
||||
nonlocal port, auth, host, transport, tag |
||||
return callrpc_wow2(port, method, params, auth=auth, rpc_host=host, timeout=timeout, transport=transport, tag=tag) |
||||
return rpc_func |
||||
|
||||
|
||||
def make_wow_rpc_func(port, auth, host='127.0.0.1', proxy_host=None, proxy_port=None, default_timeout=120, tag=''): |
||||
port = port |
||||
auth = auth |
||||
host = host |
||||
transport = None |
||||
default_timeout = default_timeout |
||||
tag = tag |
||||
|
||||
if proxy_host: |
||||
transport = SocksTransport() |
||||
transport.set_proxy(proxy_host, proxy_port) |
||||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=default_timeout): |
||||
nonlocal port, auth, host, transport, tag |
||||
return callrpc_wow(port, method, params, rpc_host=host, auth=auth, timeout=timeout, transport=transport, tag=tag) |
||||
return rpc_func |
Loading…
Reference in new issue