and added files
[bcm963xx.git] / userapps / opensource / net-snmp / snmplib / snmpUDPDomain.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
32 #if HAVE_DMALLOC_H
33 #include <dmalloc.h>
34 #endif
35
36 #include <net-snmp/types.h>
37 #include <net-snmp/output_api.h>
38 #include <net-snmp/config_api.h>
39
40 #include <net-snmp/library/snmp_transport.h>
41 #include <net-snmp/library/snmpUDPDomain.h>
42
43 #ifndef INADDR_NONE
44 #define INADDR_NONE     -1
45 #endif
46
47 static netsnmp_tdomain udpDomain;
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_udp_fmtaddr(netsnmp_transport *t, void *data, int len)
56 {
57     struct sockaddr_in *to = NULL;
58
59     if (data != NULL && len == sizeof(struct sockaddr_in)) {
60         to = (struct sockaddr_in *) data;
61     } else if (t != NULL && t->data != NULL) {
62         to = (struct sockaddr_in *) t->data;
63     }
64     if (to == NULL) {
65         return strdup("UDP: unknown");
66     } else {
67         char tmp[64];
68
69         /*
70          * Here we just print the IP address of the peer for compatibility
71          * purposes.  It would be nice if we could include the port number and
72          * some indication of the domain (c.f. AAL5PVC).  
73          */
74
75         sprintf(tmp, "%s", inet_ntoa(to->sin_addr));
76         return strdup(tmp);
77     }
78 }
79
80
81
82 /*
83  * You can write something into opaque that will subsequently get passed back 
84  * to your send function if you like.  For instance, you might want to
85  * remember where a PDU came from, so that you can send a reply there...  
86  */
87
88 static int
89 netsnmp_udp_recv(netsnmp_transport *t, void *buf, int size,
90                  void **opaque, int *olength)
91 {
92     int             rc = -1;
93     socklen_t       fromlen = sizeof(struct sockaddr);
94     struct sockaddr *from;
95
96     if (t != NULL && t->sock >= 0) {
97         from = (struct sockaddr *) malloc(sizeof(struct sockaddr_in));
98         if (from == NULL) {
99             *opaque = NULL;
100             *olength = 0;
101             return -1;
102         } else {
103             memset(from, 0, fromlen);
104         }
105
106         while (rc < 0) {
107             rc = recvfrom(t->sock, buf, size, 0, from, &fromlen);
108             if (rc < 0 && errno != EINTR) {
109                 break;
110             }
111         }
112
113         if (rc >= 0) {
114             char *string = netsnmp_udp_fmtaddr(NULL, from, fromlen);
115             DEBUGMSGTL(("netsnmp_udp",
116                         "recvfrom fd %d got %d bytes (from %s)\n",
117                         t->sock, rc, string));
118             free(string);
119         } else {
120             DEBUGMSGTL(("netsnmp_udp", "recvfrom fd %d err %d (\"%s\")\n",
121                         t->sock, errno, strerror(errno)));
122         }
123         *opaque = (void *)from;
124         *olength = sizeof(struct sockaddr_in);
125     }
126     return rc;
127 }
128
129
130
131 static int
132 netsnmp_udp_send(netsnmp_transport *t, void *buf, int size,
133                  void **opaque, int *olength)
134 {
135     int rc = -1;
136     struct sockaddr *to = NULL;
137
138     if (opaque != NULL && *opaque != NULL &&
139         *olength == sizeof(struct sockaddr_in)) {
140         to = (struct sockaddr *) (*opaque);
141     } else if (t != NULL && t->data != NULL &&
142                t->data_length == sizeof(struct sockaddr_in)) {
143         to = (struct sockaddr *) (t->data);
144     }
145
146     if (to != NULL && t != NULL && t->sock >= 0) {
147         char *string = netsnmp_udp_fmtaddr(NULL, (void *) to,
148                                         sizeof(struct sockaddr_in));
149         DEBUGMSGTL(("netsnmp_udp", "send %d bytes from %p to %s on fd %d\n",
150                     size, buf, string, t->sock));
151         free(string);
152         while (rc < 0) {
153             rc = sendto(t->sock, buf, size, 0, to, sizeof(struct sockaddr));
154             if (rc < 0 && errno != EINTR) {
155                 break;
156             }
157         }
158     }
159     return rc;
160 }
161
162
163
164 static int
165 netsnmp_udp_close(netsnmp_transport *t)
166 {
167     int rc = -1;
168     if (t->sock >= 0) {
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-based transport for SNMP.  Local is TRUE if addr is the local
183  * address to bind to (i.e. this is a server-type session); otherwise addr is 
184  * the remote address to send things to.  
185  */
186
187 netsnmp_transport *
188 netsnmp_udp_transport(struct sockaddr_in *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->sin_family != AF_INET) {
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_udp_fmtaddr(NULL, (void *)addr, 
204                                  sizeof(struct sockaddr_in));
205     DEBUGMSGTL(("netsnmp_udp", "open %s %s\n", local ? "local" : "remote",
206                 string));
207     free(string);
208
209     memset(t, 0, sizeof(netsnmp_transport));
210
211     t->domain = netsnmpUDPDomain;
212     t->domain_length = netsnmpUDPDomain_len;
213
214     t->sock = socket(PF_INET, SOCK_DGRAM, 0);
215     if (t->sock < 0) {
216         netsnmp_transport_free(t);
217         return NULL;
218     }
219 #ifdef  SO_BSDCOMPAT
220     /*
221      * Patch for Linux.  Without this, UDP packets that fail get an ICMP
222      * response.  Linux turns the failed ICMP response into an error message
223      * and return value, unlike all other OS's.  
224      */
225     {
226         int             one = 1;
227         setsockopt(t->sock, SOL_SOCKET, SO_BSDCOMPAT, (void *) &one,
228                    sizeof(one));
229     }
230 #endif                          /*SO_BSDCOMPAT */
231     /*
232      * SO_REUSEADDR will allow multiple apps to open the same port at
233      * the same time. Only the last one to open the socket will get
234      * data. Obviously, for an agent, this is a bad thing. There should
235      * only be one listener.
236      */
237 #ifdef ALLOW_PORT_HIJACKING
238 #ifdef  SO_REUSEADDR
239     /*
240      * Allow the same port to be specified multiple times without failing.
241      *    (useful for a listener)
242      */
243     {
244         int             one = 1;
245         setsockopt(t->sock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
246                    sizeof(one));
247     }
248 #endif                          /*SO_REUSEADDR */
249 #endif
250     /*
251      * Try to set the send and receive buffers to a reasonably large value, so
252      * that we can send and receive big PDUs (defaults to 8192 bytes (!) on
253      * Solaris, for instance).  Don't worry too much about errors -- just
254      * plough on regardless.  
255      */
256
257 #ifdef  SO_SNDBUF
258     if (setsockopt
259         (t->sock, SOL_SOCKET, SO_SNDBUF, (void *) &udpbuf,
260          sizeof(int)) != 0) {
261         DEBUGMSGTL(("netsnmp_udp", "couldn't set SO_SNDBUF to %d bytes: %s\n",
262                     udpbuf, strerror(errno)));
263     }
264 #endif                          /*SO_SNDBUF */
265
266 #ifdef  SO_RCVBUF
267     if (setsockopt
268         (t->sock, SOL_SOCKET, SO_RCVBUF, (void *) &udpbuf,
269          sizeof(int)) != 0) {
270         DEBUGMSGTL(("netsnmp_udp", "couldn't set SO_RCVBUF to %d bytes: %s\n",
271                     udpbuf, strerror(errno)));
272     }
273 #endif                          /*SO_RCVBUF */
274
275     if (local) {
276         /*
277          * This session is inteneded as a server, so we must bind on to the
278          * given IP address, which may include an interface address, or could
279          * be INADDR_ANY, but certainly includes a port number.
280          */
281
282         t->local = malloc(6);
283         if (t->local == NULL) {
284             netsnmp_transport_free(t);
285             return NULL;
286         }
287         memcpy(t->local, (u_char *) & (addr->sin_addr.s_addr), 4);
288         t->local[4] = (htons(addr->sin_port) & 0xff00) >> 8;
289         t->local[5] = (htons(addr->sin_port) & 0x00ff) >> 0;
290         t->local_length = 6;
291
292         rc = bind(t->sock, (struct sockaddr *) addr,
293                   sizeof(struct sockaddr));
294         if (rc != 0) {
295             netsnmp_udp_close(t);
296             netsnmp_transport_free(t);
297             return NULL;
298         }
299         t->data = NULL;
300         t->data_length = 0;
301     } else {
302         /*
303          * This is a client session.  Save the address in the
304          * transport-specific data pointer for later use by netsnmp_udp_send.
305          */
306
307         t->data = malloc(sizeof(struct sockaddr_in));
308         t->remote = malloc(6);
309         if (t->data == NULL || t->remote == NULL) {
310             netsnmp_transport_free(t);
311             return NULL;
312         }
313         memcpy(t->remote, (u_char *) & (addr->sin_addr.s_addr), 4);
314         t->remote[4] = (htons(addr->sin_port) & 0xff00) >> 8;
315         t->remote[5] = (htons(addr->sin_port) & 0x00ff) >> 0;
316         t->remote_length = 6;
317         memcpy(t->data, addr, sizeof(struct sockaddr_in));
318         t->data_length = sizeof(struct sockaddr_in);
319     }
320
321     /*
322      * 16-bit length field, 8 byte UDP header, 20 byte IPv4 header  
323      */
324
325     t->msgMaxSize = 0xffff - 8 - 20;
326     t->f_recv     = netsnmp_udp_recv;
327     t->f_send     = netsnmp_udp_send;
328     t->f_close    = netsnmp_udp_close;
329     t->f_accept   = NULL;
330     t->f_fmtaddr  = netsnmp_udp_fmtaddr;
331
332     return t;
333 }
334
335
336
337 int
338 netsnmp_sockaddr_in(struct sockaddr_in *addr,
339                     const char *inpeername, int remote_port)
340 {
341     char           *cp = NULL, *peername = NULL;
342
343     if (addr == NULL) {
344         return 0;
345     }
346     memset(addr, 0, sizeof(struct sockaddr_in));
347
348     DEBUGMSGTL(("netsnmp_sockaddr_in", "addr %p, peername \"%s\"\n",
349                 addr, inpeername ? inpeername : "[NIL]"));
350
351     addr->sin_addr.s_addr = htonl(INADDR_ANY);
352     addr->sin_family = AF_INET;
353     if (remote_port > 0) {
354         addr->sin_port = htons(remote_port);
355     } else if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
356                                   NETSNMP_DS_LIB_DEFAULT_PORT) > 0) {
357         addr->sin_port = htons(netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
358                                                  NETSNMP_DS_LIB_DEFAULT_PORT));
359     } else {
360         addr->sin_port = htons(SNMP_PORT);
361     }
362
363     if (inpeername != NULL) {
364         /*
365          * Duplicate the peername because we might want to mank around with
366          * it.  
367          */
368
369         peername = strdup(inpeername);
370         if (peername == NULL) {
371             return 0;
372         }
373
374         /*
375          * Try and extract an appended port number.  
376          */
377         cp = strchr(peername, ':');
378         if (cp != NULL) {
379             *cp = '\0';
380             cp++;
381             if (atoi(cp) != 0) {
382                 DEBUGMSGTL(("netsnmp_sockaddr_in",
383                             "port number suffix :%d\n", atoi(cp)));
384                 addr->sin_port = htons(atoi(cp));
385             }
386         }
387
388         for (cp = peername; *cp && isdigit((int) *cp); cp++);
389         if (!*cp && atoi(peername) != 0) {
390             /*
391              * Okay, it looks like just a port number.  
392              */
393             DEBUGMSGTL(("netsnmp_sockaddr_in", "totally numeric: %d\n",
394                         atoi(peername)));
395             addr->sin_port = htons(atoi(peername));
396         } else if (inet_addr(peername) != INADDR_NONE) {
397             /*
398              * It looks like an IP address.  
399              */
400             DEBUGMSGTL(("netsnmp_sockaddr_in", "IP address\n"));
401             addr->sin_addr.s_addr = inet_addr(peername);
402         } else {
403             /*
404              * Well, it must be a hostname then.  
405              */
406 #ifdef  HAVE_GETHOSTBYNAME
407             struct hostent *hp = gethostbyname(peername);
408             if (hp == NULL) {
409                 DEBUGMSGTL(("netsnmp_sockaddr_in",
410                             "hostname (couldn't resolve)\n"));
411                 free(peername);
412                 return 0;
413             } else {
414                 if (hp->h_addrtype != AF_INET) {
415                     DEBUGMSGTL(("netsnmp_sockaddr_in",
416                                 "hostname (not AF_INET!)\n"));
417                     free(peername);
418                     return 0;
419                 } else {
420                     DEBUGMSGTL(("netsnmp_sockaddr_in",
421                                 "hostname (resolved okay)\n"));
422                     memcpy(&(addr->sin_addr), hp->h_addr, hp->h_length);
423                 }
424             }
425 #else                           /*HAVE_GETHOSTBYNAME */
426             DEBUGMSGTL(("netsnmp_sockaddr_in",
427                         "hostname (no gethostbyname)\n"));
428             free(peername);
429             return 0;
430 #endif                          /*HAVE_GETHOSTBYNAME */
431         }
432     } else {
433         DEBUGMSGTL(("netsnmp_sockaddr_in", "NULL peername"));
434         return 0;
435     }
436     DEBUGMSGTL(("netsnmp_sockaddr_in", "return { AF_INET, %s:%hu }\n",
437                 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)));
438     free(peername);
439     return 1;
440 }
441
442 /*
443  * The following functions provide the "com2sec" configuration token
444  * functionality for compatibility.  
445  */
446
447 #define EXAMPLE_NETWORK         "NETWORK"
448 #define EXAMPLE_COMMUNITY       "COMMUNITY"
449
450 typedef struct _com2SecEntry {
451     char            community[VACMSTRINGLEN];
452     unsigned long   network;
453     unsigned long   mask;
454     char            secName[VACMSTRINGLEN];
455     struct _com2SecEntry *next;
456 } com2SecEntry;
457
458 com2SecEntry   *com2SecList = NULL, *com2SecListLast = NULL;
459
460 void
461 netsnmp_udp_parse_security(const char *token, char *param)
462 {
463     char           *secName = NULL, *community = NULL, *source = NULL;
464     char           *cp = NULL;
465     const char     *strmask = NULL;
466     com2SecEntry   *e = NULL;
467     unsigned long   network = 0, mask = 0;
468
469     /*
470      * Get security, source address/netmask and community strings.  
471      */
472
473     secName = strtok(param, "\t\n ");
474     if (secName == NULL) {
475         config_perror("missing NAME parameter");
476         return;
477     } else if (strlen(secName) > (VACMSTRINGLEN - 1)) {
478         config_perror("security name too long");
479         return;
480     }
481     source = strtok(NULL, "\t\n ");
482     if (source == NULL) {
483         config_perror("missing SOURCE parameter");
484         return;
485     } else if (strncmp(source, EXAMPLE_NETWORK, strlen(EXAMPLE_NETWORK)) ==
486                0) {
487         config_perror("example config NETWORK not properly configured");
488         return;
489     }
490     community = strtok(NULL, "\t\n ");
491     if (community == NULL) {
492         config_perror("missing COMMUNITY parameter\n");
493         return;
494     } else
495         if (strncmp
496             (community, EXAMPLE_COMMUNITY, strlen(EXAMPLE_COMMUNITY))
497             == 0) {
498         config_perror("example config COMMUNITY not properly configured");
499         return;
500     } else if (strlen(community) > (VACMSTRINGLEN - 1)) {
501         config_perror("community name too long");
502         return;
503     }
504
505     /*
506      * Process the source address/netmask string.  
507      */
508
509     cp = strchr(source, '/');
510     if (cp != NULL) {
511         /*
512          * Mask given.  
513          */
514         *cp = '\0';
515         strmask = cp + 1;
516     }
517
518     /*
519      * Deal with the network part first.  
520      */
521
522     if ((strcmp(source, "default") == 0)
523         || (strcmp(source, "0.0.0.0") == 0)) {
524         network = 0;
525         strmask = "0.0.0.0";
526     } else {
527         /*
528          * Try interpreting as a dotted quad.  
529          */
530         network = inet_addr(source);
531
532         if (network == (unsigned long) -1) {
533             /*
534              * Nope, wasn't a dotted quad.  Must be a hostname.  
535              */
536 #ifdef  HAVE_GETHOSTBYNAME
537             struct hostent *hp = gethostbyname(source);
538             if (hp == NULL) {
539                 config_perror("bad source address");
540                 return;
541             } else {
542                 if (hp->h_addrtype != AF_INET) {
543                     config_perror("no IP address for source hostname");
544                     return;
545                 }
546                 network = *((unsigned long *) hp->h_addr);
547             }
548 #else                           /*HAVE_GETHOSTBYNAME */
549             /*
550              * Oh dear.  
551              */
552             config_perror("cannot resolve source hostname");
553             return;
554 #endif                          /*HAVE_GETHOSTBYNAME */
555         }
556     }
557
558     /*
559      * Now work out the mask.  
560      */
561
562     if (strmask == NULL || *strmask == '\0') {
563         /*
564          * No mask was given.  Use 255.255.255.255.  
565          */
566         mask = 0xffffffffL;
567     } else {
568         if (strchr(strmask, '.')) {
569             /*
570              * Try to interpret mask as a dotted quad.  
571              */
572             mask = inet_addr(strmask);
573             if (mask == (unsigned long) -1 &&
574                 strncmp(strmask, "255.255.255.255", 15) != 0) {
575                 config_perror("bad mask");
576                 return;
577             }
578         } else {
579             /*
580              * Try to interpret mask as a "number of 1 bits".  
581              */
582             int             maskLen = atoi(strmask), maskBit = 0x80000000L;
583             if (maskLen <= 0 || maskLen > 32) {
584                 config_perror("bad mask length");
585                 return;
586             }
587             while (maskLen--) {
588                 mask |= maskBit;
589                 maskBit >>= 1;
590             }
591             mask = htonl(mask);
592         }
593     }
594
595     /*
596      * Check that the network and mask are consistent.  
597      */
598
599     if (network & ~mask) {
600         config_perror("source/mask mismatch");
601         return;
602     }
603
604     e = (com2SecEntry *) malloc(sizeof(com2SecEntry));
605     if (e == NULL) {
606         config_perror("memory error");
607         return;
608     }
609
610     /*
611      * Everything is okay.  Copy the parameters to the structure allocated
612      * above and add it to END of the list.  
613      */
614
615     DEBUGMSGTL(("netsnmp_udp_parse_security",
616                 "<\"%s\", 0x%08x/0x%08x> => \"%s\"\n", community, network,
617                 mask, secName));
618
619     strcpy(e->secName, secName);
620     strcpy(e->community, community);
621     e->network = network;
622     e->mask = mask;
623     e->next = NULL;
624
625     if (com2SecListLast != NULL) {
626         com2SecListLast->next = e;
627         com2SecListLast = e;
628     } else {
629         com2SecListLast = com2SecList = e;
630     }
631 }
632
633
634 void
635 netsnmp_udp_com2SecList_free(void)
636 {
637     com2SecEntry   *e = com2SecList;
638     while (e != NULL) {
639         com2SecEntry   *tmp = e;
640         e = e->next;
641         free(tmp);
642     }
643     com2SecList = com2SecListLast = NULL;
644 }
645
646
647 void
648 netsnmp_udp_agent_config_tokens_register(void)
649 {
650     register_app_config_handler("com2sec", netsnmp_udp_parse_security,
651                                 netsnmp_udp_com2SecList_free,
652                                 "name IPv4-network-address[/netmask] community");
653 }
654
655
656
657 /*
658  * Return 0 if there are no com2sec entries, or return 1 if there ARE com2sec 
659  * entries.  On return, if a com2sec entry matched the passed parameters,
660  * then *secName points at the appropriate security name, or is NULL if the
661  * parameters did not match any com2sec entry.  
662  */
663
664 int
665 netsnmp_udp_getSecName(void *opaque, int olength,
666                        const char *community,
667                        size_t community_len, char **secName)
668 {
669     com2SecEntry   *c;
670     struct sockaddr_in *from = (struct sockaddr_in *) opaque;
671     char           *ztcommunity = NULL;
672
673     /*
674      * Special case if there are NO entries (as opposed to no MATCHING
675      * entries).  
676      */
677
678     if (com2SecList == NULL) {
679         DEBUGMSGTL(("netsnmp_udp_getSecName", "no com2sec entries\n"));
680         if (secName != NULL) {
681             *secName = NULL;
682         }
683         return 0;
684     }
685
686     /*
687      * If there is no IPv4 source address, then there can be no valid security
688      * name.  
689      */
690
691     if (opaque == NULL || olength != sizeof(struct sockaddr_in) ||
692         from->sin_family != AF_INET) {
693         DEBUGMSGTL(("netsnmp_udp_getSecName",
694                     "no IPv4 source address in PDU?\n"));
695         if (secName != NULL) {
696             *secName = NULL;
697         }
698         return 1;
699     }
700
701     DEBUGIF("netsnmp_udp_getSecName") {
702         ztcommunity = (char *)malloc(community_len + 1);
703         if (ztcommunity != NULL) {
704             memcpy(ztcommunity, community, community_len);
705             ztcommunity[community_len] = '\0';
706         }
707
708         DEBUGMSGTL(("netsnmp_udp_getSecName", "resolve <\"%s\", 0x%08x>\n",
709                     ztcommunity ? ztcommunity : "<malloc error>",
710                     from->sin_addr.s_addr));
711     }
712     for (c = com2SecList; c != NULL; c = c->next) {
713         DEBUGMSGTL(("netsnmp_udp_getSecName","compare <\"%s\", 0x%08x/0x%08x>",
714                     c->community, c->network, c->mask));
715         if ((community_len == strlen(c->community)) &&
716             (memcmp(community, c->community, community_len) == 0) &&
717             ((from->sin_addr.s_addr & c->mask) == c->network)) {
718             DEBUGMSG(("netsnmp_udp_getSecName", "... SUCCESS\n"));
719             if (secName != NULL) {
720                 *secName = c->secName;
721             }
722             break;
723         }
724         DEBUGMSG(("netsnmp_udp_getSecName", "... nope\n"));
725     }
726     if (ztcommunity != NULL) {
727         free(ztcommunity);
728     }
729     return 1;
730 }
731
732
733 netsnmp_transport *
734 netsnmp_udp_create_tstring(const char *string, int local)
735 {
736     struct sockaddr_in addr;
737
738     if (netsnmp_sockaddr_in(&addr, string, 0)) {
739         return netsnmp_udp_transport(&addr, local);
740     } else {
741         return NULL;
742     }
743 }
744
745
746 netsnmp_transport *
747 netsnmp_udp_create_ostring(const u_char * o, size_t o_len, int local)
748 {
749     struct sockaddr_in addr;
750
751     if (o_len == 6) {
752         addr.sin_family = AF_INET;
753         memcpy((u_char *) & (addr.sin_addr.s_addr), o, 4);
754         addr.sin_port = ntohs((o[4] << 8) + o[5]);
755         return netsnmp_udp_transport(&addr, local);
756     }
757     return NULL;
758 }
759
760
761 void
762 netsnmp_udp_ctor(void)
763 {
764     udpDomain.name = netsnmpUDPDomain;
765     udpDomain.name_length = netsnmpUDPDomain_len;
766     udpDomain.prefix = calloc(2, sizeof(char *));
767     udpDomain.prefix[0] = "udp";
768
769     udpDomain.f_create_from_tstring = netsnmp_udp_create_tstring;
770     udpDomain.f_create_from_ostring = netsnmp_udp_create_ostring;
771
772     netsnmp_tdomain_register(&udpDomain);
773 }