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 <ctype.h> 00006 #include <errno.h> 00007 00008 #ifdef WIN32 00009 #include <net-snmp/library/winpipe.h> 00010 #endif 00011 #if HAVE_STRING_H 00012 #include <string.h> 00013 #else 00014 #include <strings.h> 00015 #endif 00016 #if HAVE_STDLIB_H 00017 #include <stdlib.h> 00018 #endif 00019 #if HAVE_UNISTD_H 00020 #include <unistd.h> 00021 #endif 00022 #if HAVE_SYS_SOCKET_H 00023 #include <sys/socket.h> 00024 #endif 00025 #if HAVE_SYS_UN_H 00026 #include <sys/un.h> 00027 #endif 00028 #if HAVE_IO_H 00029 #include <io.h> 00030 #endif 00031 #if HAVE_FCNTL_H 00032 #include <fcntl.h> 00033 #endif 00034 00035 #if HAVE_DMALLOC_H 00036 #include <dmalloc.h> 00037 #endif 00038 00039 #include <net-snmp/types.h> 00040 #include <net-snmp/output_api.h> 00041 #include <net-snmp/config_api.h> 00042 #include <net-snmp/utilities.h> 00043 00044 #include <net-snmp/library/snmp_transport.h> 00045 #include <net-snmp/library/snmpUnixDomain.h> 00046 #include <net-snmp/library/snmp_api.h> 00047 #include <net-snmp/library/snmp_client.h> 00048 #include <net-snmp/library/snmpCallbackDomain.h> 00049 00050 #ifndef NETSNMP_STREAM_QUEUE_LEN 00051 #define NETSNMP_STREAM_QUEUE_LEN 5 00052 #endif 00053 00054 #ifdef NETSNMP_TRANSPORT_CALLBACK_DOMAIN 00055 00056 static netsnmp_transport_list *trlist = NULL; 00057 00058 static int callback_count = 0; 00059 00060 typedef struct callback_hack_s { 00061 void *orig_transport_data; 00062 netsnmp_pdu *pdu; 00063 } callback_hack; 00064 00065 typedef struct callback_queue_s { 00066 int callback_num; 00067 netsnmp_callback_pass *item; 00068 struct callback_queue_s *next, *prev; 00069 } callback_queue; 00070 00071 callback_queue *thequeue; 00072 00073 static netsnmp_transport * 00074 find_transport_from_callback_num(int num) 00075 { 00076 static netsnmp_transport_list *ptr; 00077 for (ptr = trlist; ptr; ptr = ptr->next) 00078 if (((netsnmp_callback_info *) ptr->transport->data)-> 00079 callback_num == num) 00080 return ptr->transport; 00081 return NULL; 00082 } 00083 00084 static void 00085 callback_debug_pdu(const char *ourstring, netsnmp_pdu *pdu) 00086 { 00087 netsnmp_variable_list *vb; 00088 int i = 1; 00089 DEBUGMSGTL((ourstring, 00090 "PDU: command = %d, errstat = %d, errindex = %d\n", 00091 pdu->command, pdu->errstat, pdu->errindex)); 00092 for (vb = pdu->variables; vb; vb = vb->next_variable) { 00093 DEBUGMSGTL((ourstring, " var %d:", i++)); 00094 DEBUGMSGVAR((ourstring, vb)); 00095 DEBUGMSG((ourstring, "\n")); 00096 } 00097 } 00098 00099 void 00100 callback_push_queue(int num, netsnmp_callback_pass *item) 00101 { 00102 callback_queue *newitem = SNMP_MALLOC_TYPEDEF(callback_queue); 00103 callback_queue *ptr; 00104 00105 newitem->callback_num = num; 00106 newitem->item = item; 00107 if (thequeue) { 00108 for (ptr = thequeue; ptr && ptr->next; ptr = ptr->next) { 00109 } 00110 ptr->next = newitem; 00111 newitem->prev = ptr; 00112 } else { 00113 thequeue = newitem; 00114 } 00115 DEBUGIF("dump_send_callback_transport") { 00116 callback_debug_pdu("dump_send_callback_transport", item->pdu); 00117 } 00118 } 00119 00120 netsnmp_callback_pass * 00121 callback_pop_queue(int num) 00122 { 00123 netsnmp_callback_pass *cp; 00124 callback_queue *ptr; 00125 00126 for (ptr = thequeue; ptr; ptr = ptr->next) { 00127 if (ptr->callback_num == num) { 00128 if (ptr->prev) { 00129 ptr->prev->next = ptr->next; 00130 } else { 00131 thequeue = ptr->next; 00132 } 00133 if (ptr->next) { 00134 ptr->next->prev = ptr->prev; 00135 } 00136 cp = ptr->item; 00137 SNMP_FREE(ptr); 00138 DEBUGIF("dump_recv_callback_transport") { 00139 callback_debug_pdu("dump_recv_callback_transport", 00140 cp->pdu); 00141 } 00142 return cp; 00143 } 00144 } 00145 return NULL; 00146 } 00147 00148 /* 00149 * Return a string representing the address in data, or else the "far end" 00150 * address if data is NULL. 00151 */ 00152 00153 char * 00154 netsnmp_callback_fmtaddr(netsnmp_transport *t, void *data, int len) 00155 { 00156 char buf[SPRINT_MAX_LEN]; 00157 netsnmp_callback_info *mystuff; 00158 00159 if (!t) 00160 return strdup("callback: unknown"); 00161 00162 mystuff = (netsnmp_callback_info *) t->data; 00163 00164 if (!mystuff) 00165 return strdup("callback: unknown"); 00166 00167 snprintf(buf, SPRINT_MAX_LEN, "callback: %d on fd %d", 00168 mystuff->callback_num, mystuff->pipefds[0]); 00169 return strdup(buf); 00170 } 00171 00172 00173 00174 /* 00175 * You can write something into opaque that will subsequently get passed back 00176 * to your send function if you like. For instance, you might want to 00177 * remember where a PDU came from, so that you can send a reply there... 00178 */ 00179 00180 int 00181 netsnmp_callback_recv(netsnmp_transport *t, void *buf, int size, 00182 void **opaque, int *olength) 00183 { 00184 int rc = -1; 00185 char newbuf[1]; 00186 netsnmp_callback_info *mystuff = (netsnmp_callback_info *) t->data; 00187 00188 DEBUGMSGTL(("transport_callback", "hook_recv enter\n")); 00189 00190 while (rc < 0) { 00191 #ifdef WIN32 00192 rc = recv(mystuff->pipefds[0], newbuf, 1, 0); 00193 #else 00194 rc = read(mystuff->pipefds[0], newbuf, 1); 00195 #endif 00196 if (rc < 0 && errno != EINTR) { 00197 break; 00198 } 00199 } 00200 00201 if (mystuff->linkedto) { 00202 /* 00203 * we're the client. We don't need to do anything. 00204 */ 00205 } else { 00206 /* 00207 * malloc the space here, but it's filled in by 00208 * snmp_callback_created_pdu() below 00209 */ 00210 int *returnnum = (int *) calloc(1, sizeof(int)); 00211 *opaque = returnnum; 00212 *olength = sizeof(int); 00213 } 00214 DEBUGMSGTL(("transport_callback", "hook_recv exit\n")); 00215 return rc; 00216 } 00217 00218 00219 00220 int 00221 netsnmp_callback_send(netsnmp_transport *t, void *buf, int size, 00222 void **opaque, int *olength) 00223 { 00224 int from, rc = -1; 00225 netsnmp_callback_info *mystuff = (netsnmp_callback_info *) t->data; 00226 netsnmp_callback_pass *cp; 00227 00228 /* 00229 * extract the pdu from the hacked buffer 00230 */ 00231 netsnmp_transport *other_side; 00232 callback_hack *ch = (callback_hack *) * opaque; 00233 netsnmp_pdu *pdu = ch->pdu; 00234 *opaque = ch->orig_transport_data; 00235 SNMP_FREE(ch); 00236 00237 DEBUGMSGTL(("transport_callback", "hook_send enter\n")); 00238 00239 cp = SNMP_MALLOC_TYPEDEF(netsnmp_callback_pass); 00240 if (!cp) 00241 return -1; 00242 00243 cp->pdu = snmp_clone_pdu(pdu); 00244 if (cp->pdu->transport_data) { 00245 /* 00246 * not needed and not properly freed later 00247 */ 00248 SNMP_FREE(cp->pdu->transport_data); 00249 } 00250 00251 if (cp->pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE) 00252 cp->pdu->flags ^= UCD_MSG_FLAG_EXPECT_RESPONSE; 00253 00254 /* 00255 * push the sent pdu onto the stack 00256 */ 00257 /* 00258 * AND send a bogus byte to the remote callback receiver's pipe 00259 */ 00260 if (mystuff->linkedto) { 00261 /* 00262 * we're the client, send it to the parent 00263 */ 00264 cp->return_transport_num = mystuff->callback_num; 00265 00266 other_side = find_transport_from_callback_num(mystuff->linkedto); 00267 if (!other_side) { 00268 snmp_free_pdu(cp->pdu); 00269 SNMP_FREE(cp); 00270 return -1; 00271 } 00272 00273 while (rc < 0) { 00274 #ifdef WIN32 00275 rc = send(((netsnmp_callback_info*) other_side->data)->pipefds[1], " ", 1, 0); 00276 #else 00277 rc = write(((netsnmp_callback_info *)other_side->data)->pipefds[1], 00278 " ", 1); 00279 #endif 00280 if (rc < 0 && errno != EINTR) { 00281 break; 00282 } 00283 } 00284 callback_push_queue(mystuff->linkedto, cp); 00285 /* 00286 * we don't need the transport data any more 00287 */ 00288 if (*opaque) { 00289 SNMP_FREE(*opaque); 00290 *opaque = NULL; 00291 } 00292 } else { 00293 /* 00294 * we're the server, send it to the person that sent us the request 00295 */ 00296 from = **((int **) opaque); 00297 /* 00298 * we don't need the transport data any more 00299 */ 00300 if (*opaque) { 00301 SNMP_FREE(*opaque); 00302 *opaque = NULL; 00303 } 00304 other_side = find_transport_from_callback_num(from); 00305 if (!other_side) { 00306 snmp_free_pdu(cp->pdu); 00307 SNMP_FREE(cp); 00308 return -1; 00309 } 00310 while (rc < 0) { 00311 #ifdef WIN32 00312 rc = send(((netsnmp_callback_info*) other_side->data)->pipefds[1], " ", 1, 0); 00313 #else 00314 rc = write(((netsnmp_callback_info *)other_side->data)->pipefds[1], 00315 " ", 1); 00316 #endif 00317 if (rc < 0 && errno != EINTR) { 00318 break; 00319 } 00320 } 00321 callback_push_queue(from, cp); 00322 } 00323 00324 DEBUGMSGTL(("transport_callback", "hook_send exit\n")); 00325 return 0; 00326 } 00327 00328 00329 00330 int 00331 netsnmp_callback_close(netsnmp_transport *t) 00332 { 00333 int rc; 00334 netsnmp_callback_info *mystuff = (netsnmp_callback_info *) t->data; 00335 DEBUGMSGTL(("transport_callback", "hook_close enter\n")); 00336 00337 #ifdef WIN32 00338 rc = closesocket(mystuff->pipefds[0]); 00339 rc |= closesocket(mystuff->pipefds[1]); 00340 #else 00341 rc = close(mystuff->pipefds[0]); 00342 rc |= close(mystuff->pipefds[1]); 00343 #endif 00344 00345 rc |= netsnmp_transport_remove_from_list(&trlist, t); 00346 00347 DEBUGMSGTL(("transport_callback", "hook_close exit\n")); 00348 return rc; 00349 } 00350 00351 00352 00353 int 00354 netsnmp_callback_accept(netsnmp_transport *t) 00355 { 00356 DEBUGMSGTL(("transport_callback", "hook_accept enter\n")); 00357 DEBUGMSGTL(("transport_callback", "hook_accept exit\n")); 00358 return 0; 00359 } 00360 00361 00362 00363 /* 00364 * Open a Callback-domain transport for SNMP. Local is TRUE if addr 00365 * is the local address to bind to (i.e. this is a server-type 00366 * session); otherwise addr is the remote address to send things to 00367 * (and we make up a temporary name for the local end of the 00368 * connection). 00369 */ 00370 00371 netsnmp_transport * 00372 netsnmp_callback_transport(int to) 00373 { 00374 00375 netsnmp_transport *t = NULL; 00376 netsnmp_callback_info *mydata; 00377 int rc; 00378 00379 /* 00380 * transport 00381 */ 00382 t = SNMP_MALLOC_TYPEDEF(netsnmp_transport); 00383 if (!t) 00384 return NULL; 00385 00386 /* 00387 * our stuff 00388 */ 00389 mydata = SNMP_MALLOC_TYPEDEF(netsnmp_callback_info); 00390 mydata->linkedto = to; 00391 mydata->callback_num = ++callback_count; 00392 mydata->data = NULL; 00393 t->data = mydata; 00394 00395 #ifdef WIN32 00396 rc = create_winpipe_transport(mydata->pipefds); 00397 #else 00398 rc = pipe(mydata->pipefds); 00399 #endif 00400 t->sock = mydata->pipefds[0]; 00401 00402 if (rc) { 00403 SNMP_FREE(mydata); 00404 SNMP_FREE(t); 00405 return NULL; 00406 } 00407 00408 t->f_recv = netsnmp_callback_recv; 00409 t->f_send = netsnmp_callback_send; 00410 t->f_close = netsnmp_callback_close; 00411 t->f_accept = netsnmp_callback_accept; 00412 t->f_fmtaddr = netsnmp_callback_fmtaddr; 00413 00414 netsnmp_transport_add_to_list(&trlist, t); 00415 00416 if (to) 00417 DEBUGMSGTL(("transport_callback", "initialized %d linked to %d\n", 00418 mydata->callback_num, to)); 00419 else 00420 DEBUGMSGTL(("transport_callback", 00421 "initialized master listening on %d\n", 00422 mydata->callback_num)); 00423 return t; 00424 } 00425 00426 int 00427 netsnmp_callback_hook_parse(netsnmp_session * sp, 00428 netsnmp_pdu *pdu, 00429 u_char * packetptr, size_t len) 00430 { 00431 if (SNMP_MSG_RESPONSE == pdu->command || 00432 SNMP_MSG_REPORT == pdu->command) 00433 pdu->flags |= UCD_MSG_FLAG_RESPONSE_PDU; 00434 else 00435 pdu->flags &= (~UCD_MSG_FLAG_RESPONSE_PDU); 00436 00437 return SNMP_ERR_NOERROR; 00438 } 00439 00440 int 00441 netsnmp_callback_hook_build(netsnmp_session * sp, 00442 netsnmp_pdu *pdu, u_char * ptk, size_t * len) 00443 { 00444 /* 00445 * very gross hack, as this is passed later to the transport_send 00446 * function 00447 */ 00448 callback_hack *ch = SNMP_MALLOC_TYPEDEF(callback_hack); 00449 DEBUGMSGTL(("transport_callback", "hook_build enter\n")); 00450 ch->pdu = pdu; 00451 ch->orig_transport_data = pdu->transport_data; 00452 pdu->transport_data = ch; 00453 switch (pdu->command) { 00454 case SNMP_MSG_GETBULK: 00455 if (pdu->max_repetitions < 0) { 00456 sp->s_snmp_errno = SNMPERR_BAD_REPETITIONS; 00457 return -1; 00458 } 00459 if (pdu->non_repeaters < 0) { 00460 sp->s_snmp_errno = SNMPERR_BAD_REPEATERS; 00461 return -1; 00462 } 00463 break; 00464 case SNMP_MSG_RESPONSE: 00465 case SNMP_MSG_TRAP: 00466 case SNMP_MSG_TRAP2: 00467 pdu->flags &= (~UCD_MSG_FLAG_EXPECT_RESPONSE); 00468 /* 00469 * Fallthrough 00470 */ 00471 default: 00472 if (pdu->errstat == SNMP_DEFAULT_ERRSTAT) 00473 pdu->errstat = 0; 00474 if (pdu->errindex == SNMP_DEFAULT_ERRINDEX) 00475 pdu->errindex = 0; 00476 break; 00477 } 00478 00479 /* 00480 * Copy missing values from session defaults 00481 */ 00482 switch (pdu->version) { 00483 #ifndef NETSNMP_DISABLE_SNMPV1 00484 case SNMP_VERSION_1: 00485 #endif 00486 #ifndef NETSNMP_DISABLE_SNMPV2C 00487 case SNMP_VERSION_2c: 00488 #endif 00489 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) 00490 if (pdu->community_len == 0) { 00491 if (sp->community_len == 0) { 00492 sp->s_snmp_errno = SNMPERR_BAD_COMMUNITY; 00493 return -1; 00494 } 00495 pdu->community = (u_char *) malloc(sp->community_len); 00496 if (pdu->community == NULL) { 00497 sp->s_snmp_errno = SNMPERR_MALLOC; 00498 return -1; 00499 } 00500 memmove(pdu->community, 00501 sp->community, sp->community_len); 00502 pdu->community_len = sp->community_len; 00503 } 00504 break; 00505 #endif 00506 case SNMP_VERSION_3: 00507 if (pdu->securityNameLen == 0) { 00508 pdu->securityName = (char *)malloc(sp->securityNameLen); 00509 if (pdu->securityName == NULL) { 00510 sp->s_snmp_errno = SNMPERR_MALLOC; 00511 return -1; 00512 } 00513 memmove(pdu->securityName, 00514 sp->securityName, sp->securityNameLen); 00515 pdu->securityNameLen = sp->securityNameLen; 00516 } 00517 if (pdu->securityModel == -1) 00518 pdu->securityModel = sp->securityModel; 00519 if (pdu->securityLevel == 0) 00520 pdu->securityLevel = sp->securityLevel; 00521 /* WHAT ELSE ?? */ 00522 } 00523 *len = 1; 00524 DEBUGMSGTL(("transport_callback", "hook_build exit\n")); 00525 return 1; 00526 } 00527 00528 int 00529 netsnmp_callback_check_packet(u_char * pkt, size_t len) 00530 { 00531 return 1; 00532 } 00533 00534 netsnmp_pdu * 00535 netsnmp_callback_create_pdu(netsnmp_transport *transport, 00536 void *opaque, size_t olength) 00537 { 00538 netsnmp_pdu *pdu; 00539 netsnmp_callback_pass *cp = 00540 callback_pop_queue(((netsnmp_callback_info *) transport->data)-> 00541 callback_num); 00542 if (!cp) 00543 return NULL; 00544 pdu = cp->pdu; 00545 pdu->transport_data = opaque; 00546 pdu->transport_data_length = olength; 00547 if (opaque) /* if created, we're the server */ 00548 *((int *) opaque) = cp->return_transport_num; 00549 SNMP_FREE(cp); 00550 return pdu; 00551 } 00552 00553 netsnmp_session * 00554 netsnmp_callback_open(int attach_to, 00555 int (*return_func) (int op, 00556 netsnmp_session * session, 00557 int reqid, netsnmp_pdu *pdu, 00558 void *magic), 00559 int (*fpre_parse) (netsnmp_session *, 00560 struct netsnmp_transport_s *, 00561 void *, int), 00562 int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, 00563 int)) 00564 { 00565 netsnmp_session callback_sess, *callback_ss; 00566 netsnmp_transport *callback_tr; 00567 00568 callback_tr = netsnmp_callback_transport(attach_to); 00569 snmp_sess_init(&callback_sess); 00570 callback_sess.callback = return_func; 00571 if (attach_to) { 00572 /* 00573 * client 00574 */ 00575 /* 00576 * trysess.community = (u_char *) callback_ss; 00577 */ 00578 } else { 00579 callback_sess.isAuthoritative = SNMP_SESS_AUTHORITATIVE; 00580 } 00581 callback_sess.remote_port = 0; 00582 callback_sess.retries = 0; 00583 callback_sess.timeout = 30000000; 00584 callback_sess.version = SNMP_DEFAULT_VERSION; /* (mostly) bogus */ 00585 callback_ss = snmp_add_full(&callback_sess, callback_tr, 00586 fpre_parse, 00587 netsnmp_callback_hook_parse, fpost_parse, 00588 netsnmp_callback_hook_build, 00589 NULL, 00590 netsnmp_callback_check_packet, 00591 netsnmp_callback_create_pdu); 00592 if (callback_ss) 00593 callback_ss->local_port = 00594 ((netsnmp_callback_info *) callback_tr->data)->callback_num; 00595 return callback_ss; 00596 } 00597 00598 00599 00600 void 00601 netsnmp_clear_callback_list(void) 00602 { 00603 00604 netsnmp_transport_list *list = trlist, *next = NULL; 00605 netsnmp_transport *tr = NULL; 00606 00607 DEBUGMSGTL(("callback_clear", "called netsnmp_callback_clear_list()\n")); 00608 while (list != NULL) { 00609 next = list->next; 00610 tr = list->transport; 00611 00612 if (tr != NULL) { 00613 tr->f_close(tr); 00614 netsnmp_transport_remove_from_list(&trlist, list->transport); 00615 netsnmp_transport_free(list->transport); 00616 } 00617 list = next; 00618 } 00619 trlist = NULL; 00620 00621 } 00622 00623 #endif /* NETSNMP_TRANSPORT_CALLBACK_DOMAIN */