1c1d255d3SCy Schubert#!/usr/bin/python3 2c1d255d3SCy Schubert# 3c1d255d3SCy Schubert# Example nfcpy to wpa_supplicant wrapper for DPP NFC operations 4c1d255d3SCy Schubert# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> 5c1d255d3SCy Schubert# Copyright (c) 2019-2020, The Linux Foundation 6c1d255d3SCy Schubert# 7c1d255d3SCy Schubert# This software may be distributed under the terms of the BSD license. 8c1d255d3SCy Schubert# See README for more details. 9c1d255d3SCy Schubert 10c1d255d3SCy Schubertimport binascii 11c1d255d3SCy Schubertimport errno 12c1d255d3SCy Schubertimport os 13c1d255d3SCy Schubertimport struct 14c1d255d3SCy Schubertimport sys 15c1d255d3SCy Schubertimport time 16c1d255d3SCy Schubertimport threading 17c1d255d3SCy Schubertimport argparse 18c1d255d3SCy Schubert 19c1d255d3SCy Schubertimport nfc 20c1d255d3SCy Schubertimport ndef 21c1d255d3SCy Schubert 22c1d255d3SCy Schubertimport logging 23c1d255d3SCy Schubert 24c1d255d3SCy Schubertscriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__)) 25c1d255d3SCy Schubertsys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy')) 26c1d255d3SCy Schubertimport wpaspy 27c1d255d3SCy Schubert 28c1d255d3SCy Schubertwpas_ctrl = '/var/run/wpa_supplicant' 29c1d255d3SCy Schubertifname = None 30c1d255d3SCy Schubertinit_on_touch = False 31c1d255d3SCy Schubertin_raw_mode = False 32c1d255d3SCy Schubertprev_tcgetattr = 0 33c1d255d3SCy Schubertno_input = False 34c1d255d3SCy Schubertcontinue_loop = True 35c1d255d3SCy Schubertterminate_now = False 36c1d255d3SCy Schubertsummary_file = None 37c1d255d3SCy Schubertsuccess_file = None 38c1d255d3SCy Schubertnetrole = None 39c1d255d3SCy Schubertoperation_success = False 40c1d255d3SCy Schubertmutex = threading.Lock() 41c1d255d3SCy Schubert 42c1d255d3SCy SchubertC_NORMAL = '\033[0m' 43c1d255d3SCy SchubertC_RED = '\033[91m' 44c1d255d3SCy SchubertC_GREEN = '\033[92m' 45c1d255d3SCy SchubertC_YELLOW = '\033[93m' 46c1d255d3SCy SchubertC_BLUE = '\033[94m' 47c1d255d3SCy SchubertC_MAGENTA = '\033[95m' 48c1d255d3SCy SchubertC_CYAN = '\033[96m' 49c1d255d3SCy Schubert 50c1d255d3SCy Schubertdef summary(txt, color=None): 51c1d255d3SCy Schubert with mutex: 52c1d255d3SCy Schubert if color: 53c1d255d3SCy Schubert print(color + txt + C_NORMAL) 54c1d255d3SCy Schubert else: 55c1d255d3SCy Schubert print(txt) 56c1d255d3SCy Schubert if summary_file: 57c1d255d3SCy Schubert with open(summary_file, 'a') as f: 58c1d255d3SCy Schubert f.write(txt + "\n") 59c1d255d3SCy Schubert 60c1d255d3SCy Schubertdef success_report(txt): 61c1d255d3SCy Schubert summary(txt) 62c1d255d3SCy Schubert if success_file: 63c1d255d3SCy Schubert with open(success_file, 'a') as f: 64c1d255d3SCy Schubert f.write(txt + "\n") 65c1d255d3SCy Schubert 66c1d255d3SCy Schubertdef wpas_connect(): 67c1d255d3SCy Schubert ifaces = [] 68c1d255d3SCy Schubert if os.path.isdir(wpas_ctrl): 69c1d255d3SCy Schubert try: 70c1d255d3SCy Schubert ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] 71c1d255d3SCy Schubert except OSError as error: 72c1d255d3SCy Schubert summary("Could not find wpa_supplicant: %s", str(error)) 73c1d255d3SCy Schubert return None 74c1d255d3SCy Schubert 75c1d255d3SCy Schubert if len(ifaces) < 1: 76c1d255d3SCy Schubert summary("No wpa_supplicant control interface found") 77c1d255d3SCy Schubert return None 78c1d255d3SCy Schubert 79c1d255d3SCy Schubert for ctrl in ifaces: 80c1d255d3SCy Schubert if ifname and ifname not in ctrl: 81c1d255d3SCy Schubert continue 82c1d255d3SCy Schubert if os.path.basename(ctrl).startswith("p2p-dev-"): 83c1d255d3SCy Schubert # skip P2P management interface 84c1d255d3SCy Schubert continue 85c1d255d3SCy Schubert try: 86c1d255d3SCy Schubert summary("Trying to use control interface " + ctrl) 87c1d255d3SCy Schubert wpas = wpaspy.Ctrl(ctrl) 88c1d255d3SCy Schubert return wpas 89c1d255d3SCy Schubert except Exception as e: 90c1d255d3SCy Schubert pass 91c1d255d3SCy Schubert summary("Could not connect to wpa_supplicant") 92c1d255d3SCy Schubert return None 93c1d255d3SCy Schubert 94c1d255d3SCy Schubertdef dpp_nfc_uri_process(uri): 95c1d255d3SCy Schubert wpas = wpas_connect() 96c1d255d3SCy Schubert if wpas is None: 97c1d255d3SCy Schubert return False 98c1d255d3SCy Schubert peer_id = wpas.request("DPP_NFC_URI " + uri) 99c1d255d3SCy Schubert if "FAIL" in peer_id: 100c1d255d3SCy Schubert summary("Could not parse DPP URI from NFC URI record", color=C_RED) 101c1d255d3SCy Schubert return False 102c1d255d3SCy Schubert peer_id = int(peer_id) 103c1d255d3SCy Schubert summary("peer_id=%d for URI from NFC Tag: %s" % (peer_id, uri)) 104c1d255d3SCy Schubert cmd = "DPP_AUTH_INIT peer=%d" % peer_id 105c1d255d3SCy Schubert global enrollee_only, configurator_only, config_params 106c1d255d3SCy Schubert if enrollee_only: 107c1d255d3SCy Schubert cmd += " role=enrollee" 108c1d255d3SCy Schubert elif configurator_only: 109c1d255d3SCy Schubert cmd += " role=configurator" 110c1d255d3SCy Schubert if config_params: 111c1d255d3SCy Schubert cmd += " " + config_params 112c1d255d3SCy Schubert summary("Initiate DPP authentication: " + cmd) 113c1d255d3SCy Schubert res = wpas.request(cmd) 114c1d255d3SCy Schubert if "OK" not in res: 115c1d255d3SCy Schubert summary("Failed to initiate DPP Authentication", color=C_RED) 116c1d255d3SCy Schubert return False 117c1d255d3SCy Schubert summary("DPP Authentication initiated") 118c1d255d3SCy Schubert return True 119c1d255d3SCy Schubert 120c1d255d3SCy Schubertdef dpp_hs_tag_read(record): 121c1d255d3SCy Schubert wpas = wpas_connect() 122c1d255d3SCy Schubert if wpas is None: 123c1d255d3SCy Schubert return False 124c1d255d3SCy Schubert summary(record) 125c1d255d3SCy Schubert if len(record.data) < 5: 126c1d255d3SCy Schubert summary("Too short DPP HS", color=C_RED) 127c1d255d3SCy Schubert return False 128c1d255d3SCy Schubert if record.data[0] != 0: 129c1d255d3SCy Schubert summary("Unexpected URI Identifier Code", color=C_RED) 130c1d255d3SCy Schubert return False 131c1d255d3SCy Schubert uribuf = record.data[1:] 132c1d255d3SCy Schubert try: 133c1d255d3SCy Schubert uri = uribuf.decode() 134c1d255d3SCy Schubert except: 135c1d255d3SCy Schubert summary("Invalid URI payload", color=C_RED) 136c1d255d3SCy Schubert return False 137c1d255d3SCy Schubert summary("URI: " + uri) 138c1d255d3SCy Schubert if not uri.startswith("DPP:"): 139c1d255d3SCy Schubert summary("Not a DPP URI", color=C_RED) 140c1d255d3SCy Schubert return False 141c1d255d3SCy Schubert return dpp_nfc_uri_process(uri) 142c1d255d3SCy Schubert 143c1d255d3SCy Schubertdef get_status(wpas, extra=None): 144c1d255d3SCy Schubert if extra: 145c1d255d3SCy Schubert extra = "-" + extra 146c1d255d3SCy Schubert else: 147c1d255d3SCy Schubert extra = "" 148c1d255d3SCy Schubert res = wpas.request("STATUS" + extra) 149c1d255d3SCy Schubert lines = res.splitlines() 150c1d255d3SCy Schubert vals = dict() 151c1d255d3SCy Schubert for l in lines: 152c1d255d3SCy Schubert try: 153c1d255d3SCy Schubert [name, value] = l.split('=', 1) 154c1d255d3SCy Schubert except ValueError: 155c1d255d3SCy Schubert summary("Ignore unexpected status line: %s" % l) 156c1d255d3SCy Schubert continue 157c1d255d3SCy Schubert vals[name] = value 158c1d255d3SCy Schubert return vals 159c1d255d3SCy Schubert 160c1d255d3SCy Schubertdef get_status_field(wpas, field, extra=None): 161c1d255d3SCy Schubert vals = get_status(wpas, extra) 162c1d255d3SCy Schubert if field in vals: 163c1d255d3SCy Schubert return vals[field] 164c1d255d3SCy Schubert return None 165c1d255d3SCy Schubert 166c1d255d3SCy Schubertdef own_addr(wpas): 167c1d255d3SCy Schubert addr = get_status_field(wpas, "address") 168c1d255d3SCy Schubert if addr is None: 169c1d255d3SCy Schubert addr = get_status_field(wpas, "bssid[0]") 170c1d255d3SCy Schubert return addr 171c1d255d3SCy Schubert 172c1d255d3SCy Schubertdef dpp_bootstrap_gen(wpas, type="qrcode", chan=None, mac=None, info=None, 173c1d255d3SCy Schubert curve=None, key=None): 174c1d255d3SCy Schubert cmd = "DPP_BOOTSTRAP_GEN type=" + type 175c1d255d3SCy Schubert if chan: 176c1d255d3SCy Schubert cmd += " chan=" + chan 177c1d255d3SCy Schubert if mac: 178c1d255d3SCy Schubert if mac is True: 179c1d255d3SCy Schubert mac = own_addr(wpas) 180c1d255d3SCy Schubert if mac is None: 181c1d255d3SCy Schubert summary("Could not determine local MAC address for bootstrap info") 182c1d255d3SCy Schubert else: 183c1d255d3SCy Schubert cmd += " mac=" + mac.replace(':', '') 184c1d255d3SCy Schubert if info: 185c1d255d3SCy Schubert cmd += " info=" + info 186c1d255d3SCy Schubert if curve: 187c1d255d3SCy Schubert cmd += " curve=" + curve 188c1d255d3SCy Schubert if key: 189c1d255d3SCy Schubert cmd += " key=" + key 190c1d255d3SCy Schubert res = wpas.request(cmd) 191c1d255d3SCy Schubert if "FAIL" in res: 192c1d255d3SCy Schubert raise Exception("Failed to generate bootstrapping info") 193c1d255d3SCy Schubert return int(res) 194c1d255d3SCy Schubert 195c1d255d3SCy Schubertdef dpp_start_listen(wpas, freq): 196c1d255d3SCy Schubert if get_status_field(wpas, "bssid[0]"): 197c1d255d3SCy Schubert summary("Own AP freq: %s MHz" % str(get_status_field(wpas, "freq"))) 198c1d255d3SCy Schubert if get_status_field(wpas, "beacon_set", extra="DRIVER") is None: 199c1d255d3SCy Schubert summary("Enable beaconing to have radio ready for RX") 200c1d255d3SCy Schubert wpas.request("DISABLE") 201c1d255d3SCy Schubert wpas.request("SET start_disabled 0") 202c1d255d3SCy Schubert wpas.request("ENABLE") 203c1d255d3SCy Schubert cmd = "DPP_LISTEN %d" % freq 204c1d255d3SCy Schubert global enrollee_only 205c1d255d3SCy Schubert global configurator_only 206c1d255d3SCy Schubert if enrollee_only: 207c1d255d3SCy Schubert cmd += " role=enrollee" 208c1d255d3SCy Schubert elif configurator_only: 209c1d255d3SCy Schubert cmd += " role=configurator" 210c1d255d3SCy Schubert global netrole 211c1d255d3SCy Schubert if netrole: 212c1d255d3SCy Schubert cmd += " netrole=" + netrole 213c1d255d3SCy Schubert summary(cmd) 214c1d255d3SCy Schubert res = wpas.request(cmd) 215c1d255d3SCy Schubert if "OK" not in res: 216c1d255d3SCy Schubert summary("Failed to start DPP listen", color=C_RED) 217c1d255d3SCy Schubert return False 218c1d255d3SCy Schubert return True 219c1d255d3SCy Schubert 220c1d255d3SCy Schubertdef wpas_get_nfc_uri(start_listen=True, pick_channel=False, chan_override=None): 221c1d255d3SCy Schubert listen_freq = 2412 222c1d255d3SCy Schubert wpas = wpas_connect() 223c1d255d3SCy Schubert if wpas is None: 224c1d255d3SCy Schubert return None 225c1d255d3SCy Schubert global own_id, chanlist 226c1d255d3SCy Schubert if chan_override: 227c1d255d3SCy Schubert chan = chan_override 228c1d255d3SCy Schubert else: 229c1d255d3SCy Schubert chan = chanlist 230c1d255d3SCy Schubert if chan and chan.startswith("81/"): 231c1d255d3SCy Schubert listen_freq = int(chan[3:].split(',')[0]) * 5 + 2407 232c1d255d3SCy Schubert if chan is None and get_status_field(wpas, "bssid[0]"): 233c1d255d3SCy Schubert freq = get_status_field(wpas, "freq") 234c1d255d3SCy Schubert if freq: 235c1d255d3SCy Schubert freq = int(freq) 236c1d255d3SCy Schubert if freq >= 2412 and freq <= 2462: 237c1d255d3SCy Schubert chan = "81/%d" % ((freq - 2407) / 5) 238c1d255d3SCy Schubert summary("Use current AP operating channel (%d MHz) as the URI channel list (%s)" % (freq, chan)) 239c1d255d3SCy Schubert listen_freq = freq 240c1d255d3SCy Schubert if chan is None and pick_channel: 241c1d255d3SCy Schubert chan = "81/6" 242c1d255d3SCy Schubert summary("Use channel 2437 MHz since no other preference provided") 243c1d255d3SCy Schubert listen_freq = 2437 244c1d255d3SCy Schubert own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chan, mac=True) 245c1d255d3SCy Schubert res = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip() 246c1d255d3SCy Schubert if "FAIL" in res: 247c1d255d3SCy Schubert return None 248c1d255d3SCy Schubert if start_listen: 249c1d255d3SCy Schubert if not dpp_start_listen(wpas, listen_freq): 250c1d255d3SCy Schubert raise Exception("Failed to start listen operation on %d MHz" % listen_freq) 251c1d255d3SCy Schubert return res 252c1d255d3SCy Schubert 253c1d255d3SCy Schubertdef wpas_report_handover_req(uri): 254c1d255d3SCy Schubert wpas = wpas_connect() 255c1d255d3SCy Schubert if wpas is None: 256c1d255d3SCy Schubert return None 257c1d255d3SCy Schubert global own_id 258c1d255d3SCy Schubert cmd = "DPP_NFC_HANDOVER_REQ own=%d uri=%s" % (own_id, uri) 259c1d255d3SCy Schubert return wpas.request(cmd) 260c1d255d3SCy Schubert 261c1d255d3SCy Schubertdef wpas_report_handover_sel(uri): 262c1d255d3SCy Schubert wpas = wpas_connect() 263c1d255d3SCy Schubert if wpas is None: 264c1d255d3SCy Schubert return None 265c1d255d3SCy Schubert global own_id 266c1d255d3SCy Schubert cmd = "DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (own_id, uri) 267c1d255d3SCy Schubert return wpas.request(cmd) 268c1d255d3SCy Schubert 269c1d255d3SCy Schubertdef dpp_handover_client(handover, alt=False): 270c1d255d3SCy Schubert summary("About to start run_dpp_handover_client (alt=%s)" % str(alt)) 271c1d255d3SCy Schubert if alt: 272c1d255d3SCy Schubert handover.i_m_selector = False 273c1d255d3SCy Schubert run_dpp_handover_client(handover, alt) 274c1d255d3SCy Schubert summary("Done run_dpp_handover_client (alt=%s)" % str(alt)) 275c1d255d3SCy Schubert 276c1d255d3SCy Schubertdef run_client_alt(handover, alt): 277c1d255d3SCy Schubert if handover.start_client_alt and not alt: 278c1d255d3SCy Schubert handover.start_client_alt = False 279c1d255d3SCy Schubert summary("Try to send alternative handover request") 280c1d255d3SCy Schubert dpp_handover_client(handover, alt=True) 281c1d255d3SCy Schubert 282c1d255d3SCy Schubertclass HandoverClient(nfc.handover.HandoverClient): 283c1d255d3SCy Schubert def __init__(self, handover, llc): 284c1d255d3SCy Schubert super(HandoverClient, self).__init__(llc) 285c1d255d3SCy Schubert self.handover = handover 286c1d255d3SCy Schubert 287c1d255d3SCy Schubert def recv_records(self, timeout=None): 288c1d255d3SCy Schubert msg = self.recv_octets(timeout) 289c1d255d3SCy Schubert if msg is None: 290c1d255d3SCy Schubert return None 291c1d255d3SCy Schubert records = list(ndef.message_decoder(msg, 'relax')) 292c1d255d3SCy Schubert if records and records[0].type == 'urn:nfc:wkt:Hs': 293c1d255d3SCy Schubert summary("Handover client received message '{0}'".format(records[0].type)) 294c1d255d3SCy Schubert return list(ndef.message_decoder(msg, 'relax')) 295c1d255d3SCy Schubert summary("Handover client received invalid message: %s" + binascii.hexlify(msg)) 296c1d255d3SCy Schubert return None 297c1d255d3SCy Schubert 298c1d255d3SCy Schubert def recv_octets(self, timeout=None): 299c1d255d3SCy Schubert start = time.time() 300c1d255d3SCy Schubert msg = bytearray() 301c1d255d3SCy Schubert while True: 302c1d255d3SCy Schubert poll_timeout = 0.1 if timeout is None or timeout > 0.1 else timeout 303c1d255d3SCy Schubert if not self.socket.poll('recv', poll_timeout): 304c1d255d3SCy Schubert if timeout: 305c1d255d3SCy Schubert timeout -= time.time() - start 306c1d255d3SCy Schubert if timeout <= 0: 307c1d255d3SCy Schubert return None 308c1d255d3SCy Schubert start = time.time() 309c1d255d3SCy Schubert continue 310c1d255d3SCy Schubert try: 311c1d255d3SCy Schubert r = self.socket.recv() 312c1d255d3SCy Schubert if r is None: 313c1d255d3SCy Schubert return None 314c1d255d3SCy Schubert msg += r 315c1d255d3SCy Schubert except TypeError: 316c1d255d3SCy Schubert return b'' 317c1d255d3SCy Schubert try: 318c1d255d3SCy Schubert list(ndef.message_decoder(msg, 'strict', {})) 319c1d255d3SCy Schubert return bytes(msg) 320c1d255d3SCy Schubert except ndef.DecodeError: 321c1d255d3SCy Schubert if timeout: 322c1d255d3SCy Schubert timeout -= time.time() - start 323c1d255d3SCy Schubert if timeout <= 0: 324c1d255d3SCy Schubert return None 325c1d255d3SCy Schubert start = time.time() 326c1d255d3SCy Schubert continue 327c1d255d3SCy Schubert return None 328c1d255d3SCy Schubert 329c1d255d3SCy Schubertdef run_dpp_handover_client(handover, alt=False): 330c1d255d3SCy Schubert chan_override = None 331c1d255d3SCy Schubert if alt: 332c1d255d3SCy Schubert chan_override = handover.altchanlist 333c1d255d3SCy Schubert handover.alt_proposal_used = True 334c1d255d3SCy Schubert global test_uri, test_alt_uri 335c1d255d3SCy Schubert if test_uri: 336c1d255d3SCy Schubert summary("TEST MODE: Using specified URI (alt=%s)" % str(alt)) 337c1d255d3SCy Schubert uri = test_alt_uri if alt else test_uri 338c1d255d3SCy Schubert else: 339c1d255d3SCy Schubert uri = wpas_get_nfc_uri(start_listen=False, chan_override=chan_override) 340c1d255d3SCy Schubert if uri is None: 341c1d255d3SCy Schubert summary("Cannot start handover client - no bootstrap URI available", 342c1d255d3SCy Schubert color=C_RED) 343c1d255d3SCy Schubert return 344c1d255d3SCy Schubert handover.my_uri = uri 345c1d255d3SCy Schubert uri = ndef.UriRecord(uri) 346c1d255d3SCy Schubert summary("NFC URI record for DPP: " + str(uri)) 347c1d255d3SCy Schubert carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data) 348c1d255d3SCy Schubert global test_crn 349c1d255d3SCy Schubert if test_crn: 350c1d255d3SCy Schubert prev, = struct.unpack('>H', test_crn) 351c1d255d3SCy Schubert summary("TEST MODE: Use specified crn %d" % prev) 352c1d255d3SCy Schubert crn = test_crn 353c1d255d3SCy Schubert test_crn = struct.pack('>H', prev + 0x10) 354c1d255d3SCy Schubert else: 355c1d255d3SCy Schubert crn = os.urandom(2) 356c1d255d3SCy Schubert hr = ndef.HandoverRequestRecord(version="1.4", crn=crn) 357c1d255d3SCy Schubert hr.add_alternative_carrier('active', carrier.name) 358c1d255d3SCy Schubert message = [hr, carrier] 359c1d255d3SCy Schubert summary("NFC Handover Request message for DPP: " + str(message)) 360c1d255d3SCy Schubert 361c1d255d3SCy Schubert if handover.peer_crn is not None and not alt: 362*a90b9d01SCy Schubert summary("NFC handover request from peer was already received - do not send own[1]") 363c1d255d3SCy Schubert return 364c1d255d3SCy Schubert if handover.client: 365c1d255d3SCy Schubert summary("Use already started handover client") 366c1d255d3SCy Schubert client = handover.client 367c1d255d3SCy Schubert else: 368c1d255d3SCy Schubert summary("Start handover client") 369c1d255d3SCy Schubert client = HandoverClient(handover, handover.llc) 370c1d255d3SCy Schubert try: 371c1d255d3SCy Schubert summary("Trying to initiate NFC connection handover") 372c1d255d3SCy Schubert client.connect() 373c1d255d3SCy Schubert summary("Connected for handover") 374c1d255d3SCy Schubert except nfc.llcp.ConnectRefused: 375c1d255d3SCy Schubert summary("Handover connection refused") 376c1d255d3SCy Schubert client.close() 377c1d255d3SCy Schubert return 378c1d255d3SCy Schubert except Exception as e: 379c1d255d3SCy Schubert summary("Other exception: " + str(e)) 380c1d255d3SCy Schubert client.close() 381c1d255d3SCy Schubert return 382c1d255d3SCy Schubert handover.client = client 383c1d255d3SCy Schubert 384c1d255d3SCy Schubert if handover.peer_crn is not None and not alt: 385*a90b9d01SCy Schubert summary("NFC handover request from peer was already received - do not send own[2] (except alt)") 386*a90b9d01SCy Schubert run_client_alt(handover, alt) 387c1d255d3SCy Schubert return 388c1d255d3SCy Schubert 389c1d255d3SCy Schubert summary("Sending handover request") 390c1d255d3SCy Schubert 391c1d255d3SCy Schubert handover.my_crn_ready = True 392c1d255d3SCy Schubert 393c1d255d3SCy Schubert if not client.send_records(message): 394c1d255d3SCy Schubert handover.my_crn_ready = False 395c1d255d3SCy Schubert summary("Failed to send handover request", color=C_RED) 396c1d255d3SCy Schubert run_client_alt(handover, alt) 397c1d255d3SCy Schubert return 398c1d255d3SCy Schubert 399c1d255d3SCy Schubert handover.my_crn, = struct.unpack('>H', crn) 400c1d255d3SCy Schubert 401c1d255d3SCy Schubert summary("Receiving handover response") 402c1d255d3SCy Schubert try: 403c1d255d3SCy Schubert start = time.time() 404c1d255d3SCy Schubert message = client.recv_records(timeout=3.0) 405c1d255d3SCy Schubert end = time.time() 406c1d255d3SCy Schubert summary("Received {} record(s) in {} seconds".format(len(message) if message is not None else -1, end - start)) 407c1d255d3SCy Schubert except Exception as e: 408c1d255d3SCy Schubert # This is fine if we are the handover selector 409c1d255d3SCy Schubert if handover.hs_sent: 410c1d255d3SCy Schubert summary("Client receive failed as expected since I'm the handover server: %s" % str(e)) 411c1d255d3SCy Schubert elif handover.alt_proposal_used and not alt: 412c1d255d3SCy Schubert summary("Client received failed for initial proposal as expected since alternative proposal was also used: %s" % str(e)) 413c1d255d3SCy Schubert else: 414c1d255d3SCy Schubert summary("Client receive failed: %s" % str(e), color=C_RED) 415c1d255d3SCy Schubert message = None 416c1d255d3SCy Schubert if message is None: 417c1d255d3SCy Schubert if handover.hs_sent: 418c1d255d3SCy Schubert summary("No response received as expected since I'm the handover server") 419c1d255d3SCy Schubert elif handover.alt_proposal_used and not alt: 420c1d255d3SCy Schubert summary("No response received for initial proposal as expected since alternative proposal was also used") 421c1d255d3SCy Schubert elif handover.try_own and not alt: 422c1d255d3SCy Schubert summary("No response received for initial proposal as expected since alternative proposal will also be sent") 423c1d255d3SCy Schubert else: 424c1d255d3SCy Schubert summary("No response received", color=C_RED) 425c1d255d3SCy Schubert run_client_alt(handover, alt) 426c1d255d3SCy Schubert return 427c1d255d3SCy Schubert summary("Received message: " + str(message)) 428c1d255d3SCy Schubert if len(message) < 1 or \ 429c1d255d3SCy Schubert not isinstance(message[0], ndef.HandoverSelectRecord): 430c1d255d3SCy Schubert summary("Response was not Hs - received: " + message.type) 431c1d255d3SCy Schubert return 432c1d255d3SCy Schubert 433c1d255d3SCy Schubert summary("Received handover select message") 434c1d255d3SCy Schubert summary("alternative carriers: " + str(message[0].alternative_carriers)) 435c1d255d3SCy Schubert if handover.i_m_selector: 436c1d255d3SCy Schubert summary("Ignore the received select since I'm the handover selector") 437c1d255d3SCy Schubert run_client_alt(handover, alt) 438c1d255d3SCy Schubert return 439c1d255d3SCy Schubert 440c1d255d3SCy Schubert if handover.alt_proposal_used and not alt: 441c1d255d3SCy Schubert summary("Ignore received handover select for the initial proposal since alternative proposal was sent") 442c1d255d3SCy Schubert client.close() 443c1d255d3SCy Schubert return 444c1d255d3SCy Schubert 445c1d255d3SCy Schubert dpp_found = False 446c1d255d3SCy Schubert for carrier in message: 447c1d255d3SCy Schubert if isinstance(carrier, ndef.HandoverSelectRecord): 448c1d255d3SCy Schubert continue 449c1d255d3SCy Schubert summary("Remote carrier type: " + carrier.type) 450c1d255d3SCy Schubert if carrier.type == "application/vnd.wfa.dpp": 451c1d255d3SCy Schubert if len(carrier.data) == 0 or carrier.data[0] != 0: 452c1d255d3SCy Schubert summary("URI Identifier Code 'None' not seen", color=C_RED) 453c1d255d3SCy Schubert continue 454c1d255d3SCy Schubert summary("DPP carrier type match - send to wpa_supplicant") 455c1d255d3SCy Schubert dpp_found = True 456c1d255d3SCy Schubert uri = carrier.data[1:].decode("utf-8") 457c1d255d3SCy Schubert summary("DPP URI: " + uri) 458c1d255d3SCy Schubert handover.peer_uri = uri 459c1d255d3SCy Schubert if test_uri: 460c1d255d3SCy Schubert summary("TEST MODE: Fake processing") 461c1d255d3SCy Schubert break 462c1d255d3SCy Schubert res = wpas_report_handover_sel(uri) 463c1d255d3SCy Schubert if res is None or "FAIL" in res: 464c1d255d3SCy Schubert summary("DPP handover report rejected", color=C_RED) 465c1d255d3SCy Schubert break 466c1d255d3SCy Schubert 467c1d255d3SCy Schubert success_report("DPP handover reported successfully (initiator)") 468c1d255d3SCy Schubert summary("peer_id=" + res) 469c1d255d3SCy Schubert peer_id = int(res) 470c1d255d3SCy Schubert wpas = wpas_connect() 471c1d255d3SCy Schubert if wpas is None: 472c1d255d3SCy Schubert break 473c1d255d3SCy Schubert 474c1d255d3SCy Schubert global enrollee_only 475c1d255d3SCy Schubert global config_params 476c1d255d3SCy Schubert if enrollee_only: 477c1d255d3SCy Schubert extra = " role=enrollee" 478c1d255d3SCy Schubert elif config_params: 479c1d255d3SCy Schubert extra = " role=configurator " + config_params 480c1d255d3SCy Schubert else: 481c1d255d3SCy Schubert # TODO: Single Configurator instance 482c1d255d3SCy Schubert res = wpas.request("DPP_CONFIGURATOR_ADD") 483c1d255d3SCy Schubert if "FAIL" in res: 484c1d255d3SCy Schubert summary("Failed to initiate Configurator", color=C_RED) 485c1d255d3SCy Schubert break 486c1d255d3SCy Schubert conf_id = int(res) 487c1d255d3SCy Schubert extra = " conf=sta-dpp configurator=%d" % conf_id 488c1d255d3SCy Schubert global own_id 489c1d255d3SCy Schubert summary("Initiate DPP authentication") 490c1d255d3SCy Schubert cmd = "DPP_AUTH_INIT peer=%d own=%d" % (peer_id, own_id) 491c1d255d3SCy Schubert cmd += extra 492c1d255d3SCy Schubert res = wpas.request(cmd) 493c1d255d3SCy Schubert if "FAIL" in res: 494c1d255d3SCy Schubert summary("Failed to initiate DPP authentication", color=C_RED) 495c1d255d3SCy Schubert break 496c1d255d3SCy Schubert 497c1d255d3SCy Schubert if not dpp_found and handover.no_alt_proposal: 498c1d255d3SCy Schubert summary("DPP carrier not seen in response - do not allow alternative proposal anymore") 499c1d255d3SCy Schubert elif not dpp_found: 500c1d255d3SCy Schubert summary("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters") 501c1d255d3SCy Schubert handover.alt_proposal = True 502c1d255d3SCy Schubert handover.my_crn_ready = False 503c1d255d3SCy Schubert handover.my_crn = None 504c1d255d3SCy Schubert handover.peer_crn = None 505c1d255d3SCy Schubert handover.hs_sent = False 506c1d255d3SCy Schubert summary("Returning from dpp_handover_client") 507c1d255d3SCy Schubert return 508c1d255d3SCy Schubert 509c1d255d3SCy Schubert summary("Remove peer") 510c1d255d3SCy Schubert handover.close() 511c1d255d3SCy Schubert summary("Done with handover") 512c1d255d3SCy Schubert global only_one 513c1d255d3SCy Schubert if only_one: 514c1d255d3SCy Schubert print("only_one -> stop loop") 515c1d255d3SCy Schubert global continue_loop 516c1d255d3SCy Schubert continue_loop = False 517c1d255d3SCy Schubert 518c1d255d3SCy Schubert global no_wait 519c1d255d3SCy Schubert if no_wait or only_one: 520c1d255d3SCy Schubert summary("Trying to exit..") 521c1d255d3SCy Schubert global terminate_now 522c1d255d3SCy Schubert terminate_now = True 523c1d255d3SCy Schubert 524c1d255d3SCy Schubert summary("Returning from dpp_handover_client") 525c1d255d3SCy Schubert 526c1d255d3SCy Schubertclass HandoverServer(nfc.handover.HandoverServer): 527c1d255d3SCy Schubert def __init__(self, handover, llc): 528c1d255d3SCy Schubert super(HandoverServer, self).__init__(llc) 529c1d255d3SCy Schubert self.sent_carrier = None 530c1d255d3SCy Schubert self.ho_server_processing = False 531c1d255d3SCy Schubert self.success = False 532c1d255d3SCy Schubert self.llc = llc 533c1d255d3SCy Schubert self.handover = handover 534c1d255d3SCy Schubert 535c1d255d3SCy Schubert def serve(self, socket): 536c1d255d3SCy Schubert peer_sap = socket.getpeername() 537c1d255d3SCy Schubert summary("Serving handover client on remote sap {0}".format(peer_sap)) 538c1d255d3SCy Schubert send_miu = socket.getsockopt(nfc.llcp.SO_SNDMIU) 539c1d255d3SCy Schubert try: 540c1d255d3SCy Schubert while socket.poll("recv"): 541c1d255d3SCy Schubert req = bytearray() 542c1d255d3SCy Schubert while socket.poll("recv"): 543c1d255d3SCy Schubert r = socket.recv() 544c1d255d3SCy Schubert if r is None: 545c1d255d3SCy Schubert return None 546c1d255d3SCy Schubert summary("Received %d octets" % len(r)) 547c1d255d3SCy Schubert req += r 548c1d255d3SCy Schubert if len(req) == 0: 549c1d255d3SCy Schubert continue 550c1d255d3SCy Schubert try: 551c1d255d3SCy Schubert list(ndef.message_decoder(req, 'strict', {})) 552c1d255d3SCy Schubert except ndef.DecodeError: 553c1d255d3SCy Schubert continue 554c1d255d3SCy Schubert summary("Full message received") 555c1d255d3SCy Schubert resp = self._process_request_data(req) 556c1d255d3SCy Schubert if resp is None or len(resp) == 0: 557c1d255d3SCy Schubert summary("No handover select to send out - wait for a possible alternative handover request") 558c1d255d3SCy Schubert handover.alt_proposal = True 559c1d255d3SCy Schubert req = bytearray() 560c1d255d3SCy Schubert continue 561c1d255d3SCy Schubert 562c1d255d3SCy Schubert for offset in range(0, len(resp), send_miu): 563c1d255d3SCy Schubert if not socket.send(resp[offset:offset + send_miu]): 564c1d255d3SCy Schubert summary("Failed to send handover select - connection closed") 565c1d255d3SCy Schubert return 566c1d255d3SCy Schubert summary("Sent out full handover select") 567c1d255d3SCy Schubert if handover.terminate_on_hs_send_completion: 568c1d255d3SCy Schubert handover.delayed_exit() 569c1d255d3SCy Schubert 570c1d255d3SCy Schubert except nfc.llcp.Error as e: 571c1d255d3SCy Schubert global terminate_now 572c1d255d3SCy Schubert summary("HandoverServer exception: %s" % e, 573c1d255d3SCy Schubert color=None if e.errno == errno.EPIPE or terminate_now else C_RED) 574c1d255d3SCy Schubert finally: 575c1d255d3SCy Schubert socket.close() 576c1d255d3SCy Schubert summary("Handover serve thread exiting") 577c1d255d3SCy Schubert 578c1d255d3SCy Schubert def process_handover_request_message(self, records): 579c1d255d3SCy Schubert handover = self.handover 580c1d255d3SCy Schubert self.ho_server_processing = True 581c1d255d3SCy Schubert global in_raw_mode 582c1d255d3SCy Schubert was_in_raw_mode = in_raw_mode 583c1d255d3SCy Schubert clear_raw_mode() 584c1d255d3SCy Schubert if was_in_raw_mode: 585c1d255d3SCy Schubert print("\n") 586c1d255d3SCy Schubert summary("HandoverServer - request received: " + str(records)) 587c1d255d3SCy Schubert 588c1d255d3SCy Schubert for carrier in records: 589c1d255d3SCy Schubert if not isinstance(carrier, ndef.HandoverRequestRecord): 590c1d255d3SCy Schubert continue 591c1d255d3SCy Schubert if carrier.collision_resolution_number: 592c1d255d3SCy Schubert handover.peer_crn = carrier.collision_resolution_number 593c1d255d3SCy Schubert summary("peer_crn: %d" % handover.peer_crn) 594c1d255d3SCy Schubert 595c1d255d3SCy Schubert if handover.my_crn is None and handover.my_crn_ready: 596c1d255d3SCy Schubert summary("Still trying to send own handover request - wait a moment to see if that succeeds before checking crn values") 597c1d255d3SCy Schubert for i in range(10): 598c1d255d3SCy Schubert if handover.my_crn is not None: 599c1d255d3SCy Schubert break 600c1d255d3SCy Schubert time.sleep(0.01) 601c1d255d3SCy Schubert if handover.my_crn is not None: 602c1d255d3SCy Schubert summary("my_crn: %d" % handover.my_crn) 603c1d255d3SCy Schubert 604c1d255d3SCy Schubert if handover.my_crn is not None and handover.peer_crn is not None: 605c1d255d3SCy Schubert if handover.my_crn == handover.peer_crn: 606c1d255d3SCy Schubert summary("Same crn used - automatic collision resolution failed") 607c1d255d3SCy Schubert # TODO: Should generate a new Handover Request message 608c1d255d3SCy Schubert return '' 609c1d255d3SCy Schubert if ((handover.my_crn & 1) == (handover.peer_crn & 1) and \ 610c1d255d3SCy Schubert handover.my_crn > handover.peer_crn) or \ 611c1d255d3SCy Schubert ((handover.my_crn & 1) != (handover.peer_crn & 1) and \ 612c1d255d3SCy Schubert handover.my_crn < handover.peer_crn): 613c1d255d3SCy Schubert summary("I'm the Handover Selector Device") 614c1d255d3SCy Schubert handover.i_m_selector = True 615c1d255d3SCy Schubert else: 616c1d255d3SCy Schubert summary("Peer is the Handover Selector device") 617c1d255d3SCy Schubert summary("Ignore the received request.") 618c1d255d3SCy Schubert return '' 619c1d255d3SCy Schubert 620c1d255d3SCy Schubert hs = ndef.HandoverSelectRecord('1.4') 621c1d255d3SCy Schubert sel = [hs] 622c1d255d3SCy Schubert 623c1d255d3SCy Schubert found = False 624c1d255d3SCy Schubert 625c1d255d3SCy Schubert for carrier in records: 626c1d255d3SCy Schubert if isinstance(carrier, ndef.HandoverRequestRecord): 627c1d255d3SCy Schubert continue 628c1d255d3SCy Schubert summary("Remote carrier type: " + carrier.type) 629c1d255d3SCy Schubert if carrier.type == "application/vnd.wfa.dpp": 630c1d255d3SCy Schubert summary("DPP carrier type match - add DPP carrier record") 631c1d255d3SCy Schubert if len(carrier.data) == 0 or carrier.data[0] != 0: 632c1d255d3SCy Schubert summary("URI Identifier Code 'None' not seen", color=C_RED) 633c1d255d3SCy Schubert continue 634c1d255d3SCy Schubert uri = carrier.data[1:].decode("utf-8") 635c1d255d3SCy Schubert summary("Received DPP URI: " + uri) 636c1d255d3SCy Schubert 637c1d255d3SCy Schubert global test_uri, test_alt_uri 638c1d255d3SCy Schubert if test_uri: 639c1d255d3SCy Schubert summary("TEST MODE: Using specified URI") 640c1d255d3SCy Schubert data = test_sel_uri if test_sel_uri else test_uri 641c1d255d3SCy Schubert elif handover.alt_proposal and handover.altchanlist: 642c1d255d3SCy Schubert summary("Use alternative channel list while processing alternative proposal from peer") 643c1d255d3SCy Schubert data = wpas_get_nfc_uri(start_listen=False, 644c1d255d3SCy Schubert chan_override=handover.altchanlist, 645c1d255d3SCy Schubert pick_channel=True) 646c1d255d3SCy Schubert else: 647c1d255d3SCy Schubert data = wpas_get_nfc_uri(start_listen=False, 648c1d255d3SCy Schubert pick_channel=True) 649c1d255d3SCy Schubert summary("Own URI (pre-processing): %s" % data) 650c1d255d3SCy Schubert 651c1d255d3SCy Schubert if test_uri: 652c1d255d3SCy Schubert summary("TEST MODE: Fake processing") 653c1d255d3SCy Schubert res = "OK" 654c1d255d3SCy Schubert data += " [%s]" % uri 655c1d255d3SCy Schubert else: 656c1d255d3SCy Schubert res = wpas_report_handover_req(uri) 657c1d255d3SCy Schubert if res is None or "FAIL" in res: 658c1d255d3SCy Schubert summary("DPP handover request processing failed", 659c1d255d3SCy Schubert color=C_RED) 660c1d255d3SCy Schubert if handover.altchanlist: 661c1d255d3SCy Schubert data = wpas_get_nfc_uri(start_listen=False, 662c1d255d3SCy Schubert chan_override=handover.altchanlist) 663c1d255d3SCy Schubert summary("Own URI (try another channel list): %s" % data) 664c1d255d3SCy Schubert continue 665c1d255d3SCy Schubert 666c1d255d3SCy Schubert if test_alt_uri: 667c1d255d3SCy Schubert summary("TEST MODE: Reject initial proposal") 668c1d255d3SCy Schubert continue 669c1d255d3SCy Schubert 670c1d255d3SCy Schubert found = True 671c1d255d3SCy Schubert 672c1d255d3SCy Schubert if not test_uri: 673c1d255d3SCy Schubert wpas = wpas_connect() 674c1d255d3SCy Schubert if wpas is None: 675c1d255d3SCy Schubert continue 676c1d255d3SCy Schubert global own_id 677c1d255d3SCy Schubert data = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip() 678c1d255d3SCy Schubert if "FAIL" in data: 679c1d255d3SCy Schubert continue 680c1d255d3SCy Schubert summary("Own URI (post-processing): %s" % data) 681c1d255d3SCy Schubert handover.my_uri = data 682c1d255d3SCy Schubert handover.peer_uri = uri 683c1d255d3SCy Schubert uri = ndef.UriRecord(data) 684c1d255d3SCy Schubert summary("Own bootstrapping NFC URI record: " + str(uri)) 685c1d255d3SCy Schubert 686c1d255d3SCy Schubert if not test_uri: 687c1d255d3SCy Schubert info = wpas.request("DPP_BOOTSTRAP_INFO %d" % own_id) 688c1d255d3SCy Schubert freq = None 689c1d255d3SCy Schubert for line in info.splitlines(): 690c1d255d3SCy Schubert if line.startswith("use_freq="): 691c1d255d3SCy Schubert freq = int(line.split('=')[1]) 692c1d255d3SCy Schubert if freq is None or freq == 0: 693c1d255d3SCy Schubert summary("No channel negotiated over NFC - use channel 6") 694c1d255d3SCy Schubert freq = 2437 695c1d255d3SCy Schubert else: 696c1d255d3SCy Schubert summary("Negotiated channel: %d MHz" % freq) 697c1d255d3SCy Schubert if not dpp_start_listen(wpas, freq): 698c1d255d3SCy Schubert break 699c1d255d3SCy Schubert 700c1d255d3SCy Schubert carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data) 701c1d255d3SCy Schubert summary("Own DPP carrier record: " + str(carrier)) 702c1d255d3SCy Schubert hs.add_alternative_carrier('active', carrier.name) 703c1d255d3SCy Schubert sel = [hs, carrier] 704c1d255d3SCy Schubert break 705c1d255d3SCy Schubert 706c1d255d3SCy Schubert summary("Sending handover select: " + str(sel)) 707c1d255d3SCy Schubert if found: 708c1d255d3SCy Schubert summary("Handover completed successfully") 709c1d255d3SCy Schubert handover.terminate_on_hs_send_completion = True 710c1d255d3SCy Schubert self.success = True 711c1d255d3SCy Schubert handover.hs_sent = True 712c1d255d3SCy Schubert handover.i_m_selector = True 713c1d255d3SCy Schubert elif handover.no_alt_proposal: 714c1d255d3SCy Schubert summary("Do not try alternative proposal anymore - handover failed", 715c1d255d3SCy Schubert color=C_RED) 716c1d255d3SCy Schubert handover.hs_sent = True 717c1d255d3SCy Schubert else: 718c1d255d3SCy Schubert summary("Try to initiate with alternative parameters") 719c1d255d3SCy Schubert handover.try_own = True 720c1d255d3SCy Schubert handover.hs_sent = False 721c1d255d3SCy Schubert handover.no_alt_proposal = True 722c1d255d3SCy Schubert if handover.client_thread: 723c1d255d3SCy Schubert handover.start_client_alt = True 724c1d255d3SCy Schubert else: 725c1d255d3SCy Schubert handover.client_thread = threading.Thread(target=llcp_worker, 726c1d255d3SCy Schubert args=(self.llc, True)) 727c1d255d3SCy Schubert handover.client_thread.start() 728c1d255d3SCy Schubert return sel 729c1d255d3SCy Schubert 730c1d255d3SCy Schubertdef clear_raw_mode(): 731c1d255d3SCy Schubert import sys, tty, termios 732c1d255d3SCy Schubert global prev_tcgetattr, in_raw_mode 733c1d255d3SCy Schubert if not in_raw_mode: 734c1d255d3SCy Schubert return 735c1d255d3SCy Schubert fd = sys.stdin.fileno() 736c1d255d3SCy Schubert termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr) 737c1d255d3SCy Schubert in_raw_mode = False 738c1d255d3SCy Schubert 739c1d255d3SCy Schubertdef getch(): 740c1d255d3SCy Schubert import sys, tty, termios, select 741c1d255d3SCy Schubert global prev_tcgetattr, in_raw_mode 742c1d255d3SCy Schubert fd = sys.stdin.fileno() 743c1d255d3SCy Schubert prev_tcgetattr = termios.tcgetattr(fd) 744c1d255d3SCy Schubert ch = None 745c1d255d3SCy Schubert try: 746c1d255d3SCy Schubert tty.setraw(fd) 747c1d255d3SCy Schubert in_raw_mode = True 748c1d255d3SCy Schubert [i, o, e] = select.select([fd], [], [], 0.05) 749c1d255d3SCy Schubert if i: 750c1d255d3SCy Schubert ch = sys.stdin.read(1) 751c1d255d3SCy Schubert finally: 752c1d255d3SCy Schubert termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr) 753c1d255d3SCy Schubert in_raw_mode = False 754c1d255d3SCy Schubert return ch 755c1d255d3SCy Schubert 756c1d255d3SCy Schubertdef dpp_tag_read(tag): 757c1d255d3SCy Schubert success = False 758c1d255d3SCy Schubert for record in tag.ndef.records: 759c1d255d3SCy Schubert summary(record) 760c1d255d3SCy Schubert summary("record type " + record.type) 761c1d255d3SCy Schubert if record.type == "application/vnd.wfa.dpp": 762c1d255d3SCy Schubert summary("DPP HS tag - send to wpa_supplicant") 763c1d255d3SCy Schubert success = dpp_hs_tag_read(record) 764c1d255d3SCy Schubert break 765c1d255d3SCy Schubert if isinstance(record, ndef.UriRecord): 766c1d255d3SCy Schubert summary("URI record: uri=" + record.uri) 767c1d255d3SCy Schubert summary("URI record: iri=" + record.iri) 768c1d255d3SCy Schubert if record.iri.startswith("DPP:"): 769c1d255d3SCy Schubert summary("DPP URI") 770c1d255d3SCy Schubert if not dpp_nfc_uri_process(record.iri): 771c1d255d3SCy Schubert break 772c1d255d3SCy Schubert success = True 773c1d255d3SCy Schubert else: 774c1d255d3SCy Schubert summary("Ignore unknown URI") 775c1d255d3SCy Schubert break 776c1d255d3SCy Schubert 777c1d255d3SCy Schubert if success: 778c1d255d3SCy Schubert success_report("Tag read succeeded") 779c1d255d3SCy Schubert 780c1d255d3SCy Schubert return success 781c1d255d3SCy Schubert 782c1d255d3SCy Schubertdef rdwr_connected_write_tag(tag): 783c1d255d3SCy Schubert summary("Tag found - writing - " + str(tag)) 784c1d255d3SCy Schubert if not tag.ndef: 785c1d255d3SCy Schubert summary("Not a formatted NDEF tag", color=C_RED) 786c1d255d3SCy Schubert return 787c1d255d3SCy Schubert if not tag.ndef.is_writeable: 788c1d255d3SCy Schubert summary("Not a writable tag", color=C_RED) 789c1d255d3SCy Schubert return 790c1d255d3SCy Schubert global dpp_tag_data 791c1d255d3SCy Schubert if tag.ndef.capacity < len(dpp_tag_data): 792c1d255d3SCy Schubert summary("Not enough room for the message") 793c1d255d3SCy Schubert return 794c1d255d3SCy Schubert try: 795c1d255d3SCy Schubert tag.ndef.records = dpp_tag_data 796c1d255d3SCy Schubert except ValueError as e: 797c1d255d3SCy Schubert summary("Writing the tag failed: %s" % str(e), color=C_RED) 798c1d255d3SCy Schubert return 799c1d255d3SCy Schubert success_report("Tag write succeeded") 800c1d255d3SCy Schubert summary("Tag writing completed - remove tag", color=C_GREEN) 801c1d255d3SCy Schubert global only_one, operation_success 802c1d255d3SCy Schubert operation_success = True 803c1d255d3SCy Schubert if only_one: 804c1d255d3SCy Schubert global continue_loop 805c1d255d3SCy Schubert continue_loop = False 806c1d255d3SCy Schubert global dpp_sel_wait_remove 807c1d255d3SCy Schubert return dpp_sel_wait_remove 808c1d255d3SCy Schubert 809c1d255d3SCy Schubertdef write_nfc_uri(clf, wait_remove=True): 810c1d255d3SCy Schubert summary("Write NFC URI record") 811c1d255d3SCy Schubert data = wpas_get_nfc_uri() 812c1d255d3SCy Schubert if data is None: 813c1d255d3SCy Schubert summary("Could not get NFC URI from wpa_supplicant", color=C_RED) 814c1d255d3SCy Schubert return 815c1d255d3SCy Schubert 816c1d255d3SCy Schubert global dpp_sel_wait_remove 817c1d255d3SCy Schubert dpp_sel_wait_remove = wait_remove 818c1d255d3SCy Schubert summary("URI: %s" % data) 819c1d255d3SCy Schubert uri = ndef.UriRecord(data) 820c1d255d3SCy Schubert summary(uri) 821c1d255d3SCy Schubert 822c1d255d3SCy Schubert summary("Touch an NFC tag to write URI record", color=C_CYAN) 823c1d255d3SCy Schubert global dpp_tag_data 824c1d255d3SCy Schubert dpp_tag_data = [uri] 825c1d255d3SCy Schubert clf.connect(rdwr={'on-connect': rdwr_connected_write_tag}) 826c1d255d3SCy Schubert 827c1d255d3SCy Schubertdef write_nfc_hs(clf, wait_remove=True): 828c1d255d3SCy Schubert summary("Write NFC Handover Select record on a tag") 829c1d255d3SCy Schubert data = wpas_get_nfc_uri() 830c1d255d3SCy Schubert if data is None: 831c1d255d3SCy Schubert summary("Could not get NFC URI from wpa_supplicant", color=C_RED) 832c1d255d3SCy Schubert return 833c1d255d3SCy Schubert 834c1d255d3SCy Schubert global dpp_sel_wait_remove 835c1d255d3SCy Schubert dpp_sel_wait_remove = wait_remove 836c1d255d3SCy Schubert summary("URI: %s" % data) 837c1d255d3SCy Schubert uri = ndef.UriRecord(data) 838c1d255d3SCy Schubert summary(uri) 839c1d255d3SCy Schubert carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data) 840c1d255d3SCy Schubert hs = ndef.HandoverSelectRecord('1.4') 841c1d255d3SCy Schubert hs.add_alternative_carrier('active', carrier.name) 842c1d255d3SCy Schubert summary(hs) 843c1d255d3SCy Schubert summary(carrier) 844c1d255d3SCy Schubert 845c1d255d3SCy Schubert summary("Touch an NFC tag to write HS record", color=C_CYAN) 846c1d255d3SCy Schubert global dpp_tag_data 847c1d255d3SCy Schubert dpp_tag_data = [hs, carrier] 848c1d255d3SCy Schubert summary(dpp_tag_data) 849c1d255d3SCy Schubert clf.connect(rdwr={'on-connect': rdwr_connected_write_tag}) 850c1d255d3SCy Schubert 851c1d255d3SCy Schubertdef rdwr_connected(tag): 852c1d255d3SCy Schubert global only_one, no_wait 853c1d255d3SCy Schubert summary("Tag connected: " + str(tag)) 854c1d255d3SCy Schubert 855c1d255d3SCy Schubert if tag.ndef: 856c1d255d3SCy Schubert summary("NDEF tag: " + tag.type) 857c1d255d3SCy Schubert summary(tag.ndef.records) 858c1d255d3SCy Schubert success = dpp_tag_read(tag) 859c1d255d3SCy Schubert if only_one and success: 860c1d255d3SCy Schubert global continue_loop 861c1d255d3SCy Schubert continue_loop = False 862c1d255d3SCy Schubert else: 863c1d255d3SCy Schubert summary("Not an NDEF tag - remove tag", color=C_RED) 864c1d255d3SCy Schubert return True 865c1d255d3SCy Schubert 866c1d255d3SCy Schubert return not no_wait 867c1d255d3SCy Schubert 868c1d255d3SCy Schubertdef llcp_worker(llc, try_alt): 869c1d255d3SCy Schubert global handover 870c1d255d3SCy Schubert print("Start of llcp_worker()") 871c1d255d3SCy Schubert if try_alt: 872c1d255d3SCy Schubert summary("Starting handover client (try_alt)") 873c1d255d3SCy Schubert dpp_handover_client(handover, alt=True) 874c1d255d3SCy Schubert summary("Exiting llcp_worker thread (try_alt)") 875c1d255d3SCy Schubert return 876c1d255d3SCy Schubert global init_on_touch 877c1d255d3SCy Schubert if init_on_touch: 878c1d255d3SCy Schubert summary("Starting handover client (init_on_touch)") 879c1d255d3SCy Schubert dpp_handover_client(handover) 880*a90b9d01SCy Schubert summary("llcp_worker init_on_touch processing completed: try_own={} hs_sent={} no_alt_proposal={} start_client_alt={}".format(handover.try_own, handover.hs_sent, handover.no_alt_proposal, handover.start_client_alt)) 881*a90b9d01SCy Schubert if handover.start_client_alt and not handover.hs_sent: 882*a90b9d01SCy Schubert summary("Try alternative handover request before exiting llcp_worker") 883*a90b9d01SCy Schubert handover.start_client_alt = False 884*a90b9d01SCy Schubert dpp_handover_client(handover, alt=True) 885c1d255d3SCy Schubert summary("Exiting llcp_worker thread (init_on_touch)") 886c1d255d3SCy Schubert return 887c1d255d3SCy Schubert 888c1d255d3SCy Schubert global no_input 889c1d255d3SCy Schubert if no_input: 890c1d255d3SCy Schubert summary("Wait for handover to complete") 891c1d255d3SCy Schubert else: 892c1d255d3SCy Schubert print("Wait for handover to complete - press 'i' to initiate") 893c1d255d3SCy Schubert while not handover.wait_connection and handover.srv.sent_carrier is None: 894c1d255d3SCy Schubert if handover.try_own: 895c1d255d3SCy Schubert handover.try_own = False 896c1d255d3SCy Schubert summary("Try to initiate another handover with own parameters") 897c1d255d3SCy Schubert handover.my_crn_ready = False 898c1d255d3SCy Schubert handover.my_crn = None 899c1d255d3SCy Schubert handover.peer_crn = None 900c1d255d3SCy Schubert handover.hs_sent = False 901c1d255d3SCy Schubert dpp_handover_client(handover, alt=True) 902c1d255d3SCy Schubert summary("Exiting llcp_worker thread (retry with own parameters)") 903c1d255d3SCy Schubert return 904c1d255d3SCy Schubert if handover.srv.ho_server_processing: 905c1d255d3SCy Schubert time.sleep(0.025) 906c1d255d3SCy Schubert elif no_input: 907c1d255d3SCy Schubert time.sleep(0.5) 908c1d255d3SCy Schubert else: 909c1d255d3SCy Schubert res = getch() 910c1d255d3SCy Schubert if res != 'i': 911c1d255d3SCy Schubert continue 912c1d255d3SCy Schubert clear_raw_mode() 913c1d255d3SCy Schubert summary("Starting handover client") 914c1d255d3SCy Schubert dpp_handover_client(handover) 915c1d255d3SCy Schubert summary("Exiting llcp_worker thread (manual init)") 916c1d255d3SCy Schubert return 917c1d255d3SCy Schubert 918c1d255d3SCy Schubert global in_raw_mode 919c1d255d3SCy Schubert was_in_raw_mode = in_raw_mode 920c1d255d3SCy Schubert clear_raw_mode() 921c1d255d3SCy Schubert if was_in_raw_mode: 922c1d255d3SCy Schubert print("\r") 923c1d255d3SCy Schubert summary("Exiting llcp_worker thread") 924c1d255d3SCy Schubert 925c1d255d3SCy Schubertclass ConnectionHandover(): 926c1d255d3SCy Schubert def __init__(self): 927c1d255d3SCy Schubert self.client = None 928c1d255d3SCy Schubert self.client_thread = None 929c1d255d3SCy Schubert self.reset() 930c1d255d3SCy Schubert self.exit_thread = None 931c1d255d3SCy Schubert 932c1d255d3SCy Schubert def reset(self): 933c1d255d3SCy Schubert self.wait_connection = False 934c1d255d3SCy Schubert self.my_crn_ready = False 935c1d255d3SCy Schubert self.my_crn = None 936c1d255d3SCy Schubert self.peer_crn = None 937c1d255d3SCy Schubert self.hs_sent = False 938c1d255d3SCy Schubert self.no_alt_proposal = False 939c1d255d3SCy Schubert self.alt_proposal_used = False 940c1d255d3SCy Schubert self.i_m_selector = False 941c1d255d3SCy Schubert self.start_client_alt = False 942c1d255d3SCy Schubert self.terminate_on_hs_send_completion = False 943c1d255d3SCy Schubert self.try_own = False 944c1d255d3SCy Schubert self.my_uri = None 945c1d255d3SCy Schubert self.peer_uri = None 946c1d255d3SCy Schubert self.connected = False 947c1d255d3SCy Schubert self.alt_proposal = False 948c1d255d3SCy Schubert 949c1d255d3SCy Schubert def start_handover_server(self, llc): 950c1d255d3SCy Schubert summary("Start handover server") 951c1d255d3SCy Schubert self.llc = llc 952c1d255d3SCy Schubert self.srv = HandoverServer(self, llc) 953c1d255d3SCy Schubert 954c1d255d3SCy Schubert def close(self): 955c1d255d3SCy Schubert if self.client: 956c1d255d3SCy Schubert self.client.close() 957c1d255d3SCy Schubert self.client = None 958c1d255d3SCy Schubert 959c1d255d3SCy Schubert def run_delayed_exit(self): 960c1d255d3SCy Schubert summary("Trying to exit (delayed)..") 961c1d255d3SCy Schubert time.sleep(0.25) 962c1d255d3SCy Schubert summary("Trying to exit (after wait)..") 963c1d255d3SCy Schubert global terminate_now 964c1d255d3SCy Schubert terminate_now = True 965c1d255d3SCy Schubert 966c1d255d3SCy Schubert def delayed_exit(self): 967c1d255d3SCy Schubert global only_one 968c1d255d3SCy Schubert if only_one: 969c1d255d3SCy Schubert self.exit_thread = threading.Thread(target=self.run_delayed_exit) 970c1d255d3SCy Schubert self.exit_thread.start() 971c1d255d3SCy Schubert 972c1d255d3SCy Schubertdef llcp_startup(llc): 973c1d255d3SCy Schubert global handover 974c1d255d3SCy Schubert handover.start_handover_server(llc) 975c1d255d3SCy Schubert return llc 976c1d255d3SCy Schubert 977c1d255d3SCy Schubertdef llcp_connected(llc): 978c1d255d3SCy Schubert summary("P2P LLCP connected") 979c1d255d3SCy Schubert global handover 980c1d255d3SCy Schubert handover.connected = True 981c1d255d3SCy Schubert handover.srv.start() 982c1d255d3SCy Schubert if init_on_touch or not no_input: 983c1d255d3SCy Schubert handover.client_thread = threading.Thread(target=llcp_worker, 984c1d255d3SCy Schubert args=(llc, False)) 985c1d255d3SCy Schubert handover.client_thread.start() 986c1d255d3SCy Schubert return True 987c1d255d3SCy Schubert 988c1d255d3SCy Schubertdef llcp_release(llc): 989c1d255d3SCy Schubert summary("LLCP release") 990c1d255d3SCy Schubert global handover 991c1d255d3SCy Schubert handover.close() 992c1d255d3SCy Schubert return True 993c1d255d3SCy Schubert 994c1d255d3SCy Schubertdef terminate_loop(): 995c1d255d3SCy Schubert global terminate_now 996c1d255d3SCy Schubert return terminate_now 997c1d255d3SCy Schubert 998c1d255d3SCy Schubertdef main(): 999c1d255d3SCy Schubert clf = nfc.ContactlessFrontend() 1000c1d255d3SCy Schubert 1001c1d255d3SCy Schubert parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for DPP NFC operations') 1002c1d255d3SCy Schubert parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, 1003c1d255d3SCy Schubert action='store_const', dest='loglevel', 1004c1d255d3SCy Schubert help='verbose debug output') 1005c1d255d3SCy Schubert parser.add_argument('-q', const=logging.WARNING, action='store_const', 1006c1d255d3SCy Schubert dest='loglevel', help='be quiet') 1007c1d255d3SCy Schubert parser.add_argument('--only-one', '-1', action='store_true', 1008c1d255d3SCy Schubert help='run only one operation and exit') 1009c1d255d3SCy Schubert parser.add_argument('--init-on-touch', '-I', action='store_true', 1010c1d255d3SCy Schubert help='initiate handover on touch') 1011c1d255d3SCy Schubert parser.add_argument('--no-wait', action='store_true', 1012c1d255d3SCy Schubert help='do not wait for tag to be removed before exiting') 1013c1d255d3SCy Schubert parser.add_argument('--ifname', '-i', 1014c1d255d3SCy Schubert help='network interface name') 1015c1d255d3SCy Schubert parser.add_argument('--no-input', '-a', action='store_true', 1016c1d255d3SCy Schubert help='do not use stdout input to initiate handover') 1017c1d255d3SCy Schubert parser.add_argument('--tag-read-only', '-t', action='store_true', 1018c1d255d3SCy Schubert help='tag read only (do not allow connection handover)') 1019c1d255d3SCy Schubert parser.add_argument('--handover-only', action='store_true', 1020c1d255d3SCy Schubert help='connection handover only (do not allow tag read)') 1021c1d255d3SCy Schubert parser.add_argument('--enrollee', action='store_true', 1022c1d255d3SCy Schubert help='run as Enrollee-only') 1023c1d255d3SCy Schubert parser.add_argument('--configurator', action='store_true', 1024c1d255d3SCy Schubert help='run as Configurator-only') 1025c1d255d3SCy Schubert parser.add_argument('--config-params', default='', 1026c1d255d3SCy Schubert help='configurator parameters') 1027c1d255d3SCy Schubert parser.add_argument('--ctrl', default='/var/run/wpa_supplicant', 1028c1d255d3SCy Schubert help='wpa_supplicant/hostapd control interface') 1029c1d255d3SCy Schubert parser.add_argument('--summary', 1030c1d255d3SCy Schubert help='summary file for writing status updates') 1031c1d255d3SCy Schubert parser.add_argument('--success', 1032c1d255d3SCy Schubert help='success file for writing success update') 1033c1d255d3SCy Schubert parser.add_argument('--device', default='usb', help='NFC device to open') 1034c1d255d3SCy Schubert parser.add_argument('--chan', default=None, help='channel list') 1035c1d255d3SCy Schubert parser.add_argument('--altchan', default=None, help='alternative channel list') 1036c1d255d3SCy Schubert parser.add_argument('--netrole', default=None, help='netrole for Enrollee') 1037c1d255d3SCy Schubert parser.add_argument('--test-uri', default=None, 1038c1d255d3SCy Schubert help='test mode: initial URI') 1039c1d255d3SCy Schubert parser.add_argument('--test-alt-uri', default=None, 1040c1d255d3SCy Schubert help='test mode: alternative URI') 1041c1d255d3SCy Schubert parser.add_argument('--test-sel-uri', default=None, 1042c1d255d3SCy Schubert help='test mode: handover select URI') 1043c1d255d3SCy Schubert parser.add_argument('--test-crn', default=None, 1044c1d255d3SCy Schubert help='test mode: hardcoded crn') 1045c1d255d3SCy Schubert parser.add_argument('command', choices=['write-nfc-uri', 1046c1d255d3SCy Schubert 'write-nfc-hs'], 1047c1d255d3SCy Schubert nargs='?') 1048c1d255d3SCy Schubert args = parser.parse_args() 1049c1d255d3SCy Schubert summary(args) 1050c1d255d3SCy Schubert 1051c1d255d3SCy Schubert global handover 1052c1d255d3SCy Schubert handover = ConnectionHandover() 1053c1d255d3SCy Schubert 1054c1d255d3SCy Schubert global only_one 1055c1d255d3SCy Schubert only_one = args.only_one 1056c1d255d3SCy Schubert 1057c1d255d3SCy Schubert global no_wait 1058c1d255d3SCy Schubert no_wait = args.no_wait 1059c1d255d3SCy Schubert 1060c1d255d3SCy Schubert global chanlist, netrole, test_uri, test_alt_uri, test_sel_uri 1061c1d255d3SCy Schubert global test_crn 1062c1d255d3SCy Schubert chanlist = args.chan 1063c1d255d3SCy Schubert handover.altchanlist = args.altchan 1064c1d255d3SCy Schubert netrole = args.netrole 1065c1d255d3SCy Schubert test_uri = args.test_uri 1066c1d255d3SCy Schubert test_alt_uri = args.test_alt_uri 1067c1d255d3SCy Schubert test_sel_uri = args.test_sel_uri 1068c1d255d3SCy Schubert if args.test_crn: 1069c1d255d3SCy Schubert test_crn = struct.pack('>H', int(args.test_crn)) 1070c1d255d3SCy Schubert else: 1071c1d255d3SCy Schubert test_crn = None 1072c1d255d3SCy Schubert 1073c1d255d3SCy Schubert logging.basicConfig(level=args.loglevel) 1074c1d255d3SCy Schubert for l in ['nfc.clf.rcs380', 1075c1d255d3SCy Schubert 'nfc.clf.transport', 1076c1d255d3SCy Schubert 'nfc.clf.device', 1077c1d255d3SCy Schubert 'nfc.clf.__init__', 1078c1d255d3SCy Schubert 'nfc.llcp', 1079c1d255d3SCy Schubert 'nfc.handover']: 1080c1d255d3SCy Schubert log = logging.getLogger(l) 1081c1d255d3SCy Schubert log.setLevel(args.loglevel) 1082c1d255d3SCy Schubert 1083c1d255d3SCy Schubert global init_on_touch 1084c1d255d3SCy Schubert init_on_touch = args.init_on_touch 1085c1d255d3SCy Schubert 1086c1d255d3SCy Schubert global enrollee_only 1087c1d255d3SCy Schubert enrollee_only = args.enrollee 1088c1d255d3SCy Schubert 1089c1d255d3SCy Schubert global configurator_only 1090c1d255d3SCy Schubert configurator_only = args.configurator 1091c1d255d3SCy Schubert 1092c1d255d3SCy Schubert global config_params 1093c1d255d3SCy Schubert config_params = args.config_params 1094c1d255d3SCy Schubert 1095c1d255d3SCy Schubert if args.ifname: 1096c1d255d3SCy Schubert global ifname 1097c1d255d3SCy Schubert ifname = args.ifname 1098c1d255d3SCy Schubert summary("Selected ifname " + ifname) 1099c1d255d3SCy Schubert 1100c1d255d3SCy Schubert if args.ctrl: 1101c1d255d3SCy Schubert global wpas_ctrl 1102c1d255d3SCy Schubert wpas_ctrl = args.ctrl 1103c1d255d3SCy Schubert 1104c1d255d3SCy Schubert if args.summary: 1105c1d255d3SCy Schubert global summary_file 1106c1d255d3SCy Schubert summary_file = args.summary 1107c1d255d3SCy Schubert 1108c1d255d3SCy Schubert if args.success: 1109c1d255d3SCy Schubert global success_file 1110c1d255d3SCy Schubert success_file = args.success 1111c1d255d3SCy Schubert 1112c1d255d3SCy Schubert if args.no_input: 1113c1d255d3SCy Schubert global no_input 1114c1d255d3SCy Schubert no_input = True 1115c1d255d3SCy Schubert 1116c1d255d3SCy Schubert clf = nfc.ContactlessFrontend() 1117c1d255d3SCy Schubert 1118c1d255d3SCy Schubert try: 1119c1d255d3SCy Schubert if not clf.open(args.device): 1120c1d255d3SCy Schubert summary("Could not open connection with an NFC device", color=C_RED) 1121c1d255d3SCy Schubert raise SystemExit(1) 1122c1d255d3SCy Schubert 1123c1d255d3SCy Schubert if args.command == "write-nfc-uri": 1124c1d255d3SCy Schubert write_nfc_uri(clf, wait_remove=not args.no_wait) 1125c1d255d3SCy Schubert if not operation_success: 1126c1d255d3SCy Schubert raise SystemExit(1) 1127c1d255d3SCy Schubert raise SystemExit 1128c1d255d3SCy Schubert 1129c1d255d3SCy Schubert if args.command == "write-nfc-hs": 1130c1d255d3SCy Schubert write_nfc_hs(clf, wait_remove=not args.no_wait) 1131c1d255d3SCy Schubert if not operation_success: 1132c1d255d3SCy Schubert raise SystemExit(1) 1133c1d255d3SCy Schubert raise SystemExit 1134c1d255d3SCy Schubert 1135c1d255d3SCy Schubert global continue_loop 1136c1d255d3SCy Schubert while continue_loop: 1137c1d255d3SCy Schubert global in_raw_mode 1138c1d255d3SCy Schubert was_in_raw_mode = in_raw_mode 1139c1d255d3SCy Schubert clear_raw_mode() 1140c1d255d3SCy Schubert if was_in_raw_mode: 1141c1d255d3SCy Schubert print("\r") 1142c1d255d3SCy Schubert if args.handover_only: 1143c1d255d3SCy Schubert summary("Waiting a peer to be touched", color=C_MAGENTA) 1144c1d255d3SCy Schubert elif args.tag_read_only: 1145c1d255d3SCy Schubert summary("Waiting for a tag to be touched", color=C_BLUE) 1146c1d255d3SCy Schubert else: 1147c1d255d3SCy Schubert summary("Waiting for a tag or peer to be touched", 1148c1d255d3SCy Schubert color=C_GREEN) 1149c1d255d3SCy Schubert handover.wait_connection = True 1150c1d255d3SCy Schubert try: 1151c1d255d3SCy Schubert if args.tag_read_only: 1152c1d255d3SCy Schubert if not clf.connect(rdwr={'on-connect': rdwr_connected}): 1153c1d255d3SCy Schubert break 1154c1d255d3SCy Schubert elif args.handover_only: 1155c1d255d3SCy Schubert if not clf.connect(llcp={'on-startup': llcp_startup, 1156c1d255d3SCy Schubert 'on-connect': llcp_connected, 1157c1d255d3SCy Schubert 'on-release': llcp_release}, 1158c1d255d3SCy Schubert terminate=terminate_loop): 1159c1d255d3SCy Schubert break 1160c1d255d3SCy Schubert else: 1161c1d255d3SCy Schubert if not clf.connect(rdwr={'on-connect': rdwr_connected}, 1162c1d255d3SCy Schubert llcp={'on-startup': llcp_startup, 1163c1d255d3SCy Schubert 'on-connect': llcp_connected, 1164c1d255d3SCy Schubert 'on-release': llcp_release}, 1165c1d255d3SCy Schubert terminate=terminate_loop): 1166c1d255d3SCy Schubert break 1167c1d255d3SCy Schubert except Exception as e: 1168c1d255d3SCy Schubert summary("clf.connect failed: " + str(e)) 1169c1d255d3SCy Schubert break 1170c1d255d3SCy Schubert 1171c1d255d3SCy Schubert if only_one and handover.connected: 1172c1d255d3SCy Schubert role = "selector" if handover.i_m_selector else "requestor" 1173c1d255d3SCy Schubert summary("Connection handover result: I'm the %s" % role, 1174c1d255d3SCy Schubert color=C_YELLOW) 1175c1d255d3SCy Schubert if handover.peer_uri: 1176c1d255d3SCy Schubert summary("Peer URI: " + handover.peer_uri, color=C_YELLOW) 1177c1d255d3SCy Schubert if handover.my_uri: 1178c1d255d3SCy Schubert summary("My URI: " + handover.my_uri, color=C_YELLOW) 1179c1d255d3SCy Schubert if not (handover.peer_uri and handover.my_uri): 1180c1d255d3SCy Schubert summary("Negotiated connection handover failed", 1181c1d255d3SCy Schubert color=C_YELLOW) 1182c1d255d3SCy Schubert break 1183c1d255d3SCy Schubert 1184c1d255d3SCy Schubert except KeyboardInterrupt: 1185c1d255d3SCy Schubert raise SystemExit 1186c1d255d3SCy Schubert finally: 1187c1d255d3SCy Schubert clf.close() 1188c1d255d3SCy Schubert 1189c1d255d3SCy Schubert raise SystemExit 1190c1d255d3SCy Schubert 1191c1d255d3SCy Schubertif __name__ == '__main__': 1192c1d255d3SCy Schubert main() 1193