Use memcmp() to check the magic number.
[metze/wireshark/wip.git] / wiretap / mplog.c
1 /* mplog.c
2  *
3  * File format support for Micropross mplog files
4  * Copyright (c) 2016 by Martin Kaiser <martin@kaiser.cx>
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25
26 /*
27    The mplog file format logs the communication between a contactless
28    smartcard and a card reader. Such files contain information about the
29    physical layer as well as the bytes exchanged between devices.
30    Some commercial logging and testing tools by the French company Micropross
31    use this format.
32
33    The information used for implementing this wiretap module were
34    obtained from reverse-engineering. There is no publicly available
35    documentation of the mplog file format.
36
37    Mplog files start with the string "MPCSII". This string is part of
38    the header which is in total 0x80 bytes long.
39
40    Following the header, the file is a sequence of 8 byte-blocks.
41         data       (one byte)
42         block type (one byte)
43         timestamp  (six bytes)
44
45    The timestamp is a counter in little-endian format. The counter is in
46    units of 10ns.
47 */
48
49 #include "config.h"
50
51 #include <string.h>
52 #include <wtap-int.h>
53 #include <file_wrappers.h>
54
55 #include "mplog.h"
56
57 /* the block types */
58 #define TYPE_PCD_PICC_A  0x70
59 #define TYPE_PICC_PCD_A  0x71
60 #define TYPE_PCD_PICC_B  0x72
61 #define TYPE_PICC_PCD_B  0x73
62 #define TYPE_UNKNOWN     0xFF
63
64 #define KNOWN_TYPE(x) \
65 ( \
66   ((x) == TYPE_PCD_PICC_A) || \
67   ((x) == TYPE_PICC_PCD_A) || \
68   ((x) == TYPE_PCD_PICC_B) || \
69   ((x) == TYPE_PICC_PCD_B) \
70 )
71
72 #define MPLOG_BLOCK_SIZE 8
73
74 /* ISO14443 pseudo-header, see http://www.kaiser.cx/pcap-iso14443.html */
75 #define ISO14443_PSEUDO_HDR_VER  0
76 #define ISO14443_PSEUDO_HDR_LEN  4
77 /*  the two transfer events are the types that include a trailing CRC
78     the CRC is always present in mplog files */
79 #define ISO14443_PSEUDO_HDR_PICC_TO_PCD  0xFF
80 #define ISO14443_PSEUDO_HDR_PCD_TO_PICC  0xFE
81
82
83 #define ISO14443_MAX_PKT_LEN     256
84
85 #define PKT_BUF_LEN   (ISO14443_PSEUDO_HDR_LEN + ISO14443_MAX_PKT_LEN)
86
87
88 /* read the next packet, starting at the current position of fh
89    as we know very little about the file format, our approach is rather simple:
90    - we read block-by-block until a known block-type is found
91         - this block's type is the type of the next packet
92         - this block's timestamp will become the packet's timestamp
93         - the data byte will be our packet's first byte
94    - we carry on reading blocks and add the data bytes
95      of all blocks of "our" type
96    - if a different well-known block type is found, this is the end of
97      our packet, we go back one block so that this block can be picked
98      up as the start of the next packet
99    - if two blocks of our packet's block type are more than 200us apart,
100      we treat this as a packet boundary as described above
101    */
102 static gboolean mplog_read_packet(FILE_T fh, struct wtap_pkthdr *phdr,
103         Buffer *buf, int *err, gchar **err_info)
104 {
105     guint8 *p, *start_p;
106     /* --- the last block of a known type --- */
107     guint64 last_ctr = 0;
108     /* --- the current block --- */
109     guint8 block[MPLOG_BLOCK_SIZE]; /* the entire block */
110     guint8 data, type; /* its data and block type bytes */
111     guint64 ctr; /* its timestamp counter */
112     /* --- the packet we're assembling --- */
113     gint pkt_bytes = 0;
114     guint8 pkt_type = TYPE_UNKNOWN;
115     /* the timestamp of the packet's first block,
116        this will become the packet's timestamp */
117     guint64 pkt_ctr = 0;
118
119
120     ws_buffer_assure_space(buf, PKT_BUF_LEN);
121     p = ws_buffer_start_ptr(buf);
122     start_p = p;
123
124     /* leave space for the iso14443 pseudo header
125        we can't create it until we've seen the entire packet */
126     p += ISO14443_PSEUDO_HDR_LEN;
127
128     do {
129         if (!wtap_read_bytes_or_eof(fh, block, sizeof(block), err, err_info))
130             break;
131         data = block[0];
132         type = block[1];
133         ctr = pletoh48(&block[2]);
134
135         if (pkt_type == TYPE_UNKNOWN) {
136             if (KNOWN_TYPE(type)) {
137                 pkt_type = type;
138                 pkt_ctr = ctr;
139             }
140         }
141
142         if (type == pkt_type) {
143             if (last_ctr != 0) {
144                 /* if the distance to the last byte of the
145                    same type is larger than 200us, this is very likely the
146                    first byte of a new packet -> go back one block and exit
147                    ctr and last_ctr are in units of 10ns
148                    at 106kbit/s, it takes approx 75us to send one byte */
149                 if (ctr - last_ctr > 200*100) {
150                     file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
151                     break;
152                 }
153             }
154
155             *p++ = data;
156             pkt_bytes++;
157             last_ctr = ctr;
158         }
159         else if (KNOWN_TYPE(type)) {
160             file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
161             break;
162         }
163     } while (pkt_bytes < ISO14443_MAX_PKT_LEN);
164
165     if (pkt_type == TYPE_UNKNOWN)
166         return FALSE;
167
168     start_p[0] = ISO14443_PSEUDO_HDR_VER;
169
170     if (pkt_type==TYPE_PCD_PICC_A || pkt_type==TYPE_PCD_PICC_B)
171         start_p[1] = ISO14443_PSEUDO_HDR_PCD_TO_PICC;
172     else
173         start_p[1] = ISO14443_PSEUDO_HDR_PICC_TO_PCD;
174
175     start_p[2] = pkt_bytes >> 8;
176     start_p[3] = pkt_bytes & 0xFF;
177
178     phdr->rec_type = REC_TYPE_PACKET;
179     phdr->pkt_encap = WTAP_ENCAP_ISO14443;
180     phdr->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN;
181     phdr->ts.secs = (time_t)((pkt_ctr*10)/(1000*1000*1000));
182     phdr->ts.nsecs = (int)((pkt_ctr*10)%(1000*1000*1000));
183     phdr->caplen = ISO14443_PSEUDO_HDR_LEN + pkt_bytes;
184     phdr->len = phdr->caplen;
185
186     return TRUE;
187 }
188
189
190 static gboolean
191 mplog_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
192 {
193     *data_offset = file_tell(wth->fh);
194
195     return mplog_read_packet(
196             wth->fh, &wth->phdr, wth->frame_buffer, err, err_info);
197 }
198
199
200 static gboolean
201 mplog_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *pkthdr,
202         Buffer *buf, int *err, gchar **err_info)
203 {
204     if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err))
205         return FALSE;
206
207     return mplog_read_packet(wth->random_fh, pkthdr, buf, err, err_info);
208 }
209
210
211 wtap_open_return_val mplog_open(wtap *wth, int *err, gchar **err_info _U_)
212 {
213     gboolean ok;
214     guint8 str[6];
215
216     /* rewind the fh so we re-read from the beginning */
217     if (-1 == file_seek(wth->fh, 0, SEEK_SET, err))
218         return WTAP_OPEN_ERROR;
219
220     ok = wtap_read_bytes_or_eof(wth->fh, str, 6, err, err_info);
221     if (!ok)
222         return WTAP_OPEN_NOT_MINE;
223     if (memcmp(str, "MPCSII", 6) != 0)
224         return WTAP_OPEN_NOT_MINE;
225
226     wth->file_encap = WTAP_ENCAP_ISO14443;
227     wth->snapshot_length = 0;
228     wth->file_tsprec = WTAP_TSPREC_NSEC;
229
230     wth->priv = NULL;
231
232     wth->subtype_read = mplog_read;
233     wth->subtype_seek_read = mplog_seek_read;
234     wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_MPLOG;
235
236     /* skip the file header */
237     if (-1 == file_seek(wth->fh, 0x80, SEEK_SET, err))
238         return WTAP_OPEN_ERROR;
239
240     *err = 0;
241     return WTAP_OPEN_MINE;
242 }
243
244
245 /*
246  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
247  *
248  * Local variables:
249  * c-basic-offset: 4
250  * tab-width: 8
251  * indent-tabs-mode: nil
252  * End:
253  *
254  * vi: set shiftwidth=4 tabstop=8 expandtab:
255  * :indentSize=4:tabSize=8:noTabs=true:
256  */