]> git.decadent.org.uk Git - ap-utils.git/blob - lib/ap_search.c
Imported Upstream version 1.5~pre1
[ap-utils.git] / lib / ap_search.c
1 /*
2  *      ap_search.c from Access Point SNMP Utils for Linux
3  *
4  * Copyright (c) 2002 Roman Festchook <roma at polesye dot net>
5  * Copyright (c) 2003 Jan Rafaj <aputils at cedric dot vabo dot cz>
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 <string.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <fcntl.h>
27 #include <sys/ioctl.h>
28
29 #ifdef OS_SOLARIS
30 #include <sys/sockio.h>
31 #endif
32
33 #ifdef OS_X
34 #include <sys/socket.h>
35 #endif
36
37 #include <sys/socket.h>
38 #include <net/if.h>
39 #include <sys/time.h>
40 #include <errno.h>
41 #include "ap-utils.h"
42 #include "config.h"
43
44 #if defined (__GLIBC__)
45 #include <libgen.h>
46 #endif
47
48 #define SEARCH_COMMUNITY _("Community name: ")
49 #define SEARCH_HEADER _("  NUM  IP ADDRESS       MIB TYPE    NAME")
50 #define MAX_APS LAST_ROW-7
51
52 extern int atmel410_filter;
53 extern char *community;
54
55 char q_pressed = 0;
56 int i, f_ifctr;
57 struct faps {
58     struct in_addr ip;
59     int type;
60 } *fapsa;
61
62 void scan_local_segment (struct sockaddr_in *from, struct sockaddr_in *to,
63     char *ifname)
64 {
65     extern WINDOW *main_sub;
66     extern char *ap_types[];
67     unsigned char message[1024], *start;
68         extern int atmel410_filter; /* to check is this function called from ap-gl utility */
69
70     
71     char Wireless[3][12] = {
72       {0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x01, 0x01, 0x00},
73       {0x2B, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00},
74       {0x2B, 0x06, 0x01, 0x04, 0x01, 0xE0, 0x3E, 0x01, 0x01, 0x01, 0x01, 0x00}
75     };
76
77     /*
78      * operAccessPointName OIDs used to detect AP MIB type [in order
79      * of appearance in the 'for' loop below: ATMEL410, NWN, ATMEL12350]
80      */
81 /*    char operAccessPointName[3][12] = {
82       {0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x01, 0x0A, 0x00},
83       {0x2B, 0x06, 0x01, 0x02, 0x01, 0x01, 0x05, 0x00},
84       {0x2B, 0x06, 0x01, 0x04, 0x01, 0xE0, 0x3E, 0x01, 0x02, 0x01, 0x0A, 0x00}
85     };
86 */
87     int scd_ap_type, last_searched_type=ATMEL12350;
88     int c, s2, errno, len, client_len = SIZE;
89     struct in_addr to_addr_reserv;
90     struct sopts {
91         int broad;
92     } opts = { 1 };
93     struct ip_mreq mult = { {0}, {INADDR_ANY} };
94     varbind varbinds[1];
95     fd_set rds;
96     struct timeval timeout, starttime, curtime;
97     struct timezone tz;
98
99     if (!i)
100         print_help(_("Please wait while scanning, or press 'Q' to quit."));
101
102     if ((s2 = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
103         print_helperr(CREATE_SOCKET_ERROR);
104         getch();
105         return;
106     }
107
108     if (bind(s2, (struct sockaddr *) from, SIZE) == -1) {
109         print_helperr(BIND_SOCKET_ERROR);
110         getch();
111         goto close_ret;
112     }
113
114     if (setsockopt (s2, SOL_SOCKET, SO_BROADCAST, &opts,
115         sizeof(struct sopts)) == -1) {
116         print_helperr(_("Can't set broadcast option on socket. "
117             "Press any key."));
118         getch();
119         goto close_ret;
120     }
121
122     inet_aton("224.0.1.43", &mult.imr_multiaddr);
123     if (setsockopt(s2, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mult,
124         sizeof(struct ip_mreq)) == -1) {
125         print_helperr(_("Can't set multicast membership on socket. "
126             "Press any key."));
127         getch();
128         goto close_ret;
129     }
130
131     mvwaddstr(main_sub, LAST_ROW - 5, 1,
132         _("Scanning via network interface:"));
133     sprintf(message, _("  Index: %i"), f_ifctr);
134     mvwaddstr(main_sub, LAST_ROW - 4, 1, message);
135     sprintf(message, _("  Name: %s"), ifname);
136     mvwaddstr(main_sub, LAST_ROW - 3, 1, message);
137     sprintf(message, _("  IP: %s"), inet_ntoa(from->sin_addr));
138     mvwaddstr(main_sub, LAST_ROW - 2, 1, message);
139
140     if(atmel410_filter) {
141         last_searched_type=ATMEL410;
142     }   
143     
144     for (scd_ap_type = ATMEL410; scd_ap_type <= last_searched_type; scd_ap_type++) {
145         clear_main_new(LAST_ROW - 1, LAST_ROW);
146         sprintf(message, _("Scanning for AP with MIB type: %s"),
147             ap_types[scd_ap_type]);
148         mvwaddstr(main_sub, LAST_ROW - 1, 1, message);
149         wrefresh(main_sub);
150
151 /*      varbinds[0].oid = operAccessPointName[scd_ap_type];*/
152         varbinds[0].oid = Wireless[scd_ap_type];
153         varbinds[0].len_oid = (scd_ap_type == NWN ?
154 /*          8 : sizeof(operAccessPointName[scd_ap_type]));*/
155             8 : sizeof(Wireless[scd_ap_type]));
156         varbinds[0].len_val = 0;
157         varbinds[0].type = NULL_VALUE;
158         len = ber(message, varbinds, 1, GET);
159
160         if (scd_ap_type == NWN) {
161             to_addr_reserv = to->sin_addr;
162             to->sin_addr = mult.imr_multiaddr;
163         }
164
165         errno = 0;
166         if (sendto(s2, message, len, 0, (struct sockaddr *) to, SIZE) == -1) {
167             sprintf(message, _("Failure in sendto(): %s. Press any key."),
168                 strerror(errno));
169             print_helperr(message);
170             getch();
171             continue;
172         }
173
174         timeout.tv_sec = 2;
175         timeout.tv_usec = 0;
176         gettimeofday(&starttime, &tz);
177
178         while (1) {
179
180             FD_ZERO(&rds);
181             FD_SET(s2, &rds);
182             FD_SET(0, &rds);
183
184             /*
185              * Compute time difference. Note that for portability reasons,
186              * we may not rely on select() below setting up timeout to
187              * remaining time upon its return, although this works on Linux.
188              */
189             gettimeofday(&curtime, &tz);
190             c = (curtime.tv_sec * 1000000 + curtime.tv_usec) -
191                 (starttime.tv_sec * 1000000 + starttime.tv_usec);
192             if (c < (2 * 1000000 + 0)) {
193                 c = (2 * 1000000 + 0) - c;
194                 timeout.tv_sec = c / 1000000;
195                 timeout.tv_usec = c - timeout.tv_sec * 1000000;
196             } else
197                 /* Return if nothing has been received after timeout secs. */
198                 break;
199
200             c = select(s2 + 1, &rds, NULL, NULL, &timeout);
201
202             /* error occured */
203             if (c == -1)
204                 break;
205
206             /* timed out */
207             if (c == 0)
208                 continue;
209
210             /* Key pressed. If it is 'Q', return. */
211             if (FD_ISSET(0, &rds)) {
212                 c = getc(stdin);
213                 if (c == 'q' || c == 'Q') {
214                     q_pressed = 1;
215                     goto close_ret;
216                 }
217             }
218
219             /* If data are available for reading on s2, try to read them now. */
220             if (FD_ISSET(s2, &rds))
221                 if ((len = recvfrom(s2, message, 512, 0,
222                     (struct sockaddr *) from, &client_len)) == -1)
223                         continue;
224
225             start = message;
226             if (*start != ASN_HEADER)
227                 continue;
228
229             start += (start[1] & 0x80) ? (start[1] & 0x7F) + 2 : 2;
230             start += *(start + 4) + 5;
231
232             if (*start != RESPONSE)
233                 continue;
234
235             start += (start[1] & 0x80) ? (start[1] & 0x7F) + 2 : 2;
236
237             if (*(start + 5) || *(start + 9) != ASN_HEADER)
238                 continue;
239
240             start += (start[10] & 0x80) ? (start[10] & 0x7F) + 11 : 11;
241
242             if (*(start) != ASN_HEADER)
243                 continue;
244
245             start += (start[1] & 0x80) ? (start[1] & 0x7F) + 2 : 2;
246             start += *(start + 1) + 2;
247
248             if (start[1] & 0x80) {
249                 varbinds[0].len_val = start[2];
250                 start += (start[1] & 0x7F) + 2;
251             } else {
252                 varbinds[0].len_val = start[1];
253                 start += 2;
254             }
255
256             /*
257              * Dupe check. There are 2 reasons why to do this:
258              * 1. If interface-based IP aliases are used (f.e. on Linux),
259              *    it may well happen that their broadcast addresses may
260              *    inadvertedly overlap each other, so the same AP IPs would
261              *    answer twice (for a specific interface and its alias
262              *    device pass).
263              * 2. ATMEL410 devices are capable to answer both queries with
264              *    410 as well as 12350 IDs, while ATMEL12350 device only
265              *    answers queries with 12350 ID. Hence, we need to check
266              *    for duplicate responses from ATMEL410 devices, when
267              *    ATMEL410 check has already performed and ATMEL12350 one
268              *    is pending.
269              * Note, that ATMEL410 devices may, under certain circumstances,
270              * fail to pass the ATMEL410 check, and can be accidentally
271              * marked as an ATMEL12350 device. This is a known bug and
272              * should be eventually solved in any of upcomming releases (TODO).
273              */
274             if (i) {
275                 int dupcnt = 0, j = i - 1;
276
277                 do {
278                     if (from->sin_addr.s_addr == fapsa[j].ip.s_addr)
279                         dupcnt++;
280                 } while (--j >= 0); 
281
282                 if (dupcnt)
283                     continue;
284             }
285
286             /* new AP (unique IP/APtype pair) found */
287
288             fapsa = realloc(fapsa, (i + 1) * sizeof(struct faps));
289
290             fapsa[i].ip = from->sin_addr;
291             fapsa[i].type = scd_ap_type;
292
293             for (len = 0; len < varbinds[0].len_val && start[len]; len++);
294             start[len + 1] = '\0';
295             if (len > 25) {
296                 /* Truncate (limited screen size) and screen-width independent! */
297                 start[COLS-53-atmel410_filter] = '>';
298                 start[COLS-52-atmel410_filter] = '\0';
299             }
300             sprintf(message, "  %3i  %-15s  %-10s  %s", i,
301                 inet_ntoa(fapsa[i].ip), ap_types[fapsa[i].type], start);
302             i++;
303
304             mvwaddstr(main_sub, i, 0, message);
305             wrefresh(main_sub);
306
307             /* Bail out if the number of found devices exceeds sane limit. */
308             if (i == MAX_APS)
309                 goto close_ret;
310         }
311
312         if (scd_ap_type == NWN)
313             to->sin_addr = to_addr_reserv;
314
315     }
316
317 close_ret:
318     close(s2);
319     return;
320 }
321
322 void ap_search()
323 {
324     extern WINDOW *main_sub;
325     int s1, ac;
326     unsigned int ilen = 256;
327     struct sockaddr_in from, to;
328     struct ifconf ifc;
329     struct ifreq *ifr;
330     char *ifbuf_ptr = NULL, *ifrec_ptr;
331     char *old_community;
332     char buf[16];
333
334     print_help(_("Please enter SNMP community name that will be used for AP "
335                  "detection."));
336     mvwaddstr(main_sub, 0, 2, SEARCH_COMMUNITY);
337     wrefresh(main_sub);
338     get_pass(buf, 0, 2 + strlen(SEARCH_COMMUNITY), sizeof(buf));
339     old_community = community;
340     community = malloc(strlen(buf) + 1);
341     strncpy(community, buf, strlen(buf) + 1);
342     print_help("");
343     clear_main(0);
344
345     print_top(NULL, _("Access Points Search"));
346     mvwaddstr(main_sub, 0, 0, SEARCH_HEADER);
347     wrefresh(main_sub);
348
349     if ((s1 = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
350         print_helperr(CREATE_SOCKET_ERROR);
351         goto wait_quit;
352     }
353
354     i = f_ifctr = 0;
355     fapsa = NULL;
356
357     /*
358      * Find all IPs of locally available IPv4 interfaces and corresponding
359      * broadcast and/or point-to-point addresses.
360      */
361     for (;;) {
362         if (!(ifbuf_ptr = realloc(ifbuf_ptr, ilen))) {
363             print_helperr(_("realloc() error."));
364             goto wait_quit;
365         }
366
367         ifc.ifc_buf = ifbuf_ptr;
368         ifc.ifc_len = ilen;
369         if (ioctl(s1, SIOCGIFCONF, &ifc) >= 0)
370             /* Stupid condition thx to BSD and 2 SIOCGIFCONF implementations */
371             if (ifc.ifc_len + sizeof(struct ifreq) + 64 < ilen)
372                 break;
373
374         if (ilen > 200000) {
375             print_helperr(_("Network interface discovery error."));
376             goto wait_quit;
377         }
378         ilen += 100 + (ilen >> 2);
379     }
380     ifrec_ptr = ifbuf_ptr;
381     while (ifrec_ptr < ifbuf_ptr + ifc.ifc_len) {
382         memset(&from.sin_addr, 0, sizeof(struct in_addr));
383         memset(&to.sin_addr, 0, sizeof(struct in_addr));
384         ifr = (struct ifreq *) ifrec_ptr;
385 #ifdef HAVE_SA_LEN
386         ilen = sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len;
387         if (ilen < sizeof(*ifr))
388             ilen = sizeof(*ifr);
389
390         if (ifr->ifr_addr.sa_family == AF_INET)
391             if (ioctl(s1, SIOCGIFFLAGS, ifrec_ptr) == 0)
392                 if (ifr->ifr_flags & IFF_UP)
393                     if (!(ifr->ifr_flags & IFF_LOOPBACK)) {
394                         from.sin_addr =
395                         ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
396                         if (ifr->ifr_flags & IFF_POINTOPOINT) {
397                           ioctl(s1, SIOCGIFDSTADDR, ifrec_ptr);
398                           to.sin_addr =
399                           ((struct sockaddr_in *)&ifr->ifr_dstaddr)->sin_addr;
400                         } else {
401                           ioctl(s1, SIOCGIFBRDADDR, ifrec_ptr);
402                           to.sin_addr =
403                           ((struct sockaddr_in *)&ifr->ifr_broadaddr)->sin_addr;
404                         }
405                     }
406 #else
407         ilen = sizeof(*ifr);
408
409         if (ioctl(s1, SIOCGIFFLAGS, ifrec_ptr) == 0)
410             if (ifr->ifr_flags & IFF_UP)
411                 if (!(ifr->ifr_flags & IFF_LOOPBACK)) {
412                     if (ifr->ifr_flags & IFF_POINTOPOINT) {
413                         ioctl(s1, SIOCGIFDSTADDR, ifrec_ptr);
414                         to.sin_addr =
415                         ((struct sockaddr_in *)&ifr->ifr_dstaddr)->sin_addr;
416                     } else {
417                         ioctl(s1, SIOCGIFBRDADDR, ifrec_ptr);
418                         to.sin_addr =
419                         ((struct sockaddr_in *)&ifr->ifr_broadaddr)->sin_addr;
420                     }
421                     if (ioctl(s1, SIOCGIFADDR, ifrec_ptr) == 0)
422                         if (ifr->ifr_addr.sa_family == AF_INET)
423                             from.sin_addr =
424                             ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
425                 }
426 #endif
427         if (from.sin_addr.s_addr && to.sin_addr.s_addr) {
428             f_ifctr++;
429             from.sin_family = to.sin_family = AF_INET;
430             from.sin_port = INADDR_ANY;
431             to.sin_port = htons(161);
432             scan_local_segment(&from, &to, ifr->ifr_name);
433             clear_main_new(LAST_ROW - 5, LAST_ROW);
434         }
435         ifrec_ptr += ilen;
436         /* No need to process next interfaces if the table is already full, */
437         if (i == MAX_APS)
438             break;
439
440         /* or if someone pressed 'Q'. */
441         if (q_pressed) {
442             q_pressed = 0;
443             break;
444         }
445     }
446
447     close(s1);
448
449     if (!f_ifctr) {
450         print_helperr(_("No local network interfaces found. Press any key."));
451     } else if (!i) {
452         print_help(_("No directly reachable Access Points found. "
453             "Press any key."));
454     } else {
455         if (i == MAX_APS)
456             mvwaddstr(main_sub, LAST_ROW - 1, 1,
457                 _("Single-screen maximum number of APs found."));
458
459         wrefresh(main_sub);
460         print_help(_("# - connect to AP; Q - quit"));
461         while (1)
462             switch (ac = getch()) {
463                 case 'Q':
464                 case 'q':
465                     goto quit;
466                 case '0':
467                 case '1':
468                 case '2':
469                 case '3':
470                 case '4':
471                 case '5':
472                 case '6':
473                 case '7':
474                 case '8':
475                 case '9':
476                     if (ac - '0' > (i - 1))
477                         continue;
478
479                     print_top(NULL, NULL);
480                     clear_main(0);
481                     free(old_community);
482                     connect_options(fapsa[ac - '0'].ip.s_addr,
483                         fapsa[ac - '0'].type + 1);
484                     free(fapsa);
485                     return;
486             }
487     }
488
489 wait_quit:
490     getch();
491 quit:
492     if (ifbuf_ptr)
493         free(ifbuf_ptr);
494
495     if (fapsa)
496         free(fapsa);
497
498     free(community);
499     community = old_community;
500
501     print_help("");
502     print_top(NULL, NULL);
503     clear_main(0);
504 }
505