wpa_ctrl.c revision 189251
1189251Ssam/*
2189251Ssam * wpa_supplicant/hostapd control interface library
3189251Ssam * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
4189251Ssam *
5189251Ssam * This program is free software; you can redistribute it and/or modify
6189251Ssam * it under the terms of the GNU General Public License version 2 as
7189251Ssam * published by the Free Software Foundation.
8189251Ssam *
9189251Ssam * Alternatively, this software may be distributed under the terms of BSD
10189251Ssam * license.
11189251Ssam *
12189251Ssam * See README and COPYING for more details.
13189251Ssam */
14189251Ssam
15189251Ssam#include "includes.h"
16189251Ssam
17189251Ssam#ifdef CONFIG_CTRL_IFACE
18189251Ssam
19189251Ssam#ifdef CONFIG_CTRL_IFACE_UNIX
20189251Ssam#include <sys/un.h>
21189251Ssam#endif /* CONFIG_CTRL_IFACE_UNIX */
22189251Ssam
23189251Ssam#include "wpa_ctrl.h"
24189251Ssam#include "common.h"
25189251Ssam
26189251Ssam
27189251Ssam#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
28189251Ssam#define CTRL_IFACE_SOCKET
29189251Ssam#endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */
30189251Ssam
31189251Ssam
32189251Ssam/**
33189251Ssam * struct wpa_ctrl - Internal structure for control interface library
34189251Ssam *
35189251Ssam * This structure is used by the wpa_supplicant/hostapd control interface
36189251Ssam * library to store internal data. Programs using the library should not touch
37189251Ssam * this data directly. They can only use the pointer to the data structure as
38189251Ssam * an identifier for the control interface connection and use this as one of
39189251Ssam * the arguments for most of the control interface library functions.
40189251Ssam */
41189251Ssamstruct wpa_ctrl {
42189251Ssam#ifdef CONFIG_CTRL_IFACE_UDP
43189251Ssam	int s;
44189251Ssam	struct sockaddr_in local;
45189251Ssam	struct sockaddr_in dest;
46189251Ssam	char *cookie;
47189251Ssam#endif /* CONFIG_CTRL_IFACE_UDP */
48189251Ssam#ifdef CONFIG_CTRL_IFACE_UNIX
49189251Ssam	int s;
50189251Ssam	struct sockaddr_un local;
51189251Ssam	struct sockaddr_un dest;
52189251Ssam#endif /* CONFIG_CTRL_IFACE_UNIX */
53189251Ssam#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
54189251Ssam	HANDLE pipe;
55189251Ssam#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
56189251Ssam};
57189251Ssam
58189251Ssam
59189251Ssam#ifdef CONFIG_CTRL_IFACE_UNIX
60189251Ssam
61189251Ssamstruct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
62189251Ssam{
63189251Ssam	struct wpa_ctrl *ctrl;
64189251Ssam	static int counter = 0;
65189251Ssam	int ret;
66189251Ssam	size_t res;
67189251Ssam	int tries = 0;
68189251Ssam
69189251Ssam	ctrl = os_malloc(sizeof(*ctrl));
70189251Ssam	if (ctrl == NULL)
71189251Ssam		return NULL;
72189251Ssam	os_memset(ctrl, 0, sizeof(*ctrl));
73189251Ssam
74189251Ssam	ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
75189251Ssam	if (ctrl->s < 0) {
76189251Ssam		os_free(ctrl);
77189251Ssam		return NULL;
78189251Ssam	}
79189251Ssam
80189251Ssam	ctrl->local.sun_family = AF_UNIX;
81189251Ssam	counter++;
82189251Ssamtry_again:
83189251Ssam	ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
84189251Ssam			  "/tmp/wpa_ctrl_%d-%d", getpid(), counter);
85189251Ssam	if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
86189251Ssam		close(ctrl->s);
87189251Ssam		os_free(ctrl);
88189251Ssam		return NULL;
89189251Ssam	}
90189251Ssam	tries++;
91189251Ssam	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
92189251Ssam		    sizeof(ctrl->local)) < 0) {
93189251Ssam		if (errno == EADDRINUSE && tries < 2) {
94189251Ssam			/*
95189251Ssam			 * getpid() returns unique identifier for this instance
96189251Ssam			 * of wpa_ctrl, so the existing socket file must have
97189251Ssam			 * been left by unclean termination of an earlier run.
98189251Ssam			 * Remove the file and try again.
99189251Ssam			 */
100189251Ssam			unlink(ctrl->local.sun_path);
101189251Ssam			goto try_again;
102189251Ssam		}
103189251Ssam		close(ctrl->s);
104189251Ssam		os_free(ctrl);
105189251Ssam		return NULL;
106189251Ssam	}
107189251Ssam
108189251Ssam	ctrl->dest.sun_family = AF_UNIX;
109189251Ssam	res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
110189251Ssam			 sizeof(ctrl->dest.sun_path));
111189251Ssam	if (res >= sizeof(ctrl->dest.sun_path)) {
112189251Ssam		close(ctrl->s);
113189251Ssam		os_free(ctrl);
114189251Ssam		return NULL;
115189251Ssam	}
116189251Ssam	if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
117189251Ssam		    sizeof(ctrl->dest)) < 0) {
118189251Ssam		close(ctrl->s);
119189251Ssam		unlink(ctrl->local.sun_path);
120189251Ssam		os_free(ctrl);
121189251Ssam		return NULL;
122189251Ssam	}
123189251Ssam
124189251Ssam	return ctrl;
125189251Ssam}
126189251Ssam
127189251Ssam
128189251Ssamvoid wpa_ctrl_close(struct wpa_ctrl *ctrl)
129189251Ssam{
130189251Ssam	unlink(ctrl->local.sun_path);
131189251Ssam	close(ctrl->s);
132189251Ssam	os_free(ctrl);
133189251Ssam}
134189251Ssam
135189251Ssam#endif /* CONFIG_CTRL_IFACE_UNIX */
136189251Ssam
137189251Ssam
138189251Ssam#ifdef CONFIG_CTRL_IFACE_UDP
139189251Ssam
140189251Ssamstruct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
141189251Ssam{
142189251Ssam	struct wpa_ctrl *ctrl;
143189251Ssam	char buf[128];
144189251Ssam	size_t len;
145189251Ssam
146189251Ssam	ctrl = os_malloc(sizeof(*ctrl));
147189251Ssam	if (ctrl == NULL)
148189251Ssam		return NULL;
149189251Ssam	os_memset(ctrl, 0, sizeof(*ctrl));
150189251Ssam
151189251Ssam	ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
152189251Ssam	if (ctrl->s < 0) {
153189251Ssam		perror("socket");
154189251Ssam		os_free(ctrl);
155189251Ssam		return NULL;
156189251Ssam	}
157189251Ssam
158189251Ssam	ctrl->local.sin_family = AF_INET;
159189251Ssam	ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
160189251Ssam	if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
161189251Ssam		 sizeof(ctrl->local)) < 0) {
162189251Ssam		close(ctrl->s);
163189251Ssam		os_free(ctrl);
164189251Ssam		return NULL;
165189251Ssam	}
166189251Ssam
167189251Ssam	ctrl->dest.sin_family = AF_INET;
168189251Ssam	ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
169189251Ssam	ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT);
170189251Ssam	if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
171189251Ssam		    sizeof(ctrl->dest)) < 0) {
172189251Ssam		perror("connect");
173189251Ssam		close(ctrl->s);
174189251Ssam		os_free(ctrl);
175189251Ssam		return NULL;
176189251Ssam	}
177189251Ssam
178189251Ssam	len = sizeof(buf) - 1;
179189251Ssam	if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) {
180189251Ssam		buf[len] = '\0';
181189251Ssam		ctrl->cookie = os_strdup(buf);
182189251Ssam	}
183189251Ssam
184189251Ssam	return ctrl;
185189251Ssam}
186189251Ssam
187189251Ssam
188189251Ssamvoid wpa_ctrl_close(struct wpa_ctrl *ctrl)
189189251Ssam{
190189251Ssam	close(ctrl->s);
191189251Ssam	os_free(ctrl->cookie);
192189251Ssam	os_free(ctrl);
193189251Ssam}
194189251Ssam
195189251Ssam#endif /* CONFIG_CTRL_IFACE_UDP */
196189251Ssam
197189251Ssam
198189251Ssam#ifdef CTRL_IFACE_SOCKET
199189251Ssamint wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
200189251Ssam		     char *reply, size_t *reply_len,
201189251Ssam		     void (*msg_cb)(char *msg, size_t len))
202189251Ssam{
203189251Ssam	struct timeval tv;
204189251Ssam	int res;
205189251Ssam	fd_set rfds;
206189251Ssam	const char *_cmd;
207189251Ssam	char *cmd_buf = NULL;
208189251Ssam	size_t _cmd_len;
209189251Ssam
210189251Ssam#ifdef CONFIG_CTRL_IFACE_UDP
211189251Ssam	if (ctrl->cookie) {
212189251Ssam		char *pos;
213189251Ssam		_cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
214189251Ssam		cmd_buf = os_malloc(_cmd_len);
215189251Ssam		if (cmd_buf == NULL)
216189251Ssam			return -1;
217189251Ssam		_cmd = cmd_buf;
218189251Ssam		pos = cmd_buf;
219189251Ssam		os_strlcpy(pos, ctrl->cookie, _cmd_len);
220189251Ssam		pos += os_strlen(ctrl->cookie);
221189251Ssam		*pos++ = ' ';
222189251Ssam		os_memcpy(pos, cmd, cmd_len);
223189251Ssam	} else
224189251Ssam#endif /* CONFIG_CTRL_IFACE_UDP */
225189251Ssam	{
226189251Ssam		_cmd = cmd;
227189251Ssam		_cmd_len = cmd_len;
228189251Ssam	}
229189251Ssam
230189251Ssam	if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
231189251Ssam		os_free(cmd_buf);
232189251Ssam		return -1;
233189251Ssam	}
234189251Ssam	os_free(cmd_buf);
235189251Ssam
236189251Ssam	for (;;) {
237189251Ssam		tv.tv_sec = 2;
238189251Ssam		tv.tv_usec = 0;
239189251Ssam		FD_ZERO(&rfds);
240189251Ssam		FD_SET(ctrl->s, &rfds);
241189251Ssam		res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
242189251Ssam		if (FD_ISSET(ctrl->s, &rfds)) {
243189251Ssam			res = recv(ctrl->s, reply, *reply_len, 0);
244189251Ssam			if (res < 0)
245189251Ssam				return res;
246189251Ssam			if (res > 0 && reply[0] == '<') {
247189251Ssam				/* This is an unsolicited message from
248189251Ssam				 * wpa_supplicant, not the reply to the
249189251Ssam				 * request. Use msg_cb to report this to the
250189251Ssam				 * caller. */
251189251Ssam				if (msg_cb) {
252189251Ssam					/* Make sure the message is nul
253189251Ssam					 * terminated. */
254189251Ssam					if ((size_t) res == *reply_len)
255189251Ssam						res = (*reply_len) - 1;
256189251Ssam					reply[res] = '\0';
257189251Ssam					msg_cb(reply, res);
258189251Ssam				}
259189251Ssam				continue;
260189251Ssam			}
261189251Ssam			*reply_len = res;
262189251Ssam			break;
263189251Ssam		} else {
264189251Ssam			return -2;
265189251Ssam		}
266189251Ssam	}
267189251Ssam	return 0;
268189251Ssam}
269189251Ssam#endif /* CTRL_IFACE_SOCKET */
270189251Ssam
271189251Ssam
272189251Ssamstatic int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach)
273189251Ssam{
274189251Ssam	char buf[10];
275189251Ssam	int ret;
276189251Ssam	size_t len = 10;
277189251Ssam
278189251Ssam	ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6,
279189251Ssam			       buf, &len, NULL);
280189251Ssam	if (ret < 0)
281189251Ssam		return ret;
282189251Ssam	if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0)
283189251Ssam		return 0;
284189251Ssam	return -1;
285189251Ssam}
286189251Ssam
287189251Ssam
288189251Ssamint wpa_ctrl_attach(struct wpa_ctrl *ctrl)
289189251Ssam{
290189251Ssam	return wpa_ctrl_attach_helper(ctrl, 1);
291189251Ssam}
292189251Ssam
293189251Ssam
294189251Ssamint wpa_ctrl_detach(struct wpa_ctrl *ctrl)
295189251Ssam{
296189251Ssam	return wpa_ctrl_attach_helper(ctrl, 0);
297189251Ssam}
298189251Ssam
299189251Ssam
300189251Ssam#ifdef CTRL_IFACE_SOCKET
301189251Ssam
302189251Ssamint wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
303189251Ssam{
304189251Ssam	int res;
305189251Ssam
306189251Ssam	res = recv(ctrl->s, reply, *reply_len, 0);
307189251Ssam	if (res < 0)
308189251Ssam		return res;
309189251Ssam	*reply_len = res;
310189251Ssam	return 0;
311189251Ssam}
312189251Ssam
313189251Ssam
314189251Ssamint wpa_ctrl_pending(struct wpa_ctrl *ctrl)
315189251Ssam{
316189251Ssam	struct timeval tv;
317189251Ssam	fd_set rfds;
318189251Ssam	tv.tv_sec = 0;
319189251Ssam	tv.tv_usec = 0;
320189251Ssam	FD_ZERO(&rfds);
321189251Ssam	FD_SET(ctrl->s, &rfds);
322189251Ssam	select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
323189251Ssam	return FD_ISSET(ctrl->s, &rfds);
324189251Ssam}
325189251Ssam
326189251Ssam
327189251Ssamint wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
328189251Ssam{
329189251Ssam	return ctrl->s;
330189251Ssam}
331189251Ssam
332189251Ssam#endif /* CTRL_IFACE_SOCKET */
333189251Ssam
334189251Ssam
335189251Ssam#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
336189251Ssam
337189251Ssam#ifndef WPA_SUPPLICANT_NAMED_PIPE
338189251Ssam#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
339189251Ssam#endif
340189251Ssam#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
341189251Ssam
342189251Ssamstruct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
343189251Ssam{
344189251Ssam	struct wpa_ctrl *ctrl;
345189251Ssam	DWORD mode;
346189251Ssam	TCHAR name[256];
347189251Ssam	int i, ret;
348189251Ssam
349189251Ssam	ctrl = os_malloc(sizeof(*ctrl));
350189251Ssam	if (ctrl == NULL)
351189251Ssam		return NULL;
352189251Ssam	os_memset(ctrl, 0, sizeof(*ctrl));
353189251Ssam
354189251Ssam#ifdef UNICODE
355189251Ssam	if (ctrl_path == NULL)
356189251Ssam		ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX);
357189251Ssam	else
358189251Ssam		ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
359189251Ssam				 ctrl_path);
360189251Ssam#else /* UNICODE */
361189251Ssam	if (ctrl_path == NULL)
362189251Ssam		ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX);
363189251Ssam	else
364189251Ssam		ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
365189251Ssam				  ctrl_path);
366189251Ssam#endif /* UNICODE */
367189251Ssam	if (ret < 0 || ret >= 256) {
368189251Ssam		os_free(ctrl);
369189251Ssam		return NULL;
370189251Ssam	}
371189251Ssam
372189251Ssam	for (i = 0; i < 10; i++) {
373189251Ssam		ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0,
374189251Ssam					NULL, OPEN_EXISTING, 0, NULL);
375189251Ssam		/*
376189251Ssam		 * Current named pipe server side in wpa_supplicant is
377189251Ssam		 * re-opening the pipe for new clients only after the previous
378189251Ssam		 * one is taken into use. This leaves a small window for race
379189251Ssam		 * conditions when two connections are being opened at almost
380189251Ssam		 * the same time. Retry if that was the case.
381189251Ssam		 */
382189251Ssam		if (ctrl->pipe != INVALID_HANDLE_VALUE ||
383189251Ssam		    GetLastError() != ERROR_PIPE_BUSY)
384189251Ssam			break;
385189251Ssam		WaitNamedPipe(name, 1000);
386189251Ssam	}
387189251Ssam	if (ctrl->pipe == INVALID_HANDLE_VALUE) {
388189251Ssam		os_free(ctrl);
389189251Ssam		return NULL;
390189251Ssam	}
391189251Ssam
392189251Ssam	mode = PIPE_READMODE_MESSAGE;
393189251Ssam	if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) {
394189251Ssam		CloseHandle(ctrl->pipe);
395189251Ssam		os_free(ctrl);
396189251Ssam		return NULL;
397189251Ssam	}
398189251Ssam
399189251Ssam	return ctrl;
400189251Ssam}
401189251Ssam
402189251Ssam
403189251Ssamvoid wpa_ctrl_close(struct wpa_ctrl *ctrl)
404189251Ssam{
405189251Ssam	CloseHandle(ctrl->pipe);
406189251Ssam	os_free(ctrl);
407189251Ssam}
408189251Ssam
409189251Ssam
410189251Ssamint wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
411189251Ssam		     char *reply, size_t *reply_len,
412189251Ssam		     void (*msg_cb)(char *msg, size_t len))
413189251Ssam{
414189251Ssam	DWORD written;
415189251Ssam	DWORD readlen = *reply_len;
416189251Ssam
417189251Ssam	if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL))
418189251Ssam		return -1;
419189251Ssam
420189251Ssam	if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL))
421189251Ssam		return -1;
422189251Ssam	*reply_len = readlen;
423189251Ssam
424189251Ssam	return 0;
425189251Ssam}
426189251Ssam
427189251Ssam
428189251Ssamint wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
429189251Ssam{
430189251Ssam	DWORD len = *reply_len;
431189251Ssam	if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL))
432189251Ssam		return -1;
433189251Ssam	*reply_len = len;
434189251Ssam	return 0;
435189251Ssam}
436189251Ssam
437189251Ssam
438189251Ssamint wpa_ctrl_pending(struct wpa_ctrl *ctrl)
439189251Ssam{
440189251Ssam	DWORD left;
441189251Ssam
442189251Ssam	if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL))
443189251Ssam		return -1;
444189251Ssam	return left ? 1 : 0;
445189251Ssam}
446189251Ssam
447189251Ssam
448189251Ssamint wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
449189251Ssam{
450189251Ssam	return -1;
451189251Ssam}
452189251Ssam
453189251Ssam#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
454189251Ssam
455189251Ssam#endif /* CONFIG_CTRL_IFACE */
456