1 /*
2 * wpa_gui - WpaGui class
3 * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15 #ifdef __MINGW32__
16 /* Need to get getopt() */
17 #include <unistd.h>
18 #endif
19
20 #include <cstdio>
21 #include <QMessageBox>
22
23 #include "wpagui.h"
24 #include "dirent.h"
25 #include "wpa_ctrl.h"
26 #include "userdatarequest.h"
27 #include "networkconfig.h"
28
WpaGui(QWidget * parent,const char *,Qt::WFlags)29 WpaGui::WpaGui(QWidget *parent, const char *, Qt::WFlags)
30 : QMainWindow(parent)
31 {
32 setupUi(this);
33
34 (void) statusBar();
35
36 connect(helpIndexAction, SIGNAL(activated()), this, SLOT(helpIndex()));
37 connect(helpContentsAction, SIGNAL(activated()), this,
38 SLOT(helpContents()));
39 connect(helpAboutAction, SIGNAL(activated()), this, SLOT(helpAbout()));
40 connect(fileExitAction, SIGNAL(activated()), this, SLOT(close()));
41 connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect()));
42 connect(scanButton, SIGNAL(clicked()), this, SLOT(scan()));
43 connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB()));
44 connect(fileEventHistoryAction, SIGNAL(activated()), this,
45 SLOT(eventHistory()));
46 connect(networkSelect, SIGNAL(activated(const QString&)), this,
47 SLOT(selectNetwork(const QString&)));
48 connect(fileEdit_networkAction, SIGNAL(activated()), this,
49 SLOT(editNetwork()));
50 connect(fileAdd_NetworkAction, SIGNAL(activated()), this,
51 SLOT(addNetwork()));
52 connect(adapterSelect, SIGNAL(activated(const QString&)), this,
53 SLOT(selectAdapter(const QString&)));
54
55 eh = NULL;
56 scanres = NULL;
57 udr = NULL;
58 ctrl_iface = NULL;
59 ctrl_conn = NULL;
60 monitor_conn = NULL;
61 msgNotifier = NULL;
62 ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
63
64 parse_argv();
65
66 textStatus->setText("connecting to wpa_supplicant");
67 timer = new QTimer(this);
68 connect(timer, SIGNAL(timeout()), SLOT(ping()));
69 timer->start(1000, FALSE);
70
71 if (openCtrlConnection(ctrl_iface) < 0) {
72 printf("Failed to open control connection to "
73 "wpa_supplicant.\n");
74 }
75
76 updateStatus();
77 networkMayHaveChanged = true;
78 updateNetworks();
79 }
80
81
~WpaGui()82 WpaGui::~WpaGui()
83 {
84 delete msgNotifier;
85
86 if (monitor_conn) {
87 wpa_ctrl_detach(monitor_conn);
88 wpa_ctrl_close(monitor_conn);
89 monitor_conn = NULL;
90 }
91 if (ctrl_conn) {
92 wpa_ctrl_close(ctrl_conn);
93 ctrl_conn = NULL;
94 }
95
96 if (eh) {
97 eh->close();
98 delete eh;
99 eh = NULL;
100 }
101
102 if (scanres) {
103 scanres->close();
104 delete scanres;
105 scanres = NULL;
106 }
107
108 if (udr) {
109 udr->close();
110 delete udr;
111 udr = NULL;
112 }
113
114 free(ctrl_iface);
115 ctrl_iface = NULL;
116
117 free(ctrl_iface_dir);
118 ctrl_iface_dir = NULL;
119 }
120
121
languageChange()122 void WpaGui::languageChange()
123 {
124 retranslateUi(this);
125 }
126
127
parse_argv()128 void WpaGui::parse_argv()
129 {
130 int c;
131 for (;;) {
132 c = getopt(qApp->argc(), qApp->argv(), "i:p:");
133 if (c < 0)
134 break;
135 switch (c) {
136 case 'i':
137 free(ctrl_iface);
138 ctrl_iface = strdup(optarg);
139 break;
140 case 'p':
141 free(ctrl_iface_dir);
142 ctrl_iface_dir = strdup(optarg);
143 break;
144 }
145 }
146 }
147
148
openCtrlConnection(const char * ifname)149 int WpaGui::openCtrlConnection(const char *ifname)
150 {
151 char *cfile;
152 int flen;
153 char buf[2048], *pos, *pos2;
154 size_t len;
155
156 if (ifname) {
157 if (ifname != ctrl_iface) {
158 free(ctrl_iface);
159 ctrl_iface = strdup(ifname);
160 }
161 } else {
162 #ifdef CONFIG_CTRL_IFACE_UDP
163 free(ctrl_iface);
164 ctrl_iface = strdup("udp");
165 #endif /* CONFIG_CTRL_IFACE_UDP */
166 #ifdef CONFIG_CTRL_IFACE_UNIX
167 struct dirent *dent;
168 DIR *dir = opendir(ctrl_iface_dir);
169 free(ctrl_iface);
170 ctrl_iface = NULL;
171 if (dir) {
172 while ((dent = readdir(dir))) {
173 #ifdef _DIRENT_HAVE_D_TYPE
174 /* Skip the file if it is not a socket.
175 * Also accept DT_UNKNOWN (0) in case
176 * the C library or underlying file
177 * system does not support d_type. */
178 if (dent->d_type != DT_SOCK &&
179 dent->d_type != DT_UNKNOWN)
180 continue;
181 #endif /* _DIRENT_HAVE_D_TYPE */
182
183 if (strcmp(dent->d_name, ".") == 0 ||
184 strcmp(dent->d_name, "..") == 0)
185 continue;
186 printf("Selected interface '%s'\n",
187 dent->d_name);
188 ctrl_iface = strdup(dent->d_name);
189 break;
190 }
191 closedir(dir);
192 }
193 #endif /* CONFIG_CTRL_IFACE_UNIX */
194 #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
195 struct wpa_ctrl *ctrl;
196 int ret;
197
198 free(ctrl_iface);
199 ctrl_iface = NULL;
200
201 ctrl = wpa_ctrl_open(NULL);
202 if (ctrl) {
203 len = sizeof(buf) - 1;
204 ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf,
205 &len, NULL);
206 if (ret >= 0) {
207 buf[len] = '\0';
208 pos = strchr(buf, '\n');
209 if (pos)
210 *pos = '\0';
211 ctrl_iface = strdup(buf);
212 }
213 wpa_ctrl_close(ctrl);
214 }
215 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
216 }
217
218 if (ctrl_iface == NULL)
219 return -1;
220
221 #ifdef CONFIG_CTRL_IFACE_UNIX
222 flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2;
223 cfile = (char *) malloc(flen);
224 if (cfile == NULL)
225 return -1;
226 snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface);
227 #else /* CONFIG_CTRL_IFACE_UNIX */
228 flen = strlen(ctrl_iface) + 1;
229 cfile = (char *) malloc(flen);
230 if (cfile == NULL)
231 return -1;
232 snprintf(cfile, flen, "%s", ctrl_iface);
233 #endif /* CONFIG_CTRL_IFACE_UNIX */
234
235 if (ctrl_conn) {
236 wpa_ctrl_close(ctrl_conn);
237 ctrl_conn = NULL;
238 }
239
240 if (monitor_conn) {
241 delete msgNotifier;
242 msgNotifier = NULL;
243 wpa_ctrl_detach(monitor_conn);
244 wpa_ctrl_close(monitor_conn);
245 monitor_conn = NULL;
246 }
247
248 printf("Trying to connect to '%s'\n", cfile);
249 ctrl_conn = wpa_ctrl_open(cfile);
250 if (ctrl_conn == NULL) {
251 free(cfile);
252 return -1;
253 }
254 monitor_conn = wpa_ctrl_open(cfile);
255 free(cfile);
256 if (monitor_conn == NULL) {
257 wpa_ctrl_close(ctrl_conn);
258 return -1;
259 }
260 if (wpa_ctrl_attach(monitor_conn)) {
261 printf("Failed to attach to wpa_supplicant\n");
262 wpa_ctrl_close(monitor_conn);
263 monitor_conn = NULL;
264 wpa_ctrl_close(ctrl_conn);
265 ctrl_conn = NULL;
266 return -1;
267 }
268
269 #if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
270 msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn),
271 QSocketNotifier::Read, this);
272 connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs()));
273 #endif
274
275 adapterSelect->clear();
276 adapterSelect->insertItem(ctrl_iface);
277 adapterSelect->setCurrentItem(0);
278
279 len = sizeof(buf) - 1;
280 if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >=
281 0) {
282 buf[len] = '\0';
283 pos = buf;
284 while (*pos) {
285 pos2 = strchr(pos, '\n');
286 if (pos2)
287 *pos2 = '\0';
288 if (strcmp(pos, ctrl_iface) != 0)
289 adapterSelect->insertItem(pos);
290 if (pos2)
291 pos = pos2 + 1;
292 else
293 break;
294 }
295 }
296
297 return 0;
298 }
299
300
wpa_gui_msg_cb(char * msg,size_t)301 static void wpa_gui_msg_cb(char *msg, size_t)
302 {
303 /* This should not happen anymore since two control connections are
304 * used. */
305 printf("missed message: %s\n", msg);
306 }
307
308
ctrlRequest(const char * cmd,char * buf,size_t * buflen)309 int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen)
310 {
311 int ret;
312
313 if (ctrl_conn == NULL)
314 return -3;
315 ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen,
316 wpa_gui_msg_cb);
317 if (ret == -2)
318 printf("'%s' command timed out.\n", cmd);
319 else if (ret < 0)
320 printf("'%s' command failed.\n", cmd);
321
322 return ret;
323 }
324
325
updateStatus()326 void WpaGui::updateStatus()
327 {
328 char buf[2048], *start, *end, *pos;
329 size_t len;
330
331 pingsToStatusUpdate = 10;
332
333 len = sizeof(buf) - 1;
334 if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) {
335 textStatus->setText("Could not get status from "
336 "wpa_supplicant");
337 textAuthentication->clear();
338 textEncryption->clear();
339 textSsid->clear();
340 textBssid->clear();
341 textIpAddress->clear();
342 return;
343 }
344
345 buf[len] = '\0';
346
347 bool auth_updated = false, ssid_updated = false;
348 bool bssid_updated = false, ipaddr_updated = false;
349 bool status_updated = false;
350 char *pairwise_cipher = NULL, *group_cipher = NULL;
351
352 start = buf;
353 while (*start) {
354 bool last = false;
355 end = strchr(start, '\n');
356 if (end == NULL) {
357 last = true;
358 end = start;
359 while (end[0] && end[1])
360 end++;
361 }
362 *end = '\0';
363
364 pos = strchr(start, '=');
365 if (pos) {
366 *pos++ = '\0';
367 if (strcmp(start, "bssid") == 0) {
368 bssid_updated = true;
369 textBssid->setText(pos);
370 } else if (strcmp(start, "ssid") == 0) {
371 ssid_updated = true;
372 textSsid->setText(pos);
373 } else if (strcmp(start, "ip_address") == 0) {
374 ipaddr_updated = true;
375 textIpAddress->setText(pos);
376 } else if (strcmp(start, "wpa_state") == 0) {
377 status_updated = true;
378 textStatus->setText(pos);
379 } else if (strcmp(start, "key_mgmt") == 0) {
380 auth_updated = true;
381 textAuthentication->setText(pos);
382 /* TODO: could add EAP status to this */
383 } else if (strcmp(start, "pairwise_cipher") == 0) {
384 pairwise_cipher = pos;
385 } else if (strcmp(start, "group_cipher") == 0) {
386 group_cipher = pos;
387 }
388 }
389
390 if (last)
391 break;
392 start = end + 1;
393 }
394
395 if (pairwise_cipher || group_cipher) {
396 QString encr;
397 if (pairwise_cipher && group_cipher &&
398 strcmp(pairwise_cipher, group_cipher) != 0) {
399 encr.append(pairwise_cipher);
400 encr.append(" + ");
401 encr.append(group_cipher);
402 } else if (pairwise_cipher) {
403 encr.append(pairwise_cipher);
404 } else if (group_cipher) {
405 encr.append(group_cipher);
406 encr.append(" [group key only]");
407 } else {
408 encr.append("?");
409 }
410 textEncryption->setText(encr);
411 } else
412 textEncryption->clear();
413
414 if (!status_updated)
415 textStatus->clear();
416 if (!auth_updated)
417 textAuthentication->clear();
418 if (!ssid_updated)
419 textSsid->clear();
420 if (!bssid_updated)
421 textBssid->clear();
422 if (!ipaddr_updated)
423 textIpAddress->clear();
424 }
425
426
updateNetworks()427 void WpaGui::updateNetworks()
428 {
429 char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
430 size_t len;
431 int first_active = -1;
432 bool selected = false;
433
434 if (!networkMayHaveChanged)
435 return;
436
437 networkSelect->clear();
438
439 if (ctrl_conn == NULL)
440 return;
441
442 len = sizeof(buf) - 1;
443 if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
444 return;
445
446 buf[len] = '\0';
447 start = strchr(buf, '\n');
448 if (start == NULL)
449 return;
450 start++;
451
452 while (*start) {
453 bool last = false;
454 end = strchr(start, '\n');
455 if (end == NULL) {
456 last = true;
457 end = start;
458 while (end[0] && end[1])
459 end++;
460 }
461 *end = '\0';
462
463 id = start;
464 ssid = strchr(id, '\t');
465 if (ssid == NULL)
466 break;
467 *ssid++ = '\0';
468 bssid = strchr(ssid, '\t');
469 if (bssid == NULL)
470 break;
471 *bssid++ = '\0';
472 flags = strchr(bssid, '\t');
473 if (flags == NULL)
474 break;
475 *flags++ = '\0';
476
477 QString network(id);
478 network.append(": ");
479 network.append(ssid);
480 networkSelect->insertItem(network);
481
482 if (strstr(flags, "[CURRENT]")) {
483 networkSelect->setCurrentItem(networkSelect->count() -
484 1);
485 selected = true;
486 } else if (first_active < 0 &&
487 strstr(flags, "[DISABLED]") == NULL)
488 first_active = networkSelect->count() - 1;
489
490 if (last)
491 break;
492 start = end + 1;
493 }
494
495 if (!selected && first_active >= 0)
496 networkSelect->setCurrentItem(first_active);
497
498 networkMayHaveChanged = false;
499 }
500
501
helpIndex()502 void WpaGui::helpIndex()
503 {
504 printf("helpIndex\n");
505 }
506
507
helpContents()508 void WpaGui::helpContents()
509 {
510 printf("helpContents\n");
511 }
512
513
helpAbout()514 void WpaGui::helpAbout()
515 {
516 QMessageBox::about(this, "wpa_gui for wpa_supplicant",
517 "Copyright (c) 2003-2008,\n"
518 "Jouni Malinen <j@w1.fi>\n"
519 "and contributors.\n"
520 "\n"
521 "This program is free software. You can\n"
522 "distribute it and/or modify it under the terms "
523 "of\n"
524 "the GNU General Public License version 2.\n"
525 "\n"
526 "Alternatively, this software may be distributed\n"
527 "under the terms of the BSD license.\n"
528 "\n"
529 "This product includes software developed\n"
530 "by the OpenSSL Project for use in the\n"
531 "OpenSSL Toolkit (http://www.openssl.org/)\n");
532 }
533
534
disconnect()535 void WpaGui::disconnect()
536 {
537 char reply[10];
538 size_t reply_len = sizeof(reply);
539 ctrlRequest("DISCONNECT", reply, &reply_len);
540 }
541
542
scan()543 void WpaGui::scan()
544 {
545 if (scanres) {
546 scanres->close();
547 delete scanres;
548 }
549
550 scanres = new ScanResults();
551 if (scanres == NULL)
552 return;
553 scanres->setWpaGui(this);
554 scanres->show();
555 scanres->exec();
556 }
557
558
eventHistory()559 void WpaGui::eventHistory()
560 {
561 if (eh) {
562 eh->close();
563 delete eh;
564 }
565
566 eh = new EventHistory();
567 if (eh == NULL)
568 return;
569 eh->addEvents(msgs);
570 eh->show();
571 eh->exec();
572 }
573
574
ping()575 void WpaGui::ping()
576 {
577 char buf[10];
578 size_t len;
579
580 #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
581 /*
582 * QSocketNotifier cannot be used with Windows named pipes, so use a
583 * timer to check for received messages for now. This could be
584 * optimized be doing something specific to named pipes or Windows
585 * events, but it is not clear what would be the best way of doing that
586 * in Qt.
587 */
588 receiveMsgs();
589 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
590
591 if (scanres && !scanres->isVisible()) {
592 delete scanres;
593 scanres = NULL;
594 }
595
596 if (eh && !eh->isVisible()) {
597 delete eh;
598 eh = NULL;
599 }
600
601 if (udr && !udr->isVisible()) {
602 delete udr;
603 udr = NULL;
604 }
605
606 len = sizeof(buf) - 1;
607 if (ctrlRequest("PING", buf, &len) < 0) {
608 printf("PING failed - trying to reconnect\n");
609 if (openCtrlConnection(ctrl_iface) >= 0) {
610 printf("Reconnected successfully\n");
611 pingsToStatusUpdate = 0;
612 }
613 }
614
615 pingsToStatusUpdate--;
616 if (pingsToStatusUpdate <= 0) {
617 updateStatus();
618 updateNetworks();
619 }
620 }
621
622
str_match(const char * a,const char * b)623 static int str_match(const char *a, const char *b)
624 {
625 return strncmp(a, b, strlen(b)) == 0;
626 }
627
628
processMsg(char * msg)629 void WpaGui::processMsg(char *msg)
630 {
631 char *pos = msg, *pos2;
632 int priority = 2;
633
634 if (*pos == '<') {
635 /* skip priority */
636 pos++;
637 priority = atoi(pos);
638 pos = strchr(pos, '>');
639 if (pos)
640 pos++;
641 else
642 pos = msg;
643 }
644
645 WpaMsg wm(pos, priority);
646 if (eh)
647 eh->addEvent(wm);
648 msgs.append(wm);
649 while (msgs.count() > 100)
650 msgs.pop_front();
651
652 /* Update last message with truncated version of the event */
653 if (strncmp(pos, "CTRL-", 5) == 0) {
654 pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' ');
655 if (pos2)
656 pos2++;
657 else
658 pos2 = pos;
659 } else
660 pos2 = pos;
661 QString lastmsg = pos2;
662 lastmsg.truncate(40);
663 textLastMessage->setText(lastmsg);
664
665 pingsToStatusUpdate = 0;
666 networkMayHaveChanged = true;
667
668 if (str_match(pos, WPA_CTRL_REQ))
669 processCtrlReq(pos + strlen(WPA_CTRL_REQ));
670 }
671
672
processCtrlReq(const char * req)673 void WpaGui::processCtrlReq(const char *req)
674 {
675 if (udr) {
676 udr->close();
677 delete udr;
678 }
679 udr = new UserDataRequest();
680 if (udr == NULL)
681 return;
682 if (udr->setParams(this, req) < 0) {
683 delete udr;
684 udr = NULL;
685 return;
686 }
687 udr->show();
688 udr->exec();
689 }
690
691
receiveMsgs()692 void WpaGui::receiveMsgs()
693 {
694 char buf[256];
695 size_t len;
696
697 while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) {
698 len = sizeof(buf) - 1;
699 if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) {
700 buf[len] = '\0';
701 processMsg(buf);
702 }
703 }
704 }
705
706
connectB()707 void WpaGui::connectB()
708 {
709 char reply[10];
710 size_t reply_len = sizeof(reply);
711 ctrlRequest("REASSOCIATE", reply, &reply_len);
712 }
713
714
selectNetwork(const QString & sel)715 void WpaGui::selectNetwork( const QString &sel )
716 {
717 QString cmd(sel);
718 char reply[10];
719 size_t reply_len = sizeof(reply);
720
721 int pos = cmd.find(':');
722 if (pos < 0) {
723 printf("Invalid selectNetwork '%s'\n", cmd.ascii());
724 return;
725 }
726 cmd.truncate(pos);
727 cmd.prepend("SELECT_NETWORK ");
728 ctrlRequest(cmd.ascii(), reply, &reply_len);
729 }
730
731
editNetwork()732 void WpaGui::editNetwork()
733 {
734 QString sel(networkSelect->currentText());
735 int pos = sel.find(':');
736 if (pos < 0) {
737 printf("Invalid selectNetwork '%s'\n", sel.ascii());
738 return;
739 }
740 sel.truncate(pos);
741
742 NetworkConfig *nc = new NetworkConfig();
743 if (nc == NULL)
744 return;
745 nc->setWpaGui(this);
746
747 nc->paramsFromConfig(sel.toInt());
748 nc->show();
749 nc->exec();
750 }
751
752
triggerUpdate()753 void WpaGui::triggerUpdate()
754 {
755 updateStatus();
756 networkMayHaveChanged = true;
757 updateNetworks();
758 }
759
760
addNetwork()761 void WpaGui::addNetwork()
762 {
763 NetworkConfig *nc = new NetworkConfig();
764 if (nc == NULL)
765 return;
766 nc->setWpaGui(this);
767 nc->newNetwork();
768 nc->show();
769 nc->exec();
770 }
771
772
selectAdapter(const QString & sel)773 void WpaGui::selectAdapter( const QString & sel )
774 {
775 if (openCtrlConnection(sel.ascii()) < 0)
776 printf("Failed to open control connection to "
777 "wpa_supplicant.\n");
778 updateStatus();
779 updateNetworks();
780 }
781