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