1 #include <net-snmp/net-snmp-config.h>
20 #include <sys/socket.h>
23 #include <netinet/in.h>
26 #include <arpa/inet.h>
36 #include <net-snmp/types.h>
37 #include <net-snmp/output_api.h>
38 #include <net-snmp/config_api.h>
40 #include <net-snmp/library/snmp_transport.h>
41 #include <net-snmp/library/snmpUDPDomain.h>
44 #define INADDR_NONE -1
47 static netsnmp_tdomain udpDomain;
50 * Return a string representing the address in data, or else the "far end"
51 * address if data is NULL.
55 netsnmp_udp_fmtaddr(netsnmp_transport *t, void *data, int len)
57 struct sockaddr_in *to = NULL;
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;
65 return strdup("UDP: unknown");
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).
75 sprintf(tmp, "%s", inet_ntoa(to->sin_addr));
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...
89 netsnmp_udp_recv(netsnmp_transport *t, void *buf, int size,
90 void **opaque, int *olength)
93 socklen_t fromlen = sizeof(struct sockaddr);
94 struct sockaddr *from;
96 if (t != NULL && t->sock >= 0) {
97 from = (struct sockaddr *) malloc(sizeof(struct sockaddr_in));
103 memset(from, 0, fromlen);
107 rc = recvfrom(t->sock, buf, size, 0, from, &fromlen);
108 if (rc < 0 && errno != EINTR) {
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));
120 DEBUGMSGTL(("netsnmp_udp", "recvfrom fd %d err %d (\"%s\")\n",
121 t->sock, errno, strerror(errno)));
123 *opaque = (void *)from;
124 *olength = sizeof(struct sockaddr_in);
132 netsnmp_udp_send(netsnmp_transport *t, void *buf, int size,
133 void **opaque, int *olength)
136 struct sockaddr *to = NULL;
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);
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));
153 rc = sendto(t->sock, buf, size, 0, to, sizeof(struct sockaddr));
154 if (rc < 0 && errno != EINTR) {
165 netsnmp_udp_close(netsnmp_transport *t)
169 #ifndef HAVE_CLOSESOCKET
172 rc = closesocket(t->sock);
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.
188 netsnmp_udp_transport(struct sockaddr_in *addr, int local)
190 netsnmp_transport *t = NULL;
191 int rc = 0, udpbuf = (1 << 17);
194 if (addr == NULL || addr->sin_family != AF_INET) {
198 t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport));
203 string = netsnmp_udp_fmtaddr(NULL, (void *)addr,
204 sizeof(struct sockaddr_in));
205 DEBUGMSGTL(("netsnmp_udp", "open %s %s\n", local ? "local" : "remote",
209 memset(t, 0, sizeof(netsnmp_transport));
211 t->domain = netsnmpUDPDomain;
212 t->domain_length = netsnmpUDPDomain_len;
214 t->sock = socket(PF_INET, SOCK_DGRAM, 0);
216 netsnmp_transport_free(t);
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.
227 setsockopt(t->sock, SOL_SOCKET, SO_BSDCOMPAT, (void *) &one,
230 #endif /*SO_BSDCOMPAT */
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.
237 #ifdef ALLOW_PORT_HIJACKING
240 * Allow the same port to be specified multiple times without failing.
241 * (useful for a listener)
245 setsockopt(t->sock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
248 #endif /*SO_REUSEADDR */
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.
259 (t->sock, SOL_SOCKET, SO_SNDBUF, (void *) &udpbuf,
261 DEBUGMSGTL(("netsnmp_udp", "couldn't set SO_SNDBUF to %d bytes: %s\n",
262 udpbuf, strerror(errno)));
264 #endif /*SO_SNDBUF */
268 (t->sock, SOL_SOCKET, SO_RCVBUF, (void *) &udpbuf,
270 DEBUGMSGTL(("netsnmp_udp", "couldn't set SO_RCVBUF to %d bytes: %s\n",
271 udpbuf, strerror(errno)));
273 #endif /*SO_RCVBUF */
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.
282 t->local = malloc(6);
283 if (t->local == NULL) {
284 netsnmp_transport_free(t);
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;
292 rc = bind(t->sock, (struct sockaddr *) addr,
293 sizeof(struct sockaddr));
295 netsnmp_udp_close(t);
296 netsnmp_transport_free(t);
303 * This is a client session. Save the address in the
304 * transport-specific data pointer for later use by netsnmp_udp_send.
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);
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);
322 * 16-bit length field, 8 byte UDP header, 20 byte IPv4 header
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;
330 t->f_fmtaddr = netsnmp_udp_fmtaddr;
338 netsnmp_sockaddr_in(struct sockaddr_in *addr,
339 const char *inpeername, int remote_port)
341 char *cp = NULL, *peername = NULL;
346 memset(addr, 0, sizeof(struct sockaddr_in));
348 DEBUGMSGTL(("netsnmp_sockaddr_in", "addr %p, peername \"%s\"\n",
349 addr, inpeername ? inpeername : "[NIL]"));
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));
360 addr->sin_port = htons(SNMP_PORT);
363 if (inpeername != NULL) {
365 * Duplicate the peername because we might want to mank around with
369 peername = strdup(inpeername);
370 if (peername == NULL) {
375 * Try and extract an appended port number.
377 cp = strchr(peername, ':');
382 DEBUGMSGTL(("netsnmp_sockaddr_in",
383 "port number suffix :%d\n", atoi(cp)));
384 addr->sin_port = htons(atoi(cp));
388 for (cp = peername; *cp && isdigit((int) *cp); cp++);
389 if (!*cp && atoi(peername) != 0) {
391 * Okay, it looks like just a port number.
393 DEBUGMSGTL(("netsnmp_sockaddr_in", "totally numeric: %d\n",
395 addr->sin_port = htons(atoi(peername));
396 } else if (inet_addr(peername) != INADDR_NONE) {
398 * It looks like an IP address.
400 DEBUGMSGTL(("netsnmp_sockaddr_in", "IP address\n"));
401 addr->sin_addr.s_addr = inet_addr(peername);
404 * Well, it must be a hostname then.
406 #ifdef HAVE_GETHOSTBYNAME
407 struct hostent *hp = gethostbyname(peername);
409 DEBUGMSGTL(("netsnmp_sockaddr_in",
410 "hostname (couldn't resolve)\n"));
414 if (hp->h_addrtype != AF_INET) {
415 DEBUGMSGTL(("netsnmp_sockaddr_in",
416 "hostname (not AF_INET!)\n"));
420 DEBUGMSGTL(("netsnmp_sockaddr_in",
421 "hostname (resolved okay)\n"));
422 memcpy(&(addr->sin_addr), hp->h_addr, hp->h_length);
425 #else /*HAVE_GETHOSTBYNAME */
426 DEBUGMSGTL(("netsnmp_sockaddr_in",
427 "hostname (no gethostbyname)\n"));
430 #endif /*HAVE_GETHOSTBYNAME */
433 DEBUGMSGTL(("netsnmp_sockaddr_in", "NULL peername"));
436 DEBUGMSGTL(("netsnmp_sockaddr_in", "return { AF_INET, %s:%hu }\n",
437 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)));
443 * The following functions provide the "com2sec" configuration token
444 * functionality for compatibility.
447 #define EXAMPLE_NETWORK "NETWORK"
448 #define EXAMPLE_COMMUNITY "COMMUNITY"
450 typedef struct _com2SecEntry {
451 char community[VACMSTRINGLEN];
452 unsigned long network;
454 char secName[VACMSTRINGLEN];
455 struct _com2SecEntry *next;
458 com2SecEntry *com2SecList = NULL, *com2SecListLast = NULL;
461 netsnmp_udp_parse_security(const char *token, char *param)
463 char *secName = NULL, *community = NULL, *source = NULL;
465 const char *strmask = NULL;
466 com2SecEntry *e = NULL;
467 unsigned long network = 0, mask = 0;
470 * Get security, source address/netmask and community strings.
473 secName = strtok(param, "\t\n ");
474 if (secName == NULL) {
475 config_perror("missing NAME parameter");
477 } else if (strlen(secName) > (VACMSTRINGLEN - 1)) {
478 config_perror("security name too long");
481 source = strtok(NULL, "\t\n ");
482 if (source == NULL) {
483 config_perror("missing SOURCE parameter");
485 } else if (strncmp(source, EXAMPLE_NETWORK, strlen(EXAMPLE_NETWORK)) ==
487 config_perror("example config NETWORK not properly configured");
490 community = strtok(NULL, "\t\n ");
491 if (community == NULL) {
492 config_perror("missing COMMUNITY parameter\n");
496 (community, EXAMPLE_COMMUNITY, strlen(EXAMPLE_COMMUNITY))
498 config_perror("example config COMMUNITY not properly configured");
500 } else if (strlen(community) > (VACMSTRINGLEN - 1)) {
501 config_perror("community name too long");
506 * Process the source address/netmask string.
509 cp = strchr(source, '/');
519 * Deal with the network part first.
522 if ((strcmp(source, "default") == 0)
523 || (strcmp(source, "0.0.0.0") == 0)) {
528 * Try interpreting as a dotted quad.
530 network = inet_addr(source);
532 if (network == (unsigned long) -1) {
534 * Nope, wasn't a dotted quad. Must be a hostname.
536 #ifdef HAVE_GETHOSTBYNAME
537 struct hostent *hp = gethostbyname(source);
539 config_perror("bad source address");
542 if (hp->h_addrtype != AF_INET) {
543 config_perror("no IP address for source hostname");
546 network = *((unsigned long *) hp->h_addr);
548 #else /*HAVE_GETHOSTBYNAME */
552 config_perror("cannot resolve source hostname");
554 #endif /*HAVE_GETHOSTBYNAME */
559 * Now work out the mask.
562 if (strmask == NULL || *strmask == '\0') {
564 * No mask was given. Use 255.255.255.255.
568 if (strchr(strmask, '.')) {
570 * Try to interpret mask as a dotted quad.
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");
580 * Try to interpret mask as a "number of 1 bits".
582 int maskLen = atoi(strmask), maskBit = 0x80000000L;
583 if (maskLen <= 0 || maskLen > 32) {
584 config_perror("bad mask length");
596 * Check that the network and mask are consistent.
599 if (network & ~mask) {
600 config_perror("source/mask mismatch");
604 e = (com2SecEntry *) malloc(sizeof(com2SecEntry));
606 config_perror("memory error");
611 * Everything is okay. Copy the parameters to the structure allocated
612 * above and add it to END of the list.
615 DEBUGMSGTL(("netsnmp_udp_parse_security",
616 "<\"%s\", 0x%08x/0x%08x> => \"%s\"\n", community, network,
619 strcpy(e->secName, secName);
620 strcpy(e->community, community);
621 e->network = network;
625 if (com2SecListLast != NULL) {
626 com2SecListLast->next = e;
629 com2SecListLast = com2SecList = e;
635 netsnmp_udp_com2SecList_free(void)
637 com2SecEntry *e = com2SecList;
639 com2SecEntry *tmp = e;
643 com2SecList = com2SecListLast = NULL;
648 netsnmp_udp_agent_config_tokens_register(void)
650 register_app_config_handler("com2sec", netsnmp_udp_parse_security,
651 netsnmp_udp_com2SecList_free,
652 "name IPv4-network-address[/netmask] community");
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.
665 netsnmp_udp_getSecName(void *opaque, int olength,
666 const char *community,
667 size_t community_len, char **secName)
670 struct sockaddr_in *from = (struct sockaddr_in *) opaque;
671 char *ztcommunity = NULL;
674 * Special case if there are NO entries (as opposed to no MATCHING
678 if (com2SecList == NULL) {
679 DEBUGMSGTL(("netsnmp_udp_getSecName", "no com2sec entries\n"));
680 if (secName != NULL) {
687 * If there is no IPv4 source address, then there can be no valid security
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) {
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';
708 DEBUGMSGTL(("netsnmp_udp_getSecName", "resolve <\"%s\", 0x%08x>\n",
709 ztcommunity ? ztcommunity : "<malloc error>",
710 from->sin_addr.s_addr));
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;
724 DEBUGMSG(("netsnmp_udp_getSecName", "... nope\n"));
726 if (ztcommunity != NULL) {
734 netsnmp_udp_create_tstring(const char *string, int local)
736 struct sockaddr_in addr;
738 if (netsnmp_sockaddr_in(&addr, string, 0)) {
739 return netsnmp_udp_transport(&addr, local);
747 netsnmp_udp_create_ostring(const u_char * o, size_t o_len, int local)
749 struct sockaddr_in addr;
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);
762 netsnmp_udp_ctor(void)
764 udpDomain.name = netsnmpUDPDomain;
765 udpDomain.name_length = netsnmpUDPDomain_len;
766 udpDomain.prefix = calloc(2, sizeof(char *));
767 udpDomain.prefix[0] = "udp";
769 udpDomain.f_create_from_tstring = netsnmp_udp_create_tstring;
770 udpDomain.f_create_from_ostring = netsnmp_udp_create_ostring;
772 netsnmp_tdomain_register(&udpDomain);