]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * WPA Supplicant / Windows Named Pipe -based control interface | |
3 | * Copyright (c) 2004-2006, 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 | #include "common.h" | |
12 | #include "eloop.h" | |
13 | #include "config.h" | |
14 | #include "eapol_supp/eapol_supp_sm.h" | |
15 | #include "wpa_supplicant_i.h" | |
16 | #include "ctrl_iface.h" | |
90973fb2 | 17 | #include "common/wpa_ctrl.h" |
6fc6879b JM |
18 | |
19 | #ifdef __MINGW32_VERSION | |
20 | /* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here | |
21 | */ | |
22 | #define SDDL_REVISION_1 1 | |
23 | BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA( | |
24 | LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG); | |
25 | BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW( | |
26 | LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG); | |
27 | #ifdef UNICODE | |
28 | #define ConvertStringSecurityDescriptorToSecurityDescriptor \ | |
29 | ConvertStringSecurityDescriptorToSecurityDescriptorW | |
30 | #else | |
31 | #define ConvertStringSecurityDescriptorToSecurityDescriptor \ | |
32 | ConvertStringSecurityDescriptorToSecurityDescriptorA | |
33 | #endif | |
34 | #else /* __MINGW32_VERSION */ | |
35 | #ifndef _WIN32_WINNT | |
36 | #define _WIN32_WINNT 0x0500 | |
37 | #endif | |
38 | #include <sddl.h> | |
39 | #endif /* __MINGW32_VERSION */ | |
40 | ||
41 | #ifndef WPA_SUPPLICANT_NAMED_PIPE | |
42 | #define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant" | |
43 | #endif | |
44 | #define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE) | |
45 | ||
46 | /* Per-interface ctrl_iface */ | |
47 | ||
48 | #define REQUEST_BUFSIZE 256 | |
49 | #define REPLY_BUFSIZE 4096 | |
50 | ||
51 | struct ctrl_iface_priv; | |
52 | ||
53 | /** | |
54 | * struct wpa_ctrl_dst - Internal data structure of control interface clients | |
55 | * | |
56 | * This structure is used to store information about registered control | |
57 | * interface monitors into struct wpa_supplicant. This data is private to | |
58 | * ctrl_iface_named_pipe.c and should not be touched directly from other files. | |
59 | */ | |
60 | struct wpa_ctrl_dst { | |
61 | /* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */ | |
62 | OVERLAPPED overlap; | |
63 | struct wpa_ctrl_dst *next, *prev; | |
64 | struct ctrl_iface_priv *priv; | |
65 | HANDLE pipe; | |
66 | int attached; | |
67 | int debug_level; | |
68 | int errors; | |
69 | char req_buf[REQUEST_BUFSIZE]; | |
70 | char *rsp_buf; | |
71 | int used; | |
72 | }; | |
73 | ||
74 | ||
75 | struct ctrl_iface_priv { | |
76 | struct wpa_supplicant *wpa_s; | |
77 | struct wpa_ctrl_dst *ctrl_dst; | |
78 | SECURITY_ATTRIBUTES attr; | |
79 | int sec_attr_set; | |
80 | }; | |
81 | ||
82 | ||
83 | static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, | |
84 | int level, const char *buf, | |
85 | size_t len); | |
86 | ||
87 | static void ctrl_close_pipe(struct wpa_ctrl_dst *dst); | |
88 | static void wpa_supplicant_ctrl_iface_receive(void *, void *); | |
89 | static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes, | |
90 | LPOVERLAPPED overlap); | |
91 | ||
92 | struct wpa_global_dst; | |
93 | static void global_close_pipe(struct wpa_global_dst *dst); | |
94 | static void wpa_supplicant_global_iface_receive(void *eloop_data, | |
95 | void *user_ctx); | |
96 | static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes, | |
97 | LPOVERLAPPED overlap); | |
98 | ||
99 | ||
100 | static int ctrl_broken_pipe(HANDLE pipe, int used) | |
101 | { | |
102 | DWORD err; | |
103 | ||
104 | if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL)) | |
105 | return 0; | |
106 | ||
107 | err = GetLastError(); | |
108 | if (err == ERROR_BROKEN_PIPE || (err == ERROR_BAD_PIPE && used)) | |
109 | return 1; | |
110 | return 0; | |
111 | } | |
112 | ||
113 | ||
114 | static void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv) | |
115 | { | |
116 | struct wpa_ctrl_dst *dst, *next; | |
117 | ||
118 | dst = priv->ctrl_dst; | |
119 | ||
120 | while (dst) { | |
121 | next = dst->next; | |
122 | if (ctrl_broken_pipe(dst->pipe, dst->used)) { | |
123 | wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p", | |
124 | dst); | |
125 | ctrl_close_pipe(dst); | |
126 | } | |
127 | dst = next; | |
128 | } | |
129 | } | |
130 | ||
131 | ||
132 | static int ctrl_open_pipe(struct ctrl_iface_priv *priv) | |
133 | { | |
134 | struct wpa_ctrl_dst *dst; | |
135 | DWORD err; | |
136 | TCHAR name[256]; | |
137 | ||
138 | dst = os_zalloc(sizeof(*dst)); | |
139 | if (dst == NULL) | |
140 | return -1; | |
141 | wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst); | |
142 | ||
143 | dst->priv = priv; | |
144 | dst->debug_level = MSG_INFO; | |
145 | dst->pipe = INVALID_HANDLE_VALUE; | |
146 | ||
147 | dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); | |
148 | if (dst->overlap.hEvent == NULL) { | |
149 | wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d", | |
150 | (int) GetLastError()); | |
151 | goto fail; | |
152 | } | |
153 | ||
154 | eloop_register_event(dst->overlap.hEvent, | |
155 | sizeof(dst->overlap.hEvent), | |
156 | wpa_supplicant_ctrl_iface_receive, dst, NULL); | |
157 | ||
158 | #ifdef UNICODE | |
159 | _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), | |
160 | priv->wpa_s->ifname); | |
161 | #else /* UNICODE */ | |
162 | os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", | |
163 | priv->wpa_s->ifname); | |
164 | #endif /* UNICODE */ | |
165 | ||
166 | /* TODO: add support for configuring access list for the pipe */ | |
167 | dst->pipe = CreateNamedPipe(name, | |
168 | PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, | |
169 | PIPE_TYPE_MESSAGE | | |
170 | PIPE_READMODE_MESSAGE | | |
171 | PIPE_WAIT, | |
172 | 15, REPLY_BUFSIZE, REQUEST_BUFSIZE, | |
173 | 1000, | |
174 | priv->sec_attr_set ? &priv->attr : NULL); | |
175 | if (dst->pipe == INVALID_HANDLE_VALUE) { | |
176 | wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d", | |
177 | (int) GetLastError()); | |
178 | goto fail; | |
179 | } | |
180 | ||
181 | if (ConnectNamedPipe(dst->pipe, &dst->overlap)) { | |
182 | wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d", | |
183 | (int) GetLastError()); | |
184 | CloseHandle(dst->pipe); | |
185 | os_free(dst); | |
186 | return -1; | |
187 | } | |
188 | ||
189 | err = GetLastError(); | |
190 | switch (err) { | |
191 | case ERROR_IO_PENDING: | |
192 | wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in " | |
193 | "progress"); | |
194 | break; | |
195 | case ERROR_PIPE_CONNECTED: | |
196 | wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already " | |
197 | "connected"); | |
198 | if (SetEvent(dst->overlap.hEvent)) | |
199 | break; | |
200 | /* fall through */ | |
201 | default: | |
202 | wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d", | |
203 | (int) err); | |
204 | CloseHandle(dst->pipe); | |
205 | os_free(dst); | |
206 | return -1; | |
207 | } | |
208 | ||
209 | dst->next = priv->ctrl_dst; | |
210 | if (dst->next) | |
211 | dst->next->prev = dst; | |
212 | priv->ctrl_dst = dst; | |
213 | ||
214 | return 0; | |
215 | ||
216 | fail: | |
217 | ctrl_close_pipe(dst); | |
218 | return -1; | |
219 | } | |
220 | ||
221 | ||
222 | static void ctrl_close_pipe(struct wpa_ctrl_dst *dst) | |
223 | { | |
224 | wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst); | |
225 | ||
226 | if (dst->overlap.hEvent) { | |
227 | eloop_unregister_event(dst->overlap.hEvent, | |
228 | sizeof(dst->overlap.hEvent)); | |
229 | CloseHandle(dst->overlap.hEvent); | |
230 | } | |
231 | ||
232 | if (dst->pipe != INVALID_HANDLE_VALUE) { | |
233 | /* | |
234 | * Could use FlushFileBuffers() here to guarantee that all data | |
235 | * gets delivered to the client, but that can block, so let's | |
236 | * not do this for now. | |
237 | * FlushFileBuffers(dst->pipe); | |
238 | */ | |
239 | CloseHandle(dst->pipe); | |
240 | } | |
241 | ||
242 | if (dst->prev) | |
243 | dst->prev->next = dst->next; | |
244 | else | |
245 | dst->priv->ctrl_dst = dst->next; | |
246 | if (dst->next) | |
247 | dst->next->prev = dst->prev; | |
248 | ||
249 | os_free(dst->rsp_buf); | |
250 | os_free(dst); | |
251 | } | |
252 | ||
253 | ||
254 | static VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes, | |
255 | LPOVERLAPPED overlap) | |
256 | { | |
257 | struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap; | |
258 | wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p " | |
259 | "err=%d bytes=%d", dst, (int) err, (int) bytes); | |
260 | if (err) { | |
261 | ctrl_close_pipe(dst); | |
262 | return; | |
263 | } | |
264 | ||
265 | os_free(dst->rsp_buf); | |
266 | dst->rsp_buf = NULL; | |
267 | ||
268 | if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf), | |
269 | &dst->overlap, ctrl_iface_read_completed)) { | |
270 | wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d", | |
271 | (int) GetLastError()); | |
272 | ctrl_close_pipe(dst); | |
273 | return; | |
274 | } | |
275 | wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst); | |
276 | } | |
277 | ||
278 | ||
279 | static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len) | |
280 | { | |
281 | struct wpa_supplicant *wpa_s = dst->priv->wpa_s; | |
282 | char *reply = NULL, *send_buf; | |
283 | size_t reply_len = 0, send_len; | |
284 | int new_attached = 0; | |
285 | char *buf = dst->req_buf; | |
286 | ||
287 | dst->used = 1; | |
288 | if (len >= REQUEST_BUFSIZE) | |
289 | len = REQUEST_BUFSIZE - 1; | |
290 | buf[len] = '\0'; | |
291 | ||
292 | if (os_strcmp(buf, "ATTACH") == 0) { | |
293 | dst->attached = 1; | |
294 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached"); | |
295 | new_attached = 1; | |
296 | reply_len = 2; | |
297 | } else if (os_strcmp(buf, "DETACH") == 0) { | |
298 | dst->attached = 0; | |
299 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached"); | |
300 | reply_len = 2; | |
301 | } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { | |
302 | wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6); | |
303 | dst->debug_level = atoi(buf + 6); | |
304 | reply_len = 2; | |
305 | } else { | |
306 | reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, | |
307 | &reply_len); | |
308 | } | |
309 | ||
310 | if (reply) { | |
311 | send_buf = reply; | |
312 | send_len = reply_len; | |
313 | } else if (reply_len == 2) { | |
314 | send_buf = "OK\n"; | |
315 | send_len = 3; | |
316 | } else { | |
317 | send_buf = "FAIL\n"; | |
318 | send_len = 5; | |
319 | } | |
320 | ||
321 | os_free(dst->rsp_buf); | |
a1f11e34 | 322 | dst->rsp_buf = os_memdup(send_buf, send_len); |
6fc6879b JM |
323 | if (dst->rsp_buf == NULL) { |
324 | ctrl_close_pipe(dst); | |
325 | os_free(reply); | |
326 | return; | |
327 | } | |
6fc6879b JM |
328 | os_free(reply); |
329 | ||
330 | if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, | |
331 | ctrl_iface_write_completed)) { | |
332 | wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d", | |
333 | (int) GetLastError()); | |
334 | ctrl_close_pipe(dst); | |
335 | } else { | |
336 | wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", | |
337 | dst); | |
338 | } | |
339 | ||
340 | if (new_attached) | |
341 | eapol_sm_notify_ctrl_attached(wpa_s->eapol); | |
342 | } | |
343 | ||
344 | ||
345 | static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes, | |
346 | LPOVERLAPPED overlap) | |
347 | { | |
348 | struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap; | |
349 | wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d " | |
350 | "bytes=%d", dst, (int) err, (int) bytes); | |
351 | if (err == 0 && bytes > 0) | |
352 | wpa_supplicant_ctrl_iface_rx(dst, bytes); | |
353 | } | |
354 | ||
355 | ||
356 | static void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx) | |
357 | { | |
358 | struct wpa_ctrl_dst *dst = eloop_data; | |
359 | struct ctrl_iface_priv *priv = dst->priv; | |
360 | DWORD bytes; | |
361 | ||
362 | wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive"); | |
363 | ResetEvent(dst->overlap.hEvent); | |
364 | ||
365 | if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) { | |
366 | wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d", | |
367 | (int) GetLastError()); | |
368 | return; | |
369 | } | |
370 | wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client " | |
371 | "connected"); | |
372 | ||
373 | /* Open a new named pipe for the next client. */ | |
374 | ctrl_open_pipe(priv); | |
375 | ||
376 | /* Use write completion function to start reading a command */ | |
377 | ctrl_iface_write_completed(0, 0, &dst->overlap); | |
378 | ||
379 | ctrl_flush_broken_pipes(priv); | |
380 | } | |
381 | ||
382 | ||
383 | static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params) | |
384 | { | |
385 | const char *sddl = NULL; | |
386 | TCHAR *t_sddl; | |
387 | ||
388 | if (os_strncmp(params, "SDDL=", 5) == 0) | |
389 | sddl = params + 5; | |
390 | if (!sddl) { | |
391 | sddl = os_strstr(params, " SDDL="); | |
392 | if (sddl) | |
393 | sddl += 6; | |
394 | } | |
395 | ||
396 | if (!sddl) | |
397 | return 0; | |
398 | ||
399 | wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl); | |
400 | os_memset(&priv->attr, 0, sizeof(priv->attr)); | |
401 | priv->attr.nLength = sizeof(priv->attr); | |
402 | priv->attr.bInheritHandle = FALSE; | |
403 | t_sddl = wpa_strdup_tchar(sddl); | |
404 | if (t_sddl == NULL) | |
405 | return -1; | |
406 | if (!ConvertStringSecurityDescriptorToSecurityDescriptor( | |
407 | t_sddl, SDDL_REVISION_1, | |
9e72e1d3 JM |
408 | (PSECURITY_DESCRIPTOR *) (void *) |
409 | &priv->attr.lpSecurityDescriptor, | |
6fc6879b JM |
410 | NULL)) { |
411 | os_free(t_sddl); | |
412 | wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to " | |
413 | "security descriptor: %d", | |
414 | sddl, (int) GetLastError()); | |
415 | return -1; | |
416 | } | |
417 | os_free(t_sddl); | |
418 | ||
419 | priv->sec_attr_set = 1; | |
420 | ||
421 | return 0; | |
422 | } | |
423 | ||
424 | ||
995a3a06 JM |
425 | static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, |
426 | enum wpa_msg_type type, | |
6fc6879b JM |
427 | const char *txt, size_t len) |
428 | { | |
429 | struct wpa_supplicant *wpa_s = ctx; | |
430 | if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) | |
431 | return; | |
432 | wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); | |
433 | } | |
434 | ||
435 | ||
436 | struct ctrl_iface_priv * | |
437 | wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) | |
438 | { | |
439 | struct ctrl_iface_priv *priv; | |
440 | ||
441 | priv = os_zalloc(sizeof(*priv)); | |
442 | if (priv == NULL) | |
443 | return NULL; | |
444 | priv->wpa_s = wpa_s; | |
445 | ||
446 | if (wpa_s->conf->ctrl_interface == NULL) | |
447 | return priv; | |
448 | ||
449 | if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) { | |
450 | os_free(priv); | |
451 | return NULL; | |
452 | } | |
453 | ||
454 | if (ctrl_open_pipe(priv) < 0) { | |
455 | os_free(priv); | |
456 | return NULL; | |
457 | } | |
458 | ||
459 | wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); | |
460 | ||
461 | return priv; | |
462 | } | |
463 | ||
464 | ||
465 | void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) | |
466 | { | |
467 | while (priv->ctrl_dst) | |
468 | ctrl_close_pipe(priv->ctrl_dst); | |
469 | if (priv->sec_attr_set) | |
470 | LocalFree(priv->attr.lpSecurityDescriptor); | |
471 | os_free(priv); | |
472 | } | |
473 | ||
474 | ||
475 | static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, | |
476 | int level, const char *buf, | |
477 | size_t len) | |
478 | { | |
479 | struct wpa_ctrl_dst *dst, *next; | |
480 | char levelstr[10]; | |
481 | int idx; | |
482 | char *sbuf; | |
483 | int llen; | |
484 | DWORD written; | |
485 | ||
486 | dst = priv->ctrl_dst; | |
487 | if (dst == NULL) | |
488 | return; | |
489 | ||
490 | os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); | |
491 | ||
492 | llen = os_strlen(levelstr); | |
493 | sbuf = os_malloc(llen + len); | |
494 | if (sbuf == NULL) | |
495 | return; | |
496 | ||
497 | os_memcpy(sbuf, levelstr, llen); | |
498 | os_memcpy(sbuf + llen, buf, len); | |
499 | ||
500 | idx = 0; | |
501 | while (dst) { | |
502 | next = dst->next; | |
503 | if (dst->attached && level >= dst->debug_level) { | |
504 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p", | |
505 | dst); | |
506 | if (!WriteFile(dst->pipe, sbuf, llen + len, &written, | |
507 | NULL)) { | |
508 | wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst " | |
509 | "%p failed: %d", | |
510 | dst, (int) GetLastError()); | |
511 | dst->errors++; | |
512 | if (dst->errors > 10) | |
513 | ctrl_close_pipe(dst); | |
514 | } else | |
515 | dst->errors = 0; | |
516 | } | |
517 | idx++; | |
518 | dst = next; | |
519 | } | |
520 | os_free(sbuf); | |
521 | } | |
522 | ||
523 | ||
524 | void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) | |
525 | { | |
526 | wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", | |
527 | priv->wpa_s->ifname); | |
528 | if (priv->ctrl_dst == NULL) | |
529 | return; | |
530 | WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE); | |
531 | } | |
532 | ||
533 | ||
534 | /* Global ctrl_iface */ | |
535 | ||
536 | struct ctrl_iface_global_priv; | |
537 | ||
538 | struct wpa_global_dst { | |
539 | /* Note: OVERLAPPED must be the first member of struct wpa_global_dst | |
540 | */ | |
541 | OVERLAPPED overlap; | |
542 | struct wpa_global_dst *next, *prev; | |
543 | struct ctrl_iface_global_priv *priv; | |
544 | HANDLE pipe; | |
545 | char req_buf[REQUEST_BUFSIZE]; | |
546 | char *rsp_buf; | |
547 | int used; | |
548 | }; | |
549 | ||
550 | struct ctrl_iface_global_priv { | |
551 | struct wpa_global *global; | |
552 | struct wpa_global_dst *ctrl_dst; | |
553 | }; | |
554 | ||
555 | ||
556 | static void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv) | |
557 | { | |
558 | struct wpa_global_dst *dst, *next; | |
559 | ||
560 | dst = priv->ctrl_dst; | |
561 | ||
562 | while (dst) { | |
563 | next = dst->next; | |
564 | if (ctrl_broken_pipe(dst->pipe, dst->used)) { | |
565 | wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p", | |
566 | dst); | |
567 | global_close_pipe(dst); | |
568 | } | |
569 | dst = next; | |
570 | } | |
571 | } | |
572 | ||
573 | ||
574 | static int global_open_pipe(struct ctrl_iface_global_priv *priv) | |
575 | { | |
576 | struct wpa_global_dst *dst; | |
577 | DWORD err; | |
578 | ||
579 | dst = os_zalloc(sizeof(*dst)); | |
580 | if (dst == NULL) | |
581 | return -1; | |
582 | wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst); | |
583 | ||
584 | dst->priv = priv; | |
585 | dst->pipe = INVALID_HANDLE_VALUE; | |
586 | ||
587 | dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); | |
588 | if (dst->overlap.hEvent == NULL) { | |
589 | wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d", | |
590 | (int) GetLastError()); | |
591 | goto fail; | |
592 | } | |
593 | ||
594 | eloop_register_event(dst->overlap.hEvent, | |
595 | sizeof(dst->overlap.hEvent), | |
596 | wpa_supplicant_global_iface_receive, dst, NULL); | |
597 | ||
598 | /* TODO: add support for configuring access list for the pipe */ | |
599 | dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX, | |
600 | PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, | |
601 | PIPE_TYPE_MESSAGE | | |
602 | PIPE_READMODE_MESSAGE | | |
603 | PIPE_WAIT, | |
604 | 10, REPLY_BUFSIZE, REQUEST_BUFSIZE, | |
605 | 1000, NULL); | |
606 | if (dst->pipe == INVALID_HANDLE_VALUE) { | |
607 | wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d", | |
608 | (int) GetLastError()); | |
609 | goto fail; | |
610 | } | |
611 | ||
612 | if (ConnectNamedPipe(dst->pipe, &dst->overlap)) { | |
613 | wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d", | |
614 | (int) GetLastError()); | |
615 | CloseHandle(dst->pipe); | |
616 | os_free(dst); | |
617 | return -1; | |
618 | } | |
619 | ||
620 | err = GetLastError(); | |
621 | switch (err) { | |
622 | case ERROR_IO_PENDING: | |
623 | wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in " | |
624 | "progress"); | |
625 | break; | |
626 | case ERROR_PIPE_CONNECTED: | |
627 | wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already " | |
628 | "connected"); | |
629 | if (SetEvent(dst->overlap.hEvent)) | |
630 | break; | |
631 | /* fall through */ | |
632 | default: | |
633 | wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d", | |
634 | (int) err); | |
635 | CloseHandle(dst->pipe); | |
636 | os_free(dst); | |
637 | return -1; | |
638 | } | |
639 | ||
640 | dst->next = priv->ctrl_dst; | |
641 | if (dst->next) | |
642 | dst->next->prev = dst; | |
643 | priv->ctrl_dst = dst; | |
644 | ||
645 | return 0; | |
646 | ||
647 | fail: | |
648 | global_close_pipe(dst); | |
649 | return -1; | |
650 | } | |
651 | ||
652 | ||
653 | static void global_close_pipe(struct wpa_global_dst *dst) | |
654 | { | |
655 | wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst); | |
656 | ||
657 | if (dst->overlap.hEvent) { | |
658 | eloop_unregister_event(dst->overlap.hEvent, | |
659 | sizeof(dst->overlap.hEvent)); | |
660 | CloseHandle(dst->overlap.hEvent); | |
661 | } | |
662 | ||
663 | if (dst->pipe != INVALID_HANDLE_VALUE) { | |
664 | /* | |
665 | * Could use FlushFileBuffers() here to guarantee that all data | |
666 | * gets delivered to the client, but that can block, so let's | |
667 | * not do this for now. | |
668 | * FlushFileBuffers(dst->pipe); | |
669 | */ | |
670 | CloseHandle(dst->pipe); | |
671 | } | |
672 | ||
673 | if (dst->prev) | |
674 | dst->prev->next = dst->next; | |
675 | else | |
676 | dst->priv->ctrl_dst = dst->next; | |
677 | if (dst->next) | |
678 | dst->next->prev = dst->prev; | |
679 | ||
680 | os_free(dst->rsp_buf); | |
681 | os_free(dst); | |
682 | } | |
683 | ||
684 | ||
685 | static VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes, | |
686 | LPOVERLAPPED overlap) | |
687 | { | |
688 | struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap; | |
689 | wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p " | |
690 | "err=%d bytes=%d", dst, (int) err, (int) bytes); | |
691 | if (err) { | |
692 | global_close_pipe(dst); | |
693 | return; | |
694 | } | |
695 | ||
696 | os_free(dst->rsp_buf); | |
697 | dst->rsp_buf = NULL; | |
698 | ||
699 | if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf), | |
700 | &dst->overlap, global_iface_read_completed)) { | |
701 | wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d", | |
702 | (int) GetLastError()); | |
703 | global_close_pipe(dst); | |
704 | /* FIX: if this was the pipe waiting for new global | |
705 | * connections, at this point there are no open global pipes.. | |
706 | * Should try to open a new pipe.. */ | |
707 | return; | |
708 | } | |
709 | wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst); | |
710 | } | |
711 | ||
712 | ||
713 | static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst, | |
714 | size_t len) | |
715 | { | |
716 | struct wpa_global *global = dst->priv->global; | |
717 | char *reply = NULL, *send_buf; | |
718 | size_t reply_len = 0, send_len; | |
719 | char *buf = dst->req_buf; | |
720 | ||
721 | dst->used = 1; | |
722 | if (len >= REQUEST_BUFSIZE) | |
723 | len = REQUEST_BUFSIZE - 1; | |
724 | buf[len] = '\0'; | |
725 | ||
726 | reply = wpa_supplicant_global_ctrl_iface_process(global, buf, | |
727 | &reply_len); | |
728 | if (reply) { | |
729 | send_buf = reply; | |
730 | send_len = reply_len; | |
731 | } else if (reply_len) { | |
732 | send_buf = "FAIL\n"; | |
733 | send_len = 5; | |
734 | } else { | |
735 | os_free(dst->rsp_buf); | |
736 | dst->rsp_buf = NULL; | |
737 | return; | |
738 | } | |
739 | ||
740 | os_free(dst->rsp_buf); | |
a1f11e34 | 741 | dst->rsp_buf = os_memdup(send_buf, send_len); |
6fc6879b JM |
742 | if (dst->rsp_buf == NULL) { |
743 | global_close_pipe(dst); | |
744 | os_free(reply); | |
745 | return; | |
746 | } | |
6fc6879b JM |
747 | os_free(reply); |
748 | ||
749 | if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, | |
750 | global_iface_write_completed)) { | |
751 | wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d", | |
752 | (int) GetLastError()); | |
753 | global_close_pipe(dst); | |
754 | } else { | |
755 | wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p", | |
756 | dst); | |
757 | } | |
758 | } | |
759 | ||
760 | ||
761 | static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes, | |
762 | LPOVERLAPPED overlap) | |
763 | { | |
764 | struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap; | |
765 | wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d " | |
766 | "bytes=%d", dst, (int) err, (int) bytes); | |
767 | if (err == 0 && bytes > 0) | |
768 | wpa_supplicant_global_iface_rx(dst, bytes); | |
769 | } | |
770 | ||
771 | ||
772 | static void wpa_supplicant_global_iface_receive(void *eloop_data, | |
773 | void *user_ctx) | |
774 | { | |
775 | struct wpa_global_dst *dst = eloop_data; | |
776 | struct ctrl_iface_global_priv *priv = dst->priv; | |
777 | DWORD bytes; | |
778 | ||
779 | wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive"); | |
780 | ResetEvent(dst->overlap.hEvent); | |
781 | ||
782 | if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) { | |
783 | wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d", | |
784 | (int) GetLastError()); | |
785 | return; | |
786 | } | |
787 | wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client " | |
788 | "connected"); | |
789 | ||
790 | /* Open a new named pipe for the next client. */ | |
791 | if (global_open_pipe(priv) < 0) { | |
792 | wpa_printf(MSG_DEBUG, "CTRL: global_open_pipe failed"); | |
793 | return; | |
794 | } | |
795 | ||
796 | /* Use write completion function to start reading a command */ | |
797 | global_iface_write_completed(0, 0, &dst->overlap); | |
798 | ||
799 | global_flush_broken_pipes(priv); | |
800 | } | |
801 | ||
802 | ||
803 | struct ctrl_iface_global_priv * | |
804 | wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) | |
805 | { | |
806 | struct ctrl_iface_global_priv *priv; | |
807 | ||
808 | priv = os_zalloc(sizeof(*priv)); | |
809 | if (priv == NULL) | |
810 | return NULL; | |
811 | priv->global = global; | |
812 | ||
813 | if (global_open_pipe(priv) < 0) { | |
814 | os_free(priv); | |
815 | return NULL; | |
816 | } | |
817 | ||
818 | return priv; | |
819 | } | |
820 | ||
821 | ||
822 | void | |
823 | wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) | |
824 | { | |
825 | while (priv->ctrl_dst) | |
826 | global_close_pipe(priv->ctrl_dst); | |
827 | os_free(priv); | |
828 | } |