source: lib/trace.c @ 6f75b9d

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 6f75b9d was 6f75b9d, checked in by Jamie Curtis <jamie@…>, 16 years ago

Fixed bug where the compiler would optimise away the initialisation of the
scan variable.

Cleaned up some other code, added xstrncpy() and xstrndup() as platform
independant functions that dwim.

  • Property mode set to 100644
File size: 37.9 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
54#ifdef HAVE_LIMITS_H
55#  include <limits.h>
56#endif
57
58#ifdef HAVE_SYS_LIMITS_H
59#  include <sys/limits.h>
60#endif
61
62#include <sys/socket.h>
63#include <sys/un.h>
64#include <sys/mman.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 <err.h>
118
119#define MAXOPTS 1024
120
121#if HAVE_BPF
122/* A type encapsulating a bpf filter
123 * This type covers the compiled bpf filter, as well as the original filter
124 * string
125 *
126 */
127struct libtrace_filter_t {
128        struct bpf_insn *filter;
129        char * filterstring;
130};
131#endif
132
133struct trace_err_t trace_err;
134
135struct libtrace_format_t **format_list = 0;
136int format_size = 0;
137int nformats = 0;
138
139/* strncpy is not assured to copy the final \0, so we
140 * will use our own one that does
141 */
142static void xstrncpy(char *dest, const char *src, size_t n)
143{
144        strncpy(dest,src,n);
145        dest[n]='\0';
146}
147 
148static char *xstrndup(const char *src,size_t n)
149{       
150        char *ret=malloc(n+1);
151        xstrncpy(ret,src,n);
152        return ret;
153}
154
155void register_format(struct libtrace_format_t *f) {
156//      fprintf(stderr,"Registering input format %s\n",f->name);
157        if (format_list == 0) {
158                format_size = 10;
159                format_list = malloc(
160                                        sizeof(struct libtrace_format_t *) *
161                                        format_size
162                                );
163        } else if (format_size == nformats) {
164                format_size = format_size + 10;
165                format_list = realloc(format_list,
166                                sizeof(struct libtrace_format_t *) * 
167                                format_size);
168        }
169        format_list[nformats] = f;
170        nformats++;
171}
172
173/* Prints help information for libtrace
174 *
175 * Function prints out some basic help information regarding libtrace,
176 * and then prints out the help() function registered with each input module
177 */
178void trace_help() {
179        int i = 0;
180        printf("libtrace %s\n",PACKAGE_VERSION);
181        for (i = 0; i < nformats; i++) {
182                if (format_list[i]->help) {
183                        format_list[i]->help();
184                }
185        }
186}
187
188/* Prints error information
189 *
190 * Prints out a descriptive error message for the currently set trace_err value
191 */
192void trace_perror(const char *caller) {
193        switch (trace_err.err_num) {
194                case E_BAD_FORMAT:
195                        fprintf(stderr, "%s: No support for format (%s)\n", caller, trace_err.problem);
196                        break;
197                case E_NO_INIT:
198                        fprintf(stderr, "%s: Format (%s) does not have an init_trace function defined\n", caller, trace_err.problem);
199                        break;
200                case E_NO_INIT_OUT:
201                        fprintf(stderr, "%s: Format (%s) does not have an init_output function defined\n", caller, trace_err.problem);
202                        break;
203                case E_URI_LONG:
204                        fprintf(stderr, "%s: uri is too long\n", caller);
205                        break;
206                case E_URI_NOCOLON:
207                        fprintf(stderr, "%s: A uri must contain at least one colon e.g. format:destination\n", caller);
208                        break;
209                case E_INIT_FAILED:
210                        fprintf(stderr, "%s: libtrace failed to initialise (%s)\n",caller,trace_err.problem);
211                       
212                default:
213                        fprintf(stderr, "Unknown errcode %d\n",trace_err.err_num);
214                        break; 
215        }
216        trace_err.err_num = E_NOERROR;
217}
218
219#define RP_BUFSIZE 65536
220#define URI_PROTO_LINE 16
221
222/* Gets the name of the output format for a given output trace.
223 *
224 * @params libtrace     the output trace to get the name of the format for
225 * @returns callee-owned null-terminated char* containing the output format
226 *
227 */
228SIMPLE_FUNCTION
229char *trace_get_output_format(const struct libtrace_out_t *libtrace) {
230        char * format = libtrace->format->name;
231
232        return format;
233}
234
235/* Create a trace file from a URI
236 *
237 * @params char * containing a valid libtrace URI
238 * @returns opaque pointer to a libtrace_t
239 *
240 * Valid URI's are:
241 *  erf:/path/to/erf/file
242 *  erf:/path/to/erf/file.gz
243 *  erf:/path/to/rtclient/socket
244 *  erf:-                       (stdin)
245 *  dag:/dev/dagcard
246 *  pcapint:pcapinterface               (eg: pcapint:eth0)
247 *  pcap:/path/to/pcap/file
248 *  pcap:-
249 *  rtclient:hostname
250 *  rtclient:hostname:port
251 *  wag:-
252 *  wag:/path/to/wag/file
253 *  wag:/path/to/wag/file.gz
254 *  wag:/path/to/wag/socket
255 *
256 * If an error occured when attempting to open a trace, NULL is returned
257 * and an error is output to stdout.
258 */
259struct libtrace_t *trace_create(const char *uri) {
260        struct libtrace_t *libtrace = malloc(sizeof(struct libtrace_t));
261        char *scan = 0;
262        const char *uridata = 0;                 
263        int i = 0;
264       
265        trace_err.err_num = E_NOERROR;
266       
267        // parse the URI to determine what sort of event we are dealing with
268        if ((uridata = trace_parse_uri(uri, &scan)) == 0) {
269                return 0;
270        }
271       
272        libtrace->event.tdelta = 0.0;
273
274        libtrace->format = 0;
275        for (i = 0; i < nformats; i++) {
276                if (strlen(scan) == strlen(format_list[i]->name) &&
277                                strncasecmp(scan,
278                                        format_list[i]->name,
279                                        strlen(scan)) == 0) {
280                        libtrace->format=format_list[i];
281                        break;
282                }
283        }
284        if (libtrace->format == 0) {
285                trace_err.err_num = E_BAD_FORMAT;
286                strcpy(trace_err.problem, scan);
287                //trace_err.problem = scan;
288                return 0;
289        }
290
291        libtrace->uridata = strdup(uridata);
292        // libtrace->format now contains the type of uri
293        // libtrace->uridata contains the appropriate data for this
294       
295        if (libtrace->format->init_input) {
296                if (!libtrace->format->init_input( libtrace)) {
297                        trace_err.err_num = E_INIT_FAILED;
298                        strcpy(trace_err.problem, scan);
299                        //trace_err.problem = scan;
300                        return 0;
301                }
302        } else {
303                trace_err.err_num = E_NO_INIT;
304                strcpy(trace_err.problem, scan);
305                //trace_err.problem = scan;
306                return 0;
307        }
308       
309
310        libtrace->fifo = create_tracefifo(1048576);
311        assert( libtrace->fifo);
312        free(scan);
313        return libtrace;
314}
315
316/* Creates a "dummy" trace file that has only the format type set.
317 *
318 * @returns opaque pointer to a (sparsely initialised) libtrace_t
319 *
320 * IMPORTANT: Do not attempt to call trace_read_packet or other such functions with
321 * the dummy trace. Its intended purpose is to act as a packet->trace for libtrace_packet_t's
322 * that are not associated with a libtrace_t structure.
323 */
324struct libtrace_t * trace_create_dead (const char *uri) {
325        struct libtrace_t *libtrace = malloc(sizeof(struct libtrace_t));
326        char *scan = calloc(sizeof(char),URI_PROTO_LINE);
327        char *uridata;
328        int i;
329       
330        trace_err.err_num = E_NOERROR;
331
332        if((uridata = strchr(uri,':')) == NULL) {
333                xstrncpy(scan, uri, strlen(uri));
334        } else {
335                xstrncpy(scan,uri, (uridata - uri));
336        }
337       
338        libtrace->format = 0;   
339       
340        for (i = 0; i < nformats; i++) {
341                if (strlen(scan) == strlen(format_list[i]->name) &&
342                                !strncasecmp(scan,
343                                        format_list[i]->name,
344                                        strlen(scan))) {
345                                libtrace->format=format_list[i];
346                                break;
347                                }
348        }
349        if (libtrace->format == 0) {
350                trace_err.err_num = E_BAD_FORMAT;
351                strcpy(trace_err.problem, scan);
352                //trace_err.problem = scan;
353                return 0;
354        }
355       
356        free(scan);
357        return libtrace;
358
359}
360
361/* Creates a trace output file from a URI.
362 *
363 * @param uri   the uri string describing the output format and destination
364 * @returns opaque pointer to a libtrace_output_t
365 * @author Shane Alcock
366 *
367 * Valid URI's are:
368 *  - gzerf:/path/to/erf/file.gz
369 *  - gzerf:/path/to/erf/file
370 *  - rtserver:hostname
371 *  - rtserver:hostname:port
372 *
373 *  If an error occured when attempting to open the output trace, NULL is returned
374 *  and trace_errno is set. Use trace_perror() to get more information
375 */
376       
377struct libtrace_out_t *trace_output_create(const char *uri) {
378        struct libtrace_out_t *libtrace = malloc(sizeof(struct libtrace_out_t));
379       
380        char *scan = 0;
381        const char *uridata = 0;
382        int i;
383
384        trace_err.err_num = E_NOERROR;
385        // parse the URI to determine what sort of event we are dealing with
386
387        if ((uridata = trace_parse_uri(uri, &scan)) == 0) {
388                return 0;
389        }
390       
391       
392        libtrace->format = 0;
393        for (i = 0; i < nformats; i++) {
394                if (strlen(scan) == strlen(format_list[i]->name) &&
395                                !strncasecmp(scan,
396                                        format_list[i]->name,
397                                        strlen(scan))) {
398                                libtrace->format=format_list[i];
399                                break;
400                                }
401        }
402        if (libtrace->format == 0) {
403                trace_err.err_num = E_BAD_FORMAT;
404                strcpy(trace_err.problem, scan);
405                //trace_err.problem = scan;     
406                return 0;
407        }
408        libtrace->uridata = strdup(uridata);
409
410
411        // libtrace->format now contains the type of uri
412        // libtrace->uridata contains the appropriate data for this
413
414        if (libtrace->format->init_output) {
415                if(!libtrace->format->init_output( libtrace)) {
416                        return 0;
417                }
418        } else {
419                trace_err.err_num = E_NO_INIT_OUT;
420                strcpy(trace_err.problem, scan);
421                //trace_err.problem = scan;
422                return 0;
423        }
424
425
426        libtrace->fifo = create_tracefifo(1048576);
427        assert( libtrace->fifo);
428        free(scan);
429        return libtrace;
430}
431
432/* Parses an output options string and calls the appropriate function to deal with output options.
433 *
434 * @param libtrace      the output trace object to apply the options to
435 * @param options       the options string
436 * @returns -1 if option configuration failed, 0 otherwise
437 *
438 * @author Shane Alcock
439 */
440int trace_output_config(struct libtrace_out_t *libtrace, char *options) {
441        char *opt_string = 0;
442        char *opt_argv[MAXOPTS];
443        int opt_argc = 0;
444       
445        assert(libtrace);
446       
447        if (!options) {
448                return 0;
449        }
450        asprintf(&opt_string, "%s %s", libtrace->format->name, options);
451        parse_cmd(opt_string, &opt_argc, opt_argv, MAXOPTS);
452       
453        if (libtrace->format->config_output) {
454                return libtrace->format->config_output(libtrace, opt_argc, opt_argv);
455        }
456        return -1;
457}
458
459/* Close a trace file, freeing up any resources it may have been using
460 *
461 */
462void trace_destroy(struct libtrace_t *libtrace) {
463        assert(libtrace);
464        libtrace->format->fin_input(libtrace);
465        // need to free things!
466        free(libtrace->uridata);
467        destroy_tracefifo(libtrace->fifo);
468        free(libtrace);
469}
470
471
472void trace_destroy_dead(struct libtrace_t *libtrace) {
473        assert(libtrace);
474        free(libtrace);
475}
476/* Close an output trace file, freeing up any resources it may have been using
477 *
478 * @param libtrace      the output trace file to be destroyed
479 *
480 * @author Shane Alcock
481 * */
482void trace_output_destroy(struct libtrace_out_t *libtrace) {
483        assert(libtrace);
484        libtrace->format->fin_output(libtrace);
485        free(libtrace->uridata);
486        destroy_tracefifo(libtrace->fifo);
487        free(libtrace);
488}
489
490/* Read one packet from the trace into buffer
491 *
492 * @param libtrace      the libtrace opaque pointer
493 * @param packet        the packet opaque pointer
494 * @returns 0 on EOF, negative value on error
495 *
496 */
497int trace_read_packet(struct libtrace_t *libtrace, struct libtrace_packet_t *packet) {
498
499        if (!libtrace) {
500                fprintf(stderr,"You called trace_read_packet() with a NULL libtrace parameter!\n");
501        }
502        assert(libtrace);
503        assert(packet);
504     
505        /* Store the trace we are reading from into the packet opaque
506         * structure */
507        packet->trace = libtrace;
508
509        if (libtrace->format->read_packet) {
510                return libtrace->format->read_packet(libtrace,packet);
511        }
512        return -1;
513}
514
515/* Writes a packet to the specified output
516 *
517 * @param libtrace      describes the output format, destination, etc.
518 * @param packet        the packet to be written out
519 * @returns the number of bytes written, -1 if write failed
520 *
521 * @author Shane Alcock
522 * */
523int trace_write_packet(struct libtrace_out_t *libtrace, const struct libtrace_packet_t *packet) {
524        assert(libtrace);
525        assert(packet); 
526
527        if (libtrace->format->write_packet) {
528                return libtrace->format->write_packet(libtrace, packet);
529        }
530        return -1;
531}
532
533/* get a pointer to the link layer
534 * @param packet        a pointer to a libtrace_packet structure
535 *
536 * @returns a pointer to the link layer, or NULL if there is no link layer
537 *
538 * @note you should call trace_get_link_type() to find out what type of link layer this is
539 */
540void *trace_get_link(const struct libtrace_packet_t *packet) {
541        const void *ethptr = 0;
542       
543        if (packet->trace->format->get_link) {
544                ethptr = packet->trace->format->get_link(packet);
545        }
546        return (void *)ethptr;
547}
548
549typedef struct legacy_framing {
550        uint64_t        ts;
551        uint32_t        crc;
552        uint32_t        header;
553        uint32_t        data[12]; /* pad to 64 bytes */
554} legacy_framing_t;
555
556/* get a pointer to the IP header (if any)
557 * @param packet        a pointer to a libtrace_packet structure
558 *
559 * @returns a pointer to the IP header, or NULL if there is not an IP packet
560 */
561struct libtrace_ip *trace_get_ip(const struct libtrace_packet_t *packet) {
562        struct libtrace_ip *ipptr = 0;
563
564        switch(trace_get_link_type(packet)) {
565                case TRACE_TYPE_80211:
566                        { 
567                               
568                                struct ieee_802_11_header *wifi = trace_get_link(packet);       
569                                if (!wifi) {
570                                        ipptr = NULL;
571                                        break;
572                                }
573
574                                // Data packet?
575                                if (wifi->type != 2) {
576                                        ipptr = NULL;
577                                }
578                                else {
579                                        struct ieee_802_11_payload *eth = (void*)wifi->data;
580                                        ipptr = NULL;
581
582                                        if (eth->type == 0x0008) {
583                                                ipptr=(void*)eth->data;
584                                        } else if (eth->type == 0x0081) {
585                                                // VLAN
586                                                if ((*(uint16_t *)(eth + 16)) == 0x0008) {
587                                                        ipptr = (void*)eth->data + 4;
588                                                }
589                                        }
590                                }
591                        }
592                        break;
593                case TRACE_TYPE_ETH:
594                        {
595                                struct libtrace_ether *eth = 
596                                        trace_get_link(packet);
597                                if (!eth) {
598                                        ipptr = NULL;
599                                        break;
600                                }
601                                ipptr = NULL;
602                               
603                                if (eth->ether_type==0x0008) {
604                                        ipptr = ((void *)eth) + 14;
605                                } else if (eth->ether_type == 0x0081) {
606                                        struct libtrace_8021q *vlanhdr = 
607                                                (struct libtrace_8021q *)eth;
608                                        if (vlanhdr->vlan_ether_type == 0x0008) {
609                                                ipptr = ((void *)eth) + 18;
610                                        }
611                                }
612                                break;
613                        }
614                case TRACE_TYPE_NONE:
615                        ipptr = trace_get_link(packet);
616                        break;
617                case TRACE_TYPE_LINUX_SLL:
618                        {
619                                struct trace_sll_header_t *sll;
620
621                                sll = trace_get_link(packet);
622                                if (!sll) {
623                                        ipptr = NULL;
624                                        break;
625                                }
626                                if (ntohs(sll->protocol)!=0x0800) {
627                                        ipptr = NULL;
628                                }
629                                else {
630                                        ipptr = ((void*)sll)+sizeof(*sll);
631                                }
632                        }
633                        break;
634                case TRACE_TYPE_PFLOG:
635                        {
636                                struct trace_pflog_header_t *pflog;
637                                pflog = trace_get_link(packet);
638                                if (!pflog) {
639                                        ipptr = NULL;
640                                        break;
641                                }
642                                if (pflog->af != AF_INET) {
643                                        ipptr = NULL;
644                                } else {
645                                        ipptr = ((void*)pflog)+sizeof(*pflog);
646                                }
647                        }
648                        break;
649                case TRACE_TYPE_ATM:
650                        {
651                                struct atm_rec *atm = 
652                                        trace_get_link(packet);
653                                // TODO: Find out what ATM does, and return
654                                //       NULL for non IP data
655                                //       Presumably it uses the normal stuff
656                                if (!atm) {
657                                        ipptr = NULL;
658                                        break;
659                                }
660                                ipptr =  (void*)&atm->pload;
661                                break;
662                        }
663                case TRACE_TYPE_LEGACY_POS:
664                        {
665                                // 64 byte capture.
666                                legacy_framing_t *cell = 
667                                        trace_get_link(packet);
668                                // check ethertype
669                                uint16_t *etype = (uint16_t *)cell->data + 1;
670                                if (*etype == 0x0008) {
671                                        ipptr = (void *)&cell->data[1];
672                                } else {
673                                        ipptr = NULL;
674                                }
675                                break;
676                               
677                        }
678                case TRACE_TYPE_LEGACY_ATM:
679                case TRACE_TYPE_LEGACY_ETH:
680                case TRACE_TYPE_LEGACY:
681                        {
682                                // 64 byte capture.
683                                legacy_framing_t *cell =
684                                        trace_get_link(packet);
685                                uint16_t *etype = (uint16_t *)cell->data + 3;
686                                if (*etype == 0x0008) {
687                                        ipptr = (void *)&cell->data[2];
688                                } else {
689                                        ipptr = NULL;
690                                }
691                        }
692                default:
693                        fprintf(stderr,"Don't understand link layer type %i in trace_get_ip()\n",
694                                trace_get_link_type(packet));
695                        ipptr=NULL;
696                        break;
697        }
698
699        return ipptr;
700}
701
702#define SW_IP_OFFMASK 0xff1f
703
704/* get a pointer to the TCP header (if any)
705 * @param packet        a pointer to a libtrace_packet structure
706 *
707 * @returns a pointer to the TCP header, or NULL if there is not a TCP packet
708 */
709struct libtrace_tcp *trace_get_tcp(const struct libtrace_packet_t *packet) {
710        struct libtrace_tcp *tcpptr = 0;
711        struct libtrace_ip *ipptr = 0;
712
713        if(!(ipptr = trace_get_ip(packet))) {
714                return 0;
715        }
716        if ((ipptr->ip_p == 6) && ((ipptr->ip_off & SW_IP_OFFMASK) == 0))  {
717                tcpptr = (struct libtrace_tcp *)((ptrdiff_t)ipptr + (ipptr->ip_hl * 4));
718        }
719        return tcpptr;
720}
721
722/* get a pointer to the TCP header (if any) given a pointer to the IP header
723 * @param ip            The IP header
724 * @param[out] skipped  An output variable of the number of bytes skipped
725 *
726 * @returns a pointer to the TCP header, or NULL if this is not a TCP packet
727 *
728 * Skipped can be NULL, in which case it will be ignored by the program.
729 */
730struct libtrace_tcp *trace_get_tcp_from_ip(const struct libtrace_ip *ip, int *skipped)
731{
732#define SW_IP_OFFMASK 0xff1f
733        struct libtrace_tcp *tcpptr = 0;
734
735        if ((ip->ip_p == 6) && ((ip->ip_off & SW_IP_OFFMASK) == 0))  {
736                tcpptr = (struct libtrace_tcp *)((ptrdiff_t)ip+ (ip->ip_hl * 4));
737        }
738
739        if (skipped)
740                *skipped=(ip->ip_hl*4);
741
742        return tcpptr;
743}
744
745/* get a pointer to the UDP header (if any)
746 * @param packet        a pointer to a libtrace_packet structure
747 *
748 * @returns a pointer to the UDP header, or NULL if this is not a UDP packet
749 */
750struct libtrace_udp *trace_get_udp(const struct libtrace_packet_t *packet) {
751        struct libtrace_udp *udpptr = 0;
752        struct libtrace_ip *ipptr = 0;
753       
754        if(!(ipptr = trace_get_ip(packet))) {
755                return 0;
756        }
757        if ((ipptr->ip_p == 17) && ((ipptr->ip_off & SW_IP_OFFMASK) == 0)) {
758                udpptr = (struct libtrace_udp *)((ptrdiff_t)ipptr + (ipptr->ip_hl * 4));
759        }
760
761        return udpptr;
762}
763
764/* get a pointer to the UDP header (if any) given a pointer to the IP header
765 * @param ip            The IP header
766 * @param[out] skipped  An output variable of the number of bytes skipped
767 *
768 * @returns a pointer to the UDP header, or NULL if this is not a UDP packet
769 *
770 * Skipped can be NULL, in which case it will be ignored by the program.
771 */
772struct libtrace_udp *trace_get_udp_from_ip(const struct libtrace_ip *ip, int *skipped)
773{
774        struct libtrace_udp *udpptr = 0;
775
776        if ((ip->ip_p == 6) && ((ip->ip_off & SW_IP_OFFMASK) == 0))  {
777                udpptr = (struct libtrace_udp *)((ptrdiff_t)ip+ (ip->ip_hl * 4));
778        }
779
780        if (skipped)
781                *skipped=(ip->ip_hl*4);
782
783        return udpptr;
784}
785
786
787/* get a pointer to the ICMP header (if any)
788 * @param packet        a pointer to a libtrace_packet structure
789 *
790 * @returns a pointer to the ICMP header, or NULL if this is not a ICMP packet
791 */
792struct libtrace_icmp *trace_get_icmp(const struct libtrace_packet_t *packet) {
793        struct libtrace_icmp *icmpptr = 0;
794        struct libtrace_ip *ipptr = 0;
795       
796        if(!(ipptr = trace_get_ip(packet))) {
797                return 0;
798        }
799        if ((ipptr->ip_p == 1)&& ((ipptr->ip_off & SW_IP_OFFMASK) == 0 )){
800                icmpptr = (struct libtrace_icmp *)((ptrdiff_t)ipptr + (ipptr->ip_hl * 4));
801        }
802        return icmpptr;
803}
804
805/* get a pointer to the ICMP header (if any) given a pointer to the IP header
806 * @param ip            The IP header
807 * @param[out] skipped  An output variable of the number of bytes skipped
808 *
809 * @returns a pointer to the ICMP header, or NULL if this is not a ICMP packet
810 *
811 * Skipped can be NULL, in which case it will be ignored by the program.
812 */
813struct libtrace_icmp *trace_get_icmp_from_ip(const struct libtrace_ip *ip, int *skipped)
814{
815        struct libtrace_icmp *icmpptr = 0;
816
817        if ((ip->ip_p == 6) && ((ip->ip_off & SW_IP_OFFMASK) == 0))  {
818                icmpptr = (struct libtrace_icmp *)((ptrdiff_t)ip+ (ip->ip_hl * 4));
819        }
820
821        if (skipped)
822                *skipped=(ip->ip_hl*4);
823
824        return icmpptr;
825}
826/* parse an ip or tcp option
827 * @param[in,out] ptr   the pointer to the current option
828 * @param[in,out] len   the length of the remaining buffer
829 * @param[out] type     the type of the option
830 * @param[out] optlen   the length of the option
831 * @param[out] data     the data of the option
832 *
833 * @returns bool true if there is another option (and the fields are filled in)
834 *               or false if this was the last option.
835 *
836 * This updates ptr to point to the next option after this one, and updates
837 * len to be the number of bytes remaining in the options area.  Type is updated
838 * to be the code of this option, and data points to the data of this option,
839 * with optlen saying how many bytes there are.
840 *
841 * @note Beware of fragmented packets.
842 * @author Perry Lorier
843 */
844int trace_get_next_option(unsigned char **ptr,int *len,
845                        unsigned char *type,
846                        unsigned char *optlen,
847                        unsigned char **data)
848{
849        if (*len<=0)
850                return 0;
851        *type=**ptr;
852        switch(*type) {
853                case 0: /* End of options */
854                        return 0;
855                case 1: /* Pad */
856                        (*ptr)++;
857                        (*len)--;
858                        return 1;
859                default:
860                        *optlen = *(*ptr+1);
861                        if (*optlen<2)
862                                return 0; // I have no idea wtf is going on
863                                          // with these packets
864                        (*len)-=*optlen;
865                        (*data)=(*ptr+2);
866                        (*ptr)+=*optlen;
867                        if (*len<0)
868                                return 0;
869                        return 1;
870        }
871        assert(0);
872}
873
874
875/* Get the current time in DAG time format
876 * @param packet        a pointer to a libtrace_packet structure
877 * @returns a 64 bit timestamp in DAG ERF format (upper 32 bits are the seconds
878 * past 1970-01-01, the lower 32bits are partial seconds)
879 * @author Daniel Lawson
880 */ 
881uint64_t trace_get_erf_timestamp(const struct libtrace_packet_t *packet) {
882        uint64_t timestamp = 0;
883        double seconds = 0.0;
884        struct timeval ts;
885
886        if (packet->trace->format->get_erf_timestamp) {
887                // timestamp -> timestamp
888                timestamp = packet->trace->format->get_erf_timestamp(packet);
889        } else if (packet->trace->format->get_timeval) {
890                // timeval -> timestamp
891                ts = packet->trace->format->get_timeval(packet);
892                timestamp = ((((uint64_t)ts.tv_sec) << 32) + \
893                                (((uint64_t)ts.tv_usec * UINT_MAX)/1000000));
894        } else if (packet->trace->format->get_seconds) {
895                // seconds -> timestamp
896                seconds = packet->trace->format->get_seconds(packet);
897                timestamp = ((uint64_t)((uint32_t)seconds) << 32) + \
898                            (( seconds - (uint32_t)seconds   ) * UINT_MAX);
899        }
900        return timestamp;
901}
902
903/* Get the current time in struct timeval
904 * @param packet        a pointer to a libtrace_packet structure
905 *
906 * @returns time that this packet was seen in a struct timeval
907 * @author Daniel Lawson
908 * @author Perry Lorier
909 */ 
910struct timeval trace_get_timeval(const struct libtrace_packet_t *packet) {
911        struct timeval tv;
912        uint64_t ts = 0;
913        double seconds = 0.0;
914        if (packet->trace->format->get_timeval) {
915                // timeval -> timeval
916                tv = packet->trace->format->get_timeval(packet);
917        } else if (packet->trace->format->get_erf_timestamp) {
918                // timestamp -> timeval
919                ts = packet->trace->format->get_erf_timestamp(packet);
920#if __BYTE_ORDER == __BIG_ENDIAN
921                tv.tv_sec = ts & 0xFFFFFFFF;
922#elif __BYTE_ORDER == __LITTLE_ENDIAN
923                tv.tv_sec = ts >> 32;
924#else
925#error "What on earth are you running this on?"
926#endif
927                ts = (1000000 * (ts & 0xffffffffULL));
928                ts += (ts & 0x80000000ULL) << 1;
929                tv.tv_usec = ts >> 32;
930                if (tv.tv_usec >= 1000000) {
931                        tv.tv_usec -= 1000000;
932                        tv.tv_sec += 1;
933                }
934        } else if (packet->trace->format->get_seconds) {
935                // seconds -> timeval
936                seconds = packet->trace->format->get_seconds(packet);
937                tv.tv_sec = (uint32_t)seconds;
938                tv.tv_usec = (uint32_t)(((seconds - tv.tv_sec) * 1000000)/UINT_MAX);
939        }
940
941        return tv;
942}
943
944/* Get the current time in floating point seconds
945 * @param packet        a pointer to a libtrace_packet structure
946 * @returns time that this packet was seen in 64bit floating point seconds
947 * @author Perry Lorier
948 */ 
949double trace_get_seconds(const struct libtrace_packet_t *packet) {
950        double seconds = 0.0;
951        uint64_t ts = 0;
952        struct timeval tv;
953       
954        if (packet->trace->format->get_seconds) {
955                // seconds->seconds
956                seconds = packet->trace->format->get_seconds(packet);
957        } else if (packet->trace->format->get_erf_timestamp) {
958                // timestamp -> seconds
959                ts = packet->trace->format->get_erf_timestamp(packet);
960                seconds =  (ts>>32) + ((ts & UINT_MAX)*1.0 / UINT_MAX);
961        } else if (packet->trace->format->get_timeval) {
962                // timeval -> seconds
963                tv = packet->trace->format->get_timeval(packet);
964                seconds = tv.tv_sec + ((tv.tv_usec * UINT_MAX * 1.0)/1000000);
965        }
966
967        return seconds;
968}
969
970/* Get the size of the packet in the trace
971 * @param packet        the packet opaque pointer
972 * @returns the size of the packet in the trace
973 * @author Perry Lorier
974 * @note The return size refers to the network-level payload of the packet and
975 * does not include any capture headers. For example, an Ethernet packet with
976 * an empty TCP packet will return sizeof(ethernet_header) + sizeof(ip_header)
977 * + sizeof(tcp_header).
978 * @note Due to this being a header capture, or anonymisation, this may not
979 * be the same size as the original packet.  See trace_get_wire_length() for the
980 * original size of the packet.
981 * @note This can (and often is) different for different packets in a trace!
982 * @note This is sometimes called the "snaplen".
983 */ 
984int trace_get_capture_length(const struct libtrace_packet_t *packet) {
985
986        if (packet->trace->format->get_capture_length) {
987                return packet->trace->format->get_capture_length(packet);
988        }
989        return -1;
990}
991       
992/* Get the size of the packet as it was seen on the wire.
993 * @param packet        a pointer to a libtrace_packet structure
994 *
995 * @returns the size of the packet as it was on the wire.
996 * @author Perry Lorier
997 * @author Daniel Lawson
998 * @note Due to the trace being a header capture, or anonymisation this may
999 * not be the same as the Capture Len.
1000 */ 
1001int trace_get_wire_length(const struct libtrace_packet_t *packet){
1002        if (packet->trace->format->get_wire_length) {
1003                return packet->trace->format->get_wire_length(packet);
1004        }
1005        return -1;
1006
1007}
1008
1009/* Get the type of the link layer
1010 * @param packet        a pointer to a libtrace_packet structure
1011 * @returns libtrace_linktype_t
1012 * @author Perry Lorier
1013 * @author Daniel Lawson
1014 */
1015libtrace_linktype_t trace_get_link_type(const struct libtrace_packet_t *packet ) {
1016        if (packet->trace->format->get_link_type) {
1017                return packet->trace->format->get_link_type(packet);
1018        }
1019        return -1;
1020}
1021
1022/* Get the source MAC addres
1023 * @param packet        a pointer to a libtrace_packet structure
1024 * @returns a pointer to the source mac, (or NULL if there is no source MAC)
1025 * @author Perry Lorier
1026 */
1027uint8_t *trace_get_source_mac(const struct libtrace_packet_t *packet) {
1028        void *link = trace_get_link(packet);
1029        struct ieee_802_11_header *wifi = link;
1030        struct libtrace_ether *ethptr = link;
1031        if (!link)
1032                return NULL;
1033        switch (trace_get_link_type(packet)) {
1034                case TRACE_TYPE_80211:
1035                        return (uint8_t*)&wifi->mac2;
1036                case TRACE_TYPE_ETH:
1037                        return (uint8_t*)&ethptr->ether_shost;
1038                default:
1039                        fprintf(stderr,"Not implemented\n");
1040                        assert(0);
1041        }
1042}
1043
1044/* Get the destination MAC addres
1045 * @param packet a libtrace_packet pointer
1046 * @returns a pointer to the destination mac, (or NULL if there is no
1047 * destination MAC)
1048 * @author Perry Lorier
1049 */
1050uint8_t *trace_get_destination_mac(const struct libtrace_packet_t *packet) {
1051        void *link = trace_get_link(packet);
1052        struct ieee_802_11_header *wifi = link;
1053        struct libtrace_ether *ethptr = link;
1054        if (!link)
1055                return NULL;
1056        switch (trace_get_link_type(packet)) {
1057                case TRACE_TYPE_80211:
1058                        return (uint8_t*)&wifi->mac1;
1059                case TRACE_TYPE_ETH:
1060                        return (uint8_t*)&ethptr->ether_dhost;
1061                default:
1062                        fprintf(stderr,"Not implemented\n");
1063                        assert(0);
1064        }
1065}
1066
1067
1068/* process a libtrace event
1069 * @param trace the libtrace opaque pointer
1070 * @param packet the libtrace_packet opaque pointer
1071 * @returns
1072 *  TRACE_EVENT_IOWAIT  Waiting on I/O on fd
1073 *  TRACE_EVENT_SLEEP   Next event in seconds
1074 *  TRACE_EVENT_PACKET  Packet arrived in buffer with size size
1075 *  TRACE_EVENT_TERMINATE Trace terminated (perhaps with an error condition)
1076 * FIXME currently keeps a copy of the packet inside the trace pointer,
1077 * which in turn is stored inside the new packet object...
1078 * @author Perry Lorier
1079 */
1080struct libtrace_eventobj_t trace_event(struct libtrace_t *trace, 
1081                struct libtrace_packet_t *packet) {
1082        struct libtrace_eventobj_t event;
1083
1084        if (!trace) {
1085                fprintf(stderr,"You called trace_event() with a NULL trace object!\n");
1086        }
1087        assert(trace);
1088        assert(packet);
1089
1090        /* Store the trace we are reading from into the packet opaque
1091         * structure */
1092        packet->trace = trace;
1093
1094        if (packet->trace->format->trace_event) {
1095                return packet->trace->format->trace_event(trace,packet);
1096        } else {
1097                return event;
1098        }
1099
1100}
1101
1102/* setup a BPF filter
1103 * @param filterstring a char * containing the bpf filter string
1104 * @returns opaque pointer pointer to a libtrace_filter_t object
1105 * @author Daniel Lawson
1106 */
1107struct libtrace_filter_t *trace_bpf_setfilter(const char *filterstring) {
1108#if HAVE_BPF
1109        struct libtrace_filter_t *filter = malloc(sizeof(struct libtrace_filter_t));
1110        filter->filterstring = strdup(filterstring);
1111        filter->filter = 0;
1112        return filter;
1113#else
1114        fprintf(stderr,"This version of libtrace does not have bpf filter support\n");
1115        return 0;
1116#endif
1117}
1118
1119/* apply a BPF filter
1120 * @param filter the filter opaque pointer
1121 * @param packet the packet opaque pointer
1122 * @returns 0 if the filter fails, 1 if it succeeds
1123 * @author Daniel Lawson
1124 */
1125int trace_bpf_filter(struct libtrace_filter_t *filter,
1126                        const struct libtrace_packet_t *packet) {
1127#if HAVE_BPF
1128        void *linkptr = 0;
1129        int clen = 0;
1130        assert(filter);
1131        assert(packet);
1132        linkptr = trace_get_link(packet);
1133        if (!linkptr) {
1134                return 0;
1135        }
1136       
1137        clen = trace_get_capture_length(packet);
1138       
1139
1140        if (filter->filterstring && ! filter->filter) {
1141                pcap_t *pcap;
1142                struct bpf_program bpfprog;
1143
1144                switch (trace_get_link_type(packet)) {
1145                        case TRACE_TYPE_ETH:
1146                                pcap = (pcap_t *)pcap_open_dead(DLT_EN10MB, 1500);
1147                                break;
1148#ifdef DLT_LINUX_SLL
1149                        case TRACE_TYPE_LINUX_SLL:
1150                                pcap = (pcap_t *)pcap_open_dead(DLT_LINUX_SLL, 1500);
1151                                break;
1152#endif
1153#ifdef DLT_PFLOG
1154                        case TRACE_TYPE_PFLOG:
1155                                pcap = (pcap_t *)pcap_open_dead(DLT_PFLOG, 1500);
1156                                break;
1157#endif
1158                        default:
1159                                printf("only works for ETH and LINUX_SLL (ppp) at the moment\n");
1160                                assert(0);
1161                }               
1162
1163                // build filter
1164                if (pcap_compile( pcap, &bpfprog, filter->filterstring, 1, 0)) {
1165                        printf("bpf compilation error: %s: %s\n", 
1166                                pcap_geterr(pcap),filter->filterstring);
1167                        assert(0);
1168                }
1169                pcap_close(pcap);
1170                filter->filter = bpfprog.bf_insns;     
1171        }
1172
1173        assert(filter->filter);
1174        return bpf_filter(filter->filter, linkptr, clen, clen);
1175#else
1176        fprintf(stderr,"This version of libtrace does not have bpf filter support\n");
1177        return 0;
1178#endif
1179}
1180
1181/* Set the direction flag, if it has one
1182 * @param packet the packet opaque pointer
1183 * @param direction the new direction (0,1,2,3)
1184 * @returns a signed value containing the direction flag, or -1 if this is not supported
1185 * @author Daniel Lawson
1186 */
1187int8_t trace_set_direction(struct libtrace_packet_t *packet, int8_t direction) {
1188        assert(packet);
1189        if (packet->trace->format->set_direction) {
1190                return packet->trace->format->set_direction(packet,direction);
1191        }
1192        return -1;
1193}
1194
1195/* Get the direction flag, if it has one
1196 * @param packet a pointer to a libtrace_packet structure
1197 * @returns a signed value containing the direction flag, or -1 if this is not supported
1198 * The direction is defined as 0 for packets originating locally (ie, outbound)
1199 * and 1 for packets originating remotely (ie, inbound).
1200 * Other values are possible, which might be overloaded to mean special things
1201 * for a special trace.
1202 * @author Daniel Lawson
1203 */
1204int8_t trace_get_direction(const struct libtrace_packet_t *packet) {
1205        assert(packet);
1206        if (packet->trace->format->get_direction) {
1207                return packet->trace->format->get_direction(packet);
1208        }
1209        return -1;
1210}
1211
1212struct ports_t {
1213        uint16_t src;
1214        uint16_t dst;
1215};
1216
1217/* Return the client port
1218 */
1219uint16_t trace_get_source_port(const struct libtrace_packet_t *packet)
1220{
1221        struct libtrace_ip *ip = trace_get_ip(packet);
1222        struct ports_t *port;
1223        if (6 != ip->ip_p
1224          && 17 != ip->ip_p)
1225                return 0;
1226        if (0 != (ip->ip_off & SW_IP_OFFMASK))
1227                return 0;
1228
1229        port = (struct ports_t *)((ptrdiff_t)ip + (ip->ip_hl * 4));
1230
1231        return ntohs(port->src);
1232}
1233
1234/* Same as get_source_port except use the destination port */
1235uint16_t trace_get_destination_port(const struct libtrace_packet_t *packet)
1236{
1237        struct libtrace_ip *ip = trace_get_ip(packet);
1238        struct ports_t *port;
1239
1240        if (6 != ip->ip_p
1241          && 17 != ip->ip_p)
1242                return 0;
1243
1244        if (0 != (ip->ip_off & SW_IP_OFFMASK))
1245                return 0;
1246
1247        port = (struct ports_t *)((ptrdiff_t)ip + (ip->ip_hl * 4));
1248
1249        return ntohs(port->dst);
1250}
1251
1252#define ROOT_SERVER(x) ((x) < 512)
1253#define ROOT_CLIENT(x) ((512 <= (x)) && ((x) < 1024))
1254#define NONROOT_SERVER(x) ((x) >= 5000)
1255#define NONROOT_CLIENT(x) ((1024 <= (x)) && ((x) < 5000))
1256#define DYNAMIC(x) ((49152 < (x)) && ((x) < 65535))
1257#define SERVER(x) ROOT_SERVER(x) || NONROOT_SERVER(x)
1258#define CLIENT(x) ROOT_CLIENT(x) || NONROOT_CLIENT(x)
1259
1260/* Attempt to deduce the 'server' port
1261 * @param protocol the IP protocol (eg, 6 or 17 for TCP or UDP)
1262 * @param source the TCP or UDP source port
1263 * @param dest the TCP or UDP destination port
1264 * @returns a hint as to which port is the server port
1265 * @author Daniel Lawson
1266 */
1267int8_t trace_get_server_port(uint8_t protocol __attribute__((unused)), uint16_t source, uint16_t dest) {
1268        /*
1269         * * If the ports are equal, return DEST
1270         * * Check for well-known ports in the given protocol
1271         * * Root server ports: 0 - 511
1272         * * Root client ports: 512 - 1023
1273         * * non-root client ports: 1024 - 4999
1274         * * non-root server ports: 5000+
1275         * * Check for static ranges: 1024 - 49151
1276         * * Check for dynamic ranges: 49152 - 65535
1277         * * flip a coin.
1278         */
1279       
1280        /* equal */
1281        if (source == dest)
1282                return USE_DEST;
1283
1284        /* root server port, 0 - 511 */
1285        if (ROOT_SERVER(source) && ROOT_SERVER(dest)) {
1286                if (source < dest)
1287                        return USE_SOURCE;
1288                return USE_DEST;
1289        }
1290
1291        if (ROOT_SERVER(source) && !ROOT_SERVER(dest))
1292                return USE_SOURCE;
1293        if (!ROOT_SERVER(source) && ROOT_SERVER(dest))
1294                return USE_DEST;
1295
1296        /* non-root server */
1297        if (NONROOT_SERVER(source) && NONROOT_SERVER(dest)) {
1298                if (source < dest)
1299                        return USE_SOURCE;
1300                return USE_DEST;
1301        }
1302        if (NONROOT_SERVER(source) && !NONROOT_SERVER(dest))
1303                return USE_SOURCE;
1304        if (!NONROOT_SERVER(source) && NONROOT_SERVER(dest))
1305                return USE_DEST;
1306
1307        /* root client */
1308        if (ROOT_CLIENT(source) && ROOT_CLIENT(dest)) {
1309                if (source < dest)
1310                        return USE_SOURCE;
1311                return USE_DEST;
1312        }
1313        if (ROOT_CLIENT(source) && !ROOT_CLIENT(dest)) {
1314                /* prefer root-client over nonroot-client */
1315                if (NONROOT_CLIENT(dest))
1316                        return USE_SOURCE;
1317                return USE_DEST;
1318        }
1319        if (!ROOT_CLIENT(source) && ROOT_CLIENT(dest)) {
1320                /* prefer root-client over nonroot-client */
1321                if (NONROOT_CLIENT(source))
1322                        return USE_DEST;
1323                return USE_SOURCE;
1324        }
1325       
1326        /* nonroot client */
1327        if (NONROOT_CLIENT(source) && NONROOT_CLIENT(dest)) {
1328                if (source < dest) 
1329                        return USE_SOURCE;
1330                return USE_DEST;
1331        }
1332        if (NONROOT_CLIENT(source) && !NONROOT_CLIENT(dest))
1333                return USE_DEST;
1334        if (!NONROOT_CLIENT(source) && NONROOT_CLIENT(dest))
1335                return USE_SOURCE;
1336
1337        /* dynamic range */
1338        if (DYNAMIC(source) && DYNAMIC(dest))
1339                if (source < dest)
1340                        return USE_SOURCE;
1341                return USE_DEST;
1342        if (DYNAMIC(source) && !DYNAMIC(dest))
1343                return USE_DEST;
1344        if (!DYNAMIC(source) && DYNAMIC(dest))
1345                return USE_SOURCE;
1346        /*
1347        if (SERVER(source) && CLIENT(dest))
1348                return USE_SOURCE;
1349       
1350        if (SERVER(dest) && CLIENT(source))
1351                return USE_DEST;
1352        if (ROOT_SERVER(source) && !ROOT_SERVER(dest))
1353                return USE_SOURCE;
1354        if (ROOT_SERVER(dest) && !ROOT_SERVER(source))
1355                return USE_DEST;
1356        */
1357        // failing that test...
1358        if (source < dest) {
1359                return USE_SOURCE;
1360        } 
1361        return USE_DEST;
1362       
1363}
1364
1365/* Truncate the packet at the suggested length
1366 * @param packet        the packet opaque pointer
1367 * @param size          the new length of the packet
1368 * @returns the new size of the packet
1369 * @note size and the return size refer to the network-level payload of the
1370 * packet, and do not include any capture headers. For example, to truncate a
1371 * packet after the IP header, set size to sizeof(ethernet_header) +
1372 * sizeof(ip_header)
1373 * @note If the original network-level payload is smaller than size, then the
1374 * original size is returned and the packet is left unchanged.
1375 * @author Daniel Lawson
1376 */
1377size_t trace_set_capture_length(struct libtrace_packet_t *packet, size_t size) {
1378        assert(packet);
1379
1380        if (packet->trace->format->set_capture_length) {
1381                return packet->trace->format->set_capture_length(packet,size);
1382        }
1383
1384        return -1;
1385}
1386
1387const char * trace_parse_uri(const char *uri, char **format) {
1388        const char *uridata = 0;
1389       
1390        if((uridata = strchr(uri,':')) == NULL) {
1391                // badly formed URI - needs a :
1392                trace_err.err_num = E_URI_NOCOLON;
1393                return 0;
1394        }
1395
1396        if ((uridata - uri) > URI_PROTO_LINE) {
1397                // badly formed URI - uri type is too long
1398                trace_err.err_num = E_URI_LONG;
1399                return 0;
1400        }
1401
1402        *format=xstrndup(uri, (uridata - uri));
1403
1404        // push uridata past the delimiter
1405        uridata++;
1406       
1407        return uridata;
1408}
1409       
Note: See TracBrowser for help on using the repository browser.