├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── make_proto.sh ├── plugins ├── __init__.py ├── c2c_pb2.py ├── c2s_pb2.py └── mod_log.py ├── server.py └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "protocol"] 2 | path = protocol 3 | url = ../protocol.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015, NFCGate Team 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFCGate Server 2 | This is the NFCGate server application using Python 3 and the Google [Protobuf](https://github.com/google/protobuf/) library, version 3. 3 | 4 | To run, simply start the server using `python server.py`. You can then connect to the server using the IP address of your device and the default port of 5566. 5 | The server features a plugin system for data filtering. When starting the server, you can specify a list of plugins to be loaded as parameters, e.g. `python server.py log`. For an example, see the shipped `mod_log.py` plugin. 6 | -------------------------------------------------------------------------------- /make_proto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROTO_IN=protocol/protobuf 4 | PROTO_OUT=plugins 5 | 6 | protoc -I=$PROTO_IN --python_out=$PROTO_OUT $PROTO_IN/*.proto 7 | -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /plugins/c2c_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: c2c.proto 4 | 5 | import sys 6 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import message as _message 9 | from google.protobuf import reflection as _reflection 10 | from google.protobuf import symbol_database as _symbol_database 11 | # @@protoc_insertion_point(imports) 12 | 13 | _sym_db = _symbol_database.Default() 14 | 15 | 16 | 17 | 18 | DESCRIPTOR = _descriptor.FileDescriptor( 19 | name='c2c.proto', 20 | package='de.tu_darmstadt.seemoo.nfcgate.network.c2c', 21 | syntax='proto3', 22 | serialized_options=None, 23 | serialized_pb=_b('\n\tc2c.proto\x12*de.tu_darmstadt.seemoo.nfcgate.network.c2c\"\x9f\x02\n\x07NFCData\x12S\n\x0b\x64\x61ta_source\x18\x01 \x01(\x0e\x32>.de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData.DataSource\x12O\n\tdata_type\x18\x02 \x01(\x0e\x32<.de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData.DataType\x12\x11\n\ttimestamp\x18\x04 \x01(\x03\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\x0c\"\"\n\nDataSource\x12\n\n\x06READER\x10\x00\x12\x08\n\x04\x43\x41RD\x10\x01\")\n\x08\x44\x61taType\x12\x0b\n\x07INITIAL\x10\x00\x12\x10\n\x0c\x43ONTINUATION\x10\x01\x62\x06proto3') 24 | ) 25 | 26 | 27 | 28 | _NFCDATA_DATASOURCE = _descriptor.EnumDescriptor( 29 | name='DataSource', 30 | full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData.DataSource', 31 | filename=None, 32 | file=DESCRIPTOR, 33 | values=[ 34 | _descriptor.EnumValueDescriptor( 35 | name='READER', index=0, number=0, 36 | serialized_options=None, 37 | type=None), 38 | _descriptor.EnumValueDescriptor( 39 | name='CARD', index=1, number=1, 40 | serialized_options=None, 41 | type=None), 42 | ], 43 | containing_type=None, 44 | serialized_options=None, 45 | serialized_start=268, 46 | serialized_end=302, 47 | ) 48 | _sym_db.RegisterEnumDescriptor(_NFCDATA_DATASOURCE) 49 | 50 | _NFCDATA_DATATYPE = _descriptor.EnumDescriptor( 51 | name='DataType', 52 | full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData.DataType', 53 | filename=None, 54 | file=DESCRIPTOR, 55 | values=[ 56 | _descriptor.EnumValueDescriptor( 57 | name='INITIAL', index=0, number=0, 58 | serialized_options=None, 59 | type=None), 60 | _descriptor.EnumValueDescriptor( 61 | name='CONTINUATION', index=1, number=1, 62 | serialized_options=None, 63 | type=None), 64 | ], 65 | containing_type=None, 66 | serialized_options=None, 67 | serialized_start=304, 68 | serialized_end=345, 69 | ) 70 | _sym_db.RegisterEnumDescriptor(_NFCDATA_DATATYPE) 71 | 72 | 73 | _NFCDATA = _descriptor.Descriptor( 74 | name='NFCData', 75 | full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData', 76 | filename=None, 77 | file=DESCRIPTOR, 78 | containing_type=None, 79 | fields=[ 80 | _descriptor.FieldDescriptor( 81 | name='data_source', full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData.data_source', index=0, 82 | number=1, type=14, cpp_type=8, label=1, 83 | has_default_value=False, default_value=0, 84 | message_type=None, enum_type=None, containing_type=None, 85 | is_extension=False, extension_scope=None, 86 | serialized_options=None, file=DESCRIPTOR), 87 | _descriptor.FieldDescriptor( 88 | name='data_type', full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData.data_type', index=1, 89 | number=2, type=14, cpp_type=8, label=1, 90 | has_default_value=False, default_value=0, 91 | message_type=None, enum_type=None, containing_type=None, 92 | is_extension=False, extension_scope=None, 93 | serialized_options=None, file=DESCRIPTOR), 94 | _descriptor.FieldDescriptor( 95 | name='timestamp', full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData.timestamp', index=2, 96 | number=4, type=3, cpp_type=2, label=1, 97 | has_default_value=False, default_value=0, 98 | message_type=None, enum_type=None, containing_type=None, 99 | is_extension=False, extension_scope=None, 100 | serialized_options=None, file=DESCRIPTOR), 101 | _descriptor.FieldDescriptor( 102 | name='data', full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData.data', index=3, 103 | number=3, type=12, cpp_type=9, label=1, 104 | has_default_value=False, default_value=_b(""), 105 | message_type=None, enum_type=None, containing_type=None, 106 | is_extension=False, extension_scope=None, 107 | serialized_options=None, file=DESCRIPTOR), 108 | ], 109 | extensions=[ 110 | ], 111 | nested_types=[], 112 | enum_types=[ 113 | _NFCDATA_DATASOURCE, 114 | _NFCDATA_DATATYPE, 115 | ], 116 | serialized_options=None, 117 | is_extendable=False, 118 | syntax='proto3', 119 | extension_ranges=[], 120 | oneofs=[ 121 | ], 122 | serialized_start=58, 123 | serialized_end=345, 124 | ) 125 | 126 | _NFCDATA.fields_by_name['data_source'].enum_type = _NFCDATA_DATASOURCE 127 | _NFCDATA.fields_by_name['data_type'].enum_type = _NFCDATA_DATATYPE 128 | _NFCDATA_DATASOURCE.containing_type = _NFCDATA 129 | _NFCDATA_DATATYPE.containing_type = _NFCDATA 130 | DESCRIPTOR.message_types_by_name['NFCData'] = _NFCDATA 131 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 132 | 133 | NFCData = _reflection.GeneratedProtocolMessageType('NFCData', (_message.Message,), dict( 134 | DESCRIPTOR = _NFCDATA, 135 | __module__ = 'c2c_pb2' 136 | # @@protoc_insertion_point(class_scope:de.tu_darmstadt.seemoo.nfcgate.network.c2c.NFCData) 137 | )) 138 | _sym_db.RegisterMessage(NFCData) 139 | 140 | 141 | # @@protoc_insertion_point(module_scope) 142 | -------------------------------------------------------------------------------- /plugins/c2s_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: c2s.proto 4 | 5 | import sys 6 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 7 | from google.protobuf import descriptor as _descriptor 8 | from google.protobuf import message as _message 9 | from google.protobuf import reflection as _reflection 10 | from google.protobuf import symbol_database as _symbol_database 11 | # @@protoc_insertion_point(imports) 12 | 13 | _sym_db = _symbol_database.Default() 14 | 15 | 16 | 17 | 18 | DESCRIPTOR = _descriptor.FileDescriptor( 19 | name='c2s.proto', 20 | package='de.tu_darmstadt.seemoo.nfcgate.network.c2s', 21 | syntax='proto3', 22 | serialized_options=None, 23 | serialized_pb=_b('\n\tc2s.proto\x12*de.tu_darmstadt.seemoo.nfcgate.network.c2s\"\xa3\x01\n\nServerData\x12M\n\x06opcode\x18\x01 \x01(\x0e\x32=.de.tu_darmstadt.seemoo.nfcgate.network.c2s.ServerData.Opcode\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"8\n\x06Opcode\x12\n\n\x06OP_PSH\x10\x00\x12\n\n\x06OP_SYN\x10\x01\x12\n\n\x06OP_ACK\x10\x02\x12\n\n\x06OP_FIN\x10\x03\x62\x06proto3') 24 | ) 25 | 26 | 27 | 28 | _SERVERDATA_OPCODE = _descriptor.EnumDescriptor( 29 | name='Opcode', 30 | full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2s.ServerData.Opcode', 31 | filename=None, 32 | file=DESCRIPTOR, 33 | values=[ 34 | _descriptor.EnumValueDescriptor( 35 | name='OP_PSH', index=0, number=0, 36 | serialized_options=None, 37 | type=None), 38 | _descriptor.EnumValueDescriptor( 39 | name='OP_SYN', index=1, number=1, 40 | serialized_options=None, 41 | type=None), 42 | _descriptor.EnumValueDescriptor( 43 | name='OP_ACK', index=2, number=2, 44 | serialized_options=None, 45 | type=None), 46 | _descriptor.EnumValueDescriptor( 47 | name='OP_FIN', index=3, number=3, 48 | serialized_options=None, 49 | type=None), 50 | ], 51 | containing_type=None, 52 | serialized_options=None, 53 | serialized_start=165, 54 | serialized_end=221, 55 | ) 56 | _sym_db.RegisterEnumDescriptor(_SERVERDATA_OPCODE) 57 | 58 | 59 | _SERVERDATA = _descriptor.Descriptor( 60 | name='ServerData', 61 | full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2s.ServerData', 62 | filename=None, 63 | file=DESCRIPTOR, 64 | containing_type=None, 65 | fields=[ 66 | _descriptor.FieldDescriptor( 67 | name='opcode', full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2s.ServerData.opcode', index=0, 68 | number=1, type=14, cpp_type=8, label=1, 69 | has_default_value=False, default_value=0, 70 | message_type=None, enum_type=None, containing_type=None, 71 | is_extension=False, extension_scope=None, 72 | serialized_options=None, file=DESCRIPTOR), 73 | _descriptor.FieldDescriptor( 74 | name='data', full_name='de.tu_darmstadt.seemoo.nfcgate.network.c2s.ServerData.data', index=1, 75 | number=2, type=12, cpp_type=9, label=1, 76 | has_default_value=False, default_value=_b(""), 77 | message_type=None, enum_type=None, containing_type=None, 78 | is_extension=False, extension_scope=None, 79 | serialized_options=None, file=DESCRIPTOR), 80 | ], 81 | extensions=[ 82 | ], 83 | nested_types=[], 84 | enum_types=[ 85 | _SERVERDATA_OPCODE, 86 | ], 87 | serialized_options=None, 88 | is_extendable=False, 89 | syntax='proto3', 90 | extension_ranges=[], 91 | oneofs=[ 92 | ], 93 | serialized_start=58, 94 | serialized_end=221, 95 | ) 96 | 97 | _SERVERDATA.fields_by_name['opcode'].enum_type = _SERVERDATA_OPCODE 98 | _SERVERDATA_OPCODE.containing_type = _SERVERDATA 99 | DESCRIPTOR.message_types_by_name['ServerData'] = _SERVERDATA 100 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 101 | 102 | ServerData = _reflection.GeneratedProtocolMessageType('ServerData', (_message.Message,), dict( 103 | DESCRIPTOR = _SERVERDATA, 104 | __module__ = 'c2s_pb2' 105 | # @@protoc_insertion_point(class_scope:de.tu_darmstadt.seemoo.nfcgate.network.c2s.ServerData) 106 | )) 107 | _sym_db.RegisterMessage(ServerData) 108 | 109 | 110 | # @@protoc_insertion_point(module_scope) 111 | -------------------------------------------------------------------------------- /plugins/mod_log.py: -------------------------------------------------------------------------------- 1 | from plugins.c2c_pb2 import NFCData 2 | from plugins.c2s_pb2 import ServerData 3 | 4 | 5 | def format_data(data): 6 | if len(data) == 0: 7 | return "" 8 | 9 | nfc_data = NFCData() 10 | nfc_data.ParseFromString(data) 11 | 12 | letter = "C" if nfc_data.data_source == NFCData.CARD else "R" 13 | initial = "(initial) " if nfc_data.data_type == NFCData.INITIAL else "" 14 | return "%s: %s%s" % (letter, initial, bytes(nfc_data.data).hex()) 15 | 16 | 17 | def handle_data(log, data, state): 18 | server_message = ServerData() 19 | server_message.ParseFromString(data) 20 | 21 | log(ServerData.Opcode.Name(server_message.opcode), format_data(server_message.data)) 22 | return data 23 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import socket 4 | import socketserver 5 | import ssl 6 | import struct 7 | import datetime 8 | import sys 9 | 10 | HOST = "0.0.0.0" 11 | PORT = 5566 12 | 13 | 14 | class PluginHandler: 15 | def __init__(self, plugins): 16 | self.plugin_list = [] 17 | 18 | for modname in plugins: 19 | self.plugin_list.append((modname, __import__("plugins.mod_%s" % modname, fromlist=["plugins"]))) 20 | print("Loaded", "mod_%s" % modname) 21 | 22 | def filter(self, client, data): 23 | for modname, plugin in self.plugin_list: 24 | if type(data) == list: 25 | first = data[0] 26 | else: 27 | first = data 28 | first = plugin.handle_data(lambda *x: client.log(*x, tag=modname), first, client.state) 29 | if type(data) == list: 30 | data = [first] + data[1:] 31 | else: 32 | data = first 33 | 34 | return data 35 | 36 | 37 | class NFCGateClientHandler(socketserver.StreamRequestHandler): 38 | def __init__(self, request, client_address, srv): 39 | super().__init__(request, client_address, srv) 40 | 41 | def log(self, *args, tag="server"): 42 | self.server.log(*args, origin=self.client_address, tag=tag) 43 | 44 | def setup(self): 45 | super().setup() 46 | 47 | self.session = None 48 | self.state = {} 49 | self.request.settimeout(300) 50 | self.log("server", "connected") 51 | 52 | def handle(self): 53 | super().handle() 54 | 55 | while True: 56 | try: 57 | msg_len_data = self.rfile.read(5) 58 | except socket.timeout: 59 | self.log("server", "Timeout") 60 | break 61 | if len(msg_len_data) < 5: 62 | break 63 | 64 | msg_len, session = struct.unpack("!IB", msg_len_data) 65 | data = self.rfile.read(msg_len) 66 | self.log("server", "data:", bytes(data)) 67 | 68 | # no data was sent or no session number supplied and none set yet 69 | if msg_len == 0 or session == 0 and self.session is None: 70 | break 71 | 72 | # change in session number detected 73 | if self.session != session: 74 | # remove from old association 75 | self.server.remove_client(self, self.session) 76 | # update and add association 77 | self.session = session 78 | self.server.add_client(self, session) 79 | 80 | # allow plugins to filter data before sending it to all clients in the session 81 | self.server.send_to_clients(self.session, self.server.plugins.filter(self, data), self) 82 | 83 | def finish(self): 84 | super().finish() 85 | 86 | self.server.remove_client(self, self.session) 87 | self.log("server", "disconnected") 88 | 89 | 90 | class NFCGateServer(socketserver.ThreadingTCPServer): 91 | def __init__(self, server_address, request_handler, plugins, tls_options=None, bind_and_activate=True): 92 | self.allow_reuse_address = True 93 | super().__init__(server_address, request_handler, bind_and_activate) 94 | 95 | self.clients = {} 96 | self.plugins = PluginHandler(plugins) 97 | 98 | # TLS 99 | self.tls_options = tls_options 100 | 101 | self.log("NFCGate server listening on", server_address) 102 | if self.tls_options: 103 | self.log("TLS enabled with cert {} and key {}".format(self.tls_options["cert_file"], 104 | self.tls_options["key_file"])) 105 | 106 | def get_request(self): 107 | client_socket, from_addr = super().get_request() 108 | if not self.tls_options: 109 | return client_socket, from_addr 110 | # if TLS enabled, wrap the socket 111 | return self.tls_options["context"].wrap_socket(client_socket, server_side=True), from_addr 112 | 113 | def log(self, *args, origin="0", tag="server"): 114 | print(datetime.datetime.now(), "["+tag+"]", origin, *args) 115 | 116 | def add_client(self, client, session): 117 | if session is None: 118 | return 119 | 120 | if session not in self.clients: 121 | self.clients[session] = [] 122 | 123 | self.clients[session].append(client) 124 | client.log("joined session", session) 125 | 126 | def remove_client(self, client, session): 127 | if session is None or session not in self.clients: 128 | return 129 | 130 | self.clients[session].remove(client) 131 | client.log("left session", session) 132 | 133 | def send_to_clients(self, session, msgs, origin): 134 | if session is None or session not in self.clients: 135 | return 136 | 137 | for client in self.clients[session]: 138 | # do not send message back to originator 139 | if client is origin: 140 | continue 141 | 142 | if type(msgs) != list: 143 | msgs = [msgs] 144 | 145 | for msg in msgs: 146 | client.wfile.write(int.to_bytes(len(msg), 4, byteorder='big')) 147 | client.wfile.write(msg) 148 | 149 | self.log("Publish reached", len(self.clients[session]) - 1, "clients") 150 | 151 | 152 | def parse_args(): 153 | parser = argparse.ArgumentParser(prog="NFCGate server") 154 | parser.add_argument("plugins", type=str, nargs="*", help="List of plugin modules to load.") 155 | parser.add_argument("-s", "--tls", help="Enable TLS. You must specify certificate and key.", 156 | default=False, action="store_true") 157 | parser.add_argument("--tls_cert", help="TLS certificate file in PEM format.", action="store") 158 | parser.add_argument("--tls_key", help="TLS key file in PEM format.", action="store") 159 | 160 | args = parser.parse_args() 161 | tls_options = None 162 | 163 | if args.tls: 164 | # check cert and key file 165 | if args.tls_cert is None or args.tls_key is None: 166 | print("You must specify tls_cert and tls_key!") 167 | sys.exit(1) 168 | 169 | tls_options = { 170 | "cert_file": args.tls_cert, 171 | "key_file": args.tls_key 172 | } 173 | try: 174 | tls_options["context"] = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH) 175 | tls_options["context"].load_cert_chain(tls_options["cert_file"], tls_options["key_file"]) 176 | except ssl.SSLError: 177 | print("Certificate or key could not be loaded. Please check format and file permissions!") 178 | sys.exit(1) 179 | return args.plugins, tls_options 180 | 181 | 182 | def main(): 183 | plugins, tls_options = parse_args() 184 | NFCGateServer((HOST, PORT), NFCGateClientHandler, plugins, tls_options).serve_forever() 185 | 186 | 187 | if __name__ == "__main__": 188 | main() 189 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import socket, struct 4 | 5 | from sys import stdout 6 | from os import urandom 7 | 8 | from messages.c2c_pb2 import NFCData, Status 9 | from messages.c2s_pb2 import Session, Data 10 | from messages.metaMessage_pb2 import Wrapper 11 | 12 | def printMsg(msg): 13 | assert len(msg) <= 74 14 | print msg, " "*(74-len(msg)), 15 | 16 | def getSocket(): 17 | tsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 18 | tsock.connect(("127.0.0.1", 5566)) 19 | return tsock 20 | 21 | 22 | def SocketReadN(sock, n): 23 | buf = b'' 24 | while n > 0: 25 | data = sock.recv(n) 26 | if data == b'': 27 | raise RuntimeError('unexpected connection close') 28 | buf += data 29 | n -= len(data) 30 | return buf 31 | 32 | 33 | def RecvOneMsg(sock): 34 | try: 35 | lengthbuf = SocketReadN(sock, 4) 36 | length = struct.unpack(">i", lengthbuf)[0] 37 | wrapper = Wrapper() 38 | wrapper.ParseFromString(SocketReadN(sock, length)) 39 | return wrapper 40 | except: 41 | return None 42 | 43 | def sendOneMsg(msg, sock): 44 | mm = msg.SerializeToString() 45 | sock.sendall(struct.pack(">i", len(mm)) + mm) 46 | 47 | def transceive(msg, sock): 48 | sendOneMsg(msg, sock) 49 | return RecvOneMsg(sock) 50 | 51 | def getStatusMessage(): 52 | data = Data() 53 | data.errcode = Data.ERROR_NOERROR 54 | status = Status() 55 | status.code = Status.CARD_FOUND 56 | iWrapper = Wrapper() 57 | iWrapper.Status.MergeFrom(status) 58 | data.blob = iWrapper.SerializeToString() 59 | wrapper = Wrapper() 60 | wrapper.Data.MergeFrom(data) 61 | return wrapper 62 | 63 | def getSessionMessage(opcode, errcode=Session.ERROR_NOERROR, secret=None): 64 | session = Session() 65 | session.opcode = opcode 66 | session.errcode = errcode 67 | if secret is not None: 68 | session.session_secret = secret 69 | wrapper = Wrapper() 70 | wrapper.Session.MergeFrom(session) 71 | return wrapper 72 | 73 | def getDummyDataMessage(): 74 | data = Data() 75 | data.errcode = Data.ERROR_NOERROR 76 | nfcdata = NFCData() 77 | nfcdata.data_source = NFCData.CARD 78 | nfcdata.data_bytes = urandom(8) 79 | iWrapper = Wrapper() 80 | iWrapper.NFCData.MergeFrom(nfcdata) 81 | data.blob = iWrapper.SerializeToString() 82 | wrapper = Wrapper() 83 | wrapper.Data.MergeFrom(data) 84 | return wrapper 85 | 86 | def assertSessionMessageState(msg, opcode, errcode=Session.ERROR_NOERROR): 87 | assert msg.WhichOneof('message') == 'Session' 88 | assert msg.Session.opcode == opcode 89 | assert msg.Session.errcode == errcode 90 | 91 | def assertDataMessageState(msg, errcode=Data.ERROR_NOERROR, blob=None): 92 | assert msg.WhichOneof('message') == 'Data' 93 | assert msg.Data.errcode == errcode 94 | if blob is not None: 95 | assert msg.Data.blob == blob 96 | 97 | 98 | ### Session tests 99 | sock = getSocket() 100 | printMsg('Testing session creation...') 101 | msg = getSessionMessage(Session.SESSION_CREATE) 102 | reply = transceive(msg, sock) 103 | assertSessionMessageState(reply, Session.SESSION_CREATE_SUCCESS) 104 | assert reply.Session.session_secret != "" 105 | secret = reply.Session.session_secret 106 | print '[OK]' 107 | # State: sock1 in Session 1 108 | 109 | printMsg('Testing illegal creation of second session...') 110 | msg = getSessionMessage(Session.SESSION_CREATE) 111 | reply = transceive(msg, sock) 112 | assertSessionMessageState(reply, Session.SESSION_CREATE_FAIL, Session.ERROR_CREATE_ALREADY_HAS_SESSION) 113 | print '[OK]' 114 | # State: sock1 in Session 1 115 | 116 | sock4 = getSocket() 117 | printMsg('Testing legal creation of second session') 118 | msg = getSessionMessage(Session.SESSION_CREATE) 119 | reply = transceive(msg, sock4) 120 | assertSessionMessageState(reply, Session.SESSION_CREATE_SUCCESS) 121 | assert reply.Session.session_secret != "" 122 | secret2 = reply.Session.session_secret 123 | print '[OK]' 124 | # State: sock1 in Session 1, sock4 in Session2 125 | 126 | sock2 = getSocket() 127 | printMsg('Testing session join...') 128 | msg = getSessionMessage(Session.SESSION_JOIN, secret=secret) 129 | reply = transceive(msg, sock2) 130 | assertSessionMessageState(reply, Session.SESSION_JOIN_SUCCESS) 131 | notify = RecvOneMsg(sock) 132 | assertSessionMessageState(notify, Session.SESSION_PEER_JOINED) 133 | print '[OK]' 134 | # State: sock1 and sock2 in Session 1, sock4 in Session 2 135 | 136 | printMsg('Testing illegal second session join...') 137 | msg = getSessionMessage(Session.SESSION_JOIN, secret=secret2) 138 | reply = transceive(msg, sock2) 139 | assertSessionMessageState(reply, Session.SESSION_JOIN_FAIL, Session.ERROR_JOIN_ALREADY_HAS_SESSION) 140 | print '[OK]' 141 | # State: sock1 and sock2 in Session 1, sock4 in Session 2 142 | 143 | sock3 = getSocket() 144 | printMsg('Testing join on full session...') 145 | msg = getSessionMessage(Session.SESSION_JOIN, secret=secret) 146 | reply = transceive(msg, sock3) 147 | assertSessionMessageState(reply, Session.SESSION_JOIN_FAIL, Session.ERROR_JOIN_SESSION_FULL) 148 | print '[OK]' 149 | # State: sock1 and sock2 in Session 1, sock4 in Session 2 150 | 151 | printMsg('Testing legal second session join...') 152 | msg = getSessionMessage(Session.SESSION_JOIN, secret=secret2) 153 | reply = transceive(msg, sock3) 154 | assertSessionMessageState(reply, Session.SESSION_JOIN_SUCCESS) 155 | notify = RecvOneMsg(sock4) 156 | assertSessionMessageState(notify, Session.SESSION_PEER_JOINED) 157 | print '[OK]' 158 | # State: sock1 and sock2 in Session 1, sock4 and sock3 in Session 2 159 | 160 | printMsg('Testing message passing in session 1...') 161 | msg = getDummyDataMessage() 162 | reply = transceive(msg, sock) 163 | assertDataMessageState(reply) 164 | msgI = RecvOneMsg(sock2) 165 | assertDataMessageState(msgI, blob=msg.Data.blob) 166 | print '[OK]' 167 | 168 | printMsg('Testing NFC Card found status message in Session 1...') 169 | msg = getStatusMessage() 170 | reply = transceive(msg, sock) 171 | assertDataMessageState(reply) 172 | msgI = RecvOneMsg(sock2) 173 | assertDataMessageState(msgI, blob=msg.Data.blob) 174 | print '[OK]' 175 | 176 | printMsg('Testing message reply in session 1...') 177 | msg = getDummyDataMessage() 178 | reply = transceive(msg, sock2) 179 | assertDataMessageState(reply) 180 | msgI = RecvOneMsg(sock) 181 | assertDataMessageState(msgI, blob=msg.Data.blob) 182 | print '[OK]' 183 | 184 | printMsg('Testing message passing in session 2...') 185 | msg = getDummyDataMessage() 186 | reply = transceive(msg, sock3) 187 | assertDataMessageState(reply) 188 | msgI = RecvOneMsg(sock4) 189 | assertDataMessageState(msgI, blob=msg.Data.blob) 190 | print '[OK]' 191 | 192 | printMsg('Testing message reply in session 2...') 193 | msg = getDummyDataMessage() 194 | reply = transceive(msg, sock4) 195 | assertDataMessageState(reply) 196 | msgI = RecvOneMsg(sock3) 197 | assertDataMessageState(msgI, blob=msg.Data.blob) 198 | print '[OK]' 199 | 200 | # TODO Interleaved send and receive 201 | printMsg('Testing session leave...') 202 | msg = getSessionMessage(Session.SESSION_LEAVE, secret=secret) 203 | reply = transceive(msg, sock2) 204 | assertSessionMessageState(reply, Session.SESSION_LEAVE_SUCCESS) 205 | notify = RecvOneMsg(sock) 206 | assertSessionMessageState(notify, Session.SESSION_PEER_LEFT) 207 | print '[OK]' 208 | # State: sock1 in Session 1, sock4 and sock3 in Session 2 209 | 210 | printMsg('Testing session leave 2...') 211 | msg = getSessionMessage(Session.SESSION_LEAVE, secret=secret2) 212 | reply = transceive(msg, sock3) 213 | assertSessionMessageState(reply, Session.SESSION_LEAVE_SUCCESS) 214 | notify = RecvOneMsg(sock4) 215 | assertSessionMessageState(notify, Session.SESSION_PEER_LEFT) 216 | print '[OK]' 217 | # State: sock1 in Session1, sock4 in Session 2 218 | 219 | printMsg('Testing join of recently vacated session...') 220 | msg = getSessionMessage(Session.SESSION_JOIN, secret=secret) 221 | reply = transceive(msg, sock3) 222 | assertSessionMessageState(reply, Session.SESSION_JOIN_SUCCESS) 223 | notify = RecvOneMsg(sock) 224 | assertSessionMessageState(notify, Session.SESSION_PEER_JOINED) 225 | print '[OK]' 226 | # State: sock1 and sock3 in Session 1, sock4 in Session 2 227 | 228 | printMsg('Testing session leave 3...') 229 | msg = getSessionMessage(Session.SESSION_LEAVE, secret=secret) 230 | reply = transceive(msg, sock) 231 | assertSessionMessageState(reply, Session.SESSION_LEAVE_SUCCESS) 232 | notify = RecvOneMsg(sock3) 233 | assertSessionMessageState(notify, Session.SESSION_PEER_LEFT) 234 | print '[OK]' 235 | # State: sock3 in Session 1, sock4 in Session 2 236 | 237 | printMsg('Testing session destruction 1...') 238 | msg = getSessionMessage(Session.SESSION_LEAVE, secret=secret2) 239 | reply = transceive(msg, sock4) 240 | assertSessionMessageState(reply, Session.SESSION_LEAVE_SUCCESS) 241 | print '[OK]' 242 | # State: sock3 in Session 1, Session 2 destroyed 243 | 244 | printMsg('Testing session destruction 2...') 245 | msg = getSessionMessage(Session.SESSION_LEAVE, secret=secret) 246 | reply = transceive(msg, sock3) 247 | assertSessionMessageState(reply, Session.SESSION_LEAVE_SUCCESS) 248 | print '[OK]' 249 | # State: All Sessions destroyed 250 | 251 | printMsg('Testing join on recently destroyed session...') 252 | msg = getSessionMessage(Session.SESSION_JOIN, secret=secret) 253 | reply = transceive(msg, sock) 254 | assertSessionMessageState(reply, Session.SESSION_JOIN_FAIL, Session.ERROR_JOIN_UNKNOWN_SECRET) 255 | print '[OK]' 256 | # State: All Sessions destroyed 257 | --------------------------------------------------------------------------------