Home | History | Annotate | Line # | Download | only in bthcid
client.c revision 1.3
      1 /*	$NetBSD: client.c,v 1.3 2006/09/26 19:18:19 plunky Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006 Itronix Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. The name of Itronix Inc. may not be used to endorse
     16  *    or promote products derived from this software without specific
     17  *    prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
     23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     26  * ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __RCSID("$NetBSD: client.c,v 1.3 2006/09/26 19:18:19 plunky Exp $");
     34 
     35 #include <sys/ioctl.h>
     36 #include <sys/queue.h>
     37 #include <sys/time.h>
     38 #include <sys/un.h>
     39 #include <bluetooth.h>
     40 #include <errno.h>
     41 #include <event.h>
     42 #include <fcntl.h>
     43 #include <stdlib.h>
     44 #include <string.h>
     45 #include <syslog.h>
     46 #include <unistd.h>
     47 
     48 #include "bthcid.h"
     49 
     50 /*
     51  * A client is anybody who connects to our control socket to
     52  * receive PIN requests.
     53  */
     54 struct client {
     55 	struct event		ev;
     56 	int			fd;		/* client descriptor */
     57 	LIST_ENTRY(client)	next;
     58 };
     59 
     60 /*
     61  * PIN cache items are made when we have sent a client pin
     62  * request. The event is used to expire the item.
     63  */
     64 struct item {
     65 	struct event	 ev;
     66 	bdaddr_t	 laddr;			/* local device BDADDR */
     67 	bdaddr_t	 raddr;			/* remote device BDADDR */
     68 	uint8_t		 pin[HCI_PIN_SIZE];	/* PIN */
     69 	int		 hci;			/* HCI socket */
     70 	LIST_ENTRY(item) next;
     71 };
     72 
     73 static struct event		control_ev;
     74 
     75 static LIST_HEAD(,client)	client_list;
     76 static LIST_HEAD(,item)		item_list;
     77 
     78 static void process_control	(int, short, void *);
     79 static void process_client	(int, short, void *);
     80 static void process_item	(int, short, void *);
     81 
     82 #define PIN_REQUEST_TIMEOUT	30	/* Request is valid */
     83 #define PIN_TIMEOUT		300	/* PIN is valid */
     84 
     85 int
     86 init_control(const char *name, mode_t mode)
     87 {
     88 	struct sockaddr_un	un;
     89 	int			ctl;
     90 
     91 	LIST_INIT(&client_list);
     92 	LIST_INIT(&item_list);
     93 
     94 	if (name == NULL)
     95 		return 0;
     96 
     97 	if (unlink(name) < 0 && errno != ENOENT)
     98 		return -1;
     99 
    100 	ctl = socket(PF_LOCAL, SOCK_STREAM, 0);
    101 	if (ctl < 0)
    102 		return -1;
    103 
    104 	memset(&un, 0, sizeof(un));
    105 	un.sun_len = sizeof(un);
    106 	un.sun_family = AF_LOCAL;
    107 	strlcpy(un.sun_path, name, sizeof(un.sun_path));
    108 	if (bind(ctl, (struct sockaddr *)&un, sizeof(un)) < 0) {
    109 		close(ctl);
    110 		return -1;
    111 	}
    112 
    113 	if (chmod(name, mode) < 0) {
    114 		close(ctl);
    115 		unlink(name);
    116 		return -1;
    117 	}
    118 
    119 	if (listen(ctl, 10) < 0) {
    120 		close(ctl);
    121 		unlink(name);
    122 		return -1;
    123 	}
    124 
    125 	event_set(&control_ev, ctl, EV_READ | EV_PERSIST, process_control, NULL);
    126 	if (event_add(&control_ev, NULL) < 0) {
    127 		close(ctl);
    128 		unlink(name);
    129 		return -1;
    130 	}
    131 
    132 	return 0;
    133 }
    134 
    135 /* Process control socket event */
    136 static void
    137 process_control(int sock, short ev, void *arg)
    138 {
    139 	struct sockaddr_un	un;
    140 	socklen_t		n;
    141 	int			fd;
    142 	struct client		*cl;
    143 
    144 	n = sizeof(un);
    145 	fd = accept(sock, (struct sockaddr *)&un, &n);
    146 	if (fd < 0) {
    147 		syslog(LOG_ERR, "Could not accept PIN client connection");
    148 		return;
    149 	}
    150 
    151 	n = 1;
    152 	if (ioctl(fd, FIONBIO, &n) < 0) {
    153 		syslog(LOG_ERR, "Could not set non blocking IO for client");
    154 		close(fd);
    155 		return;
    156 	}
    157 
    158 	cl = malloc(sizeof(struct client));
    159 	if (cl == NULL) {
    160 		syslog(LOG_ERR, "Could not malloc client");
    161 		close(fd);
    162 		return;
    163 	}
    164 
    165 	memset(cl, 0, sizeof(struct client));
    166 	cl->fd = fd;
    167 
    168 	event_set(&cl->ev, fd, EV_READ | EV_PERSIST, process_client, cl);
    169 	if (event_add(&cl->ev, NULL) < 0) {
    170 		syslog(LOG_ERR, "Could not add client event");
    171 		free(cl);
    172 		close(fd);
    173 		return;
    174 	}
    175 
    176 	syslog(LOG_DEBUG, "New Client");
    177 	LIST_INSERT_HEAD(&client_list, cl, next);
    178 }
    179 
    180 /* Process client response packet */
    181 static void
    182 process_client(int sock, short ev, void *arg)
    183 {
    184 	bthcid_pin_response_t	 rp;
    185 	struct timeval		 tv;
    186 	struct sockaddr_bt	 sa;
    187 	struct client		*cl = arg;
    188 	struct item		*item;
    189 	int			 n;
    190 
    191 	n = recv(sock, &rp, sizeof(rp), 0);
    192 	if (n != sizeof(rp)) {
    193 		if (n != 0)
    194 			syslog(LOG_ERR, "Bad Client");
    195 
    196 		close(sock);
    197 		LIST_REMOVE(cl, next);
    198 		free(cl);
    199 
    200 		syslog(LOG_DEBUG, "Client Closed");
    201 		return;
    202 	}
    203 
    204 	syslog(LOG_DEBUG, "New PIN for %s", bt_ntoa(&rp.raddr, NULL));
    205 
    206 	LIST_FOREACH(item, &item_list, next) {
    207 		if (bdaddr_same(&rp.laddr, &item->laddr) == 0
    208 		    || bdaddr_same(&rp.raddr, &item->raddr) == 0)
    209 			continue;
    210 
    211 		evtimer_del(&item->ev);
    212 		if (item->hci != -1) {
    213 			memset(&sa, 0, sizeof(sa));
    214 			sa.bt_len = sizeof(sa);
    215 			sa.bt_family = AF_BLUETOOTH;
    216 			bdaddr_copy(&sa.bt_bdaddr, &item->laddr);
    217 
    218 			send_pin_code_reply(item->hci, &sa, &item->raddr, rp.pin);
    219 		}
    220 		goto newpin;
    221 	}
    222 
    223 	item = malloc(sizeof(struct item));
    224 	if (item == NULL) {
    225 		syslog(LOG_ERR, "Item allocation failed");
    226 		return;
    227 	}
    228 
    229 	memset(item, 0, sizeof(struct item));
    230 	bdaddr_copy(&item->laddr, &rp.laddr);
    231 	bdaddr_copy(&item->raddr, &rp.raddr);
    232 	evtimer_set(&item->ev, process_item, item);
    233 	LIST_INSERT_HEAD(&item_list, item, next);
    234 
    235 newpin:
    236 	memcpy(item->pin, rp.pin, HCI_PIN_SIZE);
    237 	item->hci = -1;
    238 
    239 	tv.tv_sec = PIN_TIMEOUT;
    240 	tv.tv_usec = 0;
    241 
    242 	if (evtimer_add(&item->ev, &tv) < 0) {
    243 		syslog(LOG_ERR, "Cannot add event timer for item");
    244 		LIST_REMOVE(item, next);
    245 		free(item);
    246 	}
    247 }
    248 
    249 /* Send PIN request to client */
    250 int
    251 send_client_request(bdaddr_t *laddr, bdaddr_t *raddr, int hci)
    252 {
    253 	bthcid_pin_request_t	 cp;
    254 	struct client		*cl;
    255 	struct item		*item;
    256 	int			 n = 0;
    257 	struct timeval		 tv;
    258 
    259 	memset(&cp, 0, sizeof(cp));
    260 	bdaddr_copy(&cp.laddr, laddr);
    261 	bdaddr_copy(&cp.raddr, raddr);
    262 	cp.time = PIN_REQUEST_TIMEOUT;
    263 
    264 	LIST_FOREACH(cl, &client_list, next) {
    265 		if (send(cl->fd, &cp, sizeof(cp), 0) != sizeof(cp))
    266 			syslog(LOG_ERR, "send PIN request failed");
    267 		else
    268 			n++;
    269 	}
    270 
    271 	if (n == 0)
    272 		return 0;
    273 
    274 	syslog(LOG_DEBUG, "Sent PIN requests to %d client%s.",
    275 				n, (n == 1 ? "" : "s"));
    276 
    277 	item = malloc(sizeof(struct item));
    278 	if (item == NULL) {
    279 		syslog(LOG_ERR, "Cannot allocate PIN request item");
    280 		return 0;
    281 	}
    282 
    283 	memset(item, 0, sizeof(struct item));
    284 	bdaddr_copy(&item->laddr, laddr);
    285 	bdaddr_copy(&item->raddr, raddr);
    286 	item->hci = hci;
    287 	evtimer_set(&item->ev, process_item, item);
    288 
    289 	tv.tv_sec = cp.time;
    290 	tv.tv_usec = 0;
    291 
    292 	if (evtimer_add(&item->ev, &tv) < 0) {
    293 		syslog(LOG_ERR, "Cannot add request timer");
    294 		free(item);
    295 		return 0;
    296 	}
    297 
    298 	LIST_INSERT_HEAD(&item_list, item, next);
    299 	return 1;
    300 }
    301 
    302 /* Process item event (by expiring it) */
    303 static void
    304 process_item(int fd, short ev, void *arg)
    305 {
    306 	struct item *item = arg;
    307 
    308 	syslog(LOG_DEBUG, "PIN for %s expired", bt_ntoa(&item->raddr, NULL));
    309 
    310 	LIST_REMOVE(item, next);
    311 	free(item);
    312 }
    313 
    314 /* lookup PIN in item cache */
    315 uint8_t *
    316 lookup_pin(bdaddr_t *laddr, bdaddr_t *raddr)
    317 {
    318 	struct item *item;
    319 
    320 	LIST_FOREACH(item, &item_list, next) {
    321 		if (bdaddr_same(raddr, &item->raddr) == 0)
    322 			continue;
    323 
    324 		if (bdaddr_same(laddr, &item->laddr) == 0
    325 		    && bdaddr_any(&item->laddr) == 0)
    326 			continue;
    327 
    328 		if (item->hci >= 0)
    329 			break;
    330 
    331 		syslog(LOG_DEBUG, "Matched PIN from cache");
    332 		return item->pin;
    333 	}
    334 
    335 	return NULL;
    336 }
    337