source: lib/trace.c @ c7658ea

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since c7658ea was 67a14d4, checked in by Perry Lorier <perry@…>, 15 years ago

Move more types to the libtrace standard (typedef'd libtrace_*_t)
Add casts where appropriate
Remove default:'s from enum'd switch()'s

  • Property mode set to 100644
File size: 37.4 KB
Line 
1/*
2 * This file is part of libtrace
3 *
4 * Copyright (c) 2004 The University of Waikato, Hamilton, New Zealand.
5 * Authors: Daniel Lawson
6 *          Perry Lorier
7 *         
8 * All rights reserved.
9 *
10 * This code has been developed by the University of Waikato WAND
11 * research group. For further information please see http://www.wand.net.nz/
12 *
13 * libtrace is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * libtrace is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with libtrace; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 *
27 * $Id$
28 *
29 */
30
31
32/* @file
33 *
34 * @brief Trace file processing library
35 *
36 * @author Daniel Lawson
37 * @author Perry Lorier
38 *
39 * @internal
40 */
41#define _GNU_SOURCE
42#include "common.h"
43#include "config.h"
44#include <assert.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <netdb.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <sys/stat.h>
52#include <sys/types.h>
53#include <stdarg.h>
54
55#ifdef HAVE_LIMITS_H
56#  include <limits.h>
57#endif
58
59#ifdef HAVE_SYS_LIMITS_H
60#  include <sys/limits.h>
61#endif
62
63#include <sys/socket.h>
64#include <sys/un.h>
65#include <unistd.h>
66
67#ifdef HAVE_NET_IF_ARP_H
68#  include <net/if_arp.h>
69#endif
70
71#ifdef HAVE_NET_IF_H
72#  include <net/if.h>
73#endif
74
75#ifdef HAVE_NETINET_IN_H
76#  include <netinet/in.h>
77#endif
78
79#ifdef HAVE_NET_ETHERNET_H
80#  include <net/ethernet.h>
81#endif
82
83#ifdef HAVE_NETINET_IF_ETHER_H
84#  include <netinet/if_ether.h>
85#endif
86
87#include <time.h>
88#include <sys/ioctl.h>
89
90#ifdef HAVE_INTTYPES_H
91#  include <inttypes.h>
92#else
93#  error "Can't find inttypes.h - this needs to be fixed"
94#endif
95
96#ifdef HAVE_STDDEF_H
97#  include <stddef.h>
98#else
99# error "Can't find stddef.h - do you define ptrdiff_t elsewhere?"
100#endif
101
102#include "libtrace.h"
103#include "fifo.h"
104#include "libtrace_int.h"
105#include "parse_cmd.h"
106
107#if HAVE_PCAP_BPF_H
108#  include <pcap-bpf.h>
109#else
110#  ifdef HAVE_NET_BPF_H
111#    include <net/bpf.h>
112#  endif
113#endif
114
115#include "libtrace_int.h"
116#include "format_helper.h"
117#include "rt_protocol.h"
118#include <err.h>
119
120#define MAXOPTS 1024
121
122
123static struct libtrace_format_t *formats_list = 0;
124
125/* strncpy is not assured to copy the final \0, so we
126 * will use our own one that does
127 */
128static void xstrncpy(char *dest, const char *src, size_t n)
129{
130        strncpy(dest,src,n);
131        dest[n]='\0';
132}
133 
134static char *xstrndup(const char *src,size_t n)
135{       
136        char *ret=(char*)malloc(n+1);
137        xstrncpy(ret,src,n);
138        return ret;
139}
140
141void register_format(struct libtrace_format_t *f) {
142        assert(f->next==NULL);
143        f->next=formats_list;
144        formats_list=f;
145        /* Now, verify things */
146#if 0
147        if (format_list[nformats]->init_input) {
148#define REQUIRE(x) \
149                if (!format_list[nformats]->x) \
150                        fprintf(stderr,"%s: Input format should provide " #x "\n",format_list[nformats]->name)
151                REQUIRE(read_packet);
152                REQUIRE(start_input);
153                REQUIRE(config_input);
154                REQUIRE(pause_input);
155                REQUIRE(fin_input);
156                REQUIRE(get_link_type);
157                REQUIRE(get_capture_length);
158                REQUIRE(get_wire_length);
159                REQUIRE(get_framing_length);
160                REQUIRE(trace_event);
161                if (!format_list[nformats]->get_erf_timestamp
162                        && !format_list[nformats]->get_seconds
163                        && !format_list[nformats]->get_timeval) {
164                        fprintf(stderr,"%s: A trace format capable of input, should provide at least one of\n"
165"get_erf_timestamp, get_seconds or trace_timeval\n",format_list[nformats]->name);
166                }
167                if (format_list[nformats]->trace_event==trace_event_device) {
168                        REQUIRE(get_fd);
169                }
170                else {
171                        if (format_list[nformats]->get_fd) {
172                                fprintf(stderr,"%s: Unnecessary get_fd\n",
173                                                format_list[nformats]->name);
174                        }
175                }
176#undef REQUIRE
177        }
178        else {
179#define REQUIRE(x) \
180                if (format_list[nformats]->x) \
181                        fprintf(stderr,"%s: Non Input format shouldn't need " #x "\n",format_list[nformats]->name)
182                REQUIRE(read_packet);
183                REQUIRE(start_input);
184                REQUIRE(pause_input);
185                REQUIRE(fin_input);
186                REQUIRE(get_link_type);
187                REQUIRE(get_capture_length);
188                REQUIRE(get_wire_length);
189                REQUIRE(get_framing_length);
190                REQUIRE(trace_event);
191                REQUIRE(get_seconds);
192                REQUIRE(get_timeval);
193                REQUIRE(get_erf_timestamp);
194#undef REQUIRE
195        }
196        if (format_list[nformats]->init_output) {
197#define REQUIRE(x) \
198                if (!format_list[nformats]->x) \
199                        fprintf(stderr,"%s: Output format should provide " #x "\n",format_list[nformats]->name)
200                REQUIRE(write_packet);
201                REQUIRE(start_output);
202                REQUIRE(config_output);
203                REQUIRE(fin_output);
204#undef REQUIRE
205        }
206        else {
207#define REQUIRE(x) \
208                if (format_list[nformats]->x) \
209                        fprintf(stderr,"%s: Non Output format shouldn't need " #x "\n",format_list[nformats]->name)
210                REQUIRE(write_packet);
211                REQUIRE(start_output);
212                REQUIRE(config_output);
213                REQUIRE(fin_output);
214#undef REQUIRE
215        }
216#endif
217}
218
219/* Prints help information for libtrace
220 *
221 * Function prints out some basic help information regarding libtrace,
222 * and then prints out the help() function registered with each input module
223 */
224void trace_help() {
225        struct libtrace_format_t *tmp;
226        printf("libtrace %s\n",PACKAGE_VERSION);
227        for(tmp=formats_list;tmp;tmp=tmp->next) {
228                if (tmp->help)
229                        tmp->help();
230        }
231}
232
233#define RP_BUFSIZE 65536
234#define URI_PROTO_LINE 16
235
236/* Gets the name of the output format for a given output trace.
237 *
238 * @params libtrace     the output trace to get the name of the format for
239 * @returns callee-owned null-terminated char* containing the output format
240 *
241 */
242SIMPLE_FUNCTION
243char *trace_get_output_format(const struct libtrace_out_t *libtrace) {
244        char * format = libtrace->format->name;
245
246        return format;
247}
248
249/* Create a trace file from a URI
250 *
251 * @params char * containing a valid libtrace URI
252 * @returns opaque pointer to a libtrace_t
253 *
254 * Valid URI's are:
255 *  erf:/path/to/erf/file
256 *  erf:/path/to/erf/file.gz
257 *  erf:/path/to/rtclient/socket
258 *  erf:-                       (stdin)
259 *  dag:/dev/dagcard
260 *  pcapint:pcapinterface               (eg: pcapint:eth0)
261 *  pcap:/path/to/pcap/file
262 *  pcap:-
263 *  rtclient:hostname
264 *  rtclient:hostname:port
265 *  wag:-
266 *  wag:/path/to/wag/file
267 *  wag:/path/to/wag/file.gz
268 *  wag:/path/to/wag/socket
269 *
270 * If an error occured when attempting to open a trace, NULL is returned
271 * and an error is output to stdout.
272 */
273struct libtrace_t *trace_create(const char *uri) {
274        struct libtrace_t *libtrace = 
275                        (struct libtrace_t *)malloc(sizeof(struct libtrace_t));
276        char *scan = 0;
277        const char *uridata = 0;                 
278        struct libtrace_format_t *tmp;
279       
280        libtrace->err.err_num = TRACE_ERR_NOERROR;
281        libtrace->format=NULL;
282       
283        /* parse the URI to determine what sort of event we are dealing with */
284        if ((uridata = trace_parse_uri(uri, &scan)) == 0) {
285                trace_set_err(libtrace,TRACE_ERR_BAD_FORMAT,"Bad uri format (%s)",uri);
286                return libtrace;
287        }
288       
289        libtrace->event.tdelta = 0.0;
290        libtrace->filter = NULL;
291        libtrace->snaplen = 0;
292        libtrace->started=false;
293
294        for (tmp=formats_list;tmp;tmp=tmp->next) {
295                if (strlen(scan) == strlen(tmp->name) &&
296                                strncasecmp(scan, tmp->name, strlen(scan)) == 0
297                                ) {
298                        libtrace->format=tmp;
299                        break;
300                }
301        }
302        if (libtrace->format == 0) {
303                trace_set_err(libtrace, TRACE_ERR_BAD_FORMAT,
304                                "Unknown format (%s)",scan);
305                return libtrace;
306        }
307
308        libtrace->uridata = strdup(uridata);
309        /* libtrace->format now contains the type of uri
310         * libtrace->uridata contains the appropriate data for this
311         */
312       
313        if (libtrace->format->init_input) {
314                int err=libtrace->format->init_input(libtrace);
315                assert (err==-1 || err==0);
316                if (err==-1) {
317                        /* init_input should call trace_set_err to set
318                         * the error message
319                         */
320                        return libtrace;
321                }
322        } else {
323                trace_set_err(libtrace,TRACE_ERR_NO_INIT,
324                                "Format does not support input (%s)",scan);
325                return libtrace;
326        }
327       
328
329        libtrace->fifo = create_tracefifo(1048576);
330        if (!libtrace->fifo) {
331                trace_set_err(libtrace,ENOMEM,"Could not allocate memory for fifo");
332                free(scan);
333                return libtrace;
334        }
335        assert(libtrace->fifo);
336        free(scan);
337        trace_set_err(libtrace,0,"");
338        return libtrace;
339}
340
341/* Creates a "dummy" trace file that has only the format type set.
342 *
343 * @returns opaque pointer to a (sparsely initialised) libtrace_t
344 *
345 * IMPORTANT: Do not attempt to call trace_read_packet or other such functions
346 * with the dummy trace. Its intended purpose is to act as a packet->trace for
347 * libtrace_packet_t's that are not associated with a libtrace_t structure.
348 */
349struct libtrace_t * trace_create_dead (const char *uri) {
350        struct libtrace_t *libtrace = (struct libtrace_t *)
351                                        malloc(sizeof(struct libtrace_t));
352        char *scan = (char *)calloc(sizeof(char),URI_PROTO_LINE);
353        char *uridata;
354        struct libtrace_format_t *tmp;
355       
356        libtrace->err.err_num = TRACE_ERR_NOERROR;
357
358        if((uridata = strchr(uri,':')) == NULL) {
359                xstrncpy(scan, uri, strlen(uri));
360        } else {
361                xstrncpy(scan,uri, (uridata - uri));
362        }
363       
364        libtrace->format = 0;   
365       
366        for(tmp=formats_list;tmp;tmp=tmp->next) {
367                if (strlen(scan) == strlen(tmp->name) &&
368                                !strncasecmp(scan,
369                                        tmp->name,
370                                        strlen(scan))) {
371                                libtrace->format=tmp;
372                                break;
373                                }
374        }
375        if (libtrace->format == 0) {
376                trace_set_err(libtrace,TRACE_ERR_BAD_FORMAT,
377                                "Unknown format (%s)",scan);
378                return 0;
379        }
380
381        libtrace->format_data = NULL;
382        free(scan);
383        return libtrace;
384
385}
386
387/* Creates a trace output file from a URI.
388 *
389 * @param uri   the uri string describing the output format and destination
390 * @returns opaque pointer to a libtrace_output_t
391 *
392 *  If an error occured when attempting to open the output trace, NULL is
393 *  returned and trace_errno is set.
394 */
395       
396libtrace_out_t *trace_create_output(const char *uri) {
397        libtrace_out_t *libtrace = 
398                        (libtrace_out_t*)malloc(sizeof(struct libtrace_out_t));
399       
400        char *scan = 0;
401        const char *uridata = 0;
402        struct libtrace_format_t *tmp;
403
404        libtrace->err.err_num = TRACE_ERR_NOERROR;
405        strcat(libtrace->err.problem,"Error message set\n");
406       
407        /* parse the URI to determine what sort of event we are dealing with */
408
409        if ((uridata = trace_parse_uri(uri, &scan)) == 0) {
410                trace_set_err_out(libtrace,TRACE_ERR_BAD_FORMAT,
411                                "Bad uri format (%s)",uri);
412                return libtrace;
413        }
414       
415        libtrace->format = NULL;
416        for(tmp=formats_list;tmp;tmp=tmp->next) {
417                if (strlen(scan) == strlen(tmp->name) &&
418                                !strncasecmp(scan,
419                                        tmp->name,
420                                        strlen(scan))) {
421                                libtrace->format=tmp;
422                                break;
423                                }
424        }
425        if (libtrace->format == NULL) {
426                trace_set_err_out(libtrace,TRACE_ERR_BAD_FORMAT,
427                                "Unknown output format (%s)",scan);
428                return libtrace;
429        }
430        libtrace->uridata = strdup(uridata);
431
432
433        /* libtrace->format now contains the type of uri
434         * libtrace->uridata contains the appropriate data for this
435         */
436
437        if (libtrace->format->init_output) {
438                /* 0 on success, -1 on failure */
439                switch(libtrace->format->init_output(libtrace)) {
440                        case -1: /* failure */
441                                free(libtrace);
442                                return libtrace;
443                        case 0: /* success */
444                                break;
445                        default:
446                                assert(!"init_output() should return -1 for failure, or 0 for success");
447                }
448        } else {
449                trace_set_err_out(libtrace,TRACE_ERR_NO_INIT_OUT,
450                                "Format does not support writing (%s)",scan);
451                return libtrace;
452        }
453
454
455        free(scan);
456        libtrace->started=false;
457        return libtrace;
458}
459
460/* Start a trace
461 * @param libtrace      the input trace to start
462 * @returns 0 on success
463 *
464 * This does the work associated with actually starting up
465 * the trace.  it may fail.
466 */
467int trace_start(struct libtrace_t *libtrace)
468{
469        assert(libtrace);
470        if (libtrace->format->start_input) {
471                int ret=libtrace->format->start_input(libtrace);
472                if (ret < 0) {
473                        return ret;
474                }
475        }
476
477        libtrace->started=true;
478        return 0;
479}
480
481int trace_start_output(libtrace_out_t *libtrace) 
482{
483        assert(libtrace);
484        if (libtrace->format->start_output) {
485                int ret=libtrace->format->start_output(libtrace);
486                if (ret < 0) {
487                        return ret;
488                }
489        }
490
491        libtrace->started=true;
492        return 0;
493}
494
495int trace_pause(libtrace_t *libtrace)
496{
497        assert(libtrace);
498        assert(libtrace->started && "BUG: Called trace_pause without calling trace_start first");
499        if (libtrace->format->pause_input)
500                libtrace->format->pause_input(libtrace);
501        libtrace->started=false;
502        return 0;
503}
504
505int trace_config(libtrace_t *libtrace,
506                trace_option_t option,
507                void *value)
508{
509        int ret;
510        if (libtrace->format->config_input) {
511                ret=libtrace->format->config_input(libtrace,option,value);
512                if (ret==0)
513                        return 0;
514        }
515        switch(option) {
516                case TRACE_OPTION_SNAPLEN:
517                        libtrace->snaplen=*(int*)value;
518                        return 0;
519                case TRACE_OPTION_FILTER:
520                        libtrace->filter=(struct libtrace_filter_t *)value;
521                        return 0;
522                case TRACE_OPTION_PROMISC:
523                        trace_set_err(libtrace,TRACE_ERR_OPTION_UNAVAIL,
524                                "Promisc mode is not supported by this format module");
525                        return -1;
526        }
527        trace_set_err(libtrace,TRACE_ERR_UNKNOWN_OPTION,
528                "Unknown option %i", option);
529        return -1;
530}
531
532/* Parses an output options string and calls the appropriate function to deal with output options.
533 *
534 * @param libtrace      the output trace object to apply the options to
535 * @param options       the options string
536 * @returns -1 if option configuration failed, 0 otherwise
537 *
538 * @author Shane Alcock
539 */
540int trace_config_output(struct libtrace_out_t *libtrace, 
541                trace_option_output_t option,
542                void *value) {
543        if (libtrace->format->config_output) {
544                return libtrace->format->config_output(libtrace, option, value);
545        }
546        return -1;
547}
548
549/* Close a trace file, freeing up any resources it may have been using
550 *
551 */
552void trace_destroy(struct libtrace_t *libtrace) {
553        assert(libtrace);
554        if (libtrace->started && libtrace->format->pause_input)
555                libtrace->format->pause_input(libtrace);
556        libtrace->format->fin_input(libtrace);
557        /* need to free things! */
558        free(libtrace->uridata);
559        destroy_tracefifo(libtrace->fifo);
560        free(libtrace);
561}
562
563
564void trace_destroy_dead(struct libtrace_t *libtrace) {
565        assert(libtrace);
566        free(libtrace);
567}
568/* Close an output trace file, freeing up any resources it may have been using
569 *
570 * @param libtrace      the output trace file to be destroyed
571 *
572 * @author Shane Alcock
573 * */
574void trace_destroy_output(struct libtrace_out_t *libtrace) {
575        assert(libtrace);
576        libtrace->format->fin_output(libtrace);
577        free(libtrace->uridata);
578        free(libtrace);
579}
580
581libtrace_packet_t *trace_create_packet() {
582        libtrace_packet_t *packet = 
583                (libtrace_packet_t*)calloc(1,sizeof(libtrace_packet_t));
584        packet->buf_control=TRACE_CTRL_PACKET;
585        return packet;
586}
587
588libtrace_packet_t *trace_copy_packet(const libtrace_packet_t *packet) {
589        libtrace_packet_t *dest = 
590                (libtrace_packet_t *)malloc(sizeof(libtrace_packet_t));
591        dest->trace=packet->trace;
592        dest->buffer=malloc(
593                        trace_get_framing_length(packet)
594                        +trace_get_capture_length(packet));
595        dest->header=dest->buffer;
596        dest->payload=(void*)
597                ((char*)dest->buffer+trace_get_framing_length(packet));
598        dest->size=packet->size;
599        dest->type=packet->type;
600        dest->buf_control=TRACE_CTRL_PACKET;
601        memcpy(dest->header,packet->header,trace_get_framing_length(packet));
602        memcpy(dest->payload,packet->payload,trace_get_capture_length(packet));
603
604        return dest;
605}
606
607/** Destroy a packet object
608 *
609 * sideeffect: sets packet to NULL
610 */
611void trace_destroy_packet(struct libtrace_packet_t **packet) {
612        if ((*packet)->buf_control == TRACE_CTRL_PACKET) {
613                free((*packet)->buffer);
614        }
615        free((*packet));
616        *packet = NULL;
617}       
618
619/* Read one packet from the trace into buffer
620 *
621 * @param libtrace      the libtrace opaque pointer
622 * @param packet        the packet opaque pointer
623 * @returns 0 on EOF, negative value on error
624 *
625 */
626int trace_read_packet(libtrace_t *libtrace, libtrace_packet_t *packet) {
627
628        assert(libtrace && "You called trace_read_packet() with a NULL libtrace parameter!\n");
629        assert(libtrace->started && "BUG: You must call libtrace_start() before trace_read_packet()\n");
630        assert(packet);
631        assert((packet->buf_control==TRACE_CTRL_PACKET || packet->buf_control==TRACE_CTRL_EXTERNAL)&&
632                "BUG: You must allocate a packet using packet_create()");
633     
634        /* Store the trace we are reading from into the packet opaque
635         * structure */
636        packet->trace = libtrace;
637
638
639        if (libtrace->format->read_packet) {
640                do {
641                        packet->size=libtrace->format->read_packet(libtrace,packet);
642                        if (packet->size==(size_t)-1 || packet->size==0)
643                                return packet->size;
644                        if (libtrace->filter) {
645                                /* If the filter doesn't match, read another
646                                 * packet
647                                 */
648                                if (!trace_bpf_filter(libtrace->filter,packet)){
649                                        continue;
650                                }
651                        }
652                        if (libtrace->snaplen>0) {
653                                /* Snap the packet */
654                                trace_set_capture_length(packet,
655                                                libtrace->snaplen);
656                        }
657
658                        return packet->size;
659                } while(1);
660        }
661        packet->size=-1;
662        return -1;
663}
664
665/* Writes a packet to the specified output
666 *
667 * @param libtrace      describes the output format, destination, etc.
668 * @param packet        the packet to be written out
669 * @returns the number of bytes written, -1 if write failed
670 *
671 * @author Shane Alcock
672 * */
673int trace_write_packet(struct libtrace_out_t *libtrace, const struct libtrace_packet_t *packet) {
674        assert(libtrace);
675        assert(packet); 
676        /* Verify the packet is valid */
677        assert(packet->size<65536);
678        assert(packet->size>0);
679        assert(libtrace->started);
680
681        if (libtrace->format->write_packet) {
682                return libtrace->format->write_packet(libtrace, packet);
683        }
684        return -1;
685}
686
687void *trace_get_link(const struct libtrace_packet_t *packet) {
688        return (void *)packet->payload;
689}
690
691/*
692typedef struct legacy_framing {
693        uint64_t        ts;
694        uint32_t        crc;
695        uint32_t        header;
696        uint32_t        data[12]; // pad to 64 bytes
697} legacy_framing_t;
698*/
699
700
701
702
703/* parse an ip or tcp option
704 * @param[in,out] ptr   the pointer to the current option
705 * @param[in,out] len   the length of the remaining buffer
706 * @param[out] type     the type of the option
707 * @param[out] optlen   the length of the option
708 * @param[out] data     the data of the option
709 *
710 * @returns bool true if there is another option (and the fields are filled in)
711 *               or false if this was the last option.
712 *
713 * This updates ptr to point to the next option after this one, and updates
714 * len to be the number of bytes remaining in the options area.  Type is updated
715 * to be the code of this option, and data points to the data of this option,
716 * with optlen saying how many bytes there are.
717 *
718 * @note Beware of fragmented packets.
719 * @author Perry Lorier
720 */
721int trace_get_next_option(unsigned char **ptr,int *len,
722                        unsigned char *type,
723                        unsigned char *optlen,
724                        unsigned char **data)
725{
726        if (*len<=0)
727                return 0;
728        *type=**ptr;
729        switch(*type) {
730                case 0: /* End of options */
731                        return 0;
732                case 1: /* Pad */
733                        (*ptr)++;
734                        (*len)--;
735                        return 1;
736                default:
737                        *optlen = *(*ptr+1);
738                        if (*optlen<2)
739                                return 0; /* I have no idea wtf is going on
740                                           * with these packets
741                                           */
742                        (*len)-=*optlen;
743                        (*data)=(*ptr+2);
744                        (*ptr)+=*optlen;
745                        if (*len<0)
746                                return 0;
747                        return 1;
748        }
749        assert(0);
750}
751
752
753/* Get the current time in DAG time format
754 * @param packet        a pointer to a libtrace_packet structure
755 * @returns a 64 bit timestamp in DAG ERF format (upper 32 bits are the seconds
756 * past 1970-01-01, the lower 32bits are partial seconds)
757 * @author Daniel Lawson
758 */ 
759uint64_t trace_get_erf_timestamp(const libtrace_packet_t *packet) {
760        uint64_t timestamp = 0;
761        double seconds = 0.0;
762        struct timeval ts;
763
764        assert(packet->size>0 && packet->size<65536);
765
766        if (packet->trace->format->get_erf_timestamp) {
767                /* timestamp -> timestamp */
768                timestamp = packet->trace->format->get_erf_timestamp(packet);
769        } else if (packet->trace->format->get_timeval) {
770                /* timeval -> timestamp */
771                ts = packet->trace->format->get_timeval(packet);
772                timestamp = ((((uint64_t)ts.tv_sec) << 32) + \
773                                (((uint64_t)ts.tv_usec * UINT_MAX)/1000000));
774        } else if (packet->trace->format->get_seconds) {
775                /* seconds -> timestamp */
776                seconds = packet->trace->format->get_seconds(packet);
777                timestamp = ((uint64_t)((uint32_t)seconds) << 32) + \
778                      (uint64_t)(( seconds - (uint32_t)seconds   ) * UINT_MAX);
779        }
780        return timestamp;
781}
782
783/* Get the current time in struct timeval
784 * @param packet        a pointer to a libtrace_packet structure
785 *
786 * @returns time that this packet was seen in a struct timeval
787 * @author Daniel Lawson
788 * @author Perry Lorier
789 */ 
790struct timeval trace_get_timeval(const libtrace_packet_t *packet) {
791        struct timeval tv;
792        uint64_t ts = 0;
793        double seconds = 0.0;
794        assert(packet->size>0 && packet->size<65536);
795        if (packet->trace->format->get_timeval) {
796                /* timeval -> timeval */
797                tv = packet->trace->format->get_timeval(packet);
798        } else if (packet->trace->format->get_erf_timestamp) {
799                /* timestamp -> timeval */
800                ts = packet->trace->format->get_erf_timestamp(packet);
801#if __BYTE_ORDER == __BIG_ENDIAN
802                tv.tv_sec = ts & 0xFFFFFFFF;
803#elif __BYTE_ORDER == __LITTLE_ENDIAN
804                tv.tv_sec = ts >> 32;
805#else
806#error "What on earth are you running this on?"
807#endif
808                tv.tv_usec = ((ts&0xFFFFFFFF)*1000000)>>32;
809                if (tv.tv_usec >= 1000000) {
810                        tv.tv_usec -= 1000000;
811                        tv.tv_sec += 1;
812                }
813        } else if (packet->trace->format->get_seconds) {
814                /* seconds -> timeval */
815                seconds = packet->trace->format->get_seconds(packet);
816                tv.tv_sec = (uint32_t)seconds;
817                tv.tv_usec = (uint32_t)(((seconds - tv.tv_sec) * 1000000)/UINT_MAX);
818        }
819
820        return tv;
821}
822
823/* Get the current time in floating point seconds
824 * @param packet        a pointer to a libtrace_packet structure
825 * @returns time that this packet was seen in 64bit floating point seconds
826 * @author Perry Lorier
827 */ 
828double trace_get_seconds(const struct libtrace_packet_t *packet) {
829        double seconds = 0.0;
830        uint64_t ts = 0;
831        struct timeval tv;
832
833        assert(packet->size>0 && packet->size<65536);
834       
835        if (packet->trace->format->get_seconds) {
836                /* seconds->seconds */
837                seconds = packet->trace->format->get_seconds(packet);
838        } else if (packet->trace->format->get_erf_timestamp) {
839                /* timestamp -> seconds */
840                ts = packet->trace->format->get_erf_timestamp(packet);
841                seconds =  (ts>>32) + ((ts & UINT_MAX)*1.0 / UINT_MAX);
842        } else if (packet->trace->format->get_timeval) {
843                /* timeval -> seconds */
844                tv = packet->trace->format->get_timeval(packet);
845                seconds = tv.tv_sec + ((tv.tv_usec * 1.0) / 1000000);
846        }
847
848        return seconds;
849}
850
851size_t trace_get_capture_length(const libtrace_packet_t *packet) {
852
853        assert(packet->size<65536);
854
855        if (packet->trace->format->get_capture_length) {
856                return packet->trace->format->get_capture_length(packet);
857        }
858        return -1;
859}
860       
861/* Get the size of the packet as it was seen on the wire.
862 * @param packet        a pointer to a libtrace_packet structure
863 *
864 * @returns the size of the packet as it was on the wire.
865 * @author Perry Lorier
866 * @author Daniel Lawson
867 * @note Due to the trace being a header capture, or anonymisation this may
868 * not be the same as the Capture Len.
869 */ 
870size_t trace_get_wire_length(const libtrace_packet_t *packet){
871        assert(packet->size>0 && packet->size<65536);
872
873        if (packet->trace->format->get_wire_length) {
874                return packet->trace->format->get_wire_length(packet);
875        }
876        return -1;
877
878}
879
880/* Get the length of the capture framing headers.
881 * @param packet        the packet opaque pointer
882 * @returns the size of the packet as it was on the wire.
883 * @author Perry Lorier
884 * @author Daniel Lawson
885 * @note this length corresponds to the difference between the size of a
886 * captured packet in memory, and the captured length of the packet
887 */ 
888SIMPLE_FUNCTION
889size_t trace_get_framing_length(const libtrace_packet_t *packet) {
890        if (packet->trace->format->get_framing_length) {
891                return packet->trace->format->get_framing_length(packet);
892        }
893        return -1;
894}
895
896
897/* Get the type of the link layer
898 * @param packet        a pointer to a libtrace_packet structure
899 * @returns libtrace_linktype_t
900 * @author Perry Lorier
901 * @author Daniel Lawson
902 */
903libtrace_linktype_t trace_get_link_type(const libtrace_packet_t *packet ) {
904        if (packet->trace->format->get_link_type) {
905                return packet->trace->format->get_link_type(packet);
906        }
907        return (libtrace_linktype_t)-1;
908}
909
910/* process a libtrace event
911 * @param trace the libtrace opaque pointer
912 * @param packet the libtrace_packet opaque pointer
913 * @returns
914 *  TRACE_EVENT_IOWAIT  Waiting on I/O on fd
915 *  TRACE_EVENT_SLEEP   Next event in seconds
916 *  TRACE_EVENT_PACKET  Packet arrived in buffer with size size
917 *  TRACE_EVENT_TERMINATE Trace terminated (perhaps with an error condition)
918 * FIXME currently keeps a copy of the packet inside the trace pointer,
919 * which in turn is stored inside the new packet object...
920 * @author Perry Lorier
921 */
922struct libtrace_eventobj_t trace_event(struct libtrace_t *trace, 
923                struct libtrace_packet_t *packet) {
924        struct libtrace_eventobj_t event = {TRACE_EVENT_IOWAIT,0,0.0,0};
925
926        if (!trace) {
927                fprintf(stderr,"You called trace_event() with a NULL trace object!\n");
928        }
929        assert(trace);
930        assert(packet);
931
932        /* Store the trace we are reading from into the packet opaque
933         * structure */
934        packet->trace = trace;
935
936        if (packet->trace->format->trace_event) {
937                return packet->trace->format->trace_event(trace,packet);
938        } else {
939                return event;
940        }
941
942}
943
944/* setup a BPF filter
945 * @param filterstring a char * containing the bpf filter string
946 * @returns opaque pointer pointer to a libtrace_filter_t object
947 * @author Daniel Lawson
948 */
949struct libtrace_filter_t *trace_bpf_setfilter(const char *filterstring) {
950#if HAVE_BPF
951        struct libtrace_filter_t *filter = (struct libtrace_filter_t*)
952                                malloc(sizeof(struct libtrace_filter_t));
953        filter->filterstring = strdup(filterstring);
954        filter->flag = 0;
955        return filter;
956#else
957        fprintf(stderr,"This version of libtrace does not have bpf filter support\n");
958        return 0;
959#endif
960}
961
962/* compile a bpf filter, now we know what trace it's on
963 * @internal
964 *
965 * @returns -1 on error, 0 on success
966 */
967int trace_bpf_compile(libtrace_filter_t *filter,
968                const libtrace_packet_t *packet ) {
969#if HAVE_BPF
970        void *linkptr = 0;
971        assert(filter);
972
973        /* If this isn't a real packet, then fail */
974        linkptr = trace_get_link(packet);
975        if (!linkptr) {
976                trace_set_err(packet->trace,
977                                TRACE_ERR_BAD_PACKET,"Packet has no payload");
978                return -1;
979        }
980       
981        if (filter->filterstring && ! filter->flag) {
982                pcap_t *pcap;
983                libtrace_linktype_t linktype=trace_get_link_type(packet);
984                if (linktype==(libtrace_linktype_t)-1) {
985                        trace_set_err(packet->trace,TRACE_ERR_BAD_PACKET,
986                                        "Packet has an unknown linktype");
987                        return -1;
988                }
989                if (libtrace_to_pcap_dlt(linktype) == -1) {
990                        trace_set_err(packet->trace,TRACE_ERR_BAD_PACKET,
991                                        "Unknown pcap equivilent linktype");
992                        return -1;
993                }
994                pcap=(pcap_t *)pcap_open_dead(
995                                libtrace_to_pcap_dlt(linktype),
996                                1500);
997                /* build filter */
998                if (pcap_compile( pcap, &filter->filter, filter->filterstring, 
999                                        1, 0)) {
1000                        pcap_close(pcap);
1001                        trace_set_err(packet->trace,TRACE_ERR_BAD_PACKET,
1002                                        "Packet has no payload");
1003                        return -1;
1004                }
1005                pcap_close(pcap);
1006                filter->flag=1;
1007        }
1008        return 0;
1009#else
1010        assert(!"This should never be called when BPF not enabled");
1011#endif
1012}
1013
1014int trace_bpf_filter(struct libtrace_filter_t *filter,
1015                        const struct libtrace_packet_t *packet) {
1016#if HAVE_BPF
1017        void *linkptr = 0;
1018        int clen = 0;
1019        assert(filter);
1020        assert(packet);
1021        linkptr = trace_get_link(packet);
1022        if (!linkptr) {
1023                return 0;
1024        }
1025
1026        /* We need to compile it now, because before we didn't know what the
1027         * link type was
1028         */
1029        if (trace_bpf_compile(filter,packet)==-1)
1030                return -1;
1031       
1032        clen = trace_get_capture_length(packet);
1033
1034        assert(filter->flag);
1035        return bpf_filter(filter->filter.bf_insns, linkptr, clen, clen);
1036#else
1037        fprintf(stderr,"This version of libtrace does not have bpf filter support\n");
1038        return 0;
1039#endif
1040}
1041
1042/* Set the direction flag, if it has one
1043 * @param packet the packet opaque pointer
1044 * @param direction the new direction (0,1,2,3)
1045 * @returns a signed value containing the direction flag, or -1 if this is not supported
1046 * @author Daniel Lawson
1047 */
1048int8_t trace_set_direction(struct libtrace_packet_t *packet, int8_t direction) {
1049        assert(packet);
1050        assert(packet->size>0 && packet->size<65536);
1051        if (packet->trace->format->set_direction) {
1052                return packet->trace->format->set_direction(packet,direction);
1053        }
1054        return -1;
1055}
1056
1057/* Get the direction flag, if it has one
1058 * @param packet a pointer to a libtrace_packet structure
1059 * @returns a signed value containing the direction flag, or -1 if this is not supported
1060 * The direction is defined as 0 for packets originating locally (ie, outbound)
1061 * and 1 for packets originating remotely (ie, inbound).
1062 * Other values are possible, which might be overloaded to mean special things
1063 * for a special trace.
1064 * @author Daniel Lawson
1065 */
1066int8_t trace_get_direction(const struct libtrace_packet_t *packet) {
1067        assert(packet);
1068        assert(packet->size>0 && packet->size<65536);
1069        if (packet->trace->format->get_direction) {
1070                return packet->trace->format->get_direction(packet);
1071        }
1072        return -1;
1073}
1074
1075#define ROOT_SERVER(x) ((x) < 512)
1076#define ROOT_CLIENT(x) ((512 <= (x)) && ((x) < 1024))
1077#define NONROOT_SERVER(x) ((x) >= 5000)
1078#define NONROOT_CLIENT(x) ((1024 <= (x)) && ((x) < 5000))
1079#define DYNAMIC(x) ((49152 < (x)) && ((x) < 65535))
1080#define SERVER(x) ROOT_SERVER(x) || NONROOT_SERVER(x)
1081#define CLIENT(x) ROOT_CLIENT(x) || NONROOT_CLIENT(x)
1082
1083/* Attempt to deduce the 'server' port
1084 * @param protocol the IP protocol (eg, 6 or 17 for TCP or UDP)
1085 * @param source the TCP or UDP source port
1086 * @param dest the TCP or UDP destination port
1087 * @returns a hint as to which port is the server port
1088 * @author Daniel Lawson
1089 */
1090int8_t trace_get_server_port(uint8_t protocol __attribute__((unused)), uint16_t source, uint16_t dest) {
1091        /*
1092         * * If the ports are equal, return DEST
1093         * * Check for well-known ports in the given protocol
1094         * * Root server ports: 0 - 511
1095         * * Root client ports: 512 - 1023
1096         * * non-root client ports: 1024 - 4999
1097         * * non-root server ports: 5000+
1098         * * Check for static ranges: 1024 - 49151
1099         * * Check for dynamic ranges: 49152 - 65535
1100         * * flip a coin.
1101         */
1102       
1103        /* equal */
1104        if (source == dest)
1105                return USE_DEST;
1106
1107        /* root server port, 0 - 511 */
1108        if (ROOT_SERVER(source) && ROOT_SERVER(dest)) {
1109                if (source < dest)
1110                        return USE_SOURCE;
1111                return USE_DEST;
1112        }
1113
1114        if (ROOT_SERVER(source) && !ROOT_SERVER(dest))
1115                return USE_SOURCE;
1116        if (!ROOT_SERVER(source) && ROOT_SERVER(dest))
1117                return USE_DEST;
1118
1119        /* non-root server */
1120        if (NONROOT_SERVER(source) && NONROOT_SERVER(dest)) {
1121                if (source < dest)
1122                        return USE_SOURCE;
1123                return USE_DEST;
1124        }
1125        if (NONROOT_SERVER(source) && !NONROOT_SERVER(dest))
1126                return USE_SOURCE;
1127        if (!NONROOT_SERVER(source) && NONROOT_SERVER(dest))
1128                return USE_DEST;
1129
1130        /* root client */
1131        if (ROOT_CLIENT(source) && ROOT_CLIENT(dest)) {
1132                if (source < dest)
1133                        return USE_SOURCE;
1134                return USE_DEST;
1135        }
1136        if (ROOT_CLIENT(source) && !ROOT_CLIENT(dest)) {
1137                /* prefer root-client over nonroot-client */
1138                if (NONROOT_CLIENT(dest))
1139                        return USE_SOURCE;
1140                return USE_DEST;
1141        }
1142        if (!ROOT_CLIENT(source) && ROOT_CLIENT(dest)) {
1143                /* prefer root-client over nonroot-client */
1144                if (NONROOT_CLIENT(source))
1145                        return USE_DEST;
1146                return USE_SOURCE;
1147        }
1148       
1149        /* nonroot client */
1150        if (NONROOT_CLIENT(source) && NONROOT_CLIENT(dest)) {
1151                if (source < dest) 
1152                        return USE_SOURCE;
1153                return USE_DEST;
1154        }
1155        if (NONROOT_CLIENT(source) && !NONROOT_CLIENT(dest))
1156                return USE_DEST;
1157        if (!NONROOT_CLIENT(source) && NONROOT_CLIENT(dest))
1158                return USE_SOURCE;
1159
1160        /* dynamic range */
1161        if (DYNAMIC(source) && DYNAMIC(dest))
1162                if (source < dest)
1163                        return USE_SOURCE;
1164                return USE_DEST;
1165        if (DYNAMIC(source) && !DYNAMIC(dest))
1166                return USE_DEST;
1167        if (!DYNAMIC(source) && DYNAMIC(dest))
1168                return USE_SOURCE;
1169        /*
1170        if (SERVER(source) && CLIENT(dest))
1171                return USE_SOURCE;
1172       
1173        if (SERVER(dest) && CLIENT(source))
1174                return USE_DEST;
1175        if (ROOT_SERVER(source) && !ROOT_SERVER(dest))
1176                return USE_SOURCE;
1177        if (ROOT_SERVER(dest) && !ROOT_SERVER(source))
1178                return USE_DEST;
1179        */
1180        /* failing that test... */
1181        if (source < dest) {
1182                return USE_SOURCE;
1183        } 
1184        return USE_DEST;
1185       
1186}
1187
1188/* Truncate the packet at the suggested length
1189 * @param packet        the packet opaque pointer
1190 * @param size          the new length of the packet
1191 * @returns the new size of the packet
1192 * @note size and the return size refer to the network-level payload of the
1193 * packet, and do not include any capture headers. For example, to truncate a
1194 * packet after the IP header, set size to sizeof(ethernet_header) +
1195 * sizeof(ip_header)
1196 * @note If the original network-level payload is smaller than size, then the
1197 * original size is returned and the packet is left unchanged.
1198 * @author Daniel Lawson
1199 */
1200size_t trace_set_capture_length(struct libtrace_packet_t *packet, size_t size) {
1201        assert(packet);
1202        assert(packet->size>0 && packet->size<65536);
1203
1204        if (packet->trace->format->set_capture_length) {
1205                int caplen=packet->trace->format->set_capture_length(packet,size);
1206                if (caplen!=-1) {
1207                        packet->size=trace_get_framing_length(packet)+caplen;
1208                }
1209                return caplen;
1210        }
1211
1212        return -1;
1213}
1214
1215const char * trace_parse_uri(const char *uri, char **format) {
1216        const char *uridata = 0;
1217       
1218        if((uridata = strchr(uri,':')) == NULL) {
1219                /* badly formed URI - needs a : */
1220                return 0;
1221        }
1222
1223        if ((uridata - uri) > URI_PROTO_LINE) {
1224                /* badly formed URI - uri type is too long */
1225                return 0;
1226        }
1227
1228        *format=xstrndup(uri, (uridata - uri));
1229
1230        /* push uridata past the delimiter */
1231        uridata++;
1232       
1233        return uridata;
1234}
1235
1236enum base_format_t trace_get_format(struct libtrace_packet_t *packet) 
1237{
1238        assert(packet);
1239
1240        return packet->trace->format->type;
1241}
1242       
1243libtrace_err_t trace_get_err(libtrace_t *trace)
1244{
1245        libtrace_err_t err = trace->err;
1246        trace->err.err_num = 0; /* "OK" */
1247        trace->err.problem[0]='\0';
1248        return err;
1249}
1250
1251bool trace_is_err(libtrace_t *trace)
1252{
1253        return trace->err.err_num != 0;
1254}
1255
1256void trace_perror(libtrace_t *trace,const char *msg,...)
1257{
1258        char buf[256];
1259        va_list va;
1260        va_start(va,msg);
1261        vsnprintf(buf,sizeof(buf),msg,va);
1262        va_end(va);
1263        if(trace->err.err_num) {
1264                fprintf(stderr,"%s(%s): %s\n",
1265                                buf,trace->uridata,trace->err.problem);
1266        } else {
1267                fprintf(stderr,"%s(%s): No error\n",
1268                                buf,trace->uridata);
1269        }
1270}
1271
1272libtrace_err_t trace_get_err_output(libtrace_out_t *trace)
1273{
1274        libtrace_err_t err = trace->err;
1275        trace->err.err_num = 0; /* "OK" */
1276        trace->err.problem[0]='\0';
1277        return err;
1278}
1279
1280bool trace_is_err_output(libtrace_out_t *trace)
1281{
1282        return trace->err.err_num != 0;
1283}
1284
1285void trace_perror_output(libtrace_out_t *trace,const char *msg,...)
1286{
1287        char buf[256];
1288        va_list va;
1289        va_start(va,msg);
1290        vsnprintf(buf,sizeof(buf),msg,va);
1291        va_end(va);
1292        if(trace->err.err_num) {
1293                fprintf(stderr,"%s(%s): %s\n",
1294                                buf,trace->uridata,trace->err.problem);
1295        } else {
1296                fprintf(stderr,"%s(%s): No error\n",buf,trace->uridata);
1297        }
1298}
1299
1300int trace_seek_erf_timestamp(libtrace_t *trace, uint64_t ts)
1301{
1302        if (trace->format->seek_erf) {
1303                return trace->format->seek_erf(trace,ts);
1304        }
1305        else {
1306                if (trace->format->seek_timeval) {
1307                        struct timeval tv;
1308#if __BYTE_ORDER == __BIG_ENDIAN
1309                        tv.tv_sec = ts & 0xFFFFFFFF;
1310#elif __BYTE_ORDER == __LITTLE_ENDIAN
1311                        tv.tv_sec = ts >> 32;
1312#else
1313#error "What on earth are you running this on?"
1314#endif
1315                        tv.tv_usec = ((ts&0xFFFFFFFF)*1000000)>>32;
1316                        if (tv.tv_usec >= 1000000) {
1317                                tv.tv_usec -= 1000000;
1318                                tv.tv_sec += 1;
1319                        }
1320                        return trace->format->seek_timeval(trace,tv);
1321                }
1322                if (trace->format->seek_seconds) {
1323                        double seconds = 
1324                                (ts>>32) + ((ts & UINT_MAX)*1.0 / UINT_MAX);
1325                        return trace->format->seek_seconds(trace,seconds);
1326                }
1327                trace_set_err(trace,
1328                                TRACE_ERR_OPTION_UNAVAIL,
1329                                "Feature unimplemented");
1330                return -1;
1331        }
1332}
1333
1334int trace_seek_seconds(libtrace_t *trace, double seconds)
1335{
1336        if (trace->format->seek_seconds) {
1337                return trace->format->seek_seconds(trace,seconds);
1338        }
1339        else {
1340                if (trace->format->seek_timeval) {
1341                        struct timeval tv;
1342                        tv.tv_sec = (uint32_t)seconds;
1343                        tv.tv_usec = (uint32_t)(((seconds - tv.tv_sec) * 1000000)/UINT_MAX);
1344                        return trace->format->seek_timeval(trace,tv);
1345                }
1346                if (trace->format->seek_erf) {
1347                        uint64_t timestamp = 
1348                                ((uint64_t)((uint32_t)seconds) << 32) + \
1349                            (uint64_t)(( seconds - (uint32_t)seconds   ) * UINT_MAX);
1350                        return trace->format->seek_erf(trace,timestamp);
1351                }
1352                trace_set_err(trace,
1353                                TRACE_ERR_OPTION_UNAVAIL,
1354                                "Feature unimplemented");
1355                return -1;
1356        }
1357}
1358
1359int trace_seek_timeval(libtrace_t *trace, struct timeval tv)
1360{
1361        if (trace->format->seek_timeval) {
1362                return trace->format->seek_timeval(trace,tv);
1363        }
1364        else {
1365                if (trace->format->seek_erf) {
1366                        uint64_t timestamp = ((((uint64_t)tv.tv_sec) << 32) + \
1367                                (((uint64_t)tv.tv_usec * UINT_MAX)/1000000));
1368                        return trace->format->seek_erf(trace,timestamp);
1369                }
1370                if (trace->format->seek_seconds) {
1371                        double seconds = tv.tv_sec + ((tv.tv_usec * 1.0)/1000000);
1372                        return trace->format->seek_seconds(trace,seconds);
1373                }
1374                trace_set_err(trace,
1375                                TRACE_ERR_OPTION_UNAVAIL,
1376                                "Feature unimplemented");
1377                return -1;
1378        }
1379}
1380
1381char *trace_ether_ntoa(const uint8_t *addr, char *buf)
1382{
1383        char *buf2 = buf;
1384        static char staticbuf[17]={0,};
1385        if (!buf2)
1386                buf2=staticbuf;
1387        sprintf(buf2,"%02x:%02x:%02x:%02x:%02x:%02x",
1388                        addr[0],addr[1],addr[2],
1389                        addr[3],addr[4],addr[5]);
1390        return buf2;
1391}
1392
1393uint8_t *trace_ether_aton(const char *buf, uint8_t *addr)
1394{
1395        uint8_t *buf2 = addr;
1396        unsigned int tmp[6];
1397        static uint8_t staticaddr[6];
1398        if (!buf2)
1399                buf2=staticaddr;
1400        sscanf(buf,"%x:%x:%x:%x:%x:%x",
1401                        &tmp[0],&tmp[1],&tmp[2],
1402                        &tmp[3],&tmp[4],&tmp[5]);
1403        buf2[0]=tmp[0]; buf2[1]=tmp[1]; buf2[2]=tmp[2];
1404        buf2[3]=tmp[3]; buf2[4]=tmp[4]; buf2[5]=tmp[5];
1405        return buf2;
1406}
1407
Note: See TracBrowser for help on using the repository browser.