]> granicus.if.org Git - php/commitdiff
Adding IPv6 support (FR #42918)
authorBoris Lytochkin <lytboris@php.net>
Sat, 20 Aug 2011 15:53:44 +0000 (15:53 +0000)
committerBoris Lytochkin <lytboris@php.net>
Sat, 20 Aug 2011 15:53:44 +0000 (15:53 +0000)
ext/snmp/snmp.c
ext/snmp/tests/README
ext/snmp/tests/ipv6.phpt [new file with mode: 0644]
ext/snmp/tests/snmp_include.inc
ext/snmp/tests/wrong_hostname.phpt

index 67d6cebe364076e8f4919e1e5d444c7a808481a6..c0ab7f5f6afeaab8ecc798bc72e70215d734d082 100644 (file)
@@ -28,6 +28,7 @@
 #endif
 
 #include "php.h"
+#include "main/php_network.h"
 #include "ext/standard/info.h"
 #include "php_snmp.h"
 
@@ -1090,9 +1091,13 @@ static int php_snmp_parse_oid(int st, struct objid_query *objid_query, zval **oi
 */
 static int netsnmp_session_init(php_snmp_session **session_p, int version, char *hostname, char *community, int timeout, int retries TSRMLS_DC)
 {
-       int remote_port = SNMP_PORT;
        php_snmp_session *session;
        char *pptr;
+       char buf[MAX_NAME_LEN];
+       int force_ipv6 = FALSE;
+       int n;
+       struct sockaddr **psal;
+       struct sockaddr **res;
 
        *session_p = (php_snmp_session *)emalloc(sizeof(php_snmp_session));
        session = *session_p;
@@ -1100,25 +1105,86 @@ static int netsnmp_session_init(php_snmp_session **session_p, int version, char
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "emalloc() failed allocating session");
                return (-1);
        }
+       memset(session, 0, sizeof(php_snmp_session));
+
+       strlcpy(buf, hostname, sizeof(buf));
 
        snmp_sess_init(session);
 
        session->version = version;
+       session->remote_port = SNMP_PORT;
 
        session->peername = emalloc(MAX_NAME_LEN);
-       if(session->peername == NULL) {
+       if (session->peername == NULL) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "emalloc() failed while copying hostname");
-               netsnmp_session_free(&session);
                return (-1);
        }
+       *(session->peername) = '\0';
 
        /* Reading the hostname and its optional non-default port number */
-       strlcpy(session->peername, hostname, MAX_NAME_LEN);
-       if ((pptr = strchr(session->peername, ':'))) {
-               remote_port = strtol(pptr + 1, NULL, 0);
+       if (*hostname == '[') { /* IPv6 address */
+               force_ipv6 = TRUE;
+               hostname++;
+               if ((pptr = strchr(hostname, ']'))) {
+                       if (pptr[1] == ':') {
+                               session->remote_port = atoi(pptr + 2);
+                       }
+                       *pptr = '\0';
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "mailformed IPv6 address, closing square bracket missing");
+                       return (-1);
+               }
+       } else { /* IPv4 address */
+               if ((pptr = strchr(hostname, ':'))) {
+                       session->remote_port = atoi(pptr + 1);
+                       *pptr = '\0';
+               }
+       }
+
+       /* since Net-SNMP library requires 'udp6:' prefix for all IPv6 addresses (in FQDN form too) we need to
+          perform possible name resolution before running any SNMP queries */
+       if ((n = php_network_getaddresses(hostname, SOCK_DGRAM, &psal, NULL TSRMLS_CC)) == 0) { /* some resover error */
+               /* warnings sent, bailing out */
+               return (-1);
+       }
+
+       res = psal;
+       while (n-- > 0) {
+               pptr = session->peername;
+#if HAVE_GETADDRINFO && HAVE_IPV6 && HAVE_INET_NTOP
+               if (force_ipv6 && (*res)->sa_family != AF_INET6) {
+                       res++;
+                       continue;
+               }
+               if ((*res)->sa_family == AF_INET6) {
+                       strcpy(session->peername, "udp6:");
+                       pptr = session->peername + strlen(session->peername);
+                       inet_ntop((*res)->sa_family, &(((struct sockaddr_in6*)(*res))->sin6_addr), pptr, MAX_NAME_LEN);
+               } else if ((*res)->sa_family == AF_INET) {
+                       inet_ntop((*res)->sa_family, &(((struct sockaddr_in*)(*res))->sin_addr), pptr, MAX_NAME_LEN);
+               } else {
+                       res++;
+                       continue;
+               }
+#else
+               if (res->sa_family != AF_INET) {
+                       res++;
+                       continue;
+               }
+               strcat(pptr, inet_ntoa(res));
+#endif
+               break;
+       }
+
+       if (strlen(session->peername) == 0) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown failure while resolving '%s'", buf);
+               return (-1);
        }
