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