PageRenderTime 57ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/nfc/llcp/llc.py

https://bitbucket.org/jialvarez/nfcpy
Python | 597 lines | 493 code | 64 blank | 40 comment | 198 complexity | 9a307df232cd4f39510c0e6439d507a1 MD5 | raw file
  1. # -*- coding: latin-1 -*-
  2. # -----------------------------------------------------------------------------
  3. # Copyright 2009-2011 Stephen Tiedemann <stephen.tiedemann@googlemail.com>
  4. #
  5. # Licensed under the EUPL, Version 1.1 or - as soon they
  6. # will be approved by the European Commission - subsequent
  7. # versions of the EUPL (the "Licence");
  8. # You may not use this work except in compliance with the
  9. # Licence.
  10. # You may obtain a copy of the Licence at:
  11. #
  12. # http://www.osor.eu/eupl
  13. #
  14. # Unless required by applicable law or agreed to in
  15. # writing, software distributed under the Licence is
  16. # distributed on an "AS IS" basis,
  17. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  18. # express or implied.
  19. # See the Licence for the specific language governing
  20. # permissions and limitations under the Licence.
  21. # -----------------------------------------------------------------------------
  22. import logging
  23. log = logging.getLogger(__name__)
  24. import time
  25. from types import *
  26. import threading
  27. import collections
  28. import random
  29. # local imports
  30. from tco import *
  31. from pdu import *
  32. from err import *
  33. from opt import *
  34. RAW_ACCESS_POINT, LOGICAL_DATA_LINK, DATA_LINK_CONNECTION = range(3)
  35. wks_map = {
  36. "urn:nfc:sn:sdp" : 1,
  37. "urn:nfc:sn:ip" : 2,
  38. "urn:nfc:sn:obex": 3,
  39. "urn:nfc:sn:snep": 4}
  40. class ServiceAccessPoint(object):
  41. def __init__(self, addr, llc):
  42. self.llc = llc
  43. self.addr = addr
  44. self.sock_list = collections.deque()
  45. self.send_list = collections.deque()
  46. def __str__(self):
  47. return "SAP {0:>2}".format(self.addr)
  48. @property
  49. def mode(self):
  50. with self.llc.lock:
  51. try:
  52. if isinstance(self.sock_list[0], RawAccessPoint):
  53. return RAW_ACCESS_POINT
  54. if isinstance(self.sock_list[0], LogicalDataLink):
  55. return LOGICAL_DATA_LINK
  56. if isinstance(self.sock_list[0], DataLinkConnection):
  57. return DATA_LINK_CONNECTION
  58. except IndexError: return 0
  59. def insert_socket(self, socket):
  60. with self.llc.lock:
  61. try: insertable = type(socket) == type(self.sock_list[0])
  62. except IndexError: insertable = True
  63. if insertable:
  64. socket.bind(self.addr)
  65. self.sock_list.appendleft(socket)
  66. else: log.error("can't insert socket of differing type")
  67. return insertable
  68. def remove_socket(self, socket):
  69. assert socket.addr == self.addr
  70. socket.close()
  71. with self.llc.lock:
  72. try: self.sock_list.remove(socket)
  73. except ValueError: pass
  74. if len(self.sock_list) == 0:
  75. # completely remove this sap
  76. self.llc.sap[self.addr] = None
  77. def send(self, pdu):
  78. self.send_list.append(pdu)
  79. def shutdown(self):
  80. while True:
  81. try: socket = self.sock_list.pop()
  82. except IndexError: return
  83. log.debug("shutdown socket %s" % str(socket))
  84. socket.bind(None); socket.close()
  85. #
  86. # enqueue() and dequeue() are called from llc run thread
  87. #
  88. def enqueue(self, pdu):
  89. with self.llc.lock:
  90. if isinstance(pdu, Connect):
  91. for socket in self.sock_list:
  92. if socket.state.LISTEN:
  93. socket.enqueue(pdu)
  94. return
  95. else:
  96. for socket in self.sock_list:
  97. if pdu.ssap == socket.peer or socket.peer is None:
  98. socket.enqueue(pdu)
  99. return
  100. if pdu.type in connection_mode_pdu_types:
  101. self.send(DisconnectedMode(pdu.ssap, pdu.dsap, reason=1))
  102. def dequeue(self, max_size):
  103. with self.llc.lock:
  104. for socket in self.sock_list:
  105. #print "dequeue from", socket
  106. pdu = socket.dequeue(max_size)
  107. if pdu: return pdu
  108. else:
  109. try: return self.send_list.popleft()
  110. except IndexError: pass
  111. def sendack(self, max_size):
  112. with self.llc.lock:
  113. for socket in self.sock_list:
  114. pdu = socket.sendack(max_size)
  115. if pdu: return pdu
  116. class ServiceDiscovery(object):
  117. def __init__(self, llc):
  118. self.llc = llc
  119. self.snl = dict()
  120. self.tids = range(256)
  121. self.resp = threading.Condition(self.llc.lock)
  122. self.sent = dict()
  123. self.sdreq = collections.deque()
  124. self.sdres = collections.deque()
  125. self.dmpdu = collections.deque()
  126. def __str__(self):
  127. return "SAP 1"
  128. @property
  129. def mode(self):
  130. return LOGICAL_DATA_LINK
  131. def resolve(self, name):
  132. with self.resp:
  133. if self.snl is None: return None
  134. log.debug("resolve service name '{0}'".format(name))
  135. try: return self.snl[name]
  136. except KeyError: pass
  137. tid = random.choice(self.tids)
  138. self.tids.remove(tid)
  139. self.sdreq.append((tid, name))
  140. while not self.snl is None and not name in self.snl:
  141. self.resp.wait()
  142. return None if self.snl is None else self.snl[name]
  143. #
  144. # enqueue() and dequeue() are called from llc run thread
  145. #
  146. def enqueue(self, pdu):
  147. with self.llc.lock:
  148. if isinstance(pdu, ServiceNameLookup) and not self.snl is None:
  149. for tid, sap in pdu.sdres:
  150. try: name = self.sent[tid]
  151. except KeyError: pass
  152. else:
  153. log.debug("resolved '{0}' to remote addr {1}"
  154. .format(name, sap))
  155. self.snl[name] = sap
  156. self.tids.append(tid)
  157. self.resp.notify_all()
  158. for tid, name in pdu.sdreq:
  159. try: sap = self.llc.snl[name]
  160. except KeyError: sap = 0
  161. self.sdres.append((tid, sap))
  162. def dequeue(self, max_size):
  163. if max_size < 2:
  164. return None
  165. with self.llc.lock:
  166. if len(self.sdres) > 0 or len(self.sdreq) > 0:
  167. pdu = ServiceNameLookup(dsap=1, ssap=1)
  168. max_size -= len(pdu)
  169. while max_size > 0:
  170. try: pdu.sdres.append(self.sdres.popleft())
  171. except IndexError: break
  172. for i in range(len(self.sdreq)):
  173. tid, name = self.sdreq[0]
  174. if 1 + len(name) > max_size:
  175. self.sdreq.rotate(-1)
  176. else:
  177. pdu.sdreq.append(self.sdreq.popleft())
  178. self.sent[tid] = name
  179. return pdu
  180. if len(self.dmpdu) > 0 and max_size >= 2:
  181. return self.dmpdu.popleft()
  182. def shutdown(self):
  183. with self.llc.lock:
  184. self.snl = None
  185. self.resp.notify_all()
  186. class LogicalLinkControl(threading.Thread):
  187. def __init__(self, config):
  188. super(LogicalLinkControl, self).__init__()
  189. self.lock = threading.RLock()
  190. self.cfg = dict()
  191. self.cfg['recv-miu'] = config.get('recv-miu', 248)
  192. self.cfg['send-lto'] = config.get('send-lto', 500)
  193. self.cfg['send-agf'] = config.get('send-agf', True)
  194. self.snl = dict({"urn:nfc:sn:sdp" : 1})
  195. self.sap = 64 * [None]
  196. self.sap[0] = ServiceAccessPoint(0, self)
  197. self.sap[1] = ServiceDiscovery(self)
  198. @property
  199. def parameter_string(self):
  200. miu = self.cfg['recv-miu']
  201. lto = self.cfg['send-lto']
  202. wks = 1+sum(sorted([1<<sap for sap in self.snl.values() if sap < 15]))
  203. pax = ParameterExchange(version=(1,1), miu=miu, lto=lto, wks=wks)
  204. return "Ffm" + pax.to_string().lstrip("\x00\x40")
  205. def activate(self, mac):
  206. info = ["LLCP Link established, I'm the DEP {0}".format(mac.role)]
  207. pax = "\x00\x40" + self.parameter_string.lstrip("Ffm")
  208. pax = ProtocolDataUnit.from_string(pax)
  209. info.append("Local LLCP Settings")
  210. info.append(" LLCP Version: {0[0]}.{0[1]}".format(pax.version))
  211. info.append(" Link Timeout: {0} ms".format(pax.lto))
  212. info.append(" Max Inf Unit: {0} octet".format(pax.miu))
  213. info.append(" Service List: {0:016b}".format(pax.wks))
  214. pax = "\x00\x40" + mac.general_bytes.lstrip("Ffm")
  215. pax = ProtocolDataUnit.from_string(pax)
  216. info.append("Remote LLCP Settings")
  217. info.append(" LLCP Version: {0[0]}.{0[1]}".format(pax.version))
  218. info.append(" Link Timeout: {0} ms".format(pax.lto))
  219. info.append(" Max Inf Unit: {0} octet".format(pax.miu))
  220. info.append(" Service List: {0:016b}".format(pax.wks))
  221. self.mac = mac
  222. self.cfg['rcvd-ver'] = pax.version
  223. self.cfg['send-miu'] = pax.miu
  224. self.cfg['recv-lto'] = pax.lto
  225. self.cfg['send-wks'] = pax.wks
  226. self.cfg['send-lsc'] = pax.lsc
  227. log.debug("llc cfg {0}".format(self.cfg))
  228. log.info('\n'.join(info))
  229. def shutdown(self):
  230. log.debug("shutdown requested")
  231. if self.sap[0]:
  232. self.sap[0].send(Disconnect(dsap=0, ssap=0))
  233. def run(self):
  234. def shutdown_clients(sap):
  235. for i in range(63, -1, -1):
  236. if not sap[i] is None:
  237. log.debug("closing service access point %d" % i)
  238. sap[i].shutdown()
  239. sap[i] = None
  240. link_terminate_pdu = Disconnect(dsap=0, ssap=0)
  241. link_terminate_str = link_terminate_pdu.to_string()
  242. link_symmetry_pdu = Symmetry()
  243. recv_timeout = self.cfg['recv-lto'] + 50
  244. send_timeout = self.cfg['send-lto'] / 2
  245. recv_symm_count = 0
  246. recv_symm_level = 10
  247. if self.mac.role == "Initiator":
  248. pdu = self._collect()
  249. while True:
  250. if pdu is None:
  251. pdu = Symmetry()
  252. if pdu == link_terminate_pdu:
  253. log.info("shutdown on local request")
  254. log.debug("SEND " + str(pdu))
  255. try: self.mac.exchange(pdu.to_string(), timeout=1)
  256. except IOError: pass
  257. shutdown_clients(self.sap)
  258. break
  259. log.debug("SEND " + str(pdu))
  260. try: data = self.mac.exchange(pdu.to_string(), recv_timeout)
  261. except IOError as error:
  262. log.debug("in exchange => IOError {0}".format(error))
  263. data = None
  264. if data is None or data == link_terminate_str:
  265. if data: log.info("shutdown on remote request")
  266. else: log.info("shutdown on link disruption")
  267. shutdown_clients(self.sap)
  268. break
  269. pdu = ProtocolDataUnit.from_string(data)
  270. log.debug("RECV " + str(pdu))
  271. if pdu == link_symmetry_pdu:
  272. recv_symm_count += 1
  273. else:
  274. recv_symm_count = 0
  275. self._dispatch(pdu)
  276. pdu = self._collect()
  277. if pdu is None and recv_symm_count >= recv_symm_level:
  278. time.sleep(0.001 * send_timeout)
  279. pdu = self._collect()
  280. if self.mac.role == "Target":
  281. while True:
  282. try: data = self.mac.wait_command(recv_timeout)
  283. except IOError as error:
  284. log.debug("wait_command: IOError {0}".format(str(error)))
  285. data = None
  286. if data:
  287. pdu = ProtocolDataUnit.from_string(data)
  288. log.debug("RECV " + str(pdu))
  289. if data is None or data == link_terminate_str:
  290. if data: log.info("shutdown on remote request")
  291. else: log.info("shutdown on link disruption")
  292. shutdown_clients(self.sap)
  293. break
  294. #FIXME: must be coordinated with DEP RWT
  295. #if pdu == link_symmetry_pdu:
  296. # recv_symm_count += 1
  297. #else:
  298. # recv_symm_count = 0
  299. self._dispatch(pdu)
  300. pdu = self._collect()
  301. if pdu is None and recv_symm_count >= recv_symm_level:
  302. time.sleep(0.001 * send_timeout)
  303. pdu = self._collect()
  304. if pdu is None:
  305. pdu = Symmetry()
  306. log.debug("SEND " + str(pdu))
  307. try: self.mac.send_response(pdu.to_string(), recv_timeout)
  308. except IOError as err:
  309. if not pdu == link_terminate_pdu:
  310. log.debug("send_response: IOError {0}".format(err))
  311. log.info("shutdown on link disruption")
  312. shutdown_clients(self.sap)
  313. break
  314. log.debug("llc run thread terminated")
  315. def _collect(self):
  316. pdu_list = list()
  317. max_data = None
  318. with self.lock:
  319. active_sap_list = [sap for sap in self.sap if sap is not None]
  320. for sap in active_sap_list:
  321. #log.debug("query sap {0}, max_data={1}"
  322. # .format(sap, max_data))
  323. pdu = sap.dequeue(max_data if max_data else 2179)
  324. if not pdu is None:
  325. if self.cfg['send-agf'] == False:
  326. return pdu
  327. pdu_list.append(pdu)
  328. if max_data is None:
  329. max_data = self.cfg["send-miu"] + 2
  330. max_data -= len(pdu)
  331. if max_data < bool(len(pdu_list)==1) * 2 + 2 + 2:
  332. break
  333. else: max_data = self.cfg["send-miu"] + 2
  334. for sap in active_sap_list:
  335. if sap.mode == DATA_LINK_CONNECTION:
  336. pdu = sap.sendack(max_data)
  337. if not pdu is None:
  338. if self.cfg['send-agf'] == False:
  339. return pdu
  340. pdu_list.append(pdu)
  341. max_data -= len(pdu)
  342. if max_data < bool(len(pdu_list)==1) * 2 + 2 + 3:
  343. break
  344. if len(pdu_list) > 1:
  345. return AggregatedFrame(aggregate=pdu_list)
  346. if len(pdu_list) == 1:
  347. return pdu_list[0]
  348. return None
  349. def _dispatch(self, pdu):
  350. if isinstance(pdu, Symmetry):
  351. return
  352. if isinstance(pdu, AggregatedFrame):
  353. if pdu.dsap == 0 and pdu.ssap == 0:
  354. [log.debug(" " + str(p)) for p in pdu]
  355. [self._dispatch(p) for p in pdu]
  356. return
  357. if isinstance(pdu, Connect) and pdu.dsap == 1:
  358. # connect-by-name
  359. addr = self.snl.get(pdu.sn)
  360. if not addr or self.sap[addr] is None:
  361. log.debug("no service named '{0}'".format(pdu.sn))
  362. pdu = DisconnectedMode(pdu.ssap, 1, reason=2)
  363. self.sap[1].dmpdu.append(pdu)
  364. return
  365. pdu = Connect(dsap=addr, ssap=pdu.ssap, rw=pdu.rw, miu=pdu.miu)
  366. with self.lock:
  367. sap = self.sap[pdu.dsap]
  368. if sap:
  369. sap.enqueue(pdu)
  370. return
  371. log.debug("discard PDU {0}".format(str(pdu)))
  372. return
  373. def resolve(self, name):
  374. return self.sap[1].resolve(name)
  375. def socket(self, socket_type):
  376. if socket_type == RAW_ACCESS_POINT:
  377. return RawAccessPoint(self.cfg["send-miu"], self.cfg["recv-miu"])
  378. if socket_type == LOGICAL_DATA_LINK:
  379. return LogicalDataLink(self.cfg["send-miu"], self.cfg["recv-miu"])
  380. if socket_type == DATA_LINK_CONNECTION:
  381. return DataLinkConnection()
  382. def setsockopt(self, socket, option, value):
  383. if not isinstance(socket, TransmissionControlObject):
  384. raise Error(errno.ENOTSOCK)
  385. return socket.setsockopt(option, value)
  386. def getsockopt(self, socket, option):
  387. if not isinstance(socket, TransmissionControlObject):
  388. raise Error(errno.ENOTSOCK)
  389. if isinstance(socket, LogicalDataLink):
  390. if option == SO_SNDMIU:
  391. return self.cfg["send-miu"]
  392. if option == SO_RCVMIU:
  393. return self.cfg["recv-miu"]
  394. return socket.getsockopt(option)
  395. def bind(self, socket, addr_or_name=None):
  396. """Bind a socket to an address or service name.
  397. """
  398. if not isinstance(socket, TransmissionControlObject):
  399. raise Error(errno.ENOTSOCK)
  400. if not socket.addr is None:
  401. raise Error(errno.EINVAL)
  402. if addr_or_name is None:
  403. self._bind_by_none(socket)
  404. elif type(addr_or_name) is IntType:
  405. self._bind_by_addr(socket, addr_or_name)
  406. elif type(addr_or_name) is StringType:
  407. self._bind_by_name(socket, addr_or_name)
  408. else: raise Error(errno.EFAULT)
  409. def _bind_by_none(self, socket):
  410. with self.lock:
  411. try: addr = 32 + self.sap[32:64].index(None)
  412. except ValueError: raise Error(errno.EAGAIN)
  413. else:
  414. socket.bind(addr)
  415. self.sap[addr] = ServiceAccessPoint(addr, self)
  416. self.sap[addr].insert_socket(socket)
  417. def _bind_by_addr(self, socket, addr):
  418. with self.lock:
  419. if addr in range(32, 64):
  420. if self.sap[addr] is None:
  421. socket.bind(addr)
  422. self.sap[addr] = ServiceAccessPoint(addr, self)
  423. self.sap[addr].insert_socket(socket)
  424. else: raise Error(errno.EADDRINUSE)
  425. else: raise Error(errno.EACCES)
  426. def _bind_by_name(self, socket, name):
  427. if not (name.startswith("urn:nfc:sn") or
  428. name.startswith("urn:nfc:xsn") or
  429. name == "com.android.npp"): # invalid name but legacy
  430. raise Error(errno.EFAULT)
  431. with self.lock:
  432. if self.snl.get(name) != None:
  433. raise Error(errno.EADDRINUSE)
  434. addr = wks_map.get(name)
  435. if addr is None:
  436. try: addr = 16 + self.sap[16:32].index(None)
  437. except ValueError: raise Error(errno.EADDRNOTAVAIL)
  438. socket.bind(addr)
  439. self.sap[addr] = ServiceAccessPoint(addr, self)
  440. self.sap[addr].insert_socket(socket)
  441. self.snl[name] = addr
  442. def connect(self, socket, dest):
  443. if not isinstance(socket, TransmissionControlObject):
  444. raise Error(errno.ENOTSOCK)
  445. if not socket.is_bound:
  446. self.bind(socket)
  447. socket.connect(dest)
  448. log.debug("connected ({dlc.addr} ===> {dlc.peer})"
  449. .format(dlc=socket))
  450. def listen(self, socket, backlog):
  451. if not isinstance(socket, TransmissionControlObject):
  452. raise Error(errno.ENOTSOCK)
  453. if not isinstance(socket, DataLinkConnection):
  454. raise Error(errno.EOPNOTSUPP)
  455. if not type(backlog) == IntType:
  456. raise TypeError("backlog must be integer")
  457. if backlog < 0:
  458. raise ValueError("backlog mmust not be negative")
  459. backlog = min(backlog, 16)
  460. if not socket.is_bound:
  461. self.bind(socket)
  462. socket.listen(backlog)
  463. def accept(self, socket):
  464. if not isinstance(socket, TransmissionControlObject):
  465. raise Error(errno.ENOTSOCK)
  466. if not isinstance(socket, DataLinkConnection):
  467. raise Error(errno.EOPNOTSUPP)
  468. while True:
  469. client = socket.accept()
  470. if not client.is_bound:
  471. self.bind(client)
  472. if self.sap[client.addr].insert_socket(client):
  473. log.debug("new data link connection ({0} <=== {1})"
  474. .format(client.addr, client.peer))
  475. return client
  476. else:
  477. pdu = DisconnectedMode(client.peer, socket.addr, reason=0x20)
  478. super(DataLinkConnection, socket).send(pdu)
  479. def send(self, socket, message):
  480. return self.sendto(socket, message, socket.peer)
  481. def sendto(self, socket, message, dest):
  482. if not isinstance(socket, TransmissionControlObject):
  483. raise Error(errno.ENOTSOCK)
  484. if isinstance(socket, RawAccessPoint):
  485. if not isinstance(message, ProtocolDataUnit):
  486. raise TypeError("message must be a pdu on raw access point")
  487. if not socket.is_bound:
  488. self.bind(socket)
  489. return socket.send(message)
  490. if not type(message) == StringType:
  491. raise TypeError("sendto() argument *message* must be a string")
  492. if isinstance(socket, LogicalDataLink):
  493. if dest is None:
  494. raise Error(errno.EDESTADDRREQ)
  495. if not socket.is_bound:
  496. self.bind(socket)
  497. return socket.sendto(message, dest)
  498. if isinstance(socket, DataLinkConnection):
  499. return socket.send(message)
  500. def recv(self, socket):
  501. message, sender = self.recvfrom(socket)
  502. return message
  503. def recvfrom(self, socket):
  504. if not isinstance(socket, TransmissionControlObject):
  505. raise Error(errno.ENOTSOCK)
  506. if not (socket.addr and self.sap[socket.addr]):
  507. raise Error(errno.EBADF)
  508. if isinstance(socket, RawAccessPoint):
  509. return (socket.recv(), None)
  510. if isinstance(socket, LogicalDataLink):
  511. return socket.recvfrom()
  512. if isinstance(socket, DataLinkConnection):
  513. return (socket.recv(), socket.peer)
  514. def poll(self, socket, event, timeout=None):
  515. if not isinstance(socket, TransmissionControlObject):
  516. raise Error(errno.ENOTSOCK)
  517. if not (socket.addr and self.sap[socket.addr]):
  518. raise Error(errno.EBADF)
  519. return socket.poll(event, timeout)
  520. def close(self, socket):
  521. if not isinstance(socket, TransmissionControlObject):
  522. raise Error(errno.ENOTSOCK)
  523. if socket.is_bound:
  524. self.sap[socket.addr].remove_socket(socket)
  525. else: socket.close()
  526. def getsockname(self, socket):
  527. if not isinstance(socket, TransmissionControlObject):
  528. raise Error(errno.ENOTSOCK)
  529. return socket.addr
  530. def getpeername(self, socket):
  531. if not isinstance(socket, TransmissionControlObject):
  532. raise Error(errno.ENOTSOCK)
  533. return socket.peer