snmpUDPDomain.c

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

net-snmpに対してSat Sep 5 13:14:26 2009に生成されました。  doxygen 1.4.7