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