snmpclient.c (295685c5c1f1b55e360c6328f6323a72a199c160) | snmpclient.c (04d1781439aa9bf5c34c0eee606a3909f3503fe4) |
---|---|
1/* | 1/* |
2 * Copyright (c) 2004-2005 | 2 * Copyright (c) 2004-2005,2018 |
3 * Hartmut Brandt. 4 * All rights reserved. 5 * Copyright (c) 2001-2003 6 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 7 * All rights reserved. 8 * 9 * Author: Harti Brandt <harti@freebsd.org> 10 * Kendy Kutzner --- 18 unchanged lines hidden (view full) --- 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $Begemot: bsnmp/lib/snmpclient.c,v 1.36 2005/10/06 07:14:58 brandt_h Exp $ 34 * 35 * Support functions for SNMP clients. 36 */ | 3 * Hartmut Brandt. 4 * All rights reserved. 5 * Copyright (c) 2001-2003 6 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 7 * All rights reserved. 8 * 9 * Author: Harti Brandt <harti@freebsd.org> 10 * Kendy Kutzner --- 18 unchanged lines hidden (view full) --- 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $Begemot: bsnmp/lib/snmpclient.c,v 1.36 2005/10/06 07:14:58 brandt_h Exp $ 34 * 35 * Support functions for SNMP clients. 36 */ |
37#include <sys/types.h> | 37#include <sys/param.h> |
38#include <sys/time.h> 39#include <sys/queue.h> 40#include <sys/socket.h> 41#include <sys/un.h> | 38#include <sys/time.h> 39#include <sys/queue.h> 40#include <sys/socket.h> 41#include <sys/un.h> |
42#include <net/if.h> 43#include <ctype.h> |
|
42#include <stdio.h> 43#include <stdlib.h> 44#include <stddef.h> 45#include <stdarg.h> 46#include <string.h> 47#include <errno.h> 48#include <unistd.h> 49#include <fcntl.h> 50#include <netdb.h> 51#ifdef HAVE_STDINT_H 52#include <stdint.h> 53#elif defined(HAVE_INTTYPES_H) 54#include <inttypes.h> 55#endif 56#include <limits.h> 57#ifdef HAVE_ERR_H 58#include <err.h> 59#endif 60 | 44#include <stdio.h> 45#include <stdlib.h> 46#include <stddef.h> 47#include <stdarg.h> 48#include <string.h> 49#include <errno.h> 50#include <unistd.h> 51#include <fcntl.h> 52#include <netdb.h> 53#ifdef HAVE_STDINT_H 54#include <stdint.h> 55#elif defined(HAVE_INTTYPES_H) 56#include <inttypes.h> 57#endif 58#include <limits.h> 59#ifdef HAVE_ERR_H 60#include <err.h> 61#endif 62 |
63#include <arpa/inet.h> 64 |
|
61#include "support.h" 62#include "asn1.h" 63#include "snmp.h" 64#include "snmpclient.h" 65#include "snmppriv.h" 66 | 65#include "support.h" 66#include "asn1.h" 67#include "snmp.h" 68#include "snmpclient.h" 69#include "snmppriv.h" 70 |
71#define DEBUG_PARSE 0 72 |
|
67/* global context */ 68struct snmp_client snmp_client; 69 70/* List of all outstanding requests */ 71struct sent_pdu { 72 int reqid; 73 struct snmp_pdu *pdu; 74 struct timeval time; --- 844 unchanged lines hidden (view full) --- 919 free(snmp_client.cport); 920 snmp_client.cport = ptr; 921 strcpy(snmp_client.cport, port); 922 } 923 924 /* open connection */ 925 memset(&hints, 0, sizeof(hints)); 926 hints.ai_flags = AI_CANONNAME; | 73/* global context */ 74struct snmp_client snmp_client; 75 76/* List of all outstanding requests */ 77struct sent_pdu { 78 int reqid; 79 struct snmp_pdu *pdu; 80 struct timeval time; --- 844 unchanged lines hidden (view full) --- 925 free(snmp_client.cport); 926 snmp_client.cport = ptr; 927 strcpy(snmp_client.cport, port); 928 } 929 930 /* open connection */ 931 memset(&hints, 0, sizeof(hints)); 932 hints.ai_flags = AI_CANONNAME; |
927 hints.ai_family = AF_INET; | 933 hints.ai_family = snmp_client.trans == SNMP_TRANS_UDP ? AF_INET: 934 AF_INET6; |
928 hints.ai_socktype = SOCK_DGRAM; 929 hints.ai_protocol = 0; 930 error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0); 931 if (error != 0) { 932 seterr(&snmp_client, "%s: %s", snmp_client.chost, 933 gai_strerror(error)); 934 return (-1); 935 } --- 127 unchanged lines hidden (view full) --- 1063 sizeof(snmp_client.read_community)); 1064 if (writecomm != NULL) 1065 strlcpy(snmp_client.write_community, writecomm, 1066 sizeof(snmp_client.write_community)); 1067 1068 switch (snmp_client.trans) { 1069 1070 case SNMP_TRANS_UDP: | 935 hints.ai_socktype = SOCK_DGRAM; 936 hints.ai_protocol = 0; 937 error = getaddrinfo(snmp_client.chost, snmp_client.cport, &hints, &res0); 938 if (error != 0) { 939 seterr(&snmp_client, "%s: %s", snmp_client.chost, 940 gai_strerror(error)); 941 return (-1); 942 } --- 127 unchanged lines hidden (view full) --- 1070 sizeof(snmp_client.read_community)); 1071 if (writecomm != NULL) 1072 strlcpy(snmp_client.write_community, writecomm, 1073 sizeof(snmp_client.write_community)); 1074 1075 switch (snmp_client.trans) { 1076 1077 case SNMP_TRANS_UDP: |
1078 case SNMP_TRANS_UDP6: |
|
1071 if (open_client_udp(host, port) != 0) 1072 return (-1); 1073 break; 1074 1075 case SNMP_TRANS_LOC_DGRAM: 1076 case SNMP_TRANS_LOC_STREAM: 1077 if (open_client_local(host) != 0) 1078 return (-1); --- 782 unchanged lines hidden (view full) --- 1861 strcpy(np, p); 1862 if (cl->cport != NULL) 1863 free(cl->cport); 1864 cl->cport = np; 1865 } 1866 return (0); 1867} 1868 | 1079 if (open_client_udp(host, port) != 0) 1080 return (-1); 1081 break; 1082 1083 case SNMP_TRANS_LOC_DGRAM: 1084 case SNMP_TRANS_LOC_STREAM: 1085 if (open_client_local(host) != 0) 1086 return (-1); --- 782 unchanged lines hidden (view full) --- 1869 strcpy(np, p); 1870 if (cl->cport != NULL) 1871 free(cl->cport); 1872 cl->cport = np; 1873 } 1874 return (0); 1875} 1876 |
1869/* 1870 * parse a server specification | 1877/** 1878 * Try to get a transport identifier which is a leading alphanumeric string 1879 * (starting with '_' or a letter and including also '_') terminated by 1880 * a double colon. The string may not be empty. The transport identifier 1881 * is optional. |
1871 * | 1882 * |
1872 * [trans::][community@][server][:port] | 1883 * \param sc client struct to set errors 1884 * \param strp possible start of transport; updated to point to 1885 * the next character to parse 1886 * 1887 * \return end of transport; equals *strp if there is none; NULL if there 1888 * was an error |
1873 */ | 1889 */ |
1874int 1875snmp_parse_server(struct snmp_client *sc, const char *str) | 1890static inline const char * 1891get_transp(struct snmp_client *sc, const char **strp) |
1876{ | 1892{ |
1877 const char *p, *s = str; | 1893 const char *p = *strp; |
1878 | 1894 |
1879 /* look for a double colon */ 1880 for (p = s; *p != '\0'; p++) { 1881 if (*p == '\\' && p[1] != '\0') { | 1895 if (isascii(*p) && (isalpha(*p) || *p == '_')) { 1896 p++; 1897 while (isascii(*p) && (isalnum(*p) || *p == '_')) |
1882 p++; | 1898 p++; |
1883 continue; | 1899 if (p[0] == ':' && p[1] == ':') { 1900 *strp = p + 2; 1901 return (p); |
1884 } | 1902 } |
1885 if (*p == ':' && p[1] == ':') 1886 break; | |
1887 } | 1903 } |
1888 if (*p != '\0') { 1889 if (p > s) { 1890 if (p - s == 3 && strncmp(s, "udp", 3) == 0) 1891 sc->trans = SNMP_TRANS_UDP; 1892 else if (p - s == 6 && strncmp(s, "stream", 6) == 0) 1893 sc->trans = SNMP_TRANS_LOC_STREAM; 1894 else if (p - s == 5 && strncmp(s, "dgram", 5) == 0) 1895 sc->trans = SNMP_TRANS_LOC_DGRAM; 1896 else { 1897 seterr(sc, "unknown SNMP transport '%.*s'", 1898 (int)(p - s), s); 1899 return (-1); 1900 } 1901 } 1902 s = p + 2; | 1904 if (p[0] == ':' && p[1] == ':') { 1905 seterr(sc, "empty transport specifier"); 1906 return (NULL); |
1903 } | 1907 } |
1908 return (*strp); 1909} |
|
1904 | 1910 |
1905 /* look for a @ */ 1906 for (p = s; *p != '\0'; p++) { 1907 if (*p == '\\' && p[1] != '\0') { 1908 p++; 1909 continue; 1910 } 1911 if (*p == '@') 1912 break; | 1911/** 1912 * Try to get community string. Eat everything up to the last @ (if there is 1913 * any) but only if it is not longer than SNMP_COMMUNITY_MAXLEN. Empty 1914 * community strings are legal. 1915 * 1916 * \param sc client struct to set errors 1917 * \param strp possible start of community; updated to the point to 1918 * the next character to parse 1919 * 1920 * \return end of community; equals *strp if there is none; NULL if there 1921 * was an error 1922 */ 1923static inline const char * 1924get_comm(struct snmp_client *sc, const char **strp) 1925{ 1926 const char *p = strrchr(*strp, '@'); 1927 1928 if (p == NULL) 1929 /* no community string */ 1930 return (*strp); 1931 1932 if (p - *strp > SNMP_COMMUNITY_MAXLEN) { 1933 seterr(sc, "community string too long '%.*s'", 1934 p - *strp, *strp); 1935 return (NULL); |
1913 } 1914 | 1936 } 1937 |
1915 if (*p != '\0') { 1916 if (p - s > SNMP_COMMUNITY_MAXLEN) { 1917 seterr(sc, "community string too long"); 1918 return (-1); | 1938 *strp = p + 1; 1939 return (p); 1940} 1941 1942/** 1943 * Try to get an IPv6 address. This starts with an [ and should end with an ] 1944 * and everything between should be not longer than INET6_ADDRSTRLEN and 1945 * parseable by inet_pton(). 1946 * 1947 * \param sc client struct to set errors 1948 * \param strp possible start of IPv6 address (the '['); updated to point to 1949 * the next character to parse (the one after the closing ']') 1950 * 1951 * \return end of address (equals *strp + 1 if there is none) or NULL 1952 * on errors 1953 */ 1954static inline const char * 1955get_ipv6(struct snmp_client *sc, const char **strp) 1956{ 1957 char str[INET6_ADDRSTRLEN + IF_NAMESIZE]; 1958 struct addrinfo hints, *res; 1959 int error; 1960 1961 if (**strp != '[') 1962 return (*strp + 1); 1963 1964 const char *p = *strp + 1; 1965 while (*p != ']' ) { 1966 if (*p == '\0') { 1967 seterr(sc, "unterminated IPv6 address '%.*s'", 1968 p - *strp, *strp); 1969 return (NULL); |
1919 } | 1970 } |
1920 strncpy(sc->read_community, s, p - s); 1921 sc->read_community[p - s] = '\0'; 1922 strncpy(sc->write_community, s, p - s); 1923 sc->write_community[p - s] = '\0'; 1924 s = p + 1; | 1971 p++; |
1925 } 1926 | 1972 } 1973 |
1927 /* look for a colon */ 1928 for (p = s; *p != '\0'; p++) { 1929 if (*p == '\\' && p[1] != '\0') { 1930 p++; 1931 continue; 1932 } 1933 if (*p == ':') 1934 break; | 1974 if (p - *strp > INET6_ADDRSTRLEN + IF_NAMESIZE) { 1975 seterr(sc, "IPv6 address too long '%.*s'", p - *strp, *strp); 1976 return (NULL); |
1935 } 1936 | 1977 } 1978 |
1937 if (*p == ':') { 1938 if (p > s) { 1939 /* host:port */ 1940 free(sc->chost); 1941 if ((sc->chost = malloc(p - s + 1)) == NULL) { 1942 seterr(sc, "%s", strerror(errno)); 1943 return (-1); 1944 } 1945 strncpy(sc->chost, s, p - s); 1946 sc->chost[p - s] = '\0'; | 1979 strncpy(str, *strp + 1, p - (*strp + 1)); 1980 str[p - (*strp + 1)] = '\0'; 1981 1982 memset(&hints, 0, sizeof(hints)); 1983 hints.ai_flags = AI_CANONNAME | AI_NUMERICHOST; 1984 hints.ai_family = AF_INET6; 1985 hints.ai_socktype = SOCK_DGRAM; 1986 hints.ai_protocol = IPPROTO_UDP; 1987 error = getaddrinfo(str, NULL, &hints, &res); 1988 if (error != 0) { 1989 seterr(sc, "%s: %s", str, gai_strerror(error)); 1990 return (NULL); 1991 } 1992 freeaddrinfo(res); 1993 *strp = p + 1; 1994 return (p); 1995} 1996 1997/** 1998 * Try to get an IPv4 address. This starts with a digit and consists of digits 1999 * and dots, is not longer INET_ADDRSTRLEN and must be parseable by 2000 * inet_aton(). 2001 * 2002 * \param sc client struct to set errors 2003 * \param strp possible start of IPv4 address; updated to point to the 2004 * next character to parse 2005 * 2006 * \return end of address (equals *strp if there is none) or NULL 2007 * on errors 2008 */ 2009static inline const char * 2010get_ipv4(struct snmp_client *sc, const char **strp) 2011{ 2012 const char *p = *strp; 2013 2014 while (isascii(*p) && (isdigit(*p) || *p == '.')) 2015 p++; 2016 2017 if (p - *strp > INET_ADDRSTRLEN) { 2018 seterr(sc, "IPv4 address too long '%.*s'", p - *strp, *strp); 2019 return (NULL); 2020 } 2021 if (*strp == p) 2022 return *strp; 2023 2024 char str[INET_ADDRSTRLEN + 1]; 2025 strncpy(str, *strp, p - *strp); 2026 str[p - *strp] = '\0'; 2027 2028 struct in_addr addr; 2029 if (inet_aton(str, &addr) != 1) { 2030 seterr(sc, "illegal IPv4 address '%s'", str); 2031 return (NULL); 2032 } 2033 2034 *strp = p; 2035 return (p); 2036} 2037 2038/** 2039 * Try to get a hostname. This includes everything up to but not including 2040 * the last colon (if any). There is no length restriction. 2041 * 2042 * \param sc client struct to set errors 2043 * \param strp possible start of hostname; updated to point to the next 2044 * character to parse (the trailing NUL character or the last 2045 * colon) 2046 * 2047 * \return end of address (equals *strp if there is none) 2048 */ 2049static inline const char * 2050get_host(struct snmp_client *sc __unused, const char **strp) 2051{ 2052 const char *p = strrchr(*strp, ':'); 2053 2054 if (p == NULL) { 2055 *strp += strlen(*strp); 2056 return (*strp); 2057 } 2058 2059 *strp = p; 2060 return (p); 2061} 2062 2063/** 2064 * Try to get a port number. This start with a colon and extends to the end 2065 * of string. The port number must not be empty. 2066 * 2067 * \param sc client struct to set errors 2068 * \param strp possible start of port specification; if this points to a 2069 * colon there is a port specification 2070 * 2071 * \return end of port number (equals *strp if there is none); NULL 2072 * if there is no port number 2073 */ 2074static inline const char * 2075get_port(struct snmp_client *sc, const char **strp) 2076{ 2077 if (**strp != ':') 2078 return (*strp + 1); 2079 2080 if ((*strp)[1] == '\0') { 2081 seterr(sc, "empty port name"); 2082 return (NULL); 2083 } 2084 2085 *strp += strlen(*strp); 2086 return (*strp); 2087} 2088 2089/** 2090 * Save the string in the range given by two pointers. 2091 * 2092 * \param sc client struct to set errors 2093 * \param s begin and end pointers 2094 * 2095 * \return freshly allocated copy of the string between s[0] and s[1] 2096 */ 2097static inline char * 2098save_str(struct snmp_client *sc, const char *const s[2]) 2099{ 2100 char *m; 2101 2102 if ((m = malloc(s[1] - s[0] + 1)) == NULL) { 2103 seterr(sc, "%s: %s", __func__, strerror(errno)); 2104 return (NULL); 2105 } 2106 strncpy(m, s[0], s[1] - s[0]); 2107 m[s[1] - s[0]] = '\0'; 2108 2109 return (m); 2110} 2111 2112/** 2113 * Parse a server specification. All parts are optional: 2114 * 2115 * [<trans>::][<comm>@][<host-or-ip>][:<port>] 2116 * 2117 * The transport string consists of letters, digits or '_' and starts with 2118 * a letter or digit. It is terminated by two colons and may not be empty. 2119 * 2120 * The community string is terminated by the last '@' and does not exceed 2121 * SNMP_COMMUNITY_MAXLEN. It may be empty. 2122 * 2123 * The host or ip is either an IPv4 address (as parsed by inet_pton()), an 2124 * IPv6 address in '[' and ']' and parseable by inet_aton() or a hostname 2125 * terminated by the last colon or by the NUL character. 2126 * 2127 * The port number may be specified numerically or symbolically and starts 2128 * with the last colon. 2129 * 2130 * The functions sets the chost, cport, trans, read_community and 2131 * write_community fields on success and the error field on errors. 2132 * The chost and cport fields are allocated by malloc(3), their previous 2133 * content is deallocated by free(3). 2134 * 2135 * The function explicitly allows mismatches between the transport and 2136 * the address type in order to support IPv4 in IPv6 addresses. 2137 * 2138 * \param sc client struct to fill 2139 * \param str string to parse 2140 * 2141 * \return 0 on success and -1 on errors 2142 */ 2143int 2144snmp_parse_server(struct snmp_client *sc, const char *str) 2145{ 2146#if DEBUG_PARSE 2147 const char *const orig = str; 2148#endif 2149 2150 const char *const trans_list[] = { 2151 [SNMP_TRANS_UDP] = "udp", 2152 [SNMP_TRANS_LOC_DGRAM] = "dgram", 2153 [SNMP_TRANS_LOC_STREAM] = "stream", 2154 [SNMP_TRANS_UDP6] = "udp6", 2155 }; 2156 2157 /* parse input */ 2158 const char *const transp[2] = { 2159 str, 2160 get_transp(sc, &str), 2161 }; 2162 if (transp[1] == NULL) 2163 return (-1); 2164 2165 const char *const comm[2] = { 2166 str, 2167 get_comm(sc, &str), 2168 }; 2169 if (comm[1] == NULL) 2170 return (-1); 2171 2172 const char *const ipv6[2] = { 2173 str + 1, 2174 get_ipv6(sc, &str), 2175 }; 2176 if (ipv6[1] == NULL) 2177 return (-1); 2178 2179 const char *ipv4[2] = { 2180 str, 2181 str, 2182 }; 2183 2184 const char *host[2] = { 2185 str, 2186 str, 2187 }; 2188 2189 if (ipv6[0] == ipv6[1]) { 2190 ipv4[1] = get_ipv4(sc, &str); 2191 2192 if (ipv4[0] == ipv4[1]) 2193 host[1] = get_host(sc, &str); 2194 } 2195 2196 const char *port[2] = { 2197 str + 1, 2198 get_port(sc, &str), 2199 }; 2200 if (port[1] == NULL) 2201 return (-1); 2202 2203 if (*str != '\0') { 2204 seterr(sc, "junk at end of server specification '%s'", str); 2205 return (-1); 2206 } 2207 2208#if DEBUG_PARSE 2209 printf("transp: %zu %zu\n", transp[0] - orig, transp[1] - orig); 2210 printf("comm: %zu %zu\n", comm[0] - orig, comm[1] - orig); 2211 printf("ipv6: %zu %zu\n", ipv6[0] - orig, ipv6[1] - orig); 2212 printf("ipv4: %zu %zu\n", ipv4[0] - orig, ipv4[1] - orig); 2213 printf("host: %zu %zu\n", host[0] - orig, host[1] - orig); 2214 printf("port: %zu %zu\n", port[0] - orig, port[1] - orig); 2215#endif 2216 2217 /* analyse and allocate */ 2218 int i = -1; 2219 if (transp[0] != transp[1]) { 2220 for (i = 0; i < (int)nitems(trans_list); i++) { 2221 if (trans_list[i] != NULL && 2222 strlen(trans_list[i]) == (size_t)(transp[1] - 2223 transp[0]) && !strncmp(trans_list[i], transp[0], 2224 transp[1] - transp[0])) 2225 break; |
1947 } | 2226 } |
1948 /* port */ 1949 free(sc->cport); 1950 if ((sc->cport = strdup(p + 1)) == NULL) { 1951 seterr(sc, "%s", strerror(errno)); | 2227 2228 if (i == (int)nitems(trans_list)) { 2229 seterr(sc, "unknown transport specifier '%.*s'", 2230 transp[1] - transp[0], transp[0]); |
1952 return (-1); 1953 } | 2231 return (-1); 2232 } |
2233 } |
|
1954 | 2234 |
1955 } else if (p > s) { 1956 /* host */ 1957 free(sc->chost); 1958 if ((sc->chost = strdup(s)) == NULL) { 1959 seterr(sc, "%s", strerror(errno)); | 2235 char *chost; 2236 2237 if (ipv6[0] != ipv6[1]) { 2238 if ((chost = save_str(sc, ipv6)) == NULL) |
1960 return (-1); | 2239 return (-1); |
2240 if (i == -1) 2241 i = SNMP_TRANS_UDP6; 2242 } else if (ipv4[0] != ipv4[1]) { 2243 if ((chost = save_str(sc, ipv4)) == NULL) 2244 return (-1); 2245 if (i == -1) 2246 i = SNMP_TRANS_UDP; 2247 } else { 2248 if ((chost = save_str(sc, host)) == NULL) 2249 return (-1); 2250 2251 if (i == -1) { 2252 /* Default transport is UDP unless the host contains 2253 * a slash in which case we default to DGRAM. */ 2254 i = SNMP_TRANS_UDP; 2255 for (const char *p = host[0]; p < host[1]; p++) 2256 if (*p == '/') { 2257 i = SNMP_TRANS_LOC_DGRAM; 2258 break; 2259 } |
|
1961 } 1962 } | 2260 } 2261 } |
2262 2263 char *cport = save_str(sc, port); 2264 if (cport == NULL) { 2265 free(chost); 2266 return (-1); 2267 } 2268 2269 /* commit */ 2270 sc->trans = i; 2271 2272 strncpy(sc->read_community, comm[0], comm[1] - comm[0]); 2273 sc->read_community[comm[1] - comm[0]] = '\0'; 2274 strncpy(sc->write_community, comm[0], comm[1] - comm[0]); 2275 sc->write_community[comm[1] - comm[0]] = '\0'; 2276 2277 free(sc->chost); 2278 sc->chost = chost; 2279 free(sc->cport); 2280 sc->cport = cport; 2281 |
|
1963 return (0); 1964} | 2282 return (0); 2283} |