X-Git-Url: https://git.decadent.org.uk/gitweb/?p=ap-utils.git;a=blobdiff_plain;f=lib%2Fsnmp.c;fp=lib%2Fsnmp.c;h=cab421281a14ff1414c535daa2e841435b51c6ee;hp=03bbf81a277a78bc6cef8a5c358b93b19e3654d5;hb=1aac4ac30a9a0d6cd2182013d2b3fd48b65ed2fd;hpb=5c77e013a46530bb3650f61d768dfed0dd3b72cb diff --git a/lib/snmp.c b/lib/snmp.c index 03bbf81..cab4212 100644 --- a/lib/snmp.c +++ b/lib/snmp.c @@ -3,7 +3,6 @@ * basic snmp packets assembly/disassembly and send/receive functions * * Copyright (c) Roman Festchook - * Jan Rafaj * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 from @@ -22,29 +21,47 @@ #include #include #include -#include -#include +#include #include #include #include #include #include "ap-utils.h" -#define RETRIES 5 - -sigjmp_buf position; - extern char *community; extern short ap_type; -extern int sockfd; extern struct in_addr ap_ip; -int retries; -char *buf = NULL; -static void alarm_handler() +int sockfd = 0, snmp_quit_by_keypress = 0; + +void close_sockfd() +{ + if (sockfd) + close(sockfd); +} + +int open_sockfd() +{ + struct sockaddr_in client; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + return -1; + + memset(&client, 0, sizeof client); + client.sin_family = AF_INET; + client.sin_port = INADDR_ANY; + client.sin_addr.s_addr = INADDR_ANY; + + if (bind(sockfd, (struct sockaddr *) &client, SIZE) == -1) + return -1; + + return 0; +} + +int reopen_sockfd() { - retries--; - siglongjmp(position, 1); + close_sockfd(); + return (open_sockfd()); } /* @@ -145,43 +162,75 @@ int ber(char *message, varbind * varbindlist, int num, int type) int snmp(varbind * varbindlist, int num, int type) { - unsigned char message[1024], *start; + static char buf[1024]; + unsigned char *start; unsigned int num_reply; - int len; + int len = 0, tries = 5; struct sockaddr_in server; + struct timeval timeout; + fd_set rds; if (num == 0) return 1; + /* + * Flush sockfd by reopening. This prevents various 'something received/ + * available on sockfd prior snmp() call' desync problems. + */ + if (reopen_sockfd() == -1) + return 0; + memset(&server, 0, sizeof server); server.sin_family = AF_INET; server.sin_port = htons(161); server.sin_addr.s_addr = ap_ip.s_addr; - signal(SIGALRM, alarm_handler); - retries = RETRIES; - sigsetjmp(position, 1); - if (!retries) { - return 0; - } + while (tries--) { + len = ber(buf, varbindlist, num, type); + if (sendto(sockfd, buf, len, 0, (struct sockaddr *) &server, SIZE) + == -1) { + return 0; + } - alarm(1); - len = ber(message, varbindlist, num, type); - if (sendto(sockfd, message, len, 0, (struct sockaddr *) &server, SIZE) - == -1) { - alarm(0); - return 0; - } - if ((len = recv(sockfd, message, 1024, 0)) == -1) { - alarm(0); - return 0; + FD_ZERO(&rds); + FD_SET(sockfd, &rds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + if (select(sockfd + 1, &rds, NULL, NULL, &timeout) == -1) + return 0; + + if (FD_ISSET(sockfd, &rds)) { + if ((len = recv(sockfd, buf, sizeof buf, 0)) <= 0) + return 0; + else + break; + } + + /* timeout => next try, as long as no key has been pressed */ + + /* + * Allow for quick 'last resort' escape using q/Q. Note: + * we may not use one select() for checking both fd 0 and sockfd, since + * something may appear on sockfd later than key has been pressed => + * give gratuitous 1sec delay for response arrival to sockfd, and THEN + * just poll for anything on fd 0. + */ + if (snmp_quit_by_keypress && type == GET) { + FD_ZERO(&rds); + FD_SET(0, &rds); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select(1, &rds, NULL, NULL, &timeout) == -1) + return 0; + + if (FD_ISSET(0, &rds)) + return 0; + } } - alarm(0); - if (buf) - free(buf); - buf = (char *) malloc(len); - memcpy(buf, message, len); + if (!tries) + return 0; start = buf; num_reply = 0;