blob: 88d3a026484cbc54d5c4da4e2dcff9a49095be74 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * wpa_supplicant/hostapd control interface library
3 * Copyright (c) 2004-2007, 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
17#ifdef CONFIG_CTRL_IFACE
18
19#ifdef CONFIG_CTRL_IFACE_UNIX
20#include <sys/un.h>
21#endif /* CONFIG_CTRL_IFACE_UNIX */
22
23#ifdef ANDROID
24#include <cutils/sockets.h>
25#include "private/android_filesystem_config.h"
26#endif /* ANDROID */
27
28#include "wpa_ctrl.h"
29#include "common.h"
30
31
32#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
33#define CTRL_IFACE_SOCKET
34#endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */
35
36
37/**
38 * struct wpa_ctrl - Internal structure for control interface library
39 *
40 * This structure is used by the wpa_supplicant/hostapd control interface
41 * library to store internal data. Programs using the library should not touch
42 * this data directly. They can only use the pointer to the data structure as
43 * an identifier for the control interface connection and use this as one of
44 * the arguments for most of the control interface library functions.
45 */
46struct wpa_ctrl {
47#ifdef CONFIG_CTRL_IFACE_UDP
48 int s;
49 struct sockaddr_in local;
50 struct sockaddr_in dest;
51 char *cookie;
52#endif /* CONFIG_CTRL_IFACE_UDP */
53#ifdef CONFIG_CTRL_IFACE_UNIX
54 int s;
55 struct sockaddr_un local;
56 struct sockaddr_un dest;
57#endif /* CONFIG_CTRL_IFACE_UNIX */
58#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
59 HANDLE pipe;
60#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
61};
62
63
64#ifdef CONFIG_CTRL_IFACE_UNIX
65
66#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR
67#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp"
68#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */
69#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX
70#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_"
71#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */
72
73
74struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
75{
76 struct wpa_ctrl *ctrl;
77 static int counter = 0;
78 int ret;
79 size_t res;
80 int tries = 0;
81
82 ctrl = os_malloc(sizeof(*ctrl));
83 if (ctrl == NULL)
84 return NULL;
85 os_memset(ctrl, 0, sizeof(*ctrl));
86
87 ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
88 if (ctrl->s < 0) {
89 os_free(ctrl);
90 return NULL;
91 }
92
93 ctrl->local.sun_family = AF_UNIX;
94 counter++;
95try_again:
96 ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
97 CONFIG_CTRL_IFACE_CLIENT_DIR "/"
98 CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
99 (int) getpid(), counter);
100 if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
101 close(ctrl->s);
102 os_free(ctrl);
103 return NULL;
104 }
105 tries++;
106 if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
107 sizeof(ctrl->local)) < 0) {
108 if (errno == EADDRINUSE && tries < 2) {
109 /*
110 * getpid() returns unique identifier for this instance
111 * of wpa_ctrl, so the existing socket file must have
112 * been left by unclean termination of an earlier run.
113 * Remove the file and try again.
114 */
115 unlink(ctrl->local.sun_path);
116 goto try_again;
117 }
118 close(ctrl->s);
119 os_free(ctrl);
120 return NULL;
121 }
122
123#ifdef ANDROID
124 chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
125 chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
126 /*
127 * If the ctrl_path isn't an absolute pathname, assume that
128 * it's the name of a socket in the Android reserved namespace.
129 * Otherwise, it's a normal UNIX domain socket appearing in the
130 * filesystem.
131 */
132 if (ctrl_path != NULL && *ctrl_path != '/') {
133 char buf[21];
134 os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path);
135 if (socket_local_client_connect(
136 ctrl->s, buf,
137 ANDROID_SOCKET_NAMESPACE_RESERVED,
138 SOCK_DGRAM) < 0) {
139 close(ctrl->s);
140 unlink(ctrl->local.sun_path);
141 os_free(ctrl);
142 return NULL;
143 }
144 return ctrl;
145 }
146#endif /* ANDROID */
147
148 ctrl->dest.sun_family = AF_UNIX;
149 res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
150 sizeof(ctrl->dest.sun_path));
151 if (res >= sizeof(ctrl->dest.sun_path)) {
152 close(ctrl->s);
153 os_free(ctrl);
154 return NULL;
155 }
156 if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
157 sizeof(ctrl->dest)) < 0) {
158 close(ctrl->s);
159 unlink(ctrl->local.sun_path);
160 os_free(ctrl);
161 return NULL;
162 }
163
164 return ctrl;
165}
166
167
168void wpa_ctrl_close(struct wpa_ctrl *ctrl)
169{
170 if (ctrl == NULL)
171 return;
172 unlink(ctrl->local.sun_path);
173 if (ctrl->s >= 0)
174 close(ctrl->s);
175 os_free(ctrl);
176}
177
178#endif /* CONFIG_CTRL_IFACE_UNIX */
179
180
181#ifdef CONFIG_CTRL_IFACE_UDP
182
183struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
184{
185 struct wpa_ctrl *ctrl;
186 char buf[128];
187 size_t len;
188
189 ctrl = os_malloc(sizeof(*ctrl));
190 if (ctrl == NULL)
191 return NULL;
192 os_memset(ctrl, 0, sizeof(*ctrl));
193
194 ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
195 if (ctrl->s < 0) {
196 perror("socket");
197 os_free(ctrl);
198 return NULL;
199 }
200
201 ctrl->local.sin_family = AF_INET;
202 ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
203 if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
204 sizeof(ctrl->local)) < 0) {
205 close(ctrl->s);
206 os_free(ctrl);
207 return NULL;
208 }
209
210 ctrl->dest.sin_family = AF_INET;
211 ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
212 ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT);
213 if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
214 sizeof(ctrl->dest)) < 0) {
215 perror("connect");
216 close(ctrl->s);
217 os_free(ctrl);
218 return NULL;
219 }
220
221 len = sizeof(buf) - 1;
222 if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) {
223 buf[len] = '\0';
224 ctrl->cookie = os_strdup(buf);
225 }
226
227 return ctrl;
228}
229
230
231void wpa_ctrl_close(struct wpa_ctrl *ctrl)
232{
233 close(ctrl->s);
234 os_free(ctrl->cookie);
235 os_free(ctrl);
236}
237
238#endif /* CONFIG_CTRL_IFACE_UDP */
239
240
241#ifdef CTRL_IFACE_SOCKET
242int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
243 char *reply, size_t *reply_len,
244 void (*msg_cb)(char *msg, size_t len))
245{
246 struct timeval tv;
247 int res;
248 fd_set rfds;
249 const char *_cmd;
250 char *cmd_buf = NULL;
251 size_t _cmd_len;
252
253#ifdef CONFIG_CTRL_IFACE_UDP
254 if (ctrl->cookie) {
255 char *pos;
256 _cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
257 cmd_buf = os_malloc(_cmd_len);
258 if (cmd_buf == NULL)
259 return -1;
260 _cmd = cmd_buf;
261 pos = cmd_buf;
262 os_strlcpy(pos, ctrl->cookie, _cmd_len);
263 pos += os_strlen(ctrl->cookie);
264 *pos++ = ' ';
265 os_memcpy(pos, cmd, cmd_len);
266 } else
267#endif /* CONFIG_CTRL_IFACE_UDP */
268 {
269 _cmd = cmd;
270 _cmd_len = cmd_len;
271 }
272
273 if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
274 os_free(cmd_buf);
275 return -1;
276 }
277 os_free(cmd_buf);
278
279 for (;;) {
280 tv.tv_sec = 10;
281 tv.tv_usec = 0;
282 FD_ZERO(&rfds);
283 FD_SET(ctrl->s, &rfds);
284 res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
285 if (res < 0)
286 return res;
287 if (FD_ISSET(ctrl->s, &rfds)) {
288 res = recv(ctrl->s, reply, *reply_len, 0);
289 if (res < 0)
290 return res;
291 if (res > 0 && reply[0] == '<') {
292 /* This is an unsolicited message from
293 * wpa_supplicant, not the reply to the
294 * request. Use msg_cb to report this to the
295 * caller. */
296 if (msg_cb) {
297 /* Make sure the message is nul
298 * terminated. */
299 if ((size_t) res == *reply_len)
300 res = (*reply_len) - 1;
301 reply[res] = '\0';
302 msg_cb(reply, res);
303 }
304 continue;
305 }
306 *reply_len = res;
307 break;
308 } else {
309 return -2;
310 }
311 }
312 return 0;
313}
314#endif /* CTRL_IFACE_SOCKET */
315
316
317static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach)
318{
319 char buf[10];
320 int ret;
321 size_t len = 10;
322
323 ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6,
324 buf, &len, NULL);
325 if (ret < 0)
326 return ret;
327 if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0)
328 return 0;
329 return -1;
330}
331
332
333int wpa_ctrl_attach(struct wpa_ctrl *ctrl)
334{
335 return wpa_ctrl_attach_helper(ctrl, 1);
336}
337
338
339int wpa_ctrl_detach(struct wpa_ctrl *ctrl)
340{
341 return wpa_ctrl_attach_helper(ctrl, 0);
342}
343
344
345#ifdef CTRL_IFACE_SOCKET
346
347int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
348{
349 int res;
350
351 res = recv(ctrl->s, reply, *reply_len, 0);
352 if (res < 0)
353 return res;
354 *reply_len = res;
355 return 0;
356}
357
358
359int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
360{
361 struct timeval tv;
362 fd_set rfds;
363 tv.tv_sec = 0;
364 tv.tv_usec = 0;
365 FD_ZERO(&rfds);
366 FD_SET(ctrl->s, &rfds);
367 select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
368 return FD_ISSET(ctrl->s, &rfds);
369}
370
371
372int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
373{
374 return ctrl->s;
375}
376
377#endif /* CTRL_IFACE_SOCKET */
378
379
380#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
381
382#ifndef WPA_SUPPLICANT_NAMED_PIPE
383#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
384#endif
385#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
386
387struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
388{
389 struct wpa_ctrl *ctrl;
390 DWORD mode;
391 TCHAR name[256];
392 int i, ret;
393
394 ctrl = os_malloc(sizeof(*ctrl));
395 if (ctrl == NULL)
396 return NULL;
397 os_memset(ctrl, 0, sizeof(*ctrl));
398
399#ifdef UNICODE
400 if (ctrl_path == NULL)
401 ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX);
402 else
403 ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
404 ctrl_path);
405#else /* UNICODE */
406 if (ctrl_path == NULL)
407 ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX);
408 else
409 ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
410 ctrl_path);
411#endif /* UNICODE */
412 if (ret < 0 || ret >= 256) {
413 os_free(ctrl);
414 return NULL;
415 }
416
417 for (i = 0; i < 10; i++) {
418 ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0,
419 NULL, OPEN_EXISTING, 0, NULL);
420 /*
421 * Current named pipe server side in wpa_supplicant is
422 * re-opening the pipe for new clients only after the previous
423 * one is taken into use. This leaves a small window for race
424 * conditions when two connections are being opened at almost
425 * the same time. Retry if that was the case.
426 */
427 if (ctrl->pipe != INVALID_HANDLE_VALUE ||
428 GetLastError() != ERROR_PIPE_BUSY)
429 break;
430 WaitNamedPipe(name, 1000);
431 }
432 if (ctrl->pipe == INVALID_HANDLE_VALUE) {
433 os_free(ctrl);
434 return NULL;
435 }
436
437 mode = PIPE_READMODE_MESSAGE;
438 if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) {
439 CloseHandle(ctrl->pipe);
440 os_free(ctrl);
441 return NULL;
442 }
443
444 return ctrl;
445}
446
447
448void wpa_ctrl_close(struct wpa_ctrl *ctrl)
449{
450 CloseHandle(ctrl->pipe);
451 os_free(ctrl);
452}
453
454
455int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
456 char *reply, size_t *reply_len,
457 void (*msg_cb)(char *msg, size_t len))
458{
459 DWORD written;
460 DWORD readlen = *reply_len;
461
462 if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL))
463 return -1;
464
465 if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL))
466 return -1;
467 *reply_len = readlen;
468
469 return 0;
470}
471
472
473int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
474{
475 DWORD len = *reply_len;
476 if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL))
477 return -1;
478 *reply_len = len;
479 return 0;
480}
481
482
483int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
484{
485 DWORD left;
486
487 if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL))
488 return -1;
489 return left ? 1 : 0;
490}
491
492
493int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
494{
495 return -1;
496}
497
498#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
499
500#endif /* CONFIG_CTRL_IFACE */