source: lib/protocols.c @ 496864e

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

Store the port number in the sockaddr in trace_get_{source,destination}_address

  • Property mode set to 100644
File size: 16.6 KB
Line 
1/* This file has the various helper functions used to decode various protocols
2 *
3 * $Id$
4 */ 
5#include "libtrace.h"
6#include "libtrace_int.h"
7#include "wag.h"
8#include <assert.h>
9#include <stdio.h>
10
11#ifndef WIN32
12#include <net/if_arp.h>
13#endif
14
15#ifndef ARPHRD_ETHER
16#define ARPHRD_ETHER    1               /* Ethernet 10/100Mbps.  */
17#endif
18
19#ifndef ARPHRD_PPP
20#define ARPHRD_PPP      512
21#endif
22
23
24/* Returns the payload from 802.3 ethernet.  Type optionally returned in
25 * "type" in host byte order.  This will return a vlan header.
26 */
27static void *trace_get_payload_from_ethernet(void *ethernet, 
28                uint16_t *type,
29                uint32_t *remaining)
30{
31        libtrace_ether_t *eth = (libtrace_ether_t*)ethernet;
32
33        if (remaining) {
34                if (*remaining < sizeof(*eth))
35                        return NULL;
36                *remaining-=sizeof(*eth);
37        }
38
39        if (type)
40                *type = ntohs(eth->ether_type);
41
42        return (void*)((char *)eth + sizeof(*eth));
43}
44
45/* skip any 802.1q headers if necessary
46 * type is input/output
47 */
48static void *trace_get_vlan_payload_from_ethernet_payload(void *ethernet, uint16_t *type,
49                uint32_t *remaining)
50{
51        assert(type && "You must pass a type in!");
52
53        if (*type == 0x8100) {
54                libtrace_8021q_t *vlanhdr = (libtrace_8021q_t *)ethernet;
55
56                if (remaining) {
57                        if (*remaining < sizeof(libtrace_8021q_t))
58                                return NULL;
59
60                        *remaining=*remaining-sizeof(libtrace_8021q_t);
61                }
62
63                *type = ntohs(vlanhdr->vlan_ether_type);
64
65                return (void*)((char *)ethernet + sizeof(*vlanhdr));
66        }
67
68        return ethernet;
69}
70
71static void *trace_get_payload_from_80211(void *link, uint16_t *type, uint32_t *remaining)
72{
73        libtrace_80211_t *wifi;
74        libtrace_802_11_payload_t *eth;
75
76        if (remaining && *remaining < sizeof(libtrace_80211_t))
77                return NULL;
78
79        wifi=(libtrace_80211_t*)link;
80
81        /* Data packet? */
82        if (wifi->type != 2) {
83                return NULL;
84        }
85
86        if (remaining && *remaining < sizeof(*eth))
87                return NULL;
88
89        eth=(libtrace_802_11_payload_t *)((char*)wifi+sizeof(*wifi));
90
91        if (type) *type=ntohs(eth->type);
92
93        return (void*)((char*)eth+sizeof(*eth));
94}
95
96static void *trace_get_payload_from_linux_sll(void *link,
97                uint16_t *type, uint32_t *remaining) 
98{
99        libtrace_sll_header_t *sll;
100        void *ret;
101
102        sll = (libtrace_sll_header_t*) link;
103
104        if (remaining) {
105                if (*remaining < sizeof(*sll))
106                        return NULL;
107                *remaining-=sizeof(*sll);
108        }
109
110        /* What kind of wacked out header, has this in host order?! */
111        if (type) *type = htons(sll->protocol); 
112
113        ret=(void*)((char*)sll+sizeof(*sll));
114
115        switch(sll->hatype) {
116                case ARPHRD_PPP:
117                        break;
118                case ARPHRD_ETHER:
119                        ret=trace_get_payload_from_ethernet(ret,type,remaining);
120        }
121
122        return ret;
123}
124
125static void *trace_get_payload_from_atm(void *link,
126                uint16_t *type, uint32_t *remaining)
127{
128        /* 64 byte capture. */
129        libtrace_llcsnap_t *llc = (libtrace_llcsnap_t*)link;
130
131        if (remaining) {
132                if (*remaining < sizeof(libtrace_llcsnap_t)+4)
133                        return NULL;
134                *remaining-=(sizeof(libtrace_llcsnap_t)+4);
135        }
136
137        /* advance the llc ptr +4 into the link layer.
138         * TODO: need to check what is in these 4 bytes.
139         * don't have time!
140         */
141        llc = (libtrace_llcsnap_t*)((char *)llc + 4);
142
143        if (type) *type = ntohs(llc->type);
144
145        return (void*)((char*)llc+sizeof(*llc));
146}
147
148static void *trace_get_payload_from_pos(void *link, 
149                uint16_t *type, uint32_t *remaining)
150{
151        /* 64 byte capture. */
152        libtrace_pos_t *pos = (libtrace_pos_t*)link;
153
154        if (remaining) {
155                if (*remaining < sizeof(libtrace_pos_t))
156                        return NULL;
157                *remaining-=sizeof(libtrace_pos_t);
158        }
159
160        if (type) *type = ntohs(pos->ether_type);
161
162        return (void*)((char *)pos+sizeof(*pos));
163}
164
165static void *trace_get_payload_from_pflog(void *link,
166                uint16_t *type, uint32_t *remaining)
167{
168        libtrace_pflog_header_t *pflog = (libtrace_pflog_header_t*)link;
169        if (remaining) {
170                if (*remaining<sizeof(*pflog)) 
171                        return NULL;
172                *remaining-=sizeof(*pflog);
173        }
174        if (type) {
175                switch(pflog->af) {
176                        case AF_INET6: *type=0x86DD; break;
177                        case AF_INET:  *type=0x0800; break;
178                        default:
179                                      /* Unknown */
180                                      return NULL;
181                }
182        }
183        return (void*)((char*)pflog+ sizeof(*pflog));
184}
185
186void *trace_get_payload_from_link(void *link, libtrace_linktype_t linktype, 
187                uint16_t *type, uint32_t *remaining)
188{
189        switch(linktype) {
190                case TRACE_TYPE_80211_PRISM:
191                        return trace_get_payload_from_80211((char*)link+144,
192                                        type,remaining);
193                case TRACE_TYPE_80211:
194                        return trace_get_payload_from_80211(link,type,remaining);
195                case TRACE_TYPE_ETH:
196                        return trace_get_payload_from_ethernet(link,type,remaining);
197                case TRACE_TYPE_NONE:
198                        if ((*(char*)link&0xF0) == 0x40)
199                                *type=0x0800;
200                        else if ((*(char*)link&0xF0) == 0x60)
201                                *type=0x86DD;
202                        return link; /* I love the simplicity */
203                case TRACE_TYPE_LINUX_SLL:
204                        return trace_get_payload_from_linux_sll(link,type,remaining);
205                case TRACE_TYPE_PFLOG:
206                        return trace_get_payload_from_pflog(link,type,remaining);
207                case TRACE_TYPE_POS:
208                        return trace_get_payload_from_pos(link,type,remaining);
209                case TRACE_TYPE_ATM:
210                        return trace_get_payload_from_atm(link,type,remaining);
211                case TRACE_TYPE_DUCK:
212                        return NULL; /* duck packets have no payload! */
213        }
214        fprintf(stderr,"Don't understand link layer type %i in trace_get_payload_from_link()\n",
215                linktype);
216        return NULL;
217}
218
219libtrace_ip_t *trace_get_ip(libtrace_packet_t *packet) 
220{
221        uint16_t type;
222        void *link = trace_get_link(packet);
223        void *ret;
224
225        if (!link)
226                return NULL;
227       
228        ret=trace_get_payload_from_link(
229                        link,
230                        trace_get_link_type(packet),
231                        &type, NULL);
232
233        if (!ret)
234                return NULL;
235
236        ret=trace_get_vlan_payload_from_ethernet_payload(ret,&type,NULL);
237
238        if (!ret || type!=0x0800)
239                return NULL;
240
241        return (libtrace_ip_t*)ret;
242}
243
244libtrace_ip6_t *trace_get_ip6(libtrace_packet_t *packet) 
245{
246        uint16_t type;
247        void *link=trace_get_link(packet);
248        void *ret;
249       
250        if (!link)
251                return NULL;
252
253        ret=trace_get_payload_from_link(
254                        link,
255                        trace_get_link_type(packet),
256                        &type,NULL);
257
258        if (!ret)
259                return NULL;
260
261        ret=trace_get_vlan_payload_from_ethernet_payload(ret,&type,NULL);
262
263        if (!ret || type!=0x86DD)
264                return NULL;
265
266        return (libtrace_ip6_t*)ret;
267}
268
269#define SW_IP_OFFMASK 0xff1f
270
271DLLEXPORT void *trace_get_payload_from_ip(libtrace_ip_t *ipptr, uint8_t *prot,
272                uint32_t *remaining) 
273{
274        void *trans_ptr = 0;
275
276        if ((ipptr->ip_off & SW_IP_OFFMASK) != 0)
277                return NULL;
278
279        if (remaining) {
280                if (*remaining<(ipptr->ip_hl*4U)) {
281                        return NULL;
282                }
283                *remaining-=(ipptr->ip_hl * 4);
284        }
285
286        trans_ptr = (void *)((char *)ipptr + (ipptr->ip_hl * 4));
287
288        if (prot) *prot = ipptr->ip_p;
289
290        return trans_ptr;
291}
292
293void *trace_get_payload_from_ip6(libtrace_ip6_t *ipptr, uint8_t *prot,
294                uint32_t *remaining) 
295{
296        void *payload = (char*)ipptr+sizeof(libtrace_ip6_t);
297        uint8_t nxt = ipptr->nxt;
298
299        if (remaining) {
300                if (*remaining<sizeof(libtrace_ip6_t))
301                        return NULL;
302                *remaining-=sizeof(libtrace_ip6_t);
303        }
304
305        while(1) {
306                switch (nxt) {
307                        case 0: /* hop by hop options */
308                        case 43: /* routing */
309                        case 44: /* fragment */
310                        case 50: /* ESP */
311                        case 51: /* AH */
312                        case 60: /* Destination options */
313                                {
314                                        uint16_t len=((libtrace_ip6_ext_t*)payload)->len
315                                        +sizeof(libtrace_ip6_ext_t);
316
317                                        if (remaining) {
318                                                if (*remaining < len) {
319                                                        /* Snap too short */
320                                                        return NULL;
321                                                }
322                                                *remaining-=len;
323                                        }
324
325                                        payload=(char*)payload+len;
326                                        nxt=((libtrace_ip6_ext_t*)payload)->nxt;
327                                        continue;
328                                }
329                        default:
330                                if (prot) *prot=nxt;
331                                return payload;
332                }
333        }
334}
335
336DLLEXPORT void *trace_get_transport(libtrace_packet_t *packet, 
337                uint8_t *proto,
338                uint32_t *remaining
339                ) 
340{
341        void *transport;
342        uint8_t dummy_proto;
343        uint16_t ethertype;
344        void *link;
345        uint32_t dummy_remaining;
346
347        if (!proto) proto=&dummy_proto;
348
349        if (!remaining) remaining=&dummy_remaining;
350
351        *remaining = trace_get_capture_length(packet);
352
353        link=trace_get_link(packet);
354
355        if (!link)
356                return NULL;
357
358        transport = trace_get_payload_from_link(
359                        link,
360                        trace_get_link_type(packet),
361                        &ethertype,
362                        remaining);
363
364        if (!transport)
365                return NULL;
366
367        transport = trace_get_vlan_payload_from_ethernet_payload(transport,
368                        &ethertype,
369                        remaining);
370
371        if (!transport)
372                return NULL;
373
374        switch (ethertype) {
375                case 0x0800: /* IPv4 */
376                        transport=trace_get_payload_from_ip(
377                                (libtrace_ip_t*)transport, proto, remaining);
378                        /* IPv6 */
379                        if (transport && *proto == 41) {
380                                transport=trace_get_payload_from_ip6(
381                                 (libtrace_ip6_t*)transport, proto,remaining);
382                        }
383                        return transport;
384                case 0x86DD: /* IPv6 */
385                        return trace_get_payload_from_ip6(
386                                (libtrace_ip6_t*)transport, proto, remaining);
387                       
388                default:
389                        *proto=0;
390                        return NULL;
391        }
392
393}
394
395DLLEXPORT libtrace_tcp_t *trace_get_tcp(libtrace_packet_t *packet) {
396        uint8_t proto;
397        libtrace_tcp_t *tcp;
398
399        tcp=(libtrace_tcp_t*)trace_get_transport(packet,&proto,NULL);
400
401        if (!tcp || proto != 6)
402                return NULL;
403
404        return (libtrace_tcp_t*)tcp;
405}
406
407DLLEXPORT libtrace_tcp_t *trace_get_tcp_from_ip(libtrace_ip_t *ip, uint32_t *remaining)
408{
409        libtrace_tcp_t *tcpptr = 0;
410
411        if (ip->ip_p == 6)  {
412                tcpptr = (libtrace_tcp_t *)
413                        trace_get_payload_from_ip(ip, NULL, remaining);
414        }
415
416        return tcpptr;
417}
418
419DLLEXPORT libtrace_udp_t *trace_get_udp(libtrace_packet_t *packet) {
420        uint8_t proto;
421        libtrace_udp_t *udp;
422
423        udp=(libtrace_udp_t*)trace_get_transport(packet,&proto,NULL);
424
425        if (!udp || proto != 17)
426                return NULL;
427
428        return udp;
429}
430
431DLLEXPORT libtrace_udp_t *trace_get_udp_from_ip(libtrace_ip_t *ip, uint32_t *remaining)
432{
433        libtrace_udp_t *udpptr = 0;
434
435        if (ip->ip_p == 17) {
436                udpptr = (libtrace_udp_t *)
437                        trace_get_payload_from_ip(ip, NULL, remaining);
438        }
439
440        return udpptr;
441}
442
443DLLEXPORT libtrace_icmp_t *trace_get_icmp(libtrace_packet_t *packet) {
444        uint8_t proto;
445        libtrace_icmp_t *icmp;
446
447        icmp=(libtrace_icmp_t*)trace_get_transport(packet,&proto,NULL);
448
449        if (!icmp || proto != 1)
450                return NULL;
451
452        return icmp;
453}
454
455DLLEXPORT libtrace_icmp_t *trace_get_icmp_from_ip(libtrace_ip_t *ip, uint32_t *remaining)
456{
457        libtrace_icmp_t *icmpptr = 0;
458
459        if (ip->ip_p == 1)  {
460                icmpptr = (libtrace_icmp_t *)trace_get_payload_from_ip(ip, 
461                                NULL, remaining);
462        }
463
464        return icmpptr;
465}
466
467DLLEXPORT void *trace_get_payload_from_udp(libtrace_udp_t *udp, uint32_t *remaining)
468{
469        if (remaining) {
470                if (*remaining < sizeof(libtrace_udp_t))
471                        return NULL;
472                *remaining-=sizeof(libtrace_udp_t);
473        }
474        return (void*)((char*)udp+sizeof(libtrace_udp_t));
475}
476
477DLLEXPORT void *trace_get_payload_from_tcp(libtrace_tcp_t *tcp, uint32_t *remaining)
478{
479        unsigned int dlen = tcp->doff*4;
480        if (remaining) {
481                if (*remaining < dlen)
482                        return NULL;
483                *remaining-=dlen;
484        }
485        return (void *)((char *)tcp+dlen);
486}
487
488DLLEXPORT void *trace_get_payload_from_icmp(libtrace_icmp_t *icmp, uint32_t *remaining)
489{
490        if (remaining) {
491                if (*remaining < sizeof(libtrace_icmp_t))
492                        return NULL;
493                *remaining-=sizeof(libtrace_icmp_t);
494        }
495        return (char*)icmp+sizeof(libtrace_icmp_t);
496}
497
498struct ports_t {
499        uint16_t src;
500        uint16_t dst;
501};
502
503/* Return the client port
504 */
505DLLEXPORT uint16_t trace_get_source_port(const libtrace_packet_t *packet)
506{
507        uint32_t remaining;
508        struct ports_t *port = 
509                (struct ports_t*)trace_get_transport((libtrace_packet_t*)packet,
510                        NULL, &remaining);
511
512        /* snapped too early */
513        if (remaining<2)
514                return 0;
515
516        if (port)
517                return ntohs(port->src);
518        else
519                return 0;
520}
521
522/* Same as get_source_port except use the destination port */
523DLLEXPORT uint16_t trace_get_destination_port(const libtrace_packet_t *packet)
524{
525        uint32_t remaining;
526        struct ports_t *port = 
527                (struct ports_t*)trace_get_transport((libtrace_packet_t*)packet,
528                        NULL, &remaining);
529        /* snapped to early */
530        if (remaining<4)
531                return 0;
532
533        if (port)
534                return ntohs(port->dst);
535        else
536                return 0;
537}
538
539
540uint8_t *trace_get_source_mac(libtrace_packet_t *packet) {
541        void *link = trace_get_link(packet);
542        libtrace_80211_t *wifi;
543        libtrace_ether_t *ethptr = (libtrace_ether_t*)link;
544        if (!link)
545                return NULL;
546        switch (trace_get_link_type(packet)) {
547                case TRACE_TYPE_80211:
548                        wifi=(libtrace_80211_t*)link;
549                        return (uint8_t*)&wifi->mac2;
550                case TRACE_TYPE_80211_PRISM:
551                        wifi=(libtrace_80211_t*)((char*)link+144);
552                        return (uint8_t*)&wifi->mac2;
553                case TRACE_TYPE_ETH:
554                        return (uint8_t*)&ethptr->ether_shost;
555                case TRACE_TYPE_POS:
556                case TRACE_TYPE_NONE:
557                case TRACE_TYPE_HDLC_POS:
558                case TRACE_TYPE_LINUX_SLL:
559                case TRACE_TYPE_PFLOG:
560                case TRACE_TYPE_ATM:
561                case TRACE_TYPE_DUCK:
562                        return NULL;
563        }
564        fprintf(stderr,"Not implemented\n");
565        assert(0);
566        return NULL;
567}
568
569DLLEXPORT uint8_t *trace_get_destination_mac(libtrace_packet_t *packet) {
570        void *link = trace_get_link(packet);
571        libtrace_80211_t *wifi;
572        libtrace_ether_t *ethptr = (libtrace_ether_t*)link;
573        if (!link)
574                return NULL;
575        switch (trace_get_link_type(packet)) {
576                case TRACE_TYPE_80211:
577                        wifi=(libtrace_80211_t*)link;
578                        return (uint8_t*)&wifi->mac1;
579                case TRACE_TYPE_80211_PRISM:
580                        wifi=(libtrace_80211_t*)((char*)link+144);
581                        return (uint8_t*)&wifi->mac1;
582                case TRACE_TYPE_ETH:
583                        return (uint8_t*)&ethptr->ether_dhost;
584                case TRACE_TYPE_POS:
585                case TRACE_TYPE_NONE:
586                case TRACE_TYPE_ATM:
587                case TRACE_TYPE_HDLC_POS:
588                case TRACE_TYPE_LINUX_SLL:
589                case TRACE_TYPE_PFLOG:
590                case TRACE_TYPE_DUCK:
591                        /* No MAC address */
592                        return NULL;
593        }
594        fprintf(stderr,"Not implemented\n");
595        assert(0);
596        return NULL;
597}
598
599DLLEXPORT struct sockaddr *trace_get_source_address(const libtrace_packet_t *packet, 
600                struct sockaddr *addr)
601{
602        uint16_t proto;
603        uint32_t remaining;
604        void *l3;
605        struct ports_t *ports;
606        static struct sockaddr_storage dummy;
607
608        if (!addr)
609                addr=(struct sockaddr*)&dummy;
610
611        remaining = trace_get_capture_length(packet);
612
613        l3 = trace_get_payload_from_link(
614                        trace_get_link(packet),
615                        trace_get_link_type(packet),
616                        &proto,
617                        &remaining);
618
619        if (!l3)
620                return false;
621
622        l3 = trace_get_vlan_payload_from_ethernet_payload(l3,
623                        &proto,
624                        &remaining);
625
626        if (!l3)
627                return NULL;
628
629        switch (proto) {
630                case 0x0800: /* IPv4 */
631                {
632                        struct sockaddr_in *addr4=(struct sockaddr_in*)addr;
633                        libtrace_ip_t *ip = (libtrace_ip_t*)l3;
634                        ports = trace_get_payload_from_ip(ip,NULL,&remaining);
635                        addr4->sin_family=AF_INET;
636                        if (ports && remaining>=sizeof(*ports))
637                                addr4->sin_port=ports->src;
638                        else
639                                addr4->sin_port=0;
640                        addr4->sin_addr=ip->ip_src;
641                        return addr;
642                }
643                case 0x86DD: /* IPv6 */
644                {
645                        struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;
646                        libtrace_ip6_t *ip6 = (libtrace_ip6_t*)l3;
647                        ports = trace_get_payload_from_ip6(ip6,NULL,&remaining);
648                        addr6->sin6_family=AF_INET6;
649                        if (ports && remaining>=sizeof(*ports))
650                                addr6->sin6_port=ports->dst;
651                        else
652                                addr6->sin6_port=0;
653                        addr6->sin6_flowinfo=0;
654                        addr6->sin6_addr=ip6->ip_src;
655                        return addr;
656                }
657                default:
658                        return NULL;
659        }
660}
661
662DLLEXPORT struct sockaddr *trace_get_destination_address(const libtrace_packet_t *packet, 
663                struct sockaddr *addr)
664{
665        uint16_t proto;
666        uint32_t remaining;
667        void *transport;
668        static struct sockaddr_storage dummy;
669
670        if (!addr)
671                addr=(struct sockaddr*)&dummy;
672
673        remaining = trace_get_capture_length(packet);
674
675        transport = trace_get_payload_from_link(
676                        trace_get_link(packet),
677                        trace_get_link_type(packet),
678                        &proto,
679                        &remaining);
680
681        if (!transport)
682                return false;
683
684        transport = trace_get_vlan_payload_from_ethernet_payload(transport,
685                        &proto,
686                        &remaining);
687
688        if (!transport)
689                return false;
690
691        switch (proto) {
692                case 0x0800: /* IPv4 */
693                {
694                        struct sockaddr_in *addr4=(struct sockaddr_in*)addr;
695                        libtrace_ip_t *ip = (libtrace_ip_t*)transport;
696                        addr4->sin_family=AF_INET;
697                        addr4->sin_port=0;
698                        addr4->sin_addr=ip->ip_dst;
699                        return addr;
700                }
701                case 0x86DD: /* IPv6 */
702                {
703                        struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;
704                        libtrace_ip6_t *ip6 = (libtrace_ip6_t*)transport;
705                        addr6->sin6_family=AF_INET6;
706                        addr6->sin6_port=0;
707                        addr6->sin6_flowinfo=0;
708                        addr6->sin6_addr=ip6->ip_dst;
709                        return addr;
710                }
711                default:
712                        return NULL;
713        }
714}
715
716/* parse an ip or tcp option
717 * @param[in,out] ptr   the pointer to the current option
718 * @param[in,out] len   the length of the remaining buffer
719 * @param[out] type     the type of the option
720 * @param[out] optlen   the length of the option
721 * @param[out] data     the data of the option
722 *
723 * @returns bool true if there is another option (and the fields are filled in)
724 *               or false if this was the last option.
725 *
726 * This updates ptr to point to the next option after this one, and updates
727 * len to be the number of bytes remaining in the options area.  Type is updated
728 * to be the code of this option, and data points to the data of this option,
729 * with optlen saying how many bytes there are.
730 *
731 * @note Beware of fragmented packets.
732 * @author Perry Lorier
733 */
734DLLEXPORT int trace_get_next_option(unsigned char **ptr,int *len,
735                        unsigned char *type,
736                        unsigned char *optlen,
737                        unsigned char **data)
738{
739        if (*len<=0)
740                return 0;
741        *type=**ptr;
742        switch(*type) {
743                case 0: /* End of options */
744                        return 0;
745                case 1: /* Pad */
746                        (*ptr)++;
747                        (*len)--;
748                        return 1;
749                default:
750                        *optlen = *(*ptr+1);
751                        if (*optlen<2)
752                                return 0; /* I have no idea wtf is going on
753                                           * with these packets
754                                           */
755                        (*len)-=*optlen;
756                        (*data)=(*ptr+2);
757                        (*ptr)+=*optlen;
758                        if (*len<0)
759                                return 0;
760                        return 1;
761        }
762        assert(0);
763}
764
765
Note: See TracBrowser for help on using the repository browser.