Initial checkin of the 32 bit tektronix k12 binary format (rf5)
[obnox/wireshark/wip.git] / wiretap / k12.c
1 /*
2  * k12.c
3  *
4  *  routines for importing tektronix k12xx *.rf5 files
5  *
6  *  Copyright (c) 2005, Luis E. Garia Ontanon <luis.ontanon@gmail.com>
7  *
8  * $Id$
9  *
10  * Wiretap Library
11  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include "wtap-int.h"
35 #include "wtap.h"
36 #include "file_wrappers.h"
37 #include "buffer.h"
38
39
40 static const guint8 k12_file_magic[] = { 0x00, 0x00, 0x02, 0x00 ,0x12, 0x05, 0x00, 0x10 };
41
42 #define K12_REC_PACKET  0x00010020
43 #define K12_REC_SRCDSC  0x00070041
44
45 /* XXX: we don't know what is in these type of records */
46 #define K12_REC_UNK001  0x00070040
47 #define K12_REC_UNK002  0x00070042
48 #define K12_REC_UNK003  0x00070044
49
50 /* So far we've seen the following appear only at the end of the file */
51 #define K12_REC_UNK004  0x00020030
52 #define K12_REC_UNK005  0x00020031
53
54 #define K12_HDR_LEN 0x10
55
56 typedef struct {
57         guint32 len;
58         guint32 type;
59         guint32 frame_len;
60         guint32 port_id;
61 } k12_record_hdr_t;
62
63 typedef struct {
64 } k12_scrdsc_hdr_t;
65
66 typedef struct  {
67         gchar* name;
68         guint32 encap;
69 } k12_stack_encap_t;
70
71 typedef struct  {
72         guint32 port_id;
73         guint32 encap;
74 } k12_port_encap_t;
75
76 struct _k12_t {
77         k12_stack_encap_t* stack_encap;
78         guint stack_encap_p;
79         GPtrArray *port_encaps;
80         guint32 file_len;
81 };
82
83 static const k12_stack_encap_t virgin_stack_encap[] = {
84         {NULL,WTAP_ENCAP_USER0},
85         {NULL,WTAP_ENCAP_USER1},
86         {NULL,WTAP_ENCAP_USER2},
87         {NULL,WTAP_ENCAP_USER3},
88         {NULL,WTAP_ENCAP_USER4},
89         {NULL,WTAP_ENCAP_USER5},
90         {NULL,WTAP_ENCAP_USER6},
91         {NULL,WTAP_ENCAP_USER7},
92         {NULL,WTAP_ENCAP_USER8},
93         {NULL,WTAP_ENCAP_USER9},
94         {NULL,WTAP_ENCAP_USER10},
95         {NULL,WTAP_ENCAP_USER11},
96         {NULL,WTAP_ENCAP_USER12},
97         {NULL,WTAP_ENCAP_USER13},
98         {NULL,WTAP_ENCAP_USER14},
99 /*      {NULL,WTAP_ENCAP_USER15},  used for unnknown sources */
100         {NULL,0}        
101 };
102 static guint32 choose_encap(k12_t* file_data, guint32 port_id, gchar* stack_name) {
103         guint32 encap = 0;
104         k12_port_encap_t* pe;
105         guint i;
106         
107         for (i =0; i < file_data->stack_encap_p; i++) {
108
109                 if (strcmp(stack_name,file_data->stack_encap[i].name) == 0) {
110                         encap = file_data->stack_encap[i].encap;
111                         g_free(stack_name);
112                         break;
113                 }
114         }
115
116         if (file_data->stack_encap_p > 14) {
117                 /* g_warning("k12_choose_encap: Cannot handle more than 15 stack types"); */
118                 return WTAP_ENCAP_USER15;
119         }
120         
121         if ( encap == 0 ) {
122                 file_data->stack_encap[file_data->stack_encap_p].name = stack_name;
123                 encap = file_data->stack_encap[file_data->stack_encap_p].encap;
124         }
125
126         pe = g_malloc(sizeof(k12_port_encap_t));
127         pe->port_id = port_id;
128         pe->encap = encap;
129
130         g_ptr_array_add(file_data->port_encaps,pe);
131         return encap;
132 }
133
134 static guint32 get_encap(k12_t* file_data, guint32 port_id) {
135         guint i;
136         k12_port_encap_t* pe;
137
138         for (i = 0; i < file_data->port_encaps->len; i++) {
139                 pe = g_ptr_array_index(file_data->port_encaps,i);
140                 
141                 if (pe->port_id == port_id)
142                         return pe->encap;
143         }
144         
145         /*g_warning("k12_get_encap: BUG: found no encapsulation for source 0x%.8x\n"
146                           "please report this to ethereal-dev@ethereal.com", port_id);*/
147         
148         return WTAP_ENCAP_USER15;
149 }
150
151
152
153 /*
154  * get_k12_hdr:  hunt for the next valid header in the file.
155  *   will return:
156  *      -2 on I/O errors
157  *              -1 at EOF
158  *       the lenght of the preamble (0 if none) if OK.
159  *
160  *   Record headers are 4 4byte words long,
161  *       - the first is the lenght of the record
162  *       - the second is the type of the record
163  *       - the third is the lenght of the frame in packet records
164  *               - the last is the source id to which it refers
165  *
166  *   Every about 0x2000 bytes up to 4 words are inserted in the file,
167  *   not being able yet to understand *exactly* how and where these
168  *   are inserted we need to scan the file for the next valid header.
169  *
170  */
171 gboolean get_k12_hdr(k12_record_hdr_t* hdr, wtap* wth, int* err, gchar **err_info) {
172         guint8 hdr_buf[0x14]; /* five 32bit "slots" */
173         guint32 magic;
174         guint i;
175         guint len;
176         
177         /*
178          * XXX: as most records are contiguous we could
179          * avoid hunting when not in the "risky zones".
180          *
181          * gboolean risky = ( (file_offset-0x210) % 0x2000) > 0x1f00 || 
182          *                    (file_offset-0x210) % 0x2000) < 0x0100   );
183          *
184          * We'll take the conservative approach and avoid trouble altogether.
185          */
186         
187         /* read the first three words inserting them from the second slot on */
188         
189         if ((len = file_read(hdr_buf + 0x4, 1, 0xC, wth->fh)) != 0xC) {
190                 if (len == 2) {
191                         if (hdr_buf[0x4] == 0xff && hdr_buf[0x5] == 0xff) {
192                                 return -1;
193                         }
194                 }
195                 
196                 *err = file_error(wth->fh);
197                 if (*err == 0)
198                         *err = WTAP_ERR_SHORT_READ;
199                 return -2;
200         }
201         
202         do {
203                 
204                 /*
205                  * XXX: The stuffing should be be at most 0x10.
206                  *
207                  * We do not know if the record types we know are all of them.
208                  *
209                  * Instead of failing we could  try to skip a record whose type we do
210                  * not know yet. In that case however it is possible that a "magic"
211                  * number appears in the record and unpredictable things would happen.
212                  * We won't try, we'll fail and ask for feedback.
213                  */
214                 if ( len > 0x20) {
215                         /*
216                         g_warning("get_k12_hdr: found more than 4 words of stuffing, this should not happen!\n"
217                                           "please report this issue to ethereal-dev@ethereal.com");
218                         */
219                         return -2;
220                 }
221                 
222                 /* read the next word into the last slot */
223                 if ( file_read( hdr_buf + K12_HDR_LEN, 1, 0x4, wth->fh) != 0x4 ) {
224                         *err = WTAP_ERR_SHORT_READ;
225                         *err_info = "record too short while reading .rf5 file";
226                         return -2;
227                 }
228                 
229                 len += 0x4;
230                 
231                 /* shift the buffer one word left */
232                 /* XXX: working with words this would be faster */
233                 for ( i = 0 ; i < 16 ; i++ )
234                         hdr_buf[i] = hdr_buf[i + 0x4]; 
235
236                 /* we'll be done if the second word is a magic number */
237                 magic = pntohl( hdr_buf + 0x4 );
238                 
239         } while (magic != K12_REC_PACKET &&
240                          magic != K12_REC_SRCDSC &&
241                          magic != K12_REC_UNK001 &&
242                          magic != K12_REC_UNK002 &&
243                          magic != K12_REC_UNK003 &&
244                          magic != K12_REC_UNK004 &&
245                          magic != K12_REC_UNK005 );
246         
247         hdr->len = 0x0000FFFF & pntohl( hdr_buf );      /* the first two bytes off the record len may contain junk */
248         hdr->type = magic;
249         hdr->frame_len = 0x0000FFFF & pntohl( hdr_buf + 0x8 );
250         hdr->port_id = pntohl( hdr_buf + 0xC );
251         
252         return len - K12_HDR_LEN;
253 }
254
255 static gboolean k12_read(wtap *wth, int *err, gchar **err_info, long *data_offset) {
256         guint64 ts;
257         guint8 b[8];
258         guint8* junk[0x1000];
259         k12_record_hdr_t hdr;
260         gint stuffing = -1;
261         
262         *data_offset = wth->data_offset;
263         
264         /* ignore the record if it isn't a packet */    
265         do {
266                 gint s;
267                 
268                 if (stuffing >= 0) {
269                         stuffing += hdr.len;
270                         
271                         /* skip the whole record */
272                         
273                         if ( file_read(junk,1, hdr.len - K12_HDR_LEN , wth->fh) != (gint) (hdr.len - K12_HDR_LEN) ) {
274                                 *err = WTAP_ERR_SHORT_READ;
275                                 *err_info = "record too short while reading .rf5 file";
276                                 return FALSE;           
277                         }
278                         
279                 } else if (stuffing < 0) {
280                         stuffing = 0;
281                 }
282                 
283                 switch ( s = get_k12_hdr(&hdr, wth, err, err_info) ) {
284                         case -1:
285                                 /* eof */
286                                 *err = 0;
287                                 *data_offset = wth->data_offset = wth->capture.k12->file_len;
288                                 return FALSE;
289                         case -2:
290                                 /* io_error */
291                                 return FALSE;
292                         default:
293                                 break;
294                 }
295                 
296                 stuffing += s;
297                 
298         } while ( hdr.type != K12_REC_PACKET
299                           || hdr.len < hdr.frame_len + 0x20 );
300         
301         wth->data_offset += stuffing + 0x10;
302         
303         if ( wth->file_encap == WTAP_ENCAP_PER_PACKET) {
304                 wth->phdr.pkt_encap = get_encap(wth->capture.k12,hdr.port_id);
305         } else {
306                 wth->phdr.pkt_encap = WTAP_ENCAP_USER0;
307         }
308         
309         /* XXX: is in there something useful in these 8 bytes ? */
310         if ( file_read(b,1,8,wth->fh) != 8 ) {
311                 *err = WTAP_ERR_SHORT_READ;
312                 *err_info = "record too short while reading .rf5 file";
313                 return FALSE; 
314         }
315         
316         wth->data_offset += 8;
317
318         
319         /* the next 8 bytes are the timestamp */
320         if ( file_read(b,1,8,wth->fh) != 8 ) {
321                 *err = WTAP_ERR_SHORT_READ;
322                 *err_info = "record too short while reading .rf5 file";
323                 return FALSE; 
324         }
325         
326         wth->data_offset += 8;
327         
328         ts = pntohll(b);
329         
330         wth->phdr.ts.tv_usec = (guint32) ( (ts % 2000000) / 2);
331         wth->phdr.ts.tv_sec = (guint32) ((ts / 2000000) + 631152000);
332         
333         wth->phdr.caplen = wth->phdr.len = hdr.frame_len;
334         
335         /* the frame */
336         buffer_assure_space(wth->frame_buffer, hdr.frame_len);
337         wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer), hdr.frame_len, wth->fh, err);
338         wth->data_offset += hdr.frame_len;
339
340         /* XXX: should we read to a junk buffer instead of seeking? */
341         /* XXX: is there useful stuff in the trailer? */
342         if ( file_read(junk,1, hdr.len - ( hdr.frame_len + 0x20) , wth->fh) != (gint) ( hdr.len - ( hdr.frame_len + 0x20)) ) {
343                 *err = WTAP_ERR_SHORT_READ;
344                 *err_info = "record too short while reading .rf5 file";
345                 return FALSE;           
346         }
347         
348         wth->data_offset += hdr.len - ( hdr.frame_len + 0x20);
349         
350         return TRUE;
351 }
352
353 static gboolean k12_seek_read(wtap *wth, long seek_off, union wtap_pseudo_header *pseudo_header _U_, guchar *pd, int length, int *err _U_, gchar **err_info _U_) {
354
355         if ( file_seek(wth->random_fh, seek_off+0x20, SEEK_SET, err) == -1)
356                 return FALSE;
357         
358         if ( file_read(pd, 1, length, wth->random_fh) != length) {
359                 *err = file_error(wth->random_fh);
360                 if (*err == 0)
361                         *err = WTAP_ERR_SHORT_READ;
362                 return FALSE;
363         }
364         
365         return TRUE;
366 }
367
368 static void destroy_k12_file_data(k12_t* file_data) {
369         guint i;
370         for (i =0; i<=file_data->stack_encap_p; i++) {
371                 if (file_data->stack_encap[i].name) {
372                         g_free(file_data->stack_encap[i].name);
373                         file_data->stack_encap[i].name = NULL;
374                 }
375         }
376         
377         if (file_data->port_encaps) {
378                 g_ptr_array_free(file_data->port_encaps,TRUE);
379         }
380         
381 }
382
383 static void k12_close(wtap *wth) {
384         destroy_k12_file_data(wth->capture.k12);
385 }
386
387 /*
388  * The first few records of a file contain a description of the file:
389  *   - the description of the sources (ports or circuits)
390  *   - some other useless or yet unknown data.
391  *
392  * After that we'll find the packet records. At the end sometimes we find
393  * some other (summary?) records.
394  */
395
396 int k12_open(wtap *wth, int *err, gchar **err_info) {
397         gchar read_buffer[0x1000];
398         k12_record_hdr_t hdr;
399         long offset = 0;
400         gchar* stack_file;
401         gchar* port_name;
402         guint port_name_len;
403         guint stuffing;
404         k12_t* file_data;
405
406         /*
407          *  let's check the magic number.
408          */
409         if ( file_read(read_buffer,1,8,wth->fh) != 8 ) {
410                 return -1;
411         } else {
412                 if ( memcmp(read_buffer,k12_file_magic,8) != 0 )
413                         return 0;
414         }
415
416         /* the lenght of the file is in the next 4byte word */
417         if ( file_read(read_buffer,1,4,wth->fh) != 4 ) {
418                 return -1;
419         } 
420         
421         file_data = g_malloc(sizeof(k12_t));
422         
423         file_data->stack_encap_p = 0;
424         file_data->port_encaps = g_ptr_array_new();
425         file_data->stack_encap = g_memdup(virgin_stack_encap,sizeof(virgin_stack_encap));
426         file_data->file_len = pntohl( read_buffer );
427         
428         /*
429          * we don't know yet what's in the file header
430          */
431         if (file_read(read_buffer,1,0x204,wth->fh) != 0x204 ) {
432                 destroy_k12_file_data(file_data);
433                 return -1;
434         }
435                 
436         wth->data_offset = offset = 0x210;
437         
438         /*
439          * start reading the records until we hit the first packet record
440          */
441         
442         do {
443                 if (offset > 0x10000) {
444                         /* too much to be ok. */
445                         return 0;
446                 }
447                 
448                 stuffing = get_k12_hdr(&hdr, wth, err, err_info);
449                 
450                 offset += stuffing;
451                                 
452                 if ( hdr.type == K12_REC_PACKET) {
453                         /*
454                          * we are at the first packet record, rewind and leave.
455                          */
456                         if (file_seek(wth->fh, -0x10, SEEK_CUR, err) == -1) {
457                                 destroy_k12_file_data(file_data);
458                                 return -1;
459                         }
460
461                         break;
462                 } else if (hdr.type == K12_REC_SRCDSC) {
463                         guint32 name_len;
464                         guint32 stack_len;
465                         gint read_len;
466                         
467                         if ( file_read( read_buffer, 1, 0x14, wth->fh) != 0x14 ) {
468                                 *err = WTAP_ERR_SHORT_READ;
469                                 return FALSE;
470                         }
471                         
472                         name_len = pntohs( read_buffer + 0x10 );
473                         stack_len = pntohs( read_buffer + 0x12 );
474                         
475
476                         read_len = hdr.len - (0x10 + 0x14 + name_len + stack_len);
477                         
478                         if (read_len > 0) {
479                                 /* skip the still unknown part */
480                                 if (file_read(read_buffer,1, read_len,wth->fh) != read_len ) {
481                                         destroy_k12_file_data(file_data);
482                                         *err = WTAP_ERR_SHORT_READ;
483                                         return -1;
484                                 }
485                         } else if (read_len < 0) {
486                                 destroy_k12_file_data(file_data);
487                                 *err = WTAP_ERR_BAD_RECORD;
488                                 return -1;
489                         }
490                         
491                         /* the rest of the record contains two null terminated strings:
492                                 the source label and the "stack" filename */
493                         if ( file_read(read_buffer, 1, name_len, wth->fh) != (int)name_len ) {
494                                 destroy_k12_file_data(file_data);
495                                 *err = WTAP_ERR_SHORT_READ;
496                                 *err_info = "record too short while reading .rf5 file";
497                                 return -1;
498                         }
499                         
500                         port_name = g_strndup(read_buffer,stack_len);
501
502                         if ( file_read(read_buffer, 1, stack_len, wth->fh) != (int)stack_len ) {
503                                 destroy_k12_file_data(file_data);
504                                 *err = WTAP_ERR_SHORT_READ;
505                                 *err_info = "record too short while reading .rf5 file";
506                                 return -1;
507                         }
508                         
509                         stack_file =g_strndup(read_buffer,stack_len);
510                                                 
511                         if (choose_encap(file_data,hdr.port_id,stack_file) == WTAP_NUM_ENCAP_TYPES ) {
512                                 destroy_k12_file_data(file_data);
513                                 /* more encapsulation types than we can handle */
514                                 return 0;
515                         }
516                         
517                         offset += hdr.len;
518                         continue;
519                 } else {
520                         /* we don't need these other fellows */
521                         
522                         if (file_read(read_buffer,1, hdr.len - K12_HDR_LEN, wth->fh) != (int) hdr.len - K12_HDR_LEN ) {
523                                 destroy_k12_file_data(file_data);
524                                 return -1;
525                         }
526                         
527                         offset += hdr.len;
528                         
529                         continue;
530                 }
531         } while(1);
532         
533         wth->data_offset = offset;
534         wth->file_type = WTAP_FILE_K12;
535         wth->snapshot_length = 0;
536         wth->subtype_read = k12_read;
537         wth->subtype_seek_read = k12_seek_read;
538         wth->subtype_close = k12_close;
539         wth->capture.k12 = file_data;
540         
541         /* if we use just one encapsulation for all the file
542                 we will use that for the whole file so we can
543                 use more formats to save to */
544         
545         if (file_data->port_encaps->len == 1) {
546                 wth->file_encap = ((k12_stack_encap_t*)g_ptr_array_index(file_data->port_encaps,0))->encap;
547         } else {
548                 wth->file_encap = WTAP_ENCAP_PER_PACKET;
549         }
550         
551         return 1;
552 }
553