Rename buffer_ routines to ws_buffer_ to avoid name collisions.
[metze/wireshark/wip.git] / wiretap / cosine.c
1 /* cosine.c
2  *
3  * CoSine IPNOS L2 debug output parsing
4  * Copyright (c) 2002 by Motonori Shindo <motonori@shin.do>
5  *
6  * Wiretap Library
7  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 #include "config.h"
25 #include "wtap-int.h"
26 #include <wsutil/buffer.h>
27 #include "cosine.h"
28 #include "file_wrappers.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34
35 /*
36
37   IPNOS: CONFIG VPN(100) VR(1.1.1.1)# diags
38   ipnos diags: Control (1/0) :: layer-2 ?
39   Registered commands for area "layer-2"
40       apply-pkt-log-profile  Configure packet logging on an interface
41       create-pkt-log-profile  Set packet-log-profile to be used for packet logging (see layer-2 pkt-log)
42       detail                Get Layer 2 low-level details
43
44   ipnos diags: Control (1/0) :: layer-2 create ?
45       create-pkt-log-profile  <pkt-log-profile-id ctl-tx-trace-length ctl-rx-trace-length data-tx-trace-length data-rx-trace-length pe-logging-or-control-blade>
46
47   ipnos diags: Control (1/0) :: layer-2 create 1 32 32 0 0 0
48   ipnos diags: Control (1/0) :: layer-2 create 2 32 32 100 100 0
49   ipnos diags: Control (1/0) :: layer-2 apply ?
50       apply-pkt-log-profile  <slot port channel subif pkt-log-profile-id>
51
52   ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 1
53   Successfully applied packet-log-profile on LI
54
55   -- Note that only the control packets are logged because the data packet size parameters are 0 in profile 1
56   IPNOS: CONFIG VPN(200) VR(3.3.3.3)# ping 20.20.20.43
57   vpn 200 : [max tries 4, timeout 5 seconds, data length 64 bytes, ttl 255]
58   ping #1 ok, RTT 0.000 seconds
59   ping #2 ok, RTT 0.000 seconds
60   ping #3 ok, RTT 0.000 seconds
61   ping #4 ok, RTT 0.000 seconds
62   [finished]
63
64   IPNOS: CONFIG VPN(200) VR(3.3.3.3)# 2000-2-1,18:19:46.8:  l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
65
66
67   2000-2-1,18:19:46.8:  l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
68
69   2000-2-1,18:19:46.8:  l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
70
71   2000-2-1,18:19:46.8:  l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x8030000]
72
73   ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 0
74   Successfully applied packet-log-profile on LI
75   ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 2
76   Successfully applied packet-log-profile on LI
77
78   -- Note that both control and data packets are logged because the data packet size parameter is 100 in profile 2
79      Please ignore the event-log messages getting mixed up with the ping command
80   ping 20.20.20.43 cou2000-2-1,18:20:17.0:  l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
81
82           00 D0 D8 D2 FF 03 C0 21  09 29 00 08 6B 60 84 AA
83
84   2000-2-1,18:20:17.0:  l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
85           00 D0 D8 D2 FF 03 C0 21  09 29 00 08 6D FE FA AA
86
87   2000-2-1,18:20:17.0:  l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
88           00 D0 D8 D2 FF 03 C0 21  0A 29 00 08 6B 60 84 AA
89
90   2000-2-1,18:20:17.0:  l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x8030000]
91           00 D0 D8 D2 FF 03 C0 21  0A 29 00 08 6D FE FA AA
92
93   nt 1 length 500
94   vpn 200 : [max tries 1, timeout 5 seconds, data length 500 bytes, ttl 255]
95   2000-2-1,18:20:24.1:  l2-tx (PPP:3/7/1:100), Length:536, Pro:1, Off:8, Pri:7, RM:0, Err:0 [0x4070, 0x801]
96           00 D0 D8 D2 FF 03 00 21  45 00 02 10 00 27 00 00
97           FF 01 69 51 14 14 14 22  14 14 14 2B 08 00 AD B8
98           00 03 00 01 10 11 12 13  14 15 16 17 18 19 1A 1B
99           1C 1D 1E 1F 20 21 22 23  24 25 26 27 28 29 2A 2B
100           2C 2D 2E 2F 30 31 32 33  34 35 36 37 38 39 3A 3B
101           3C 3D 3E 3F 40 41 42 43  44 45 46 47 48 49 4A 4B
102           4C 4D 4E 4F
103
104   ping #1 ok, RTT 0.010 seconds
105   2000-2-1,18:20:24.1:  l2-rx (PPP:3/7/1:100), Length:536, Pro:1, Off:8, Pri:7, RM:0, Err:0 [0x4071, 0x30801]
106           00 D0 D8 D2 FF 03 00 21  45 00 02 10 00 23 00 00
107           FF 01 69 55 14 14 14 2B  14 14 14 22 00 00 B5 B8
108           00 03 00 01 10 11 12 13  14 15 16 17 18 19 1A 1B
109           1C 1D 1E 1F 20 21 22 23  24 25 26 27 28 29 2A 2B
110           2C 2D 2E 2F 30 31 32 33  34 35 36 37 38 39 3A 3B
111           3C 3D 3E 3F 40 41 42 43  44 45 46 47 48 49 4A 4B
112           4C 4D 4E 4F
113
114   [finished]
115
116   IPNOS: CONFIG VPN(200) VR(3.3.3.3)# 2000-2-1,18:20:27.0:  l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
117
118           00 D0 D8 D2 FF 03 C0 21  09 2A 00 08 6B 60 84 AA
119
120   2000-2-1,18:20:27.0:  l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
121           00 D0 D8 D2 FF 03 C0 21  09 2A 00 08 6D FE FA AA
122
123   2000-2-1,18:20:27.0:  l2-tx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
124           00 D0 D8 D2 FF 03 C0 21  0A 2A 00 08 6B 60 84 AA
125
126   2000-2-1,18:20:27.0:  l2-rx (PPP:3/7/1:100), Length:16, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4001, 0x30000]
127           00 D0 D8 D2 FF 03 C0 21  0A 2A 00 08 6D FE FA AA
128
129
130   ipnos diags: Control (1/0) :: layer-2 apply 3 0x0701 100 0 0
131   Successfully applied packet-log-profile on LI
132   ipnos diags: Control (1/0) ::
133
134  */
135
136 /* XXX TODO:
137
138   o Handle a case where an empty line doesn't exists as a delimiter of
139     each packet. If the output is sent to a control blade and
140     displayed as an event log, there's always an empty line between
141     each packet output, but it may not be true when it is an PE
142     output.
143
144   o Some telnet client on Windows may put in a line break at 80
145     columns when it save the session to a text file ("CRT" is such an
146     example). I don't think it's a good idea for the telnet client to
147     do so, but CRT is widely used in Windows community, I should
148     take care of that in the future.
149
150 */
151
152 /* Magic text to check for CoSine L2 debug output */
153 #define COSINE_HDR_MAGIC_STR1   "l2-tx"
154 #define COSINE_HDR_MAGIC_STR2   "l2-rx"
155
156 /* Magic text for start of packet */
157 #define COSINE_REC_MAGIC_STR1   COSINE_HDR_MAGIC_STR1
158 #define COSINE_REC_MAGIC_STR2   COSINE_HDR_MAGIC_STR2
159
160 #define COSINE_HEADER_LINES_TO_CHECK    200
161 #define COSINE_LINE_LENGTH              240
162
163 #define COSINE_MAX_PACKET_LEN   65536
164
165 static gboolean empty_line(const gchar *line);
166 static gint64 cosine_seek_next_packet(wtap *wth, int *err, gchar **err_info,
167         char *hdr);
168 static gboolean cosine_check_file_type(wtap *wth, int *err, gchar **err_info);
169 static gboolean cosine_read(wtap *wth, int *err, gchar **err_info,
170         gint64 *data_offset);
171 static gboolean cosine_seek_read(wtap *wth, gint64 seek_off,
172         struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
173 static int parse_cosine_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
174         int *err, gchar **err_info);
175 static gboolean parse_cosine_hex_dump(FILE_T fh, struct wtap_pkthdr *phdr,
176         int pkt_len, Buffer* buf, int *err, gchar **err_info);
177 static int parse_single_hex_dump_line(char* rec, guint8 *buf,
178         guint byte_offset);
179
180 /* Returns TRUE if the line appears to be an empty line. Otherwise it
181    returns FALSE. */
182 static gboolean empty_line(const gchar *line)
183 {
184         while (*line) {
185                 if (isspace((guchar)*line)) {
186                         line++;
187                         continue;
188                 } else {
189                         break;
190                 }
191         }
192         if (*line == '\0')
193                 return TRUE;
194         else
195                 return FALSE;
196 }
197
198 /* Seeks to the beginning of the next packet, and returns the
199    byte offset. Copy the header line to hdr. Returns -1 on failure,
200    and sets "*err" to the error and sets "*err_info" to null or an
201    additional error string. */
202 static gint64 cosine_seek_next_packet(wtap *wth, int *err, gchar **err_info,
203         char *hdr)
204 {
205         gint64 cur_off;
206         char buf[COSINE_LINE_LENGTH];
207
208         while (1) {
209                 cur_off = file_tell(wth->fh);
210                 if (cur_off == -1) {
211                         /* Error */
212                         *err = file_error(wth->fh, err_info);
213                         return -1;
214                 }
215                 if (file_gets(buf, sizeof(buf), wth->fh) == NULL) {
216                         *err = file_error(wth->fh, err_info);
217                         return -1;
218                 }
219                 if (strstr(buf, COSINE_REC_MAGIC_STR1) ||
220                     strstr(buf, COSINE_REC_MAGIC_STR2)) {
221                         g_strlcpy(hdr, buf, COSINE_LINE_LENGTH);
222                         return cur_off;
223                 }
224         }
225         return -1;
226 }
227
228 /* Look through the first part of a file to see if this is
229  * a CoSine L2 debug output.
230  *
231  * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
232  * if we get an I/O error, "*err" will be set to a non-zero value and
233  * "*err_info" will be set to null or an additional error string.
234  */
235 static gboolean cosine_check_file_type(wtap *wth, int *err, gchar **err_info)
236 {
237         char    buf[COSINE_LINE_LENGTH];
238         gsize   reclen;
239         guint   line;
240
241         buf[COSINE_LINE_LENGTH-1] = '\0';
242
243         for (line = 0; line < COSINE_HEADER_LINES_TO_CHECK; line++) {
244                 if (file_gets(buf, COSINE_LINE_LENGTH, wth->fh) == NULL) {
245                         /* EOF or error. */
246                         *err = file_error(wth->fh, err_info);
247                         return FALSE;
248                 }
249
250                 reclen = strlen(buf);
251                 if (reclen < strlen(COSINE_HDR_MAGIC_STR1) ||
252                         reclen < strlen(COSINE_HDR_MAGIC_STR2)) {
253                         continue;
254                 }
255
256                 if (strstr(buf, COSINE_HDR_MAGIC_STR1) ||
257                     strstr(buf, COSINE_HDR_MAGIC_STR2)) {
258                         return TRUE;
259                 }
260         }
261         *err = 0;
262         return FALSE;
263 }
264
265
266 int cosine_open(wtap *wth, int *err, gchar **err_info)
267 {
268         /* Look for CoSine header */
269         if (!cosine_check_file_type(wth, err, err_info)) {
270                 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
271                         return -1;
272                 return 0;
273         }
274
275         if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1)        /* rewind */
276                 return -1;
277
278         wth->file_encap = WTAP_ENCAP_COSINE;
279         wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_COSINE;
280         wth->snapshot_length = 0; /* not known */
281         wth->subtype_read = cosine_read;
282         wth->subtype_seek_read = cosine_seek_read;
283         wth->tsprecision = WTAP_FILE_TSPREC_CSEC;
284
285         return 1;
286 }
287
288 /* Find the next packet and parse it; called from wtap_read(). */
289 static gboolean cosine_read(wtap *wth, int *err, gchar **err_info,
290     gint64 *data_offset)
291 {
292         gint64  offset;
293         int     pkt_len;
294         char    line[COSINE_LINE_LENGTH];
295
296         /* Find the next packet */
297         offset = cosine_seek_next_packet(wth, err, err_info, line);
298         if (offset < 0)
299                 return FALSE;
300         *data_offset = offset;
301
302         /* Parse the header */
303         pkt_len = parse_cosine_rec_hdr(&wth->phdr, line, err, err_info);
304         if (pkt_len == -1)
305                 return FALSE;
306
307         /* Convert the ASCII hex dump to binary data */
308         return parse_cosine_hex_dump(wth->fh, &wth->phdr, pkt_len,
309             wth->frame_buffer, err, err_info);
310 }
311
312 /* Used to read packets in random-access fashion */
313 static gboolean
314 cosine_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *phdr,
315         Buffer *buf, int *err, gchar **err_info)
316 {
317         int     pkt_len;
318         char    line[COSINE_LINE_LENGTH];
319
320         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
321                 return FALSE;
322
323         if (file_gets(line, COSINE_LINE_LENGTH, wth->random_fh) == NULL) {
324                 *err = file_error(wth->random_fh, err_info);
325                 if (*err == 0) {
326                         *err = WTAP_ERR_SHORT_READ;
327                 }
328                 return FALSE;
329         }
330
331         /* Parse the header */
332         pkt_len = parse_cosine_rec_hdr(phdr, line, err, err_info);
333         if (pkt_len == -1)
334                 return FALSE;
335
336         /* Convert the ASCII hex dump to binary data */
337         return parse_cosine_hex_dump(wth->random_fh, phdr, pkt_len, buf, err,
338             err_info);
339 }
340
341 /* Parses a packet record header. There are two possible formats:
342     1) output to a control blade with date and time
343         2002-5-10,20:1:31.4:  l2-tx (FR:3/7/1:1), Length:18, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0]
344     2) output to PE without date and time
345         l2-tx (FR:3/7/1:1), Length:18, Pro:0, Off:0, Pri:0, RM:0, Err:0 [0x4000, 0x0] */
346 static int
347 parse_cosine_rec_hdr(struct wtap_pkthdr *phdr, const char *line,
348      int *err, gchar **err_info)
349 {
350         union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
351         int     num_items_scanned;
352         int     yy, mm, dd, hr, min, sec, csec, pkt_len;
353         int     pro, off, pri, rm, error;
354         guint   code1, code2;
355         char    if_name[COSINE_MAX_IF_NAME_LEN] = "", direction[6] = "";
356         struct  tm tm;
357
358         if (sscanf(line, "%4d-%2d-%2d,%2d:%2d:%2d.%9d:",
359                    &yy, &mm, &dd, &hr, &min, &sec, &csec) == 7) {
360                 /* appears to be output to a control blade */
361                 num_items_scanned = sscanf(line,
362                    "%4d-%2d-%2d,%2d:%2d:%2d.%9d: %5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
363                         &yy, &mm, &dd, &hr, &min, &sec, &csec,
364                                    direction, if_name, &pkt_len,
365                                    &pro, &off, &pri, &rm, &error,
366                                    &code1, &code2);
367
368                 if (num_items_scanned != 17) {
369                         *err = WTAP_ERR_BAD_FILE;
370                         *err_info = g_strdup("cosine: purported control blade line doesn't have code values");
371                         return -1;
372                 }
373         } else {
374                 /* appears to be output to PE */
375                 num_items_scanned = sscanf(line,
376                    "%5s (%127[A-Za-z0-9/:]), Length:%9d, Pro:%9d, Off:%9d, Pri:%9d, RM:%9d, Err:%9d [%8x, %8x]",
377                                    direction, if_name, &pkt_len,
378                                    &pro, &off, &pri, &rm, &error,
379                                    &code1, &code2);
380
381                 if (num_items_scanned != 10) {
382                         *err = WTAP_ERR_BAD_FILE;
383                         *err_info = g_strdup("cosine: header line is neither control blade nor PE output");
384                         return -1;
385                 }
386                 yy = mm = dd = hr = min = sec = csec = 0;
387         }
388
389         phdr->rec_type = REC_TYPE_PACKET;
390         phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
391         tm.tm_year = yy - 1900;
392         tm.tm_mon = mm - 1;
393         tm.tm_mday = dd;
394         tm.tm_hour = hr;
395         tm.tm_min = min;
396         tm.tm_sec = sec;
397         tm.tm_isdst = -1;
398         phdr->ts.secs = mktime(&tm);
399         phdr->ts.nsecs = csec * 10000000;
400         phdr->len = pkt_len;
401
402         /* XXX need to handle other encapsulations like Cisco HDLC,
403            Frame Relay and ATM */
404         if (strncmp(if_name, "TEST:", 5) == 0) {
405                 pseudo_header->cosine.encap = COSINE_ENCAP_TEST;
406         } else if (strncmp(if_name, "PPoATM:", 7) == 0) {
407                 pseudo_header->cosine.encap = COSINE_ENCAP_PPoATM;
408         } else if (strncmp(if_name, "PPoFR:", 6) == 0) {
409                 pseudo_header->cosine.encap = COSINE_ENCAP_PPoFR;
410         } else if (strncmp(if_name, "ATM:", 4) == 0) {
411                 pseudo_header->cosine.encap = COSINE_ENCAP_ATM;
412         } else if (strncmp(if_name, "FR:", 3) == 0) {
413                 pseudo_header->cosine.encap = COSINE_ENCAP_FR;
414         } else if (strncmp(if_name, "HDLC:", 5) == 0) {
415                 pseudo_header->cosine.encap = COSINE_ENCAP_HDLC;
416         } else if (strncmp(if_name, "PPP:", 4) == 0) {
417                 pseudo_header->cosine.encap = COSINE_ENCAP_PPP;
418         } else if (strncmp(if_name, "ETH:", 4) == 0) {
419                 pseudo_header->cosine.encap = COSINE_ENCAP_ETH;
420         } else {
421                 pseudo_header->cosine.encap = COSINE_ENCAP_UNKNOWN;
422         }
423         if (strncmp(direction, "l2-tx", 5) == 0) {
424                 pseudo_header->cosine.direction = COSINE_DIR_TX;
425         } else if (strncmp(direction, "l2-rx", 5) == 0) {
426                 pseudo_header->cosine.direction = COSINE_DIR_RX;
427         }
428         g_strlcpy(pseudo_header->cosine.if_name, if_name,
429                 COSINE_MAX_IF_NAME_LEN);
430         pseudo_header->cosine.pro = pro;
431         pseudo_header->cosine.off = off;
432         pseudo_header->cosine.pri = pri;
433         pseudo_header->cosine.rm = rm;
434         pseudo_header->cosine.err = error;
435
436         return pkt_len;
437 }
438
439 /* Converts ASCII hex dump to binary data. Returns TRUE on success,
440    FALSE if any error is encountered. */
441 static gboolean
442 parse_cosine_hex_dump(FILE_T fh, struct wtap_pkthdr *phdr, int pkt_len,
443     Buffer* buf, int *err, gchar **err_info)
444 {
445         guint8 *pd;
446         gchar   line[COSINE_LINE_LENGTH];
447         int     i, hex_lines, n, caplen = 0;
448
449         /* Make sure we have enough room for the packet */
450         ws_buffer_assure_space(buf, COSINE_MAX_PACKET_LEN);
451         pd = ws_buffer_start_ptr(buf);
452
453         /* Calculate the number of hex dump lines, each
454          * containing 16 bytes of data */
455         hex_lines = pkt_len / 16 + ((pkt_len % 16) ? 1 : 0);
456
457         for (i = 0; i < hex_lines; i++) {
458                 if (file_gets(line, COSINE_LINE_LENGTH, fh) == NULL) {
459                         *err = file_error(fh, err_info);
460                         if (*err == 0) {
461                                 *err = WTAP_ERR_SHORT_READ;
462                         }
463                         return FALSE;
464                 }
465                 if (empty_line(line)) {
466                         break;
467                 }
468                 if ((n = parse_single_hex_dump_line(line, pd, i*16)) == -1) {
469                         *err = WTAP_ERR_BAD_FILE;
470                         *err_info = g_strdup("cosine: hex dump line doesn't have 16 numbers");
471                         return FALSE;
472                 }
473                 caplen += n;
474         }
475         phdr->caplen = caplen;
476         return TRUE;
477 }
478
479
480 /* Take a string representing one line from a hex dump and converts
481  * the text to binary data. We place the bytes in the buffer at the
482  * specified offset.
483  *
484  * Returns number of bytes successfully read, -1 if bad.  */
485 static int
486 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset)
487 {
488         int num_items_scanned, i;
489         unsigned int bytes[16];
490
491         num_items_scanned = sscanf(rec, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
492                                &bytes[0], &bytes[1], &bytes[2], &bytes[3],
493                                &bytes[4], &bytes[5], &bytes[6], &bytes[7],
494                                &bytes[8], &bytes[9], &bytes[10], &bytes[11],
495                                &bytes[12], &bytes[13], &bytes[14], &bytes[15]);
496         if (num_items_scanned == 0)
497                 return -1;
498
499         if (num_items_scanned > 16)
500                 num_items_scanned = 16;
501
502         for (i=0; i<num_items_scanned; i++) {
503                 buf[byte_offset + i] = (guint8)bytes[i];
504         }
505
506         return num_items_scanned;
507 }