Sigh. The "data" element of a GArray is, alas, a "gchar *", not a "void
[obnox/wireshark/wip.git] / wiretap / network_instruments.c
1 /*
2  * $Id$
3  */
4
5 /***************************************************************************
6                           NetworkInstruments.c  -  description
7                              -------------------
8     begin                : Wed Oct 29 2003
9     copyright            : (C) 2003 by root
10     email                : scotte[AT}netinst.com
11  ***************************************************************************/
12
13 /***************************************************************************
14  *                                                                         *
15  *   This program is free software; you can redistribute it and/or modify  *
16  *   it under the terms of the GNU General Public License as published by  *
17  *   the Free Software Foundation; either version 2 of the License, or     *
18  *   (at your option) any later version.                                   *
19  *                                                                         *
20  ***************************************************************************/
21  
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <string.h>
29 #include "wtap-int.h"
30 #include "file_wrappers.h"
31 #include "buffer.h"
32 #include "network_instruments.h"
33
34 static const char network_instruments_magic[] = {"ObserverPktBufferVersion=09.00"};
35 static const int true_magic_length = 17;
36
37 static const guint32 observer_packet_magic = 0x88888888;
38
39 static const int observer_encap[] = {
40         WTAP_ENCAP_ETHERNET,
41         WTAP_ENCAP_TOKEN_RING
42 };
43 #define NUM_OBSERVER_ENCAPS (sizeof observer_encap / sizeof observer_encap[0])
44
45 static const int from_wtap_encap[] = {
46         OBSERVER_UNDEFINED,
47         OBSERVER_ETHERNET,
48         OBSERVER_TOKENRING,
49 };
50 #define NUM_FROM_WTAP_ENCAPS (sizeof from_wtap_encap / sizeof observer_encap[0])
51
52 #define CAPTUREFILE_HEADER_SIZE sizeof(capture_file_header)
53
54 /*
55  * The time in Observer files is in nanoseconds since midnight, January 1,
56  * 2000, 00:00:00 local time.
57  *
58  * We want the seconds portion to be seconds since midnight, January 1,
59  * 1970, 00:00:00 GMT.
60  *
61  * To do that, we add the number of seconds between midnight, January 1,
62  * 2000, 00:00:00 local time and midnight, January 1, 1970, 00:00:00 GMT.
63  * (That gets the wrong answer if the time zone is being read in a different
64  * time zone, but there's not much we can do about that.)
65  */
66 static gboolean have_time_offset;
67 static time_t seconds1970to2000;
68
69 static void init_time_offset(void)
70 {
71         if (!have_time_offset) {
72                 struct tm midnight_2000_01_01;
73
74                 /*
75                  * Get the number of seconds between midnight, January 1,
76                  * 2000, 00:00:00 local time - that's just the UNIX
77                  * time stamp for 2000-01-01 00:00:00 local time.
78                  */
79                 midnight_2000_01_01.tm_year = 2000 - 1900;
80                 midnight_2000_01_01.tm_mon = 0;
81                 midnight_2000_01_01.tm_mday = 1;
82                 midnight_2000_01_01.tm_hour = 0;
83                 midnight_2000_01_01.tm_min = 0;
84                 midnight_2000_01_01.tm_sec = 0;
85                 midnight_2000_01_01.tm_isdst = -1;
86                 seconds1970to2000 = mktime(&midnight_2000_01_01);
87                 have_time_offset = TRUE;
88         }
89 }
90
91 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
92     gint64 *data_offset);
93 static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
94     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
95     int *err, gchar **err_info);
96 static int read_packet_header(FILE_T fh, packet_entry_header *packet_header,
97     int *err, gchar **err_info);
98 static gboolean read_packet_data(FILE_T fh, int offset_to_frame, int offset,
99     guint8 *pd, int length, int *err, char **err_info);
100 static gboolean skip_to_next_packet(wtap *wth, int offset,
101     int offset_to_next_packet, int *err, char **err_info);
102 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
103     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err);
104
105 int network_instruments_open(wtap *wth, int *err, gchar **err_info)
106 {
107         int bytes_read;
108         int offset;
109         capture_file_header file_header;
110         guint i;
111         tlv_header tlvh;
112         int seek_increment;
113         packet_entry_header packet_header;
114
115         errno = WTAP_ERR_CANT_READ;
116         offset = 0;
117
118         /* read in the buffer file header */
119         bytes_read = file_read(&file_header, sizeof file_header, wth->fh);
120         if (bytes_read != sizeof file_header) {
121                 *err = file_error(wth->fh);
122                 if (*err != 0)
123                         return -1;
124                 return 0;
125         }
126         offset += bytes_read;
127
128         /* check the magic number */
129         if (memcmp(file_header.observer_version, network_instruments_magic, true_magic_length)!=0) {
130                 return 0;
131         }
132
133         /* check the version */
134         if (strncmp(network_instruments_magic, file_header.observer_version, 30)!=0) {
135                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
136                 *err_info = g_strdup_printf("Observer: unsupported file version %s", file_header.observer_version);
137                 return -1;
138         }
139
140         /* process extra information */
141         for (i = 0; i < file_header.number_of_information_elements; i++) {
142                 /* read the TLV header */
143                 bytes_read = file_read(&tlvh, sizeof tlvh, wth->fh);
144                 if (bytes_read != sizeof tlvh) {
145                         *err = file_error(wth->fh);
146                         if (*err == 0)
147                                 *err = WTAP_ERR_SHORT_READ;
148                         return -1;
149                 }
150                 offset += bytes_read;
151
152                 tlvh.length = GUINT16_FROM_LE(tlvh.length);
153                 if (tlvh.length < sizeof tlvh) {
154                         *err = WTAP_ERR_BAD_RECORD;
155                         *err_info = g_strdup_printf("Observer: bad record (TLV length %u < %lu)",
156                             tlvh.length, (unsigned long)sizeof tlvh);
157                         return -1;
158                 }
159
160                 /* skip the TLV data */
161                 seek_increment = tlvh.length - (int)sizeof tlvh;
162                 if (seek_increment > 0) {
163                         if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
164                                 return -1;
165                 }
166                 offset += seek_increment;
167         }
168
169         /* get to the first packet */
170         file_header.offset_to_first_packet =
171             GUINT16_FROM_LE(file_header.offset_to_first_packet);
172         if (file_header.offset_to_first_packet < offset) {
173                 *err = WTAP_ERR_BAD_RECORD;
174                 *err_info = g_strdup_printf("Observer: bad record (offset to first packet %d < %d)",
175                     file_header.offset_to_first_packet, offset);
176                 return FALSE;
177         }
178         seek_increment = file_header.offset_to_first_packet - offset;
179         if (seek_increment > 0) {
180                 if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
181                         return -1;
182         }
183
184         /* pull off the packet header */
185         bytes_read = file_read(&packet_header, sizeof packet_header, wth->fh);
186         if (bytes_read != sizeof packet_header) {
187                 *err = file_error(wth->fh);
188                 if (*err != 0)
189                         return -1;
190                 return 0;
191         }
192
193         /* check the packet's magic number; the magic number is all 8's,
194            so the byte order doesn't matter */
195         if (packet_header.packet_magic != observer_packet_magic) {
196                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
197                 *err_info = g_strdup_printf("Observer: unsupported packet version %ul", packet_header.packet_magic);
198                 return -1;
199         }
200
201         /* check the data link type */
202         if (packet_header.network_type >= NUM_OBSERVER_ENCAPS) {
203                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
204                 *err_info = g_strdup_printf("Observer: network type %u unknown or unsupported", packet_header.network_type);
205                 return -1;
206         }
207         wth->file_encap = observer_encap[packet_header.network_type];
208
209         wth->file_type = WTAP_FILE_NETWORK_INSTRUMENTS_V9;
210
211         /* set up the rest of the capture parameters */
212         wth->subtype_read = observer_read;
213         wth->subtype_seek_read = observer_seek_read;
214         wth->subtype_close = NULL;
215         wth->subtype_sequential_close = NULL;
216         wth->snapshot_length = 0;       /* not available in header */
217         wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
218
219         /* reset the pointer to the first packet */
220         if (file_seek(wth->fh, file_header.offset_to_first_packet, SEEK_SET,
221             err) == -1)
222                 return -1;
223         wth->data_offset = file_header.offset_to_first_packet;
224
225         init_time_offset();
226
227         return 1;
228 }
229
230 /* Reads the next packet. */
231 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
232     gint64 *data_offset)
233 {
234         int offset;
235         packet_entry_header packet_header;
236
237         /* skip records other than data records */
238         for (;;) {
239                 *data_offset = wth->data_offset;
240
241                 /* process the packet header, including TLVs */
242                 offset = read_packet_header(wth->fh, &packet_header, err,
243                     err_info);
244                 if (offset <= 0)
245                         return FALSE;   /* EOF or error */
246
247                 wth->data_offset += offset;
248
249                 if (packet_header.packet_type == PACKET_TYPE_DATA_PACKET)
250                         break;
251
252                 /* skip to next packet */
253                 if (!skip_to_next_packet(wth, offset,
254                     packet_header.offset_to_next_packet, err, err_info))
255                         return FALSE;   /* EOF or error */
256         }
257
258         /* neglect frame markers for wiretap */
259         if (packet_header.network_size < 4) {
260                 *err = WTAP_ERR_BAD_RECORD;
261                 *err_info = g_strdup_printf("Observer: bad record: Packet length %u < 4",
262                     packet_header.network_size);
263                 return FALSE;
264         }
265
266         /* set the wiretap packet header fields */
267         wth->phdr.pkt_encap = observer_encap[packet_header.network_type];
268         wth->phdr.len = packet_header.network_size - 4;
269         wth->phdr.caplen = MIN(packet_header.captured_size, wth->phdr.len);
270         wth->phdr.ts.secs =
271             (time_t) (packet_header.nano_seconds_since_2000/1000000000 + seconds1970to2000);
272         wth->phdr.ts.nsecs = (int) (packet_header.nano_seconds_since_2000%1000000000);
273
274         /* update the pseudo header */
275         switch (wth->file_encap) {
276
277         case WTAP_ENCAP_ETHERNET:
278                 /* There is no FCS in the frame */
279                 wth->pseudo_header.eth.fcs_len = 0;
280                 break;
281         }
282
283         /* set-up the packet buffer */
284         buffer_assure_space(wth->frame_buffer, packet_header.captured_size);
285
286         /* read the frame data */
287         if (!read_packet_data(wth->fh, packet_header.offset_to_frame, offset,
288             buffer_start_ptr(wth->frame_buffer), packet_header.captured_size,
289             err, err_info))
290                 return FALSE;
291         wth->data_offset += packet_header.captured_size;
292         offset += packet_header.captured_size;
293
294         /* skip over any extra bytes following the frame data */
295         if (!skip_to_next_packet(wth, offset, packet_header.offset_to_next_packet,
296                 err, err_info))
297                 return FALSE;
298
299         return TRUE;
300 }
301
302 /* Reads a packet at an offset. */
303 static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
304     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
305     int *err, gchar **err_info)
306 {
307         packet_entry_header packet_header;
308         int offset;
309
310         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
311                 return FALSE;
312
313         /* process the packet header, including TLVs */
314         offset = read_packet_header(wth->random_fh, &packet_header, err,
315             err_info);
316         if (offset <= 0)
317                 return FALSE;   /* EOF or error */
318
319         /* update the pseudo header */
320         switch (wth->file_encap) {
321
322         case WTAP_ENCAP_ETHERNET:
323                 /* There is no FCS in the frame */
324                 pseudo_header->eth.fcs_len = 0;
325                 break;
326         }
327
328         /* read the frame data */
329         if (!read_packet_data(wth->random_fh, packet_header.offset_to_frame,
330             offset, pd, length, err, err_info))
331                 return FALSE;
332
333         return TRUE;
334 }
335
336 static int
337 read_packet_header(FILE_T fh, packet_entry_header *packet_header, int *err,
338     gchar **err_info)
339 {
340         int offset;
341         int bytes_read;
342         guint i;
343         tlv_header tlvh;
344         int seek_increment;
345
346         offset = 0;
347
348         /* pull off the packet header */
349         bytes_read = file_read(packet_header, sizeof *packet_header, fh);
350         if (bytes_read != sizeof *packet_header) {
351                 *err = file_error(fh);
352                 if (*err != 0)
353                         return -1;
354                 return 0;       /* EOF */
355         }
356         offset += bytes_read;
357
358         /* swap all multi-byte fields immediately */
359         packet_header->packet_magic = GUINT32_FROM_LE(packet_header->packet_magic);
360         packet_header->network_speed = GUINT32_FROM_LE(packet_header->network_speed);
361         packet_header->captured_size = GUINT16_FROM_LE(packet_header->captured_size);
362         packet_header->network_size = GUINT16_FROM_LE(packet_header->network_size);
363         packet_header->offset_to_frame = GUINT16_FROM_LE(packet_header->offset_to_frame);
364         packet_header->offset_to_next_packet = GUINT16_FROM_LE(packet_header->offset_to_next_packet);
365         packet_header->errors = GUINT16_FROM_LE(packet_header->errors);
366         packet_header->reserved = GUINT16_FROM_LE(packet_header->reserved);
367         packet_header->packet_number = GUINT64_FROM_LE(packet_header->packet_number);
368         packet_header->original_packet_number = GUINT64_FROM_LE(packet_header->original_packet_number);
369         packet_header->nano_seconds_since_2000 = GUINT64_FROM_LE(packet_header->nano_seconds_since_2000);
370
371         /* check the packet's magic number */
372         if (packet_header->packet_magic != observer_packet_magic) {
373
374                 /*
375                  * Some files are zero-padded at the end. There is no warning of this
376                  * in the previous packet header information, such as setting
377                  * offset_to_next_packet to zero. So detect this situation by treating
378                  * an all-zero header as a sentinel. Return EOF when it is encountered,
379                  * rather than treat it as a bad record.
380                  */
381                 for (i = 0; i < sizeof *packet_header; i++) {
382                         if (((guint8*) packet_header)[i] != 0)
383                                 break;
384                 }
385                 if (i == sizeof *packet_header) {
386                         *err = 0;
387                         return 0;       /* EOF */
388                 }
389
390                 *err = WTAP_ERR_BAD_RECORD;
391                 *err_info = g_strdup_printf("Observer: bad record: Invalid magic number 0x%08x",
392                     GUINT32_FROM_LE(packet_header->packet_magic));
393                 return -1;
394         }
395
396         /* process extra information */
397         for (i = 0; i < packet_header->number_of_information_elements; i++) {
398                 /* read the TLV header */
399                 bytes_read = file_read(&tlvh, sizeof tlvh, fh);
400                 if (bytes_read != sizeof tlvh) {
401                         *err = file_error(fh);
402                         if (*err == 0)
403                                 *err = WTAP_ERR_SHORT_READ;
404                         return -1;
405                 }
406                 offset += bytes_read;
407
408                 tlvh.length = GUINT16_FROM_LE(tlvh.length);
409                 if (tlvh.length < sizeof tlvh) {
410                         *err = WTAP_ERR_BAD_RECORD;
411                         *err_info = g_strdup_printf("Observer: bad record (TLV length %u < %lu)",
412                             tlvh.length, (unsigned long)sizeof tlvh);
413                         return -1;
414                 }
415
416                 /* skip the TLV data */
417                 seek_increment = tlvh.length - (int)sizeof tlvh;
418                 if (seek_increment > 0) {
419                         if (file_seek(fh, seek_increment, SEEK_CUR, err) == -1)
420                                 return -1;
421                 }
422                 offset += seek_increment;
423         }
424
425         return offset;
426 }
427
428 static gboolean
429 read_packet_data(FILE_T fh, int offset_to_frame, int offset, guint8 *pd,
430     int length, int *err, char **err_info)
431 {
432         int seek_increment;
433
434         /* get to the packet data */
435         if (offset_to_frame < offset) {
436                 *err = WTAP_ERR_BAD_RECORD;
437                 *err_info = g_strdup_printf("Observer: bad record (offset to packet data %d < %d)",
438                     offset_to_frame, offset);
439                 return FALSE;
440         }
441         seek_increment = offset_to_frame - offset;
442         if (seek_increment > 0) {
443                 if (file_seek(fh, seek_increment, SEEK_CUR, err) == -1)
444                         return FALSE;
445         }
446
447         /* read in the packet */
448         wtap_file_read_expected_bytes(pd, length, fh, err);
449         return TRUE;
450 }
451
452 static gboolean
453 skip_to_next_packet(wtap *wth, int offset, int offset_to_next_packet, int *err,
454     char **err_info)
455 {
456         int seek_increment;
457
458         if (offset_to_next_packet < offset) {
459                 *err = WTAP_ERR_BAD_RECORD;
460                 *err_info = g_strdup_printf("Observer: bad record (offset to next packet %d < %d)",
461                     offset_to_next_packet, offset);
462                 return FALSE;
463         }
464         seek_increment = offset_to_next_packet - offset;
465         if (seek_increment > 0) {
466                 if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
467                         return FALSE;
468         }
469         wth->data_offset += seek_increment;
470         return TRUE;
471 }
472
473 typedef struct {
474         guint64 packet_count;
475         guint8  network_type;
476 } niobserver_dump_t;
477
478 /* Returns 0 if we could write the specified encapsulation type,
479    an error indication otherwise. */
480 int network_instruments_dump_can_write_encap(int encap)
481 {
482         /* per-packet encapsulations aren't supported */
483         if (encap == WTAP_ENCAP_PER_PACKET)
484                 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
485         
486         if (encap < 0 || (unsigned) encap >= NUM_FROM_WTAP_ENCAPS || from_wtap_encap[encap] == OBSERVER_UNDEFINED)
487                 return WTAP_ERR_UNSUPPORTED_ENCAP;
488
489         return 0;
490 }
491
492 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
493    failure. */
494 gboolean network_instruments_dump_open(wtap_dumper *wdh, int *err)
495 {
496         capture_file_header file_header;
497         tlv_header comment_header;
498         char comment[64];
499         struct tm *current_time;
500         time_t system_time;
501         niobserver_dump_t *niobserver;
502
503         wdh->subtype_write = observer_dump;
504
505         niobserver = (niobserver_dump_t *)g_malloc(sizeof(niobserver_dump_t));
506         wdh->priv = (void *)niobserver;
507         niobserver->packet_count = 0;
508         niobserver->network_type = from_wtap_encap[wdh->encap];
509
510         /* create the file comment */
511         time(&system_time);
512         current_time = localtime(&system_time);
513         memset(&comment, 0x00, sizeof(comment));
514         g_snprintf(comment, 64, "This capture was saved from Wireshark on %s", asctime(current_time));
515
516         /* create the file header */
517         if (fseek(wdh->fh, 0, SEEK_SET) == -1) {
518                 *err = errno;
519                 return FALSE;
520         }
521         memset(&file_header, 0x00, sizeof(capture_file_header));
522         g_strlcpy(file_header.observer_version, network_instruments_magic, 32);
523         file_header.offset_to_first_packet = (guint16) (sizeof(capture_file_header) + sizeof(tlv_header) + strlen(comment));
524         file_header.offset_to_first_packet = GUINT16_TO_LE(file_header.offset_to_first_packet);
525         file_header.number_of_information_elements = 1;
526         if(!wtap_dump_file_write(wdh, &file_header, sizeof(capture_file_header), err))
527                 return FALSE;
528
529         /* create the comment entry */
530         comment_header.type = GUINT16_TO_LE(INFORMATION_TYPE_COMMENT);
531         comment_header.length = (guint16) (sizeof(tlv_header) + strlen(comment));
532         comment_header.length = GUINT16_TO_LE(comment_header.length);
533         if(!wtap_dump_file_write(wdh, &comment_header, sizeof(tlv_header), err))
534                 return FALSE;
535         if(!wtap_dump_file_write(wdh, &comment, strlen(comment), err))
536                 return FALSE;
537
538         init_time_offset();
539
540         return TRUE;
541 }
542
543 /* Write a record for a packet to a dump file.
544    Returns TRUE on success, FALSE on failure. */
545 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
546     const union wtap_pseudo_header *pseudo_header _U_, const guchar *pd,
547     int *err)
548 {
549         niobserver_dump_t *niobserver = (niobserver_dump_t *)wdh->priv;
550         packet_entry_header packet_header;
551         guint64 capture_nanoseconds;
552
553         if (phdr->ts.secs < seconds1970to2000) {
554                 if (phdr->ts.secs < 0)
555                         capture_nanoseconds = 0;
556                 else
557                         capture_nanoseconds = phdr->ts.secs;
558         } else
559                 capture_nanoseconds = phdr->ts.secs - seconds1970to2000;
560         capture_nanoseconds = capture_nanoseconds*1000000000 + phdr->ts.nsecs;
561
562         memset(&packet_header, 0x00, sizeof(packet_entry_header));
563         packet_header.packet_magic = GUINT32_TO_LE(observer_packet_magic);
564         packet_header.network_speed = GUINT32_TO_LE(1000000);
565         packet_header.captured_size = GUINT16_TO_LE((guint16)phdr->caplen);
566         packet_header.network_size = GUINT16_TO_LE((guint16)(phdr->len+4));
567         packet_header.offset_to_frame = GUINT16_TO_LE(sizeof(packet_entry_header));
568         packet_header.offset_to_next_packet = GUINT16_TO_LE(sizeof(packet_entry_header) + phdr->caplen);
569         packet_header.network_type = niobserver->network_type;
570         packet_header.flags = 0x00;
571         packet_header.number_of_information_elements = 0;
572         packet_header.packet_type = PACKET_TYPE_DATA_PACKET;
573         packet_header.packet_number = GUINT64_TO_LE(niobserver->packet_count);
574         packet_header.original_packet_number = GUINT64_TO_LE(niobserver->packet_count);
575         niobserver->packet_count++;
576         packet_header.nano_seconds_since_2000 = GUINT64_TO_LE(capture_nanoseconds);
577
578         if (!wtap_dump_file_write(wdh, &packet_header, sizeof(packet_header),
579             err))
580                 return FALSE;
581
582         if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))
583                 return FALSE;
584
585         return TRUE;
586 }