The "file types" we have are actually combinations of types and
[metze/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.org>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #include "config.h"
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #include "wtap-int.h"
34 #include "wtap.h"
35 #include "file_wrappers.h"
36 #include "buffer.h"
37 #include "k12.h"
38
39 #include <wsutil/str_util.h>
40
41 /*
42  * See
43  *
44  *  http://www2.tek.com/cmswpt/madownload.lotr?ct=MA&cs=mpm&ci=11284&lc=EN
45  *
46  * for some information about the file format.  You may have to fill in
47  * a form to download the document ("Recored File API Programmer Manual").
48  *
49  * Unfortunately, it describes an API that delivers records from an rf5
50  * file, not the raw format of an rf5 file, so, while it gives the formats
51  * of the records with various types, it does not indicate how those records
52  * are stored in the file.
53  */
54
55 /* #define DEBUG_K12 */
56 #ifdef DEBUG_K12
57 #include <stdio.h>
58 #include <ctype.h>
59 #include <stdarg.h>
60 #include <wsutil/file_util.h>
61
62 FILE* dbg_out = NULL;
63 char* env_file = NULL;
64
65 static unsigned int debug_level = 0;
66
67 void k12_fprintf(char* fmt, ...) {
68     va_list ap;
69
70     va_start(ap,fmt);
71     vfprintf(dbg_out, fmt, ap);
72     va_end(ap);
73 }
74
75 #define CAT(a,b) a##b
76 #define K12_DBG(level,args) do { if (level <= debug_level) { \
77         fprintf(dbg_out,"%s:%d: ",CAT(__FI,LE__),CAT(__LI,NE__)); \
78         k12_fprintf args ; \
79         fprintf(dbg_out,"\n"); \
80 } } while(0)
81
82 void k12_hexdump(guint level, gint64 offset, char* label, unsigned char* b, unsigned int len) {
83     static const char* c2t[] = {
84         "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f",
85         "10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f",
86         "20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f",
87         "30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f",
88         "40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f",
89         "50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f",
90         "60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f",
91         "70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f",
92         "80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f",
93         "90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f",
94         "a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af",
95         "b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf",
96         "c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf",
97         "d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df",
98         "e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef",
99         "f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff"
100     };
101     unsigned int i;
102
103     if (debug_level < level) return;
104
105     fprintf(dbg_out,"%s(%.8" G_GINT64_MODIFIER "x,%.4x): ",label,offset,len);
106
107     for (i=0 ; i<len ; i++) {
108
109         if (!(i%32))
110             fprintf(dbg_out,"\n");
111         else if (!(i%4))
112             fprintf(dbg_out," ");
113
114         fprintf(dbg_out, "%s", c2t[b[i]]);
115     }
116
117         fprintf(dbg_out,"\n");
118 }
119
120 #define K12_HEXDMP(x,a,b,c,d) k12_hexdump(x,a,b,c,d)
121
122 #else
123 #define K12_DBG(level,args) (void)0
124 #define K12_HEXDMP(x,a,b,c,d)
125 #endif
126
127
128
129 /*
130  * the 32 bits .rf5 file contains:
131  *  an 8 byte magic number
132  *  32bit length
133  *  32bit number of records
134  *  other 0x200 bytes bytes of uncharted territory
135  *     1 or more copies of the num_of_records in there
136  *  the records whose first 32bits word is the length
137  *     they are stuffed by one to four words every 0x2000 bytes
138  *  and a 2 byte terminator FFFF
139  */
140
141 static const guint8 k12_file_magic[] = { 0x00, 0x00, 0x02, 0x00 ,0x12, 0x05, 0x00, 0x10 };
142
143 typedef struct {
144     guint32 file_len;
145     guint32 num_of_records; /* XXX: not sure about this */
146
147     GHashTable* src_by_id; /* k12_srcdsc_recs by input */
148     GHashTable* src_by_name; /* k12_srcdsc_recs by stack_name */
149
150     Buffer extra_info; /* Buffer to hold per packet extra information */
151 } k12_t;
152
153 typedef struct _k12_src_desc_t {
154     guint32 input;
155     guint32 input_type;
156     gchar* input_name;
157     gchar* stack_file;
158     k12_input_info_t input_info;
159 } k12_src_desc_t;
160
161
162 /*
163  * According to the Tektronix documentation, this value is a combination of
164  * a "group" code and a "type" code, with both being 2-byte values and
165  * with the "group" code followe by the "type" code.  The "group" values
166  * are:
167  *
168  *      0x0001 - "data event"
169  *      0x0002 - "text or L1 event"
170  *      0x0007 - "configuration event"
171  *
172  * and the "type" values are:
173  *
174  *  data events:
175  *      0x0020 - "frame" (i.e., "an actual packet")
176  *      0x0021 - "transparent frame"
177  *      0x0022 - "bit data (TRAU frame)"
178  *      0x0024 - "used to mark the frame which is a fragment"
179  *      0x0026 - "used to mark the frame which is a fragment"
180  *      0x0028 - "used to mark the frame which is generated by the LSA"
181  *      0x002A - "used to mark the frame which is generated by the LSA"
182  *
183  *  text or L1 events:
184  *      0x0030 - "text event"
185  *      0x0031 - "L1 event"
186  *      0x0032 - "L1 event (BAI)"
187  *      0x0033 - "L1 event (VX)"
188  *
189  *  configuration events:
190  *      0x0040 - Logical Data Source configuration event
191  *      0x0041 - Logical Link configuration event
192  */
193 /* so far we've seen these types of records */
194 #define K12_REC_PACKET        0x00010020 /* an actual packet */
195 #define K12_REC_SRCDSC        0x00070041 /* port-stack mapping + more, the key of the whole thing */
196 #define K12_REC_SCENARIO      0x00070040 /* what appears as the window's title */
197 #define K12_REC_STK_FILE      0x00070042 /* a dump of an stk file */
198 #define K12_REC_SRCDSC2       0x00070043 /* another port-stack mapping */
199 #define K12_REC_TEXT          0x00070044 /* a string containing something with a grammar (conditions/responses?) */
200 #define K12_REC_START         0x00020030 /* a string containing human readable start time  */
201 #define K12_REC_STOP          0x00020031 /* a string containing human readable stop time */
202
203 /*
204  * According to the Tektronix documentation, packets, i.e. "data events",
205  * have several different group/type values, which differ in the last
206  * nibble of the type code.  For now, we just mask that nibble off; the
207  * format of the items are different, so we might have to treat different
208  * data event types differently.
209  */
210 #define K12_MASK_PACKET       0xfffffff0
211
212 /* offsets of elements in the records */
213 #define K12_RECORD_LEN         0x0 /* uint32, in bytes */
214 #define K12_RECORD_TYPE        0x4 /* uint32, see above */
215 #define K12_RECORD_FRAME_LEN   0x8 /* uint32, in bytes */
216 #define K12_RECORD_SRC_ID      0xc /* uint32 */
217
218 /*
219  * Some records from K15 files have a port ID of an undeclared
220  * interface which happens to be the only one with the first byte changed.
221  * It is still unknown how to recognize when this happens.
222  * If the lookup of the interface record fails we'll mask it
223  * and retry.
224  */
225 #define K12_RECORD_SRC_ID_MASK 0x00ffffff
226
227 /* elements of packet records */
228 #define K12_PACKET_TIMESTAMP  0x18 /* int64 (8b) representing 1/2us since 01-01-1990 Z00:00:00 */
229
230 #define K12_PACKET_FRAME      0x20 /* start of the actual frame in the record */
231
232 #define K12_PACKET_OFFSET_VP  0x08 /* 2 bytes, big endian */
233 #define K12_PACKET_OFFSET_VC  0x0a /* 2 bytes, big endian */
234 #define K12_PACKET_OFFSET_CID 0x0c /* 1 byte */
235
236 /* elements of the source description records */
237 #define K12_SRCDESC_COLOR_FOREGROUND 0x12 /* 1 byte */
238 #define K12_SRCDESC_COLOR_BACKGROUND 0x13 /* 1 byte */
239
240 #define K12_SRCDESC_PORT_TYPE  0x1a   /* 1 byte */
241 #define K12_SRCDESC_EXTRALEN   0x1e   /* uint16, big endian */
242 #define K12_SRCDESC_NAMELEN    0x20   /* uint16, big endian */
243 #define K12_SRCDESC_STACKLEN   0x22   /* uint16, big endian */
244
245 #define K12_SRCDESC_EXTRATYPE  0x24   /* uint32, big endian */
246 #define K12_SRCDESC_ATM_VPI    0x38   /* uint16, big endian */
247 #define K12_SRCDESC_ATM_VCI    0x3a   /* uint16, big endian */
248
249 #define K12_SRCDESC_ATM_AAL    0x3c    /* 1 byte */
250 #define K12_SRCDESC_DS0_MASK   0x3c    /* 1 byte */
251
252
253 /*
254  * get_record: Get the next record into a buffer
255  *   Every about 0x2000 bytes 0x10 bytes are inserted in the file,
256  *   even in the middle of a record.
257  *   This reads the next record without the eventual 0x10 bytes.
258  *   returns the length of the record + the stuffing (if any)
259  *
260  *   Returns number of bytes read on success, 0 on EOF, -1 on error;
261  *   if -1 is returned, *err is set to the error indication and, for
262  *   errors where that's appropriate, *err_info is set to an additional
263  *   error string.
264  *
265  * XXX: works at most with 0x1FFF bytes per record
266  */
267 static gint get_record(guint8** bufferp, FILE_T fh, gint64 file_offset,
268                        int *err, gchar **err_info) {
269     static guint8* buffer = NULL;
270     static guint buffer_len = 0x2000 ;
271     guint bytes_read;
272     guint last_read;
273     guint left;
274     guint8 junk[0x14];
275     guint8* writep;
276 #ifdef DEBUG_K12
277     guint actual_len;
278 #endif
279
280     /* where the next unknown 0x10 bytes are stuffed to the file */
281     guint junky_offset = 0x2000 - (gint) ( (file_offset - 0x200) % 0x2000 );
282
283     K12_DBG(6,("get_record: ENTER: junky_offset=%" G_GINT64_MODIFIER "d, file_offset=%" G_GINT64_MODIFIER "d",junky_offset,file_offset));
284
285     /* no buffer is given, lets create it */
286     if (buffer == NULL) {
287         buffer = (guint8*)g_malloc(0x2000);
288         buffer_len = 0x2000;
289     }
290
291     *bufferp = buffer;
292
293     if  ( junky_offset == 0x2000 ) {
294         /* the length of the record is 0x10 bytes ahead from we are reading */
295         bytes_read = file_read(junk,0x14,fh);
296
297         if (bytes_read == 2 && junk[0] == 0xff && junk[1] == 0xff) {
298             K12_DBG(1,("get_record: EOF"));
299             return 0;
300         } else if ( bytes_read < 0x14 ){
301             K12_DBG(1,("get_record: SHORT READ OR ERROR"));
302             *err = file_error(fh, err_info);
303             if (*err == 0) {
304                 *err = WTAP_ERR_SHORT_READ;
305             }
306             return -1;
307         }
308
309         memcpy(buffer,&(junk[0x10]),4);
310     } else {
311         /* the length of the record is right where we are reading */
312         bytes_read = file_read(buffer, 0x4, fh);
313
314         if (bytes_read == 2 && buffer[0] == 0xff && buffer[1] == 0xff) {
315             K12_DBG(1,("get_record: EOF"));
316             return 0;
317         } else if ( bytes_read != 0x4 ) {
318             K12_DBG(1,("get_record: SHORT READ OR ERROR"));
319             *err = file_error(fh, err_info);
320             if (*err == 0) {
321                 *err = WTAP_ERR_SHORT_READ;
322             }
323             return -1;
324         }
325     }
326
327     left = pntohl(buffer);
328 #ifdef DEBUG_K12
329     actual_len = left;
330 #endif
331     junky_offset -= 0x4;
332
333     K12_DBG(5,("get_record: GET length=%u",left));
334
335     /* XXX - Is WTAP_MAX_PACKET_SIZE */
336     if (left < 4 || left > WTAP_MAX_PACKET_SIZE) {
337         K12_DBG(1,("get_record: Invalid GET length=%u",left));
338         *err = WTAP_ERR_BAD_FILE;
339         *err_info = g_strdup_printf("get_record: Invalid GET length=%u",left);
340         return -1;
341     }
342
343     while (left > buffer_len) *bufferp = buffer = (guint8*)g_realloc(buffer,buffer_len*=2);
344
345     writep = buffer + 4;
346     left -= 4;
347
348     do {
349         K12_DBG(6,("get_record: looping left=%d junky_offset=%" G_GINT64_MODIFIER "d",left,junky_offset));
350
351         if (junky_offset > left) {
352             bytes_read += last_read = file_read(writep, left, fh);
353
354             if ( last_read != left ) {
355                 K12_DBG(1,("get_record: SHORT READ OR ERROR"));
356                 *err = file_error(fh, err_info);
357                 if (*err == 0) {
358                     *err = WTAP_ERR_SHORT_READ;
359                 }
360                 return -1;
361             } else {
362                 K12_HEXDMP(5,file_offset, "GOT record", buffer, actual_len);
363                 return bytes_read;
364             }
365         } else {
366             bytes_read += last_read = file_read(writep, junky_offset, fh);
367
368             if ( last_read != junky_offset ) {
369                 K12_DBG(1,("get_record: SHORT READ OR ERROR, read=%d expected=%d",last_read, junky_offset));
370                 *err = file_error(fh, err_info);
371                 if (*err == 0) {
372                     *err = WTAP_ERR_SHORT_READ;
373                 }
374                 return -1;
375             }
376
377             writep += last_read;
378
379             bytes_read += last_read = file_read(junk, 0x10, fh);
380
381             if ( last_read != 0x10 ) {
382                 K12_DBG(1,("get_record: SHORT READ OR ERROR"));
383                 *err = file_error(fh, err_info);
384                 if (*err == 0) {
385                     *err = WTAP_ERR_SHORT_READ;
386                 }
387                 return -1;
388             }
389
390             left -= junky_offset;
391             junky_offset = 0x2000;
392         }
393
394     } while(left);
395
396     K12_HEXDMP(5,file_offset, "GOT record", buffer, actual_len);
397     return bytes_read;
398 }
399
400 static void
401 process_packet_data(struct wtap_pkthdr *phdr, Buffer *target, guint8 *buffer,
402                     gint len, k12_t *k12)
403 {
404     guint64 ts;
405     guint32 length;
406     guint32 extra_len;
407     guint32 src_id;
408     k12_src_desc_t* src_desc;
409
410     phdr->presence_flags = WTAP_HAS_TS;
411
412     ts = pntohll(buffer + K12_PACKET_TIMESTAMP);
413
414     phdr->ts.secs = (guint32) ((ts / 2000000) + 631152000);
415     phdr->ts.nsecs = (guint32) ( (ts % 2000000) * 500 );
416
417     length = pntohl(buffer + K12_RECORD_FRAME_LEN) & 0x00001FFF;
418     phdr->len = phdr->caplen = length;
419
420     buffer_assure_space(target, length);
421     memcpy(buffer_start_ptr(target), buffer + K12_PACKET_FRAME, length);
422
423     /* extra information need by some protocols */
424     extra_len = len - K12_PACKET_FRAME - length;
425     buffer_assure_space(&(k12->extra_info), extra_len);
426     memcpy(buffer_start_ptr(&(k12->extra_info)),
427            buffer + K12_PACKET_FRAME + length, extra_len);
428     phdr->pseudo_header.k12.extra_info = (guint8*)buffer_start_ptr(&(k12->extra_info));
429     phdr->pseudo_header.k12.extra_length = extra_len;
430
431     src_id = pntohl(buffer + K12_RECORD_SRC_ID);
432     K12_DBG(5,("process_packet_data: src_id=%.8x",src_id));
433     phdr->pseudo_header.k12.input = src_id;
434
435     if ( ! (src_desc = (k12_src_desc_t*)g_hash_table_lookup(k12->src_by_id,GUINT_TO_POINTER(src_id))) ) {
436         /*
437          * Some records from K15 files have a port ID of an undeclared
438          * interface which happens to be the only one with the first byte changed.
439          * It is still unknown how to recognize when this happens.
440          * If the lookup of the interface record fails we'll mask it
441          * and retry.
442          */
443         src_desc = (k12_src_desc_t*)g_hash_table_lookup(k12->src_by_id,GUINT_TO_POINTER(src_id&K12_RECORD_SRC_ID_MASK));
444     }
445
446     if (src_desc) {
447         K12_DBG(5,("process_packet_data: input_name='%s' stack_file='%s' type=%x",src_desc->input_name,src_desc->stack_file,src_desc->input_type));
448         phdr->pseudo_header.k12.input_name = src_desc->input_name;
449         phdr->pseudo_header.k12.stack_file = src_desc->stack_file;
450         phdr->pseudo_header.k12.input_type = src_desc->input_type;
451
452         switch(src_desc->input_type) {
453             case K12_PORT_ATMPVC:
454                 if ((long)(K12_PACKET_FRAME + length + K12_PACKET_OFFSET_CID) < len) {
455                     phdr->pseudo_header.k12.input_info.atm.vp =  pntohs(buffer + K12_PACKET_FRAME + length + K12_PACKET_OFFSET_VP);
456                     phdr->pseudo_header.k12.input_info.atm.vc =  pntohs(buffer + K12_PACKET_FRAME + length + K12_PACKET_OFFSET_VC);
457                     phdr->pseudo_header.k12.input_info.atm.cid =  *((unsigned char*)(buffer + K12_PACKET_FRAME + length + K12_PACKET_OFFSET_CID));
458                     break;
459                 }
460                 /* Fall through */
461             default:
462                 memcpy(&(phdr->pseudo_header.k12.input_info),&(src_desc->input_info),sizeof(src_desc->input_info));
463                 break;
464         }
465     } else {
466         K12_DBG(5,("process_packet_data: NO SRC_RECORD FOUND"));
467
468         memset(&(phdr->pseudo_header.k12),0,sizeof(phdr->pseudo_header.k12));
469         phdr->pseudo_header.k12.input_name = "unknown port";
470         phdr->pseudo_header.k12.stack_file = "unknown stack file";
471     }
472
473     phdr->pseudo_header.k12.input = src_id;
474     phdr->pseudo_header.k12.stuff = k12;
475 }
476
477 static gboolean k12_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset) {
478     k12_t *k12 = (k12_t *)wth->priv;
479     k12_src_desc_t* src_desc;
480     guint8* buffer = NULL;
481     gint64 offset;
482     gint len;
483     guint32 type;
484     guint32 src_id;
485
486     offset = file_tell(wth->fh);
487
488     /* ignore the record if it isn't a packet */
489     do {
490         K12_DBG(5,("k12_read: offset=%i",offset));
491
492         *data_offset = offset;
493
494         len = get_record(&buffer, wth->fh, offset, err, err_info);
495
496         if (len < 0) {
497             return FALSE;
498         } else if (len == 0) {
499             *err = 0;
500             return FALSE;
501         }
502
503         type = pntohl(buffer + K12_RECORD_TYPE);
504         src_id = pntohl(buffer + K12_RECORD_SRC_ID);
505
506
507         if ( ! (src_desc = (k12_src_desc_t*)g_hash_table_lookup(k12->src_by_id,GUINT_TO_POINTER(src_id))) ) {
508             /*
509              * Some records from K15 files have a port ID of an undeclared
510              * interface which happens to be the only one with the first byte changed.
511              * It is still unknown how to recognize when this happens.
512              * If the lookup of the interface record fails we'll mask it
513              * and retry.
514              */
515             src_desc = (k12_src_desc_t*)g_hash_table_lookup(k12->src_by_id,GUINT_TO_POINTER(src_id&K12_RECORD_SRC_ID_MASK));
516         }
517
518         K12_DBG(5,("k12_read: record type=%x src_id=%x",type,src_id));
519
520         offset += len;
521
522     } while ( ((type & K12_MASK_PACKET) != K12_REC_PACKET) || !src_id || !src_desc );
523
524     process_packet_data(&wth->phdr, wth->frame_buffer, buffer, len, k12);
525
526     return TRUE;
527 }
528
529
530 static gboolean k12_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *phdr, Buffer *buf, int length _U_, int *err, gchar **err_info) {
531     k12_t *k12 = (k12_t *)wth->priv;
532     guint8* buffer;
533     gint len;
534
535     K12_DBG(5,("k12_seek_read: ENTER"));
536
537     if ( file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
538         K12_DBG(5,("k12_seek_read: SEEK ERROR"));
539         return FALSE;
540     }
541
542     len = get_record(&buffer, wth->random_fh, seek_off, err, err_info);
543     if (len < 0) {
544         K12_DBG(5,("k12_seek_read: READ ERROR"));
545         return FALSE;
546     }
547     if (len < 1) {
548         K12_DBG(5,("k12_seek_read: SHORT READ"));
549         *err = WTAP_ERR_SHORT_READ;
550         return FALSE;
551     }
552
553     process_packet_data(phdr, buf, buffer, len, k12);
554
555     K12_DBG(5,("k12_seek_read: DONE OK"));
556
557     return TRUE;
558 }
559
560
561 static k12_t* new_k12_file_data(void) {
562     k12_t* fd = g_new(k12_t,1);
563
564     fd->file_len = 0;
565     fd->num_of_records = 0;
566     fd->src_by_name = g_hash_table_new(g_str_hash,g_str_equal);
567     fd->src_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
568
569     buffer_init(&(fd->extra_info), 100);
570
571     return fd;
572 }
573
574 static gboolean destroy_srcdsc(gpointer k _U_, gpointer v, gpointer p _U_) {
575     k12_src_desc_t* rec = (k12_src_desc_t*)v;
576
577     g_free(rec->input_name);
578     g_free(rec->stack_file);
579     g_free(rec);
580
581     return TRUE;
582 }
583
584 static void destroy_k12_file_data(k12_t* fd) {
585     g_hash_table_destroy(fd->src_by_id);
586     g_hash_table_foreach_remove(fd->src_by_name,destroy_srcdsc,NULL);
587     g_hash_table_destroy(fd->src_by_name);
588     buffer_free(&(fd->extra_info));
589     g_free(fd);
590 }
591
592 static void k12_close(wtap *wth) {
593     k12_t *k12 = (k12_t *)wth->priv;
594
595     destroy_k12_file_data(k12);
596     wth->priv = NULL;   /* destroy_k12_file_data freed it */
597 #ifdef DEBUG_K12
598     K12_DBG(5,("k12_close: CLOSED"));
599     if (env_file) fclose(dbg_out);
600 #endif
601 }
602
603
604 int k12_open(wtap *wth, int *err, gchar **err_info) {
605     k12_src_desc_t* rec;
606     guint8 header_buffer[0x200];
607     guint8* read_buffer;
608     guint32 type;
609     long offset;
610     long len;
611     guint32 rec_len;
612     guint32 extra_len;
613     guint32 name_len;
614     guint32 stack_len;
615     guint i;
616     k12_t* file_data;
617
618 #ifdef DEBUG_K12
619     gchar* env_level = getenv("K12_DEBUG_LEVEL");
620     env_file = getenv("K12_DEBUG_FILENAME");
621     if ( env_file ) {
622         dbg_out = ws_fopen(env_file,"w");
623         if (dbg_out == NULL) {
624                 dbg_out = stderr;
625                 K12_DBG(1,("unable to open K12 DEBUG FILENAME for writing!  Logging to standard error"));
626         }
627     else
628         dbg_out = stderr;
629     if ( env_level ) debug_level = strtoul(env_level,NULL,10);
630     K12_DBG(1,("k12_open: ENTER debug_level=%u",debug_level));
631 #endif
632
633     if ( file_read(header_buffer,0x200,wth->fh) != 0x200 ) {
634         K12_DBG(1,("k12_open: FILE HEADER TOO SHORT OR READ ERROR"));
635         *err = file_error(wth->fh, err_info);
636         if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
637             return -1;
638         }
639         return 0;
640     } else {
641         if ( memcmp(header_buffer,k12_file_magic,8) != 0 ) {
642             K12_DBG(1,("k12_open: BAD MAGIC"));
643             return 0;
644         }
645     }
646
647     offset = 0x200;
648
649     file_data = new_k12_file_data();
650
651     file_data->file_len = pntohl( header_buffer + 0x8);
652     file_data->num_of_records = pntohl( header_buffer + 0xC );
653
654     K12_DBG(5,("k12_open: FILE_HEADER OK: offset=%x file_len=%i records=%i",
655             offset,
656             file_data->file_len,
657             file_data->num_of_records ));
658
659     do {
660
661         len = get_record(&read_buffer, wth->fh, offset, err, err_info);
662
663         if ( len < 0 ) {
664             K12_DBG(1,("k12_open: BAD HEADER RECORD",len));
665             destroy_k12_file_data(file_data);
666             return -1;
667         }
668         if (len == 0) {
669             K12_DBG(1,("k12_open: BAD HEADER RECORD",len));
670             *err = WTAP_ERR_SHORT_READ;
671             destroy_k12_file_data(file_data);
672             return -1;
673         }
674
675
676         type = pntohl( read_buffer + K12_RECORD_TYPE );
677
678         if ( (type & K12_MASK_PACKET) == K12_REC_PACKET) {
679             /*
680              * we are at the first packet record, rewind and leave.
681              */
682             if (file_seek(wth->fh, offset, SEEK_SET, err) == -1) {
683                 destroy_k12_file_data(file_data);
684                 return -1;
685             }
686             K12_DBG(5,("k12_open: FIRST PACKET offset=%x",offset));
687             break;
688         } else if (type == K12_REC_SRCDSC || type == K12_REC_SRCDSC2 ) {
689             rec = g_new0(k12_src_desc_t,1);
690
691             rec_len = pntohl( read_buffer + K12_RECORD_LEN );
692             extra_len = pntohs( read_buffer + K12_SRCDESC_EXTRALEN );
693             name_len = pntohs( read_buffer + K12_SRCDESC_NAMELEN );
694             stack_len = pntohs( read_buffer + K12_SRCDESC_STACKLEN );
695
696             rec->input = pntohl( read_buffer + K12_RECORD_SRC_ID );
697
698             K12_DBG(5,("k12_open: INTERFACE RECORD offset=%x interface=%x",offset,rec->input));
699
700             if (name_len == 0 || stack_len == 0
701                 || 0x20 + extra_len + name_len + stack_len > rec_len ) {
702                 g_free(rec);
703                 K12_DBG(5,("k12_open: failed (name_len == 0 || stack_len == 0 "
704                         "|| 0x20 + extra_len + name_len + stack_len > rec_len)  extra_len=%i name_len=%i stack_len=%i"));
705                 destroy_k12_file_data(file_data);
706                 return 0;
707             }
708
709             if (extra_len)
710                 switch(( rec->input_type = pntohl( read_buffer + K12_SRCDESC_EXTRATYPE ) )) {
711                     case K12_PORT_DS0S:
712                         rec->input_info.ds0mask = 0x00000000;
713
714                         for (i = 0; i < 32; i++) {
715                             rec->input_info.ds0mask |= ( *(read_buffer + K12_SRCDESC_DS0_MASK + i) == 0xff ) ? 0x1<<(31-i) : 0x0;
716                         }
717
718                         break;
719                     case K12_PORT_ATMPVC:
720                         rec->input_info.atm.vp = pntohs( read_buffer + K12_SRCDESC_ATM_VPI );
721                         rec->input_info.atm.vc = pntohs( read_buffer + K12_SRCDESC_ATM_VCI );
722                         break;
723                     default:
724                         break;
725                 }
726             else {    /* Record viewer generated files
727                    don't have this information */
728                 if (read_buffer[K12_SRCDESC_PORT_TYPE] >= 0x14
729                     && read_buffer[K12_SRCDESC_PORT_TYPE] <= 0x17)
730                     /* For ATM2_E1DS1, ATM2_E3DS3,
731                        ATM2_STM1EL and ATM2_STM1OP */
732                     rec->input_type = K12_PORT_ATMPVC;
733             }
734
735             /* XXX - this is assumed, in a number of places (not just in the
736                ascii_strdown_inplace() call below) to be null-terminated;
737                is that guaranteed (even with a corrupt file)?
738                Obviously not, as a corrupt file could contain anything
739                here; the Tektronix document says the strings "must end
740                with \0", but a bad file could fail to add the \0. */
741             rec->input_name = (gchar *)g_memdup(read_buffer + K12_SRCDESC_EXTRATYPE + extra_len, name_len);
742             rec->stack_file = (gchar *)g_memdup(read_buffer + K12_SRCDESC_EXTRATYPE + extra_len + name_len, stack_len);
743
744             ascii_strdown_inplace (rec->stack_file);
745
746             g_hash_table_insert(file_data->src_by_id,GUINT_TO_POINTER(rec->input),rec);
747             g_hash_table_insert(file_data->src_by_name,rec->stack_file,rec);
748
749             offset += len;
750             continue;
751         } else {
752             offset += len;
753             continue;
754         }
755     } while(1);
756
757     wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_K12;
758     wth->file_encap = WTAP_ENCAP_K12;
759     wth->snapshot_length = 0;
760     wth->subtype_read = k12_read;
761     wth->subtype_seek_read = k12_seek_read;
762     wth->subtype_close = k12_close;
763     wth->priv = (void *)file_data;
764     wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
765
766     return 1;
767 }
768
769 typedef struct {
770         guint32 file_len;
771         guint32 num_of_records;
772         guint32 file_offset;
773 } k12_dump_t;
774
775 int k12_dump_can_write_encap(int encap) {
776
777     if (encap == WTAP_ENCAP_PER_PACKET)
778         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
779
780     if (encap != WTAP_ENCAP_K12)
781         return WTAP_ERR_UNSUPPORTED_ENCAP;
782
783     return 0;
784 }
785
786 static const gchar dumpy_junk[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
787
788 static gboolean k12_dump_record(wtap_dumper *wdh, guint32 len,  guint8* buffer, int *err_p) {
789     k12_dump_t *k12 = (k12_dump_t *)wdh->priv;
790     guint32 junky_offset = (0x2000 - ( (k12->file_offset - 0x200) % 0x2000 )) % 0x2000;
791
792     if (len > junky_offset) {
793         if (junky_offset) {
794             if (! wtap_dump_file_write(wdh, buffer, junky_offset, err_p))
795                 return FALSE;
796         }
797         if (! wtap_dump_file_write(wdh, dumpy_junk, 0x10, err_p))
798             return FALSE;
799
800         if (! wtap_dump_file_write(wdh, buffer+junky_offset, len - junky_offset, err_p))
801             return FALSE;
802
803         k12->file_offset += len + 0x10;
804     } else {
805         if (! wtap_dump_file_write(wdh, buffer, len, err_p))
806             return FALSE;
807         k12->file_offset += len;
808     }
809
810     k12->num_of_records++;
811     return TRUE;
812 }
813
814 static void k12_dump_src_setting(gpointer k _U_, gpointer v, gpointer p) {
815     k12_src_desc_t* src_desc = (k12_src_desc_t*)v;
816     wtap_dumper *wdh = (wtap_dumper *)p;
817     guint32 len;
818     guint offset;
819     guint i;
820     int   errxxx; /* dummy */
821
822     union {
823         guint8 buffer[0x2000];
824
825         struct {
826             guint32 len;
827             guint32 type;
828             guint32 unk32_1;
829             guint32 input;
830
831             guint16 unk32_2;
832             guint16 color;
833             guint32 unk32_3;
834             guint32 unk32_4;
835             guint16 unk16_1;
836             guint16 extra_len;
837
838             guint16 name_len;
839             guint16 stack_len;
840
841             struct {
842                 guint32 type;
843
844                 union {
845                     struct {
846                         guint32 unk32;
847                         guint8 mask[32];
848                     } ds0mask;
849
850                     struct {
851                         guint8 unk_data[0x10];
852                         guint16 vp;
853                         guint16 vc;
854                     } atm;
855
856                     guint32 unk;
857                 } desc;
858             } extra;
859         } record;
860     } obj;
861
862     obj.record.type = g_htonl(K12_REC_SRCDSC);
863     obj.record.unk32_1 = g_htonl(0x00000001);
864     obj.record.input = g_htonl(src_desc->input);
865
866     obj.record.unk32_2 = g_htons(0x0000);
867     obj.record.color = g_htons(0x060f);
868     obj.record.unk32_3 = g_htonl(0x00000003);
869     switch (src_desc->input_type) {
870         case K12_PORT_ATMPVC:
871             obj.record.unk32_4 = g_htonl(0x01001400);
872             break;
873         default:
874             obj.record.unk32_4 = g_htonl(0x01000100);
875     }
876
877     obj.record.unk16_1 = g_htons(0x0000);
878     obj.record.name_len = (guint16) strlen(src_desc->input_name) + 1;
879     obj.record.stack_len = (guint16) strlen(src_desc->stack_file) + 1;
880
881     obj.record.extra.type = g_htonl(src_desc->input_type);
882
883     switch (src_desc->input_type) {
884         case K12_PORT_ATMPVC:
885             obj.record.extra_len = g_htons(0x18);
886             obj.record.extra.desc.atm.vp = g_htons(src_desc->input_info.atm.vp);
887             obj.record.extra.desc.atm.vc = g_htons(src_desc->input_info.atm.vc);
888             offset = 0x3c;
889             break;
890         case K12_PORT_DS0S:
891             obj.record.extra_len = g_htons(0x18);
892             for( i=0; i<32; i++ ) {
893                 obj.record.extra.desc.ds0mask.mask[i] =
894                 (src_desc->input_info.ds0mask & (1 << i)) ? 0xff : 0x00;
895             }
896             offset = 0x3c;
897             break;
898         default:
899             obj.record.extra_len = g_htons(0x08);
900             offset = 0x2c;
901             break;
902     }
903
904     memcpy(obj.buffer + offset,
905            src_desc->input_name,
906            obj.record.name_len);
907
908     memcpy(obj.buffer + offset + obj.record.name_len,
909            src_desc->stack_file,
910            obj.record.stack_len);
911
912     len = offset + obj.record.name_len + obj.record.stack_len;
913     len += (len % 4) ? 4 - (len % 4) : 0;
914
915     obj.record.len = g_htonl(len);
916     obj.record.name_len =  g_htons(obj.record.name_len);
917     obj.record.stack_len = g_htons(obj.record.stack_len);
918
919     k12_dump_record(wdh,len,obj.buffer, &errxxx); /* fwrite errs ignored: see k12_dump below */
920 }
921
922 static gboolean k12_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
923                          const guint8 *pd, int *err) {
924     const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
925     k12_dump_t *k12 = (k12_dump_t *)wdh->priv;
926     guint32 len;
927     union {
928         guint8 buffer[0x2000];
929         struct {
930             guint32 len;
931             guint32 type;
932             guint32 frame_len;
933             guint32 input;
934
935             guint32 datum_1;
936             guint32 datum_2;
937             guint64 ts;
938
939             guint8 frame[0x1fc0];
940         } record;
941     } obj;
942
943     if (k12->num_of_records == 0) {
944         k12_t* file_data = (k12_t*)pseudo_header->k12.stuff;
945         /* XXX: We'll assume that any fwrite errors in k12_dump_src_setting will    */
946         /*      repeat during the final k12_dump_record at the end of k12_dump      */
947         /*      (and thus cause an error return from k12_dump).                     */
948         /*      (I don't see a reasonably clean way to handle any fwrite errors     */
949         /*       encountered in k12_dump_src_setting).                              */
950         g_hash_table_foreach(file_data->src_by_id,k12_dump_src_setting,wdh);
951     }
952     obj.record.len = 0x20 + phdr->len;
953     obj.record.len += (obj.record.len % 4) ? 4 - obj.record.len % 4 : 0;
954
955     len = obj.record.len;
956
957     obj.record.len = g_htonl(obj.record.len);
958
959     obj.record.type = g_htonl(K12_REC_PACKET);
960     obj.record.frame_len = g_htonl(phdr->len);
961     obj.record.input = g_htonl(pseudo_header->k12.input);
962
963     obj.record.ts = GUINT64_TO_BE((((guint64)phdr->ts.secs - 631152000) * 2000000) + (phdr->ts.nsecs / 1000 * 2));
964
965     memcpy(obj.record.frame,pd,phdr->len);
966
967     return k12_dump_record(wdh,len,obj.buffer, err);
968 }
969
970 static const guint8 k12_eof[] = {0xff,0xff};
971
972 static gboolean k12_dump_close(wtap_dumper *wdh, int *err) {
973     k12_dump_t *k12 = (k12_dump_t *)wdh->priv;
974     union {
975         guint8 b[sizeof(guint32)];
976         guint32 u;
977     } d;
978
979     if (! wtap_dump_file_write(wdh, k12_eof, 2, err))
980         return FALSE;
981
982     if (wtap_dump_file_seek(wdh, 8, SEEK_SET, err) == -1) 
983         return FALSE;
984
985     d.u = g_htonl(k12->file_len);
986
987     if (! wtap_dump_file_write(wdh, d.b, 4, err))
988         return FALSE;
989
990     d.u = g_htonl(k12->num_of_records);
991
992     if (! wtap_dump_file_write(wdh, d.b, 4, err))
993         return FALSE;
994
995     return TRUE;
996 }
997
998
999 gboolean k12_dump_open(wtap_dumper *wdh, int *err) {
1000     k12_dump_t *k12;
1001
1002     if ( ! wtap_dump_file_write(wdh, k12_file_magic, 8, err)) {
1003         return FALSE;
1004     }
1005
1006     if (wtap_dump_file_seek(wdh, 0x200, SEEK_SET, err) == -1) 
1007         return FALSE;
1008
1009     wdh->subtype_write = k12_dump;
1010     wdh->subtype_close = k12_dump_close;
1011
1012     k12 = (k12_dump_t *)g_malloc(sizeof(k12_dump_t));
1013     wdh->priv = (void *)k12;
1014     k12->file_len = 0x200;
1015     k12->num_of_records = 0;
1016     k12->file_offset  = 0x200;
1017
1018     return TRUE;
1019 }
1020
1021