xref: /freebsd-src/contrib/wpa/wpa_supplicant/examples/dpp-nfc.py (revision a90b9d0159070121c221b966469c3e36d912bf82)
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