2 * snmp.c from Access Point SNMP Utils for Linux
3 * basic snmp packets assembly/disassembly and send/receive functions
5 * Copyright (c) Roman Festchook <roma at polesye dot net>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License Version 2 from
9 * June 1991 as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <sys/types.h>
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
31 extern char *community;
33 extern struct in_addr ap_ip;
35 int sockfd = 0, snmp_quit_by_keypress = 0;
45 struct sockaddr_in client;
47 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
50 memset(&client, 0, sizeof client);
51 client.sin_family = AF_INET;
52 client.sin_port = INADDR_ANY;
53 client.sin_addr.s_addr = INADDR_ANY;
55 if (bind(sockfd, (struct sockaddr *) &client, SIZE) == -1)
64 return (open_sockfd());
68 unsigned int ber_decode_uint(unsigned char *bevp, int len)
73 out = (out << 7) | (*bevp & (*bevp & 0x80 ? 0x7f : 0xff));
81 int ber(char *message, varbind * varbindlist, int num, int type)
84 char pdu1[1024], pdu[1024], *list = pdu1;
85 int len_var = 0, lenp, len_tmp, len = 0, community_len =
87 char snmp_ver[] = { 0x02, 0x01, 0x00 };
88 const char req_id[] = { 0x02, 0x01 }, err[] = {
91 for (i = 0; i < num; i++) {
92 *(pdu1 + len++) = ASN_HEADER;
94 (varbindlist + i)->len_oid + (varbindlist + i)->len_val + 4;
95 if (!(ap_type == NWN && type == SET)) {
96 *(pdu1 + len++) = 0x82;
97 *(pdu1 + len++) = (len_tmp - (len_tmp % 256)) / 256;
99 *(pdu1 + len++) = len_tmp % 256;
100 *(pdu1 + len++) = OID_VALUE;
101 *(pdu1 + len++) = (varbindlist + i)->len_oid;
102 memcpy(pdu1 + len, (varbindlist + i)->oid,
103 (varbindlist + i)->len_oid);
104 len += (varbindlist + i)->len_oid;
105 *(pdu1 + len++) = (varbindlist + i)->type;
106 *(pdu1 + len++) = (varbindlist + i)->len_val;
107 memcpy(pdu1 + len, (varbindlist + i)->value,
108 (varbindlist + i)->len_val);
109 len += (varbindlist + i)->len_val;
115 len_tmp = len_var + ((ap_type == NWN && type == SET) ? 11 : 13);
116 *(pdu + lenp++) = type;
117 if (!(ap_type == NWN && type == SET)) {
118 *(pdu + lenp++) = 0x82;
119 *(pdu + lenp++) = (len_tmp - (len_tmp % 256)) / 256;
121 *(pdu + lenp++) = len_tmp % 256;
123 memcpy(pdu + lenp, req_id, sizeof(req_id));
124 lenp += sizeof(req_id);
126 (1 + (int) (255.0 * rand() / (RAND_MAX + 1.0))) & 0xFF;
127 memcpy(pdu + lenp, err, sizeof(err));
129 memcpy(pdu + lenp, err, sizeof(err));
131 *(pdu + lenp++) = ASN_HEADER;
133 if (!(ap_type == NWN && type == SET)) {
134 *(pdu + lenp++) = 0x82;
135 *(pdu + lenp++) = (len_tmp - (len_tmp % 256)) / 256;
137 *(pdu + lenp++) = len_tmp % 256;
138 memcpy(pdu + lenp, list, len_var);
141 *message = ASN_HEADER;
144 i = lenp + community_len + 5;
145 if (!(ap_type == NWN && type == SET)) {
146 *(message + len++) = 0x82;
147 *(message + len++) = (i - (i % 256)) / 256;
149 *(message + len++) = i % 256;
151 memcpy(message + len, snmp_ver, 3);
153 *(message + len++) = STRING_VALUE;
154 *(message + len++) = community_len & 0xFF;
155 memcpy(message + len, community, community_len);
156 len += community_len;
157 memcpy(message + len, pdu, lenp);
163 int snmp(varbind * varbindlist, int num, int type)
165 static char buf[1024];
166 unsigned char *start;
167 unsigned int num_reply;
168 int len = 0, tries = 5;
169 struct sockaddr_in server;
170 struct timeval timeout;
177 * Flush sockfd by reopening. This prevents various 'something received/
178 * available on sockfd prior snmp() call' desync problems.
180 if (reopen_sockfd() == -1)
183 memset(&server, 0, sizeof server);
184 server.sin_family = AF_INET;
185 server.sin_port = htons(161);
186 server.sin_addr.s_addr = ap_ip.s_addr;
189 len = ber(buf, varbindlist, num, type);
190 if (sendto(sockfd, buf, len, 0, (struct sockaddr *) &server, SIZE)
196 FD_SET(sockfd, &rds);
200 if (select(sockfd + 1, &rds, NULL, NULL, &timeout) == -1)
203 if (FD_ISSET(sockfd, &rds)) {
204 if ((len = recv(sockfd, buf, sizeof buf, 0)) <= 0)
210 /* timeout => next try, as long as no key has been pressed */
213 * Allow for quick 'last resort' escape using q/Q. Note:
214 * we may not use one select() for checking both fd 0 and sockfd, since
215 * something may appear on sockfd later than key has been pressed =>
216 * give gratuitous 1sec delay for response arrival to sockfd, and THEN
217 * just poll for anything on fd 0.
219 if (snmp_quit_by_keypress && type == GET) {
224 if (select(1, &rds, NULL, NULL, &timeout) == -1)
227 if (FD_ISSET(0, &rds))
237 if (*start != ASN_HEADER) {
241 if (start[1] & 0x80) {
242 start += (start[1] & 0x7F) + 2;
243 len -= ((start[1] & 0x7F) + 2);
249 len -= *(start + 4) + 5;
250 start += *(start + 4) + 5;
252 if (*(start) != RESPONSE) {
258 if (start[1] & 0x80) {
259 start += (start[1] & 0x7F) + 2;
260 len -= ((start[1] & 0x7F) + 2);
267 return -*(start + 8);
271 if (*(start) != ASN_HEADER) {
276 if (start[1] & 0x80) {
277 start += (start[1] & 0x7F) + 2;
278 len -= ((start[1] & 0x7F) + 2);
284 if (*(start) != ASN_HEADER) {
287 if (start[1] & 0x80) {
288 start += (start[1] & 0x7F) + 2;
289 len -= ((start[1] & 0x7F) + 2);
296 varbindlist[num_reply].len_oid = start[1];
297 /* if(varbindlist[num_reply].oid)
298 free(varbindlist[num_reply].oid);
299 varbindlist[num_reply].oid =
300 (char *) malloc(varbindlist[num_reply].len_oid);
301 memcpy(varbindlist[num_reply].oid, start + 2,
302 varbindlist[num_reply].len_oid);
304 varbindlist[num_reply].oid = start + 2;
305 len -= *(start + 1) + 2;
306 start += *(start + 1) + 2;
307 varbindlist[num_reply].type = *(start);
309 if (start[1] & 0x80) {
310 varbindlist[num_reply].len_val = start[2];
311 start += (start[1] & 0x7F) + 2;
312 len -= ((start[1] & 0x7F) + 2);
314 varbindlist[num_reply].len_val = start[1];
319 /* if(varbindlist[num_reply].value)
320 free(varbindlist[num_reply].value);
321 varbindlist[num_reply].value =
322 (char *) malloc(varbindlist[num_reply].len_val);
323 memcpy(varbindlist[num_reply].value, start,
324 varbindlist[num_reply].len_val);
326 varbindlist[num_reply].value = start;
327 len -= varbindlist[num_reply].len_val;
328 start += varbindlist[num_reply].len_val;