]> git.decadent.org.uk Git - ap-utils.git/blob - lib/snmp.c
cab421281a14ff1414c535daa2e841435b51c6ee
[ap-utils.git] / lib / snmp.c
1 /*
2  *      snmp.c from Access Point SNMP Utils for Linux
3  *      basic snmp packets assembly/disassembly and send/receive functions
4  *
5  * Copyright (c) Roman Festchook <roma at polesye dot net>
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
29 #include "ap-utils.h"
30
31 extern char *community;
32 extern short ap_type;
33 extern struct in_addr ap_ip;
34
35 int sockfd = 0, snmp_quit_by_keypress = 0;
36
37 void close_sockfd()
38 {
39     if (sockfd)
40         close(sockfd);
41 }
42
43 int open_sockfd()
44 {
45     struct sockaddr_in client;
46
47     if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
48         return -1;
49
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;
54
55     if (bind(sockfd, (struct sockaddr *) &client, SIZE) == -1)
56         return -1;
57
58     return 0;
59 }
60
61 int reopen_sockfd()
62 {
63     close_sockfd();
64     return (open_sockfd());
65 }
66
67 /*
68 unsigned int ber_decode_uint(unsigned char *bevp, int len)
69 {
70     unsigned int out = 0;
71
72     while (len--) {
73         out = (out << 7) | (*bevp & (*bevp & 0x80 ? 0x7f : 0xff));
74         bevp++;
75     }
76
77     return out;
78 }
79 */
80
81 int ber(char *message, varbind * varbindlist, int num, int type)
82 {
83
84     char pdu1[1024], pdu[1024], *list = pdu1;
85     int len_var = 0, lenp, len_tmp, len = 0, community_len =
86         strlen(community), i;
87     char snmp_ver[] = { 0x02, 0x01, 0x00 };
88     const char req_id[] = { 0x02, 0x01 }, err[] = {
89     0x02, 0x01, 0x00};
90
91     for (i = 0; i < num; i++) {
92         *(pdu1 + len++) = ASN_HEADER;
93         len_tmp =
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;
98         }
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;
110     }
111
112     len_var = len;
113
114     lenp = 0;
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;
120     }
121     *(pdu + lenp++) = len_tmp % 256;
122
123     memcpy(pdu + lenp, req_id, sizeof(req_id));
124     lenp += sizeof(req_id);
125     *(pdu + lenp++) =
126         (1 + (int) (255.0 * rand() / (RAND_MAX + 1.0))) & 0xFF;
127     memcpy(pdu + lenp, err, sizeof(err));
128     lenp += sizeof(err);
129     memcpy(pdu + lenp, err, sizeof(err));
130     lenp += sizeof(err);
131     *(pdu + lenp++) = ASN_HEADER;
132     len_tmp = len_var;
133     if (!(ap_type == NWN && type == SET)) {
134         *(pdu + lenp++) = 0x82;
135         *(pdu + lenp++) = (len_tmp - (len_tmp % 256)) / 256;
136     }
137     *(pdu + lenp++) = len_tmp % 256;
138     memcpy(pdu + lenp, list, len_var);
139     lenp += len_var;
140
141     *message = ASN_HEADER;
142     len = 1;
143
144     i = lenp + community_len + 5;
145     if (!(ap_type == NWN && type == SET)) {
146         *(message + len++) = 0x82;
147         *(message + len++) = (i - (i % 256)) / 256;
148     }
149     *(message + len++) = i % 256;
150
151     memcpy(message + len, snmp_ver, 3);
152     len += 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);
158     len += lenp;
159
160     return len;
161 }
162
163 int snmp(varbind * varbindlist, int num, int type)
164 {
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;
171     fd_set rds;
172
173     if (num == 0)
174         return 1;
175
176     /*
177      * Flush sockfd by reopening. This prevents various 'something received/
178      * available on sockfd prior snmp() call' desync problems.
179      */
180     if (reopen_sockfd() == -1)
181         return 0;
182
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;
187
188     while (tries--) {
189         len = ber(buf, varbindlist, num, type);
190         if (sendto(sockfd, buf, len, 0, (struct sockaddr *) &server, SIZE)
191             == -1) {
192             return 0;
193         }
194
195         FD_ZERO(&rds);
196         FD_SET(sockfd, &rds);
197         timeout.tv_sec = 1;
198         timeout.tv_usec = 0;
199
200         if (select(sockfd + 1, &rds, NULL, NULL, &timeout) == -1)
201             return 0;
202
203         if (FD_ISSET(sockfd, &rds)) {
204             if ((len = recv(sockfd, buf, sizeof buf, 0)) <= 0)
205                 return 0;
206             else
207                 break;
208         }
209
210         /* timeout => next try, as long as no key has been pressed */
211
212         /*
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.
218          */
219         if (snmp_quit_by_keypress && type == GET) {
220             FD_ZERO(&rds);
221             FD_SET(0, &rds);
222             timeout.tv_sec = 0;
223             timeout.tv_usec = 0;
224             if (select(1, &rds, NULL, NULL, &timeout) == -1)
225                 return 0;
226
227             if (FD_ISSET(0, &rds))
228                 return 0;
229         }
230     }
231
232     if (!tries)
233         return 0;
234
235     start = buf;
236     num_reply = 0;
237     if (*start != ASN_HEADER) {
238         return 0;
239     }
240
241     if (start[1] & 0x80) {
242         start += (start[1] & 0x7F) + 2;
243         len -= ((start[1] & 0x7F) + 2);
244     } else {
245         start += 2;
246         len -= 2;
247     }
248
249     len -= *(start + 4) + 5;
250     start += *(start + 4) + 5;
251
252     if (*(start) != RESPONSE) {
253         return 0;
254     }
255
256
257
258     if (start[1] & 0x80) {
259         start += (start[1] & 0x7F) + 2;
260         len -= ((start[1] & 0x7F) + 2);
261     } else {
262         start += 2;
263         len -= 2;
264     }
265
266     if (*(start + 5))
267         return -*(start + 8);
268
269     start += 9;
270     len -= 9;
271     if (*(start) != ASN_HEADER) {
272         return 0;
273     }
274
275
276     if (start[1] & 0x80) {
277         start += (start[1] & 0x7F) + 2;
278         len -= ((start[1] & 0x7F) + 2);
279     } else {
280         start += 2;
281         len -= 2;
282     }
283     while (len) {
284         if (*(start) != ASN_HEADER) {
285             return num_reply;
286         }
287         if (start[1] & 0x80) {
288             start += (start[1] & 0x7F) + 2;
289             len -= ((start[1] & 0x7F) + 2);
290         } else {
291             start += 2;
292             len -= 2;
293         }
294
295
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);
303 */
304         varbindlist[num_reply].oid = start + 2;
305         len -= *(start + 1) + 2;
306         start += *(start + 1) + 2;
307         varbindlist[num_reply].type = *(start);
308
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);
313         } else {
314             varbindlist[num_reply].len_val = start[1];
315             start += 2;
316             len -= 2;
317         }
318
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);
325 */
326         varbindlist[num_reply].value = start;
327         len -= varbindlist[num_reply].len_val;
328         start += varbindlist[num_reply].len_val;
329         num_reply++;
330     }
331
332     return num_reply;
333 }