ip-over-ib description entry was missing
[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             /* If we've already read some data, if this failed with an EOF,
131                so that *err is 0, it's a short read. */
132             if (pkt_bytes != 0) {
133                 if (*err == 0)
134                     *err = WTAP_ERR_SHORT_READ;
135             }
136             break;
137         }
138         data = block[0];
139         type = block[1];
140         ctr = pletoh48(&block[2]);
141
142         if (pkt_type == TYPE_UNKNOWN) {
143             if (KNOWN_TYPE(type)) {
144                 pkt_type = type;
145                 pkt_ctr = ctr;
146             }
147         }
148
149         if (type == pkt_type) {
150             if (last_ctr != 0) {
151                 /* if the distance to the last byte of the
152                    same type is larger than 200us, this is very likely the
153                    first byte of a new packet -> go back one block and exit
154                    ctr and last_ctr are in units of 10ns
155                    at 106kbit/s, it takes approx 75us to send one byte */
156                 if (ctr - last_ctr > 200*100) {
157                     file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
158                     break;
159                 }
160             }
161
162             *p++ = data;
163             pkt_bytes++;
164             last_ctr = ctr;
165         }
166         else if (KNOWN_TYPE(type)) {
167             file_seek(fh, -MPLOG_BLOCK_SIZE, SEEK_CUR, err);
168             break;
169         }
170     } while (pkt_bytes < ISO14443_MAX_PKT_LEN);
171
172     if (pkt_type == TYPE_UNKNOWN)
173         return FALSE;
174
175     start_p[0] = ISO14443_PSEUDO_HDR_VER;
176
177     if (pkt_type==TYPE_PCD_PICC_A || pkt_type==TYPE_PCD_PICC_B)
178         start_p[1] = ISO14443_PSEUDO_HDR_PCD_TO_PICC;
179     else
180         start_p[1] = ISO14443_PSEUDO_HDR_PICC_TO_PCD;
181
182     start_p[2] = pkt_bytes >> 8;
183     start_p[3] = pkt_bytes & 0xFF;
184
185     phdr->rec_type = REC_TYPE_PACKET;
186     phdr->pkt_encap = WTAP_ENCAP_ISO14443;
187     phdr->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN;
188     phdr->ts.secs = (time_t)((pkt_ctr*10)/(1000*1000*1000));
189     phdr->ts.nsecs = (int)((pkt_ctr*10)%(1000*1000*1000));
190     phdr->caplen = ISO14443_PSEUDO_HDR_LEN + pkt_bytes;
191     phdr->len = phdr->caplen;
192
193     return TRUE;
194 }
195
196
197 static gboolean
198 mplog_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
199 {
200     *data_offset = file_tell(wth->fh);
201
202     return mplog_read_packet(
203             wth->fh, &wth->phdr, wth->frame_buffer, err, err_info);
204 }
205
206
207 static gboolean
208 mplog_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *pkthdr,
209         Buffer *buf, int *err, gchar **err_info)
210 {
211     if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err))
212         return FALSE;
213
214     if (!mplog_read_packet(wth->random_fh, pkthdr, buf, err, err_info)) {
215         /* Even if we got an immediate EOF, that's an error. */
216         if (*err == 0)
217             *err = WTAP_ERR_SHORT_READ;
218         return FALSE;
219     }
220     return TRUE;
221 }
222
223
224 wtap_open_return_val mplog_open(wtap *wth, int *err, gchar **err_info)
225 {
226     gboolean ok;
227     guint8 magic[6];
228
229     ok = wtap_read_bytes(wth->fh, magic, 6, err, err_info);
230     if (!ok) {
231         if (*err != WTAP_ERR_SHORT_READ)
232             return WTAP_OPEN_ERROR;
233         return WTAP_OPEN_NOT_MINE;
234     }
235     if (memcmp(magic, "MPCSII", 6) != 0)
236         return WTAP_OPEN_NOT_MINE;
237
238     wth->file_encap = WTAP_ENCAP_ISO14443;
239     wth->snapshot_length = 0;
240     wth->file_tsprec = WTAP_TSPREC_NSEC;
241
242     wth->priv = NULL;
243
244     wth->subtype_read = mplog_read;
245     wth->subtype_seek_read = mplog_seek_read;
246     wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_MPLOG;
247
248     /* skip the file header */
249     if (-1 == file_seek(wth->fh, 0x80, SEEK_SET, err))
250         return WTAP_OPEN_ERROR;
251
252     *err = 0;
253     return WTAP_OPEN_MINE;
254 }
255
256
257 /*
258  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
259  *
260  * Local variables:
261  * c-basic-offset: 4
262  * tab-width: 8
263  * indent-tabs-mode: nil
264  * End:
265  *
266  * vi: set shiftwidth=4 tabstop=8 expandtab:
267  * :indentSize=4:tabSize=8:noTabs=true:
268  */