1 |
/* |
2 |
* hostapd - command line interface for hostapd daemon |
3 |
* Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> |
4 |
* |
5 |
* This software may be distributed under the terms of the BSD license. |
6 |
* See README for more details. |
7 |
*/ |
8 |
|
9 |
#include "includes.h" |
10 |
#include <dirent.h> |
11 |
|
12 |
#include "common/wpa_ctrl.h" |
13 |
#include "utils/common.h" |
14 |
#include "utils/eloop.h" |
15 |
#include "utils/edit.h" |
16 |
#include "common/version.h" |
17 |
|
18 |
|
19 |
static const char *hostapd_cli_version = |
20 |
"hostapd_cli v" VERSION_STR "\n" |
21 |
"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors"; |
22 |
|
23 |
|
24 |
static const char *hostapd_cli_license = |
25 |
"This software may be distributed under the terms of the BSD license.\n" |
26 |
"See README for more details.\n"; |
27 |
|
28 |
static const char *hostapd_cli_full_license = |
29 |
"This software may be distributed under the terms of the BSD license.\n" |
30 |
"\n" |
31 |
"Redistribution and use in source and binary forms, with or without\n" |
32 |
"modification, are permitted provided that the following conditions are\n" |
33 |
"met:\n" |
34 |
"\n" |
35 |
"1. Redistributions of source code must retain the above copyright\n" |
36 |
" notice, this list of conditions and the following disclaimer.\n" |
37 |
"\n" |
38 |
"2. Redistributions in binary form must reproduce the above copyright\n" |
39 |
" notice, this list of conditions and the following disclaimer in the\n" |
40 |
" documentation and/or other materials provided with the distribution.\n" |
41 |
"\n" |
42 |
"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" |
43 |
" names of its contributors may be used to endorse or promote products\n" |
44 |
" derived from this software without specific prior written permission.\n" |
45 |
"\n" |
46 |
"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" |
47 |
"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" |
48 |
"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" |
49 |
"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" |
50 |
"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" |
51 |
"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" |
52 |
"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" |
53 |
"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" |
54 |
"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" |
55 |
"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" |
56 |
"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" |
57 |
"\n"; |
58 |
|
59 |
static const char *commands_help = |
60 |
"Commands:\n" |
61 |
" mib get MIB variables (dot1x, dot11, radius)\n" |
62 |
" sta <addr> get MIB variables for one station\n" |
63 |
" all_sta get MIB variables for all stations\n" |
64 |
" new_sta <addr> add a new station\n" |
65 |
" deauthenticate <addr> deauthenticate a station\n" |
66 |
" disassociate <addr> disassociate a station\n" |
67 |
#ifdef CONFIG_IEEE80211W |
68 |
" sa_query <addr> send SA Query to a station\n" |
69 |
#endif /* CONFIG_IEEE80211W */ |
70 |
#ifdef CONFIG_WPS |
71 |
" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n" |
72 |
" wps_check_pin <PIN> verify PIN checksum\n" |
73 |
" wps_pbc indicate button pushed to initiate PBC\n" |
74 |
" wps_cancel cancel the pending WPS operation\n" |
75 |
#ifdef CONFIG_WPS_NFC |
76 |
" wps_nfc_tag_read <hexdump> report read NFC tag with WPS data\n" |
77 |
" wps_nfc_config_token <WPS/NDEF> build NFC configuration token\n" |
78 |
" wps_nfc_token <WPS/NDEF/enable/disable> manager NFC password token\n" |
79 |
#endif /* CONFIG_WPS_NFC */ |
80 |
" wps_ap_pin <cmd> [params..] enable/disable AP PIN\n" |
81 |
" wps_config <SSID> <auth> <encr> <key> configure AP\n" |
82 |
#endif /* CONFIG_WPS */ |
83 |
" get_config show current configuration\n" |
84 |
" help show this usage help\n" |
85 |
" interface [ifname] show interfaces/select interface\n" |
86 |
" level <debug level> change debug level\n" |
87 |
" license show full hostapd_cli license\n" |
88 |
" quit exit hostapd_cli\n"; |
89 |
|
90 |
static struct wpa_ctrl *ctrl_conn; |
91 |
static int hostapd_cli_quit = 0; |
92 |
static int hostapd_cli_attached = 0; |
93 |
static const char *ctrl_iface_dir = "/var/run/hostapd"; |
94 |
static char *ctrl_ifname = NULL; |
95 |
static const char *pid_file = NULL; |
96 |
static const char *action_file = NULL; |
97 |
static int ping_interval = 5; |
98 |
static int interactive = 0; |
99 |
|
100 |
|
101 |
static void usage(void) |
102 |
{ |
103 |
fprintf(stderr, "%s\n", hostapd_cli_version); |
104 |
fprintf(stderr, |
105 |
"\n" |
106 |
"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] " |
107 |
"[-a<path>] \\\n" |
108 |
" [-G<ping interval>] [command..]\n" |
109 |
"\n" |
110 |
"Options:\n" |
111 |
" -h help (show this usage text)\n" |
112 |
" -v shown version information\n" |
113 |
" -p<path> path to find control sockets (default: " |
114 |
"/var/run/hostapd)\n" |
115 |
" -a<file> run in daemon mode executing the action file " |
116 |
"based on events\n" |
117 |
" from hostapd\n" |
118 |
" -B run a daemon in the background\n" |
119 |
" -i<ifname> Interface to listen on (default: first " |
120 |
"interface found in the\n" |
121 |
" socket path)\n\n" |
122 |
"%s", |
123 |
commands_help); |
124 |
} |
125 |
|
126 |
|
127 |
static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) |
128 |
{ |
129 |
char *cfile; |
130 |
int flen; |
131 |
|
132 |
if (ifname == NULL) |
133 |
return NULL; |
134 |
|
135 |
flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2; |
136 |
cfile = malloc(flen); |
137 |
if (cfile == NULL) |
138 |
return NULL; |
139 |
snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); |
140 |
|
141 |
ctrl_conn = wpa_ctrl_open(cfile); |
142 |
free(cfile); |
143 |
return ctrl_conn; |
144 |
} |
145 |
|
146 |
|
147 |
static void hostapd_cli_close_connection(void) |
148 |
{ |
149 |
if (ctrl_conn == NULL) |
150 |
return; |
151 |
|
152 |
if (hostapd_cli_attached) { |
153 |
wpa_ctrl_detach(ctrl_conn); |
154 |
hostapd_cli_attached = 0; |
155 |
} |
156 |
wpa_ctrl_close(ctrl_conn); |
157 |
ctrl_conn = NULL; |
158 |
} |
159 |
|
160 |
|
161 |
static void hostapd_cli_msg_cb(char *msg, size_t len) |
162 |
{ |
163 |
printf("%s\n", msg); |
164 |
} |
165 |
|
166 |
|
167 |
static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) |
168 |
{ |
169 |
char buf[4096]; |
170 |
size_t len; |
171 |
int ret; |
172 |
|
173 |
if (ctrl_conn == NULL) { |
174 |
printf("Not connected to hostapd - command dropped.\n"); |
175 |
return -1; |
176 |
} |
177 |
len = sizeof(buf) - 1; |
178 |
ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, |
179 |
hostapd_cli_msg_cb); |
180 |
if (ret == -2) { |
181 |
printf("'%s' command timed out.\n", cmd); |
182 |
return -2; |
183 |
} else if (ret < 0) { |
184 |
printf("'%s' command failed.\n", cmd); |
185 |
return -1; |
186 |
} |
187 |
if (print) { |
188 |
buf[len] = '\0'; |
189 |
printf("%s", buf); |
190 |
} |
191 |
return 0; |
192 |
} |
193 |
|
194 |
|
195 |
static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) |
196 |
{ |
197 |
return _wpa_ctrl_command(ctrl, cmd, 1); |
198 |
} |
199 |
|
200 |
|
201 |
static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
202 |
{ |
203 |
return wpa_ctrl_command(ctrl, "PING"); |
204 |
} |
205 |
|
206 |
|
207 |
static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
208 |
{ |
209 |
return wpa_ctrl_command(ctrl, "RELOG"); |
210 |
} |
211 |
|
212 |
|
213 |
static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
214 |
{ |
215 |
return wpa_ctrl_command(ctrl, "MIB"); |
216 |
} |
217 |
|
218 |
|
219 |
static int hostapd_cli_exec(const char *program, const char *arg1, |
220 |
const char *arg2) |
221 |
{ |
222 |
char *cmd; |
223 |
size_t len; |
224 |
int res; |
225 |
int ret = 0; |
226 |
|
227 |
len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3; |
228 |
cmd = os_malloc(len); |
229 |
if (cmd == NULL) |
230 |
return -1; |
231 |
res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2); |
232 |
if (res < 0 || (size_t) res >= len) { |
233 |
os_free(cmd); |
234 |
return -1; |
235 |
} |
236 |
cmd[len - 1] = '\0'; |
237 |
#ifndef _WIN32_WCE |
238 |
if (system(cmd) < 0) |
239 |
ret = -1; |
240 |
#endif /* _WIN32_WCE */ |
241 |
os_free(cmd); |
242 |
|
243 |
return ret; |
244 |
} |
245 |
|
246 |
|
247 |
static void hostapd_cli_action_process(char *msg, size_t len) |
248 |
{ |
249 |
const char *pos; |
250 |
|
251 |
pos = msg; |
252 |
if (*pos == '<') { |
253 |
pos = os_strchr(pos, '>'); |
254 |
if (pos) |
255 |
pos++; |
256 |
else |
257 |
pos = msg; |
258 |
} |
259 |
|
260 |
hostapd_cli_exec(action_file, ctrl_ifname, pos); |
261 |
} |
262 |
|
263 |
|
264 |
static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
265 |
{ |
266 |
char buf[64]; |
267 |
if (argc != 1) { |
268 |
printf("Invalid 'sta' command - exactly one argument, STA " |
269 |
"address, is required.\n"); |
270 |
return -1; |
271 |
} |
272 |
snprintf(buf, sizeof(buf), "STA %s", argv[0]); |
273 |
return wpa_ctrl_command(ctrl, buf); |
274 |
} |
275 |
|
276 |
|
277 |
static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, |
278 |
char *argv[]) |
279 |
{ |
280 |
char buf[64]; |
281 |
if (argc != 1) { |
282 |
printf("Invalid 'new_sta' command - exactly one argument, STA " |
283 |
"address, is required.\n"); |
284 |
return -1; |
285 |
} |
286 |
snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]); |
287 |
return wpa_ctrl_command(ctrl, buf); |
288 |
} |
289 |
|
290 |
|
291 |
static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, |
292 |
char *argv[]) |
293 |
{ |
294 |
char buf[64]; |
295 |
if (argc < 1) { |
296 |
printf("Invalid 'deauthenticate' command - exactly one " |
297 |
"argument, STA address, is required.\n"); |
298 |
return -1; |
299 |
} |
300 |
if (argc > 1) |
301 |
os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s", |
302 |
argv[0], argv[1]); |
303 |
else |
304 |
os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]); |
305 |
return wpa_ctrl_command(ctrl, buf); |
306 |
} |
307 |
|
308 |
|
309 |
static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, |
310 |
char *argv[]) |
311 |
{ |
312 |
char buf[64]; |
313 |
if (argc < 1) { |
314 |
printf("Invalid 'disassociate' command - exactly one " |
315 |
"argument, STA address, is required.\n"); |
316 |
return -1; |
317 |
} |
318 |
if (argc > 1) |
319 |
os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s", |
320 |
argv[0], argv[1]); |
321 |
else |
322 |
os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]); |
323 |
return wpa_ctrl_command(ctrl, buf); |
324 |
} |
325 |
|
326 |
|
327 |
#ifdef CONFIG_IEEE80211W |
328 |
static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, |
329 |
char *argv[]) |
330 |
{ |
331 |
char buf[64]; |
332 |
if (argc != 1) { |
333 |
printf("Invalid 'sa_query' command - exactly one argument, " |
334 |
"STA address, is required.\n"); |
335 |
return -1; |
336 |
} |
337 |
snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]); |
338 |
return wpa_ctrl_command(ctrl, buf); |
339 |
} |
340 |
#endif /* CONFIG_IEEE80211W */ |
341 |
|
342 |
|
343 |
#ifdef CONFIG_WPS |
344 |
static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, |
345 |
char *argv[]) |
346 |
{ |
347 |
char buf[256]; |
348 |
if (argc < 2) { |
349 |
printf("Invalid 'wps_pin' command - at least two arguments, " |
350 |
"UUID and PIN, are required.\n"); |
351 |
return -1; |
352 |
} |
353 |
if (argc > 3) |
354 |
snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s", |
355 |
argv[0], argv[1], argv[2], argv[3]); |
356 |
else if (argc > 2) |
357 |
snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s", |
358 |
argv[0], argv[1], argv[2]); |
359 |
else |
360 |
snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]); |
361 |
return wpa_ctrl_command(ctrl, buf); |
362 |
} |
363 |
|
364 |
|
365 |
static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc, |
366 |
char *argv[]) |
367 |
{ |
368 |
char cmd[256]; |
369 |
int res; |
370 |
|
371 |
if (argc != 1 && argc != 2) { |
372 |
printf("Invalid WPS_CHECK_PIN command: needs one argument:\n" |
373 |
"- PIN to be verified\n"); |
374 |
return -1; |
375 |
} |
376 |
|
377 |
if (argc == 2) |
378 |
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s", |
379 |
argv[0], argv[1]); |
380 |
else |
381 |
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s", |
382 |
argv[0]); |
383 |
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { |
384 |
printf("Too long WPS_CHECK_PIN command.\n"); |
385 |
return -1; |
386 |
} |
387 |
return wpa_ctrl_command(ctrl, cmd); |
388 |
} |
389 |
|
390 |
|
391 |
static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, |
392 |
char *argv[]) |
393 |
{ |
394 |
return wpa_ctrl_command(ctrl, "WPS_PBC"); |
395 |
} |
396 |
|
397 |
|
398 |
static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, |
399 |
char *argv[]) |
400 |
{ |
401 |
return wpa_ctrl_command(ctrl, "WPS_CANCEL"); |
402 |
} |
403 |
|
404 |
|
405 |
#ifdef CONFIG_WPS_NFC |
406 |
static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc, |
407 |
char *argv[]) |
408 |
{ |
409 |
int ret; |
410 |
char *buf; |
411 |
size_t buflen; |
412 |
|
413 |
if (argc != 1) { |
414 |
printf("Invalid 'wps_nfc_tag_read' command - one argument " |
415 |
"is required.\n"); |
416 |
return -1; |
417 |
} |
418 |
|
419 |
buflen = 18 + os_strlen(argv[0]); |
420 |
buf = os_malloc(buflen); |
421 |
if (buf == NULL) |
422 |
return -1; |
423 |
os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]); |
424 |
|
425 |
ret = wpa_ctrl_command(ctrl, buf); |
426 |
os_free(buf); |
427 |
|
428 |
return ret; |
429 |
} |
430 |
|
431 |
|
432 |
static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, |
433 |
int argc, char *argv[]) |
434 |
{ |
435 |
char cmd[64]; |
436 |
int res; |
437 |
|
438 |
if (argc != 1) { |
439 |
printf("Invalid 'wps_nfc_config_token' command - one argument " |
440 |
"is required.\n"); |
441 |
return -1; |
442 |
} |
443 |
|
444 |
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s", |
445 |
argv[0]); |
446 |
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { |
447 |
printf("Too long WPS_NFC_CONFIG_TOKEN command.\n"); |
448 |
return -1; |
449 |
} |
450 |
return wpa_ctrl_command(ctrl, cmd); |
451 |
} |
452 |
|
453 |
|
454 |
static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, |
455 |
int argc, char *argv[]) |
456 |
{ |
457 |
char cmd[64]; |
458 |
int res; |
459 |
|
460 |
if (argc != 1) { |
461 |
printf("Invalid 'wps_nfc_token' command - one argument is " |
462 |
"required.\n"); |
463 |
return -1; |
464 |
} |
465 |
|
466 |
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]); |
467 |
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { |
468 |
printf("Too long WPS_NFC_TOKEN command.\n"); |
469 |
return -1; |
470 |
} |
471 |
return wpa_ctrl_command(ctrl, cmd); |
472 |
} |
473 |
#endif /* CONFIG_WPS_NFC */ |
474 |
|
475 |
|
476 |
static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, |
477 |
char *argv[]) |
478 |
{ |
479 |
char buf[64]; |
480 |
if (argc < 1) { |
481 |
printf("Invalid 'wps_ap_pin' command - at least one argument " |
482 |
"is required.\n"); |
483 |
return -1; |
484 |
} |
485 |
if (argc > 2) |
486 |
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s", |
487 |
argv[0], argv[1], argv[2]); |
488 |
else if (argc > 1) |
489 |
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s", |
490 |
argv[0], argv[1]); |
491 |
else |
492 |
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]); |
493 |
return wpa_ctrl_command(ctrl, buf); |
494 |
} |
495 |
|
496 |
|
497 |
static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, |
498 |
char *argv[]) |
499 |
{ |
500 |
char buf[256]; |
501 |
char ssid_hex[2 * 32 + 1]; |
502 |
char key_hex[2 * 64 + 1]; |
503 |
int i; |
504 |
|
505 |
if (argc < 1) { |
506 |
printf("Invalid 'wps_config' command - at least two arguments " |
507 |
"are required.\n"); |
508 |
return -1; |
509 |
} |
510 |
|
511 |
ssid_hex[0] = '\0'; |
512 |
for (i = 0; i < 32; i++) { |
513 |
if (argv[0][i] == '\0') |
514 |
break; |
515 |
os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]); |
516 |
} |
517 |
|
518 |
key_hex[0] = '\0'; |
519 |
if (argc > 3) { |
520 |
for (i = 0; i < 64; i++) { |
521 |
if (argv[3][i] == '\0') |
522 |
break; |
523 |
os_snprintf(&key_hex[i * 2], 3, "%02x", |
524 |
argv[3][i]); |
525 |
} |
526 |
} |
527 |
|
528 |
if (argc > 3) |
529 |
snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s", |
530 |
ssid_hex, argv[1], argv[2], key_hex); |
531 |
else if (argc > 2) |
532 |
snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s", |
533 |
ssid_hex, argv[1], argv[2]); |
534 |
else |
535 |
snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s", |
536 |
ssid_hex, argv[1]); |
537 |
return wpa_ctrl_command(ctrl, buf); |
538 |
} |
539 |
#endif /* CONFIG_WPS */ |
540 |
|
541 |
|
542 |
static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc, |
543 |
char *argv[]) |
544 |
{ |
545 |
char buf[300]; |
546 |
int res; |
547 |
|
548 |
if (argc < 2) { |
549 |
printf("Invalid 'disassoc_imminent' command - two arguments " |
550 |
"(STA addr and Disassociation Timer) are needed\n"); |
551 |
return -1; |
552 |
} |
553 |
|
554 |
res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s", |
555 |
argv[0], argv[1]); |
556 |
if (res < 0 || res >= (int) sizeof(buf)) |
557 |
return -1; |
558 |
return wpa_ctrl_command(ctrl, buf); |
559 |
} |
560 |
|
561 |
|
562 |
static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc, |
563 |
char *argv[]) |
564 |
{ |
565 |
char buf[300]; |
566 |
int res; |
567 |
|
568 |
if (argc < 2) { |
569 |
printf("Invalid 'ess_disassoc' command - two arguments (STA " |
570 |
"addr and URL) are needed\n"); |
571 |
return -1; |
572 |
} |
573 |
|
574 |
res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s", |
575 |
argv[0], argv[1]); |
576 |
if (res < 0 || res >= (int) sizeof(buf)) |
577 |
return -1; |
578 |
return wpa_ctrl_command(ctrl, buf); |
579 |
} |
580 |
|
581 |
|
582 |
static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc, |
583 |
char *argv[]) |
584 |
{ |
585 |
return wpa_ctrl_command(ctrl, "GET_CONFIG"); |
586 |
} |
587 |
|
588 |
|
589 |
static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, |
590 |
char *addr, size_t addr_len) |
591 |
{ |
592 |
char buf[4096], *pos; |
593 |
size_t len; |
594 |
int ret; |
595 |
|
596 |
if (ctrl_conn == NULL) { |
597 |
printf("Not connected to hostapd - command dropped.\n"); |
598 |
return -1; |
599 |
} |
600 |
len = sizeof(buf) - 1; |
601 |
ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, |
602 |
hostapd_cli_msg_cb); |
603 |
if (ret == -2) { |
604 |
printf("'%s' command timed out.\n", cmd); |
605 |
return -2; |
606 |
} else if (ret < 0) { |
607 |
printf("'%s' command failed.\n", cmd); |
608 |
return -1; |
609 |
} |
610 |
|
611 |
buf[len] = '\0'; |
612 |
if (memcmp(buf, "FAIL", 4) == 0) |
613 |
return -1; |
614 |
printf("%s", buf); |
615 |
|
616 |
pos = buf; |
617 |
while (*pos != '\0' && *pos != '\n') |
618 |
pos++; |
619 |
*pos = '\0'; |
620 |
os_strlcpy(addr, buf, addr_len); |
621 |
return 0; |
622 |
} |
623 |
|
624 |
|
625 |
static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, |
626 |
char *argv[]) |
627 |
{ |
628 |
char addr[32], cmd[64]; |
629 |
|
630 |
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) |
631 |
return 0; |
632 |
do { |
633 |
snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); |
634 |
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); |
635 |
|
636 |
return -1; |
637 |
} |
638 |
|
639 |
|
640 |
static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
641 |
{ |
642 |
printf("%s", commands_help); |
643 |
return 0; |
644 |
} |
645 |
|
646 |
|
647 |
static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, |
648 |
char *argv[]) |
649 |
{ |
650 |
printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license); |
651 |
return 0; |
652 |
} |
653 |
|
654 |
|
655 |
static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
656 |
{ |
657 |
hostapd_cli_quit = 1; |
658 |
if (interactive) |
659 |
eloop_terminate(); |
660 |
return 0; |
661 |
} |
662 |
|
663 |
|
664 |
static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
665 |
{ |
666 |
char cmd[256]; |
667 |
if (argc != 1) { |
668 |
printf("Invalid LEVEL command: needs one argument (debug " |
669 |
"level)\n"); |
670 |
return 0; |
671 |
} |
672 |
snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); |
673 |
return wpa_ctrl_command(ctrl, cmd); |
674 |
} |
675 |
|
676 |
|
677 |
static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl) |
678 |
{ |
679 |
struct dirent *dent; |
680 |
DIR *dir; |
681 |
|
682 |
dir = opendir(ctrl_iface_dir); |
683 |
if (dir == NULL) { |
684 |
printf("Control interface directory '%s' could not be " |
685 |
"openned.\n", ctrl_iface_dir); |
686 |
return; |
687 |
} |
688 |
|
689 |
printf("Available interfaces:\n"); |
690 |
while ((dent = readdir(dir))) { |
691 |
if (strcmp(dent->d_name, ".") == 0 || |
692 |
strcmp(dent->d_name, "..") == 0) |
693 |
continue; |
694 |
printf("%s\n", dent->d_name); |
695 |
} |
696 |
closedir(dir); |
697 |
} |
698 |
|
699 |
|
700 |
static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, |
701 |
char *argv[]) |
702 |
{ |
703 |
if (argc < 1) { |
704 |
hostapd_cli_list_interfaces(ctrl); |
705 |
return 0; |
706 |
} |
707 |
|
708 |
hostapd_cli_close_connection(); |
709 |
free(ctrl_ifname); |
710 |
ctrl_ifname = strdup(argv[0]); |
711 |
|
712 |
if (hostapd_cli_open_connection(ctrl_ifname)) { |
713 |
printf("Connected to interface '%s.\n", ctrl_ifname); |
714 |
if (wpa_ctrl_attach(ctrl_conn) == 0) { |
715 |
hostapd_cli_attached = 1; |
716 |
} else { |
717 |
printf("Warning: Failed to attach to " |
718 |
"hostapd.\n"); |
719 |
} |
720 |
} else { |
721 |
printf("Could not connect to interface '%s' - re-trying\n", |
722 |
ctrl_ifname); |
723 |
} |
724 |
return 0; |
725 |
} |
726 |
|
727 |
|
728 |
static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
729 |
{ |
730 |
char cmd[256]; |
731 |
int res; |
732 |
|
733 |
if (argc != 2) { |
734 |
printf("Invalid SET command: needs two arguments (variable " |
735 |
"name and value)\n"); |
736 |
return -1; |
737 |
} |
738 |
|
739 |
res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]); |
740 |
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { |
741 |
printf("Too long SET command.\n"); |
742 |
return -1; |
743 |
} |
744 |
return wpa_ctrl_command(ctrl, cmd); |
745 |
} |
746 |
|
747 |
|
748 |
static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
749 |
{ |
750 |
char cmd[256]; |
751 |
int res; |
752 |
|
753 |
if (argc != 1) { |
754 |
printf("Invalid GET command: needs one argument (variable " |
755 |
"name)\n"); |
756 |
return -1; |
757 |
} |
758 |
|
759 |
res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]); |
760 |
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { |
761 |
printf("Too long GET command.\n"); |
762 |
return -1; |
763 |
} |
764 |
return wpa_ctrl_command(ctrl, cmd); |
765 |
} |
766 |
|
767 |
|
768 |
struct hostapd_cli_cmd { |
769 |
const char *cmd; |
770 |
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); |
771 |
}; |
772 |
|
773 |
static struct hostapd_cli_cmd hostapd_cli_commands[] = { |
774 |
{ "ping", hostapd_cli_cmd_ping }, |
775 |
{ "mib", hostapd_cli_cmd_mib }, |
776 |
{ "relog", hostapd_cli_cmd_relog }, |
777 |
{ "sta", hostapd_cli_cmd_sta }, |
778 |
{ "all_sta", hostapd_cli_cmd_all_sta }, |
779 |
{ "new_sta", hostapd_cli_cmd_new_sta }, |
780 |
{ "deauthenticate", hostapd_cli_cmd_deauthenticate }, |
781 |
{ "disassociate", hostapd_cli_cmd_disassociate }, |
782 |
#ifdef CONFIG_IEEE80211W |
783 |
{ "sa_query", hostapd_cli_cmd_sa_query }, |
784 |
#endif /* CONFIG_IEEE80211W */ |
785 |
#ifdef CONFIG_WPS |
786 |
{ "wps_pin", hostapd_cli_cmd_wps_pin }, |
787 |
{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin }, |
788 |
{ "wps_pbc", hostapd_cli_cmd_wps_pbc }, |
789 |
{ "wps_cancel", hostapd_cli_cmd_wps_cancel }, |
790 |
#ifdef CONFIG_WPS_NFC |
791 |
{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read }, |
792 |
{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token }, |
793 |
{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token }, |
794 |
#endif /* CONFIG_WPS_NFC */ |
795 |
{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin }, |
796 |
{ "wps_config", hostapd_cli_cmd_wps_config }, |
797 |
#endif /* CONFIG_WPS */ |
798 |
{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent }, |
799 |
{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc }, |
800 |
{ "get_config", hostapd_cli_cmd_get_config }, |
801 |
{ "help", hostapd_cli_cmd_help }, |
802 |
{ "interface", hostapd_cli_cmd_interface }, |
803 |
{ "level", hostapd_cli_cmd_level }, |
804 |
{ "license", hostapd_cli_cmd_license }, |
805 |
{ "quit", hostapd_cli_cmd_quit }, |
806 |
{ "set", hostapd_cli_cmd_set }, |
807 |
{ "get", hostapd_cli_cmd_get }, |
808 |
{ NULL, NULL } |
809 |
}; |
810 |
|
811 |
|
812 |
static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) |
813 |
{ |
814 |
struct hostapd_cli_cmd *cmd, *match = NULL; |
815 |
int count; |
816 |
|
817 |
count = 0; |
818 |
cmd = hostapd_cli_commands; |
819 |
while (cmd->cmd) { |
820 |
if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { |
821 |
match = cmd; |
822 |
if (os_strcasecmp(cmd->cmd, argv[0]) == 0) { |
823 |
/* we have an exact match */ |
824 |
count = 1; |
825 |
break; |
826 |
} |
827 |
count++; |
828 |
} |
829 |
cmd++; |
830 |
} |
831 |
|
832 |
if (count > 1) { |
833 |
printf("Ambiguous command '%s'; possible commands:", argv[0]); |
834 |
cmd = hostapd_cli_commands; |
835 |
while (cmd->cmd) { |
836 |
if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == |
837 |
0) { |
838 |
printf(" %s", cmd->cmd); |
839 |
} |
840 |
cmd++; |
841 |
} |
842 |
printf("\n"); |
843 |
} else if (count == 0) { |
844 |
printf("Unknown command '%s'\n", argv[0]); |
845 |
} else { |
846 |
match->handler(ctrl, argc - 1, &argv[1]); |
847 |
} |
848 |
} |
849 |
|
850 |
|
851 |
static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, |
852 |
int action_monitor) |
853 |
{ |
854 |
int first = 1; |
855 |
if (ctrl_conn == NULL) |
856 |
return; |
857 |
while (wpa_ctrl_pending(ctrl)) { |
858 |
char buf[256]; |
859 |
size_t len = sizeof(buf) - 1; |
860 |
if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { |
861 |
buf[len] = '\0'; |
862 |
if (action_monitor) |
863 |
hostapd_cli_action_process(buf, len); |
864 |
else { |
865 |
if (in_read && first) |
866 |
printf("\n"); |
867 |
first = 0; |
868 |
printf("%s\n", buf); |
869 |
} |
870 |
} else { |
871 |
printf("Could not read pending message.\n"); |
872 |
break; |
873 |
} |
874 |
} |
875 |
} |
876 |
|
877 |
|
878 |
#define max_args 10 |
879 |
|
880 |
static int tokenize_cmd(char *cmd, char *argv[]) |
881 |
{ |
882 |
char *pos; |
883 |
int argc = 0; |
884 |
|
885 |
pos = cmd; |
886 |
for (;;) { |
887 |
while (*pos == ' ') |
888 |
pos++; |
889 |
if (*pos == '\0') |
890 |
break; |
891 |
argv[argc] = pos; |
892 |
argc++; |
893 |
if (argc == max_args) |
894 |
break; |
895 |
if (*pos == '"') { |
896 |
char *pos2 = os_strrchr(pos, '"'); |
897 |
if (pos2) |
898 |
pos = pos2 + 1; |
899 |
} |
900 |
while (*pos != '\0' && *pos != ' ') |
901 |
pos++; |
902 |
if (*pos == ' ') |
903 |
*pos++ = '\0'; |
904 |
} |
905 |
|
906 |
return argc; |
907 |
} |
908 |
|
909 |
|
910 |
static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx) |
911 |
{ |
912 |
if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { |
913 |
printf("Connection to hostapd lost - trying to reconnect\n"); |
914 |
hostapd_cli_close_connection(); |
915 |
} |
916 |
if (!ctrl_conn) { |
917 |
ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); |
918 |
if (ctrl_conn) { |
919 |
printf("Connection to hostapd re-established\n"); |
920 |
if (wpa_ctrl_attach(ctrl_conn) == 0) { |
921 |
hostapd_cli_attached = 1; |
922 |
} else { |
923 |
printf("Warning: Failed to attach to " |
924 |
"hostapd.\n"); |
925 |
} |
926 |
} |
927 |
} |
928 |
if (ctrl_conn) |
929 |
hostapd_cli_recv_pending(ctrl_conn, 1, 0); |
930 |
eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); |
931 |
} |
932 |
|
933 |
|
934 |
static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx) |
935 |
{ |
936 |
eloop_terminate(); |
937 |
} |
938 |
|
939 |
|
940 |
static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd) |
941 |
{ |
942 |
char *argv[max_args]; |
943 |
int argc; |
944 |
argc = tokenize_cmd(cmd, argv); |
945 |
if (argc) |
946 |
wpa_request(ctrl_conn, argc, argv); |
947 |
} |
948 |
|
949 |
|
950 |
static void hostapd_cli_edit_eof_cb(void *ctx) |
951 |
{ |
952 |
eloop_terminate(); |
953 |
} |
954 |
|
955 |
|
956 |
static void hostapd_cli_interactive(void) |
957 |
{ |
958 |
printf("\nInteractive mode\n\n"); |
959 |
|
960 |
eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); |
961 |
edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, |
962 |
NULL, NULL, NULL, NULL); |
963 |
eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); |
964 |
|
965 |
eloop_run(); |
966 |
|
967 |
edit_deinit(NULL, NULL); |
968 |
eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL); |
969 |
} |
970 |
|
971 |
|
972 |
static void hostapd_cli_cleanup(void) |
973 |
{ |
974 |
hostapd_cli_close_connection(); |
975 |
if (pid_file) |
976 |
os_daemonize_terminate(pid_file); |
977 |
|
978 |
os_program_deinit(); |
979 |
} |
980 |
|
981 |
|
982 |
static void hostapd_cli_action(struct wpa_ctrl *ctrl) |
983 |
{ |
984 |
fd_set rfds; |
985 |
int fd, res; |
986 |
struct timeval tv; |
987 |
char buf[256]; |
988 |
size_t len; |
989 |
|
990 |
fd = wpa_ctrl_get_fd(ctrl); |
991 |
|
992 |
while (!hostapd_cli_quit) { |
993 |
FD_ZERO(&rfds); |
994 |
FD_SET(fd, &rfds); |
995 |
tv.tv_sec = ping_interval; |
996 |
tv.tv_usec = 0; |
997 |
res = select(fd + 1, &rfds, NULL, NULL, &tv); |
998 |
if (res < 0 && errno != EINTR) { |
999 |
perror("select"); |
1000 |
break; |
1001 |
} |
1002 |
|
1003 |
if (FD_ISSET(fd, &rfds)) |
1004 |
hostapd_cli_recv_pending(ctrl, 0, 1); |
1005 |
else { |
1006 |
len = sizeof(buf) - 1; |
1007 |
if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, |
1008 |
hostapd_cli_action_process) < 0 || |
1009 |
len < 4 || os_memcmp(buf, "PONG", 4) != 0) { |
1010 |
printf("hostapd did not reply to PING " |
1011 |
"command - exiting\n"); |
1012 |
break; |
1013 |
} |
1014 |
} |
1015 |
} |
1016 |
} |
1017 |
|
1018 |
|
1019 |
int main(int argc, char *argv[]) |
1020 |
{ |
1021 |
int warning_displayed = 0; |
1022 |
int c; |
1023 |
int daemonize = 0; |
1024 |
|
1025 |
if (os_program_init()) |
1026 |
return -1; |
1027 |
|
1028 |
for (;;) { |
1029 |
c = getopt(argc, argv, "a:BhG:i:p:v"); |
1030 |
if (c < 0) |
1031 |
break; |
1032 |
switch (c) { |
1033 |
case 'a': |
1034 |
action_file = optarg; |
1035 |
break; |
1036 |
case 'B': |
1037 |
daemonize = 1; |
1038 |
break; |
1039 |
case 'G': |
1040 |
ping_interval = atoi(optarg); |
1041 |
break; |
1042 |
case 'h': |
1043 |
usage(); |
1044 |
return 0; |
1045 |
case 'v': |
1046 |
printf("%s\n", hostapd_cli_version); |
1047 |
return 0; |
1048 |
case 'i': |
1049 |
os_free(ctrl_ifname); |
1050 |
ctrl_ifname = os_strdup(optarg); |
1051 |
break; |
1052 |
case 'p': |
1053 |
ctrl_iface_dir = optarg; |
1054 |
break; |
1055 |
default: |
1056 |
usage(); |
1057 |
return -1; |
1058 |
} |
1059 |
} |
1060 |
|
1061 |
interactive = (argc == optind) && (action_file == NULL); |
1062 |
|
1063 |
if (interactive) { |
1064 |
printf("%s\n\n%s\n\n", hostapd_cli_version, |
1065 |
hostapd_cli_license); |
1066 |
} |
1067 |
|
1068 |
if (eloop_init()) |
1069 |
return -1; |
1070 |
|
1071 |
for (;;) { |
1072 |
if (ctrl_ifname == NULL) { |
1073 |
struct dirent *dent; |
1074 |
DIR *dir = opendir(ctrl_iface_dir); |
1075 |
if (dir) { |
1076 |
while ((dent = readdir(dir))) { |
1077 |
if (os_strcmp(dent->d_name, ".") == 0 |
1078 |
|| |
1079 |
os_strcmp(dent->d_name, "..") == 0) |
1080 |
continue; |
1081 |
printf("Selected interface '%s'\n", |
1082 |
dent->d_name); |
1083 |
ctrl_ifname = os_strdup(dent->d_name); |
1084 |
break; |
1085 |
} |
1086 |
closedir(dir); |
1087 |
} |
1088 |
} |
1089 |
ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); |
1090 |
if (ctrl_conn) { |
1091 |
if (warning_displayed) |
1092 |
printf("Connection established.\n"); |
1093 |
break; |
1094 |
} |
1095 |
|
1096 |
if (!interactive) { |
1097 |
perror("Failed to connect to hostapd - " |
1098 |
"wpa_ctrl_open"); |
1099 |
return -1; |
1100 |
} |
1101 |
|
1102 |
if (!warning_displayed) { |
1103 |
printf("Could not connect to hostapd - re-trying\n"); |
1104 |
warning_displayed = 1; |
1105 |
} |
1106 |
os_sleep(1, 0); |
1107 |
continue; |
1108 |
} |
1109 |
|
1110 |
if (interactive || action_file) { |
1111 |
if (wpa_ctrl_attach(ctrl_conn) == 0) { |
1112 |
hostapd_cli_attached = 1; |
1113 |
} else { |
1114 |
printf("Warning: Failed to attach to hostapd.\n"); |
1115 |
if (action_file) |
1116 |
return -1; |
1117 |
} |
1118 |
} |
1119 |
|
1120 |
if (daemonize && os_daemonize(pid_file)) |
1121 |
return -1; |
1122 |
|
1123 |
if (interactive) |
1124 |
hostapd_cli_interactive(); |
1125 |
else if (action_file) |
1126 |
hostapd_cli_action(ctrl_conn); |
1127 |
else |
1128 |
wpa_request(ctrl_conn, argc - optind, &argv[optind]); |
1129 |
|
1130 |
os_free(ctrl_ifname); |
1131 |
eloop_destroy(); |
1132 |
hostapd_cli_cleanup(); |
1133 |
return 0; |
1134 |
} |