1/*
2 *
3 * Copyright 2010 Haiku Inc. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 *
6 * Many parts
7 *
8 * Copyright 2001 The Aerospace Corporation.
9 * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
10 * All rights reserved.
11 * Distributed under the terms of the 2-clause BSD license.
12 *
13 * Authors:
14 *		Alex Botero-Lowry, alex.boterolowry@gmail.com
15 */
16
17#include <ctype.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <strings.h>
21#include <string.h>
22
23#include <sys/ioctl.h>
24#include <sys/kernel.h>
25#include <sys/sockio.h>
26#include <sys/types.h>
27#include <sys/socket.h>
28
29#include <net/if.h>
30
31#include <net80211/ieee80211_ioctl.h>
32#include <net80211/ieee80211_haiku.h>
33
34
35extern const char* __progname;
36
37
38static const char*
39get_string(const char* val, const char* sep, u_int8_t* buf, int* lenp)
40{
41	int len;
42	int hexstr;
43	u_int8_t* p;
44
45	len = *lenp;
46	p = buf;
47	hexstr = (val[0] == '0' && tolower((u_char) val[1]) == 'x');
48	if (hexstr)
49		val += 2;
50	for (;;) {
51		if (*val == '\0')
52			break;
53		if (sep != NULL && strchr(sep, *val) != NULL) {
54			val++;
55			break;
56		}
57		if (hexstr) {
58			if (!isxdigit((u_char) val[0])) {
59				printf("%s: bad hexadecimal digits", __func__);
60				return NULL;
61			}
62			if (!isxdigit((u_char) val[1])) {
63				printf("%s: odd count hexadecimal digits", __func__);
64				return NULL;
65			}
66		}
67		if (p >= buf + len) {
68			if (hexstr)
69				printf("%s: hexadecimal digits too long", __func__);
70			else
71				printf("%s: string too long", __func__);
72			return NULL;
73		}
74		if (hexstr) {
75#define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
76			*p++ = (tohex((u_char) val[0]) << 4)
77				| tohex((u_char) val[1]);
78#undef tohex
79			val += 2;
80		} else
81			*p++ = *val++;
82	}
83	len = p - buf;
84	/* The string "-" is treated as the empty string. */
85	if (!hexstr && len == 1 && buf[0] == '-') {
86		len = 0;
87		memset(buf, 0, *lenp);
88	} else if (len < *lenp)
89		memset(p, 0, *lenp - len);
90
91	*lenp = len;
92	return val;
93}
94
95
96static void
97set80211(int s, const char* dev, int type, int val, int len, void* data)
98{
99	struct ieee80211req ireq;
100
101	(void)memset(&ireq, 0, sizeof(ireq));
102	(void)strncpy(ireq.i_name, dev, sizeof(ireq.i_name));
103	ireq.i_type = type;
104	ireq.i_val = val;
105	ireq.i_len = len;
106	ireq.i_data = data;
107	if (ioctl(s, SIOCS80211, &ireq, sizeof(struct ieee80211req)) < 0) {
108		fprintf(stderr, "%s: error in handling SIOCS80211 (type %d): %s\n",
109			__progname, type, strerror(errno));
110	}
111}
112
113
114static void
115set80211ssid(const char* dev, const char* val, int s)
116{
117	int ssid;
118	int len;
119	u_int8_t data[IEEE80211_NWID_LEN];
120
121	ssid = 0;
122	len = strlen(val);
123	if (len > 2 && isdigit((int)val[0]) && val[1] == ':') {
124		ssid = atoi(val) - 1;
125		val += 2;
126	}
127	bzero(data, sizeof(data));
128	len = sizeof(data);
129	if (get_string(val, NULL, data, &len) == NULL)
130		exit(1);
131
132	set80211(s, dev, IEEE80211_IOC_SSID, ssid, len, data);
133}
134
135
136static void
137set80211nwkey(const char* dev, const char* val, int s)
138{
139	int txkey;
140	int i;
141	int len;
142	u_int8_t data[IEEE80211_KEYBUF_SIZE];
143
144	set80211(s, dev, IEEE80211_IOC_WEP, IEEE80211_WEP_ON, 0, NULL);
145
146	if (isdigit((int)val[0]) && val[1] == ':') {
147		txkey = val[0] - '0' - 1;
148		val += 2;
149
150		for (i = 0; i < 4; i++) {
151			bzero(data, sizeof(data));
152			len = sizeof(data);
153			val = get_string(val, ",", data, &len);
154			if (val == NULL)
155				exit(1);
156
157			set80211(s, dev, IEEE80211_IOC_WEPKEY, i, len, data);
158		}
159	} else {
160		bzero(data, sizeof(data));
161		len = sizeof(data);
162		get_string(val, NULL, data, &len);
163		txkey = 0;
164
165		set80211(s, dev, IEEE80211_IOC_WEPKEY, 0, len, data);
166
167		bzero(data, sizeof(data));
168		for (i = 1; i < 4; i++)
169			set80211(s, dev, IEEE80211_IOC_WEPKEY, i, 0, data);
170	}
171
172	set80211(s, dev, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
173}
174
175
176static int
177get80211val(int s, const char* dev, int type, int* val)
178{
179	struct ieee80211req ireq;
180
181	(void) memset(&ireq, 0, sizeof(ireq));
182	(void) strncpy(ireq.i_name, dev, sizeof(ireq.i_name));
183	ireq.i_type = type;
184	if (ioctl(s, SIOCG80211, &ireq) < 0)
185		return -1;
186	*val = ireq.i_val;
187	return 0;
188}
189
190
191static int
192getid(int s, const char* dev, int ix, void* data, size_t len, int* plen,
193	int mesh)
194{
195	struct ieee80211req ireq;
196
197	(void)memset(&ireq, 0, sizeof(ireq));
198	(void)strncpy(ireq.i_name, dev, sizeof(ireq.i_name));
199	ireq.i_type = (!mesh) ? IEEE80211_IOC_SSID : IEEE80211_IOC_MESH_ID;
200	ireq.i_val = ix;
201	ireq.i_data = data;
202	ireq.i_len = len;
203	if (ioctl(s, SIOCG80211, &ireq) < 0)
204		return -1;
205	*plen = ireq.i_len;
206	return 0;
207}
208
209
210static void
211print_string(const u_int8_t* buf, int len)
212{
213	int i;
214	int hasspc;
215
216	i = 0;
217	hasspc = 0;
218	for (; i < len; i++) {
219		if (!isprint(buf[i]) && buf[i] != '\0')
220			break;
221		if (isspace(buf[i]))
222			hasspc++;
223	}
224	if (i == len) {
225		if (hasspc || len == 0 || buf[0] == '\0')
226			printf("\"%.*s\"", len, buf);
227		else
228			printf("%.*s", len, buf);
229	} else {
230		printf("0x");
231		for (i = 0; i < len; i++)
232			printf("%02x", buf[i]);
233	}
234}
235
236
237static void
238show_status(const char* dev, int s)
239{
240	int len;
241	int i;
242	int num;
243	uint8_t data[32];
244
245	if (getid(s, dev, -1, data, sizeof(data), &len, 0) < 0) {
246		fprintf(stderr, "error: not a wifi device\n");
247		exit(1);
248	}
249
250	if (get80211val(s, dev, IEEE80211_IOC_NUMSSIDS, &num) < 0)
251		num = 0;
252	printf("ssid ");
253	if (num > 1) {
254		for (i = 0; i < num; i++) {
255			if (getid(s, dev, i, data, sizeof(data), &len, 0) >= 0
256				&& len > 0) {
257				printf(" %d:", i + 1);
258				print_string(data, len);
259			}
260		}
261	} else
262		print_string(data, len);
263
264	printf("\n");
265}
266
267
268static void
269usage()
270{
271	fprintf(stderr, "usage: setwep device_path [ssid] [key]\n");
272	exit(1);
273}
274
275
276int
277main(int argc, char** argv)
278{
279	int s = socket(AF_INET, SOCK_DGRAM, 0);
280
281	if (argc < 2)
282		usage();
283
284	if (argc == 2)
285		show_status(argv[1], s);
286
287	if (argc > 3)
288		set80211ssid(argv[1], argv[2], s);
289
290	if (argc == 4)
291		set80211nwkey(argv[1], argv[3], s);
292
293	return 0;
294}
295