%PDF- %PDF-
| Direktori : /proc/thread-self/root/data/old/usr/lib/python3.4/site-packages/dohproxy/ |
| Current File : //proc/thread-self/root/data/old/usr/lib/python3.4/site-packages/dohproxy/utils.py |
#!/usr/bin/env python3
#
# Copyright (c) 2018-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
#
import argparse
import binascii
import base64
import dns.exception
import dns.message
import logging
import urllib.parse
from typing import Dict, List, Tuple
from dohproxy import constants, protocol, __version__
def extract_path_params(url: str) -> Tuple[str, Dict[str, List[str]]]:
""" Given a URI, extract the path and the parameters
"""
p = urllib.parse.urlparse(url)
params = urllib.parse.parse_qs(p.query, keep_blank_values=True)
return p.path, params
def extract_ct_body(params: Dict[str, List[str]]) -> Tuple[str, bytes]:
""" Extract the content type and body from a list of get parameters.
:param params: A dictionary of key/value of parameters as provided by
urllib.parse.parse_qs
:return: a tuple that contains a string and bytes, respectively ct and
body.
:raises: a DOHParamsException with an explanatory message.
"""
if constants.DOH_CONTENT_TYPE_PARAM in params and \
len(params[constants.DOH_CONTENT_TYPE_PARAM]):
ct = params[constants.DOH_CONTENT_TYPE_PARAM][0]
if not ct:
# An empty value indicates the default
# application/dns-udpwireformat type
ct = constants.DOH_MEDIA_TYPE
else:
raise protocol.DOHParamsException(b'Missing Content Type Parameter')
if constants.DOH_DNS_PARAM in params and \
len(params[constants.DOH_DNS_PARAM]):
try:
body = doh_b64_decode(
params[constants.DOH_DNS_PARAM][0])
except binascii.Error:
raise protocol.DOHParamsException(b'Invalid Body Parameter')
if not body:
raise protocol.DOHParamsException(b'Missing Body')
else:
raise protocol.DOHParamsException(b'Missing Body Parameter')
return ct, body
def dns_query_from_body(
body: bytes,
debug: bool = False) -> dns.message.Message:
""" Given a bytes-object, attempt to unpack a DNS Message.
:param body: the bytes-object wired representation of a DNS message.
:param debug: a boolean. When True, The error message sent to client will
be more meaningful.
:return: a dns.message.Message on success, raises DOHDNSException
otherwise.
"""
exc = b'Malformed DNS query'
try:
return dns.message.from_wire(body)
except Exception as e:
if debug:
exc = str(e).encode('utf-8')
raise protocol.DOHDNSException(exc)
def doh_b64_encode(s: bytes) -> str:
"""Base 64 urlsafe encode and remove padding.
:param s: input bytes-like object to be encoded.
:return: urlsafe base 64 encoded string.
"""
return base64.urlsafe_b64encode(s).decode('utf-8').rstrip('=')
def doh_b64_decode(s: str) -> bytes:
"""Base 64 urlsafe decode, add padding as needed.
:param s: input base64 encoded string with potentially missing padding.
:return: decodes bytes
"""
padding = '=' * (-len(s) % 4)
return base64.urlsafe_b64decode(s + padding)
def build_query_params(dns_query):
"""Given a wire-format DNS query, build the query parameters.
"""
return {
constants.DOH_DNS_PARAM: doh_b64_encode(dns_query),
constants.DOH_CONTENT_TYPE_PARAM: constants.DOH_MEDIA_TYPE,
}
def make_url(domain, uri):
"""Utility function to return a URL ready to use from a browser or cURL....
"""
p = urllib.parse.ParseResult(
scheme='https',
netloc=domain,
path=uri,
params='', query='', fragment='',
)
return urllib.parse.urlunparse(p)
def client_parser_base():
"""Build a ArgumentParser object with all the default arguments that are
useful to both client and stub.
:return: a ArgumentParser object with the common client side arguments set.
"""
parser = argparse.ArgumentParser()
parser.add_argument(
'--domain',
default='localhost',
help='Domain to make DOH request against. Default: [%(default)s]'
)
parser.add_argument(
'--uri',
default=constants.DOH_URI,
help='DNS API URI. Default [%(default)s]',
)
parser.add_argument(
'--remote-address',
help='Remote address where the DOH proxy is running. If None, '
'--domain will be resolved to lookup and IP. Default: [%(default)s]',
)
parser.add_argument(
'--port',
default=443,
help='Port to connect to. Default: [%(default)s]'
)
parser.add_argument(
'--post',
action='store_true',
help='Use HTTP POST instead of GET.'
)
parser.add_argument(
'--debug',
action='store_true',
help='Prints some debugging output',
)
parser.add_argument(
'--level',
default='DEBUG',
help='log level [%(default)s]',
)
parser.add_argument(
'--insecure',
action='store_true',
help=argparse.SUPPRESS,
)
parser.add_argument(
'--version',
action='version',
version='%(prog)s {}'.format(__version__)
)
return parser
def proxy_parser_base(*, port: int,
secure: bool = True) -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
parser.add_argument(
'--listen-address',
default='::1',
help='The address the proxy should listen on. Default: [%(default)s]'
)
parser.add_argument(
'--port', '--listen-port',
default=port,
type=int,
help='Port to listen on. Default: [%(default)s]',
)
if secure:
parser.add_argument(
'--certfile',
help='SSL cert file.'
)
parser.add_argument(
'--keyfile',
help='SSL key file.'
)
parser.add_argument(
'--upstream-resolver',
default='::1',
help='Upstream recursive resolver to send the query to. '
'Default: [%(default)s]',
)
parser.add_argument(
'--upstream-port',
default=53,
help='Upstream recursive resolver port to send the query to. '
'Default: [%(default)s]',
)
parser.add_argument(
'--uri',
default=constants.DOH_URI,
help='DNS API URI. Default [%(default)s]',
)
parser.add_argument(
'--level',
default='DEBUG',
help='log level [%(default)s]',
)
parser.add_argument(
'--debug',
action='store_true',
help='Debugging messages...'
)
parser.add_argument(
'--version',
action='version',
version='%(prog)s {}'.format(__version__),
)
return parser
def configure_logger(name='', level='DEBUG'):
"""
:param name: (optional) name of the logger, default: ''.
:param level: (optional) level of logging, default: DEBUG.
:return: a logger instance.
"""
logging.basicConfig(format='%(asctime)s: %(levelname)8s: %(message)s')
logger = logging.getLogger(name)
level_name = level.upper()
level = getattr(logging, level_name, None)
if not isinstance(level, int):
raise Exception("Invalid log level name : %s" % level_name)
logger.setLevel(level)
return logger