net-snmp
5.4.1
|
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 }