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