freebsd-dev/usr.sbin/wpa/ndis_events/ndis_events.c
Pedro F. Giffuni df57947f08 spdx: initial adoption of licensing ID tags.
The Software Package Data Exchange (SPDX) group provides a specification
to make it easier for automated tools to detect and summarize well known
opensource licenses. We are gradually adopting the specification, noting
that the tags are considered only advisory and do not, in any way,
superceed or replace the license texts.

Special thanks to Wind River for providing access to "The Duke of
Highlander" tool: an older (2014) run over FreeBSD tree was useful as a
starting point.

Initially, only tag files that use BSD 4-Clause "Original" license.

RelNotes:	yes
Differential Revision:	https://reviews.freebsd.org/D13133
2017-11-18 14:26:50 +00:00

354 lines
8.1 KiB
C

/*-
* SPDX-License-Identifier: BSD-4-Clause
*
* Copyright (c) 2005
* Bill Paul <wpaul@windriver.com>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Bill Paul.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* This program simulates the behavior of the ndis_events utility
* supplied with wpa_supplicant for Windows. The original utility
* is designed to translate Windows WMI events. We don't have WMI,
* but we need to supply certain event info to wpa_supplicant in
* order to make WPA2 work correctly, so we fake up the interface.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/route.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <syslog.h>
#include <stdarg.h>
static int verbose = 0;
static int debug = 0;
static int all_events = 0;
#define PROGNAME "ndis_events"
#define WPA_SUPPLICANT_PORT 9876
#define NDIS_INDICATION_LEN 2048
#define EVENT_CONNECT 0
#define EVENT_DISCONNECT 1
#define EVENT_MEDIA_SPECIFIC 2
#define NDIS_STATUS_MEDIA_CONNECT 0x4001000B
#define NDIS_STATUS_MEDIA_DISCONNECT 0x4001000C
#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012
struct ndis_evt {
uint32_t ne_sts;
uint32_t ne_len;
#ifdef notdef
char ne_buf[1];
#endif
};
static int find_ifname(int, char *);
static int announce_event(char *, int, struct sockaddr_in *);
static void usage(void);
static void
dbgmsg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (debug)
vwarnx(fmt, ap);
else
vsyslog(LOG_ERR, fmt, ap);
va_end(ap);
return;
}
static int
find_ifname(idx, name)
int idx;
char *name;
{
int mib[6];
size_t needed;
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
char *buf, *lim, *next;
needed = 0;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0; /* protocol */
mib[3] = 0; /* wildcard address family */
mib[4] = NET_RT_IFLIST;
mib[5] = 0; /* no flags */
if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
return(EIO);
buf = malloc (needed);
if (buf == NULL)
return(ENOMEM);
if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) {
free(buf);
return(EIO);
}
lim = buf + needed;
next = buf;
while (next < lim) {
ifm = (struct if_msghdr *)next;
if (ifm->ifm_type == RTM_IFINFO) {
sdl = (struct sockaddr_dl *)(ifm + 1);
if (ifm->ifm_index == idx) {
strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
name[sdl->sdl_nlen] = '\0';
free (buf);
return (0);
}
}
next += ifm->ifm_msglen;
}
free (buf);
return(ENOENT);
}
static int
announce_event(ifname, sock, dst)
char *ifname;
int sock;
struct sockaddr_in *dst;
{
int s;
char indication[NDIS_INDICATION_LEN];
struct ifreq ifr;
struct ndis_evt *e;
char buf[512], *pos, *end;
int len, type, _type;
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
dbgmsg("socket creation failed");
return(EINVAL);
}
bzero((char *)&ifr, sizeof(ifr));
e = (struct ndis_evt *)indication;
e->ne_len = NDIS_INDICATION_LEN - sizeof(struct ndis_evt);
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_data = indication;
if (ioctl(s, SIOCGPRIVATE_0, &ifr) < 0) {
close(s);
if (verbose) {
if (errno == ENOENT)
dbgmsg("drained all events from %s",
ifname, errno);
else
dbgmsg("failed to read event info from %s: %d",
ifname, errno);
}
return(ENOENT);
}
if (e->ne_sts == NDIS_STATUS_MEDIA_CONNECT) {
type = EVENT_CONNECT;
if (verbose)
dbgmsg("Received a connect event for %s", ifname);
if (!all_events) {
close(s);
return(0);
}
}
if (e->ne_sts == NDIS_STATUS_MEDIA_DISCONNECT) {
type = EVENT_DISCONNECT;
if (verbose)
dbgmsg("Received a disconnect event for %s", ifname);
if (!all_events) {
close(s);
return(0);
}
}
if (e->ne_sts == NDIS_STATUS_MEDIA_SPECIFIC_INDICATION) {
type = EVENT_MEDIA_SPECIFIC;
if (verbose)
dbgmsg("Received a media-specific event for %s",
ifname);
}
end = buf + sizeof(buf);
_type = (int) type;
memcpy(buf, &_type, sizeof(_type));
pos = buf + sizeof(_type);
len = snprintf(pos + 1, end - pos - 1, "%s", ifname);
if (len < 0) {
close(s);
return(ENOSPC);
}
if (len > 255)
len = 255;
*pos = (unsigned char) len;
pos += 1 + len;
if (e->ne_len) {
if (e->ne_len > 255 || 1 + e->ne_len > end - pos) {
dbgmsg("Not enough room for send_event data (%d)\n",
e->ne_len);
close(s);
return(ENOSPC);
}
*pos++ = (unsigned char) e->ne_len;
memcpy(pos, (indication) + sizeof(struct ndis_evt), e->ne_len);
pos += e->ne_len;
}
len = sendto(sock, buf, pos - buf, 0, (struct sockaddr *) dst,
sizeof(struct sockaddr_in));
close(s);
return(0);
}
static void
usage()
{
fprintf(stderr, "Usage: ndis_events [-a] [-d] [-v]\n");
exit(1);
}
int
main(argc, argv)
int argc;
char *argv[];
{
int s, r, n;
struct sockaddr_in sin;
char msg[NDIS_INDICATION_LEN];
struct rt_msghdr *rtm;
struct if_msghdr *ifm;
char ifname[IFNAMSIZ];
int ch;
while ((ch = getopt(argc, argv, "dva")) != -1) {
switch(ch) {
case 'd':
debug++;
break;
case 'v':
verbose++;
break;
case 'a':
all_events++;
break;
default:
usage();
break;
}
}
if (!debug && daemon(0, 0))
err(1, "failed to daemonize ourselves");
if (!debug)
openlog(PROGNAME, LOG_PID | LOG_CONS, LOG_DAEMON);
bzero((char *)&sin, sizeof(sin));
/* Create a datagram socket. */
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
dbgmsg("socket creation failed");
exit(1);
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
sin.sin_port = htons(WPA_SUPPLICANT_PORT);
/* Create a routing socket. */
r = socket (PF_ROUTE, SOCK_RAW, 0);
if (r < 0) {
dbgmsg("routing socket creation failed");
exit(1);
}
/* Now sit and spin, waiting for events. */
if (verbose)
dbgmsg("Listening for events");
while (1) {
n = read(r, msg, NDIS_INDICATION_LEN);
rtm = (struct rt_msghdr *)msg;
if (rtm->rtm_type != RTM_IFINFO)
continue;
ifm = (struct if_msghdr *)msg;
if (find_ifname(ifm->ifm_index, ifname))
continue;
if (strstr(ifname, "ndis")) {
while(announce_event(ifname, s, &sin) == 0)
;
} else {
if (verbose)
dbgmsg("Skipping ifinfo message from %s",
ifname);
}
}
/* NOTREACHED */
exit(0);
}