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