/examples/llcp-test-client.py
Python | 893 lines | 819 code | 7 blank | 67 comment | 33 complexity | 90cea7fc00c09a0db25b7f73d82db04c MD5 | raw file
1#!/usr/bin/python 2# -*- coding: latin-1 -*- 3# ----------------------------------------------------------------------------- 4# Copyright 2009-2011 Stephen Tiedemann <stephen.tiedemann@googlemail.com> 5# 6# Licensed under the EUPL, Version 1.1 or - as soon they 7# will be approved by the European Commission - subsequent 8# versions of the EUPL (the "Licence"); 9# You may not use this work except in compliance with the 10# Licence. 11# You may obtain a copy of the Licence at: 12# 13# http://www.osor.eu/eupl 14# 15# Unless required by applicable law or agreed to in 16# writing, software distributed under the Licence is 17# distributed on an "AS IS" basis, 18# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 19# express or implied. 20# See the Licence for the specific language governing 21# permissions and limitations under the Licence. 22# ----------------------------------------------------------------------------- 23# 24# Client side implementation of an LLCP validation suite to verify 25# inter-operability of independent implementations. This suite was 26# primarily developed for the purpose of validating the LLCP 27# specification before final release by the NFC Forum. 28# 29import logging 30log = logging.getLogger() 31 32import os 33import sys 34import time 35from threading import Thread 36from collections import namedtuple 37 38sys.path.insert(1, os.path.split(sys.path[0])[0]) 39import nfc 40import nfc.llcp 41 42default_miu = 128 43 44class TestError(Exception): 45 def __init__(self, value): 46 self.value = value 47 def __str__(self): 48 return str(self.value) 49 50def info(message, prefix=" "): 51 log.info(prefix + message) 52 53def test_01(): 54 """Link activation, symmetry and deactivation 55 56 Verify that the LLCP Link can be activated successfully, that the 57 symmetry procedure is performed and the link can be intentionally 58 deactivated. 59 60 1. Start the MAC link activation procedure on two implementations 61 and verify that the version number parameter is received and 62 version number agreement is achieved. 63 64 2. Verify for a duration of 5 seconds that SYMM PDUs are exchanged 65 within the Link Timout values provided by the implementations. 66 67 3. Perform intentional link deactivation by sending a DISC PDU to 68 the remote Link Management component. Verify that SYMM PDUs 69 are no longer exchanged. 70 """ 71 info("Test 1: link activation, symmetry and deactivation", prefix="") 72 for i in range(5): 73 time.sleep(1) 74 if not nfc.llcp.connected(): 75 raise TestError("connection lost before test completion") 76 77def test_02(): 78 """Connection-less information transfer 79 80 Verify that the source and destination access point address fields 81 are correctly interpreted, the content of the information field is 82 extracted as the service data unit and the service data unit can 83 take any length between zero and the announced Link MIU. The LLCP 84 Link must be activated prior to running this scenario and the Link 85 MIU of the peer implementation must have been determined. In this 86 scenario, sending of a service data unit (SDU) means that the SDU 87 is carried within the information field of a UI PDU. 88 89 1. Send a service data unit of 128 octets length to the 90 connection-less mode echo service and verify that the same SDU 91 is sent back after the echo delay time. 92 93 2. Send within echo delay time with a time interval of at least 94 0.5 second two consecutive service data units of 128 octets 95 length to the connection-less mode echo service and verify that 96 both SDUs are sent back correctly. 97 98 3. Send within echo delay time with a time interval of at least 99 0.5 second three consecutive service data units of 128 octets 100 length to the connection-less mode echo service and verify that 101 the first two SDUs are sent back correctly and the third SDU is 102 discarded. 103 104 4. Send a service data unit of zero octets length to the 105 connection-less mode echo service and verify that the same zero 106 length SDU is sent back after the echo delay time. 107 108 5. Send a service data unit of maximum octets length to the 109 connection-less mode echo service and verify that the same SDU 110 is sent back after the echo delay time. Note that the maximum 111 length here must be the smaller value of both implementations 112 Link MIU. 113 """ 114 TestData = namedtuple("TestData", "send recv") 115 116 def send_and_receive(socket, send_count, packet_length): 117 test_data = TestData(send=[], recv=[]) 118 cl_server = nfc.llcp.getpeername(socket) 119 for i in range(1, send_count + 1): 120 data, addr = packet_length * chr(i), cl_server 121 nfc.llcp.sendto(socket, data, addr) 122 info("sent message {0}".format(i)) 123 test_data.send.append((data, addr, time.time())) 124 time.sleep(0.5) 125 while nfc.llcp.poll(socket, "recv", timeout=5): 126 data, addr = nfc.llcp.recvfrom(socket) 127 test_data.recv.append((data, addr, time.time())) 128 if len(test_data.recv) == 0: 129 raise TestError("did not receive any data within 5 seconds") 130 return test_data 131 132 def run_step_1(socket): 133 info("step 1: send one default size datagram", prefix="") 134 test_data = send_and_receive(socket, 1, default_miu) 135 if not len(test_data.recv) == len(test_data.send): 136 raise TestError("received wrong number of datagrams") 137 for i in range(len(test_data.recv)): 138 send_data, send_addr, send_time = test_data.send[i] 139 recv_data, recv_addr, recv_time = test_data.recv[i] 140 if recv_addr != send_addr: 141 raise TestError("received data from different port") 142 if recv_data != send_data: 143 raise TestError("received data does not match sent data") 144 info("rcvd message {0} after {1} ms" 145 .format(i+1, int((recv_time - send_time) * 1000))) 146 return True 147 148 def run_step_2(socket): 149 info("step 2: send two default size datagrams", prefix="") 150 test_data = send_and_receive(socket, 2, default_miu) 151 if not len(test_data.recv) == len(test_data.send): 152 raise TestError("received wrong number of datagrams") 153 for i in range(len(test_data.recv)): 154 send_data, send_addr, send_time = test_data.send[i] 155 recv_data, recv_addr, recv_time = test_data.recv[i] 156 if recv_addr != send_addr: 157 raise TestError("received data from different port") 158 if recv_data != send_data: 159 raise TestError("received data does not match sent data") 160 info("rcvd message {0} after {1} ms" 161 .format(i+1, int((recv_time - send_time) * 1000))) 162 return True 163 164 def run_step_3(socket): 165 info("step 3: send three default size datagrams", prefix="") 166 test_data = send_and_receive(socket, 3, default_miu) 167 if not len(test_data.recv) == len(test_data.send) - 1: 168 raise TestError("received wrong number of datagrams") 169 for i in range(len(test_data.recv)): 170 send_data, send_addr, send_time = test_data.send[i] 171 recv_data, recv_addr, recv_time = test_data.recv[i] 172 if recv_addr != send_addr: 173 raise TestError("received data from different port") 174 if recv_data != send_data: 175 raise TestError("received data does not match sent data") 176 info("rcvd message {0} after {1} ms" 177 .format(i+1, int((recv_time - send_time) * 1000))) 178 return True 179 180 def run_step_4(socket): 181 info("step 4: send one zero-length datagram", prefix="") 182 test_data = send_and_receive(socket, 1, packet_length=0) 183 if not len(test_data.recv) == len(test_data.send): 184 raise TestError("received wrong number of datagrams") 185 for i in range(len(test_data.recv)): 186 send_data, send_addr, send_time = test_data.send[i] 187 recv_data, recv_addr, recv_time = test_data.recv[i] 188 if recv_addr != send_addr: 189 raise TestError("received data from different port") 190 if recv_data != send_data: 191 raise TestError("received data does not match sent data") 192 info("rcvd message {0} after {1} ms" 193 .format(i+1, int((recv_time - send_time) * 1000))) 194 return True 195 196 def run_step_5(socket): 197 info("step 5: send one maximum length packet", prefix="") 198 miu = nfc.llcp.getsockopt(socket, nfc.llcp.SO_SNDMIU) 199 test_data = send_and_receive(socket, 1, packet_length=miu) 200 if not len(test_data.recv) == len(test_data.send): 201 raise TestError("received wrong number of datagrams") 202 for i in range(len(test_data.recv)): 203 send_data, send_addr, send_time = test_data.send[i] 204 recv_data, recv_addr, recv_time = test_data.recv[i] 205 if recv_addr != send_addr: 206 raise TestError("received data from different port") 207 if recv_data != send_data: 208 raise TestError("received data does not match sent data") 209 info("rcvd message {0} after {1} ms" 210 .format(i+1, int((recv_time - send_time) * 1000))) 211 return True 212 213 info("Test 2: connection-less information transfer", prefix="") 214 socket = nfc.llcp.socket(nfc.llcp.LOGICAL_DATA_LINK) 215 nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 10) 216 if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 10: 217 info("socket recv buffer set to 10") 218 else: raise TestError("could not set the socket recv buffer") 219 cl_echo_server = options.cl_echo_sap 220 if not cl_echo_server: 221 cl_echo_server = nfc.llcp.resolve("urn:nfc:sn:cl-echo") 222 if not cl_echo_server: 223 raise TestError("no connection-less echo server on peer device") 224 info("connection-less echo server on sap {0}".format(cl_echo_server)) 225 nfc.llcp.connect(socket, cl_echo_server) 226 try: 227 if run_step_1(socket): info("PASS", prefix="") 228 if run_step_2(socket): info("PASS", prefix="") 229 if run_step_3(socket): info("PASS", prefix="") 230 if run_step_4(socket): info("PASS", prefix="") 231 if run_step_5(socket): info("PASS", prefix="") 232 finally: 233 nfc.llcp.close(socket) 234 235def test_03(): 236 """Connection-oriented information transfer 237 238 Verify that a data link connection can be established, a service 239 data unit is received and sent back correctly and the data link 240 connection can be terminated. The LLCP Link must be activated 241 prior to running this scenario and the connection-oriented mode 242 echo service must be in the unconnected state. In this scenario, 243 sending of a service data unit (SDU) means that the SDU is carried 244 within the information field of an I PDU. 245 246 1. Send a CONNECT PDU to the connection-oriented mode echo service 247 and verify that the connection request is acknowledged with a 248 CC PDU. The CONNECT PDU shall encode the RW parameter with a 249 value of 2. Verify that the CC PDU encodes the RW parameter 250 with a value of 2 (as specified for the echo server). 251 252 2. Send a single service data unit of 128 octets length over the 253 data link connection and verify that the echo service sends an 254 RR PDU before returning the same SDU after the echo delay time. 255 256 3. Send a DISC PDU to terminate the data link connection and 257 verify that the echo service responds with a correct DM PDU. 258 """ 259 info("Test 3: connection-oriented information transfer", prefix="") 260 socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 261 nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 2) 262 if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 2: 263 info("socket recv window set 2") 264 else: raise TestError("could not set the socket recv window") 265 co_echo_server = options.co_echo_sap 266 if not co_echo_server: 267 co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo") 268 if not co_echo_server: 269 raise TestError("no connection-mode echo server on peer device") 270 info("connection-mode echo server on sap {0}".format(co_echo_server)) 271 nfc.llcp.connect(socket, co_echo_server) 272 peer_sap = nfc.llcp.getpeername(socket) 273 info("connected with sap {0}".format(peer_sap)) 274 nfc.llcp.send(socket, default_miu * "\xFF") 275 t0 = time.time() 276 info("sent one information pdu") 277 if nfc.llcp.poll(socket, "acks", timeout = 5): 278 elapsed = time.time() - t0 279 info("got confirm after {0:.3f}".format(elapsed)) 280 if not elapsed < 1.9: 281 raise TestError("no confirmation within 1.9 seconds") 282 nfc.llcp.recv(socket) 283 elapsed = time.time() - t0 284 info("got message after {0:.3f}".format(time.time() - t0)) 285 if not elapsed > 2.0: 286 raise TestError("echo'd data received too early") 287 else: raise TestError("no data received within 5 seconds") 288 nfc.llcp.close(socket) 289 290def test_04(): 291 """Send and receive sequence number handling 292 293 Verify that a sequence of service data units that causes the send 294 and receive sequence numbers to take all possible values is 295 received and sent back correctly. The LLCP Link must be activated 296 prior to running this scenario and the connection-oriented mode 297 echo service must be in the unconnected state. In this scenario, 298 sending of a service data unit (SDU) means that the SDU is carried 299 within the information field of an I PDU. 300 301 1. Send a CONNECT PDU to the connection-oriented mode echo service 302 and verify that the connection request is acknowledged with a 303 CC PDU. The CONNECT PDU shall encode the RW parameter with a 304 value of 2. Verify that the CC PDU encodes the RW parameter 305 with a value of 2 (as specified for the echo server). 306 307 2. Send a sequence of at least 16 data units of each 128 octets 308 length over the data link connection and verify that all SDUs 309 are sent back correctly. 310 311 3. Send a DISC PDU to terminate the data link connection and 312 verify that the echo service responds with a correct DM PDU. 313 """ 314 sent_data = [] 315 rcvd_data = [] 316 317 def receiver(socket, rcvd_data): 318 while nfc.llcp.poll(socket, "recv", timeout=5): 319 data = nfc.llcp.recv(socket) 320 if data: rcvd_data.append((data, time.time())) 321 322 info("Test 4: send and receive sequence number handling", prefix="") 323 socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 324 nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 2) 325 if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 2: 326 info("receive window set to 2") 327 else: raise TestError("failed to set receive window to 2") 328 co_echo_server = options.co_echo_sap 329 if not co_echo_server: 330 co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo") 331 if not co_echo_server: 332 raise TestError("no connection-mode echo server on peer device") 333 info("connection-mode echo server on sap {0}".format(co_echo_server)) 334 recv_thread = Thread(target=receiver, args=[socket, rcvd_data]) 335 try: 336 nfc.llcp.connect(socket, co_echo_server) 337 peer_sap = nfc.llcp.getpeername(socket) 338 info("connected with sap {0}".format(peer_sap)) 339 recv_thread.start() 340 count = 20 341 info("now sending {0} messages".format(count)) 342 for i in range(count): 343 data = default_miu * chr(i) 344 if nfc.llcp.send(socket, data): 345 info("sent message {0}".format(i+1)) 346 sent_data.append((data, time.time())) 347 recv_thread.join() 348 for i in range(count): 349 r = "correct" if rcvd_data[i][0] == rcvd_data[i][0] else "wrong" 350 t = rcvd_data[i][1] - sent_data[i][1] 351 info("message {i:2} received after {t:.3f} sec was {r}" 352 .format(i=i, t=t, r=r)) 353 finally: 354 try: recv_thread.join() 355 except RuntimeError: pass # wasn't started 356 nfc.llcp.close(socket) 357 358def test_05(): 359 """Handling of receiver busy condition 360 361 Verify the handling of a busy condition. The LLCP Link must be 362 activated prior to running this scenario and the 363 connection-oriented mode echo service must be in the unconnected 364 state. In this scenario, sending of a service data unit (SDU) 365 shall mean that the SDU is carried within the information field of 366 an I PDU. 367 368 1. Send a CONNECT PDU to the connection-oriented mode echo service 369 and verify that the connect request is acknowledged with a CC 370 PDU. The CONNECT PDU shall encode the RW parameter with a value 371 of 0. Verify that the CC PDU encodes the RW parameter with a 372 value of 2 (as specified for the echo server). 373 374 2. Send four service data units of 128 octets length over the data 375 link connection and verify that the echo service enters the 376 busy state when acknowledging the last packet. 377 378 3. Send a DISC PDU to terminate the data link connection and 379 verify that the echo service responds with a correct DM PDU. 380 """ 381 info("Test 5: handling of receiver busy condition", prefix="") 382 socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 383 nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 0) 384 if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 0: 385 info("receive window set to 0") 386 else: raise TestError("failed to set receive window to 0") 387 co_echo_server = options.co_echo_sap 388 if not co_echo_server: 389 co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo") 390 if not co_echo_server: 391 raise TestError("no connection-mode echo server on peer device") 392 info("connection-mode echo server on sap {0}".format(co_echo_server)) 393 try: 394 nfc.llcp.connect(socket, co_echo_server) 395 peer_sap = nfc.llcp.getpeername(socket) 396 info("connected with sap {0}".format(peer_sap)) 397 info("now sending 4 messages") 398 for i in range(4): 399 data = default_miu * chr(i) 400 if nfc.llcp.send(socket, data): 401 info("sent message {0}".format(i+1)) 402 for i in range(4): 403 time.sleep(1.0) 404 if nfc.llcp.getsockopt(socket, nfc.llcp.SO_SNDBSY): 405 info("connection-mode echo server entered busy state") 406 break 407 else: 408 raise TestError("did not recognize server busy state") 409 finally: 410 nfc.llcp.close(socket) 411 412def test_06(): 413 """Rejection of connect request 414 415 Verify that an attempt to establish a second connection with the 416 connection-oriented mode echo service is rejected. The LLCP Link 417 must be activated prior to running this scenario. 418 419 1. Send a first CONNECT PDU to the connection-oriented mode echo 420 service and verify that the connect request is acknowledged 421 with a CC PDU. 422 423 2. Send a second CONNECT PDU to the connection-oriented mode echo 424 service and verify that the connect request is rejected with a 425 DM PDU and appropriate reason code. 426 427 3. Send a service data unit of 128 octets length over the data 428 link connection and verify that the echo service returns the 429 same SDU after the echo delay time. 430 431 4. Send a DISC PDU to terminate the data link connection and 432 verify that the echo service responds with a correct DM PDU. 433 434 """ 435 info("Test 6: rejection of connect request", prefix="") 436 socket1 = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 437 socket2 = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 438 co_echo_server = options.co_echo_sap 439 if not co_echo_server: 440 co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo") 441 if not co_echo_server: 442 raise TestError("no connection-mode echo server on peer device") 443 info("connection-mode echo server on sap {0}".format(co_echo_server)) 444 try: 445 nfc.llcp.connect(socket1, co_echo_server) 446 peer_sap = nfc.llcp.getpeername(socket1) 447 info("first connection established with sap {0}".format(peer_sap)) 448 try: nfc.llcp.connect(socket2, co_echo_server) 449 except nfc.llcp.ConnectRefused as e: 450 info("second connection rejected with reason {0}".format(e.reason)) 451 else: raise TestError("second connection not rejected") 452 finally: nfc.llcp.close(socket2) 453 finally: 454 nfc.llcp.close(socket1) 455 456def test_07(): 457 """Connect by service name 458 459 Verify that a data link connection can be established by 460 specifying a service name. The LLCP Link must be activated prior 461 to running this scenario and the connection-oriented mode echo 462 service must be in the unconnected state. 463 464 1. Send a CONNECT PDU with an SN parameter that encodes the value 465 "urn:nfc:sn:co-echo" to the service discovery service access 466 point address and verify that the connect request is 467 acknowledged with a CC PDU. 468 469 2. Send a service data unit over the data link connection and 470 verify that it is sent back correctly. 471 472 3. Send a DISC PDU to terminate the data link connection and 473 verify that the echo service responds with a correct DM PDU. 474 """ 475 info("Test 7: connect by service name", prefix="") 476 socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 477 try: 478 nfc.llcp.connect(socket, "urn:nfc:sn:co-echo") 479 info("connected to service 'urn:nfc:sn:co-echo'") 480 peer_sap = nfc.llcp.getpeername(socket) 481 if peer_sap == 1: 482 raise TestError("connection established with SDP port") 483 info("connection established with sap {0}".format(peer_sap)) 484 if nfc.llcp.send(socket, "here's nfcpy"): 485 t0 = time.time() 486 info("sent test message") 487 if nfc.llcp.poll(socket, "recv", timeout=5): 488 if nfc.llcp.recv(socket) == "here's nfcpy": 489 info("got echo after {0:.3f} sec".format(time.time()-t0)) 490 else: raise TestError("received wrong data from echo server") 491 else: raise TestError("no echo response within 5 seconds") 492 else: raise TestError("failed to send data") 493 finally: 494 nfc.llcp.close(socket) 495 try: 496 socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 497 nfc.llcp.connect(socket, "urn:nfc:sn:co-echo-test") 498 raise TestError("connect 'co-echo-test' rejected") 499 except nfc.llcp.ConnectRefused as e: 500 info("connect 'co-echo-test' rejected with reason {0}".format(e.reason)) 501 finally: 502 nfc.llcp.close(socket) 503 504def test_08(): 505 """Aggregation and disaggregation 506 507 Verify that the aggregation procedure is performed correctly. The 508 LLCP Link must be activated prior to running this scenario. In 509 this scenario, sending of a service data unit (SDU) shall mean 510 that the SDU is carried within the information field of a UI PDU. 511 512 1. Send two service data units of 50 octets length to the 513 connection-less mode echo service such that the two resulting 514 UI PDUs will be aggregated into a single AGF PDU by the LLC 515 sublayer. Verify that both SDUs are sent back correctly and in 516 the same order. 517 518 2. Send three service data units of 50 octets length to the 519 connection-less mode echo service such that the three resulting 520 UI PDUs will be aggregated into a single AGF PDU by the LLC 521 sublayer. Verify that the two first SDUs are sent back 522 correctly and the third SDU is discarded. 523 """ 524 import nfc.llcp.pdu 525 info("Test 8: aggregation and disaggregation", prefix="") 526 try: 527 socket = nfc.llcp.socket(nfc.llcp.llc.RAW_ACCESS_POINT) 528 nfc.llcp.bind(socket, None) 529 nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 10) 530 if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) != 10: 531 raise TestError("could not set the socket recv buffer") 532 info("socket recv buffer set to 10") 533 cl_echo_server = options.cl_echo_sap 534 if not cl_echo_server: 535 cl_echo_server = nfc.llcp.resolve("urn:nfc:sn:cl-echo") 536 if not cl_echo_server: 537 raise TestError("no connection-less echo server on peer device") 538 info("connection-less echo server on sap {0}".format(cl_echo_server)) 539 addr = nfc.llcp.getsockname(socket) 540 UI = nfc.llcp.pdu.UnnumberedInformation 541 pdu1 = UI(cl_echo_server, addr, 50*"\x01") 542 pdu2 = UI(cl_echo_server, addr, 50*"\x02") 543 pdu3 = UI(cl_echo_server, addr, 50*"\x03") 544 545 info("step 1: send two datagrams with 50 byte payload") 546 agf = nfc.llcp.pdu.AggregatedFrame(aggregate=[pdu1, pdu2]) 547 nfc.llcp.send(socket, agf) 548 if not nfc.llcp.poll(socket, "recv", timeout=5): 549 raise TestError("did not receive first message within 5 sec") 550 if not nfc.llcp.recv(socket).sdu == 50*"\x01": 551 raise TestError("first message came back wrong") 552 info("received first message") 553 if not nfc.llcp.poll(socket, "recv", timeout=5): 554 raise TestError("did not receive second message within 5 sec") 555 if not nfc.llcp.recv(socket).sdu == 50*"\x02": 556 raise TestError("second message came back wrong") 557 info("received second message") 558 559 info("step2: send three datagrams with 50 byte payload") 560 agf = nfc.llcp.pdu.AggregatedFrame(aggregate=[pdu1, pdu2, pdu3]) 561 nfc.llcp.send(socket, agf) 562 if not nfc.llcp.poll(socket, "recv", timeout=5): 563 raise TestError("did not receive first message within 5 sec") 564 if not nfc.llcp.recv(socket).sdu == 50*"\x01": 565 raise TestError("first message came back wrong") 566 info("received first message") 567 if not nfc.llcp.poll(socket, "recv", timeout=5): 568 raise TestError("did not receive second message within 5 sec") 569 if not nfc.llcp.recv(socket).sdu == 50*"\x02": 570 raise TestError("second message came back wrong") 571 info("received second message") 572 if nfc.llcp.poll(socket, "recv", timeout=5): 573 raise TestError("received third message") 574 info("did not receive third message within 5 sec") 575 finally: 576 nfc.llcp.close(socket) 577 578def test_09(): 579 """Service name lookup 580 581 Verify that a service name is correctly resolved into a service 582 access point address by the remote LLC. The LLCP Link must be 583 activated prior to running this scenario. In this scenario, 584 sending of a service data unit (SDU) shall mean that the SDU is 585 carried within the information field of a UI PDU. 586 587 1. Send an SNL PDU with an SDREQ parameter in the information 588 field that encodes the value "urn:nfc:sn:sdp" to the service 589 discovery service access point address and verify that the 590 request is responded with an SNL PDU that contains an SDRES 591 parameter with the SAP value '1' and a TID value that is the 592 same as the value encoded in the antecedently transmitted SDREQ 593 parameter. 594 595 2. Send an SNL PDU with an SDREQ parameter in the information 596 field that encodes the value "urn:nfc:sn:cl-echo" to the 597 service discovery service access point address and verify that 598 the request is responded with an SNL PDU that contains an SDRES 599 parameter with a SAP value other than '0' and a TID value that 600 is the same as the value encoded in the antecedently 601 transmitted SDREQ parameter. 602 603 3. Send a service data unit of 128 octets length to the service 604 access point address received in step 2 and verify that the 605 same SDU is sent back after the echo delay time. 606 607 4. Send an SNL PDU with an SDREQ parameter in the information 608 field that encodes the value "urn:nfc:sn:sdp-test" to the 609 service discovery service access point address and verify that 610 the request is responded with an SNL PDU that contains an SDRES 611 parameter with the SAP value '0' and a TID value that is the 612 same as the value encoded in the antecedently transmitted SDREQ 613 parameter. 614 """ 615 info("Test 9: service name lookup", prefix="") 616 addr = nfc.llcp.resolve("urn:nfc:sn:sdp") 617 if not addr: 618 raise TestError("no answer for 'urn:nfc:sn:sdp' lookup") 619 info("step 1: resolved 'urn:nfc:sn:sdp' to sap {0}".format(addr)) 620 addr = nfc.llcp.resolve("urn:nfc:sn:cl-echo") 621 if not addr: 622 raise TestError("no answer for 'urn:nfc:sn:cl-echo' lookup") 623 info("step 2: resolved 'urn:nfc:sn:cl-echo' to sap {0}".format(addr)) 624 socket = nfc.llcp.socket(nfc.llcp.LOGICAL_DATA_LINK) 625 t0 = time.time() 626 if nfc.llcp.sendto(socket, 128 * "\xA9", addr): 627 info("step 3: sent 128 byte message to sap {0}".format(addr)) 628 if not nfc.llcp.poll(socket, "recv", timeout=5): 629 raise TestError("did not receive echo within 5 seconds") 630 data, peer = nfc.llcp.recvfrom(socket) 631 if not data == 128 * "\xA9": 632 raise TestError("received wrong data in step 3") 633 if not peer == addr: 634 raise TestError("received from wrong sap in step 3") 635 t1 = time.time() 636 info("step 3: received echo after {0:.3} seconds".format(t1-t0)) 637 addr = nfc.llcp.resolve("urn:nfc:sn:sdp-test") 638 if not addr == 0: 639 raise TestError("'urn:nfc:sn:sdp-test' did not yield 0") 640 info("step 4: resolved 'urn:nfc:sn:sdp-test' as {0}".format(addr)) 641 642def test_10(): 643 import nfc.llcp.pdu 644 info("Test 10: exceed the maximum information unit size", prefix="") 645 co_echo_server = options.co_echo_sap 646 if not co_echo_server: 647 co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo") 648 if not co_echo_server: 649 raise TestError("no connection-mode echo server on peer device") 650 info("connection-mode echo server on sap {0}".format(co_echo_server)) 651 dlc_socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 652 raw_socket = nfc.llcp.socket(nfc.llcp.llc.RAW_ACCESS_POINT) 653 try: 654 nfc.llcp.connect(dlc_socket, co_echo_server) 655 addr = nfc.llcp.getsockname(dlc_socket) 656 peer = nfc.llcp.getpeername(dlc_socket) 657 info("connected with sap {0}".format(peer)) 658 send_miu = nfc.llcp.getsockopt(dlc_socket, nfc.llcp.SO_SNDMIU) 659 info("the peers receive MIU is {0} octets".format(send_miu)) 660 sdu = (send_miu + 1) * '\x00' 661 pdu = nfc.llcp.pdu.Information(dsap=peer, ssap=addr, sdu=sdu) 662 pdu.ns, pdu.nr = 0, 0 663 nfc.llcp.send(raw_socket, pdu) 664 nfc.llcp.recv(dlc_socket) 665 except nfc.llcp.Error as e: 666 info(str(e)) 667 finally: 668 nfc.llcp.close(dlc_socket) 669 nfc.llcp.close(raw_socket) 670 671def test_11(): 672 import nfc.llcp.pdu 673 info("Test 11: generate invalid send sequence number", prefix="") 674 co_echo_server = options.co_echo_sap 675 if not co_echo_server: 676 co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo") 677 if not co_echo_server: 678 raise TestError("no connection-mode echo server on peer device") 679 info("connection-mode echo server on sap {0}".format(co_echo_server)) 680 dlc_socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 681 raw_socket = nfc.llcp.socket(nfc.llcp.llc.RAW_ACCESS_POINT) 682 try: 683 nfc.llcp.connect(dlc_socket, co_echo_server) 684 addr = nfc.llcp.getsockname(dlc_socket) 685 peer = nfc.llcp.getpeername(dlc_socket) 686 info("connected with sap {0}".format(peer)) 687 pdu = nfc.llcp.pdu.Information(dsap=peer, ssap=addr, sdu="wrong N(S)") 688 pdu.ns, pdu.nr = 15, 0 689 nfc.llcp.send(raw_socket, pdu) 690 nfc.llcp.recv(dlc_socket) 691 except nfc.llcp.Error as e: 692 info(str(e)) 693 finally: 694 nfc.llcp.close(dlc_socket) 695 nfc.llcp.close(raw_socket) 696 697def test_12(): 698 info("Test 12: maximum data size on data link connection", prefix="") 699 socket = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 700 nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVBUF, 2) 701 if nfc.llcp.getsockopt(socket, nfc.llcp.SO_RCVBUF) == 2: 702 info("socket recv window set 2") 703 else: raise TestError("could not set the socket recv window") 704 nfc.llcp.setsockopt(socket, nfc.llcp.SO_RCVMIU, 300) 705 co_echo_server = options.co_echo_sap 706 if not co_echo_server: 707 co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo") 708 if not co_echo_server: 709 raise TestError("no connection-mode echo server on peer device") 710 info("connection-mode echo server on sap {0}".format(co_echo_server)) 711 nfc.llcp.connect(socket, co_echo_server) 712 peer_sap = nfc.llcp.getpeername(socket) 713 info("connected with sap {0}".format(peer_sap)) 714 miu = nfc.llcp.getsockopt(socket, nfc.llcp.SO_SNDMIU) 715 nfc.llcp.send(socket, miu * "\xFF") 716 t0 = time.time() 717 info("sent one information pdu") 718 if nfc.llcp.poll(socket, "acks", timeout = 5): 719 elapsed = time.time() - t0 720 info("got confirm after {0:.3f}".format(elapsed)) 721 if not elapsed < 1.9: 722 raise TestError("no confirmation within 1.9 seconds") 723 if not nfc.llcp.poll(socket, "recv", timeout=5): 724 raise TestError("did not receive second message within 5 sec") 725 data = nfc.llcp.recv(socket) 726 info("got message after {0:.3f}".format(time.time() - t0)) 727 else: raise TestError("no data received within 5 seconds") 728 nfc.llcp.close(socket) 729 730def test_13(): 731 info("Test 13: DLC connect - release - connect", prefix="") 732 socket1 = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 733 socket2 = nfc.llcp.socket(nfc.llcp.DATA_LINK_CONNECTION) 734 co_echo_server = options.co_echo_sap 735 if not co_echo_server: 736 co_echo_server = nfc.llcp.resolve("urn:nfc:sn:co-echo") 737 if not co_echo_server: 738 raise TestError("no connection-mode echo server on peer device") 739 info("connection-mode echo server on sap {0}".format(co_echo_server)) 740 try: 741 nfc.llcp.connect(socket1, co_echo_server) 742 peer_sap = nfc.llcp.getpeername(socket1) 743 info("first connection established with sap {0}".format(peer_sap)) 744 nfc.llcp.send(socket1, "I'm the first connection") 745 assert(nfc.llcp.recv(socket1) == "I'm the first connection") 746 nfc.llcp.close(socket1) 747 info("first connection terminated") 748 nfc.llcp.connect(socket2, co_echo_server) 749 peer_sap = nfc.llcp.getpeername(socket2) 750 info("second connection established with sap {0}".format(peer_sap)) 751 nfc.llcp.send(socket2, "I'm the second connection") 752 assert(nfc.llcp.recv(socket2) == "I'm the second connection") 753 nfc.llcp.close(socket2) 754 finally: 755 pass 756 757 758def main(): 759 llcp_config = {'recv-miu': options.link_miu, 'send-lto': 500} 760 if options.quirks == "android": 761 llcp_config['send-agf'] = False 762 763 general_bytes = nfc.llcp.startup(llcp_config) 764 for device in options.device: 765 try: clf = nfc.ContactlessFrontend(device); break 766 except LookupError: pass 767 else: return 768 769 peer = None 770 try: 771 while True: 772 if options.mode == "target" or options.mode is None: 773 listen_time = 250 + ord(os.urandom(1)) 774 peer = clf.listen(listen_time, general_bytes) 775 if isinstance(peer, nfc.DEP): 776 if peer.general_bytes.startswith("Ffm"): 777 break 778 if options.mode == "initiator" or options.mode is None: 779 peer = clf.poll(general_bytes) 780 if isinstance(peer, nfc.DEP): 781 if peer.general_bytes.startswith("Ffm"): 782 if options.quirks == "android": 783 # Google Nexus S does not receive the first 784 # packet if we send immediately. 785 time.sleep(0.1) 786 break 787 except KeyboardInterrupt: 788 log.info("aborted by user") 789 clf.close() 790 return 791 792 nfc.llcp.activate(peer) 793 time.sleep(0.5) 794 795 if not options.run_test: 796 log.info("no test specified") 797 798 test_suite = [test_01, test_02, test_03, test_04, test_05, 799 test_06, test_07, test_08, test_09, test_10, 800 test_11, test_12, test_13] 801 try: 802 for test in options.run_test: 803 if test > 0 and test <= len(test_suite): 804 try: 805 test_suite[test-1]() 806 log.info("PASS") 807 except TestError as error: 808 log.error("FAIL: {0}".format(error)) 809 else: log.info("invalid test number '{0}'".format(test)) 810 except KeyboardInterrupt: 811 log.info("aborted by user") 812 for thread in threading.enumerate(): 813 log.info(thread.name) 814 finally: 815 nfc.llcp.shutdown() 816 clf.close() 817 log.info("I was the " + peer.role) 818 819 820if __name__ == '__main__': 821 from optparse import OptionParser, OptionGroup 822 parser = OptionParser() 823 parser.add_option("-t", "--test", type="int", default=[], 824 action="append", dest="run_test", metavar="N", 825 help="run test number <N>") 826 parser.add_option("-q", default=True, 827 action="store_false", dest="verbose", 828 help="be quiet, only print errors") 829 parser.add_option("-d", type="string", default=[], 830 action="append", dest="debug", metavar="MODULE", 831 help="print debug messages for MODULE") 832 parser.add_option("-f", type="string", 833 action="store", dest="logfile", 834 help="write log messages to LOGFILE") 835 parser.add_option("--device", type="string", default=[], 836 action="append", dest="device", metavar="SPEC", 837 help="use only device(s) according to SPEC: "\ 838 "usb[:vendor[:product]] (vendor and product in hex) "\ 839 "usb[:bus[:dev]] (bus and device number in decimal) "\ 840 "tty[:(usb|com)[:port]] (usb virtual or com port)") 841 parser.add_option("--mode", type="choice", default=None, 842 choices=["target", "initiator"], 843 action="store", dest="mode", 844 help="restrict mode to 'target' or 'initiator'") 845 parser.add_option("--cl-echo", type="int", default=None, 846 action="store", dest="cl_echo_sap", metavar="SAP", 847 help="connection-less echo server address") 848 parser.add_option("--co-echo", type="int", default=None, 849 action="store", dest="co_echo_sap", metavar="SAP", 850 help="connection-oriented echo server address") 851 parser.add_option("--link-miu", type="int", default=2175, 852 action="store", dest="link_miu", metavar="MIU", 853 help="set maximum information unit size to MIU") 854 parser.add_option("--quirks", type="string", 855 action="store", dest="quirks", metavar="choice", 856 help="quirks mode, choices are 'android'") 857 858 global options 859 options, args = parser.parse_args() 860 861 verbosity = logging.INFO if options.verbose else logging.ERROR 862 logging.basicConfig(level=verbosity, format='%(message)s') 863 864 if options.logfile: 865 logfile_format = '%(asctime)s %(levelname)-5s [%(name)s] %(message)s' 866 logfile = logging.FileHandler(options.logfile, "w") 867 logfile.setFormatter(logging.Formatter(logfile_format)) 868 logfile.setLevel(logging.DEBUG) 869 logging.getLogger('').addHandler(logfile) 870 871 import inspect, os, os.path 872 nfcpy_path = os.path.dirname(inspect.getfile(nfc)) 873 for name in os.listdir(nfcpy_path): 874 if os.path.isdir(os.path.join(nfcpy_path, name)): 875 logging.getLogger("nfc."+name).setLevel(verbosity) 876 elif name.endswith(".py") and name != "__init__.py": 877 logging.getLogger("nfc."+name[:-3]).setLevel(verbosity) 878 879 if options.debug: 880 logging.getLogger('').setLevel(logging.DEBUG) 881 logging.getLogger('nfc').setLevel(logging.DEBUG) 882 for module in options.debug: 883 log.info("enable debug output for module '{0}'".format(module)) 884 logging.getLogger(module).setLevel(logging.DEBUG) 885 886 try: options.run_test = [int(t) for t in options.run_test] 887 except ValueError: log.error("non-integer test number"); sys.exit(-1) 888 889 if len(options.device) == 0: 890 # search and use first 891 options.device = ["",] 892 893 main()