def OneRow(cursor, rowtransform, print_transform=False, printer=StatusPrinter()): row = cursor.fetchone() next_row = cursor.fetchone() if next_row: raise Exception("OneRow feedback strategy saw at least two rows: {} and {}".format(row, next_row)) # this function refuses to break ties for you # if you expected multiple rows, use the ManyRows feedback strategy and break them yourself # exit early if response is empty if not row: printer("...got empty result") return None printer('[Row]') with Indent(printer): printer(shorten(row)) # exit early if transform is trivial if is_identity(rowtransform): return row row = rowtransform(row) if print_transform: printer('[Transformed Row]') with Indent(printer): printer(pretty_shorten(row)) return row
def print_or_warn(string, max_length=500, printer=StatusPrinter()): if len(string) > max_length and sys.stdout.isatty(): validJson = False try: json.loads(string) validJson = True except ValueError: pass if validJson: printer("Output is {} chars (json) and stdout is a tty.".format( len(string))) else: printer("Output is {} chars and stdout is a tty.".format( len(string))) with Indent(printer): printer( "\nIf you really want that much garbage in your terminal, write to a pipe, like so:" ) with Indent(printer): printer(clistring() + " | cat") if validJson: printer( "Or better yet, use `jq` to query it:" ) # because humans shouldn't have to read non-pretty json with Indent(printer): printer(clistring() + " | jq '.someKey[3]'") sys.exit(15) else: print(string)
def execute(self, feedback, rowtransform=lambda x : x, print_transform=False, printer=StatusPrinter()): # open an ssh tunnel with PossibleSshTunnel(self.ssh_config, printer) as tun: with Indent(printer): host = Query.get_mysql_host(tun.mysql().host) # open a mysql connection db = MySQLdb.connect(user=self.mysql_user, host=host, port=tun.mysql().port, db=tun.mysql().db, passwd=self.mysql_pass, autocommit=True, cursorclass=MySQLdb.cursors.DictCursor) c = db.cursor() # show the query then run it printer("[Query]") with Indent(printer): printer(dedent(self.sql).strip()) c.execute(self.sql) # do what the caller wanted return feedback(c, rowtransform, print_transform=print_transform, printer=printer)
def ManyRows(cursor, rowtransform, print_transform=False, printer=StatusPrinter()): # exit early if response is empty rows = cursor.fetchall() if not rows: printer("...got empty result") return None printer('[Rows]') with Indent(printer): printer(shorten(rows)) # exit early if transform is trivial if is_identity(rowtransform): return rows rows = list(map(rowtransform, rows)) if print_transform: printer('[Transformed Rows]') with Indent(printer): printer(pretty_shorten(rows)) return rows
def master_clear(): printer = StatusPrinter(indent=0) printer("Clearing Device") with Indent(printer): with Indent(printer): d = get_connected_device(printer=printer) cmd = ['shell', 'am', 'broadcast', '-a', 'android.intent.action.MASTER_CLEAR', '-n', 'android/com.android.server.MasterClearReceiver'] printer('\'' + ' '.join(cmd) + '\'') adb(cmd) sleep(d.get_shutdown_delay())
def print_request(printer, endpoint, headers, data): printer("[Request] " + endpoint) with Indent(printer): printer("headers:") with Indent(printer): printer(pp.pformat(headers, indent=2)) printer("data:") with Indent(printer): printer(pretty_shorten(data))
def print_response(printer, response): printer("[Response]") with Indent(printer): printer("code:", end='') printer(response.status_code) printer("reason:", end='') printer(response.reason) printer("content:") with Indent(printer): printer(pretty_shorten_maybe_json(response))
def set_target(): parsed_args = parse(Parseable.target_type, Parseable.server) printer = StatusPrinter(indent=0) printer("Targeting attached device to {} {}".format(parsed_args.targettype, parsed_args.server)) with Indent(printer): get_connected_device(printer=printer).set_target(parsed_args.targettype, parsed_args.server)
def deprovision(): args = parse_serial_ssh() printer = StatusPrinter(indent=0) printer("Deprovisioning Device") with Indent(printer): auth_token = get_auth_token( args.ssh_config, '/v3/partner/pp/merchants/{mId}/devices/{serialNumber}/deprovision' ) mid = get_merchant(args.serial_num, args.ssh_config, printer=printer).uuid endpoint = 'https://{}/v3/partner/pp/merchants/{}/devices/{}/deprovision'.format( args.ssh_config.hostname, mid, args.serial_num) headers = {'Authorization': 'Bearer ' + auth_token} print_request(printer, endpoint, headers, {}) response = requests.put(endpoint, headers=headers) print_response(printer, response) # TODO: server/scripts/disassociate_device.py also DELETEs '/v3/resellers/{rId}/devices/{serial}' # maybe this function should do that also? if response.status_code == 200: printer('OK') else: printer('Error') sys.exit(10)
def provision(): args = parse_serial_ssh_merch() printer = StatusPrinter(indent=0) printer("Provisioning Device") with Indent(printer): endpoint = 'https://{}/v3/partner/pp/merchants/{}/devices/{}/provision'.format( args.ssh_config.hostname, args.merchant, args.serial_num) auth_token = get_auth_token( args.ssh_config, '/v3/partner/pp/merchants/{mId}/devices/{serialNumber}/provision') headers = {'Authorization': 'Bearer ' + auth_token} data = { 'mId': get_mid(args.ssh_config, args.merchant), 'merchantUuid': args.merchant, 'serial': args.serial_num, 'chipUid': args.cpuid } print_request(printer, endpoint, headers, data) response = requests.put(endpoint, headers=headers, data=data) print_response(printer, response) if response.status_code == 200: printer('OK') else: printer('Error') sys.exit(20)
def print_local_ip(): printer = StatusPrinter(indent=0) printer("Seeking local IP accessable by device") with Indent(printer): local_ip = probe_network(selector = lambda x : x['local_ip'], printer=printer) print(local_ip)
def print_device_ip(): printer = StatusPrinter(indent=0) printer("Seeking accessable device IP") with Indent(printer): device_ip = probe_network(selector = lambda x : x['device_ip'], printer=printer) print(device_ip)
def print_activation_code(): args = parse_serial_ssh() printer = StatusPrinter(indent=0) printer("Getting Activation Code") with Indent(printer): print( get_activation_code(args.ssh_config, args.serial_num, printer=printer))
def execute(self, then=lambda db: None, printer=StatusPrinter()): # open an ssh tunnel with SshTunnel(self.ssh_config, printer) as tun: with Indent(printer): # open a mysql connection db = _mysql.connect(user=self.mysql_user, host='127.0.0.1', port=tun.mysql_port, db='meta', passwd=self.mysql_pass) # show the query then run it printer("[Query]") with Indent(printer): printer(dedent(self.sql).strip()) db.query(self.sql) # do what the caller wanted return then(db)
def probe_network(selector=lambda x : x, printer=StatusPrinter()): with Indent(printer): local_remote = get_local_remote_ip() if local_remote: return selector({ "local_ip" : local_remote[0], "device_ip" : local_remote[1] }) else: printer("No connectivity between local machine and device") sys.exit(40)
def ChangeCount(cursor, rowtransform, print_transform=False, printer=StatusPrinter()): if not is_identity(rowtransform): printer("ChangeCount got nontrivial row transform. It will be ignored.") change_ct = cursor.rowcount printer('[Rows Changed]') with Indent(printer): printer(change_ct) return change_ct
def internal_auth(target, creds = {'username' : 'joe.blow', 'password' : 'letmein' }, printer=StatusPrinter()): endpoint = '{}://{}/cos/v1/dashboard/internal/login'.format( target.get_hypertext_protocol(), target.get_hostname() + ":" + str(target.get_http_port())) headers = { 'Content-Type' : 'application/json ', 'Accept' : 'application/json, text/javascript, */*; q=0.01', 'Connection' : 'keep-alive' } data = creds # first try with with a nonsense user printer("Attempting cloverDevAuth") with Indent(printer): response = post(endpoint, headers, data, obfuscate_pass=True, printer=printer) if response.status_code == 200: return response.headers['set-cookie'] # if that fails, use a real one elif response.status_code == 401: printer("{} has cloverDevAuth unset or false, looking for real credentials".format(target.get_name())) creds = get_creds(printer=printer) data = {'username' : creds.user, 'password' : creds.passwd} with Indent(printer): response = post(endpoint, headers, data, obfuscate_pass=True, printer=printer) if response.status_code == 200: return response.headers['set-cookie'] else: raise Exception("Unexpected response from login endpoint")
def can_talk(local, remote, printer): printer("Pinging {} -> {}".format(remote, local)) with Indent(printer): printer('''adb shell 'ping -c 4 {} && echo SUCCESS || echo FAIL' '''.format(local)) with Indent(printer): remote2local = str(adb(['shell', 'ping -c 4 {} && echo SUCCESS || echo FAIL'.format(local)])) printer(remote2local) if 'SUCCESS' in remote2local: printer("Pinging {} -> {}".format(local, remote)) with Indent(printer): printer('ping -c 4 {}'.format(local)) with Indent(printer): try: local2remote = ping(['-c', '4', remote]) except sh.ErrorReturnCode as err: local2remote = err printer(local2remote) if local2remote.exit_code == 0: return True return False
def get_creds(printer=StatusPrinter()): user_exists = False user_var='LDAP_USER' if user_var in os.environ: user = os.environ[user_var] user_exists = True # else: warn later so we can warn for both passwd_exists=False passwd_var='LDAP_PASSWORD' if passwd_var in os.environ: passwd = os.environ[passwd_var] passwd_exists = True # else: warn later so we can warn for both try: return UserPass(user, passwd) except NameError: with Indent(printer): printer("Please set environment variables:") with Indent(printer): if not user_exists: printer(user_var) with Indent(printer): printer("(try typing: 'export {}=<your_username>' and rerunning the command)".format( user_var)) if not passwd_exists: printer(passwd_var) with Indent(printer): printer("(try typing: \'read -s {} && export {}\', ".format(passwd_var, passwd_var), "typing your password, and rerunning the command)") if not (user_exists and passwd_exists): sys.exit(100)
def print_merchant(): args = parse_serial_ssh() printer = StatusPrinter(indent=0) printer("Finding {}'s merchant according to {}".format( args.serial_num, args.ssh_config.ssh_host)) try: with Indent(printer): merchant = get_merchant(args.serial_num, args.ssh_config, printer=printer) print(json.dumps(merchant._asdict())) except ValueError as ex: printer(str(ex)) sys.exit(30)
def screenshot(device, printer=StatusPrinter()): printer("Dumping screenshot for Device: {}".format(device.serial)) with Indent(printer): # make way for new file outfile_name = "{}_{}.png".format(device.serial, datetime.now().strftime("%Y-%m-%d_%H%M%S")) outfile_path = os.path.join(os.getcwd(), outfile_name) rm('-f', outfile_path) # get the screencap tempfile_path = '/sdcard/{}'.format(outfile_name) adb.shell('screencap', '-p', tempfile_path) adb.pull(tempfile_path) adb.shell('rm', tempfile_path) printer("Wrote " + outfile_path)
def _do_request(verb, endpoint, headers, data, print_data=None, printer=StatusPrinter()): # for obfuscating passwords if not print_data: print_data = data printer("[Http]") with Indent(printer): print_request(printer, endpoint, headers, print_data) if data: if 'json' in ''.join(headers.values()).lower(): response = verb(endpoint, headers=headers, json=data) else: response = verb(endpoint, headers=headers, data=data) else: response = verb(endpoint, headers=headers) print_response(printer, response) return response
def set_target(self, target, url, printer=StatusPrinter()): self.wait_ready() if re.match('http://.*', url): url = url[7:] printer("Targeting device to: " + url) with Indent(printer): cmd = ['su', '1000', 'content', 'call', '--uri', 'content://com.clover.service.provider', '--method', 'changeTarget', '--extra', 'target:s:{}:{}'.format(target, url)] printer('\'' + ' '.join(cmd) + '\'') adb.shell(cmd) # the above call causes a reset # wait until the adb connection is lost printer("Waiting {} seconds for device to begin reboot..." .format(self.get_shutdown_delay())) sleep(self.get_shutdown_delay())
def get_row_val(db): row = db.store_result().fetch_row(how=1) if not row: printer("...got empty result") raise ValueError(self._empty_message) else: printer("[Result]") with Indent(printer): try: # if we're selecting rows directly if isinstance(row, tuple): printer(row[0]) return getter(row[0]) # if we're selecting the output of a mysql builtin else: printer(row) return getter(row) except: printer(row[0]) raise
def print_info(): printer = StatusPrinter(indent=0) printer("Getting device info") with Indent(printer): info = json.dumps(get_connected_device(printer=printer).get_info()) print(info)
def print_serial(): printer = StatusPrinter(indent=0) printer("Getting device serial") with Indent(printer): serial = get_connected_device(printer=printer).serial print(serial)
def get_local_remote_ip(printer=StatusPrinter()): printer("Probing Network From Both Sides") Address = namedtuple("Address", "ip_str int") def read_ip(msg, ip_str, printer): if '127.0.0.1' not in ip_str: printer("{:>10}: {}".format(msg, ip_str)) return Address(ip_str, int(ipaddress.IPv4Address(ip_str))) else: return None # get all ipv4 addresses among the local network adapters printer("Local Addresses:") local_addresses = set() with Indent(printer): adapters = ifaddr.get_adapters() for adapter in adapters: for ip in adapter.ips: ip_str = str(ip.ip) if re.match(r'^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$', ip_str): address = read_ip(adapter.nice_name, ip_str, printer) if address is not None: local_addresses.add(address) printer('') # get all ipv4 addresses among the device's known routes printer("Device Address Candidates:") device_addresses = set() with Indent(printer): # keep only things that look like ip addresses device_ip_strs = set(re.findall(r'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', str( sed( # TODO: this would be cleaner with 'adb shell netcfg' which I discovered too late # dump the routing table adb(['shell', 'ip', 'route']), # print only lines containing 'src', and only the part after the 'src' ['-n', r's#^.*src\(.*\)$#\1#p'] ) ).strip())) for ip_str in device_ip_strs: address = read_ip("route entry", ip_str, printer) if address is not None: device_addresses.add(address) printer('') # In an ip address, the more significant bits are subnet bits, less significant ones are network bits # XOR will give subnet zeros when two ip addresses are in the same subnet. Therefore, smaller distances # returned by this function indicate that the addresses are more likely to be able to talk to each other def subnet_distance(ip_a, ip_b): return ip_a.int ^ ip_b.int # Ping local from remote, and ping remote from local # Return true if both succeed def can_talk(local, remote, printer): printer("Pinging {} -> {}".format(remote, local)) with Indent(printer): printer('''adb shell 'ping -c 4 {} && echo SUCCESS || echo FAIL' '''.format(local)) with Indent(printer): remote2local = str(adb(['shell', 'ping -c 4 {} && echo SUCCESS || echo FAIL'.format(local)])) printer(remote2local) if 'SUCCESS' in remote2local: printer("Pinging {} -> {}".format(local, remote)) with Indent(printer): printer('ping -c 4 {}'.format(local)) with Indent(printer): try: local2remote = ping(['-c', '4', remote]) except sh.ErrorReturnCode as err: local2remote = err printer(local2remote) if local2remote.exit_code == 0: return True return False # sort local/remote pairs by distance matches = SortedDict() for local_ip, remote_ip in cross_product(local_addresses, device_addresses): matches[subnet_distance(local_ip, remote_ip)] = (local_ip.ip_str, remote_ip.ip_str) # check connectivity (nearest first) for local, remote in matches.values(): if can_talk(local, remote, printer): return (local, remote)
def print_cpuid(): printer = StatusPrinter(indent=0) printer("Getting device cpuid") with Indent(printer): cpuid = get_connected_device().cpuid print(cpuid)