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.
225 if (0 == netsnmp_os_prematch("Linux","2.4"))
228 setsockopt(t->sock, SOL_SOCKET, SO_BSDCOMPAT, (void *) &one,
231 #endif /*SO_BSDCOMPAT */
233 * SO_REUSEADDR will allow multiple apps to open the same port at
234 * the same time. Only the last one to open the socket will get
235 * data. Obviously, for an agent, this is a bad thing. There should
236 * only be one listener.
238 #ifdef ALLOW_PORT_HIJACKING
241 * Allow the same port to be specified multiple times without failing.
242 * (useful for a listener)
246 setsockopt(t->sock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
249 #endif /*SO_REUSEADDR */
252 * Try to set the send and receive buffers to a reasonably large value, so
253 * that we can send and receive big PDUs (defaults to 8192 bytes (!) on
254 * Solaris, for instance). Don't worry too much about errors -- just
255 * plough on regardless.
260 (t->sock, SOL_SOCKET, SO_SNDBUF, (void *) &udpbuf,
262 DEBUGMSGTL(("netsnmp_udp", "couldn't set SO_SNDBUF to %d bytes: %s\n",
263 udpbuf, strerror(errno)));
265 #endif /*SO_SNDBUF */
269 (t->sock, SOL_SOCKET, SO_RCVBUF, (void *) &udpbuf,
271 DEBUGMSGTL(("netsnmp_udp", "couldn't set SO_RCVBUF to %d bytes: %s\n",
272 udpbuf, strerror(errno)));
274 #endif /*SO_RCVBUF */
278 * This session is inteneded as a server, so we must bind on to the
279 * given IP address, which may include an interface address, or could
280 * be INADDR_ANY, but certainly includes a port number.
283 t->local = malloc(6);
284 if (t->local == NULL) {
285 netsnmp_transport_free(t);
288 memcpy(t->local, (u_char *) & (addr->sin_addr.s_addr), 4);
289 t->local[4] = (htons(addr->sin_port) & 0xff00) >> 8;
290 t->local[5] = (htons(addr->sin_port) & 0x00ff) >> 0;
293 rc = bind(t->sock, (struct sockaddr *) addr,
294 sizeof(struct sockaddr));
296 netsnmp_udp_close(t);
297 netsnmp_transport_free(t);
304 * This is a client session. Save the address in the
305 * transport-specific data pointer for later use by netsnmp_udp_send.
308 t->data = malloc(sizeof(struct sockaddr_in));
309 t->remote = malloc(6);
310 if (t->data == NULL || t->remote == NULL) {
311 netsnmp_transport_free(t);
314 memcpy(t->remote, (u_char *) & (addr->sin_addr.s_addr), 4);
315 t->remote[4] = (htons(addr->sin_port) & 0xff00) >> 8;
316 t->remote[5] = (htons(addr->sin_port) & 0x00ff) >> 0;
317 t->remote_length = 6;
318 memcpy(t->data, addr, sizeof(struct sockaddr_in));
319 t->data_length = sizeof(struct sockaddr_in);
323 * 16-bit length field, 8 byte UDP header, 20 byte IPv4 header
326 t->msgMaxSize = 0xffff - 8 - 20;
327 t->f_recv = netsnmp_udp_recv;
328 t->f_send = netsnmp_udp_send;
329 t->f_close = netsnmp_udp_close;
331 t->f_fmtaddr = netsnmp_udp_fmtaddr;
339 netsnmp_sockaddr_in(struct sockaddr_in *addr,
340 const char *inpeername, int remote_port)
342 char *cp = NULL, *peername = NULL;
347 memset(addr, 0, sizeof(struct sockaddr_in));
349 DEBUGMSGTL(("netsnmp_sockaddr_in", "addr %p, peername \"%s\"\n",
350 addr, inpeername ? inpeername : "[NIL]"));
352 addr->sin_addr.s_addr = htonl(INADDR_ANY);
353 addr->sin_family = AF_INET;
354 if (remote_port > 0) {
355 addr->sin_port = htons(remote_port);
356 } else if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
357 NETSNMP_DS_LIB_DEFAULT_PORT) > 0) {
358 addr->sin_port = htons(netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
359 NETSNMP_DS_LIB_DEFAULT_PORT));
361 addr->sin_port = htons(SNMP_PORT);
364 if (inpeername != NULL) {
366 * Duplicate the peername because we might want to mank around with
370 peername = strdup(inpeername);
371 if (peername == NULL) {
376 * Try and extract an appended port number.
378 cp = strchr(peername, ':');
383 DEBUGMSGTL(("netsnmp_sockaddr_in",
384 "port number suffix :%d\n", atoi(cp)));
385 addr->sin_port = htons(atoi(cp));
389 for (cp = peername; *cp && isdigit((int) *cp); cp++);
390 if (!*cp && atoi(peername) != 0) {
392 * Okay, it looks like just a port number.
394 DEBUGMSGTL(("netsnmp_sockaddr_in", "totally numeric: %d\n",
396 addr->sin_port = htons(atoi(peername));
397 } else if (inet_addr(peername) != INADDR_NONE) {
399 * It looks like an IP address.
401 DEBUGMSGTL(("netsnmp_sockaddr_in", "IP address\n"));
402 addr->sin_addr.s_addr = inet_addr(peername);
405 * Well, it must be a hostname then.
407 #ifdef HAVE_GETHOSTBYNAME
408 struct hostent *hp = gethostbyname(peername);
410 DEBUGMSGTL(("netsnmp_sockaddr_in",
411 "hostname (couldn't resolve)\n"));
415 if (hp->h_addrtype != AF_INET) {
416 DEBUGMSGTL(("netsnmp_sockaddr_in",
417 "hostname (not AF_INET!)\n"));
421 DEBUGMSGTL(("netsnmp_sockaddr_in",
422 "hostname (resolved okay)\n"));
423 memcpy(&(addr->sin_addr), hp->h_addr, hp->h_length);
426 #else /*HAVE_GETHOSTBYNAME */
427 DEBUGMSGTL(("netsnmp_sockaddr_in",
428 "hostname (no gethostbyname)\n"));
431 #endif /*HAVE_GETHOSTBYNAME */
434 DEBUGMSGTL(("netsnmp_sockaddr_in", "NULL peername"));
437 DEBUGMSGTL(("netsnmp_sockaddr_in", "return { AF_INET, %s:%hu }\n",
438 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)));
444 * The following functions provide the "com2sec" configuration token
445 * functionality for compatibility.
448 #define EXAMPLE_NETWORK "NETWORK"
449 #define EXAMPLE_COMMUNITY "COMMUNITY"
451 typedef struct _com2SecEntry {
452 char community[VACMSTRINGLEN];
453 unsigned long network;
455 char secName[VACMSTRINGLEN];
456 struct _com2SecEntry *next;
459 com2SecEntry *com2SecList = NULL, *com2SecListLast = NULL;
462 netsnmp_udp_parse_security(const char *token, char *param)
464 char *secName = NULL, *community = NULL, *source = NULL;
466 const char *strmask = NULL;
467 com2SecEntry *e = NULL;
468 unsigned long network = 0, mask = 0;
471 * Get security, source address/netmask and community strings.
474 secName = strtok(param, "\t\n ");
475 if (secName == NULL) {
476 config_perror("missing NAME parameter");
478 } else if (strlen(secName) > (VACMSTRINGLEN - 1)) {
479 config_perror("security name too long");
482 source = strtok(NULL, "\t\n ");
483 if (source == NULL) {
484 config_perror("missing SOURCE parameter");
486 } else if (strncmp(source, EXAMPLE_NETWORK, strlen(EXAMPLE_NETWORK)) ==
488 config_perror("example config NETWORK not properly configured");
491 community = strtok(NULL, "\t\n ");
492 if (community == NULL) {
493 config_perror("missing COMMUNITY parameter\n");
497 (community, EXAMPLE_COMMUNITY, strlen(EXAMPLE_COMMUNITY))
499 config_perror("example config COMMUNITY not properly configured");
501 } else if (strlen(community) > (VACMSTRINGLEN - 1)) {
502 config_perror("community name too long");
507 * Process the source address/netmask string.
510 cp = strchr(source, '/');
520 * Deal with the network part first.
523 if ((strcmp(source, "default") == 0)
524 || (strcmp(source, "0.0.0.0") == 0)) {
529 * Try interpreting as a dotted quad.
531 network = inet_addr(source);
533 if (network == (unsigned long) -1) {
535 * Nope, wasn't a dotted quad. Must be a hostname.
537 #ifdef HAVE_GETHOSTBYNAME
538 struct hostent *hp = gethostbyname(source);
540 config_perror("bad source address");
543 if (hp->h_addrtype != AF_INET) {
544 config_perror("no IP address for source hostname");
547 network = *((unsigned long *) hp->h_addr);
549 #else /*HAVE_GETHOSTBYNAME */
553 config_perror("cannot resolve source hostname");
555 #endif /*HAVE_GETHOSTBYNAME */
560 * Now work out the mask.
563 if (strmask == NULL || *strmask == '\0') {
565 * No mask was given. Use 255.255.255.255.
569 if (strchr(strmask, '.')) {
571 * Try to interpret mask as a dotted quad.
573 mask = inet_addr(strmask);
574 if (mask == (unsigned long) -1 &&
575 strncmp(strmask, "255.255.255.255", 15) != 0) {
576 config_perror("bad mask");
581 * Try to interpret mask as a "number of 1 bits".
583 int maskLen = atoi(strmask), maskBit = 0x80000000L;
584 if (maskLen <= 0 || maskLen > 32) {
585 config_perror("bad mask length");
597 * Check that the network and mask are consistent.
600 if (network & ~mask) {
601 config_perror("source/mask mismatch");
605 e = (com2SecEntry *) malloc(sizeof(com2SecEntry));
607 config_perror("memory error");
612 * Everything is okay. Copy the parameters to the structure allocated
613 * above and add it to END of the list.
616 DEBUGMSGTL(("netsnmp_udp_parse_security",
617 "<\"%s\", 0x%08x/0x%08x> => \"%s\"\n", community, network,
620 strcpy(e->secName, secName);
621 strcpy(e->community, community);
622 e->network = network;
626 if (com2SecListLast != NULL) {
627 com2SecListLast->next = e;
630 com2SecListLast = com2SecList = e;
636 netsnmp_udp_com2SecList_free(void)
638 com2SecEntry *e = com2SecList;
640 com2SecEntry *tmp = e;
644 com2SecList = com2SecListLast = NULL;
649 netsnmp_udp_agent_config_tokens_register(void)
651 register_app_config_handler("com2sec", netsnmp_udp_parse_security,
652 netsnmp_udp_com2SecList_free,
653 "name IPv4-network-address[/netmask] community");
659 * Return 0 if there are no com2sec entries, or return 1 if there ARE com2sec
660 * entries. On return, if a com2sec entry matched the passed parameters,
661 * then *secName points at the appropriate security name, or is NULL if the
662 * parameters did not match any com2sec entry.
666 netsnmp_udp_getSecName(void *opaque, int olength,
667 const char *community,
668 size_t community_len, char **secName)
671 struct sockaddr_in *from = (struct sockaddr_in *) opaque;
672 char *ztcommunity = NULL;
675 * Special case if there are NO entries (as opposed to no MATCHING
679 if (com2SecList == NULL) {
680 DEBUGMSGTL(("netsnmp_udp_getSecName", "no com2sec entries\n"));
681 if (secName != NULL) {
688 * If there is no IPv4 source address, then there can be no valid security
692 if (opaque == NULL || olength != sizeof(struct sockaddr_in) ||
693 from->sin_family != AF_INET) {
694 DEBUGMSGTL(("netsnmp_udp_getSecName",
695 "no IPv4 source address in PDU?\n"));
696 if (secName != NULL) {
702 DEBUGIF("netsnmp_udp_getSecName") {
703 ztcommunity = (char *)malloc(community_len + 1);
704 if (ztcommunity != NULL) {
705 memcpy(ztcommunity, community, community_len);
706 ztcommunity[community_len] = '\0';
709 DEBUGMSGTL(("netsnmp_udp_getSecName", "resolve <\"%s\", 0x%08x>\n",
710 ztcommunity ? ztcommunity : "<malloc error>",
711 from->sin_addr.s_addr));
713 for (c = com2SecList; c != NULL; c = c->next) {
714 DEBUGMSGTL(("netsnmp_udp_getSecName","compare <\"%s\", 0x%08x/0x%08x>",
715 c->community, c->network, c->mask));
716 if ((community_len == strlen(c->community)) &&
717 (memcmp(community, c->community, community_len) == 0) &&
718 ((from->sin_addr.s_addr & c->mask) == c->network)) {
719 DEBUGMSG(("netsnmp_udp_getSecName", "... SUCCESS\n"));
720 if (secName != NULL) {
721 *secName = c->secName;
725 DEBUGMSG(("netsnmp_udp_getSecName", "... nope\n"));
727 if (ztcommunity != NULL) {
735 netsnmp_udp_create_tstring(const char *string, int local)
737 struct sockaddr_in addr;
739 if (netsnmp_sockaddr_in(&addr, string, 0)) {
740 return netsnmp_udp_transport(&addr, local);
748 netsnmp_udp_create_ostring(const u_char * o, size_t o_len, int local)
750 struct sockaddr_in addr;
753 addr.sin_family = AF_INET;
754 memcpy((u_char *) & (addr.sin_addr.s_addr), o, 4);
755 addr.sin_port = ntohs((o[4] << 8) + o[5]);
756 return netsnmp_udp_transport(&addr, local);
763 netsnmp_udp_ctor(void)
765 udpDomain.name = netsnmpUDPDomain;
766 udpDomain.name_length = netsnmpUDPDomain_len;
767 udpDomain.prefix = calloc(2, sizeof(char *));
768 udpDomain.prefix[0] = "udp";
770 udpDomain.f_create_from_tstring = netsnmp_udp_create_tstring;
771 udpDomain.f_create_from_ostring = netsnmp_udp_create_ostring;
773 netsnmp_tdomain_register(&udpDomain);