tests: add regression tests for Follow TCP Stream
[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  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12
13
14 /*
15    The mplog file format logs the communication between a contactless
16    smartcard and a card reader. Such files contain information about the
17    physical layer as well as the bytes exchanged between devices.
18    Some commercial logging and testing tools by the French company Micropross
19    use this format.
20
21    The information used for implementing this wiretap module were
22    obtained from reverse-engineering. There is no publicly available
23    documentation of the mplog file format.
24
25    Mplog files start with the string "MPCSII". This string is part of
26    the header which is in total 0x80 bytes long.
27
28    Following the header, the file is a sequence of 8 byte-blocks.
29         data       (one byte)
30         block type (one byte)
31         timestamp  (six bytes)
32
33    The timestamp is a counter in little-endian format. The counter is in
34    units of 10ns.
35 */
36
37 #include "config.h"
38
39 #include <string.h>
40 #include <wtap-int.h>
41 #include <file_wrappers.h>
42
43 #include "mplog.h"
44
45 /* the block types */
46 #define TYPE_PCD_PICC_A  0x70
47 #define TYPE_PICC_PCD_A  0x71
48 #define TYPE_PCD_PICC_B  0x72
49 #define TYPE_PICC_PCD_B  0x73
50 #define TYPE_UNKNOWN     0xFF
51
52 #define KNOWN_TYPE(x) \
53 ( \
54   ((x) == TYPE_PCD_PICC_A) || \
55   ((x) == TYPE_PICC_PCD_A) || \
56   ((x) == TYPE_PCD_PICC_B) || \
57   ((x) == TYPE_PICC_PCD_B) \
58 )
59
60 #define MPLOG_BLOCK_SIZE 8
61
62 /* ISO14443 pseudo-header, see http://www.kaiser.cx/pcap-iso14443.html */
63 #define ISO14443_PSEUDO_HDR_VER  0
64 #define ISO14443_PSEUDO_HDR_LEN  4
65 /*  the two transfer events are the types that include a trailing CRC
66     the CRC is always present in mplog files */
67 #define ISO14443_PSEUDO_HDR_PICC_TO_PCD  0xFF
68 #define ISO14443_PSEUDO_HDR_PCD_TO_PICC  0xFE
69
70
71 #define ISO14443_MAX_PKT_LEN     256
72
73 #define PKT_BUF_LEN   (ISO14443_PSEUDO_HDR_LEN + ISO14443_MAX_PKT_LEN)
74
75
76 /* read the next packet, starting at the current position of fh
77    as we know very little about the file format, our approach is rather simple:
78    - we read block-by-block until a known block-type is found
79         - this block's type is the type of the next packet
80         - this block's timestamp will become the packet's timestamp
81         - the data byte will be our packet's first byte
82    - we carry on reading blocks and add the data bytes
83      of all blocks of "our" type
84    - if a different well-known block type is found, this is the end of
85      our packet, we go back one block so that this block can be picked
86      up as the start of the next packet
87    - if two blocks of our packet's block type are more than 200us apart,
88      we treat this as a packet boundary as described above
89    */
90 static gboolean mplog_read_packet(FILE_T fh, wtap_rec *rec,
91         Buffer *buf, int *err, gchar **err_info)
92 {
93     guint8 *p, *start_p;
94     /* --- the last block of a known type --- */
95     guint64 last_ctr = 0;
96     /* --- the current block --- */
97     guint8 block[MPLOG_BLOCK_SIZE]; /* the entire block */
98     guint8 data, type; /* its data and block type bytes */
99     guint64 ctr; /* its timestamp counter */
100     /* --- the packet we're assembling --- */
101     gint pkt_bytes = 0;
102     guint8 pkt_type = TYPE_UNKNOWN;
103     /* the timestamp of the packet's first block,
104        this will become the packet's timestamp */
105     guint64 pkt_ctr = 0;
106
107
108     ws_buffer_assure_space(buf, PKT_BUF_LEN);
109     p = ws_buffer_start_ptr(buf);
110     start_p = p;
111
112     /* leave space for the iso14443 pseudo header
113        we can't create it until we've seen the entire packet */
114     p += ISO14443_PSEUDO_HDR_LEN;
115
116     do {
117         if (!wtap_read_bytes_or_eof(fh, block, sizeof(block), err, err_info)) {
118             /* If we've already read some data, if this failed with an EOF,
119                so that *err is 0, it's a short read. */
120             if (pkt_bytes != 0) {
121                 if (*err == 0)
122                     *err = WTAP_ERR_SHORT_READ;
123             }
124             break;
125         }
126         data = block[0];
127         type = block[1];
128         ctr = pletoh48(&block[2]);
129
130         if (pkt_type == TYPE_UNKNOWN) {
131             if (KNOWN_TYPE(type)) {
132                 pkt_type = type;
133                 pkt_ctr = ctr;
134             }
135         }
136
137         if (type == pkt_type) {
138             if (last_ctr != 0) {
139                 /* if the distance to the last byte of the
140                    same type is larger than 200us, this is very likely the
141                    first byte of a new packet -> go back one block and exit
142                    ctr and last_ctr are in units of 10ns
143                    at 106kbit/s, it takes approx 75us to send one byte */
144                 if (ctr - last_ctr > 200*100) {
145                     file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
146                     break;
147                 }
148             }
149
150             *p++ = data;
151             pkt_bytes++;
152             last_ctr = ctr;
153         }
154         else if (KNOWN_TYPE(type)) {
155             file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
156             break;
157         }
158     } while (pkt_bytes < ISO14443_MAX_PKT_LEN);
159
160     if (pkt_type == TYPE_UNKNOWN)
161         return FALSE;
162
163     start_p[0] = ISO14443_PSEUDO_HDR_VER;
164
165     if (pkt_type==TYPE_PCD_PICC_A || pkt_type==TYPE_PCD_PICC_B)
166         start_p[1] = ISO14443_PSEUDO_HDR_PCD_TO_PICC;
167     else
168         start_p[1] = ISO14443_PSEUDO_HDR_PICC_TO_PCD;
169
170     start_p[2] = pkt_bytes >> 8;
171     start_p[3] = pkt_bytes & 0xFF;
172
173     rec->rec_type = REC_TYPE_PACKET;
174     rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ISO14443;
175     rec->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN;
176     rec->ts.secs = (time_t)((pkt_ctr*10)/(1000*1000*1000));
177     rec->ts.nsecs = (int)((pkt_ctr*10)%(1000*1000*1000));
178     rec->rec_header.packet_header.caplen = ISO14443_PSEUDO_HDR_LEN + pkt_bytes;
179     rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
180
181     return TRUE;
182 }
183
184
185 static gboolean
186 mplog_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
187 {
188     *data_offset = file_tell(wth->fh);
189
190     return mplog_read_packet(
191             wth->fh, &wth->rec, wth->rec_data, err, err_info);
192 }
193
194
195 static gboolean
196 mplog_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf,
197                 int *err, gchar **err_info)
198 {
199     if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err))
200         return FALSE;
201
202     if (!mplog_read_packet(wth->random_fh, rec, buf, err, err_info)) {
203         /* Even if we got an immediate EOF, that's an error. */
204         if (*err == 0)
205             *err = WTAP_ERR_SHORT_READ;
206         return FALSE;
207     }
208     return TRUE;
209 }
210
211
212 wtap_open_return_val mplog_open(wtap *wth, int *err, gchar **err_info)
213 {
214     gboolean ok;
215     guint8 magic[6];
216
217     ok = wtap_read_bytes(wth->fh, magic, 6, err, err_info);
218     if (!ok) {
219         if (*err != WTAP_ERR_SHORT_READ)
220             return WTAP_OPEN_ERROR;
221         return WTAP_OPEN_NOT_MINE;
222     }
223     if (memcmp(magic, "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  */