[PATCH 1/6] libsnmp/transports: Introduce netsnmp_parse_ep_str()

Bart Van Assche <[email protected]>
Newsgroups gmane.network.net-snmp.devel
Message-ID <[email protected]>
The only functional change in this patch is that it is now allowed
to surround IPv4 addresses and hostnames with square brackets in
transport endpoint definitions.
---
 include/net-snmp/library/snmpIPBaseDomain.h   |  16 ++
 include/net-snmp/library/snmpIPv4BaseDomain.h |   2 +
 include/net-snmp/library/snmpIPv6BaseDomain.h |   2 +
 snmplib/transports/snmpIPBaseDomain.c         |  79 +++++++
 snmplib/transports/snmpIPv4BaseDomain.c       |  92 ++------
 snmplib/transports/snmpIPv6BaseDomain.c       | 204 ++----------------
 win32/libsnmp/Makefile.in                     |   1 +
 win32/libsnmp_dll/Makefile.in                 |   1 +
 8 files changed, 134 insertions(+), 263 deletions(-)
 create mode 100644 include/net-snmp/library/snmpIPBaseDomain.h
 create mode 100644 snmplib/transports/snmpIPBaseDomain.c

diff --git a/include/net-snmp/library/snmpIPBaseDomain.h b/include/net-snmp/library/snmpIPBaseDomain.h
new file mode 100644
index 000000000000..fb79a9365c4e
--- /dev/null
+++ b/include/net-snmp/library/snmpIPBaseDomain.h
@@ -0,0 +1,16 @@
+#ifndef _SNMPIPBASEDOMAIN_H_
+#define _SNMPIPBASEDOMAIN_H_
+
+/**
+ * SNMP endpoint with the network name in ASCII format.
+ * @addr: Network address or host name as an ASCII string.
+ * @port: Port number in host byte format.
+ */
+struct netsnmp_ep_str {
+    char     addr[64];
+    uint16_t port;
+};
+
+int netsnmp_parse_ep_str(struct netsnmp_ep_str *ep_str, const char *endpoint);
+
+#endif /* _SNMPIPBASEDOMAIN_H_ */
diff --git a/include/net-snmp/library/snmpIPv4BaseDomain.h b/include/net-snmp/library/snmpIPv4BaseDomain.h
index cb6a4585cf48..71804e3b1d72 100644
--- a/include/net-snmp/library/snmpIPv4BaseDomain.h
+++ b/include/net-snmp/library/snmpIPv4BaseDomain.h
@@ -9,6 +9,8 @@
 
 #include <net-snmp/library/snmp_transport.h>
 
