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