snmpTCPIPv6Domain.c

00001 #include <net-snmp/net-snmp-config.h>
00002 
00003 #ifdef SNMP_TRANSPORT_TCPIPV6_DOMAIN
00004 
00005 /*
00006  * hack-o-matic for Cygwin to use winsock2
00007 */
00008 #if defined(cygwin)
00009 #undef HAVE_UNISTD_H
00010 #undef HAVE_NETINET_IN_H
00011 #undef HAVE_ARPA_INET_H
00012 #undef HAVE_NET_IF_H
00013 #undef HAVE_NETDB_H
00014 #undef HAVE_SYS_PARAM_H
00015 #undef HAVE_SYS_SELECT_H
00016 #undef HAVE_SYS_SOCKET_H
00017 #undef HAVE_IN_ADDR_T
00018 #endif
00019 
00020 #include <stdio.h>
00021 #include <sys/types.h>
00022 #include <errno.h>
00023 
00024 #if HAVE_STRING_H
00025 #include <string.h>
00026 #else
00027 #include <strings.h>
00028 #endif
00029 #if HAVE_STDLIB_H
00030 #include <stdlib.h>
00031 #endif
00032 #if HAVE_UNISTD_H
00033 #include <unistd.h>
00034 #endif
00035 #if HAVE_SYS_SOCKET_H
00036 #include <sys/socket.h>
00037 #endif
00038 
00039 #if defined(HAVE_WINSOCK_H) || defined(cygwin)
00040     /*
00041      * Windows IPv6 support is part of WinSock2 only
00042      */
00043 #include <winsock2.h>
00044 #include <ws2tcpip.h>
00045 
00046 extern const char *inet_ntop(int, const void*, char*, size_t);
00047 
00048 #endif
00049 
00050 #if HAVE_NETINET_IN_H
00051 #include <netinet/in.h>
00052 #endif
00053 #if HAVE_ARPA_INET_H
00054 #include <arpa/inet.h>
00055 #endif
00056 #if HAVE_NETDB_H
00057 #include <netdb.h>
00058 #endif
00059 #if HAVE_FCNTL_H
00060 #include <fcntl.h>
00061 #endif
00062 
00063 #if HAVE_DMALLOC_H
00064 #include <dmalloc.h>
00065 #endif
00066 
00067 #include <net-snmp/types.h>
00068 #include <net-snmp/output_api.h>
00069 #include <net-snmp/config_api.h>
00070 
00071 #include <net-snmp/library/snmp_transport.h>
00072 #include <net-snmp/library/snmpUDPDomain.h>
00073 #include <net-snmp/library/snmpUDPIPv6Domain.h>
00074 #include <net-snmp/library/snmpTCPIPv6Domain.h>
00075 
00076 oid netsnmp_TCPIPv6Domain[] = { TRANSPORT_DOMAIN_TCP_IPV6 };
00077 static netsnmp_tdomain tcp6Domain;
00078 
00079 /*
00080  * Return a string representing the address in data, or else the "far end"
00081  * address if data is NULL.  
00082  */
00083 
00084 static char *
00085 netsnmp_tcp6_fmtaddr(netsnmp_transport *t, void *data, int len)
00086 {
00087     struct sockaddr_in6 *to = NULL;
00088 
00089     DEBUGMSGTL(("netsnmp_tcp6", "fmtaddr: t = %p, data = %p, len = %d\n", t,
00090                 data, len));
00091     if (data != NULL && len == sizeof(struct sockaddr_in6)) {
00092         to = (struct sockaddr_in6 *) data;
00093     } else if (t != NULL && t->data != NULL) {
00094         to = (struct sockaddr_in6 *) t->data;
00095     }
00096     if (to == NULL) {
00097         return strdup("TCP/IPv6: unknown");
00098     } else {
00099         char addr[INET6_ADDRSTRLEN];
00100         char tmp[INET6_ADDRSTRLEN + 8];
00101 
00102         sprintf(tmp, "TCP/IPv6: [%s]:%hd",
00103                 inet_ntop(AF_INET6, (void *) &(to->sin6_addr), addr,
00104                           INET6_ADDRSTRLEN), ntohs(to->sin6_port));
00105         return strdup(tmp);
00106     }
00107 }
00108 
00109 /*
00110  * You can write something into opaque that will subsequently get passed back 
00111  * to your send function if you like.  For instance, you might want to
00112  * remember where a PDU came from, so that you can send a reply there...  
00113  */
00114 
00115 static int
00116 netsnmp_tcp6_recv(netsnmp_transport *t, void *buf, int size,
00117                   void **opaque, int *olength)
00118 {
00119     int rc = -1;
00120 
00121     if (t != NULL && t->sock >= 0) {
00122         while (rc < 0) {
00123             rc = recv(t->sock, buf, size, 0);
00124             if (rc < 0 && errno != EINTR) {
00125                 DEBUGMSGTL(("netsnmp_tcp6", "recv fd %d err %d (\"%s\")\n",
00126                             t->sock, errno, strerror(errno)));
00127                 return -1;
00128             }
00129         }
00130         DEBUGMSGTL(("netsnmp_tcp6", "recv fd %d got %d bytes\n", t->sock, rc));
00131     } else {
00132         return -1;
00133     }
00134 
00135     if (opaque != NULL && olength != NULL) {
00136         if (t->data_length > 0) {
00137             if ((*opaque = malloc(t->data_length)) != NULL) {
00138                 memcpy(*opaque, t->data, t->data_length);
00139                 *olength = t->data_length;
00140             } else {
00141                 *olength = 0;
00142             }
00143         } else {
00144             *opaque = NULL;
00145             *olength = 0;
00146         }
00147     }
00148 
00149     return rc;
00150 }
00151 
00152 static int
00153 netsnmp_tcp6_send(netsnmp_transport *t, void *buf, int size,
00154                   void **opaque, int *olength)
00155 {
00156     int rc = -1;
00157 
00158     if (t != NULL && t->sock >= 0) {
00159         while (rc < 0) {
00160             rc = send(t->sock, buf, size, 0);
00161             if (rc < 0 && errno != EINTR) {
00162                 break;
00163             }
00164         }
00165     }
00166     return rc;
00167 }
00168 
00169 static int
00170 netsnmp_tcp6_close(netsnmp_transport *t)
00171 {
00172     int rc = -1;
00173     if (t != NULL && t->sock >= 0) {
00174         DEBUGMSGTL(("netsnmp_tcp6", "close fd %d\n", t->sock));
00175 #ifndef HAVE_CLOSESOCKET
00176         rc = close(t->sock);
00177 #else
00178         rc = closesocket(t->sock);
00179 #endif
00180         t->sock = -1;
00181     }
00182     return rc;
00183 }
00184 
00185 static int
00186 netsnmp_tcp6_accept(netsnmp_transport *t)
00187 {
00188     struct sockaddr_in6 *farend = NULL;
00189     int             newsock = -1, sockflags = 0;
00190     socklen_t       farendlen = sizeof(struct sockaddr_in6);
00191     char           *str = NULL;
00192 
00193     farend = (struct sockaddr_in6 *) malloc(sizeof(struct sockaddr_in6));
00194 
00195     if (farend == NULL) {
00196         /*
00197          * Indicate that the acceptance of this socket failed.  
00198          */
00199         DEBUGMSGTL(("netsnmp_tcp6", "accept: malloc failed\n"));
00200         return -1;
00201     }
00202 
00203     if (t != NULL && t->sock >= 0) {
00204         newsock = accept(t->sock, (struct sockaddr *) farend, &farendlen);
00205 
00206         if (newsock < 0) {
00207             DEBUGMSGTL(("netsnmp_tcp6","accept failed rc %d errno %d \"%s\"\n",
00208                         newsock, errno, strerror(errno)));
00209             free(farend);
00210             return newsock;
00211         }
00212 
00213         if (t->data != NULL) {
00214             free(t->data);
00215         }
00216 
00217         t->data = farend;
00218         t->data_length = farendlen;
00219         str = netsnmp_tcp6_fmtaddr(NULL, farend, farendlen);
00220         DEBUGMSGTL(("netsnmp_tcp6", "accept succeeded (from %s)\n", str));
00221         free(str);
00222 
00223         /*
00224          * Try to make the new socket blocking.  
00225          */
00226 
00227 #ifdef WIN32
00228         ioctlsocket(newsock, FIONBIO, &sockflags);
00229 #else
00230         if ((sockflags = fcntl(newsock, F_GETFL, 0)) >= 0) {
00231             fcntl(newsock, F_SETFL, (sockflags & ~O_NONBLOCK));
00232         } else {
00233             DEBUGMSGTL(("netsnmp_tcp6", "accept: couldn't f_getfl of fd %d\n",
00234                         newsock));
00235         }
00236 #endif
00237 
00238         /*
00239          * Allow user to override the send and receive buffers. Default is
00240          * to use os default.  Don't worry too much about errors --
00241          * just plough on regardless.  
00242          */
00243         netsnmp_sock_buffer_set(newsock, SO_SNDBUF, 1, 0);
00244         netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0);
00245 
00246         return newsock;
00247     } else {
00248         free(farend);
00249         return -1;
00250     }
00251 }
00252 
00253 
00254 
00255 /*
00256  * Open a TCP/IPv6-based transport for SNMP.  Local is TRUE if addr is the
00257  * local address to bind to (i.e. this is a server-type session); otherwise
00258  * addr is the remote address to send things to.  
00259  */
00260 
00261 netsnmp_transport *
00262 netsnmp_tcp6_transport(struct sockaddr_in6 *addr, int local)
00263 {
00264     netsnmp_transport *t = NULL;
00265     int             rc = 0;
00266     char           *str = NULL;
00267 
00268     if (addr == NULL || addr->sin6_family != AF_INET6) {
00269         return NULL;
00270     }
00271 
00272     t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport));
00273     if (t == NULL) {
00274         return NULL;
00275     }
00276     memset(t, 0, sizeof(netsnmp_transport));
00277 
00278     str = netsnmp_tcp6_fmtaddr(NULL, (void *)addr,
00279                                   sizeof(struct sockaddr_in6));
00280     DEBUGMSGTL(("netsnmp_tcp6", "open %s %s\n", local ? "local" : "remote",
00281                 str));
00282     free(str);
00283 
00284     memset(t, 0, sizeof(netsnmp_transport));
00285 
00286     t->data = malloc(sizeof(struct sockaddr_in6));
00287     if (t->data == NULL) {
00288         netsnmp_transport_free(t);
00289         return NULL;
00290     }
00291     t->data_length = sizeof(struct sockaddr_in6);
00292     memcpy(t->data, addr, sizeof(struct sockaddr_in6));
00293 
00294     t->domain = netsnmp_TCPIPv6Domain;
00295     t->domain_length = sizeof(netsnmp_TCPIPv6Domain) / sizeof(oid);
00296 
00297     t->sock = socket(PF_INET6, SOCK_STREAM, 0);
00298     if (t->sock < 0) {
00299         netsnmp_transport_free(t);
00300         return NULL;
00301     }
00302 
00303     t->flags = NETSNMP_TRANSPORT_FLAG_STREAM;
00304 
00305     if (local) {
00306         int sockflags = 0, opt = 1;
00307 
00308         /*
00309          * This session is inteneded as a server, so we must bind on to the
00310          * given IP address, which may include an interface address, or could
00311          * be INADDR_ANY, but certainly includes a port number.
00312          */
00313 
00314 #ifdef IPV6_V6ONLY
00315         /* Try to restrict PF_INET6 socket to IPv6 communications only. */
00316         {
00317           int one=1;
00318           if (setsockopt(t->sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) != 0) {
00319             DEBUGMSGTL(("netsnmp_udp6", "couldn't set IPV6_V6ONLY to %d bytes: %s\n", one, strerror(errno)));
00320           } 
00321         }
00322 #endif
00323 
00324         t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN;
00325         t->local = malloc(18);
00326         if (t->local == NULL) {
00327             netsnmp_tcp6_close(t);
00328             netsnmp_transport_free(t);
00329             return NULL;
00330         }
00331         memcpy(t->local, addr->sin6_addr.s6_addr, 16);
00332         t->local[16] = (addr->sin6_port & 0xff00) >> 8;
00333         t->local[17] = (addr->sin6_port & 0x00ff) >> 0;
00334         t->local_length = 18;
00335 
00336         /*
00337          * We should set SO_REUSEADDR too.  
00338          */
00339 
00340         setsockopt(t->sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
00341 
00342         rc = bind(t->sock, (struct sockaddr *) addr,
00343                   sizeof(struct sockaddr_in6));
00344         if (rc != 0) {
00345             netsnmp_tcp6_close(t);
00346             netsnmp_transport_free(t);
00347             return NULL;
00348         }
00349 
00350         /*
00351          * Since we are going to be letting select() tell us when connections
00352          * are ready to be accept()ed, we need to make the socket n0n-blocking
00353          * to avoid the race condition described in W. R. Stevens, ``Unix
00354          * Network Programming Volume I Second Edition'', pp. 422--4, which
00355          * could otherwise wedge the agent.
00356          */
00357 
00358 #ifdef WIN32
00359         opt = 1;
00360         ioctlsocket(t->sock, FIONBIO, &opt);
00361 #else
00362         sockflags = fcntl(t->sock, F_GETFL, 0);
00363         fcntl(t->sock, F_SETFL, sockflags | O_NONBLOCK);
00364 #endif
00365 
00366         /*
00367          * Now sit here and wait for connections to arrive.  
00368          */
00369 
00370         rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN);
00371         if (rc != 0) {
00372             netsnmp_tcp6_close(t);
00373             netsnmp_transport_free(t);
00374             return NULL;
00375         }
00376         
00377         /*
00378          * no buffer size on listen socket - doesn't make sense
00379          */
00380 
00381     } else {
00382         t->remote = malloc(18);
00383         if (t->remote == NULL) {
00384             netsnmp_tcp6_close(t);
00385             netsnmp_transport_free(t);
00386             return NULL;
00387         }
00388         memcpy(t->remote, addr->sin6_addr.s6_addr, 16);
00389         t->remote[16] = (addr->sin6_port & 0xff00) >> 8;
00390         t->remote[17] = (addr->sin6_port & 0x00ff) >> 0;
00391         t->remote_length = 18;
00392 
00393         /*
00394          * This is a client-type session, so attempt to connect to the far
00395          * end.  We don't go non-blocking here because it's not obvious what
00396          * you'd then do if you tried to do snmp_sends before the connection
00397          * had completed.  So this can block.
00398          */
00399 
00400         rc = connect(t->sock, (struct sockaddr *) addr,
00401                      sizeof(struct sockaddr_in6));
00402 
00403         DEBUGMSGTL(("netsnmp_tcp6", "connect returns %d\n", rc));
00404 
00405         if (rc < 0) {
00406             netsnmp_tcp6_close(t);
00407             netsnmp_transport_free(t);
00408             return NULL;
00409         }
00410 
00411         /*
00412          * Allow user to override the send and receive buffers. Default is
00413          * to use os default.  Don't worry too much about errors --
00414          * just plough on regardless.  
00415          */
00416         netsnmp_sock_buffer_set(t->sock, SO_SNDBUF, local, 0);
00417         netsnmp_sock_buffer_set(t->sock, SO_RCVBUF, local, 0);
00418     }
00419 
00420     /*
00421      * Message size is not limited by this transport (hence msgMaxSize
00422      * is equal to the maximum legal size of an SNMP message).  
00423      */
00424 
00425     t->msgMaxSize = 0x7fffffff;
00426     t->f_recv     = netsnmp_tcp6_recv;
00427     t->f_send     = netsnmp_tcp6_send;
00428     t->f_close    = netsnmp_tcp6_close;
00429     t->f_accept   = netsnmp_tcp6_accept;
00430     t->f_fmtaddr  = netsnmp_tcp6_fmtaddr;
00431 
00432     return t;
00433 }
00434 
00435 
00436 
00437 netsnmp_transport *
00438 netsnmp_tcp6_create_tstring(const char *str, int local)
00439 {
00440     struct sockaddr_in6 addr;
00441 
00442     if (netsnmp_sockaddr_in6(&addr, str, 0)) {
00443         return netsnmp_tcp6_transport(&addr, local);
00444     } else {
00445         return NULL;
00446     }
00447 }
00448 
00449 
00450 /*
00451  * See:
00452  * 
00453  * http://www.ietf.org/internet-drafts/draft-ietf-ops-taddress-mib-01.txt
00454  * 
00455  * (or newer equivalent) for details of the TC which we are using for
00456  * the mapping here.  
00457  */
00458 
00459 netsnmp_transport *
00460 netsnmp_tcp6_create_ostring(const u_char * o, size_t o_len, int local)
00461 {
00462     struct sockaddr_in6 addr;
00463 
00464     if (o_len == 18) {
00465         memset((u_char *) & addr, 0, sizeof(struct sockaddr_in6));
00466         addr.sin6_family = AF_INET6;
00467         memcpy((u_char *) & (addr.sin6_addr.s6_addr), o, 16);
00468         addr.sin6_port = (o[16] << 8) + o[17];
00469         return netsnmp_tcp6_transport(&addr, local);
00470     }
00471     return NULL;
00472 }
00473 
00474 
00475 void
00476 netsnmp_tcp6_ctor(void)
00477 {
00478     tcp6Domain.name = netsnmp_TCPIPv6Domain;
00479     tcp6Domain.name_length = sizeof(netsnmp_TCPIPv6Domain) / sizeof(oid);
00480     tcp6Domain.f_create_from_tstring = netsnmp_tcp6_create_tstring;
00481     tcp6Domain.f_create_from_ostring = netsnmp_tcp6_create_ostring;
00482     tcp6Domain.prefix = calloc(4, sizeof(char *));
00483     tcp6Domain.prefix[0] = "tcp6";
00484     tcp6Domain.prefix[1] = "tcpv6";
00485     tcp6Domain.prefix[2] = "tcpipv6";
00486 
00487     netsnmp_tdomain_register(&tcp6Domain);
00488 }
00489 
00490 #endif /* SNMP_TRANSPORT_TCPIPV6_DOMAIN */
00491 

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