6f1b8ac5aa5531bb5e76131bd9f338fe83b7500d
[obnox/wireshark/wip.git] / wiretap / iseries.c
1 /* iseries.c
2  *
3  * $Id$
4  *
5  * Wiretap Library
6  * Copyright (c) 2011 by Martin Warnes <Martin_Warnes@uk.ibm.com>
7  *
8  * Based on toshiba.c and vms.c
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (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
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /*
26  * This module will read the contents of the iSeries (OS/400) Communication trace
27  * Both ASCII & Unicode formatted traces are supported.
28  *
29  * iSeries Comms traces consist of a header page and a subsequent number of packet records
30  *
31  * The header page contains details on the options set during running of the trace,
32  * currently the following options are a requirement for this module:
33  *
34  * 1. Object protocol = ETHERNET (Default)
35  * 2. ASCII or UNICODE file formats.
36  *
37  * The above can be acheived by passing option ASCII(*YES) with the trace command
38  *
39  */
40
41 /* iSeries header page
42
43  COMMUNICATIONS TRACE       Title: OS400 - OS400 trace               10/28/05  11:44:50                           Page:       1
44    Trace Description  . . . . . :   OS400 - OS400 trace
45    Configuration object . . . . :   ETH0
46    Type . . . . . . . . . . . . :   1            1=Line, 2=Network Interface
47                                                  3=Network server
48    Object protocol  . . . . . . :   ETHERNET
49    Start date/Time  . . . . . . :   10/28/05  11:43:00.341
50    End date/Time  . . . . . . . :   10/28/05  11:44:22.148
51    Bytes collected  . . . . . . :   11999
52    Buffer size  . . . . . . . . :   2048         kilobytes
53    Data direction . . . . . . . :   3            1=Sent, 2=Received, 3=Both
54    Stop on buffer full  . . . . :   Y            Y=Yes, N=No
55    Number of bytes to trace
56      Beginning bytes  . . . . . :   *MAX         Value, *CALC, *MAX
57      Ending bytes   . . . . . . :   *CALC        Value, *CALC
58    Controller name  . . . . . . :   *ALL         *ALL, name
59    Data representation  . . . . :   1            1=ASCII, 2=EBCDIC, 3=*CALC
60    Format SNA data only . . . . :   N            Y=Yes, N=No
61    Format RR, RNR commands  . . :   N            Y=Yes, N=No
62    Format TCP/IP data only  . . :   Y            Y=Yes, N=No
63      IP address . . . . . . . . :   *ALL             *ALL, address
64      IP address . . . . . . . . :   *ALL             *ALL, address
65      IP port  . . . . . . . . . :   *ALL             *ALL, IP port
66    Format UI data only  . . . . :   N            Y=Yes, N=No
67    Select Ethernet data . . . . :   3            1=802.3, 2=ETHV2, 3=Both
68    Format Broadcast data  . . . :   Y            Y=Yes, N=No
69 */
70
71 /* iSeries IPv4 formatted packet records consist of a header line identifying the packet number,direction,size,
72  * timestamp,source/destination MAC addresses and packet type.
73  *
74  * Thereafter there will be a formated display of the IP and TCP headers as well as a hex string dump
75  * of the headers themselves displayed in the the "IP Header" and "TCP header" fields.
76  *
77  * If the packet contains data this is displayed as 4 groups of 16 hex digits followed by an ASCII
78  * representaion of the data line.
79  *
80  * Information from the header line, IP header, TCP header and if available data lines are extracted
81  * by the module for displaying.
82  *
83  *
84  Record       Data    Record           Controller  Destination   Source        Frame
85  Number  S/R  Length  Timer            Name        MAC Address   MAC Address   Format
86  ------  ---  ------  ---------------  ----------  ------------  ------------  ------
87       8   S      145  11:43:59.82956               0006299C14AE  0006299C14FE   ETHV2   Type: 0800
88                       Frame Type :  IP          DSCP: 0   ECN: 00-NECT  Length:   145   Protocol: TCP         Datagram ID: 388B
89                                     Src Addr: 10.20.144.150       Dest Addr: 10.20.144.151       Fragment Flags: DON'T,LAST
90                       IP Header  :  45000091388B40004006CC860A1490960A149097
91                       IP Options :  NONE
92                       TCP  . . . :  Src Port:  6006,Unassigned    Dest Port: 35366,Unassigned
93                                     SEQ Number:  2666470699 ('9EEF1D2B'X)  ACK Number: 2142147535 ('7FAE93CF'X)
94                                     Code Bits: ACK PSH                  Window: 32648  TCP Option: NO OP
95                       TCP Header :  17768A269EEF1D2B7FAE93CF80187F885B5600000101080A0517E0F805166DE0
96          Data . . . . . :  5443503200020010 0000004980000000  B800000080470103 01001E0000002000   *TCP2.......I*...*...*G........ .*
97                            002F010080000004 0300800700C00600  4002008000000304 00800000060FB067   *./..*.....*..*..@..*.....*....*G*
98                            FC276228786B3EB0 EF34F5F1D27EF8DF  20926820E7B322AA 739F1FB20D         **'B(XK>**4***.** *H **"*S*.*.   *
99 */
100
101 /* iSeries IPv6 formatted traces are similar to the IPv4 version above but data is no longer output as 4 hex sections
102 *
103
104 Record       Data      Record                       Destination   Source        Frame
105 Number  S/R  Length    Timer                        MAC Address   MAC Address   Format
106 ------  ---  ------    ------------                 ------------  ------------  ------
107    218   S     1488    15:01:14.389                 0011BC358680  00096B6BD918   ETHV2  Type: 86DD
108                       IPv6   Data:  Ver: 06                      Traffic Class: 00            Flow Label: 000000
109                                     Payload Length:  1448        Next Header: 06,TCP          Hop Limit:    64
110                                     Src Addr:   fd00:0:0:20f2::122
111                                     Dest Addr:  fd00:0:0:20a0::155
112                       IPv6 Header:  6000000005A80640FD000000000020F20000000000000122FD000000000020A0
113                                     0000000000000155
114                       TCP  . . . :  Src Port: 21246,Unassigned    Dest Port: 13601,Unassigned
115                                     SEQ Number:  2282300877 ('880925CD'X)  ACK Number: 3259003715 ('C2407343'X)
116                                     Code Bits: ACK                      Window: 65535  TCP Option: NO OP
117                       TCP Header :  52FE3521880925CDC24073438010FFFFCFBB00000101080A0E15127000237A08
118          Data . . . . . :  54435032000200140000061880000000ECBEB867F0000000004CE640E6C1D9D5       *TCP2........*...***g*....L*@*****
119                            C9D5C740E3C8C9E240C9E240E3C8C540E6C1D9D5C9D5C740C6C9C5D3C4404040       ****@****@**@***@*******@*****@@@*
120                            4040404040404040404040404040404040404040404040404040404040404040       *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*
121 */
122
123 /* iSeries unformatted packet record consist of the same header record as the formatted trace but all
124  * other records are simply unformatted data containing IP, TCP and packet data combined.
125  *
126  Record       Data    Record           Controller  Destination   Source        Frame            Number  Number    Poll/
127  Number  S/R  Length  Timer            Name        MAC Address   MAC Address   Format  Command  Sent    Received  Final  DSAP  SSAP
128  ------  ---  ------  ---------------  ----------  ------------  ------------  ------  -------  ------  --------  -----  ----  ----
129       1   R       64  12:19:29.97108               000629ECF48E  0006D78E23C2   ETHV2   Type: 0800
130          Data . . . . . :  4500003C27954000 3A06CE3D9797440F  0A5964EAC4F50554 58C9915500000000   *E..<'*@.:.*=**D..YD***.TX**U....*
131                            A00216D06A200000 020405B40402080A  1104B6C000000000 010303000B443BF1   **..*J .....*......**.........D;**
132 */
133
134 #ifdef HAVE_CONFIG_H
135 #include "config.h"
136 #endif
137 #include "wtap-int.h"
138 #include "buffer.h"
139 #include "iseries.h"
140 #include "file_wrappers.h"
141
142 #include <stdio.h>
143 #include <stdlib.h>
144 #include <string.h>
145 #include <ctype.h>
146 #include <errno.h>
147
148 #include <wsutil/str_util.h>
149
150 #define ISERIES_HDR_MAGIC_STR   "COMMUNICATIONS TRACE"
151 #define ISERIES_HDR_MAGIC_LEN   20
152 #define ISERIES_UNICODE_HDR_MAGIC_LEN 17
153 #define ISERIES_PKT_MAGIC_STR   "ETHV2"
154 #define ISERIES_PKT_MAGIC_LEN   5
155 #define ISERIES_LINE_LENGTH     270
156 #define ISERIES_HDR_LINES_TO_CHECK  100
157 #define ISERIES_PKT_LINES_TO_CHECK  4
158 #define ISERIES_MAX_PACKET_LEN  16384
159 #define ISERIES_MAX_TRACE_LEN   99999999
160 #define ISERIES_PKT_ALLOC_SIZE (cap_len*2)+1
161 #define ISERIES_FORMAT_ASCII    1
162 #define ISERIES_FORMAT_UNICODE  2
163
164 typedef struct {
165   gboolean have_date;     /* TRUE if we found a capture start date */
166   int year, month, day;   /* The start date */
167   gboolean tcp_formatted; /* TCP/IP data formated Y/N */
168   gboolean ipv6_trace;    /* IPv4 or IPv6  */   
169   int format;             /* Trace format type        */
170 } iseries_t;
171
172 static gboolean iseries_read (wtap * wth, int *err, gchar ** err_info,
173                               gint64 *data_offset);
174 static gboolean iseries_seek_read (wtap * wth, gint64 seek_off,
175                                    union wtap_pseudo_header *pseudo_header,
176                                    guint8 * pd, int len, int *err,
177                                    gchar ** err_info);
178 static gboolean iseries_check_file_type (wtap * wth, int *err, gchar **err_info,
179                                          int format);
180 static gint64 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info);
181 static int iseries_parse_packet (wtap * wth, FILE_T fh,
182                                  union wtap_pseudo_header *pseudo_header,
183                                  guint8 * pd, int *err, gchar ** err_info);
184 static int iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes);
185 static gboolean iseries_parse_hex_string (const char * ascii, guint8 * buf,
186                                           int len);
187
188 int
189 iseries_open (wtap * wth, int *err, gchar ** err_info)
190 {
191   int bytes_read;
192   gint offset;
193   char magic[ISERIES_LINE_LENGTH];
194   char unicodemagic[ISERIES_UNICODE_HDR_MAGIC_LEN] =
195     { '\x43', '\x00', '\x4F', '\x00', '\x4D',
196     '\x00', '\x4D', '\x00', '\x55', '\x00', '\x4E', '\x00', '\x49', '\x00',
197     '\x43', '\x00', '\x41'
198   };
199
200   /*
201    * Check that file starts with a valid iSeries COMMS TRACE header
202    * by scanning for it in the first line
203    */
204   errno = WTAP_ERR_CANT_READ;
205   bytes_read = file_read (&magic, sizeof magic, wth->fh);
206   if (bytes_read != sizeof magic)
207     {
208       *err = file_error (wth->fh, err_info);
209       if (*err != 0)
210         return -1;
211       return 0;
212     }
213
214   /* 
215    * Check if this is a UNICODE formatted file by scanning for the magic string
216    */
217   offset=0;
218   while (offset < ISERIES_LINE_LENGTH - ISERIES_UNICODE_HDR_MAGIC_LEN)
219   {
220           if (memcmp (magic + offset, unicodemagic, ISERIES_UNICODE_HDR_MAGIC_LEN) == 0) {
221                   if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
222                   {
223                           return 0;
224                   }
225                   /*
226                   * Do some basic sanity checking to ensure we can handle the
227                   * contents of this trace
228                   */
229                   if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_UNICODE))
230                   {
231                           if (*err == 0)
232                                   return 0;
233                           else
234                                   return -1;
235                   }
236                   wth->data_offset = 0;
237                   wth->file_encap = WTAP_ENCAP_ETHERNET;
238                   wth->file_type = WTAP_FILE_ISERIES;
239                   wth->snapshot_length = 0;
240                   wth->subtype_read = iseries_read;
241                   wth->subtype_seek_read = iseries_seek_read;
242                   wth->tsprecision = WTAP_FILE_TSPREC_USEC;
243                   if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
244                   {
245                           return 0;
246                   }
247                   return 1;
248           }
249           offset += 1;
250   }
251
252     /* 
253    * Check if this is a ASCII formatted file by scanning for the magic string
254    */
255   offset=0;
256   while (offset < ISERIES_LINE_LENGTH - ISERIES_HDR_MAGIC_LEN)
257   {
258           if (memcmp (magic + offset, ISERIES_HDR_MAGIC_STR, ISERIES_HDR_MAGIC_LEN) == 0) {
259
260                   if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
261                   {
262                           return 0;
263                   }
264                   /*
265                   * Do some basic sanity checking to ensure we can handle the
266                   * contents of this trace
267                   */
268                   if (!iseries_check_file_type (wth, err, err_info, ISERIES_FORMAT_ASCII))
269                   {
270                           if (*err == 0)
271                                   return 0;
272                           else
273                                   return -1;
274                   }
275                   wth->data_offset = 0;
276                   wth->file_encap = WTAP_ENCAP_ETHERNET;
277                   wth->file_type = WTAP_FILE_ISERIES;
278                   wth->snapshot_length = 0;
279                   wth->subtype_read = iseries_read;
280                   wth->subtype_seek_read = iseries_seek_read;
281                   wth->tsprecision = WTAP_FILE_TSPREC_USEC;
282                   if (file_seek (wth->fh, 0, SEEK_SET, err) == -1)
283                   {
284                           return 0;
285                   }
286                   return 1;
287           }
288           offset += 1;
289   }
290
291   /* Neither ASCII or UNICODE so not supported */
292   return 0;
293 }
294
295 /*
296  * Do some basic sanity checking to ensure we can handle the
297  * contents of this trace by checking the header page for
298  * requisit requirements and additional information.
299  */
300 static gboolean
301 iseries_check_file_type (wtap * wth, int *err, gchar **err_info, int format)
302 {
303   guint line;
304   int num_items_scanned;
305   char buf[ISERIES_LINE_LENGTH], protocol[9], type[5], work[2] = "";
306   iseries_t *iseries;
307
308   /* Save trace format for passing between packets */
309   iseries = (iseries_t *) g_malloc (sizeof (iseries_t));
310   wth->priv = (void *)iseries;
311   iseries->have_date = FALSE;
312   iseries->format = format;
313   iseries->tcp_formatted = FALSE;
314   iseries->ipv6_trace = FALSE;
315
316   for (line = 0; line < ISERIES_HDR_LINES_TO_CHECK; line++)
317     {
318       if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) != NULL)
319         {
320           /*
321            * Check that we are dealing with an ETHERNET trace
322            */
323           if (iseries->format == ISERIES_FORMAT_UNICODE)
324             {   
325             iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
326             }
327           ascii_strup_inplace(buf);
328           num_items_scanned = sscanf (buf,
329                   "%*[ \n\t]OBJECT PROTOCOL%*[ .:\n\t]%8s",
330                                       protocol);
331           if (num_items_scanned == 1)
332             {
333               if (memcmp (protocol, "ETHERNET", 8) != 0)
334                 return FALSE;
335             }
336
337           /*
338            * The header is the only place where the date part of the timestamp is held, so
339            * extract it here and store for all packets to access
340            */
341           num_items_scanned = sscanf (buf,
342                                       "%*[ \n\t]START DATE/TIME%*[ .:\n\t]%2d/%2d/%2d",
343                                       &iseries->month, &iseries->day,
344                                       &iseries->year);
345           if (num_items_scanned == 3)
346             {
347               iseries->have_date = TRUE;
348             }
349
350
351          /*
352           * Determine if this is a IPv4 or IPv6 trace
353           */
354           num_items_scanned =
355                   sscanf (buf+78,
356                   "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
357           if (num_items_scanned == 1)
358           {
359                   if (strncmp (type, "0800", 1) == 0) 
360                   {
361                         iseries->ipv6_trace = FALSE;
362                   }
363                   if (strncmp (type, "86DD", 1) == 0) 
364                   {
365                         iseries->ipv6_trace = TRUE;
366                   }
367           }
368
369           /*
370           * Determine if the data has been formatted
371           */
372           /* IPv6 formatted */ 
373           num_items_scanned = sscanf (buf,
374                   "%*[ \n\t]IPV6 HEADER%1s",
375                   work);
376           if (num_items_scanned == 1)
377           {
378                   iseries->tcp_formatted = TRUE;
379                   return TRUE;
380           }     
381           /* IPv4 formatted */
382           num_items_scanned = sscanf (buf,
383                   "%*[ \n\t]IP HEADER  %1s",
384                   work);
385           if (num_items_scanned == 1)
386           {
387                   iseries->tcp_formatted = TRUE;
388                   return TRUE;
389           }
390
391 }
392       else
393         {
394           /* EOF or error. */
395           if (file_eof (wth->fh))
396             *err = 0;
397           else 
398             *err = file_error (wth->fh, err_info);
399           return FALSE;
400         }
401     }
402   *err = 0;
403   return TRUE;
404 }
405
406 /*
407  * Find the next packet and parse it; called from wtap_read().
408  */
409 static gboolean
410 iseries_read (wtap * wth, int *err, gchar ** err_info, gint64 *data_offset)
411 {
412   gint64 offset;
413   int pkt_len;
414
415   /*
416    * Locate the next packet
417    */
418   offset = iseries_seek_next_packet (wth, err, err_info);
419   if (offset < 1)
420     return FALSE;
421
422   /*
423    * Parse the packet and extract the various fields
424    */
425   pkt_len =
426     iseries_parse_packet (wth, wth->fh, &wth->pseudo_header, NULL, err,
427                           err_info);
428   if (pkt_len == -1)
429     return FALSE;
430
431   wth->data_offset = offset;
432   *data_offset = offset;
433   return TRUE;
434 }
435
436 /*
437  * Seeks to the beginning of the next packet, and returns the
438  * byte offset.  Returns -1 on failure, and sets "*err" to the error
439  * and "*err_info" to null or an additional error string.
440  */
441 static gint64
442 iseries_seek_next_packet (wtap * wth, int *err, gchar **err_info)
443 {
444   iseries_t *iseries = (iseries_t *)wth->priv;
445   char buf[ISERIES_LINE_LENGTH],type[5];
446   int line, num_items_scanned;
447   gint64 cur_off;
448   long buflen;
449
450   /*
451    * Seeks to the beginning of the next packet, and returns the
452    * byte offset.  Returns -1 on failure, and sets "*err" to the error
453    * and "*err_info" to null or an additional error string.
454    */
455   for (line = 0; line < ISERIES_MAX_TRACE_LEN; line++)
456     {
457       if (file_gets (buf, ISERIES_LINE_LENGTH, wth->fh) != NULL)
458         {
459
460           /* Convert UNICODE to ASCII if required and determine    */
461           /* the number of bytes to rewind to beginning of record. */
462           if (iseries->format == ISERIES_FORMAT_UNICODE)
463             {
464               /* buflen is #bytes to 1st 0x0A */
465              buflen = iseries_UNICODE_to_ASCII ((guint8 *)buf, ISERIES_LINE_LENGTH);
466             }
467           else
468             {
469               /* Else buflen is just length of the ASCII string */
470               buflen = (long) strlen (buf);
471             }
472           ascii_strup_inplace(buf);
473           /* If packet header found return the offset */
474           num_items_scanned =
475                   sscanf (buf+78,
476                   "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
477           if (num_items_scanned == 1)
478             {
479               /* Rewind to beginning of line */
480               cur_off = file_tell (wth->fh);
481               if (cur_off == -1)
482                 {
483                   *err = file_error (wth->fh, err_info);
484                   return -1;
485                 }
486               if (file_seek (wth->fh, cur_off - buflen, SEEK_SET, err) == -1)
487                 {
488                   return -1;
489                 }
490               return cur_off - buflen;
491             }
492         }
493       /* Otherwise we got an error or reached EOF */
494       else
495         {
496           if (file_eof (wth->fh))
497             {
498               /* We got an EOF. */
499               *err = 0;
500             }
501           else
502             {
503               /* We got an error. */
504               *err = file_error (wth->fh, err_info);
505             }
506           return -1;
507         }
508     }
509
510   return -1;
511 }
512
513 /*
514  * Read packets in random-access fashion
515  */
516 static gboolean
517 iseries_seek_read (wtap * wth, gint64 seek_off,
518                    union wtap_pseudo_header *pseudo_header, guint8 * pd,
519                    int len, int *err, gchar ** err_info)
520 {
521   int pkt_len;
522
523   /* seek to packet location */
524   if (file_seek (wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
525     return FALSE;
526
527   /*
528    * Parse the packet and extract the various fields
529    */
530   pkt_len = iseries_parse_packet (wth, wth->random_fh, pseudo_header, pd,
531                                   err, err_info);
532
533   if (pkt_len != len)
534     {
535       if (pkt_len != -1)
536         {
537           *err = WTAP_ERR_BAD_RECORD;
538           *err_info =
539             g_strdup_printf
540             ("iseries: requested length %d doesn't match record length %d",
541              len, pkt_len);
542         }
543       return FALSE;
544     }
545   return TRUE;
546 }
547
548 /* Parses a packet. */
549 static int
550 iseries_parse_packet (wtap * wth, FILE_T fh,
551                       union wtap_pseudo_header *pseudo_header, guint8 * pd,
552                       int *err, gchar ** err_info)
553 {
554   iseries_t *iseries = (iseries_t *)wth->priv;
555   gint64 cur_off;
556   gboolean isValid, isCurrentPacket, IPread, TCPread, isDATA, isDataFormatted, isDataHandled;
557   int num_items_scanned, line, pktline, buflen;
558   guint32 pkt_len;
559   int cap_len, pktnum, hr, min, sec, csec;
560   char direction[2], destmac[13], srcmac[13], type[5], ipheader[41],
561     tcpheader[81];
562   char hex1[17], hex2[17], hex3[17], hex4[17];
563   char data[ISERIES_LINE_LENGTH * 2];
564   guint8 *buf;
565   char   *tcpdatabuf, *workbuf, *asciibuf;
566   struct tm tm;
567
568   /*
569    * Check for packet headers in first 3 lines this should handle page breaks
570    * situations and the header lines output at each page throw and ensure we
571    * read both the captured and packet lengths.
572    */
573   isValid = FALSE;
574   for (line = 1; line < ISERIES_PKT_LINES_TO_CHECK; line++)
575     {
576       cur_off = file_tell (fh);
577       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
578         {
579           *err = file_error (fh, err_info);
580           if (*err == 0)
581             {
582               *err = WTAP_ERR_SHORT_READ;
583             }
584           return -1;
585         }
586       /* Convert UNICODE data to ASCII */
587       if (iseries->format == ISERIES_FORMAT_UNICODE)
588         {
589          iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
590         }
591           ascii_strup_inplace(data);
592       num_items_scanned =
593         sscanf (data,
594                 "%*[ \n\t]%6d%*[ *\n\t]%1s%*[ \n\t]%6d%*[ \n\t]%2d:%2d:%2d.%9d%*[ \n\t]%12s%*[ \n\t]%12s%*[ \n\t]ETHV2%*[ \n\t]TYPE:%*[ \n\t]%4s",
595                 &pktnum, direction, &cap_len, &hr, &min, &sec, &csec, destmac,
596                 srcmac, type);
597       if (num_items_scanned == 10)
598         {
599           /* OK! We found the packet header line */
600           isValid = TRUE;
601           /*
602            * XXX - The Capture length returned by the iSeries trace doesn't seem to include the src/dest MAC
603            * addresses or the packet type. So we add them here.
604            */
605           cap_len += 14;
606           break;
607         }
608     }
609
610   /*
611    * If no packet header found we exit at this point and inform the user.
612    */
613   if (!isValid)
614     {
615       *err = WTAP_ERR_BAD_RECORD;
616       *err_info = g_strdup ("iseries: packet header isn't valid");
617       return -1;
618     }
619
620   /*
621    * If we have Wiretap Header then populate it here
622    *
623    * XXX - Timer resolution on the iSeries is hardware dependant; the value for csec may be
624    * different on other platforms though all the traces I've seen seem to show resolution
625    * to Milliseconds (i.e HH:MM:SS.nnnnn) or Nanoseconds (i.e HH:MM:SS.nnnnnn)
626    */
627   if (iseries->have_date)
628     {
629       tm.tm_year = 100 + iseries->year;
630       tm.tm_mon = iseries->month - 1;
631       tm.tm_mday = iseries->day;
632       tm.tm_hour = hr;
633       tm.tm_min = min;
634       tm.tm_sec = sec;
635       tm.tm_isdst = -1;
636       wth->phdr.ts.secs = mktime (&tm);
637       /* Handle Millisecond precision for timer */
638       if (csec > 99999)
639         {
640           wth->phdr.ts.nsecs = csec * 1000;
641         }
642       /* Handle Nanosecond precision for timer */
643       else
644         {
645           wth->phdr.ts.nsecs = csec * 10000;
646         }
647     }
648
649     wth->phdr.caplen = cap_len;
650     wth->phdr.pkt_encap = WTAP_ENCAP_ETHERNET;
651     pseudo_header->eth.fcs_len = -1;
652
653   /*
654    * Start Reading packet contents
655    */
656   isCurrentPacket = TRUE;
657   IPread = FALSE;
658   TCPread = FALSE;
659   isDATA = FALSE;
660   isDataFormatted = TRUE;
661   /*
662    * Allocate 2 work buffers to handle concatentation of the hex data block
663    */
664   tcpdatabuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
665   g_snprintf (tcpdatabuf, 1, "%s", "");
666   workbuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
667   g_snprintf (workbuf, 1, "%s", "");
668   /* loop through packet lines and breakout when the next packet header is read */
669   pktline = 0;
670   while (isCurrentPacket)
671     {
672       pktline++;
673       /* Read the next line */
674       if (file_gets (data, ISERIES_LINE_LENGTH, fh) == NULL)
675         {
676           if (file_eof (fh))
677             {
678               break;
679             }
680           else
681             {
682               *err = file_error (fh, err_info);
683               if (*err == 0)
684                 {
685                   *err = WTAP_ERR_SHORT_READ;
686                 }
687               return -1;
688             }
689         }
690
691       /* Convert UNICODE data to ASCII and determine line length */
692       if (iseries->format == ISERIES_FORMAT_UNICODE)
693         {
694          buflen = iseries_UNICODE_to_ASCII ((guint8 *)data, ISERIES_LINE_LENGTH);
695         }
696       else
697         {
698           /* Else bytes to rewind is just length of ASCII string */
699           buflen = (int) strlen (data);
700         }
701
702           /*                      
703           * Decode data for IPv4 traces and unformatted IPv6 traces 
704           */
705           if ((!iseries->ipv6_trace) || ((iseries->ipv6_trace) && (!iseries->tcp_formatted)))
706           {
707                   /* If this is a IP header hex string then set flag */
708                   num_items_scanned = sscanf (data + 22, "IP Header%*[ .:\n\t]%40s", ipheader);
709                   if (num_items_scanned == 1)
710                   {
711                           IPread = TRUE;
712                   }
713
714                   /* If this is TCP header hex string then set flag */
715                   num_items_scanned = sscanf (data + 22, "TCP Header%*[ .:\n\t]%80s", tcpheader);
716                   if (num_items_scanned == 1)
717                   {
718                           TCPread = TRUE;
719                   }
720
721                   /*
722                   * If there is data in the packet handle it here.
723                   *
724                   * The data header line will have the "Data . . " identifier, subsequent lines don't
725                   */
726                   num_items_scanned =
727                           sscanf (data + 27, "%16[A-F0-9] %16[A-F0-9] %16[A-F0-9] %16[A-F0-9]",
728                           hex1, hex2, hex3, hex4);
729                   if (num_items_scanned > 0)
730                   {
731                           isDATA = TRUE;
732                           /*
733                           * Scan the data line for data blocks, depending on the number of blocks scanned
734                           * add them along with current tcpdata buffer to the work buffer and then copy
735                           * work buffer to tcpdata buffer to continue building up tcpdata buffer to contain
736                           * a single hex string.
737                           */
738                           switch (num_items_scanned)
739                           {
740                           case 1:
741                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s", tcpdatabuf,
742                                           hex1);
743                                   break;
744                           case 2:
745                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s",
746                                           tcpdatabuf, hex1, hex2);
747                                   break;
748                           case 3:
749                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s",
750                                           tcpdatabuf, hex1, hex2, hex3);
751                                   break;
752                           default:
753                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s",
754                                           tcpdatabuf, hex1, hex2, hex3, hex4);
755                           }
756                           memcpy (tcpdatabuf, workbuf, ISERIES_PKT_ALLOC_SIZE);
757                   }
758           }
759       
760           /*
761            * Decode data for IPv6 formatted traces
762            */
763           if ((iseries->ipv6_trace) && (iseries->tcp_formatted))
764           {
765                   /*
766                   * If there are IPv6 headers in the packet handle it here.
767                   *
768                   * iSeries IPv6 headers are aligned after column 36 and appears as a single hex string  
769                   * of 16,32,48 or 64 bytes
770                   */
771                   isDataHandled=FALSE;
772                   num_items_scanned =
773                           sscanf (data + 35, "%*[ \n\t]%16[A-F0-9]%16[A-F0-9]%16[A-F0-9]%16[A-F0-9]",
774                           hex1, hex2, hex3, hex4);
775                   if (num_items_scanned > 0)
776                   {
777                           isDATA = TRUE;
778                           /*
779                           * Scan the data line for data blocks, depending on the number of blocks scanned
780                           * add them along with current tcpdata buffer to the work buffer and then copy
781                           * work buffer to tcpdata buffer to continue building up tcpdata buffer to contain
782                           * a single hex string.
783                           */
784                           switch (num_items_scanned)
785                           {
786                           case 1:
787                                   if (strlen(hex1)==16) 
788                                   {
789                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s", tcpdatabuf,
790                                           hex1);
791                                         isDataHandled=TRUE;
792                                   }
793                                   break;
794                           case 2:
795                                   if ((strlen(hex1)==16) && (strlen(hex2)==16)) 
796                                   {
797                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s",
798                                           tcpdatabuf, hex1, hex2);
799                                     isDataHandled=TRUE;
800                                   }
801                                   break;
802                           case 3:
803                                   if ((strlen(hex1)==16) && (strlen(hex2)==16) && (strlen(hex3)==16)) 
804                                   {
805                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s",
806                                           tcpdatabuf, hex1, hex2, hex3);
807                                 isDataHandled=TRUE;
808                                   }
809                                   break;
810                           default:
811                                   if ((strlen(hex1)==16) && (strlen(hex2)==16) && (strlen(hex3)==16) && (strlen(hex4)==16)) 
812                                   {
813                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s",
814                                           tcpdatabuf, hex1, hex2, hex3, hex4);
815                                 isDataHandled=TRUE;
816                                   }
817                           }
818                           memcpy (tcpdatabuf, workbuf, ISERIES_PKT_ALLOC_SIZE);
819                   }
820                   /*
821                   * If there is data in the packet handle it here.
822                   *
823                   * The data header line will have the "Data . . " identifier, subsequent lines don't
824                   * Check to ensure we haven't already captured and used this data block already above
825                   */
826                   num_items_scanned =
827                           sscanf (data + 26, "%*[ \n\t]%16[A-F0-9]%16[A-F0-9]%16[A-F0-9]%16[A-F0-9]",
828                           hex1, hex2, hex3, hex4);
829                   if ((num_items_scanned > 0) && (isDataHandled==FALSE))
830                   {
831                           isDATA = TRUE;
832                           /*
833                           * Scan the data line for data blocks, depending on the number of blocks scanned
834                           * add them along with current tcpdata buffer to the work buffer and then copy
835                           * work buffer to tcpdata buffer to continue building up tcpdata buffer to contain
836                           * a single hex string.
837                           */
838                           switch (num_items_scanned)
839                           {
840                           case 1:
841                                   if (strlen(hex1)==16)
842                                   {
843                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s", tcpdatabuf,
844                                           hex1);
845                                   }
846                                   break;
847                           case 2:
848                                   if ((strlen(hex1)==16) && (strlen(hex2)==16)) 
849                                   {
850                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s",
851                                           tcpdatabuf, hex1, hex2);
852                                   }
853                                   break;
854                           case 3:
855                                   if ((strlen(hex1)==16) && (strlen(hex2)==16) && (strlen(hex3)==16)) 
856                                   {
857                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s",
858                                           tcpdatabuf, hex1, hex2, hex3);
859                                   }     
860                                   break;
861                           default:
862                                   if ((strlen(hex1)==16) && (strlen(hex2)==16) && (strlen(hex3)==16) && (strlen(hex4)==16)) 
863                                   {
864                                   g_snprintf (workbuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s",
865                                           tcpdatabuf, hex1, hex2, hex3, hex4);
866                                   }
867                           }
868                           memcpy (tcpdatabuf, workbuf, ISERIES_PKT_ALLOC_SIZE);
869                   }
870           }
871
872       /*
873        * If we see the identifier for the next packet then rewind and set
874        * isCurrentPacket FALSE
875        */
876           ascii_strup_inplace(data);
877           /* If packet header found return the offset */
878           num_items_scanned =
879                   sscanf (data+78,
880                   "%*[ \n\t]ETHV2%*[ .:\n\t]TYPE%*[ .:\n\t]%4s",type);
881       if ((num_items_scanned == 1) && pktline > 1)
882         {
883           isCurrentPacket = FALSE;
884           cur_off = file_tell (fh);
885           if (cur_off == -1)
886             {
887               /* Error. */
888               *err = file_error (fh, err_info);
889               return -1;
890             }
891           if (file_seek (fh, cur_off - buflen, SEEK_SET, err) == -1)
892             {
893               return -1;
894             }
895         }
896     }
897
898         /*
899         * For a IPV4 formated trace ensure we have read at least the IP and TCP headers otherwise
900         * exit and pass error message to user.
901         */
902         if ((iseries->tcp_formatted) && (iseries->ipv6_trace == FALSE))
903         {
904                 if (!IPread)
905                 {
906                         *err = WTAP_ERR_BAD_RECORD;
907                         *err_info = g_strdup ("iseries: IP header isn't valid");
908                         return -1;
909                 }
910                 if (!TCPread)
911                 {
912                         *err = WTAP_ERR_BAD_RECORD;
913                         *err_info = g_strdup ("iseries: TCP header isn't valid");
914                         return -1;
915                 }
916         }
917
918   /*
919    * Create a buffer to hold all the ASCII Hex data and populate with all the
920    * extracted data.
921    */
922   asciibuf = g_malloc (ISERIES_PKT_ALLOC_SIZE);
923   if (isDATA)
924     {
925       /* packet contained data */
926       if ((iseries->tcp_formatted) && (iseries->ipv6_trace == FALSE))
927         {
928           /* build string for formatted fields */
929           g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s%s",
930                       destmac, srcmac, type, ipheader, tcpheader, tcpdatabuf);
931         }
932       else
933         {
934           /* build string for unformatted data fields and IPV6 data*/
935           g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s", destmac,
936                       srcmac, type, tcpdatabuf);
937         }
938     }
939   else
940     {
941       /* No data in the packet */
942       g_snprintf (asciibuf, ISERIES_PKT_ALLOC_SIZE, "%s%s%s%s%s", destmac,
943                   srcmac, type, ipheader, tcpheader);
944     }
945
946   /*
947    * Note: iSeries comms traces pad data blocks out with zeros
948    * Extract the packet length from the actual IP header; this may
949    * differ from the capture length reported by the formatted trace.
950    * IPv4 and IPv6 headers contain the length at different offsets so
951    * read from the correct location.
952    */
953   if (!iseries->ipv6_trace) 
954   {
955   num_items_scanned = sscanf (asciibuf + 32, "%4x", &pkt_len);
956   wth->phdr.len = pkt_len + 14;
957   }
958   else
959   {
960   num_items_scanned = sscanf (asciibuf + 36, "%4x", &pkt_len);
961     wth->phdr.len = pkt_len + 14;
962   }
963   if (wth->phdr.caplen > wth->phdr.len)
964     wth->phdr.len = wth->phdr.caplen;
965
966   /* Make sure we have enough room for the packet, only create buffer if none supplied */
967   if (pd == NULL)
968     {
969       buffer_assure_space (wth->frame_buffer, ISERIES_MAX_PACKET_LEN);
970       buf = buffer_start_ptr (wth->frame_buffer);
971       /* Convert ascii data to binary and return in the frame buffer */
972       iseries_parse_hex_string (asciibuf, buf, (int) strlen (asciibuf));
973     }
974   else
975     {
976       /* Convert ascii data to binary and return in the frame buffer */
977       iseries_parse_hex_string (asciibuf, pd, (int) strlen (asciibuf));
978     }
979
980   /* free buffers allocs and return */
981   *err = 0;
982   g_free (asciibuf);
983   g_free (tcpdatabuf);
984   g_free (workbuf);
985   return wth->phdr.len;
986 }
987
988 /*
989  * Simple routine to convert an UNICODE buffer to ASCII
990  *
991  * XXX - This may be possible with iconv or similar
992  */
993 static int
994 iseries_UNICODE_to_ASCII (guint8 * buf, guint bytes)
995 {
996   guint i;
997   guint8 *bufptr;
998   bufptr = buf;
999
1000   for (i = 0; i < bytes; i++)
1001     {
1002       switch (buf[i])
1003         {
1004         case 0xFE:
1005         case 0xFF:
1006         case 0x00:
1007           break;
1008         default:
1009           *bufptr = buf[i];
1010           bufptr++;
1011         }
1012       if (buf[i] == 0x0A)
1013         return i;
1014     }
1015   return i;
1016 }
1017
1018 /*
1019  * Simple routine to convert an ASCII hex string to binary data
1020  * Requires ASCII hex data and buffer to populate with binary data
1021  */
1022 static gboolean
1023 iseries_parse_hex_string (const char * ascii, guint8 * buf, int len)
1024 {
1025   int i, byte;
1026   gint hexvalue;
1027   guint8 bytevalue;
1028
1029   byte = 0;
1030   i = 0;
1031   for (;;)
1032     {
1033       if (i >= len)
1034         break;
1035       hexvalue = g_ascii_xdigit_value(ascii[i]);
1036       i++;
1037       if (hexvalue == -1)
1038         return FALSE;   /* not a valid hex digit */
1039       bytevalue = (guint8)(hexvalue << 4);
1040       if (i >= len)
1041         return FALSE;   /* only one hex digit of the byte is present */
1042       hexvalue = g_ascii_xdigit_value(ascii[i]);
1043       i++;
1044       if (hexvalue == -1)
1045         return FALSE;   /* not a valid hex digit */
1046       bytevalue |= (guint8) hexvalue;
1047       buf[byte] = bytevalue;
1048       byte++;
1049     }
1050   return TRUE;
1051 }