1 /*
2  * Copyright (C) 2008 Fred Chien <fred@lxde.org>
3  *               2008 Jim Huang <jserv.tw@gmail.com>
4  *
5  * This file is a part of LXPanel project.
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 as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 #include <stdio.h>
22 #include <string.h>
23 #include <glib.h>
24 #include <glib/gi18n.h>
25 #include <sys/time.h>
26 #include <iwlib.h>
27 #include "netstat.h"
28 #include "wireless.h"
29 
30 /*
31 static const char * iw_ie_cypher_name[] = {
32     "none",
33     "WEP-40",
34     "TKIP",
35     "WRAP",
36     "CCMP",
37     "WEP-104",
38 };
39 
40 static const char * iw_ie_key_mgmt_name[] = {
41     "none",
42     "802.1x",
43     "PSK",
44 };
45 */
46 
wireless_aplist_free(void * aplist,GObject * dummy)47 void wireless_aplist_free(void *aplist, GObject *dummy)
48 {
49     APLIST *ptr;
50     APLIST *delptr;
51 
52     if (aplist!=NULL) {
53         ptr = aplist;
54         do {
55             g_free(ptr->info->essid);
56             g_free(ptr->info->apaddr);
57             g_free(ptr->info);
58 
59             delptr = ptr;
60             ptr = ptr->next;
61             g_free(delptr);
62         } while(ptr!=NULL);
63     }
64 }
65 
wireless_gen_ie(ap_info * info,unsigned char * buffer,int ielen)66 void wireless_gen_ie(ap_info *info, unsigned char *buffer, int ielen)
67 {
68 	int offset = 2;
69 	int count;
70 	int i;
71 	unsigned char wpa1_oui[3] = {0x00, 0x50, 0xf2};
72 	unsigned char wpa2_oui[3] = {0x00, 0x0f, 0xac};
73 	unsigned char *wpa_oui;
74 
75 	/* check IE type */
76 	switch(buffer[0]) {
77 		case 0xdd: /* WPA or else */
78 			wpa_oui = wpa1_oui;
79 
80 			if((ielen < 8)
81 				|| (memcmp(&buffer[offset], wpa_oui, 3) != 0)
82 				|| (buffer[offset + 3] != 0x01)) {
83 				if (info->haskey)
84 					info->en_method = NS_WIRELESS_AUTH_WEP;
85 				else
86 					info->en_method = NS_WIRELESS_AUTH_OFF;
87 
88 				info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
89 				info->group = NS_IW_IE_CIPHER_NONE;
90 				info->pairwise = NS_IW_IE_CIPHER_NONE;
91 
92 				return;
93 			}
94 
95 			/* OUI and 0x01 */
96 			offset += 4;
97 			break;
98 
99 		case 0x30: /* IEEE 802.11i/WPA2 */
100 			wpa_oui = wpa2_oui;
101 			break;
102 
103 		default: /* Unknown */
104 			if (info->haskey)
105 				info->en_method = NS_WIRELESS_AUTH_WEP;
106 			else
107 				info->en_method = NS_WIRELESS_AUTH_OFF;
108 
109 			info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
110 			info->group = NS_IW_IE_CIPHER_NONE;
111 			info->pairwise = NS_IW_IE_CIPHER_NONE;
112 			return;
113 	}
114 
115 	/* assume TKIP */
116 	info->en_method = NS_WIRELESS_AUTH_WPA;
117 	info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
118 	info->group = NS_IW_IE_CIPHER_TKIP;
119 	info->pairwise = NS_IW_IE_CIPHER_TKIP;
120 
121 	/* 2 bytes for version number (little endian) */
122 	offset += 2;
123 
124 	/* check group cipher for short IE */
125 	if ((offset+4) > ielen) {
126 		/* this is a short IE, we can assume TKIP/TKIP. */
127 		info->group = NS_IW_IE_CIPHER_TKIP;
128 		info->pairwise = NS_IW_IE_CIPHER_TKIP;
129 		return;
130 	}
131 
132 	/* 4 Bytes for group cipher information [3 bytes][1 Byte] */
133 	if(memcmp(&buffer[offset], wpa_oui, 3)!=0) {
134 		/* the group cipher is proprietary */
135 		info->group = NS_IW_IE_CIPHER_NONE;
136 	} else {
137 		/* pick a byte for type of group cipher */
138 		info->group = buffer[offset+3];
139 	}
140 	offset += 4;
141 
142 	/* check pairwise cipher for short IE */
143 	if ((offset+2) > ielen) {
144 		/* this is a short IE, we can assume TKIP. */
145 		info->pairwise = NS_IW_IE_CIPHER_TKIP;
146 		return;
147 	}
148 
149 	/* 2 bytes for number of pairwise ciphers (little endian) */
150 	count = buffer[offset] | (buffer[offset + 1] << 8);
151 	offset += 2;
152 
153 	/* if we are done */
154 	if ((offset+4*count) > ielen) {
155 		return;
156 	}
157 
158 	/* choose first cipher of pairwise ciphers to use,
159 	 * FIXME: Let user decide the cipher is the best way. */
160 	for(i=0;i<count;i++) {
161 		if(memcmp(&buffer[offset], wpa_oui, 3)==0) {
162 			/* pick a byte for type of group cipher */
163 			info->pairwise = buffer[offset+3];
164 		}
165 		offset += 4;
166     }
167 
168 	/* check authentication suites */
169 	if ((offset+2) > ielen) {
170 		/* this is a short IE, we can assume TKIP. */
171 		info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
172 		return;
173 	}
174 
175 	/* 2 bytes for number of authentication suites (little endian) */
176 	count = buffer[offset] | (buffer[offset + 1] << 8);
177 	offset += 2;
178 
179 	/* if we are done */
180 	if ((offset+4*count) > ielen) {
181 		return;
182 	}
183 
184 	/* choose first key_mgmt of authentication suites to use,
185 	 * FIXME: Let user decide the key_mgmt is the best way. */
186 	for(i=0;i<count;i++) {
187 		if(memcmp(&buffer[offset], wpa_oui, 3)==0) {
188 			/* pick a byte for type of key_mgmt */
189 			info->key_mgmt = buffer[offset+3];
190 		}
191 		offset += 4;
192     }
193 }
194 
195 ap_info *
wireless_parse_scanning_event(struct iw_event * event,ap_info * oldinfo)196 wireless_parse_scanning_event(struct iw_event *event, ap_info *oldinfo)
197 {
198 	ap_info *info;
199 
200 	/* found a new AP */
201 	if (event->cmd==SIOCGIWAP) {
202 		char buf[128];
203 		info = g_new0(ap_info, 1);
204 		info->apaddr = g_strdup(iw_sawap_ntop(&event->u.ap_addr, buf));
205 		info->en_method = NS_WIRELESS_AUTH_OFF;
206 		info->haskey = FALSE;
207 		info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
208 		info->group = NS_IW_IE_CIPHER_TKIP;
209 		info->pairwise = NS_IW_IE_CIPHER_TKIP;
210 	} else {
211 		info = oldinfo;
212 	}
213 
214     switch (event->cmd) {
215         case SIOCGIWESSID: /* ESSID */
216 			if (!event->u.essid.flags
217 				|| event->u.essid.length==0
218 				|| strlen(event->u.essid.pointer)==0) {
219 				info->essid = NULL;
220 			} else {
221 				info->essid = g_strndup(event->u.essid.pointer, event->u.essid.length);
222 			}
223             break;
224 		case IWEVQUAL: /* Signal Quality */
225 				info->quality = (int)rint((log (event->u.qual.qual) / log (92)) * 100.0);
226             break;
227         case SIOCGIWENCODE: /* Encryption */
228 			if (!event->u.data.pointer)
229 				event->u.data.flags |= IW_ENCODE_NOKEY;
230 
231 			if (!(event->u.data.flags & IW_ENCODE_DISABLED)) {
232 				info->haskey = TRUE;
233 				/* assume WEP */
234 				info->en_method = NS_WIRELESS_AUTH_WEP;
235 			} else {
236 				info->haskey = FALSE;
237 				info->en_method = NS_WIRELESS_AUTH_OFF;
238 			}
239             break;
240 		case IWEVGENIE: /* Extra information */
241 		{
242 			int offset = 0;
243 			int ielen = event->u.data.length;
244 			unsigned char *iebuf = event->u.data.pointer;
245 
246 			while(offset <= (ielen - 2)) {
247 				/* check IE type */
248 				switch(iebuf[offset]) {
249 					case 0xdd: /* WPA or else */
250 					case 0x30: /* IEEE 802.11i/WPA2 */
251 						wireless_gen_ie(info, iebuf, ielen);
252 						break;
253 				}
254 				offset += iebuf[offset+1] + 2;
255 			}
256 		}
257 			break;
258 	}
259 
260     return info;
261 }
262 
263 /* when we have some workaround problems,
264  * we need this function to rescanning access-point.
265  * */
wireless_refresh(int iwsockfd,const char * ifname)266 gboolean wireless_refresh(int iwsockfd, const char *ifname)
267 {
268 	struct iwreq wrq;
269 	struct iw_range range;
270 	struct timeval tv;
271 	fd_set rfds; /* File descriptors for select */
272 	int selfd;
273 	char buffer[IW_SCAN_MAX_DATA];
274 
275 	/* setting interfaces name */
276 	strncpy(wrq.ifr_name, ifname, IFNAMSIZ);
277 
278 	/* Getting range */
279 	iw_get_range_info(iwsockfd, ifname, &range);
280 
281 	/* check scanning support */
282 	if (range.we_version_compiled < 14)
283 		return FALSE;
284 
285 	/* Initiate Scanning */
286 	wrq.u.data.pointer = buffer;
287 	wrq.u.data.length = IW_SCAN_MAX_DATA;
288 	wrq.u.data.flags = 0;
289 
290 	if (ioctl(iwsockfd, SIOCSIWSCAN, &wrq) < 0) {
291 		if (errno!=EPERM)
292 			return FALSE;
293 	}
294 
295 	/* Init timeout value -> 250ms */
296 	tv.tv_sec = 0;
297 	tv.tv_usec = 250000;
298 
299 	/* Scanning APs */
300 	while(1) {
301 		if (ioctl(iwsockfd, SIOCGIWSCAN, &wrq) < 0) {
302 			if (errno == EAGAIN) { /* not yet ready */
303 				FD_ZERO(&rfds);
304 				selfd = -1;
305 
306 				if (select(selfd + 1, &rfds, NULL, NULL, &tv)==0)
307 					continue; /* timeout */
308 			} else {
309 				break;
310 			}
311 		}
312 
313 		if (wrq.u.data.length <= 0)
314 			break;
315 	}
316 
317 	return TRUE;
318 }
319 
wireless_scanning(int iwsockfd,const char * ifname)320 APLIST *wireless_scanning(int iwsockfd, const char *ifname)
321 {
322 	APLIST *ap = NULL;
323 	APLIST *newap;
324 
325 	struct iwreq wrq;
326 	int scanflags = 0;		/* Flags for scan */
327 	unsigned char *	buffer = NULL;	/* Results */
328 	int buflen = IW_SCAN_MAX_DATA;	/* Min for compat WE < 17 */
329 	struct iw_range	range;
330 	int has_range;
331 	struct timeval tv;		/* select timeout */
332 	int timeout = 15000000;		/* 15s */
333 
334 	/* Get range stuff */
335 	has_range = (iw_get_range_info(iwsockfd, ifname, &range) >= 0);
336 
337 	/* Check if the interface could support scanning. */
338 	if ((!has_range) || (range.we_version_compiled < 14)) {
339 		fprintf(stderr, "%-8.16s  Interface doesn't support scanning.\n\n",
340 				ifname);
341 		return NULL;
342 	}
343 
344 	/* Init timeout value -> 250ms between set and first get */
345 	tv.tv_sec = 0;
346 	tv.tv_usec = 250000;
347 
348 	wrq.u.data.pointer = NULL;
349 	wrq.u.data.flags = 0;
350 	wrq.u.data.length = 0;
351 
352 	/* Initiate Scanning */
353 	if (iw_set_ext(iwsockfd, ifname, SIOCSIWSCAN, &wrq) < 0) {
354 		if ((errno != EPERM) || (scanflags != 0)) {
355 			fprintf(stderr, "%-8.16s  Interface doesn't support "
356 				"scanning : %s\n\n", ifname, strerror(errno));
357 			return NULL;
358 		}
359 		tv.tv_usec = 0;
360 	}
361 	timeout -= tv.tv_usec;
362 
363 	/* Forever */
364 	while (1) {
365 		fd_set rfds;		/* File descriptors for select */
366 		int last_fd;	/* Last fd */
367 		int ret;
368 
369 		/* Guess what ? We must re-generate rfds each time */
370 		FD_ZERO(&rfds);
371 		last_fd = -1;
372 
373 		/* In here, add the rtnetlink fd in the list */
374 
375 		/* Wait until something happens */
376 		ret = select(last_fd + 1, &rfds, NULL, NULL, &tv);
377 
378 		/* Check if there was an error */
379 		if (ret < 0) {
380 			if (errno == EAGAIN || errno == EINTR)
381 				continue;
382 			fprintf(stderr, "Unhandled signal - exiting...\n");
383 			return NULL;
384 		}
385 
386 		/* Check if there was a timeout */
387 		if (ret == 0) {
388 			unsigned char *newbuf;
389 
390 realloc:
391 			/* (Re)allocate the buffer - realloc(NULL, len) == malloc(len) */
392 			newbuf = realloc(buffer, buflen);
393 			if (newbuf == NULL) {
394 				if (buffer)
395 					free(buffer);
396 				fprintf(stderr, "%s: Allocation failed\n", __FUNCTION__);
397 				return NULL;
398 			}
399 			buffer = newbuf;
400 
401 			/* Try to read the results */
402 			wrq.u.data.pointer = buffer;
403 			wrq.u.data.flags = 0;
404 			wrq.u.data.length = buflen;
405 			if (iw_get_ext(iwsockfd, ifname, SIOCGIWSCAN, &wrq) < 0)	{
406 				/* Check if buffer was too small (WE-17 only) */
407 				if ((errno == E2BIG) &&
408 				    (range.we_version_compiled > 16)) {
409 					/* Check if the driver gave us any hints. */
410 					if (wrq.u.data.length > buflen)
411 						buflen = wrq.u.data.length;
412 					else
413 						buflen *= 2;
414 					/* Try again */
415 					goto realloc;
416 				}
417 
418 				/* Check if results not available yet */
419 				if(errno == EAGAIN) {
420 					/* Restart timer for only 100ms*/
421 					tv.tv_sec = 0;
422 					tv.tv_usec = 100000;
423 					timeout -= tv.tv_usec;
424 					if (timeout > 0)
425 						continue; /* Try again later */
426 				}
427 
428 				/* Bad error */
429 				free(buffer);
430 				fprintf(stderr,
431 				"%-8.16s  Failed to read scan data : %s\n\n",
432 						ifname, strerror(errno));
433 				return NULL;
434 			}
435 			else
436 				/* We have the results, go to process them */
437 				break;
438 		}
439 
440 		/* In here, check if event and event type
441 		 * if scan event, read results. All errors bad & no reset timeout */
442 	}
443 
444 	if(wrq.u.data.length) {
445 		struct iw_event           iwe;
446 		struct stream_descr       stream;
447 		int                       ret;
448 
449 		iw_init_event_stream(&stream, (char *) buffer, wrq.u.data.length);
450 		do {
451 			/* Extract an event and print it */
452 			ret = iw_extract_event_stream(&stream, &iwe, range.we_version_compiled);
453 			if (iwe.cmd==SIOCGIWAP) {
454 				newap = malloc(sizeof(APLIST));
455 				newap->info = NULL;
456 				newap->next = ap;
457 				ap = newap;
458 			}
459 			ap->info = wireless_parse_scanning_event(&iwe, ap->info);
460 		}
461 		while (ret > 0);
462 		printf("\n");
463 	}
464 	else
465 		printf("%-8.16s  No scan results\n\n", ifname);
466 
467 	free(buffer);
468 	return ap;
469 }
470