+       /* XXX FIXME
+               There should be check for non-empty session->peername!
+       */
 
-       session->remote_port = remote_port;
+       php_network_freeaddresses(psal);
 
        if (version == SNMP_VERSION_3) {
                /* Setting the security name. */
index d87d17ad1b8ba9aaff986ce317a4a9a39ca8e3f0..74258ec27f279df5e596dc13243b8176bee026ae 100644 (file)
@@ -9,7 +9,9 @@ To enable these tests, you must have :
 ** How to test **
 You need to give credentials with environment vars if default ones are not
 sutable (see snmp_include.inc for more info):
-SNMP_HOSTNAME : IP or IP:PORT to connect to
+SNMP_HOSTNAME : IPv4 of remote SNMP agent
+SNMP_HOSTNAME : IPv6 or remote SNMP agent
+SNMP_PORT : SNMP port for queries
 SNMP_COMMUNITY : community name
 SNMP_COMMUNITY_WRITE : community used for write tests (snmpset()).
 SNMP_MIBDIR : Directory containing MIBS
@@ -26,6 +28,7 @@ On Linux/FreeBSD
   
 ###### Config file #####
 rocommunity public 127.0.0.1
+rocommunity6 public ::1
 rwcommunity private 127.0.0.1
 
 Do not enable them - being set here they make appropriate OID switch into r/o
diff --git a/ext/snmp/tests/ipv6.phpt b/ext/snmp/tests/ipv6.phpt
new file mode 100644 (file)
index 0000000..78119d1
--- /dev/null
@@ -0,0 +1,24 @@
+--TEST--                                 
+IPv6 support
+--CREDITS--
+Boris Lytochkin
+--SKIPIF--
+<?php
+       require_once(dirname(__FILE__).'/skipif.inc');
+
+       if (!function_exists("inet_ntop")) die("skip no inet_ntop()");
+?>
+--FILE--
+<?php
+require_once(dirname(__FILE__).'/snmp_include.inc');
+
+$default_port = 161;
+
+//EXPECTF format is quickprint OFF
+snmp_set_quick_print(false);
+snmp_set_valueretrieval(SNMP_VALUE_PLAIN);
+
+var_dump(snmpget($hostname6_port, $community, '.1.3.6.1.2.1.1.1.0'));
+?>
+--EXPECTF--
+%unicode|string%(%d) "%s"
index c91499078da052b354e6335f34780a140890bb3b..caa0721890f7f4036bbae2b62d15c8ecfee7f961 100644 (file)
@@ -6,9 +6,11 @@ requests and 'private' community for write requests.
 Default timeout is 1000ms and there will be one request performed.
 */
 
-$hostname =    getenv('SNMP_HOSTNAME')         ? getenv('SNMP_HOSTNAME') :     '127.0.0.1';
+$hostname4 =   getenv('SNMP_HOSTNAME')         ? getenv('SNMP_HOSTNAME') :     '127.0.0.1';
+$hostname6 =   getenv('SNMP_HOSTNAME6')        ? getenv('SNMP_HOSTNAME6') :    '::1';
 $port =                getenv('SNMP_PORT')             ? getenv('SNMP_PORT') :         '161';
-$hostname .= ":$port";
+$hostname      = "$hostname4:$port";
+$hostname6_port = "[$hostname6]:$port";
 $community =   getenv('SNMP_COMMUNITY')        ? getenv('SNMP_COMMUNITY') :    'public';
 $communityWrite = getenv('SNMP_COMMUNITY_WRITE')? getenv('SNMP_COMMUNITY_WRITE'):'private';
 
index e0bfa8a54d120559f0ba23c9041df32a5563587a..4ab087e41fc6e2d8a4688c58d81f5506fc186f8f 100644 (file)
@@ -18,5 +18,5 @@ var_dump(snmpget('192.168..6.1', 'community', '.1.3.6.1.2.1.1.1.0', $timeout, $r
 
 ?>
 --EXPECTF--
-Warning: snmpget(): Could not open snmp connection: Unknown host (192.168..6.1) (%s) in %s on line %d
+Warning: snmpget(): php_network_getaddresses: getaddrinfo failed: hostname nor servname provided, or not known in %s on line %d
 bool(false)