net-snmp
5.4.1
|
00001 /* Portions of this file are subject to the following copyright(s). See 00002 * the Net-SNMP's COPYING file for more details and other copyrights 00003 * that may apply: 00004 */ 00005 /* 00006 * Portions of this file are copyrighted by: 00007 * Copyright Copyright 2003 Sun Microsystems, Inc. All rights reserved. 00008 * Use is subject to license terms specified in the COPYING file 00009 * distributed with the Net-SNMP package. 00010 */ 00011 00012 #include <net-snmp/net-snmp-config.h> 00013 00014 #include <stdio.h> 00015 #include <sys/types.h> 00016 #include <ctype.h> 00017 #include <errno.h> 00018 00019 #if HAVE_STRING_H 00020 #include <string.h> 00021 #else 00022 #include <strings.h> 00023 #endif 00024 #if HAVE_STDLIB_H 00025 #include <stdlib.h> 00026 #endif 00027 #if HAVE_UNISTD_H 00028 #include <unistd.h> 00029 #endif 00030 #if HAVE_SYS_SOCKET_H 00031 #include <sys/socket.h> 00032 #endif 00033 #if HAVE_NETINET_IN_H 00034 #include <netinet/in.h> 00035 #endif 00036 #if HAVE_ARPA_INET_H 00037 #include <arpa/inet.h> 00038 #endif 00039 #if HAVE_NETDB_H 00040 #include <netdb.h> 00041 #endif 00042 #if HAVE_SYS_UIO_H 00043 #include <sys/uio.h> 00044 #endif 00045 00046 #if HAVE_WINSOCK_H 00047 #include <winsock2.h> 00048 #include <ws2tcpip.h> 00049 #endif 00050 00051 #if HAVE_DMALLOC_H 00052 #include <dmalloc.h> 00053 #endif 00054 00055 #include <net-snmp/types.h> 00056 #include <net-snmp/output_api.h> 00057 #include <net-snmp/config_api.h> 00058 00059 #include <net-snmp/library/snmp_transport.h> 00060 #include <net-snmp/library/snmpUDPDomain.h> 00061 #include <net-snmp/library/system.h> 00062 #include <net-snmp/library/tools.h> 00063 00064 #ifndef INADDR_NONE 00065 #define INADDR_NONE -1 00066 #endif 00067 00068 static netsnmp_tdomain udpDomain; 00069 00070 typedef struct netsnmp_udp_addr_pair_s { 00071 struct sockaddr_in remote_addr; 00072 struct in_addr local_addr; 00073 } netsnmp_udp_addr_pair; 00074 00075 /* 00076 * not static, since snmpUDPIPv6Domain needs it, but not public, either. 00077 * (ie don't put it in a public header.) 00078 */ 00079 void _netsnmp_udp_sockopt_set(int fd, int server); 00080 int 00081 netsnmp_sockaddr_in2(struct sockaddr_in *addr, 00082 const char *inpeername, const char *default_target); 00083 00084 /* 00085 * Return a string representing the address in data, or else the "far end" 00086 * address if data is NULL. 00087 */ 00088 00089 static char * 00090 netsnmp_udp_fmtaddr(netsnmp_transport *t, void *data, int len) 00091 { 00092 netsnmp_udp_addr_pair *addr_pair = NULL; 00093 00094 if (data != NULL && len == sizeof(netsnmp_udp_addr_pair)) { 00095 addr_pair = (netsnmp_udp_addr_pair *) data; 00096 } else if (t != NULL && t->data != NULL) { 00097 addr_pair = (netsnmp_udp_addr_pair *) t->data; 00098 } 00099 00100 if (addr_pair == NULL) { 00101 return strdup("UDP: unknown"); 00102 } else { 00103 struct sockaddr_in *to = NULL; 00104 char tmp[64]; 00105 to = (struct sockaddr_in *) &(addr_pair->remote_addr); 00106 if (to == NULL) { 00107 return strdup("UDP: unknown"); 00108 } 00109 00110 sprintf(tmp, "UDP: [%s]:%hu", 00111 inet_ntoa(to->sin_addr), ntohs(to->sin_port)); 00112 return strdup(tmp); 00113 } 00114 } 00115 00116 00117 00118 #if defined(linux) && defined(IP_PKTINFO) 00119 00120 # define netsnmp_dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr)) 00121 00122 static int netsnmp_udp_recvfrom(int s, void *buf, int len, struct sockaddr *from, socklen_t *fromlen, struct in_addr *dstip) 00123 { 00124 int r; 00125 struct iovec iov[1]; 00126 char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; 00127 struct cmsghdr *cmsgptr; 00128 struct msghdr msg; 00129 00130 iov[0].iov_base = buf; 00131 iov[0].iov_len = len; 00132 00133 memset(&msg, 0, sizeof msg); 00134 msg.msg_name = from; 00135 msg.msg_namelen = *fromlen; 00136 msg.msg_iov = iov; 00137 msg.msg_iovlen = 1; 00138 msg.msg_control = &cmsg; 00139 msg.msg_controllen = sizeof(cmsg); 00140 00141 r = recvmsg(s, &msg, 0); 00142 00143 if (r == -1) { 00144 return -1; 00145 } 00146 00147 DEBUGMSGTL(("netsnmp_udp", "got source addr: %s\n", inet_ntoa(((struct sockaddr_in *)from)->sin_addr))); 00148 for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { 00149 if (cmsgptr->cmsg_level == SOL_IP && cmsgptr->cmsg_type == IP_PKTINFO) { 00150 memcpy((void *) dstip, netsnmp_dstaddr(cmsgptr), sizeof(struct in_addr)); 00151 DEBUGMSGTL(("netsnmp_udp", "got destination (local) addr %s\n", 00152 inet_ntoa(*dstip))); 00153 } 00154 } 00155 return r; 00156 } 00157 00158 static int netsnmp_udp_sendto(int fd, struct in_addr *srcip, struct sockaddr *remote, 00159 void *data, int len) 00160 { 00161 struct iovec iov = { data, len }; 00162 struct { 00163 struct cmsghdr cm; 00164 struct in_pktinfo ipi; 00165 } cmsg; 00166 struct msghdr m; 00167 00168 cmsg.cm.cmsg_len = sizeof(struct cmsghdr) + sizeof(struct in_pktinfo); 00169 cmsg.cm.cmsg_level = SOL_IP; 00170 cmsg.cm.cmsg_type = IP_PKTINFO; 00171 cmsg.ipi.ipi_ifindex = 0; 00172 cmsg.ipi.ipi_spec_dst.s_addr = (srcip ? srcip->s_addr : INADDR_ANY); 00173 00174 m.msg_name = remote; 00175 m.msg_namelen = sizeof(struct sockaddr_in); 00176 m.msg_iov = &iov; 00177 m.msg_iovlen = 1; 00178 m.msg_control = &cmsg; 00179 m.msg_controllen = sizeof(cmsg); 00180 m.msg_flags = 0; 00181 00182 return sendmsg(fd, &m, MSG_NOSIGNAL|MSG_DONTWAIT); 00183 } 00184 #endif /* linux && IP_PKTINFO */ 00185 00186 /* 00187 * You can write something into opaque that will subsequently get passed back 00188 * to your send function if you like. For instance, you might want to 00189 * remember where a PDU came from, so that you can send a reply there... 00190 */ 00191 00192 static int 00193 netsnmp_udp_recv(netsnmp_transport *t, void *buf, int size, 00194 void **opaque, int *olength) 00195 { 00196 int rc = -1; 00197 socklen_t fromlen = sizeof(struct sockaddr); 00198 netsnmp_udp_addr_pair *addr_pair = NULL; 00199 struct sockaddr *from; 00200 00201 if (t != NULL && t->sock >= 0) { 00202 addr_pair = (netsnmp_udp_addr_pair *) malloc(sizeof(netsnmp_udp_addr_pair)); 00203 if (addr_pair == NULL) { 00204 *opaque = NULL; 00205 *olength = 0; 00206 return -1; 00207 } else { 00208 memset(addr_pair, 0, sizeof(netsnmp_udp_addr_pair)); 00209 from = (struct sockaddr *) &(addr_pair->remote_addr); 00210 } 00211 00212 while (rc < 0) { 00213 #if defined(linux) && defined(IP_PKTINFO) 00214 rc = netsnmp_udp_recvfrom(t->sock, buf, size, from, &fromlen, &(addr_pair->local_addr)); 00215 #else 00216 rc = recvfrom(t->sock, buf, size, 0, from, &fromlen); 00217 #endif /* linux && IP_PKTINFO */ 00218 if (rc < 0 && errno != EINTR) { 00219 break; 00220 } 00221 } 00222 00223 if (rc >= 0) { 00224 char *str = netsnmp_udp_fmtaddr(NULL, addr_pair, sizeof(netsnmp_udp_addr_pair)); 00225 DEBUGMSGTL(("netsnmp_udp", 00226 "recvfrom fd %d got %d bytes (from %s)\n", 00227 t->sock, rc, str)); 00228 free(str); 00229 } else { 00230 DEBUGMSGTL(("netsnmp_udp", "recvfrom fd %d err %d (\"%s\")\n", 00231 t->sock, errno, strerror(errno))); 00232 } 00233 *opaque = (void *)addr_pair; 00234 *olength = sizeof(netsnmp_udp_addr_pair); 00235 } 00236 return rc; 00237 } 00238 00239 00240 00241 static int 00242 netsnmp_udp_send(netsnmp_transport *t, void *buf, int size, 00243 void **opaque, int *olength) 00244 { 00245 int rc = -1; 00246 netsnmp_udp_addr_pair *addr_pair = NULL; 00247 struct sockaddr *to = NULL; 00248 00249 if (opaque != NULL && *opaque != NULL && 00250 *olength == sizeof(netsnmp_udp_addr_pair)) { 00251 addr_pair = (netsnmp_udp_addr_pair *) (*opaque); 00252 } else if (t != NULL && t->data != NULL && 00253 t->data_length == sizeof(netsnmp_udp_addr_pair)) { 00254 addr_pair = (netsnmp_udp_addr_pair *) (t->data); 00255 } 00256 00257 to = (struct sockaddr *) &(addr_pair->remote_addr); 00258 00259 if (to != NULL && t != NULL && t->sock >= 0) { 00260 char *str = netsnmp_udp_fmtaddr(NULL, (void *) addr_pair, 00261 sizeof(netsnmp_udp_addr_pair)); 00262 DEBUGMSGTL(("netsnmp_udp", "send %d bytes from %p to %s on fd %d\n", 00263 size, buf, str, t->sock)); 00264 free(str); 00265 while (rc < 0) { 00266 #if defined(linux) && defined(IP_PKTINFO) 00267 rc = netsnmp_udp_sendto(t->sock, addr_pair ? &(addr_pair->local_addr) : NULL, to, buf, size); 00268 #else 00269 rc = sendto(t->sock, buf, size, 0, to, sizeof(struct sockaddr)); 00270 #endif /* linux && IP_PKTINFO */ 00271 if (rc < 0 && errno != EINTR) { 00272 DEBUGMSGTL(("netsnmp_udp", "sendto error, rc %d (errno %d)\n", 00273 rc, errno)); 00274 break; 00275 } 00276 } 00277 } 00278 return rc; 00279 } 00280 00281 00282 00283 static int 00284 netsnmp_udp_close(netsnmp_transport *t) 00285 { 00286 int rc = -1; 00287 if (t->sock >= 0) { 00288 #ifndef HAVE_CLOSESOCKET 00289 rc = close(t->sock); 00290 #else 00291 rc = closesocket(t->sock); 00292 #endif 00293 t->sock = -1; 00294 } 00295 return rc; 00296 } 00297 00298 /* 00299 * find largest possible buffer between current size and specified size. 00300 * 00301 * Try to maximize the current buffer of type "optname" 00302 * to the maximum allowable size by the OS (as close to 00303 * size as possible) 00304 */ 00305 static int 00306 _sock_buffer_maximize(int s, int optname, const char *buftype, int size) 00307 { 00308 int curbuf = 0; 00309 size_t curbuflen = sizeof(int); 00310 int lo, mid, hi; 00311 00312 /* 00313 * First we need to determine our current buffer 00314 */ 00315 if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf, 00316 &curbuflen) == 0) 00317 && (curbuflen == sizeof(int))) { 00318 00319 DEBUGMSGTL(("verbose:socket:buffer:max", "Current %s is %d\n", 00320 buftype, curbuf)); 00321 00322 /* 00323 * Let's not be stupid ... if we were asked for less than what we 00324 * already have, then forget about it 00325 */ 00326 if (size <= curbuf) { 00327 DEBUGMSGTL(("verbose:socket:buffer:max", 00328 "Requested %s <= current buffer\n", buftype)); 00329 return curbuf; 00330 } 00331 00332 /* 00333 * Do a binary search the optimal buffer within 1k of the point of 00334 * failure. This is rather bruteforce, but simple 00335 */ 00336 hi = size; 00337 lo = curbuf; 00338 00339 while (hi - lo > 1024) { 00340 mid = (lo + hi) / 2; 00341 if (setsockopt(s, SOL_SOCKET, optname, (void *) &mid, 00342 sizeof(int)) == 0) { 00343 lo = mid; /* Success: search between mid and hi */ 00344 } else { 00345 hi = mid; /* Failed: search between lo and mid */ 00346 } 00347 } 00348 00349 /* 00350 * Now print if this optimization helped or not 00351 */ 00352 if (getsockopt(s,SOL_SOCKET, optname, (void *) &curbuf, 00353 &curbuflen) == 0) { 00354 DEBUGMSGTL(("socket:buffer:max", 00355 "Maximized %s: %d\n",buftype, curbuf)); 00356 } 00357 } else { 00358 /* 00359 * There is really not a lot we can do anymore. 00360 * If the OS doesn't give us the current buffer, then what's the 00361 * point in trying to make it better 00362 */ 00363 DEBUGMSGTL(("socket:buffer:max", "Get %s failed ... giving up!\n", 00364 buftype)); 00365 curbuf = -1; 00366 } 00367 00368 return curbuf; 00369 } 00370 00371 00372 static const char * 00373 _sock_buf_type_get(int optname, int local) 00374 { 00375 if (optname == SO_SNDBUF) { 00376 if (local) 00377 return "server send buffer"; 00378 else 00379 return "client send buffer"; 00380 } else if (optname == SO_RCVBUF) { 00381 if (local) 00382 return "server receive buffer"; 00383 else 00384 return "client receive buffer"; 00385 } 00386 00387 return "unknown buffer"; 00388 } 00389 00390 /* 00391 * 00392 * Get the requested buffersize, based on 00393 * - sockettype : client (local = 0) or server (local = 1) 00394 * - buffertype : send (optname = SO_SNDBUF) or recv (SO_RCVBUF) 00395 * 00396 * In case a compile time buffer was specified, then use that one 00397 * if there was no runtime configuration override 00398 */ 00399 static int 00400 _sock_buffer_size_get(int optname, int local, const char **buftype) 00401 { 00402 int size; 00403 00404 if (NULL != buftype) 00405 *buftype = _sock_buf_type_get(optname, local); 00406 00407 if (optname == SO_SNDBUF) { 00408 if (local) { 00409 size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 00410 NETSNMP_DS_LIB_SERVERSENDBUF); 00411 #ifdef NETSNMP_DEFAULT_SERVER_SEND_BUF 00412 if (size <= 0) 00413 size = NETSNMP_DEFAULT_SERVER_SEND_BUF; 00414 #endif 00415 } else { 00416 size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 00417 NETSNMP_DS_LIB_CLIENTSENDBUF); 00418 #ifdef NETSNMP_DEFAULT_CLIENT_SEND_BUF 00419 if (size <= 0) 00420 size = NETSNMP_DEFAULT_CLIENT_SEND_BUF; 00421 #endif 00422 } 00423 } else if (optname == SO_RCVBUF) { 00424 if (local) { 00425 size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 00426 NETSNMP_DS_LIB_SERVERRECVBUF); 00427 #ifdef NETSNMP_DEFAULT_SERVER_RECV_BUF 00428 if (size <= 0) 00429 size = NETSNMP_DEFAULT_SERVER_RECV_BUF; 00430 #endif 00431 } else { 00432 size = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 00433 NETSNMP_DS_LIB_CLIENTRECVBUF); 00434 #ifdef NETSNMP_DEFAULT_CLIENT_RECV_BUF 00435 if (size <= 0) 00436 size = NETSNMP_DEFAULT_CLIENT_RECV_BUF; 00437 #endif 00438 } 00439 } else { 00440 size = 0; 00441 } 00442 00443 DEBUGMSGTL(("socket:buffer", "Requested %s is %d\n", 00444 (buftype) ? *buftype : "unknown buffer", size)); 00445 00446 return(size); 00447 } 00448 00449 /* 00450 * set socket buffer size 00451 * 00452 * @param ss : socket 00453 * @param optname: SO_SNDBUF or SO_RCVBUF 00454 * @param local : 1 for server, 0 for client 00455 * @param reqbuf : requested size, or 0 for default 00456 * 00457 * @retval -1 : error 00458 * @retval >0 : new buffer size 00459 */ 00460 int 00461 netsnmp_sock_buffer_set(int s, int optname, int local, int size) 00462 { 00463 #if ! defined(SO_SNDBUF) && ! defined(SO_RCVBUF) 00464 DEBUGMSGTL(("socket:buffer", "Changing socket buffer is not supported\n")); 00465 return -1; 00466 #else 00467 const char *buftype; 00468 int curbuf = 0; 00469 size_t curbuflen = sizeof(int); 00470 00471 # ifndef SO_SNDBUF 00472 if (SO_SNDBUF == optname) { 00473 DEBUGMSGTL(("socket:buffer", 00474 "Changing socket send buffer is not supported\n")); 00475 return -1; 00476 } 00477 # endif /*SO_SNDBUF */ 00478 # ifndef SO_RCVBUF 00479 if (SO_RCVBUF == optname) { 00480 DEBUGMSGTL(("socket:buffer", 00481 "Changing socket receive buffer is not supported\n")); 00482 return -1; 00483 } 00484 # endif /*SO_RCVBUF */ 00485 00486 /* 00487 * What is the requested buffer size ? 00488 */ 00489 if (0 == size) 00490 size = _sock_buffer_size_get(optname, local, &buftype); 00491 else { 00492 buftype = _sock_buf_type_get(optname, local); 00493 DEBUGMSGT(("verbose:socket:buffer", "Requested %s is %d\n", 00494 buftype, size)); 00495 } 00496 00497 if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf, 00498 &curbuflen) == 0) 00499 && (curbuflen == sizeof(int))) { 00500 00501 DEBUGMSGT(("verbose:socket:buffer", "Original %s is %d\n", 00502 buftype, curbuf)); 00503 if (curbuf >= size) { 00504 DEBUGMSGT(("verbose:socket:buffer", 00505 "New %s size is smaller than original!\n", buftype)); 00506 } 00507 } 00508 00509 /* 00510 * If the buffersize was not specified or it was a negative value 00511 * then don't change the OS buffers at all 00512 */ 00513 if (size <= 0) { 00514 DEBUGMSGT(("socket:buffer", 00515 "%s not valid or not specified; using OS default(%d)\n", 00516 buftype,curbuf)); 00517 return curbuf; 00518 } 00519 00520 /* 00521 * Try to set the requested send buffer 00522 */ 00523 if (setsockopt(s, SOL_SOCKET, optname, (void *) &size, sizeof(int)) == 0) { 00524 /* 00525 * Because some platforms lie about the actual buffer that has been 00526 * set (Linux will always say it worked ...), we print some 00527 * diagnostic output for debugging 00528 */ 00529 DEBUGIF("socket:buffer") { 00530 DEBUGMSGT(("socket:buffer", "Set %s to %d\n", 00531 buftype, size)); 00532 if ((getsockopt(s, SOL_SOCKET, optname, (void *) &curbuf, 00533 &curbuflen) == 0) 00534 && (curbuflen == sizeof(int))) { 00535 00536 DEBUGMSGT(("verbose:socket:buffer", 00537 "Now %s is %d\n", buftype, curbuf)); 00538 } 00539 } 00540 /* 00541 * If the new buffer is smaller than the size we requested, we will 00542 * try to increment the new buffer with 1k increments 00543 * (this will sometime allow us to reach a more optimal buffer.) 00544 * For example : On Solaris, if the max OS buffer is 100k and you 00545 * request 110k, you end up with the default 8k :-( 00546 */ 00547 if (curbuf < size) { 00548 curbuf = _sock_buffer_maximize(s, optname, buftype, size); 00549 if(-1 != curbuf) 00550 size = curbuf; 00551 } 00552 00553 } else { 00554 /* 00555 * Obviously changing the buffer failed, most like like because we 00556 * requested a buffer greater than the OS limit. 00557 * Therefore we need to search for an optimal buffer that is close 00558 * enough to the point of failure. 00559 * This will allow us to reach a more optimal buffer. 00560 * For example : On Solaris, if the max OS buffer is 100k and you 00561 * request 110k, you end up with the default 8k :-( 00562 * After this quick seach we would get 1k close to 100k (the max) 00563 */ 00564 DEBUGMSGTL(("socket:buffer", "couldn't set %s to %d\n", 00565 buftype, size)); 00566 00567 curbuf = _sock_buffer_maximize(s, optname, buftype, size); 00568 if(-1 != curbuf) 00569 size = curbuf; 00570 } 00571 00572 return size; 00573 #endif 00574 } 00575 00576 /* 00577 * Open a UDP-based transport for SNMP. Local is TRUE if addr is the local 00578 * address to bind to (i.e. this is a server-type session); otherwise addr is 00579 * the remote address to send things to. 00580 */ 00581 00582 netsnmp_transport * 00583 netsnmp_udp_transport(struct sockaddr_in *addr, int local) 00584 { 00585 netsnmp_transport *t = NULL; 00586 int rc = 0; 00587 char *str = NULL; 00588 char *client_socket = NULL; 00589 netsnmp_udp_addr_pair addr_pair; 00590 00591 if (addr == NULL || addr->sin_family != AF_INET) { 00592 return NULL; 00593 } 00594 00595 memset(&addr_pair, 0, sizeof(netsnmp_udp_addr_pair)); 00596 memcpy(&(addr_pair.remote_addr), addr, sizeof(struct sockaddr_in)); 00597 00598 t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport)); 00599 if (t == NULL) { 00600 return NULL; 00601 } 00602 00603 str = netsnmp_udp_fmtaddr(NULL, (void *)&addr_pair, 00604 sizeof(netsnmp_udp_addr_pair)); 00605 DEBUGMSGTL(("netsnmp_udp", "open %s %s\n", local ? "local" : "remote", 00606 str)); 00607 free(str); 00608 00609 memset(t, 0, sizeof(netsnmp_transport)); 00610 00611 t->domain = netsnmpUDPDomain; 00612 t->domain_length = netsnmpUDPDomain_len; 00613 00614 t->sock = socket(PF_INET, SOCK_DGRAM, 0); 00615 if (t->sock < 0) { 00616 netsnmp_transport_free(t); 00617 return NULL; 00618 } 00619 00620 _netsnmp_udp_sockopt_set(t->sock, local); 00621 00622 if (local) { 00623 /* 00624 * This session is inteneded as a server, so we must bind on to the 00625 * given IP address, which may include an interface address, or could 00626 * be INADDR_ANY, but certainly includes a port number. 00627 */ 00628 00629 t->local = (u_char *) malloc(6); 00630 if (t->local == NULL) { 00631 netsnmp_transport_free(t); 00632 return NULL; 00633 } 00634 memcpy(t->local, (u_char *) & (addr->sin_addr.s_addr), 4); 00635 t->local[4] = (htons(addr->sin_port) & 0xff00) >> 8; 00636 t->local[5] = (htons(addr->sin_port) & 0x00ff) >> 0; 00637 t->local_length = 6; 00638 00639 #if defined(linux) && defined(IP_PKTINFO) 00640 { 00641 int sockopt = 1; 00642 if (setsockopt(t->sock, SOL_IP, IP_PKTINFO, &sockopt, sizeof sockopt) == -1) { 00643 DEBUGMSGTL(("netsnmp_udp", "couldn't set IP_PKTINFO: %s\n", 00644 strerror(errno))); 00645 return NULL; 00646 } 00647 DEBUGMSGTL(("netsnmp_udp", "set IP_PKTINFO\n")); 00648 } 00649 #endif 00650 rc = bind(t->sock, (struct sockaddr *) addr, 00651 sizeof(struct sockaddr)); 00652 if (rc != 0) { 00653 netsnmp_udp_close(t); 00654 netsnmp_transport_free(t); 00655 return NULL; 00656 } 00657 t->data = NULL; 00658 t->data_length = 0; 00659 } else { 00660 /* 00661 * This is a client session. If we've been given a 00662 * client address to send from, then bind to that. 00663 * Otherwise the send will use "something sensible". 00664 */ 00665 client_socket = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 00666 NETSNMP_DS_LIB_CLIENT_ADDR); 00667 if (client_socket) { 00668 struct sockaddr_in client_addr; 00669 netsnmp_sockaddr_in2(&client_addr, client_socket, NULL); 00670 client_addr.sin_port = 0; 00671 bind(t->sock, (struct sockaddr *)&client_addr, 00672 sizeof(struct sockaddr)); 00673 } 00674 /* 00675 * Save the (remote) address in the 00676 * transport-specific data pointer for later use by netsnmp_udp_send. 00677 */ 00678 00679 t->data = malloc(sizeof(netsnmp_udp_addr_pair)); 00680 t->remote = (u_char *)malloc(6); 00681 if (t->data == NULL || t->remote == NULL) { 00682 netsnmp_transport_free(t); 00683 return NULL; 00684 } 00685 memcpy(t->remote, (u_char *) & (addr->sin_addr.s_addr), 4); 00686 t->remote[4] = (htons(addr->sin_port) & 0xff00) >> 8; 00687 t->remote[5] = (htons(addr->sin_port) & 0x00ff) >> 0; 00688 t->remote_length = 6; 00689 memcpy(t->data, &addr_pair, sizeof(netsnmp_udp_addr_pair)); 00690 t->data_length = sizeof(netsnmp_udp_addr_pair); 00691 } 00692 00693 /* 00694 * 16-bit length field, 8 byte UDP header, 20 byte IPv4 header 00695 */ 00696 00697 t->msgMaxSize = 0xffff - 8 - 20; 00698 t->f_recv = netsnmp_udp_recv; 00699 t->f_send = netsnmp_udp_send; 00700 t->f_close = netsnmp_udp_close; 00701 t->f_accept = NULL; 00702 t->f_fmtaddr = netsnmp_udp_fmtaddr; 00703 00704 return t; 00705 } 00706 00707 00708 void 00709 _netsnmp_udp_sockopt_set(int fd, int local) 00710 { 00711 #ifdef SO_BSDCOMPAT 00712 /* 00713 * Patch for Linux. Without this, UDP packets that fail get an ICMP 00714 * response. Linux turns the failed ICMP response into an error message 00715 * and return value, unlike all other OS's. 00716 */ 00717 if (0 == netsnmp_os_prematch("Linux","2.4")) 00718 { 00719 int one = 1; 00720 DEBUGMSGTL(("socket:option", "setting socket option SO_BSDCOMPAT\n")); 00721 setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (void *) &one, 00722 sizeof(one)); 00723 } 00724 #endif /*SO_BSDCOMPAT */ 00725 /* 00726 * SO_REUSEADDR will allow multiple apps to open the same port at 00727 * the same time. Only the last one to open the socket will get 00728 * data. Obviously, for an agent, this is a bad thing. There should 00729 * only be one listener. 00730 */ 00731 #ifdef ALLOW_PORT_HIJACKING 00732 #ifdef SO_REUSEADDR 00733 /* 00734 * Allow the same port to be specified multiple times without failing. 00735 * (useful for a listener) 00736 */ 00737 { 00738 int one = 1; 00739 DEBUGMSGTL(("socket:option", "setting socket option SO_REUSEADDR\n")); 00740 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, 00741 sizeof(one)); 00742 } 00743 #endif /*SO_REUSEADDR */ 00744 #endif 00745 00746 /* 00747 * Try to set the send and receive buffers to a reasonably large value, so 00748 * that we can send and receive big PDUs (defaults to 8192 bytes (!) on 00749 * Solaris, for instance). Don't worry too much about errors -- just 00750 * plough on regardless. 00751 */ 00752 netsnmp_sock_buffer_set(fd, SO_SNDBUF, local, 0); 00753 netsnmp_sock_buffer_set(fd, SO_RCVBUF, local, 0); 00754 } 00755 00756 int 00757 netsnmp_sockaddr_in2(struct sockaddr_in *addr, 00758 const char *inpeername, const char *default_target) 00759 { 00760 #if HAVE_GETADDRINFO 00761 struct addrinfo *addrs = NULL; 00762 struct addrinfo hint; 00763 int err; 00764 #elif HAVE_GETIPNODEBYNAME 00765 struct hostent *hp = NULL; 00766 int err; 00767 #elif HAVE_GETHOSTBYNAME 00768 struct hostent *hp = NULL; 00769 #endif 00770 00771 if (addr == NULL) { 00772 return 0; 00773 } 00774 00775 DEBUGMSGTL(("netsnmp_sockaddr_in", 00776 "addr %p, inpeername \"%s\", default_target \"%s\"\n", 00777 addr, inpeername ? inpeername : "[NIL]", 00778 default_target ? default_target : "[NIL]")); 00779 00780 memset(addr, 0, sizeof(struct sockaddr_in)); 00781 addr->sin_addr.s_addr = htonl(INADDR_ANY); 00782 addr->sin_family = AF_INET; 00783 addr->sin_port = htons((u_short)SNMP_PORT); 00784 00785 { 00786 int port = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 00787 NETSNMP_DS_LIB_DEFAULT_PORT); 00788 00789 if (port != 0) { 00790 addr->sin_port = htons((u_short)port); 00791 } else if (default_target != NULL) 00792 netsnmp_sockaddr_in2(addr, default_target, NULL); 00793 } 00794 00795 if (inpeername != NULL && *inpeername != '\0') { 00796 const char *host, *port; 00797 char *peername = NULL; 00798 char *cp; 00799 /* 00800 * Duplicate the peername because we might want to mank around with 00801 * it. 00802 */ 00803 00804 peername = strdup(inpeername); 00805 if (peername == NULL) { 00806 return 0; 00807 } 00808 00809 /* 00810 * Try and extract an appended port number. 00811 */ 00812 cp = strchr(peername, ':'); 00813 if (cp != NULL) { 00814 *cp = '\0'; 00815 port = cp + 1; 00816 host = peername; 00817 } else { 00818 host = NULL; 00819 port = peername; 00820 } 00821 00822 /* 00823 * Try to convert the user port specifier 00824 */ 00825 if (port && *port == '\0') 00826 port = NULL; 00827 00828 if (port != NULL) { 00829 long int l; 00830 char* ep; 00831 00832 DEBUGMSGTL(("netsnmp_sockaddr_in", "check user service %s\n", 00833 port)); 00834 00835 l = strtol(port, &ep, 10); 00836 if (ep != port && *ep == '\0' && 0 <= l && l <= 0x0ffff) 00837 addr->sin_port = htons((u_short)l); 00838 else { 00839 if (host == NULL) { 00840 DEBUGMSGTL(("netsnmp_sockaddr_in", 00841 "servname not numeric, " 00842 "check if it really is a destination)")); 00843 host = port; 00844 port = NULL; 00845 } else { 00846 DEBUGMSGTL(("netsnmp_sockaddr_in", 00847 "servname not numeric")); 00848 free(peername); 00849 return 0; 00850 } 00851 } 00852 } 00853 00854 /* 00855 * Try to convert the user host specifier 00856 */ 00857 if (host && *host == '\0') 00858 host = NULL; 00859 00860 if (host != NULL) { 00861 DEBUGMSGTL(("netsnmp_sockaddr_in", 00862 "check destination %s\n", host)); 00863 00864 #if HAVE_GETADDRINFO 00865 memset(&hint, 0, sizeof hint); 00866 hint.ai_flags = 0; 00867 hint.ai_family = PF_INET; 00868 hint.ai_socktype = SOCK_DGRAM; 00869 hint.ai_protocol = 0; 00870 00871 err = getaddrinfo(peername, NULL, &hint, &addrs); 00872 if (err != 0) { 00873 #if HAVE_GAI_STRERROR 00874 snmp_log(LOG_ERR, "getaddrinfo: %s %s\n", peername, 00875 gai_strerror(err)); 00876 #else 00877 snmp_log(LOG_ERR, "getaddrinfo: %s (error %d)\n", peername, 00878 err); 00879 #endif 00880 free(peername); 00881 return 0; 00882 } 00883 if (addrs != NULL) { 00884 DEBUGMSGTL(("netsnmp_sockaddr_in", 00885 "hostname (resolved okay)\n")); 00886 memcpy(&addr->sin_addr, 00887 &((struct sockaddr_in *) addrs->ai_addr)->sin_addr, 00888 sizeof(struct in_addr)); 00889 freeaddrinfo(addrs); 00890 } 00891 else { 00892 DEBUGMSGTL(("netsnmp_sockaddr_in", 00893 "Failed to resolve IPv4 hostname\n")); 00894 } 00895 #elif HAVE_GETHOSTBYNAME 00896 hp = gethostbyname(host); 00897 if (hp == NULL) { 00898 DEBUGMSGTL(("netsnmp_sockaddr_in", 00899 "hostname (couldn't resolve)\n")); 00900 free(peername); 00901 return 0; 00902 } else if (hp->h_addrtype != AF_INET) { 00903 DEBUGMSGTL(("netsnmp_sockaddr_in", 00904 "hostname (not AF_INET!)\n")); 00905 free(peername); 00906 return 0; 00907 } else { 00908 DEBUGMSGTL(("netsnmp_sockaddr_in", 00909 "hostname (resolved okay)\n")); 00910 memcpy(&addr->sin_addr, hp->h_addr, hp->h_length); 00911 } 00912 #elif HAVE_GETIPNODEBYNAME 00913 hp = getipnodebyname(peername, AF_INET, 0, &err); 00914 if (hp == NULL) { 00915 DEBUGMSGTL(("netsnmp_sockaddr_in", 00916 "hostname (couldn't resolve = %d)\n", err)); 00917 free(peername); 00918 return 0; 00919 } 00920 DEBUGMSGTL(("netsnmp_sockaddr_in", 00921 "hostname (resolved okay)\n")); 00922 memcpy(&(addr->sin_addr), hp->h_addr, hp->h_length); 00923 #else /* HAVE_GETIPNODEBYNAME */ 00924 /* 00925 * There is no name resolving function available. 00926 */ 00927 DEBUGMSGTL(("netsnmp_sockaddr_in", 00928 "no getaddrinfo()/getipnodebyname()/gethostbyname()\n")); 00929 free(peername); 00930 return 0; 00931 #endif /* HAVE_GETHOSTBYNAME */ 00932 } 00933 free(peername); 00934 } 00935 00936 /* 00937 * Finished 00938 */ 00939 00940 DEBUGMSGTL(("netsnmp_sockaddr_in", "return { AF_INET, %s:%hu }\n", 00941 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port))); 00942 return 1; 00943 } 00944 00945 00946 int 00947 netsnmp_sockaddr_in(struct sockaddr_in *addr, 00948 const char *inpeername, int remote_port) 00949 { 00950 char buf[sizeof(int) * 3 + 2]; 00951 sprintf(buf, ":%u", remote_port); 00952 return netsnmp_sockaddr_in2(addr, inpeername, remote_port ? buf : NULL); 00953 } 00954 00955 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) 00956 /* 00957 * The following functions provide the "com2sec" configuration token 00958 * functionality for compatibility. 00959 */ 00960 00961 #define EXAMPLE_NETWORK "NETWORK" 00962 #define EXAMPLE_COMMUNITY "COMMUNITY" 00963 00964 typedef struct _com2SecEntry { 00965 char community[COMMUNITY_MAX_LEN]; 00966 unsigned long network; 00967 unsigned long mask; 00968 char secName[VACMSTRINGLEN]; 00969 char contextName[VACMSTRINGLEN]; 00970 struct _com2SecEntry *next; 00971 } com2SecEntry; 00972 00973 com2SecEntry *com2SecList = NULL, *com2SecListLast = NULL; 00974 00975 void 00976 netsnmp_udp_parse_security(const char *token, char *param) 00977 { 00978 char secName[VACMSTRINGLEN]; 00979 char contextName[VACMSTRINGLEN]; 00980 char community[COMMUNITY_MAX_LEN]; 00981 char source[SNMP_MAXBUF_SMALL]; 00982 char *cp = NULL; 00983 const char *strmask = NULL; 00984 com2SecEntry *e = NULL; 00985 in_addr_t network = 0, mask = 0; 00986 00987 /* 00988 * Get security, source address/netmask and community strings. 00989 */ 00990 00991 cp = copy_nword( param, secName, sizeof(secName)); 00992 if (strcmp(secName, "-Cn") == 0) { 00993 if (!cp) { 00994 config_perror("missing CONTEXT_NAME parameter"); 00995 return; 00996 } 00997 cp = copy_nword( cp, contextName, sizeof(contextName)); 00998 cp = copy_nword( cp, secName, sizeof(secName)); 00999 } else { 01000 contextName[0] = '\0'; 01001 } 01002 if (secName[0] == '\0') { 01003 config_perror("missing NAME parameter"); 01004 return; 01005 } else if (strlen(secName) > (VACMSTRINGLEN - 1)) { 01006 config_perror("security name too long"); 01007 return; 01008 } 01009 cp = copy_nword( cp, source, sizeof(source)); 01010 if (source[0] == '\0') { 01011 config_perror("missing SOURCE parameter"); 01012 return; 01013 } else if (strncmp(source, EXAMPLE_NETWORK, strlen(EXAMPLE_NETWORK)) == 01014 0) { 01015 config_perror("example config NETWORK not properly configured"); 01016 return; 01017 } 01018 cp = copy_nword( cp, community, sizeof(community)); 01019 if (community[0] == '\0') { 01020 config_perror("missing COMMUNITY parameter\n"); 01021 return; 01022 } else 01023 if (strncmp 01024 (community, EXAMPLE_COMMUNITY, strlen(EXAMPLE_COMMUNITY)) 01025 == 0) { 01026 config_perror("example config COMMUNITY not properly configured"); 01027 return; 01028 } else if (strlen(community) > (COMMUNITY_MAX_LEN - 1)) { 01029 config_perror("community name too long"); 01030 return; 01031 } 01032 01033 /* 01034 * Process the source address/netmask string. 01035 */ 01036 01037 cp = strchr(source, '/'); 01038 if (cp != NULL) { 01039 /* 01040 * Mask given. 01041 */ 01042 *cp = '\0'; 01043 strmask = cp + 1; 01044 } 01045 01046 /* 01047 * Deal with the network part first. 01048 */ 01049 01050 if ((strcmp(source, "default") == 0) 01051 || (strcmp(source, "0.0.0.0") == 0)) { 01052 network = 0; 01053 strmask = "0.0.0.0"; 01054 } else { 01055 /* 01056 * Try interpreting as a dotted quad. 01057 */ 01058 network = inet_addr(source); 01059 01060 if (network == (in_addr_t) -1) { 01061 /* 01062 * Nope, wasn't a dotted quad. Must be a hostname. 01063 */ 01064 #ifdef HAVE_GETHOSTBYNAME 01065 struct hostent *hp = gethostbyname(source); 01066 if (hp == NULL) { 01067 config_perror("bad source address"); 01068 return; 01069 } else { 01070 if (hp->h_addrtype != AF_INET) { 01071 config_perror("no IP address for source hostname"); 01072 return; 01073 } 01074 network = *((in_addr_t *) hp->h_addr); 01075 } 01076 #else /*HAVE_GETHOSTBYNAME */ 01077 /* 01078 * Oh dear. 01079 */ 01080 config_perror("cannot resolve source hostname"); 01081 return; 01082 #endif /*HAVE_GETHOSTBYNAME */ 01083 } 01084 } 01085 01086 /* 01087 * Now work out the mask. 01088 */ 01089 01090 if (strmask == NULL || *strmask == '\0') { 01091 /* 01092 * No mask was given. Use 255.255.255.255. 01093 */ 01094 mask = 0xffffffffL; 01095 } else { 01096 if (strchr(strmask, '.')) { 01097 /* 01098 * Try to interpret mask as a dotted quad. 01099 */ 01100 mask = inet_addr(strmask); 01101 if (mask == (in_addr_t) -1 && 01102 strncmp(strmask, "255.255.255.255", 15) != 0) { 01103 config_perror("bad mask"); 01104 return; 01105 } 01106 } else { 01107 /* 01108 * Try to interpret mask as a "number of 1 bits". 01109 */ 01110 int maskLen = atoi(strmask), maskBit = 0x80000000L; 01111 if (maskLen <= 0 || maskLen > 32) { 01112 config_perror("bad mask length"); 01113 return; 01114 } 01115 while (maskLen--) { 01116 mask |= maskBit; 01117 maskBit >>= 1; 01118 } 01119 mask = htonl(mask); 01120 } 01121 } 01122 01123 /* 01124 * Check that the network and mask are consistent. 01125 */ 01126 01127 if (network & ~mask) { 01128 config_perror("source/mask mismatch"); 01129 return; 01130 } 01131 01132 e = (com2SecEntry *) malloc(sizeof(com2SecEntry)); 01133 if (e == NULL) { 01134 config_perror("memory error"); 01135 return; 01136 } 01137 01138 /* 01139 * Everything is okay. Copy the parameters to the structure allocated 01140 * above and add it to END of the list. 01141 */ 01142 01143 DEBUGMSGTL(("netsnmp_udp_parse_security", 01144 "<\"%s\", 0x%08x/0x%08x> => \"%s\"\n", community, network, 01145 mask, secName)); 01146 01147 strcpy(e->contextName, contextName); 01148 strcpy(e->secName, secName); 01149 strcpy(e->community, community); 01150 e->network = network; 01151 e->mask = mask; 01152 e->next = NULL; 01153 01154 if (com2SecListLast != NULL) { 01155 com2SecListLast->next = e; 01156 com2SecListLast = e; 01157 } else { 01158 com2SecListLast = com2SecList = e; 01159 } 01160 } 01161 01162 01163 void 01164 netsnmp_udp_com2SecList_free(void) 01165 { 01166 com2SecEntry *e = com2SecList; 01167 while (e != NULL) { 01168 com2SecEntry *tmp = e; 01169 e = e->next; 01170 free(tmp); 01171 } 01172 com2SecList = com2SecListLast = NULL; 01173 } 01174 #endif /* support for community based SNMP */ 01175 01176 void 01177 netsnmp_udp_agent_config_tokens_register(void) 01178 { 01179 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) 01180 register_app_config_handler("com2sec", netsnmp_udp_parse_security, 01181 netsnmp_udp_com2SecList_free, 01182 "[-Cn CONTEXT] secName IPv4-network-address[/netmask] community"); 01183 #endif /* support for community based SNMP */ 01184 } 01185 01186 01187 01188 /* 01189 * Return 0 if there are no com2sec entries, or return 1 if there ARE com2sec 01190 * entries. On return, if a com2sec entry matched the passed parameters, 01191 * then *secName points at the appropriate security name, or is NULL if the 01192 * parameters did not match any com2sec entry. 01193 */ 01194 01195 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) 01196 int 01197 netsnmp_udp_getSecName(void *opaque, int olength, 01198 const char *community, 01199 size_t community_len, char **secName, 01200 char **contextName) 01201 { 01202 com2SecEntry *c; 01203 netsnmp_udp_addr_pair *addr_pair = (netsnmp_udp_addr_pair *) opaque; 01204 struct sockaddr_in *from = (struct sockaddr_in *) &(addr_pair->remote_addr); 01205 char *ztcommunity = NULL; 01206 01207 if (secName != NULL) { 01208 *secName = NULL; /* Haven't found anything yet */ 01209 } 01210 01211 /* 01212 * Special case if there are NO entries (as opposed to no MATCHING 01213 * entries). 01214 */ 01215 01216 if (com2SecList == NULL) { 01217 DEBUGMSGTL(("netsnmp_udp_getSecName", "no com2sec entries\n")); 01218 return 0; 01219 } 01220 01221 /* 01222 * If there is no IPv4 source address, then there can be no valid security 01223 * name. 01224 */ 01225 01226 if (opaque == NULL || olength != sizeof(netsnmp_udp_addr_pair) || 01227 from->sin_family != AF_INET) { 01228 DEBUGMSGTL(("netsnmp_udp_getSecName", 01229 "no IPv4 source address in PDU?\n")); 01230 return 1; 01231 } 01232 01233 DEBUGIF("netsnmp_udp_getSecName") { 01234 ztcommunity = (char *)malloc(community_len + 1); 01235 if (ztcommunity != NULL) { 01236 memcpy(ztcommunity, community, community_len); 01237 ztcommunity[community_len] = '\0'; 01238 } 01239 01240 DEBUGMSGTL(("netsnmp_udp_getSecName", "resolve <\"%s\", 0x%08x>\n", 01241 ztcommunity ? ztcommunity : "<malloc error>", 01242 from->sin_addr.s_addr)); 01243 } 01244 01245 for (c = com2SecList; c != NULL; c = c->next) { 01246 DEBUGMSGTL(("netsnmp_udp_getSecName","compare <\"%s\", 0x%08x/0x%08x>", 01247 c->community, c->network, c->mask)); 01248 if ((community_len == strlen(c->community)) && 01249 (memcmp(community, c->community, community_len) == 0) && 01250 ((from->sin_addr.s_addr & c->mask) == c->network)) { 01251 DEBUGMSG(("netsnmp_udp_getSecName", "... SUCCESS\n")); 01252 if (secName != NULL) { 01253 *secName = c->secName; 01254 *contextName = c->contextName; 01255 } 01256 break; 01257 } 01258 DEBUGMSG(("netsnmp_udp_getSecName", "... nope\n")); 01259 } 01260 if (ztcommunity != NULL) { 01261 free(ztcommunity); 01262 } 01263 return 1; 01264 } 01265 #endif /* support for community based SNMP */ 01266 01267 01268 netsnmp_transport * 01269 netsnmp_udp_create_tstring(const char *str, int local, 01270 const char *default_target) 01271 { 01272 struct sockaddr_in addr; 01273 01274 if (netsnmp_sockaddr_in2(&addr, str, default_target)) { 01275 return netsnmp_udp_transport(&addr, local); 01276 } else { 01277 return NULL; 01278 } 01279 } 01280 01281 01282 netsnmp_transport * 01283 netsnmp_udp_create_ostring(const u_char * o, size_t o_len, int local) 01284 { 01285 struct sockaddr_in addr; 01286 01287 if (o_len == 6) { 01288 unsigned short porttmp = (o[4] << 8) + o[5]; 01289 addr.sin_family = AF_INET; 01290 memcpy((u_char *) & (addr.sin_addr.s_addr), o, 4); 01291 addr.sin_port = htons(porttmp); 01292 return netsnmp_udp_transport(&addr, local); 01293 } 01294 return NULL; 01295 } 01296 01297 01298 void 01299 netsnmp_udp_ctor(void) 01300 { 01301 udpDomain.name = netsnmpUDPDomain; 01302 udpDomain.name_length = netsnmpUDPDomain_len; 01303 udpDomain.prefix = (const char**)calloc(2, sizeof(char *)); 01304 udpDomain.prefix[0] = "udp"; 01305 01306 udpDomain.f_create_from_tstring_new = netsnmp_udp_create_tstring; 01307 udpDomain.f_create_from_ostring = netsnmp_udp_create_ostring; 01308 01309 netsnmp_tdomain_register(&udpDomain); 01310 }