source: lib/trace.c @ abc8e94

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

Fix a security flaw
Tidy up some code

  • 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        trace_set_err(packet->trace,TRACE_ERR_OPTION_UNAVAIL,
1012                                "Feature unavailable");
1013        return -1;
1014#endif
1015}
1016
1017int trace_bpf_filter(struct libtrace_filter_t *filter,
1018                        const struct libtrace_packet_t *packet) {
1019#if HAVE_BPF
1020        void *linkptr = 0;
1021        int clen = 0;
1022        assert(filter);
1023        assert(packet);
1024        linkptr = trace_get_link(packet);
1025        if (!linkptr) {
1026                return 0;
1027        }
1028
1029        /* We need to compile it now, because before we didn't know what the
1030         * link type was
1031         */
1032        if (trace_bpf_compile(filter,packet)==-1)
1033                return -1;
1034       
1035        clen = trace_get_capture_length(packet);
1036
1037        assert(filter->flag);
1038        return bpf_filter(filter->filter.bf_insns, linkptr, clen, clen);
1039#else
1040        fprintf(stderr,"This version of libtrace does not have bpf filter support\n");
1041        return 0;
1042#endif
1043}
1044
1045/* Set the direction flag, if it has one
1046 * @param packet the packet opaque pointer
1047 * @param direction the new direction (0,1,2,3)
1048 * @returns a signed value containing the direction flag, or -1 if this is not supported
1049 * @author Daniel Lawson
1050 */
1051int8_t trace_set_direction(struct libtrace_packet_t *packet, int8_t direction) {
1052        assert(packet);
1053        assert(packet->size>0 && packet->size<65536);
1054        if (packet->trace->format->set_direction) {
1055                return packet->trace->format->set_direction(packet,direction);
1056        }
1057        return -1;
1058}
1059
1060/* Get the direction flag, if it has one
1061 * @param packet a pointer to a libtrace_packet structure
1062 * @returns a signed value containing the direction flag, or -1 if this is not supported
1063 * The direction is defined as 0 for packets originating locally (ie, outbound)
1064 * and 1 for packets originating remotely (ie, inbound).
1065 * Other values are possible, which might be overloaded to mean special things
1066 * for a special trace.
1067 * @author Daniel Lawson
1068 */
1069int8_t trace_get_direction(const struct libtrace_packet_t *packet) {
1070        assert(packet);
1071        assert(packet->size>0 && packet->size<65536);
1072        if (packet->trace->format->get_direction) {
1073                return packet->trace->format->get_direction(packet);
1074        }
1075        return -1;
1076}
1077
1078#define ROOT_SERVER(x) ((x) < 512)
1079#define ROOT_CLIENT(x) ((512 <= (x)) && ((x) < 1024))
1080#define NONROOT_SERVER(x) ((x) >= 5000)
1081#define NONROOT_CLIENT(x) ((1024 <= (x)) && ((x) < 5000))
1082#define DYNAMIC(x) ((49152 < (x)) && ((x) < 65535))
1083#define SERVER(x) ROOT_SERVER(x) || NONROOT_SERVER(x)
1084#define CLIENT(x) ROOT_CLIENT(x) || NONROOT_CLIENT(x)
1085
1086/* Attempt to deduce the 'server' port
1087 * @param protocol the IP protocol (eg, 6 or 17 for TCP or UDP)
1088 * @param source the TCP or UDP source port
1089 * @param dest the TCP or UDP destination port
1090 * @returns a hint as to which port is the server port
1091 * @author Daniel Lawson
1092 */
1093int8_t trace_get_server_port(uint8_t protocol __attribute__((unused)), uint16_t source, uint16_t dest) {
1094        /*
1095         * * If the ports are equal, return DEST
1096         * * Check for well-known ports in the given protocol
1097         * * Root server ports: 0 - 511
1098         * * Root client ports: 512 - 1023
1099         * * non-root client ports: 1024 - 4999
1100         * * non-root server ports: 5000+
1101         * * Check for static ranges: 1024 - 49151
1102         * * Check for dynamic ranges: 49152 - 65535
1103         * * flip a coin.
1104         */
1105       
1106        /* equal */
1107        if (source == dest)
1108                return USE_DEST;
1109
1110        /* root server port, 0 - 511 */
1111        if (ROOT_SERVER(source) && ROOT_SERVER(dest)) {
1112                if (source < dest)
1113                        return USE_SOURCE;
1114                return USE_DEST;
1115        }
1116
1117        if (ROOT_SERVER(source) && !ROOT_SERVER(dest))
1118                return USE_SOURCE;
1119        if (!ROOT_SERVER(source) && ROOT_SERVER(dest))
1120                return USE_DEST;
1121
1122        /* non-root server */
1123        if (NONROOT_SERVER(source) && NONROOT_SERVER(dest)) {
1124                if (source < dest)
1125                        return USE_SOURCE;
1126                return USE_DEST;
1127        }
1128        if (NONROOT_SERVER(source) && !NONROOT_SERVER(dest))
1129                return USE_SOURCE;
1130        if (!NONROOT_SERVER(source) && NONROOT_SERVER(dest))
1131                return USE_DEST;
1132
1133        /* root client */
1134        if (ROOT_CLIENT(source) && ROOT_CLIENT(dest)) {
1135                if (source < dest)
1136                        return USE_SOURCE;
1137                return USE_DEST;
1138        }
1139        if (ROOT_CLIENT(source) && !ROOT_CLIENT(dest)) {
1140                /* prefer root-client over nonroot-client */
1141                if (NONROOT_CLIENT(dest))
1142                        return USE_SOURCE;
1143                return USE_DEST;
1144        }
1145        if (!ROOT_CLIENT(source) && ROOT_CLIENT(dest)) {
1146                /* prefer root-client over nonroot-client */
1147                if (NONROOT_CLIENT(source))
1148                        return USE_DEST;
1149                return USE_SOURCE;
1150        }
1151       
1152        /* nonroot client */
1153        if (NONROOT_CLIENT(source) && NONROOT_CLIENT(dest)) {
1154                if (source < dest) 
1155                        return USE_SOURCE;
1156                return USE_DEST;
1157        }
1158        if (NONROOT_CLIENT(source) && !NONROOT_CLIENT(dest))
1159                return USE_DEST;
1160        if (!NONROOT_CLIENT(source) && NONROOT_CLIENT(dest))
1161                return USE_SOURCE;
1162
1163        /* dynamic range */
1164        if (DYNAMIC(source) && DYNAMIC(dest))
1165                if (source < dest)
1166                        return USE_SOURCE;
1167                return USE_DEST;
1168        if (DYNAMIC(source) && !DYNAMIC(dest))
1169                return USE_DEST;
1170        if (!DYNAMIC(source) && DYNAMIC(dest))
1171                return USE_SOURCE;
1172        /*
1173        if (SERVER(source) && CLIENT(dest))
1174                return USE_SOURCE;
1175       
1176        if (SERVER(dest) && CLIENT(source))
1177                return USE_DEST;
1178        if (ROOT_SERVER(source) && !ROOT_SERVER(dest))
1179                return USE_SOURCE;
1180        if (ROOT_SERVER(dest) && !ROOT_SERVER(source))
1181                return USE_DEST;
1182        */
1183        /* failing that test... */
1184        if (source < dest) {
1185                return USE_SOURCE;
1186        } 
1187        return USE_DEST;
1188       
1189}
1190
1191/* Truncate the packet at the suggested length
1192 * @param packet        the packet opaque pointer
1193 * @param size          the new length of the packet
1194 * @returns the new size of the packet
1195 * @note size and the return size refer to the network-level payload of the
1196 * packet, and do not include any capture headers. For example, to truncate a
1197 * packet after the IP header, set size to sizeof(ethernet_header) +
1198 * sizeof(ip_header)
1199 * @note If the original network-level payload is smaller than size, then the
1200 * original size is returned and the packet is left unchanged.
1201 * @author Daniel Lawson
1202 */
1203size_t trace_set_capture_length(struct libtrace_packet_t *packet, size_t size) {
1204        assert(packet);
1205        assert(packet->size>0 && packet->size<65536);
1206
1207        if (packet->trace->format->set_capture_length) {
1208                int caplen=packet->trace->format->set_capture_length(packet,size);
1209                if (caplen!=-1) {
1210                        packet->size=trace_get_framing_length(packet)+caplen;
1211                }
1212                return caplen;
1213        }
1214
1215        return -1;
1216}
1217
1218const char * trace_parse_uri(const char *uri, char **format) {
1219        const char *uridata = 0;
1220       
1221        if((uridata = strchr(uri,':')) == NULL) {
1222                /* badly formed URI - needs a : */
1223                return 0;
1224        }
1225
1226        if ((uridata - uri) > URI_PROTO_LINE) {
1227                /* badly formed URI - uri type is too long */
1228                return 0;
1229        }
1230
1231        *format=xstrndup(uri, (uridata - uri));
1232
1233        /* push uridata past the delimiter */
1234        uridata++;
1235       
1236        return uridata;
1237}
1238
1239enum base_format_t trace_get_format(struct libtrace_packet_t *packet) 
1240{
1241        assert(packet);
1242
1243        return packet->trace->format->type;
1244}
1245       
1246libtrace_err_t trace_get_err(libtrace_t *trace)
1247{
1248        libtrace_err_t err = trace->err;
1249        trace->err.err_num = 0; /* "OK" */
1250        trace->err.problem[0]='\0';
1251        return err;
1252}
1253
1254bool trace_is_err(libtrace_t *trace)
1255{
1256        return trace->err.err_num != 0;
1257}
1258
1259void trace_perror(libtrace_t *trace,const char *msg,...)
1260{
1261        char buf[256];
1262        va_list va;
1263        va_start(va,msg);
1264        vsnprintf(buf,sizeof(buf),msg,va);
1265        va_end(va);
1266        if(trace->err.err_num) {
1267                fprintf(stderr,"%s(%s): %s\n",
1268                                buf,trace->uridata,trace->err.problem);
1269        } else {
1270                fprintf(stderr,"%s(%s): No error\n",
1271                                buf,trace->uridata);
1272        }
1273}
1274
1275libtrace_err_t trace_get_err_output(libtrace_out_t *trace)
1276{
1277        libtrace_err_t err = trace->err;
1278        trace->err.err_num = 0; /* "OK" */
1279        trace->err.problem[0]='\0';
1280        return err;
1281}
1282
1283bool trace_is_err_output(libtrace_out_t *trace)
1284{
1285        return trace->err.err_num != 0;
1286}
1287
1288void trace_perror_output(libtrace_out_t *trace,const char *msg,...)
1289{
1290        char buf[256];
1291        va_list va;
1292        va_start(va,msg);
1293        vsnprintf(buf,sizeof(buf),msg,va);
1294        va_end(va);
1295        if(trace->err.err_num) {
1296                fprintf(stderr,"%s(%s): %s\n",
1297                                buf,trace->uridata,trace->err.problem);
1298        } else {
1299                fprintf(stderr,"%s(%s): No error\n",buf,trace->uridata);
1300        }
1301}
1302
1303int trace_seek_erf_timestamp(libtrace_t *trace, uint64_t ts)
1304{
1305        if (trace->format->seek_erf) {
1306                return trace->format->seek_erf(trace,ts);
1307        }
1308        else {
1309                if (trace->format->seek_timeval) {
1310                        struct timeval tv;
1311#if __BYTE_ORDER == __BIG_ENDIAN
1312                        tv.tv_sec = ts & 0xFFFFFFFF;
1313#elif __BYTE_ORDER == __LITTLE_ENDIAN
1314                        tv.tv_sec = ts >> 32;
1315#else
1316#error "What on earth are you running this on?"
1317#endif
1318                        tv.tv_usec = ((ts&0xFFFFFFFF)*1000000)>>32;
1319                        if (tv.tv_usec >= 1000000) {
1320                                tv.tv_usec -= 1000000;
1321                                tv.tv_sec += 1;
1322                        }
1323                        return trace->format->seek_timeval(trace,tv);
1324                }
1325                if (trace->format->seek_seconds) {
1326                        double seconds = 
1327                                (ts>>32) + ((ts & UINT_MAX)*1.0 / UINT_MAX);
1328                        return trace->format->seek_seconds(trace,seconds);
1329                }
1330                trace_set_err(trace,
1331                                TRACE_ERR_OPTION_UNAVAIL,
1332                                "Feature unimplemented");
1333                return -1;
1334        }
1335}
1336
1337int trace_seek_seconds(libtrace_t *trace, double seconds)
1338{
1339        if (trace->format->seek_seconds) {
1340                return trace->format->seek_seconds(trace,seconds);
1341        }
1342        else {
1343                if (trace->format->seek_timeval) {
1344                        struct timeval tv;
1345                        tv.tv_sec = (uint32_t)seconds;
1346                        tv.tv_usec = (uint32_t)(((seconds - tv.tv_sec) * 1000000)/UINT_MAX);
1347                        return trace->format->seek_timeval(trace,tv);
1348                }
1349                if (trace->format->seek_erf) {
1350                        uint64_t timestamp = 
1351                                ((uint64_t)((uint32_t)seconds) << 32) + \
1352                            (uint64_t)(( seconds - (uint32_t)seconds   ) * UINT_MAX);
1353                        return trace->format->seek_erf(trace,timestamp);
1354                }
1355                trace_set_err(trace,
1356                                TRACE_ERR_OPTION_UNAVAIL,
1357                                "Feature unimplemented");
1358                return -1;
1359        }
1360}
1361
1362int trace_seek_timeval(libtrace_t *trace, struct timeval tv)
1363{
1364        if (trace->format->seek_timeval) {
1365                return trace->format->seek_timeval(trace,tv);
1366        }
1367        else {
1368                if (trace->format->seek_erf) {
1369                        uint64_t timestamp = ((((uint64_t)tv.tv_sec) << 32) + \
1370                                (((uint64_t)tv.tv_usec * UINT_MAX)/1000000));
1371                        return trace->format->seek_erf(trace,timestamp);
1372                }
1373                if (trace->format->seek_seconds) {
1374                        double seconds = tv.tv_sec + ((tv.tv_usec * 1.0)/1000000);
1375                        return trace->format->seek_seconds(trace,seconds);
1376                }
1377                trace_set_err(trace,
1378                                TRACE_ERR_OPTION_UNAVAIL,
1379                                "Feature unimplemented");
1380                return -1;
1381        }
1382}
1383
1384char *trace_ether_ntoa(const uint8_t *addr, char *buf)
1385{
1386        char *buf2 = buf;
1387        static char staticbuf[18]={0,};
1388        if (!buf2)
1389                buf2=staticbuf;
1390        snprintf(buf2,18,"%02x:%02x:%02x:%02x:%02x:%02x",
1391                        addr[0],addr[1],addr[2],
1392                        addr[3],addr[4],addr[5]);
1393        return buf2;
1394}
1395
1396uint8_t *trace_ether_aton(const char *buf, uint8_t *addr)
1397{
1398        uint8_t *buf2 = addr;
1399        unsigned int tmp[6];
1400        static uint8_t staticaddr[6];
1401        if (!buf2)
1402                buf2=staticaddr;
1403        sscanf(buf,"%x:%x:%x:%x:%x:%x",
1404                        &tmp[0],&tmp[1],&tmp[2],
1405                        &tmp[3],&tmp[4],&tmp[5]);
1406        buf2[0]=tmp[0]; buf2[1]=tmp[1]; buf2[2]=tmp[2];
1407        buf2[3]=tmp[3]; buf2[4]=tmp[4]; buf2[5]=tmp[5];
1408        return buf2;
1409}
1410
Note: See TracBrowser for help on using the repository browser.