and added files
[bcm963xx.git] / userapps / opensource / net-snmp / snmplib / snmpUDPIPv6Domain.c
1 #include <net-snmp/net-snmp-config.h>
2
3 #include <stdio.h>
4 #include <sys/types.h>
5 #include <ctype.h>
6 #include <errno.h>
7
8 #if HAVE_STRING_H
9 #include <string.h>
10 #else
11 #include <strings.h>
12 #endif
13 #if HAVE_STDLIB_H
14 #include <stdlib.h>
15 #endif
16 #if HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #if HAVE_SYS_SOCKET_H
20 #include <sys/socket.h>
21 #endif
22 #if HAVE_NETINET_IN_H
23 #include <netinet/in.h>
24 #endif
25 #if HAVE_ARPA_INET_H
26 #include <arpa/inet.h>
27 #endif
28 #if HAVE_NETDB_H
29 #include <netdb.h>
30 #endif
31 #if HAVE_NET_IF_H
32 #include <net/if.h>
33 #endif
34
35 #if HAVE_DMALLOC_H
36 #include <dmalloc.h>
37 #endif
38
39 #include <net-snmp/types.h>
40 #include <net-snmp/output_api.h>
41 #include <net-snmp/config_api.h>
42
43 #include <net-snmp/library/snmp_transport.h>
44 #include <net-snmp/library/snmpUDPIPv6Domain.h>
45
46 oid netsnmp_UDPIPv6Domain[10] = { ENTERPRISE_MIB, 3, 3, 4 };
47 static netsnmp_tdomain udp6Domain;
48
49 /*
50  * Return a string representing the address in data, or else the "far end"
51  * address if data is NULL.  
52  */
53
54 static char *
55 netsnmp_udp6_fmtaddr(netsnmp_transport *t, void *data, int len)
56 {
57     struct sockaddr_in6 *to = NULL;
58
59     DEBUGMSGTL(("netsnmp_udp6", "fmtaddr: t = %p, data = %p, len = %d\n", t,
60                 data, len));
61     if (data != NULL && len == sizeof(struct sockaddr_in6)) {
62         to = (struct sockaddr_in6 *) data;
63     } else if (t != NULL && t->data != NULL) {
64         to = (struct sockaddr_in6 *) t->data;
65     }
66     if (to == NULL) {
67         return strdup("UDP/IPv6: unknown");
68     } else {
69         char addr[INET6_ADDRSTRLEN];
70         char tmp[INET6_ADDRSTRLEN + 8];
71
72         sprintf(tmp, "[%s]:%hd",
73                 inet_ntop(AF_INET6, (void *) &(to->sin6_addr), addr,
74                           INET6_ADDRSTRLEN), ntohs(to->sin6_port));
75         return strdup(tmp);
76     }
77 }
78
79
80
81 /*
82  * You can write something into opaque that will subsequently get passed back 
83  * to your send function if you like.  For instance, you might want to
84  * remember where a PDU came from, so that you can send a reply there...  
85  */
86
87 static int
88 netsnmp_udp6_recv(netsnmp_transport *t, void *buf, int size,
89                   void **opaque, int *olength)
90 {
91     int             rc = -1;
92     socklen_t       fromlen = sizeof(struct sockaddr_in6);
93     struct sockaddr *from;
94
95     if (t != NULL && t->sock >= 0) {
96         from = (struct sockaddr *) malloc(sizeof(struct sockaddr_in6));
97         if (from == NULL) {
98             *opaque = NULL;
99             *olength = 0;
100             return -1;
101         } else {
102             memset(from, 0, fromlen);
103         }
104
105         while (rc < 0) {
106           rc = recvfrom(t->sock, buf, size, 0, from, &fromlen);
107           if (rc < 0 && errno != EINTR) {
108             break;
109           }
110         }
111
112         if (rc >= 0) {
113             char *string = netsnmp_udp6_fmtaddr(NULL, from, fromlen);
114             DEBUGMSGTL(("netsnmp_udp6",
115                         "recvfrom fd %d got %d bytes (from %s)\n", t->sock,
116                         rc, string));
117             free(string);
118         } else {
119             DEBUGMSGTL(("netsnmp_udp6", "recvfrom fd %d err %d (\"%s\")\n",
120                         t->sock, errno, strerror(errno)));
121         }
122         *opaque = (void *) from;
123         *olength = sizeof(struct sockaddr_in6);
124     }
125     return rc;
126 }
127
128
129
130 static int
131 netsnmp_udp6_send(netsnmp_transport *t, void *buf, int size,
132                   void **opaque, int *olength)
133 {
134     int rc = -1;
135     struct sockaddr *to = NULL;
136
137     if (opaque != NULL && *opaque != NULL &&
138         *olength == sizeof(struct sockaddr_in6)) {
139         to = (struct sockaddr *) (*opaque);
140     } else if (t != NULL && t->data != NULL &&
141                t->data_length == sizeof(struct sockaddr_in6)) {
142         to = (struct sockaddr *) (t->data);
143     }
144
145     if (to != NULL && t != NULL && t->sock >= 0) {
146         char *string = netsnmp_udp6_fmtaddr(NULL, (void *)to,
147                                             sizeof(struct sockaddr_in6));
148         DEBUGMSGTL(("netsnmp_udp6", "send %d bytes from %p to %s on fd %d\n",
149                     size, buf, string, t->sock));
150         free(string);
151         while (rc < 0) {
152             rc = sendto(t->sock, buf, size, 0, to,sizeof(struct sockaddr_in6));
153             if (rc < 0 && errno != EINTR) {
154                 break;
155             }
156         }
157     }
158     return rc;
159 }
160
161
162
163 static int
164 netsnmp_udp6_close(netsnmp_transport *t)
165 {
166     int rc = -1;
167     if (t != NULL && t->sock >= 0) {
168         DEBUGMSGTL(("netsnmp_udp6", "close fd %d\n", t->sock));
169 #ifndef HAVE_CLOSESOCKET
170         rc = close(t->sock);
171 #else
172         rc = closesocket(t->sock);
173 #endif
174         t->sock = -1;
175     }
176     return rc;
177 }
178
179
180
181 /*
182  * Open a UDP/IPv6-based transport for SNMP.  Local is TRUE if addr is the
183  * local address to bind to (i.e. this is a server-type session); otherwise
184  * addr is the remote address to send things to.  
185  */
186
187 netsnmp_transport *
188 netsnmp_udp6_transport(struct sockaddr_in6 *addr, int local)
189 {
190     netsnmp_transport *t = NULL;
191     int             rc = 0, udpbuf = (1 << 17);
192     char           *string = NULL;
193
194     if (addr == NULL || addr->sin6_family != AF_INET6) {
195         return NULL;
196     }
197
198     t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport));
199     if (t == NULL) {
200         return NULL;
201     }
202
203     string = netsnmp_udp6_fmtaddr(NULL, (void *) addr,
204                                   sizeof(struct sockaddr_in6));
205     DEBUGMSGTL(("netsnmp_udp6", "open %s %s\n", local ? "local" : "remote",
206                 string));
207     free(string);
208
209     memset(t, 0, sizeof(netsnmp_transport));
210
211     t->domain = netsnmp_UDPIPv6Domain;
212     t->domain_length =
213         sizeof(netsnmp_UDPIPv6Domain) / sizeof(netsnmp_UDPIPv6Domain[0]);
214
215     t->sock = socket(PF_INET6, SOCK_DGRAM, 0);
216     if (t->sock < 0) {
217         netsnmp_transport_free(t);
218         return NULL;
219     }
220 #ifdef  SO_BSDCOMPAT
221     /*
222      * Patch for Linux.  Without this, UDP packets that fail get an ICMP
223      * response.  Linux turns the failed ICMP response into an error message
224      * and return value, unlike all other OS's.  
225      */
226     {
227         int             one = 1;
228         setsockopt(t->sock, SOL_SOCKET, SO_BSDCOMPAT, &one, sizeof(one));
229     }
230 #endif                          /*SO_BSDCOMPAT */
231
232     /*
233      * Try to set the send and receive buffers to a reasonably large value, so
234      * that we can send and receive big PDUs (defaults to 8192 bytes (!) on
235      * Solaris, for instance).  Don't worry too much about errors -- just
236      * plough on regardless.  
237      */
238
239 #ifdef  SO_SNDBUF
240     if (setsockopt(t->sock, SOL_SOCKET, SO_SNDBUF, &udpbuf, sizeof(int)) != 0){
241         DEBUGMSGTL(("netsnmp_udp6", "couldn't set SO_SNDBUF to %d bytes: %s\n",
242                     udpbuf, strerror(errno)));
243     }
244 #endif                          /*SO_SNDBUF */
245
246 #ifdef  SO_RCVBUF
247     if (setsockopt(t->sock, SOL_SOCKET, SO_RCVBUF, &udpbuf, sizeof(int)) != 0){
248         DEBUGMSGTL(("netsnmp_udp6", "couldn't set SO_RCVBUF to %d bytes: %s\n",
249                     udpbuf, strerror(errno)));
250     }
251 #endif                          /*SO_RCVBUF */
252
253     if (local) {
254         /*
255          * This session is inteneded as a server, so we must bind on to the
256          * given IP address, which may include an interface address, or could
257          * be INADDR_ANY, but certainly includes a port number.
258          */
259
260 #ifdef IPV6_V6ONLY
261         /* Try to restrict PF_INET6 socket to IPv6 communications only. */
262         {
263           int one=1;
264           if (setsockopt(t->sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) != 0) {
265             DEBUGMSGTL(("netsnmp_udp6", "couldn't set IPV6_V6ONLY to %d bytes: %s\n", one, strerror(errno)));
266           } 
267         }
268 #endif
269
270         rc = bind(t->sock, (struct sockaddr *) addr,
271                   sizeof(struct sockaddr_in6));
272         if (rc != 0) {
273             netsnmp_udp6_close(t);
274             netsnmp_transport_free(t);
275             return NULL;
276         }
277         t->local = malloc(18);
278         if (t->local == NULL) {
279             netsnmp_udp6_close(t);
280             netsnmp_transport_free(t);
281         }
282         memcpy(t->local, addr->sin6_addr.s6_addr, 16);
283         t->local[16] = (addr->sin6_port & 0xff00) >> 8;
284         t->local[17] = (addr->sin6_port & 0x00ff) >> 0;
285         t->local_length = 18;
286         t->data = NULL;
287         t->data_length = 0;
288     } else {
289         /*
290          * This is a client session.  Save the address in the
291          * transport-specific data pointer for later use by netsnmp_udp6_send.
292          */
293
294         t->data = malloc(sizeof(struct sockaddr_in6));
295         if (t->data == NULL) {
296             netsnmp_transport_free(t);
297             return NULL;
298         }
299         memcpy(t->data, addr, sizeof(struct sockaddr_in6));
300         t->data_length = sizeof(struct sockaddr_in6);
301         t->remote = malloc(18);
302         if (t->remote == NULL) {
303             netsnmp_udp6_close(t);
304             netsnmp_transport_free(t);
305             return NULL;
306         }
307         memcpy(t->remote, addr->sin6_addr.s6_addr, 16);
308         t->remote[16] = (addr->sin6_port & 0xff00) >> 8;
309         t->remote[17] = (addr->sin6_port & 0x00ff) >> 0;
310         t->remote_length = 18;
311     }
312
313     /*
314      * 16-bit length field, 8 byte UDP header, 40 byte IPv6 header.  
315      */
316
317     t->msgMaxSize = 0xffff - 8 - 40;
318     t->f_recv     = netsnmp_udp6_recv;
319     t->f_send     = netsnmp_udp6_send;
320     t->f_close    = netsnmp_udp6_close;
321     t->f_accept   = NULL;
322     t->f_fmtaddr  = netsnmp_udp6_fmtaddr;
323
324     return t;
325 }
326
327
328
329 int
330 netsnmp_sockaddr_in6(struct sockaddr_in6 *addr,
331                      const char *inpeername, int remote_port)
332 {
333     char           *cp = NULL, *peername = NULL;
334     char            debug_addr[INET6_ADDRSTRLEN];
335 #if HAVE_GETADDRINFO
336     struct addrinfo *addrs = NULL;
337     struct addrinfo hint;
338     int             err;
339 #elif HAVE_GETIPNODEBYNAME
340     struct hostent *hp = NULL;
341     int             err;
342 #elif HAVE_GETHOSTBYNAME
343     struct hostent *hp = NULL;
344 #endif
345
346     if (addr == NULL) {
347         return 0;
348     }
349
350     DEBUGMSGTL(("netsnmp_sockaddr_in6", "addr %p, peername \"%s\"\n",
351                 addr, inpeername ? inpeername : "[NIL]"));
352
353     memset(addr, 0, sizeof(struct sockaddr_in6));
354     addr->sin6_family = AF_INET6;
355     addr->sin6_addr = in6addr_any;
356
357     if (remote_port > 0) {
358         addr->sin6_port = htons(remote_port);
359     } else if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
360                                   NETSNMP_DS_LIB_DEFAULT_PORT) > 0) {
361         addr->sin6_port = htons(netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
362                                                  NETSNMP_DS_LIB_DEFAULT_PORT));
363     } else {
364         addr->sin6_port = htons(SNMP_PORT);
365     }
366
367     if (inpeername != NULL) {
368         /*
369          * Duplicate the peername because we might want to mank around with
370          * it.  
371          */
372
373         peername = strdup(inpeername);
374         if (peername == NULL) {
375             return 0;
376         }
377
378         for (cp = peername; *cp && isdigit((int) *cp); cp++);
379         if (!*cp && atoi(peername) != 0) {
380             /*
381              * Okay, it looks like JUST a port number.  
382              */
383             DEBUGMSGTL(("netsnmp_sockaddr_in6", "totally numeric: %d\n",
384                         atoi(peername)));
385             addr->sin6_port = htons(atoi(peername));
386             goto resolved;
387         }
388
389         /*
390          * See if it is an IPv6 address, which covered with square brankets
391          * with an appended :port.  
392          */
393         if (*peername == '[') {
394             cp = strchr(peername, ']');
395             if (cp != NULL) {
396               /*
397                * See if it is an IPv6 link-local address with interface
398                * name as <zone_id>, like fe80::1234%eth0.
399                * Please refer to the internet draft, IPv6 Scoped Address Architecture
400                * http://www.ietf.org/internet-drafts/draft-ietf-ipngwg-scoping-arch-04.txt
401                *
402                */
403                 char *scope_id;
404                 unsigned int if_index = 0;
405                 *cp = '\0';
406                 scope_id = strchr(peername + 1, '%');
407                 if (scope_id != NULL) {
408                     *scope_id = '\0';
409                     if_index = if_nametoindex(scope_id + 1);
410                 }
411                 if (*(cp + 1) == ':') {
412                     if (atoi(cp + 2) != 0 &&
413                         inet_pton(AF_INET6, peername + 1,
414                                   (void *) &(addr->sin6_addr))) {
415                         DEBUGMSGTL(("netsnmp_sockaddr_in6",
416                                     "IPv6 address with port suffix :%d\n",
417                                     atoi(cp + 2)));
418                         addr->sin6_port = htons(atoi(cp + 2));
419                         addr->sin6_scope_id = if_index;
420                         goto resolved;
421                     }
422                 } else {
423                     if (inet_pton
424                         (AF_INET6, peername + 1,
425                          (void *) &(addr->sin6_addr))) {
426                         DEBUGMSGTL(("netsnmp_sockaddr_in6",
427                                     "IPv6 address with square brankets\n"));
428                         addr->sin6_port = htons(SNMP_PORT);
429                         addr->sin6_scope_id = if_index;
430                         goto resolved;
431                     }
432                 }
433                 if (scope_id != NULL) {
434                   *scope_id = '%';
435                 }
436                 *cp = ']';
437             }
438         }
439
440         cp = strrchr(peername, ':');
441         if (cp != NULL) {
442             char *scope_id;
443             unsigned int if_index = 0;
444             *cp = '\0';
445             scope_id = strchr(peername + 1, '%');
446             if (scope_id != NULL) {
447               *scope_id = '\0';
448               if_index = if_nametoindex(scope_id + 1);
449             }
450             if (atoi(cp + 1) != 0 &&
451                 inet_pton(AF_INET6, peername,
452                           (void *) &(addr->sin6_addr))) {
453                 DEBUGMSGTL(("netsnmp_sockaddr_in6",
454                             "IPv6 address with port suffix :%d\n",
455                             atoi(cp + 1)));
456                 addr->sin6_port = htons(atoi(cp + 1));
457                 addr->sin6_scope_id = if_index;
458                 goto resolved;
459             }
460             if (scope_id != NULL) {
461               *scope_id = '%';
462             }
463             *cp = ':';
464         }
465
466         /*
467          * See if it is JUST an IPv6 address.  
468          */
469         if (inet_pton(AF_INET6, peername, (void *) &(addr->sin6_addr))) {
470             DEBUGMSGTL(("netsnmp_sockaddr_in6", "just IPv6 address\n"));
471             goto resolved;
472         }
473
474         /*
475          * Well, it must be a hostname then, possibly with an appended :port.
476          * Sort that out first.  
477          */
478
479         cp = strrchr(peername, ':');
480         if (cp != NULL) {
481             *cp = '\0';
482             if (atoi(cp + 1) != 0) {
483                 DEBUGMSGTL(("netsnmp_sockaddr_in6",
484                             "hostname(?) with port suffix :%d\n",
485                             atoi(cp + 1)));
486                 addr->sin6_port = htons(atoi(cp + 1));
487             } else {
488                 /*
489                  * No idea, looks bogus but we might as well pass the full thing to
490                  * the name resolver below.  
491                  */
492                 *cp = ':';
493                 DEBUGMSGTL(("netsnmp_sockaddr_in6",
494                             "hostname(?) with embedded ':'?\n"));
495             }
496             /*
497              * Fall through.  
498              */
499         }
500 #if HAVE_GETADDRINFO
501         memset(&hint, 0, sizeof hint);
502         hint.ai_flags = 0;
503         hint.ai_family = PF_INET6;
504         hint.ai_socktype = SOCK_DGRAM;
505         hint.ai_protocol = 0;
506
507         err = getaddrinfo(peername, NULL, &hint, &addrs);
508         if (err != 0) {
509             snmp_log(LOG_ERR, "getaddrinfo: %s %s\n", peername,
510                      gai_strerror(err));
511             free(peername);
512             return 0;
513         }
514         DEBUGMSGTL(("netsnmp_sockaddr_in6", "hostname (resolved okay)\n"));
515         memcpy(&addr->sin6_addr,
516                &((struct sockaddr_in6 *) addrs->ai_addr)->sin6_addr,
517                sizeof(struct in6_addr));
518 #elif HAVE_GETIPNODEBYNAME
519         hp = getipnodebyname(peername, AF_INET6, 0, &err);
520         if (hp == NULL) {
521             DEBUGMSGTL(("netsnmp_sockaddr_in6",
522                         "hostname (couldn't resolve = %d)\n", err));
523             free(peername);
524             return 0;
525         }
526         DEBUGMSGTL(("netsnmp_sockaddr_in6", "hostname (resolved okay)\n"));
527         memcpy(&(addr->sin6_addr), hp->h_addr, hp->h_length);
528 #elif HAVE_GETHOSTBYNAME
529         hp = gethostbyname(peername);
530         if (hp == NULL) {
531             DEBUGMSGTL(("netsnmp_sockaddr_in6",
532                         "hostname (couldn't resolve)\n"));
533             free(peername);
534             return 0;
535         } else {
536             if (hp->h_addrtype != AF_INET6) {
537                 DEBUGMSGTL(("netsnmp_sockaddr_in6",
538                             "hostname (not AF_INET6!)\n"));
539                 free(peername);
540                 return 0;
541             } else {
542                 DEBUGMSGTL(("netsnmp_sockaddr_in6",
543                             "hostname (resolved okay)\n"));
544                 memcpy(&(addr->sin6_addr), hp->h_addr, hp->h_length);
545             }
546         }
547 #else                           /*HAVE_GETHOSTBYNAME */
548         /*
549          * There is no name resolving function available.  
550          */
551         snmp_log(LOG_ERR,
552                  "no getaddrinfo()/getipnodebyname()/gethostbyname()\n");
553         free(peername);
554         return 0;
555 #endif                          /*HAVE_GETHOSTBYNAME */
556     } else {
557         DEBUGMSGTL(("netsnmp_sockaddr_in6", "NULL peername"));
558         return 0;
559     }
560
561   resolved:
562     DEBUGMSGTL(("netsnmp_sockaddr_in6", "return { AF_INET6, [%s]:%hu }\n",
563                 inet_ntop(AF_INET6, &addr->sin6_addr, debug_addr,
564                           sizeof(debug_addr)), ntohs(addr->sin6_port)));
565     free(peername);
566     return 1;
567 }
568
569
570 /*
571  * int
572  * inet_make_mask_addr( int pf, void *dst, int masklength )
573  *      convert from bit length specified masklength to network format, 
574  *      which fills 1 from until specified bit length.
575  *      dst is usally the structer of sockaddr_in or sockaddr_in6. 
576  *      makelength must be an interger from 0 to 32 if pf is PF_INET,
577  *      or from 0 to 128 if pf is PF_INET6.
578  * return:
579  *      0 if the input data, masklength was valid for 
580  *      the specified protocol family.
581  *      -1 if the the input data wasn't valid.
582  */
583
584 int
585 inet_make_mask_addr(int pf, void *dst, int masklength)
586 {
587
588     unsigned long   Mask = 0;
589     int             maskBit = 0x80000000L;
590     unsigned char   mask = 0;
591     unsigned char   maskbit = 0x80L;
592     int             i, j, jj;
593
594
595     switch (pf) {
596     case PF_INET:
597         if (masklength < 0 || masklength > 32)
598             return -1;
599
600         ((struct in_addr *) dst)->s_addr = 0;
601
602         while (masklength--) {
603             Mask |= maskBit;
604             maskBit >>= 1;
605         }
606         ((struct in_addr *) dst)->s_addr = htonl(Mask);
607         break;
608
609     case PF_INET6:
610         if (masklength < 0 || masklength > 128)
611             return -1;
612
613
614         for (i = 0; i < 16; i++) {
615             (*(uint8_t *) (&((struct in6_addr *) dst)->s6_addr[i])) = 0x00;
616         }
617
618         j = (int) masklength / 8;
619         jj = masklength % 8;
620
621         for (i = 0; i < j; i++) {
622             (*(uint8_t *) (&((struct in6_addr *) dst)->s6_addr[i])) = 0xff;
623         }
624         while (jj--) {
625             mask |= maskbit;
626             maskbit >>= 1;
627         }
628         (*(uint8_t *) (&((struct in6_addr *) dst)->s6_addr[j])) = mask;
629         break;
630     default:
631         return -1;              /* unsupported protocol family */
632     }
633     return 0;
634 }
635
636 /*
637  * int
638  * inet_addr_complement( int pf, void *src, void *dst )
639  *      convert from src to dst, which all bits 
640  *      are bit-compliment of src.
641  *      Src, dst are ususally sockaddr_in or sockaddr_in6.  
642  * return:
643  *      0 if the input data src and dst have the same size
644  *      -1 if the the input data wasn't valid.
645  */
646
647 int
648 inet_addr_complement(int pf, void *src, void *dst)
649 {
650
651     int             i;
652
653     if (sizeof(src) != sizeof(dst))
654         return -1;
655
656     switch (pf) {
657     case PF_INET:
658         ((struct in_addr *) dst)->s_addr =
659             ~((struct in_addr *) src)->s_addr;
660         break;
661     case PF_INET6:
662         for (i = 0; i < 16; i++) {
663             (*(uint8_t *) (&((struct in6_addr *) dst)->s6_addr[i])) =
664                 (~(*(uint8_t *) (&((struct in6_addr *) src)->s6_addr[i])))
665                 & 0xff;
666         }
667         break;
668     default:
669         return -1;
670     }
671     return 0;
672 }
673
674 /*
675  * int
676  * inet_addr_and( int pf, void *src1, void *src2, void *dst) 
677  *      take AND operation on src1 and src2, and output the result to dst.
678  *      Src1, src2, and dst are ususally sockaddr_in or sockaddr_in6.  
679  * return:
680  *      0 if the input data src and dst have the same size
681  *      -1 if the the input data are not the same size
682  */
683
684 int
685 inet_addr_and(int pf, void *src1, void *src2, void *dst)
686 {
687     int             i;
688
689     if (sizeof(src1) != sizeof(src2) || sizeof(src2) != sizeof(dst))
690         return -1;
691
692     switch (pf) {
693     case PF_INET:
694         ((struct in_addr *) dst)->s_addr =
695             ((struct in_addr *) src1)->s_addr & ((struct in_addr *) src2)->
696             s_addr;
697         break;
698
699     case PF_INET6:
700         for (i = 0; i < 16; i++) {
701             (*(uint8_t *) (&((struct in6_addr *) dst)->s6_addr[i])) =
702                 (*(uint8_t *) (&((struct in6_addr *) src1)->s6_addr[i])) &
703                 (*(uint8_t *) (&((struct in6_addr *) src2)->s6_addr[i]));
704         }
705         break;
706     default:
707         return -1;
708     }
709     return 0;
710 }
711
712
713 /*
714  * int
715  * inet_addrs_consistence (int pf, void *net, void *mask ) 
716  *      This function checks if the network address net is consistent
717  *      with the netmask address, mask.
718  *      Net and mask are ususally sockaddr_in or sockaddr_in6.  
719  * Note:
720  *      Must spefiey protocol family in pf.
721  * return:
722  *      0 if there is no consistence with address "net" and "mask".
723  *      -1 if network address is inconsistent with netmask address, for 
724  *      instance, network address is 192.168.0.128 in spite of netmask, 
725  *      which is 255.255.255.0. 
726  *      The case that the size of net and mask are different also returns -1.
727  */
728
729 int
730 inet_addrs_consistence(int pf, void *net, void *mask)
731 {
732     struct sockaddr_in *tmp, *dst;
733     struct sockaddr_in6 *tmp6, *dst6;
734     int             ret;
735
736     switch (pf) {
737     case PF_INET:
738         tmp = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in));
739         memset(tmp, 0, sizeof(*tmp));
740         tmp->sin_family = PF_INET;
741         if (inet_addr_complement
742             (PF_INET, (struct in_addr *) mask, &tmp->sin_addr) != 0) {
743             config_perror("Fail in function of inet_addr_complement()");
744             free(tmp);
745             return -1;
746         }
747         dst = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in));
748         memset(dst, 0, sizeof(*dst));
749         dst->sin_family = PF_INET;
750         if (inet_addr_and
751             (PF_INET, (struct in_addr *) net, &tmp->sin_addr,
752              &dst->sin_addr) != 0) {
753             config_perror("Fail in function of inet_addr_and()");
754             free(dst);
755             free(tmp);
756             return -1;
757         }
758         ret = ((dst->sin_addr.s_addr == INADDR_ANY) ? 0 : -1);
759         free(dst);
760         free(tmp);
761         break;
762     case PF_INET6:
763         tmp6 = (struct sockaddr_in6 *) malloc(sizeof(struct sockaddr_in6));
764         memset(tmp6, 0, sizeof(*tmp6));
765         tmp6->sin6_family = PF_INET6;
766         if (inet_addr_complement
767             (PF_INET6, (struct in6_addr *) mask, &tmp6->sin6_addr) != 0) {
768             config_perror("Fail in function of inet_addr_complement()");
769             free(tmp6);
770             return -1;
771         }
772         dst6 = (struct sockaddr_in6 *) malloc(sizeof(struct sockaddr_in6));
773         memset(dst6, 0, sizeof(*dst6));
774         dst6->sin6_family = PF_INET6;
775         if (inet_addr_and
776             (PF_INET6, (struct in6_addr *) net, &tmp6->sin6_addr,
777              &dst6->sin6_addr)) {
778             config_perror("Fail in function of inet_addr_and()");
779             free(dst6);
780             free(tmp6);
781             return -1;
782         }
783         ret = (IN6_IS_ADDR_UNSPECIFIED(&dst6->sin6_addr) == 1 ? 0 : -1);
784         free(dst6);
785         free(tmp6);
786         break;
787     default:
788         return -1;
789     }
790     return ret;
791 }
792
793 /*
794  * int
795  * masked_address_are_equal (pf, from, mask, network) 
796  *      This function takes AND operation on address "from" and "mask",
797  *      and check the result is equal to address "network". 
798  *      From, net and mask are ususally sockaddr_in or sockaddr_in6.  
799  * Note:
800  *      Must spefiey protocol family in pf.
801  * return:
802  *      0 if address "from" masked by address "mask" is eqaul to 
803  *      address "network". 
804  *      -1 if address "from" masked by address "mask" isn't eqaul to 
805  *      address "network". For instance, address "from" is 
806  *       192.168.0.129 and "mask" is 255.255.255.128. Then, masked 
807  *      address is 192.168.0.128. If address "network" is 192.168.0.128,
808  *      return 0, otherwise -1.
809  *      Also retunn -1 if each address family of from, mask, network
810  *      isn't the same.
811  */
812
813 int
814 masked_address_are_equal(int af, struct sockaddr_storage *from,
815                          struct sockaddr_storage *mask,
816                          struct sockaddr_storage *network)
817 {
818
819     struct sockaddr_storage ss;
820     memset(&ss, 0, sizeof(ss));
821
822     switch (af) {
823     case PF_INET:
824         if (mask->ss_family != PF_INET || network->ss_family != PF_INET) {
825             return -1;
826         }
827         ss.ss_family = PF_INET;
828         inet_addr_and(PF_INET,
829                       &((struct sockaddr_in *) from)->sin_addr,
830                       &((struct sockaddr_in *) mask)->sin_addr,
831                       &((struct sockaddr_in *) &ss)->sin_addr);
832         if (((struct sockaddr_in *) &ss)->sin_addr.s_addr ==
833             ((struct sockaddr_in *) network)->sin_addr.s_addr) {
834             return 0;
835         } else {
836             return -1;
837         }
838         break;
839     case PF_INET6:
840         if (mask->ss_family != PF_INET6 || network->ss_family != PF_INET6) {
841             return -1;
842         }
843         ss.ss_family = PF_INET6;
844         inet_addr_and(PF_INET6,
845                       &((struct sockaddr_in6 *) from)->sin6_addr,
846                       &((struct sockaddr_in6 *) mask)->sin6_addr,
847                       &((struct sockaddr_in6 *) &ss)->sin6_addr);
848         if (IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *) &ss)->sin6_addr,
849                                &((struct sockaddr_in6 *) network)->
850                                sin6_addr) == 1) {
851             return 0;
852         } else {
853             return -1;
854         }
855         break;
856     default:
857         return -1;
858     }
859 }
860
861 /*
862  * The following functions provide the "com2sec6" configuration token
863  * functionality for compatibility.  
864  */
865
866 #define EXAMPLE_NETWORK       "NETWORK"
867 #define EXAMPLE_COMMUNITY     "COMMUNITY"
868
869 typedef struct _com2Sec6Entry {
870     char            community[VACMSTRINGLEN];
871     struct sockaddr_in6 network;
872     struct sockaddr_in6 mask;
873     char            secName[VACMSTRINGLEN];
874     struct _com2Sec6Entry *next;
875 } com2Sec6Entry;
876
877 com2Sec6Entry  *com2Sec6List = NULL, *com2Sec6ListLast = NULL;
878
879
880 void
881 memmove_com2Sec6Entry(com2Sec6Entry * c,
882                       char *secName,
883                       char *community,
884                       struct sockaddr_in6 net, struct sockaddr_in6 mask)
885 {
886     snprintf(c->secName, strlen(secName) + 1, "%s", secName);
887     snprintf(c->community, strlen(community) + 1, "%s", community);
888     memmove(&c->network, &net, sizeof(net));
889     memmove(&c->mask, &mask, sizeof(mask));
890     c->next = NULL;
891 }
892
893
894 void
895 netsnmp_udp6_parse_security(const char *token, char *param)
896 {
897     char           *secName = NULL, *community = NULL, *source = NULL;
898     char           *cp = NULL, *strnetwork = NULL, *strmask = NULL;
899     com2Sec6Entry  *e = NULL;
900     struct sockaddr_in6 net, mask;
901     struct sockaddr_in tmp;
902
903     memset(&net, 0, sizeof(net));
904     memset(&mask, 0, sizeof(mask));
905     memset(&tmp, 0, sizeof(tmp));
906     net.sin6_family = AF_INET6;
907     mask.sin6_family = AF_INET6;
908     tmp.sin_family = AF_INET;
909
910
911     /*
912      * Get security, source address/netmask and community strings.  
913      */
914     secName = strtok(param, "\t\n ");
915     if (secName == NULL) {
916         config_perror("missing NAME parameter");
917         return;
918     } else if (strlen(secName) > (VACMSTRINGLEN - 1)) {
919         config_perror("security name too long");
920         return;
921     }
922     source = strtok(NULL, "\t\n ");
923     if (source == NULL) {
924         config_perror("missing SOURCE parameter");
925         return;
926     } else if (strncmp(source, EXAMPLE_NETWORK, strlen(EXAMPLE_NETWORK)) ==
927                0) {
928         config_perror("example config NETWORK not properly configured");
929         return;
930     }
931     community = strtok(NULL, "\t\n ");
932     if (community == NULL) {
933         config_perror("missing COMMUNITY parameter\n");
934         return;
935     } else
936         if (strncmp
937             (community, EXAMPLE_COMMUNITY, strlen(EXAMPLE_COMMUNITY))
938             == 0) {
939         config_perror("example config COMMUNITY not properly configured");
940         return;
941     } else if (strlen(community) > (VACMSTRINGLEN - 1)) {
942         config_perror("community name too long");
943         return;
944     }
945
946     /*
947      * Process the source address/netmask string.  
948      */
949     cp = strchr(source, '/');
950     if (cp != NULL) {
951         /*
952          * Mask given.  
953          */
954         *cp = '\0';
955         strmask = cp + 1;
956     }
957
958     /*
959      * Deal with the network part first.  
960      */
961     if ((strcmp(source, "default") == 0) || (strcmp(source, "::") == 0)) {
962         strnetwork = strdup("0::0");
963         strmask = strdup("0::0");
964
965         inet_pton(AF_INET6, strnetwork, &net.sin6_addr);
966         inet_pton(AF_INET6, strmask, &mask.sin6_addr);
967
968         e = (com2Sec6Entry *) malloc(sizeof(com2Sec6Entry));
969         if (e == NULL) {
970             config_perror("memory error");
971             return;
972         }
973         /*
974          * Everything is okay.  Copy the parameters to the structure allocated
975          * above and add it to END of the list.  
976          */
977         if (strmask != NULL && strnetwork != NULL) {
978             DEBUGMSGTL(("netsnmp_udp6_parse_security",
979                         "<\"%s\", %s/%s> => \"%s\"\n", community,
980                         strnetwork, strmask, secName));
981             free(strmask);
982             free(strnetwork);
983         } else {
984             DEBUGMSGTL(("netsnmp_udp6_parse_security",
985                         "Couldn't allocate enough memory\n"));
986         }
987         memmove_com2Sec6Entry(e, secName, community, net, mask);
988         if (com2Sec6ListLast != NULL) {
989             com2Sec6ListLast->next = e;
990             com2Sec6ListLast = e;
991         } else {
992             com2Sec6ListLast = com2Sec6List = e;
993         }
994
995     } else {
996         /*
997          * Try interpreting as IPv6 address.  
998          */
999         if (inet_pton(AF_INET6, source, &net.sin6_addr) == 1) {
1000             if (strmask == NULL || *strmask == '\0') {
1001                 inet_make_mask_addr(PF_INET6, &mask.sin6_addr, 128);
1002             } else {
1003                 if (strchr(strmask, ':')) {
1004                     if (inet_pton(PF_INET6, strmask, &net.sin6_addr) != 1) {
1005                         config_perror("bad mask");
1006                         return;
1007                     }
1008                 } else {
1009                     if (inet_make_mask_addr
1010                         (PF_INET6, &mask.sin6_addr, atoi(strmask)) != 0) {
1011                         config_perror("bad mask");
1012                         return;
1013
1014                     }
1015                 }
1016             }
1017             /*
1018              * Check that the network and mask are consistent.  
1019              */
1020             if (inet_addrs_consistence
1021                 (PF_INET6, &net.sin6_addr, &mask.sin6_addr) != 0) {
1022                 config_perror("source/mask mismatch");
1023                 return;
1024             }
1025
1026             e = (com2Sec6Entry *) malloc(sizeof(com2Sec6Entry));
1027             if (e == NULL) {
1028                 config_perror("memory error");
1029                 return;
1030             }
1031
1032             /*
1033              * Everything is okay.  Copy the parameters to the structure allocated
1034              * above and add it to END of the list.  
1035              */
1036             if (strmask != NULL && strnetwork != NULL) {
1037                 DEBUGMSGTL(("netsnmp_udp6_parse_security",
1038                             "<\"%s\", %s/%s> => \"%s\"\n", community,
1039                             strnetwork, strmask, secName));
1040                 free(strmask);
1041                 free(strnetwork);
1042             } else {
1043                 DEBUGMSGTL(("netsnmp_udp6_parse_security",
1044                             "Couldn't allocate enough memory\n"));
1045             }
1046             memmove_com2Sec6Entry(e, secName, community, net, mask);
1047             if (com2Sec6ListLast != NULL) {
1048                 com2Sec6ListLast->next = e;
1049                 com2Sec6ListLast = e;
1050             } else {
1051                 com2Sec6ListLast = com2Sec6List = e;
1052             }
1053
1054         } else {
1055             /*
1056              * Nope, Must be a hostname.  
1057              */
1058             struct addrinfo hints, *ai, *res;
1059             char            hbuf[NI_MAXHOST];
1060             int             gai_error;
1061
1062             memset(&hints, 0, sizeof(hints));
1063             hints.ai_family = PF_INET6;
1064             hints.ai_socktype = SOCK_DGRAM;
1065             if ((gai_error = getaddrinfo(source, NULL, &hints, &res)) != 0) {
1066                 config_perror(gai_strerror(gai_error));
1067                 return;
1068             }
1069
1070             for (ai = res; ai != NULL; ai = ai->ai_next) {
1071                 if (getnameinfo
1072                     (ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), NULL,
1073                      0, NI_NUMERICHOST)) {
1074                     config_perror("getnameinfo failed");
1075                 }
1076                 memmove(ai->ai_addr, &net, sizeof(struct sockaddr_in6));
1077                 inet_make_mask_addr(AF_INET6, &mask.sin6_addr, 128);
1078
1079                 e = (com2Sec6Entry *) malloc(sizeof(com2Sec6Entry));
1080                 if (e == NULL) {
1081                     config_perror("memory error");
1082                     return;
1083                 }
1084
1085                 /*
1086                  * Everything is okay.  Copy the parameters to the structure allocated
1087                  * above and add it to END of the list.  
1088                  */
1089                 DEBUGMSGTL(("netsnmp_udp6_parse_security",
1090                             "<\"%s\", %s> => \"%s\"\n", community, hbuf,
1091                             secName));
1092                 memmove_com2Sec6Entry(e, secName, community, net, mask);
1093                 if (com2Sec6ListLast != NULL) {
1094                     com2Sec6ListLast->next = e;
1095                     com2Sec6ListLast = e;
1096                 } else {
1097                     com2Sec6ListLast = com2Sec6List = e;
1098                 }
1099             }
1100             if (res != NULL)
1101                 freeaddrinfo(res);
1102         }
1103         /*
1104          * free(strnetwork); 
1105          */
1106     }
1107 }
1108
1109 void
1110 netsnmp_udp6_com2Sec6List_free(void)
1111 {
1112     com2Sec6Entry  *e = com2Sec6List;
1113     while (e != NULL) {
1114         com2Sec6Entry  *tmp = e;
1115         e = e->next;
1116         free(tmp);
1117     }
1118     com2Sec6List = com2Sec6ListLast = NULL;
1119 }
1120
1121
1122 void
1123 netsnmp_udp6_agent_config_tokens_register(void)
1124 {
1125     register_app_config_handler("com2sec6", netsnmp_udp6_parse_security,
1126                                 netsnmp_udp6_com2Sec6List_free,
1127                                 "name IPv6-network-address[/netmask] community");
1128 }
1129
1130
1131
1132 /*
1133  * Return 0 if there are no com2sec entries, or return 1 if there ARE com2sec 
1134  * entries.  On return, if a com2sec entry matched the passed parameters,
1135  * then *secName points at the appropriate security name, or is NULL if the
1136  * parameters did not match any com2sec entry.  
1137  */
1138
1139 int
1140 netsnmp_udp6_getSecName(void *opaque, int olength,
1141                         const char *community,
1142                         int community_len, char **secName)
1143 {
1144     com2Sec6Entry  *c;
1145     struct sockaddr_in6 *from = (struct sockaddr_in6 *) opaque;
1146     char           *ztcommunity = NULL;
1147     char            str6[INET6_ADDRSTRLEN];
1148
1149     /*
1150      * Special case if there are NO entries (as opposed to no MATCHING
1151      * entries).  
1152      */
1153
1154     if (com2Sec6List == NULL) {
1155         DEBUGMSGTL(("netsnmp_udp6_getSecName", "no com2sec entries\n"));
1156         if (secName != NULL) {
1157             *secName = NULL;
1158         }
1159         return 0;
1160     }
1161
1162     /*
1163      * If there is no IPv6 source address, 
1164      * then there can be no valid security name.  
1165      */
1166
1167     if (opaque == NULL || olength != sizeof(struct sockaddr_in6)
1168         || from->sin6_family != PF_INET6) {
1169         DEBUGMSGTL(("netsnmp_udp6_getSecName",
1170                     "no IPv6 source address in PDU?\n"));
1171         if (secName != NULL) {
1172             *secName = NULL;
1173         }
1174         return 1;
1175     }
1176
1177     ztcommunity = (char *) malloc(community_len + 1);
1178     if (ztcommunity != NULL) {
1179         memcpy(ztcommunity, community, community_len);
1180         ztcommunity[community_len] = '\0';
1181     }
1182
1183     inet_ntop(AF_INET6, &from->sin6_addr, str6, sizeof(str6));
1184     DEBUGMSGTL(("netsnmp_udp6_getSecName", "resolve <\"%s\", %s>\n",
1185                 ztcommunity ? ztcommunity : "<malloc error>", str6));
1186
1187     for (c = com2Sec6List; c != NULL; c = c->next) {
1188         DEBUGMSGTL(("netsnmp_udp6_getSecName",
1189                     "compare <\"%s\", 0x%032/0x%032x>", c->community,
1190                     c->network, c->mask));
1191
1192         if ((community_len == strlen(c->community)) &&
1193             (memcmp(community, c->community, community_len) == 0) &&
1194             (masked_address_are_equal(from->sin6_family,
1195                                       (struct sockaddr_storage *) from,
1196                                       (struct sockaddr_storage *) &c->mask,
1197                                       (struct sockaddr_storage *) &c->
1198                                       network) == 0)) {
1199             DEBUGMSG(("netsnmp_udp6_getSecName", "... SUCCESS\n"));
1200             if (secName != NULL) {
1201                 *secName = c->secName;
1202             }
1203             break;
1204         }
1205         DEBUGMSG(("netsnmp_udp6_getSecName", "... nope\n"));
1206     }
1207     if (ztcommunity != NULL) {
1208         free(ztcommunity);
1209     }
1210     return 1;
1211 }
1212
1213 netsnmp_transport *
1214 netsnmp_udp6_create_tstring(const char *string, int local)
1215 {
1216     struct sockaddr_in6 addr;
1217
1218     if (netsnmp_sockaddr_in6(&addr, string, 0)) {
1219         return netsnmp_udp6_transport(&addr, local);
1220     } else {
1221         return NULL;
1222     }
1223 }
1224
1225
1226 /*
1227  * See:
1228  * 
1229  * http://www.ietf.org/internet-drafts/draft-ietf-ops-taddress-mib-01.txt
1230  * 
1231  * (or newer equivalent) for details of the TC which we are using for
1232  * the mapping here.  
1233  */
1234
1235 netsnmp_transport *
1236 netsnmp_udp6_create_ostring(const u_char * o, size_t o_len, int local)
1237 {
1238     struct sockaddr_in6 addr;
1239
1240     if (o_len == 18) {
1241         memset((u_char *) & addr, 0, sizeof(struct sockaddr_in6));
1242         addr.sin6_family = AF_INET6;
1243         memcpy((u_char *) & (addr.sin6_addr.s6_addr), o, 16);
1244         addr.sin6_port = (o[16] << 8) + o[17];
1245         return netsnmp_udp6_transport(&addr, local);
1246     }
1247     return NULL;
1248 }
1249
1250
1251 void
1252 netsnmp_udp6_ctor(void)
1253 {
1254     udp6Domain.name = netsnmp_UDPIPv6Domain;
1255     udp6Domain.name_length = sizeof(netsnmp_UDPIPv6Domain) / sizeof(oid);
1256     udp6Domain.f_create_from_tstring = netsnmp_udp6_create_tstring;
1257     udp6Domain.f_create_from_ostring = netsnmp_udp6_create_ostring;
1258     udp6Domain.prefix = calloc(5, sizeof(char *));
1259     udp6Domain.prefix[0] = "udp6";
1260     udp6Domain.prefix[1] = "ipv6";
1261     udp6Domain.prefix[2] = "udpv6";
1262     udp6Domain.prefix[3] = "udpipv6";
1263
1264     netsnmp_tdomain_register(&udp6Domain);
1265 }