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