blob: 01f81b961a6e4617f2bc9894c4e882badc9a3f1d [file] [log] [blame]
/* IPV6 base transport support functions
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/types.h>
#include <net-snmp/library/snmpIPv6BaseDomain.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#if HAVE_NETDB_H
#include <netdb.h>
#endif
#if HAVE_NET_IF_H
#include <net/if.h>
#endif
#if HAVE_DMALLOC_H
#include <dmalloc.h>
#endif
#include <net-snmp/types.h>
#include <net-snmp/library/snmp_debug.h>
#include <net-snmp/library/default_store.h>
#include <net-snmp/library/snmp_logging.h>
#include "inet_ntop.h"
#include "inet_pton.h"
#if defined(WIN32) && !defined(IF_NAMESIZE)
#define IF_NAMESIZE 12
#endif
#if defined(HAVE_WINSOCK_H) && !defined(mingw32)
static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
#endif
static unsigned
netsnmp_if_nametoindex(const char *ifname)
{
#if defined(WIN32)
return atoi(ifname);
#elif defined(HAVE_IF_NAMETOINDEX)
return if_nametoindex(ifname);
#else
return 0;
#endif
}
static char *
netsnmp_if_indextoname(unsigned ifindex, char *ifname)
{
#if defined(WIN32)
snprintf(ifname, IF_NAMESIZE, "%u", ifindex);
return ifname;
#elif defined(HAVE_IF_NAMETOINDEX)
return if_indextoname(ifindex, ifname);
#else
return NULL;
#endif
}
char *
netsnmp_ipv6_fmtaddr(const char *prefix, netsnmp_transport *t,
void *data, int len)
{
struct sockaddr_in6 *to = NULL;
char addr[INET6_ADDRSTRLEN];
char tmp[INET6_ADDRSTRLEN + 18];
DEBUGMSGTL(("netsnmp_udp6", "fmtaddr: t = %p, data = %p, len = %d\n", t,
data, len));
if (data != NULL && len == sizeof(struct sockaddr_in6)) {
to = (struct sockaddr_in6 *) data;
} else if (t != NULL && t->data != NULL) {
to = (struct sockaddr_in6 *) t->data;
}
if (to == NULL) {
snprintf(tmp, sizeof(tmp), "%s: unknown", prefix);
} else {
char scope_id[IF_NAMESIZE + 1] = "";
#if defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)
if (to->sin6_scope_id
&& netsnmp_if_indextoname(to->sin6_scope_id, &scope_id[1])) {
scope_id[0] = '%';
}
#endif
snprintf(tmp, sizeof(tmp), "%s: [%s%s]:%hu", prefix,
inet_ntop(AF_INET6, (void *) &(to->sin6_addr), addr,
INET6_ADDRSTRLEN), scope_id, ntohs(to->sin6_port));
}
tmp[sizeof(tmp)-1] = '\0';
return strdup(tmp);
}
int
netsnmp_sockaddr_in6_2(struct sockaddr_in6 *addr,
const char *inpeername, const char *default_target)
{
char *cp = NULL, *peername = NULL;
char debug_addr[INET6_ADDRSTRLEN];
#if HAVE_GETADDRINFO
struct addrinfo *addrs = NULL;
int err;
#elif HAVE_GETIPNODEBYNAME
struct hostent *hp = NULL;
int err;
#elif HAVE_GETHOSTBYNAME
struct hostent *hp = NULL;
#endif
int portno;
if (addr == NULL) {
return 0;
}
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"addr %p, peername \"%s\", default_target \"%s\"\n",
addr, inpeername ? inpeername : "[NIL]",
default_target ? default_target : "[NIL]"));
memset(addr, 0, sizeof(struct sockaddr_in6));
addr->sin6_family = AF_INET6;
addr->sin6_addr = in6addr_any;
addr->sin6_port = htons((u_short)SNMP_PORT);
{
int port = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_DEFAULT_PORT);
if (port != 0)
addr->sin6_port = htons((u_short)port);
else if (default_target != NULL)
netsnmp_sockaddr_in6_2(addr, default_target, NULL);
}
if (inpeername != NULL) {
/*
* Duplicate the peername because we might want to mank around with
* it.
*/
peername = strdup(inpeername);
if (peername == NULL) {
return 0;
}
cp = peername;
if (*cp == ':') cp++;
portno = atoi(cp);
while (*cp && isdigit((unsigned char) *cp)) cp++;
if (!*cp && portno != 0) {
/*
* Okay, it looks like JUST a port number.
*/
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "totally numeric: %d\n",
portno));
addr->sin6_port = htons((u_short)portno);
goto resolved;
}
/*
* See if it is an IPv6 address covered with square brackets. Also check
* for an appended :port.
*/
if (*peername == '[') {
cp = strchr(peername, ']');
if (cp != NULL) {
/*
* See if it is an IPv6 link-local address with interface
* name as <zone_id>, like fe80::1234%eth0.
* Please refer to the internet draft, IPv6 Scoped Address Architecture
* http://www.ietf.org/internet-drafts/draft-ietf-ipngwg-scoping-arch-04.txt
*
*/
char *scope_id;
unsigned int if_index = 0;
*cp = '\0';
scope_id = strchr(peername + 1, '%');
if (scope_id != NULL) {
*scope_id = '\0';
if_index = netsnmp_if_nametoindex(scope_id + 1);
}
if (*(cp + 1) == ':') {
portno = atoi(cp+2);
if (portno != 0 &&
inet_pton(AF_INET6, peername + 1,
(void *) &(addr->sin6_addr))) {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"IPv6 address with port suffix :%d\n",
portno));
if (portno > 0 && portno <= 0xffff) {
addr->sin6_port = htons((u_short)portno);
} else {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "invalid port number: %d", portno));
return 0;
}
#if defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)
addr->sin6_scope_id = if_index;
#endif
goto resolved;
}
} else {
if (inet_pton
(AF_INET6, peername + 1,
(void *) &(addr->sin6_addr))) {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"IPv6 address with square brackets\n"));
portno = ntohs(addr->sin6_port);
if (portno == 0)
portno = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_DEFAULT_PORT);
if (portno <= 0)
portno = SNMP_PORT;
addr->sin6_port = htons((u_short)portno);
#if defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)
addr->sin6_scope_id = if_index;
#endif
goto resolved;
}
}
if (scope_id != NULL) {
*scope_id = '%';
}
*cp = ']';
}
}
cp = strrchr(peername, ':');
if (cp != NULL) {
char *scope_id;
unsigned int if_index = 0;
*cp = '\0';
scope_id = strchr(peername + 1, '%');
if (scope_id != NULL) {
*scope_id = '\0';
if_index = netsnmp_if_nametoindex(scope_id + 1);
}
portno = atoi(cp + 1);
if (portno != 0 &&
inet_pton(AF_INET6, peername,
(void *) &(addr->sin6_addr))) {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"IPv6 address with port suffix :%d\n",
atoi(cp + 1)));
if (portno > 0 && portno <= 0xffff) {
addr->sin6_port = htons((u_short)portno);
} else {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "invalid port number: %d", portno));
return 0;
}
#if defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)
addr->sin6_scope_id = if_index;
#endif
goto resolved;
}
if (scope_id != NULL) {
*scope_id = '%';
}
*cp = ':';
}
/*
* See if it is JUST an IPv6 address.
*/
if (inet_pton(AF_INET6, peername, (void *) &(addr->sin6_addr))) {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "just IPv6 address\n"));
goto resolved;
}
/*
* Well, it must be a hostname then, possibly with an appended :port.
* Sort that out first.
*/
cp = strrchr(peername, ':');
if (cp != NULL) {
*cp = '\0';
portno = atoi(cp + 1);
if (portno != 0) {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"hostname(?) with port suffix :%d\n",
portno));
if (portno > 0 && portno <= 0xffff) {
addr->sin6_port = htons((u_short)portno);
} else {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "invalid port number: %d", portno));
return 0;
}
} else {
/*
* No idea, looks bogus but we might as well pass the full thing to
* the name resolver below.
*/
*cp = ':';
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"hostname(?) with embedded ':'?\n"));
}
/*
* Fall through.
*/
}
if (peername[0] == '\0') {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "empty hostname\n"));
free(peername);
return 0;
}
#if HAVE_GETADDRINFO
{
struct addrinfo hint = { 0 };
hint.ai_flags = 0;
hint.ai_family = PF_INET6;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = 0;
err = getaddrinfo(peername, NULL, &hint, &addrs);
}
if (err != 0) {
#if HAVE_GAI_STRERROR
snmp_log(LOG_ERR, "getaddrinfo(\"%s\", NULL, ...): %s\n", peername,
gai_strerror(err));
#else
snmp_log(LOG_ERR, "getaddrinfo(\"%s\", NULL, ...): (error %d)\n",
peername, err);
#endif
free(peername);
return 0;
}
if (addrs != NULL) {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "hostname (resolved okay)\n"));
memcpy(&addr->sin6_addr,
&((struct sockaddr_in6 *) addrs->ai_addr)->sin6_addr,
sizeof(struct in6_addr));
freeaddrinfo(addrs);
}
else {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "Failed to resolve IPv6 hostname\n"));
}
#elif HAVE_GETIPNODEBYNAME
hp = getipnodebyname(peername, AF_INET6, 0, &err);
if (hp == NULL) {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"hostname (couldn't resolve = %d)\n", err));
free(peername);
return 0;
}
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "hostname (resolved okay)\n"));
memcpy(&(addr->sin6_addr), hp->h_addr, hp->h_length);
#elif HAVE_GETHOSTBYNAME
hp = gethostbyname(peername);
if (hp == NULL) {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"hostname (couldn't resolve)\n"));
free(peername);
return 0;
} else {
if (hp->h_addrtype != AF_INET6) {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"hostname (not AF_INET6!)\n"));
free(peername);
return 0;
} else {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2",
"hostname (resolved okay)\n"));
memcpy(&(addr->sin6_addr), hp->h_addr, hp->h_length);
}
}
#else /*HAVE_GETHOSTBYNAME */
/*
* There is no name resolving function available.
*/
snmp_log(LOG_ERR,
"no getaddrinfo()/getipnodebyname()/gethostbyname()\n");
free(peername);
return 0;
#endif /*HAVE_GETHOSTBYNAME */
} else {
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "NULL peername"));
return 0;
}
resolved:
DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "return { AF_INET6, [%s]:%hu }\n",
inet_ntop(AF_INET6, &addr->sin6_addr, debug_addr,
sizeof(debug_addr)), ntohs(addr->sin6_port)));
free(peername);
return 1;
}
int
netsnmp_sockaddr_in6(struct sockaddr_in6 *addr,
const char *inpeername, int remote_port)
{
char buf[sizeof(remote_port) * 3 + 2];
sprintf(buf, ":%u", remote_port);
return netsnmp_sockaddr_in6_2(addr, inpeername, remote_port ? buf : NULL);
}