]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * wpa_supplicant/hostapd control interface library | |
3 | * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #ifdef CONFIG_CTRL_IFACE | |
12 | ||
13 | #ifdef CONFIG_CTRL_IFACE_UNIX | |
b686745c JM |
14 | #include <sys/stat.h> |
15 | #include <fcntl.h> | |
6fc6879b | 16 | #include <sys/un.h> |
4fdc8def BG |
17 | #include <unistd.h> |
18 | #include <fcntl.h> | |
6fc6879b | 19 | #endif /* CONFIG_CTRL_IFACE_UNIX */ |
d302edd3 JD |
20 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
21 | #include <netdb.h> | |
22 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
6fc6879b | 23 | |
b3f3865e | 24 | #ifdef ANDROID |
ed3eecd7 | 25 | #include <dirent.h> |
0144ecb8 | 26 | #include <sys/stat.h> |
b3f3865e DS |
27 | #include <cutils/sockets.h> |
28 | #include "private/android_filesystem_config.h" | |
29 | #endif /* ANDROID */ | |
30 | ||
4db216fc JD |
31 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
32 | #include <net/if.h> | |
33 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
34 | ||
6fc6879b JM |
35 | #include "wpa_ctrl.h" |
36 | #include "common.h" | |
37 | ||
38 | ||
39 | #if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) | |
40 | #define CTRL_IFACE_SOCKET | |
41 | #endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */ | |
42 | ||
43 | ||
44 | /** | |
45 | * struct wpa_ctrl - Internal structure for control interface library | |
46 | * | |
47 | * This structure is used by the wpa_supplicant/hostapd control interface | |
48 | * library to store internal data. Programs using the library should not touch | |
49 | * this data directly. They can only use the pointer to the data structure as | |
50 | * an identifier for the control interface connection and use this as one of | |
51 | * the arguments for most of the control interface library functions. | |
52 | */ | |
53 | struct wpa_ctrl { | |
54 | #ifdef CONFIG_CTRL_IFACE_UDP | |
55 | int s; | |
4db216fc JD |
56 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
57 | struct sockaddr_in6 local; | |
58 | struct sockaddr_in6 dest; | |
59 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
60 | struct sockaddr_in local; |
61 | struct sockaddr_in dest; | |
4db216fc | 62 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b | 63 | char *cookie; |
4307bb8c JD |
64 | char *remote_ifname; |
65 | char *remote_ip; | |
6fc6879b JM |
66 | #endif /* CONFIG_CTRL_IFACE_UDP */ |
67 | #ifdef CONFIG_CTRL_IFACE_UNIX | |
68 | int s; | |
69 | struct sockaddr_un local; | |
70 | struct sockaddr_un dest; | |
71 | #endif /* CONFIG_CTRL_IFACE_UNIX */ | |
72 | #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE | |
73 | HANDLE pipe; | |
74 | #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ | |
75 | }; | |
76 | ||
77 | ||
78 | #ifdef CONFIG_CTRL_IFACE_UNIX | |
79 | ||
73304dbf JM |
80 | #ifndef CONFIG_CTRL_IFACE_CLIENT_DIR |
81 | #define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp" | |
82 | #endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */ | |
83 | #ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX | |
84 | #define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_" | |
85 | #endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */ | |
86 | ||
87 | ||
6fc6879b | 88 | struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) |
4ae71209 MM |
89 | { |
90 | return wpa_ctrl_open2(ctrl_path, NULL); | |
91 | } | |
92 | ||
93 | ||
94 | struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, | |
95 | const char *cli_path) | |
6fc6879b JM |
96 | { |
97 | struct wpa_ctrl *ctrl; | |
98 | static int counter = 0; | |
99 | int ret; | |
100 | size_t res; | |
6e488ff0 | 101 | int tries = 0; |
4fdc8def | 102 | int flags; |
6fc6879b | 103 | |
d2a9e2c7 JM |
104 | if (ctrl_path == NULL) |
105 | return NULL; | |
106 | ||
89b48f7b | 107 | ctrl = os_zalloc(sizeof(*ctrl)); |
6fc6879b JM |
108 | if (ctrl == NULL) |
109 | return NULL; | |
6fc6879b JM |
110 | |
111 | ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); | |
112 | if (ctrl->s < 0) { | |
113 | os_free(ctrl); | |
114 | return NULL; | |
115 | } | |
116 | ||
117 | ctrl->local.sun_family = AF_UNIX; | |
6e488ff0 JM |
118 | counter++; |
119 | try_again: | |
4ae71209 MM |
120 | if (cli_path && cli_path[0] == '/') { |
121 | ret = os_snprintf(ctrl->local.sun_path, | |
122 | sizeof(ctrl->local.sun_path), | |
123 | "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", | |
124 | cli_path, (int) getpid(), counter); | |
125 | } else { | |
126 | ret = os_snprintf(ctrl->local.sun_path, | |
127 | sizeof(ctrl->local.sun_path), | |
128 | CONFIG_CTRL_IFACE_CLIENT_DIR "/" | |
129 | CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", | |
130 | (int) getpid(), counter); | |
131 | } | |
d85e1fc8 | 132 | if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) { |
6fc6879b JM |
133 | close(ctrl->s); |
134 | os_free(ctrl); | |
135 | return NULL; | |
136 | } | |
6e488ff0 | 137 | tries++; |
b686745c JM |
138 | #ifdef ANDROID |
139 | /* Set client socket file permissions so that bind() creates the client | |
140 | * socket with these permissions and there is no need to try to change | |
141 | * them with chmod() after bind() which would have potential issues with | |
142 | * race conditions. These permissions are needed to make sure the server | |
143 | * side (wpa_supplicant or hostapd) can reply to the control interface | |
144 | * messages. | |
145 | * | |
146 | * The lchown() calls below after bind() are also part of the needed | |
147 | * operations to allow the response to go through. Those are using the | |
148 | * no-deference-symlinks version to avoid races. */ | |
149 | fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); | |
150 | #endif /* ANDROID */ | |
6fc6879b JM |
151 | if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, |
152 | sizeof(ctrl->local)) < 0) { | |
6e488ff0 JM |
153 | if (errno == EADDRINUSE && tries < 2) { |
154 | /* | |
155 | * getpid() returns unique identifier for this instance | |
156 | * of wpa_ctrl, so the existing socket file must have | |
157 | * been left by unclean termination of an earlier run. | |
158 | * Remove the file and try again. | |
159 | */ | |
160 | unlink(ctrl->local.sun_path); | |
161 | goto try_again; | |
162 | } | |
6fc6879b JM |
163 | close(ctrl->s); |
164 | os_free(ctrl); | |
165 | return NULL; | |
166 | } | |
167 | ||
b3f3865e | 168 | #ifdef ANDROID |
10cac5b1 | 169 | /* Set group even if we do not have privileges to change owner */ |
b686745c JM |
170 | lchown(ctrl->local.sun_path, -1, AID_WIFI); |
171 | lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); | |
d2a9e2c7 JM |
172 | |
173 | if (os_strncmp(ctrl_path, "@android:", 9) == 0) { | |
174 | if (socket_local_client_connect( | |
175 | ctrl->s, ctrl_path + 9, | |
176 | ANDROID_SOCKET_NAMESPACE_RESERVED, | |
177 | SOCK_DGRAM) < 0) { | |
178 | close(ctrl->s); | |
179 | unlink(ctrl->local.sun_path); | |
180 | os_free(ctrl); | |
181 | return NULL; | |
182 | } | |
183 | return ctrl; | |
184 | } | |
185 | ||
b3f3865e DS |
186 | /* |
187 | * If the ctrl_path isn't an absolute pathname, assume that | |
188 | * it's the name of a socket in the Android reserved namespace. | |
189 | * Otherwise, it's a normal UNIX domain socket appearing in the | |
190 | * filesystem. | |
191 | */ | |
d2a9e2c7 | 192 | if (*ctrl_path != '/') { |
b3f3865e DS |
193 | char buf[21]; |
194 | os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path); | |
195 | if (socket_local_client_connect( | |
196 | ctrl->s, buf, | |
197 | ANDROID_SOCKET_NAMESPACE_RESERVED, | |
198 | SOCK_DGRAM) < 0) { | |
199 | close(ctrl->s); | |
200 | unlink(ctrl->local.sun_path); | |
201 | os_free(ctrl); | |
202 | return NULL; | |
203 | } | |
204 | return ctrl; | |
205 | } | |
206 | #endif /* ANDROID */ | |
207 | ||
6fc6879b | 208 | ctrl->dest.sun_family = AF_UNIX; |
d2a9e2c7 JM |
209 | if (os_strncmp(ctrl_path, "@abstract:", 10) == 0) { |
210 | ctrl->dest.sun_path[0] = '\0'; | |
211 | os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10, | |
212 | sizeof(ctrl->dest.sun_path) - 1); | |
213 | } else { | |
214 | res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, | |
215 | sizeof(ctrl->dest.sun_path)); | |
216 | if (res >= sizeof(ctrl->dest.sun_path)) { | |
217 | close(ctrl->s); | |
218 | os_free(ctrl); | |
219 | return NULL; | |
220 | } | |
6fc6879b JM |
221 | } |
222 | if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, | |
223 | sizeof(ctrl->dest)) < 0) { | |
224 | close(ctrl->s); | |
225 | unlink(ctrl->local.sun_path); | |
226 | os_free(ctrl); | |
227 | return NULL; | |
228 | } | |
229 | ||
4fdc8def BG |
230 | /* |
231 | * Make socket non-blocking so that we don't hang forever if | |
232 | * target dies unexpectedly. | |
233 | */ | |
234 | flags = fcntl(ctrl->s, F_GETFL); | |
235 | if (flags >= 0) { | |
236 | flags |= O_NONBLOCK; | |
237 | if (fcntl(ctrl->s, F_SETFL, flags) < 0) { | |
238 | perror("fcntl(ctrl->s, O_NONBLOCK)"); | |
239 | /* Not fatal, continue on.*/ | |
240 | } | |
241 | } | |
242 | ||
6fc6879b JM |
243 | return ctrl; |
244 | } | |
245 | ||
246 | ||
247 | void wpa_ctrl_close(struct wpa_ctrl *ctrl) | |
248 | { | |
36fde1e7 DS |
249 | if (ctrl == NULL) |
250 | return; | |
6fc6879b | 251 | unlink(ctrl->local.sun_path); |
36fde1e7 DS |
252 | if (ctrl->s >= 0) |
253 | close(ctrl->s); | |
6fc6879b JM |
254 | os_free(ctrl); |
255 | } | |
256 | ||
ed3eecd7 DS |
257 | |
258 | #ifdef ANDROID | |
259 | /** | |
260 | * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that | |
261 | * may be left over from clients that were previously connected to | |
262 | * wpa_supplicant. This keeps these files from being orphaned in the | |
263 | * event of crashes that prevented them from being removed as part | |
264 | * of the normal orderly shutdown. | |
265 | */ | |
266 | void wpa_ctrl_cleanup(void) | |
267 | { | |
268 | DIR *dir; | |
269 | struct dirent entry; | |
270 | struct dirent *result; | |
271 | size_t dirnamelen; | |
ed3eecd7 DS |
272 | size_t maxcopy; |
273 | char pathname[PATH_MAX]; | |
274 | char *namep; | |
275 | ||
276 | if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL) | |
277 | return; | |
278 | ||
279 | dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/", | |
280 | CONFIG_CTRL_IFACE_CLIENT_DIR); | |
281 | if (dirnamelen >= sizeof(pathname)) { | |
282 | closedir(dir); | |
283 | return; | |
284 | } | |
285 | namep = pathname + dirnamelen; | |
286 | maxcopy = PATH_MAX - dirnamelen; | |
287 | while (readdir_r(dir, &entry, &result) == 0 && result != NULL) { | |
7b74c0ac DS |
288 | if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy) |
289 | unlink(pathname); | |
ed3eecd7 DS |
290 | } |
291 | closedir(dir); | |
292 | } | |
293 | #endif /* ANDROID */ | |
294 | ||
295 | #else /* CONFIG_CTRL_IFACE_UNIX */ | |
296 | ||
297 | #ifdef ANDROID | |
298 | void wpa_ctrl_cleanup(void) | |
299 | { | |
300 | } | |
301 | #endif /* ANDROID */ | |
302 | ||
6fc6879b JM |
303 | #endif /* CONFIG_CTRL_IFACE_UNIX */ |
304 | ||
305 | ||
306 | #ifdef CONFIG_CTRL_IFACE_UDP | |
307 | ||
308 | struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) | |
309 | { | |
310 | struct wpa_ctrl *ctrl; | |
311 | char buf[128]; | |
312 | size_t len; | |
d302edd3 JD |
313 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
314 | struct hostent *h; | |
315 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
6fc6879b | 316 | |
89b48f7b | 317 | ctrl = os_zalloc(sizeof(*ctrl)); |
6fc6879b JM |
318 | if (ctrl == NULL) |
319 | return NULL; | |
6fc6879b | 320 | |
4db216fc JD |
321 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
322 | ctrl->s = socket(PF_INET6, SOCK_DGRAM, 0); | |
323 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 324 | ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); |
4db216fc | 325 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
326 | if (ctrl->s < 0) { |
327 | perror("socket"); | |
328 | os_free(ctrl); | |
329 | return NULL; | |
330 | } | |
331 | ||
4db216fc JD |
332 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
333 | ctrl->local.sin6_family = AF_INET6; | |
334 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE | |
335 | ctrl->local.sin6_addr = in6addr_any; | |
336 | #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
337 | inet_pton(AF_INET6, "::1", &ctrl->local.sin6_addr); | |
338 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
339 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 340 | ctrl->local.sin_family = AF_INET; |
d302edd3 JD |
341 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
342 | ctrl->local.sin_addr.s_addr = INADDR_ANY; | |
343 | #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
6fc6879b | 344 | ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); |
d302edd3 | 345 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
4db216fc JD |
346 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
347 | ||
6fc6879b JM |
348 | if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, |
349 | sizeof(ctrl->local)) < 0) { | |
350 | close(ctrl->s); | |
351 | os_free(ctrl); | |
352 | return NULL; | |
353 | } | |
354 | ||
4db216fc JD |
355 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
356 | ctrl->dest.sin6_family = AF_INET6; | |
357 | inet_pton(AF_INET6, "::1", &ctrl->dest.sin6_addr); | |
358 | ctrl->dest.sin6_port = htons(WPA_CTRL_IFACE_PORT); | |
359 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
360 | ctrl->dest.sin_family = AF_INET; |
361 | ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); | |
362 | ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); | |
4db216fc | 363 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
d302edd3 JD |
364 | |
365 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE | |
366 | if (ctrl_path) { | |
367 | char *port, *name; | |
368 | int port_id; | |
4db216fc JD |
369 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
370 | char *scope; | |
371 | int scope_id = 0; | |
372 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
d302edd3 JD |
373 | |
374 | name = os_strdup(ctrl_path); | |
375 | if (name == NULL) { | |
376 | close(ctrl->s); | |
377 | os_free(ctrl); | |
378 | return NULL; | |
379 | } | |
4db216fc JD |
380 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
381 | port = os_strchr(name, ','); | |
382 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
d302edd3 | 383 | port = os_strchr(name, ':'); |
4db216fc | 384 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
d302edd3 JD |
385 | |
386 | if (port) { | |
387 | port_id = atoi(&port[1]); | |
388 | port[0] = '\0'; | |
389 | } else | |
390 | port_id = WPA_CTRL_IFACE_PORT; | |
391 | ||
4db216fc JD |
392 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
393 | scope = os_strchr(name, '%'); | |
394 | if (scope) { | |
395 | scope_id = if_nametoindex(&scope[1]); | |
396 | scope[0] = '\0'; | |
397 | } | |
398 | h = gethostbyname2(name, AF_INET6); | |
399 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
d302edd3 | 400 | h = gethostbyname(name); |
4db216fc | 401 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
4307bb8c | 402 | ctrl->remote_ip = os_strdup(name); |
d302edd3 JD |
403 | os_free(name); |
404 | if (h == NULL) { | |
405 | perror("gethostbyname"); | |
406 | close(ctrl->s); | |
4307bb8c | 407 | os_free(ctrl->remote_ip); |
d302edd3 JD |
408 | os_free(ctrl); |
409 | return NULL; | |
410 | } | |
4db216fc JD |
411 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
412 | ctrl->dest.sin6_scope_id = scope_id; | |
413 | ctrl->dest.sin6_port = htons(port_id); | |
414 | os_memcpy(&ctrl->dest.sin6_addr, h->h_addr, h->h_length); | |
415 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
d302edd3 | 416 | ctrl->dest.sin_port = htons(port_id); |
4db216fc JD |
417 | os_memcpy(&ctrl->dest.sin_addr.s_addr, h->h_addr, h->h_length); |
418 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
4307bb8c JD |
419 | } else |
420 | ctrl->remote_ip = os_strdup("localhost"); | |
d302edd3 JD |
421 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
422 | ||
6fc6879b JM |
423 | if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, |
424 | sizeof(ctrl->dest)) < 0) { | |
4db216fc JD |
425 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
426 | char addr[INET6_ADDRSTRLEN]; | |
427 | wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s", | |
428 | inet_ntop(AF_INET6, &ctrl->dest.sin6_addr, addr, | |
429 | sizeof(ctrl->dest)), | |
430 | ntohs(ctrl->dest.sin6_port), | |
431 | strerror(errno)); | |
432 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
433 | wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s", | |
434 | inet_ntoa(ctrl->dest.sin_addr), | |
435 | ntohs(ctrl->dest.sin_port), | |
436 | strerror(errno)); | |
437 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 438 | close(ctrl->s); |
4307bb8c | 439 | os_free(ctrl->remote_ip); |
6fc6879b JM |
440 | os_free(ctrl); |
441 | return NULL; | |
442 | } | |
443 | ||
444 | len = sizeof(buf) - 1; | |
445 | if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) { | |
446 | buf[len] = '\0'; | |
447 | ctrl->cookie = os_strdup(buf); | |
448 | } | |
449 | ||
4307bb8c JD |
450 | if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) { |
451 | buf[len] = '\0'; | |
452 | ctrl->remote_ifname = os_strdup(buf); | |
453 | } | |
454 | ||
6fc6879b JM |
455 | return ctrl; |
456 | } | |
457 | ||
458 | ||
4307bb8c JD |
459 | char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl) |
460 | { | |
461 | #define WPA_CTRL_MAX_PS_NAME 100 | |
462 | static char ps[WPA_CTRL_MAX_PS_NAME] = {}; | |
463 | os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s", | |
464 | ctrl->remote_ip, ctrl->remote_ifname); | |
465 | return ps; | |
466 | } | |
467 | ||
468 | ||
6fc6879b JM |
469 | void wpa_ctrl_close(struct wpa_ctrl *ctrl) |
470 | { | |
471 | close(ctrl->s); | |
472 | os_free(ctrl->cookie); | |
4307bb8c JD |
473 | os_free(ctrl->remote_ifname); |
474 | os_free(ctrl->remote_ip); | |
6fc6879b JM |
475 | os_free(ctrl); |
476 | } | |
477 | ||
478 | #endif /* CONFIG_CTRL_IFACE_UDP */ | |
479 | ||
480 | ||
481 | #ifdef CTRL_IFACE_SOCKET | |
482 | int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, | |
483 | char *reply, size_t *reply_len, | |
484 | void (*msg_cb)(char *msg, size_t len)) | |
485 | { | |
486 | struct timeval tv; | |
636e19a5 | 487 | struct os_reltime started_at; |
6fc6879b JM |
488 | int res; |
489 | fd_set rfds; | |
490 | const char *_cmd; | |
491 | char *cmd_buf = NULL; | |
492 | size_t _cmd_len; | |
493 | ||
494 | #ifdef CONFIG_CTRL_IFACE_UDP | |
495 | if (ctrl->cookie) { | |
496 | char *pos; | |
497 | _cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len; | |
498 | cmd_buf = os_malloc(_cmd_len); | |
499 | if (cmd_buf == NULL) | |
500 | return -1; | |
501 | _cmd = cmd_buf; | |
502 | pos = cmd_buf; | |
503 | os_strlcpy(pos, ctrl->cookie, _cmd_len); | |
504 | pos += os_strlen(ctrl->cookie); | |
505 | *pos++ = ' '; | |
506 | os_memcpy(pos, cmd, cmd_len); | |
507 | } else | |
508 | #endif /* CONFIG_CTRL_IFACE_UDP */ | |
509 | { | |
510 | _cmd = cmd; | |
511 | _cmd_len = cmd_len; | |
512 | } | |
513 | ||
4fdc8def BG |
514 | errno = 0; |
515 | started_at.sec = 0; | |
516 | started_at.usec = 0; | |
517 | retry_send: | |
6fc6879b | 518 | if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) { |
4fdc8def BG |
519 | if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK) |
520 | { | |
521 | /* | |
522 | * Must be a non-blocking socket... Try for a bit | |
523 | * longer before giving up. | |
524 | */ | |
525 | if (started_at.sec == 0) | |
636e19a5 | 526 | os_get_reltime(&started_at); |
4fdc8def | 527 | else { |
636e19a5 JB |
528 | struct os_reltime n; |
529 | os_get_reltime(&n); | |
4fdc8def | 530 | /* Try for a few seconds. */ |
636e19a5 | 531 | if (os_reltime_expired(&n, &started_at, 5)) |
4fdc8def BG |
532 | goto send_err; |
533 | } | |
534 | os_sleep(1, 0); | |
535 | goto retry_send; | |
536 | } | |
537 | send_err: | |
6fc6879b JM |
538 | os_free(cmd_buf); |
539 | return -1; | |
540 | } | |
541 | os_free(cmd_buf); | |
542 | ||
543 | for (;;) { | |
1480633f | 544 | tv.tv_sec = 10; |
6fc6879b JM |
545 | tv.tv_usec = 0; |
546 | FD_ZERO(&rfds); | |
547 | FD_SET(ctrl->s, &rfds); | |
548 | res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); | |
70f4f052 JK |
549 | if (res < 0 && errno == EINTR) |
550 | continue; | |
c6a3a110 DS |
551 | if (res < 0) |
552 | return res; | |
6fc6879b JM |
553 | if (FD_ISSET(ctrl->s, &rfds)) { |
554 | res = recv(ctrl->s, reply, *reply_len, 0); | |
555 | if (res < 0) | |
556 | return res; | |
2a549796 BG |
557 | if ((res > 0 && reply[0] == '<') || |
558 | (res > 6 && strncmp(reply, "IFNAME=", 7) == 0)) { | |
6fc6879b JM |
559 | /* This is an unsolicited message from |
560 | * wpa_supplicant, not the reply to the | |
561 | * request. Use msg_cb to report this to the | |
562 | * caller. */ | |
563 | if (msg_cb) { | |
564 | /* Make sure the message is nul | |
565 | * terminated. */ | |
566 | if ((size_t) res == *reply_len) | |
567 | res = (*reply_len) - 1; | |
568 | reply[res] = '\0'; | |
569 | msg_cb(reply, res); | |
570 | } | |
571 | continue; | |
572 | } | |
573 | *reply_len = res; | |
574 | break; | |
575 | } else { | |
576 | return -2; | |
577 | } | |
578 | } | |
579 | return 0; | |
580 | } | |
581 | #endif /* CTRL_IFACE_SOCKET */ | |
582 | ||
583 | ||
584 | static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) | |
585 | { | |
586 | char buf[10]; | |
587 | int ret; | |
588 | size_t len = 10; | |
589 | ||
590 | ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, | |
591 | buf, &len, NULL); | |
592 | if (ret < 0) | |
593 | return ret; | |
594 | if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0) | |
595 | return 0; | |
596 | return -1; | |
597 | } | |
598 | ||
599 | ||
600 | int wpa_ctrl_attach(struct wpa_ctrl *ctrl) | |
601 | { | |
602 | return wpa_ctrl_attach_helper(ctrl, 1); | |
603 | } | |
604 | ||
605 | ||
606 | int wpa_ctrl_detach(struct wpa_ctrl *ctrl) | |
607 | { | |
608 | return wpa_ctrl_attach_helper(ctrl, 0); | |
609 | } | |
610 | ||
611 | ||
612 | #ifdef CTRL_IFACE_SOCKET | |
613 | ||
614 | int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) | |
615 | { | |
616 | int res; | |
617 | ||
618 | res = recv(ctrl->s, reply, *reply_len, 0); | |
619 | if (res < 0) | |
620 | return res; | |
621 | *reply_len = res; | |
622 | return 0; | |
623 | } | |
624 | ||
625 | ||
626 | int wpa_ctrl_pending(struct wpa_ctrl *ctrl) | |
627 | { | |
628 | struct timeval tv; | |
629 | fd_set rfds; | |
630 | tv.tv_sec = 0; | |
631 | tv.tv_usec = 0; | |
632 | FD_ZERO(&rfds); | |
633 | FD_SET(ctrl->s, &rfds); | |
634 | select(ctrl->s + 1, &rfds, NULL, NULL, &tv); | |
635 | return FD_ISSET(ctrl->s, &rfds); | |
636 | } | |
637 | ||
638 | ||
639 | int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) | |
640 | { | |
641 | return ctrl->s; | |
642 | } | |
643 | ||
644 | #endif /* CTRL_IFACE_SOCKET */ | |
645 | ||
646 | ||
647 | #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE | |
648 | ||
649 | #ifndef WPA_SUPPLICANT_NAMED_PIPE | |
650 | #define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant" | |
651 | #endif | |
652 | #define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE) | |
653 | ||
654 | struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) | |
655 | { | |
656 | struct wpa_ctrl *ctrl; | |
657 | DWORD mode; | |
658 | TCHAR name[256]; | |
659 | int i, ret; | |
660 | ||
661 | ctrl = os_malloc(sizeof(*ctrl)); | |
662 | if (ctrl == NULL) | |
663 | return NULL; | |
664 | os_memset(ctrl, 0, sizeof(*ctrl)); | |
665 | ||
666 | #ifdef UNICODE | |
667 | if (ctrl_path == NULL) | |
668 | ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX); | |
669 | else | |
670 | ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), | |
671 | ctrl_path); | |
672 | #else /* UNICODE */ | |
673 | if (ctrl_path == NULL) | |
674 | ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX); | |
675 | else | |
676 | ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", | |
677 | ctrl_path); | |
678 | #endif /* UNICODE */ | |
1f102d3b | 679 | if (os_snprintf_error(256, ret)) { |
6fc6879b JM |
680 | os_free(ctrl); |
681 | return NULL; | |
682 | } | |
683 | ||
684 | for (i = 0; i < 10; i++) { | |
685 | ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, | |
686 | NULL, OPEN_EXISTING, 0, NULL); | |
687 | /* | |
688 | * Current named pipe server side in wpa_supplicant is | |
689 | * re-opening the pipe for new clients only after the previous | |
690 | * one is taken into use. This leaves a small window for race | |
691 | * conditions when two connections are being opened at almost | |
692 | * the same time. Retry if that was the case. | |
693 | */ | |
694 | if (ctrl->pipe != INVALID_HANDLE_VALUE || | |
695 | GetLastError() != ERROR_PIPE_BUSY) | |
696 | break; | |
697 | WaitNamedPipe(name, 1000); | |
698 | } | |
699 | if (ctrl->pipe == INVALID_HANDLE_VALUE) { | |
700 | os_free(ctrl); | |
701 | return NULL; | |
702 | } | |
703 | ||
704 | mode = PIPE_READMODE_MESSAGE; | |
705 | if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) { | |
706 | CloseHandle(ctrl->pipe); | |
707 | os_free(ctrl); | |
708 | return NULL; | |
709 | } | |
710 | ||
711 | return ctrl; | |
712 | } | |
713 | ||
714 | ||
715 | void wpa_ctrl_close(struct wpa_ctrl *ctrl) | |
716 | { | |
717 | CloseHandle(ctrl->pipe); | |
718 | os_free(ctrl); | |
719 | } | |
720 | ||
721 | ||
722 | int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, | |
723 | char *reply, size_t *reply_len, | |
724 | void (*msg_cb)(char *msg, size_t len)) | |
725 | { | |
726 | DWORD written; | |
727 | DWORD readlen = *reply_len; | |
728 | ||
729 | if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL)) | |
730 | return -1; | |
731 | ||
732 | if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL)) | |
733 | return -1; | |
734 | *reply_len = readlen; | |
735 | ||
736 | return 0; | |
737 | } | |
738 | ||
739 | ||
740 | int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) | |
741 | { | |
742 | DWORD len = *reply_len; | |
743 | if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL)) | |
744 | return -1; | |
745 | *reply_len = len; | |
746 | return 0; | |
747 | } | |
748 | ||
749 | ||
750 | int wpa_ctrl_pending(struct wpa_ctrl *ctrl) | |
751 | { | |
752 | DWORD left; | |
753 | ||
754 | if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL)) | |
755 | return -1; | |
756 | return left ? 1 : 0; | |
757 | } | |
758 | ||
759 | ||
760 | int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) | |
761 | { | |
762 | return -1; | |
763 | } | |
764 | ||
765 | #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ | |
766 | ||
767 | #endif /* CONFIG_CTRL_IFACE */ |