Added preliminary Wi-Fi Protected Setup (WPS) implementation
[wpasupplicant] / hostapd / hostapd_cli.c
1 /*
2  * hostapd - command line interface for hostapd daemon
3  * Copyright (c) 2004-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 #include "includes.h"
16 #include <dirent.h>
17
18 #include "wpa_ctrl.h"
19 #include "common.h"
20 #include "version.h"
21
22
23 static const char *hostapd_cli_version =
24 "hostapd_cli v" VERSION_STR "\n"
25 "Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> and contributors";
26
27
28 static const char *hostapd_cli_license =
29 "This program is free software. You can distribute it and/or modify it\n"
30 "under the terms of the GNU General Public License version 2.\n"
31 "\n"
32 "Alternatively, this software may be distributed under the terms of the\n"
33 "BSD license. See README and COPYING for more details.\n";
34
35 static const char *hostapd_cli_full_license =
36 "This program is free software; you can redistribute it and/or modify\n"
37 "it under the terms of the GNU General Public License version 2 as\n"
38 "published by the Free Software Foundation.\n"
39 "\n"
40 "This program is distributed in the hope that it will be useful,\n"
41 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
42 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
43 "GNU General Public License for more details.\n"
44 "\n"
45 "You should have received a copy of the GNU General Public License\n"
46 "along with this program; if not, write to the Free Software\n"
47 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n"
48 "\n"
49 "Alternatively, this software may be distributed under the terms of the\n"
50 "BSD license.\n"
51 "\n"
52 "Redistribution and use in source and binary forms, with or without\n"
53 "modification, are permitted provided that the following conditions are\n"
54 "met:\n"
55 "\n"
56 "1. Redistributions of source code must retain the above copyright\n"
57 "   notice, this list of conditions and the following disclaimer.\n"
58 "\n"
59 "2. Redistributions in binary form must reproduce the above copyright\n"
60 "   notice, this list of conditions and the following disclaimer in the\n"
61 "   documentation and/or other materials provided with the distribution.\n"
62 "\n"
63 "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
64 "   names of its contributors may be used to endorse or promote products\n"
65 "   derived from this software without specific prior written permission.\n"
66 "\n"
67 "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
68 "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
69 "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
70 "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
71 "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
72 "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
73 "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
74 "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
75 "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
76 "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
77 "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
78 "\n";
79
80 static const char *commands_help =
81 "Commands:\n"
82 "   mib                  get MIB variables (dot1x, dot11, radius)\n"
83 "   sta <addr>           get MIB variables for one station\n"
84 "   all_sta              get MIB variables for all stations\n"
85 "   new_sta <addr>       add a new station\n"
86 #ifdef CONFIG_WPS
87 "   wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n"
88 "   wps_pbc              indicate button pushed to initiate PBC\n"
89 #endif /* CONFIG_WPS */
90 "   help                 show this usage help\n"
91 "   interface [ifname]   show interfaces/select interface\n"
92 "   level <debug level>  change debug level\n"
93 "   license              show full hostapd_cli license\n"
94 "   quit                 exit hostapd_cli\n";
95
96 static struct wpa_ctrl *ctrl_conn;
97 static int hostapd_cli_quit = 0;
98 static int hostapd_cli_attached = 0;
99 static const char *ctrl_iface_dir = "/var/run/hostapd";
100 static char *ctrl_ifname = NULL;
101
102
103 static void usage(void)
104 {
105         fprintf(stderr, "%s\n", hostapd_cli_version);
106         fprintf(stderr, 
107                 "\n"    
108                 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hv] "
109                 "[command..]\n"
110                 "\n"
111                 "Options:\n"
112                 "   -h           help (show this usage text)\n"
113                 "   -v           shown version information\n"
114                 "   -p<path>     path to find control sockets (default: "
115                 "/var/run/hostapd)\n"
116                 "   -i<ifname>   Interface to listen on (default: first "
117                 "interface found in the\n"
118                 "                socket path)\n\n"
119                 "%s",
120                 commands_help);
121 }
122
123
124 static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
125 {
126         char *cfile;
127         int flen;
128
129         if (ifname == NULL)
130                 return NULL;
131
132         flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
133         cfile = malloc(flen);
134         if (cfile == NULL)
135                 return NULL;
136         snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
137
138         ctrl_conn = wpa_ctrl_open(cfile);
139         free(cfile);
140         return ctrl_conn;
141 }
142
143
144 static void hostapd_cli_close_connection(void)
145 {
146         if (ctrl_conn == NULL)
147                 return;
148
149         if (hostapd_cli_attached) {
150                 wpa_ctrl_detach(ctrl_conn);
151                 hostapd_cli_attached = 0;
152         }
153         wpa_ctrl_close(ctrl_conn);
154         ctrl_conn = NULL;
155 }
156
157
158 static void hostapd_cli_msg_cb(char *msg, size_t len)
159 {
160         printf("%s\n", msg);
161 }
162
163
164 static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
165 {
166         char buf[4096];
167         size_t len;
168         int ret;
169
170         if (ctrl_conn == NULL) {
171                 printf("Not connected to hostapd - command dropped.\n");
172                 return -1;
173         }
174         len = sizeof(buf) - 1;
175         ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
176                                hostapd_cli_msg_cb);
177         if (ret == -2) {
178                 printf("'%s' command timed out.\n", cmd);
179                 return -2;
180         } else if (ret < 0) {
181                 printf("'%s' command failed.\n", cmd);
182                 return -1;
183         }
184         if (print) {
185                 buf[len] = '\0';
186                 printf("%s", buf);
187         }
188         return 0;
189 }
190
191
192 static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
193 {
194         return _wpa_ctrl_command(ctrl, cmd, 1);
195 }
196
197
198 static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
199 {
200         return wpa_ctrl_command(ctrl, "PING");
201 }
202
203
204 static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
205 {
206         return wpa_ctrl_command(ctrl, "MIB");
207 }
208
209
210 static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
211 {
212         char buf[64];
213         if (argc != 1) {
214                 printf("Invalid 'sta' command - exactly one argument, STA "
215                        "address, is required.\n");
216                 return -1;
217         }
218         snprintf(buf, sizeof(buf), "STA %s", argv[0]);
219         return wpa_ctrl_command(ctrl, buf);
220 }
221
222
223 static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
224                                    char *argv[])
225 {
226         char buf[64];
227         if (argc != 1) {
228                 printf("Invalid 'new_sta' command - exactly one argument, STA "
229                        "address, is required.\n");
230                 return -1;
231         }
232         snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
233         return wpa_ctrl_command(ctrl, buf);
234 }
235
236
237 #ifdef CONFIG_WPS
238 static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
239                                    char *argv[])
240 {
241         char buf[64];
242         if (argc != 2) {
243                 printf("Invalid 'wps_pin' command - exactly two arguments, "
244                        "UUID and PIN, are required.\n");
245                 return -1;
246         }
247         snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
248         return wpa_ctrl_command(ctrl, buf);
249 }
250
251
252 static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
253                                    char *argv[])
254 {
255         return wpa_ctrl_command(ctrl, "WPS_PBC");
256 }
257 #endif /* CONFIG_WPS */
258
259
260 static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
261                                 char *addr, size_t addr_len)
262 {
263         char buf[4096], *pos;
264         size_t len;
265         int ret;
266
267         if (ctrl_conn == NULL) {
268                 printf("Not connected to hostapd - command dropped.\n");
269                 return -1;
270         }
271         len = sizeof(buf) - 1;
272         ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
273                                hostapd_cli_msg_cb);
274         if (ret == -2) {
275                 printf("'%s' command timed out.\n", cmd);
276                 return -2;
277         } else if (ret < 0) {
278                 printf("'%s' command failed.\n", cmd);
279                 return -1;
280         }
281
282         buf[len] = '\0';
283         if (memcmp(buf, "FAIL", 4) == 0)
284                 return -1;
285         printf("%s", buf);
286
287         pos = buf;
288         while (*pos != '\0' && *pos != '\n')
289                 pos++;
290         *pos = '\0';
291         os_strlcpy(addr, buf, addr_len);
292         return 0;
293 }
294
295
296 static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
297                                    char *argv[])
298 {
299         char addr[32], cmd[64];
300
301         if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
302                 return 0;
303         do {
304                 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
305         } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
306
307         return -1;
308 }
309
310
311 static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
312 {
313         printf("%s", commands_help);
314         return 0;
315 }
316
317
318 static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
319                                    char *argv[])
320 {
321         printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
322         return 0;
323 }
324
325
326 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
327 {
328         hostapd_cli_quit = 1;
329         return 0;
330 }
331
332
333 static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
334 {
335         char cmd[256];
336         if (argc != 1) {
337                 printf("Invalid LEVEL command: needs one argument (debug "
338                        "level)\n");
339                 return 0;
340         }
341         snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
342         return wpa_ctrl_command(ctrl, cmd);
343 }
344
345
346 static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
347 {
348         struct dirent *dent;
349         DIR *dir;
350
351         dir = opendir(ctrl_iface_dir);
352         if (dir == NULL) {
353                 printf("Control interface directory '%s' could not be "
354                        "openned.\n", ctrl_iface_dir);
355                 return;
356         }
357
358         printf("Available interfaces:\n");
359         while ((dent = readdir(dir))) {
360                 if (strcmp(dent->d_name, ".") == 0 ||
361                     strcmp(dent->d_name, "..") == 0)
362                         continue;
363                 printf("%s\n", dent->d_name);
364         }
365         closedir(dir);
366 }
367
368
369 static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
370                                      char *argv[])
371 {
372         if (argc < 1) {
373                 hostapd_cli_list_interfaces(ctrl);
374                 return 0;
375         }
376
377         hostapd_cli_close_connection();
378         free(ctrl_ifname);
379         ctrl_ifname = strdup(argv[0]);
380
381         if (hostapd_cli_open_connection(ctrl_ifname)) {
382                 printf("Connected to interface '%s.\n", ctrl_ifname);
383                 if (wpa_ctrl_attach(ctrl_conn) == 0) {
384                         hostapd_cli_attached = 1;
385                 } else {
386                         printf("Warning: Failed to attach to "
387                                "hostapd.\n");
388                 }
389         } else {
390                 printf("Could not connect to interface '%s' - re-trying\n",
391                         ctrl_ifname);
392         }
393         return 0;
394 }
395
396
397 struct hostapd_cli_cmd {
398         const char *cmd;
399         int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
400 };
401
402 static struct hostapd_cli_cmd hostapd_cli_commands[] = {
403         { "ping", hostapd_cli_cmd_ping },
404         { "mib", hostapd_cli_cmd_mib },
405         { "sta", hostapd_cli_cmd_sta },
406         { "all_sta", hostapd_cli_cmd_all_sta },
407         { "new_sta", hostapd_cli_cmd_new_sta },
408 #ifdef CONFIG_WPS
409         { "wps_pin", hostapd_cli_cmd_wps_pin },
410         { "wps_pbc", hostapd_cli_cmd_wps_pbc },
411 #endif /* CONFIG_WPS */
412         { "help", hostapd_cli_cmd_help },
413         { "interface", hostapd_cli_cmd_interface },
414         { "level", hostapd_cli_cmd_level },
415         { "license", hostapd_cli_cmd_license },
416         { "quit", hostapd_cli_cmd_quit },
417         { NULL, NULL }
418 };
419
420
421 static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
422 {
423         struct hostapd_cli_cmd *cmd, *match = NULL;
424         int count;
425
426         count = 0;
427         cmd = hostapd_cli_commands;
428         while (cmd->cmd) {
429                 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
430                         match = cmd;
431                         count++;
432                 }
433                 cmd++;
434         }
435
436         if (count > 1) {
437                 printf("Ambiguous command '%s'; possible commands:", argv[0]);
438                 cmd = hostapd_cli_commands;
439                 while (cmd->cmd) {
440                         if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
441                             0) {
442                                 printf(" %s", cmd->cmd);
443                         }
444                         cmd++;
445                 }
446                 printf("\n");
447         } else if (count == 0) {
448                 printf("Unknown command '%s'\n", argv[0]);
449         } else {
450                 match->handler(ctrl, argc - 1, &argv[1]);
451         }
452 }
453
454
455 static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read)
456 {
457         int first = 1;
458         if (ctrl_conn == NULL)
459                 return;
460         while (wpa_ctrl_pending(ctrl)) {
461                 char buf[256];
462                 size_t len = sizeof(buf) - 1;
463                 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
464                         buf[len] = '\0';
465                         if (in_read && first)
466                                 printf("\n");
467                         first = 0;
468                         printf("%s\n", buf);
469                 } else {
470                         printf("Could not read pending message.\n");
471                         break;
472                 }
473         }
474 }
475
476
477 static void hostapd_cli_interactive(void)
478 {
479         const int max_args = 10;
480         char cmd[256], *res, *argv[max_args], *pos;
481         int argc;
482
483         printf("\nInteractive mode\n\n");
484
485         do {
486                 hostapd_cli_recv_pending(ctrl_conn, 0);
487                 printf("> ");
488                 alarm(1);
489                 res = fgets(cmd, sizeof(cmd), stdin);
490                 alarm(0);
491                 if (res == NULL)
492                         break;
493                 pos = cmd;
494                 while (*pos != '\0') {
495                         if (*pos == '\n') {
496                                 *pos = '\0';
497                                 break;
498                         }
499                         pos++;
500                 }
501                 argc = 0;
502                 pos = cmd;
503                 for (;;) {
504                         while (*pos == ' ')
505                                 pos++;
506                         if (*pos == '\0')
507                                 break;
508                         argv[argc] = pos;
509                         argc++;
510                         if (argc == max_args)
511                                 break;
512                         while (*pos != '\0' && *pos != ' ')
513                                 pos++;
514                         if (*pos == ' ')
515                                 *pos++ = '\0';
516                 }
517                 if (argc)
518                         wpa_request(ctrl_conn, argc, argv);
519         } while (!hostapd_cli_quit);
520 }
521
522
523 static void hostapd_cli_terminate(int sig)
524 {
525         hostapd_cli_close_connection();
526         exit(0);
527 }
528
529
530 static void hostapd_cli_alarm(int sig)
531 {
532         if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
533                 printf("Connection to hostapd lost - trying to reconnect\n");
534                 hostapd_cli_close_connection();
535         }
536         if (!ctrl_conn) {
537                 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
538                 if (ctrl_conn) {
539                         printf("Connection to hostapd re-established\n");
540                         if (wpa_ctrl_attach(ctrl_conn) == 0) {
541                                 hostapd_cli_attached = 1;
542                         } else {
543                                 printf("Warning: Failed to attach to "
544                                        "hostapd.\n");
545                         }
546                 }
547         }
548         if (ctrl_conn)
549                 hostapd_cli_recv_pending(ctrl_conn, 1);
550         alarm(1);
551 }
552
553
554 int main(int argc, char *argv[])
555 {
556         int interactive;
557         int warning_displayed = 0;
558         int c;
559
560         for (;;) {
561                 c = getopt(argc, argv, "hi:p:v");
562                 if (c < 0)
563                         break;
564                 switch (c) {
565                 case 'h':
566                         usage();
567                         return 0;
568                 case 'v':
569                         printf("%s\n", hostapd_cli_version);
570                         return 0;
571                 case 'i':
572                         free(ctrl_ifname);
573                         ctrl_ifname = strdup(optarg);
574                         break;
575                 case 'p':
576                         ctrl_iface_dir = optarg;
577                         break;
578                 default:
579                         usage();
580                         return -1;
581                 }
582         }
583
584         interactive = argc == optind;
585
586         if (interactive) {
587                 printf("%s\n\n%s\n\n", hostapd_cli_version,
588                        hostapd_cli_license);
589         }
590
591         for (;;) {
592                 if (ctrl_ifname == NULL) {
593                         struct dirent *dent;
594                         DIR *dir = opendir(ctrl_iface_dir);
595                         if (dir) {
596                                 while ((dent = readdir(dir))) {
597                                         if (strcmp(dent->d_name, ".") == 0 ||
598                                             strcmp(dent->d_name, "..") == 0)
599                                                 continue;
600                                         printf("Selected interface '%s'\n",
601                                                dent->d_name);
602                                         ctrl_ifname = strdup(dent->d_name);
603                                         break;
604                                 }
605                                 closedir(dir);
606                         }
607                 }
608                 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
609                 if (ctrl_conn) {
610                         if (warning_displayed)
611                                 printf("Connection established.\n");
612                         break;
613                 }
614
615                 if (!interactive) {
616                         perror("Failed to connect to hostapd - "
617                                "wpa_ctrl_open");
618                         return -1;
619                 }
620
621                 if (!warning_displayed) {
622                         printf("Could not connect to hostapd - re-trying\n");
623                         warning_displayed = 1;
624                 }
625                 sleep(1);
626                 continue;
627         }
628
629         signal(SIGINT, hostapd_cli_terminate);
630         signal(SIGTERM, hostapd_cli_terminate);
631         signal(SIGALRM, hostapd_cli_alarm);
632
633         if (interactive) {
634                 if (wpa_ctrl_attach(ctrl_conn) == 0) {
635                         hostapd_cli_attached = 1;
636                 } else {
637                         printf("Warning: Failed to attach to hostapd.\n");
638                 }
639                 hostapd_cli_interactive();
640         } else
641                 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
642
643         free(ctrl_ifname);
644         hostapd_cli_close_connection();
645         return 0;
646 }