+config_require(IPBase)
+
 #ifdef __cplusplus
 extern          "C" {
 #endif
diff --git a/include/net-snmp/library/snmpIPv6BaseDomain.h b/include/net-snmp/library/snmpIPv6BaseDomain.h
index 5c7d11cfd560..2f44e6a1b088 100644
--- a/include/net-snmp/library/snmpIPv6BaseDomain.h
+++ b/include/net-snmp/library/snmpIPv6BaseDomain.h
@@ -7,6 +7,8 @@
 #include <netinet/in.h>
 #endif
 
+config_require(IPBase)
+
 #include <net-snmp/library/snmp_transport.h>
 
 #ifdef __cplusplus
diff --git a/snmplib/transports/snmpIPBaseDomain.c b/snmplib/transports/snmpIPBaseDomain.c
new file mode 100644
index 000000000000..ac94ba73840d
--- /dev/null
+++ b/snmplib/transports/snmpIPBaseDomain.c
@@ -0,0 +1,79 @@
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/types.h>
+#include <net-snmp/library/system.h>
+#include <net-snmp/library/snmpIPBaseDomain.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+static int isnumber(const char *cp)
+{
+        while (isdigit((uint8_t)*cp))
+            cp++;
+        return *cp == '\0';
+}
+
+/**
+ * Parse a Net-SNMP endpoint name.
+ * @ep_str: Parsed endpoint name.
+ * @endpoint: Endpoint specification in the format [<address>]:[<port>] or
+ *   <port>.
+ *
+ * Only overwrite those fields of *@ep_str that have been set in
+ * @endpoint. Returns 1 upon success and 0 upon failure.
+ */
+int netsnmp_parse_ep_str(struct netsnmp_ep_str *ep_str, const char *endpoint)
+{
+    char *dup, *cp, *addrstr = NULL, *portstr = NULL;
+    unsigned port;
+
+    if (!endpoint)
+        return 0;
+
+    dup = strdup(endpoint);
+    if (!dup)
+        return 0;
+
+    cp = dup;
+    if (isnumber(cp)) {
+        portstr = cp;
+    } else {
+        if (*cp == '[') {
+            addrstr = cp + 1;
+            cp = strchr(cp, ']');
+            if (cp) {
+                cp[0] = '\0';
+                cp++;
+            } else {
+                goto invalid;
+            }
+        } else if (*cp != ':') {
+            addrstr = cp;
+            cp = strrchr(cp, ':');
+        }
+        if (cp && *cp == ':') {
+            *cp++ = '\0';
+            portstr = cp;
+            if (!isnumber(cp))
+                goto invalid;
+        } else if (cp && *cp) {
+            goto invalid;
+        }
+    }
+
+    if (addrstr)
+        strlcpy(ep_str->addr, addrstr, sizeof(ep_str->addr));
+    if (portstr) {
+        port = atoi(portstr);
+        if (port > 0 && port <= 0xffff)
+            ep_str->port = port;
+        else
+            goto invalid;
+    }
+
+    free(dup);
+    return 1;
+
+invalid:
+    free(dup);
+    return 0;
+}
diff --git a/snmplib/transports/snmpIPv4BaseDomain.c b/snmplib/transports/snmpIPv4BaseDomain.c
index 37ef72d4ed42..003e53a49e45 100644
--- a/snmplib/transports/snmpIPv4BaseDomain.c
+++ b/snmplib/transports/snmpIPv4BaseDomain.c
@@ -4,6 +4,7 @@
 #include <net-snmp/net-snmp-config.h>
 
 #include <net-snmp/types.h>
+#include <net-snmp/library/snmpIPBaseDomain.h>
 #include <net-snmp/library/snmpIPv4BaseDomain.h>
 #include <net-snmp/library/snmp_assert.h>
 
@@ -56,6 +57,7 @@ int
 netsnmp_sockaddr_in2(struct sockaddr_in *addr,
                      const char *inpeername, const char *default_target)
 {
+    struct netsnmp_ep_str ep_str;
     int ret;
 
     if (addr == NULL) {
@@ -72,106 +74,42 @@ netsnmp_sockaddr_in2(struct sockaddr_in *addr,
     addr->sin_family = AF_INET;
     addr->sin_port = htons((u_short)SNMP_PORT);
 
+    memset(&ep_str, 0, sizeof(ep_str));
     {
 	int port = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
 				      NETSNMP_DS_LIB_DEFAULT_PORT);
 
 	if (port != 0) {
-	    addr->sin_port = htons((u_short)port);
-	} else if (default_target != NULL)
+            ep_str.port = port;
+	} else if (default_target != NULL) {
 	    netsnmp_sockaddr_in2(addr, default_target, NULL);
-    }
-
-    if (inpeername != NULL && *inpeername != '\0') {
-	const char     *host, *port;
-	char           *peername = NULL;
-        char           *cp;
-        /*
-         * Duplicate the peername because we might want to mank around with
-         * it.
-         */
-
-        peername = strdup(inpeername);
-        if (peername == NULL) {
-            return 0;
-        }
-
-        /*
-         * Try and extract an appended port number.
-         */
-        cp = strchr(peername, ':');
-        if (cp != NULL) {
-            *cp = '\0';
-            port = cp + 1;
-            host = peername;
-        } else {
-            host = NULL;
-            port = peername;
+            strlcpy(ep_str.addr, inet_ntoa(addr->sin_addr), sizeof(ep_str.addr));
+            ep_str.port = ntohs(addr->sin_port);
         }
+    }
 
-        /*
-         * Try to convert the user port specifier
-         */
-        if (port && *port == '\0')
-            port = NULL;
-
-        if (port != NULL) {
-            long int l;
-            char* ep;
-
-            DEBUGMSGTL(("netsnmp_sockaddr_in", "check user service %s\n",
-                        port));
-
-            l = strtol(port, &ep, 10);
-            if (ep != port && *ep == '\0' && 0 <= l && l <= 0x0ffff)
-                addr->sin_port = htons((u_short)l);
-            else {
-                if (host == NULL) {
-                    DEBUGMSGTL(("netsnmp_sockaddr_in",
-                                "servname not numeric, "
-				"check if it really is a destination)\n"));
-                    host = port;
-                    port = NULL;
-                } else {
-                    DEBUGMSGTL(("netsnmp_sockaddr_in",
-                                "servname not numeric\n"));
-                    free(peername);
-                    return 0;
-                }
-            }
-        }
-
-        /*
-         * Try to convert the user host specifier
-         */
-        if (host && *host == '\0')
-            host = NULL;
-
-        if (host != NULL) {
-            DEBUGMSGTL(("netsnmp_sockaddr_in",
-                        "check destination %s\n", host));
-
-
-            if (strcmp(peername, "255.255.255.255") == 0 ) {
+    if (inpeername && *inpeername != '\0') {
+        if (netsnmp_parse_ep_str(&ep_str, inpeername)) {
+            if (ep_str.port)
+                addr->sin_port = htons(ep_str.port);
+            if (strcmp(ep_str.addr, "255.255.255.255") == 0 ) {
                 /*
                  * The explicit broadcast address hack
                  */
                 DEBUGMSGTL(("netsnmp_sockaddr_in", "Explicit UDP broadcast\n"));
                 addr->sin_addr.s_addr = INADDR_NONE;
             } else {
-                ret =
-                    netsnmp_gethostbyname_v4(peername, &addr->sin_addr.s_addr);
+                ret = netsnmp_gethostbyname_v4(ep_str.addr,
+                                               &addr->sin_addr.s_addr);
                 if (ret < 0) {
                     DEBUGMSGTL(("netsnmp_sockaddr_in",
                                 "couldn't resolve hostname\n"));
-                    free(peername);
                     return 0;
                 }
                 DEBUGMSGTL(("netsnmp_sockaddr_in",
                             "hostname (resolved okay)\n"));
             }
         }
-	free(peername);
     }
 
     /*
diff --git a/snmplib/transports/snmpIPv6BaseDomain.c b/snmplib/transports/snmpIPv6BaseDomain.c
index b1c1afce3ded..600bc602428b 100644
--- a/snmplib/transports/snmpIPv6BaseDomain.c
+++ b/snmplib/transports/snmpIPv6BaseDomain.c
@@ -15,6 +15,7 @@
 #ifdef NETSNMP_ENABLE_IPV6
 
 #include <net-snmp/types.h>
+#include <net-snmp/library/snmpIPBaseDomain.h>
 #include <net-snmp/library/snmpIPv6BaseDomain.h>
 #include <net-snmp/library/system.h>
 #include <net-snmp/library/snmp_assert.h>
@@ -247,9 +248,7 @@ 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];
-    int             portno;
 
     if (addr == NULL) {
         return 0;
@@ -275,205 +274,38 @@ netsnmp_sockaddr_in6_2(struct sockaddr_in6 *addr,
     }
 
     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;
-#if HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
-	        unsigned int if_index = 0;
-#endif
-                *cp = '\0';
-		scope_id = strchr(peername + 1, '%');
-		if (scope_id != NULL) {
-		    *scope_id = '\0';
-#if HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
-		    if_index = netsnmp_if_nametoindex(scope_id + 1);
-#endif
-		}
-                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));
-                            free(peername);
-                            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);
+        struct netsnmp_ep_str ep_str;
+
+        memset(&ep_str, 0, sizeof(ep_str));
+        if (netsnmp_parse_ep_str(&ep_str, inpeername)) {
+            DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "split: [%s]:%d",
+                        ep_str.addr, ep_str.port));
+            if (ep_str.addr[0]) {
+                char *scope_id;
+
+                scope_id = strchr(ep_str.addr, '%');
+                if (scope_id) {
+                    *scope_id = '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 = ']';
-            }
-        }
-
-        cp = strrchr(peername, ':');
-        if (cp != NULL) {
-	    char *scope_id;
-#if HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
-	    unsigned int if_index = 0;
-#endif
-	    *cp = '\0';
-	    scope_id = strchr(peername + 1, '%');
-	    if (scope_id != NULL) {
-	        *scope_id = '\0';
-#if HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
-	        if_index = netsnmp_if_nametoindex(scope_id + 1);
+                    addr->sin6_scope_id = netsnmp_if_nametoindex(scope_id + 1);
 #endif
-	    }
-            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));
-                    free(peername);
-                    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));
-                    free(peername);
+                if (!inet_pton(AF_INET6, ep_str.addr, &addr->sin6_addr) &&
+                    !netsnmp_resolve_v6_hostname(&addr->sin6_addr, ep_str.addr))
                     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 (ep_str.port)
+                addr->sin6_port = htons(ep_str.port);
         }
 
-        if (peername[0] == '\0') {
-          DEBUGMSGTL(("netsnmp_sockaddr_in6_2", "empty hostname\n"));
-          free(peername);
-          return 0;
-        }
-        if (!netsnmp_resolve_v6_hostname(&addr->sin6_addr, peername)) {
-            free(peername);
-            return 0;
-        }
     } 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;
 }
 
diff --git a/win32/libsnmp/Makefile.in b/win32/libsnmp/Makefile.in
index d5e173e9a124..88d24533abf6 100644
--- a/win32/libsnmp/Makefile.in
+++ b/win32/libsnmp/Makefile.in
@@ -55,6 +55,7 @@ LIB32_OBJS= \
 	"$(INTDIR)\snmp-tc.obj" \
 	"$(INTDIR)\snmp.obj" \
 	"$(INTDIR)\snmpCallbackDomain.obj" \
+	"$(INTDIR)\snmpIPBaseDomain.obj" \
 	"$(INTDIR)\snmpIPv4BaseDomain.obj" \
 	"$(INTDIR)\snmpSocketBaseDomain.obj" \
 	"$(INTDIR)\snmpTCPBaseDomain.obj" \
diff --git a/win32/libsnmp_dll/Makefile.in b/win32/libsnmp_dll/Makefile.in
index 3d05633b8856..bd36672dcdf4 100644
--- a/win32/libsnmp_dll/Makefile.in
+++ b/win32/libsnmp_dll/Makefile.in
@@ -55,6 +55,7 @@ LINK32_OBJS= \
 	"$(INTDIR)\snmp-tc.obj" \
 	"$(INTDIR)\snmp.obj" \
 	"$(INTDIR)\snmpCallbackDomain.obj" \
+	"$(INTDIR)\snmpIPBaseDomain.obj" \
 	"$(INTDIR)\snmpIPv4BaseDomain.obj" \
 	"$(INTDIR)\snmpSocketBaseDomain.obj" \
 	"$(INTDIR)\snmpTCPBaseDomain.obj" \
-- 
2.19.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.