/*
* bluez.c - bluetooth networking functions module - implementation
*
* nc6 - an advanced netcat clone
* Copyright (C) 2001-2006 Mauro Tortonesi <mauro _at_ deepspace6.net>
* Copyright (C) 2002-2006 Chris Leishman <chris _at_ leishman.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "system.h"
#include "bluez.h"
#include "misc.h"
#include "netsupport.h"
#include "parser.h"
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <limits.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/sco.h>
#include <bluetooth/l2cap.h>
RCSID("@(#) $Header: /ds6/cvs/nc6/src/bluez.c,v 1.4 2006/01/19 22:46:23 chris Exp $");
/* suggested size for argument to getnameinfo_ex */
static const int BA_STR_SIZE = BA_MAXHOST + 14;
static int getbluezaddr(const char *address, const char *service,
const struct addrinfo *hints, struct sockaddr *sa,
socklen_t *salen);
static void getbluezname(const struct sockaddr *sa, int protocol,
char *str, size_t size);
static bool is_allowed(const struct sockaddr *sa, socklen_t salen,
const struct addrinfo *hints,
const char *address, const char *service);
int bluez_connect(const struct addrinfo *hints,
const char *remote_address, const char *remote_service,
set_sockopt_handler_t set_sockopt_handler, void *hdata,
time_t timeout, int *rt_socktype)
{
int err, fd = -1;
struct sockaddr_storage ss;
socklen_t salen = 0;
char name_buf[BA_STR_SIZE];
/* make sure arguments are valid and preconditions are respected */
assert(hints != NULL);
assert(remote_address != NULL && strlen(remote_address) > 0);
/* this function only supports a specific protocol family */
assert(hints->ai_family == PF_BLUETOOTH);
memset(&ss, 0, sizeof(ss));
/* get the sockaddr */
if (getbluezaddr(remote_address, remote_service, hints,
(struct sockaddr *)&ss, &salen))
{
return -1;
}
/* setup name_buf if we're in verbose mode */
if (verbose_mode())
getbluezname((struct sockaddr *)&ss, hints->ai_protocol,
name_buf, sizeof(name_buf));
fd = socket(hints->ai_family, hints->ai_socktype, hints->ai_protocol);
if (fd < 0) {
warning("cannot create the bluez socket: %s", strerror(errno));
return -1;
}
if (set_sockopt_handler != NULL)
set_sockopt_handler(fd, hdata);
err = connect_with_timeout(fd, (struct sockaddr *)&ss, salen, timeout);
if (err != 0) {
if (verbose_mode()) {
if (errno == ETIMEDOUT) {
/* connection timed out */
warning(_("timeout while connecting to %s"),
name_buf);
}
else {
/* connection failed */
warning(_("cannot connect to %s: %s"),
name_buf, strerror(errno));
}
}
return err;
}
*rt_socktype = hints->ai_socktype;
return fd;
}
int bluez_listener(const struct addrinfo *hints,
const char *local_address, const char *local_service,
const char *remote_address, const char *remote_service,
set_sockopt_handler_t set_sockopt_handler, void *hdata,
listen_callback_t callback, void *cdata,
time_t timeout, int max_accept)
{
int fd = -1;
struct sockaddr_storage ss;
socklen_t salen = 0;
char name_buf[BA_STR_SIZE];
/* make sure arguments are valid and preconditions are respected */
assert(hints != NULL);
assert(local_address == NULL || strlen(local_address) > 0);
assert(remote_address == NULL || strlen(remote_address) > 0);
assert(callback != NULL);
/* this function only supports a specific protocol family */
assert(hints->ai_family == PF_BLUETOOTH);
/* if max_accept is 0, just return */
if (max_accept == 0)
return 0;
memset(&ss, 0, sizeof(ss));
/* get the sockaddr */
if (getbluezaddr(local_address, local_service, hints,
(struct sockaddr *)&ss, &salen))
{
return -1;
}
/* setup name_buf if we're in verbose mode */
if (verbose_mode())
getbluezname((struct sockaddr *)&ss, hints->ai_protocol,
name_buf, sizeof(name_buf));
fd = socket(hints->ai_family, hints->ai_socktype, hints->ai_protocol);
if (fd < 0) {
warning("cannot create the bluez socket: %s", strerror(errno));
return -1;
}
if (set_sockopt_handler != NULL)
set_sockopt_handler(fd, hdata);
if (bind(fd, (struct sockaddr *)&ss, salen) < 0) {
warning(_("bind to source %s failed: %s"),
name_buf, strerror(errno));
return -1;
}
if (listen(fd, SOMAXCONN) != 0) {
warning(_("cannot listen on %s: %s"),
name_buf, strerror(errno));
}
if (verbose_mode())
warning(_("listening on %s ..."), name_buf);
/* enter into the accept loop */
for (;;) {
fd_set accept_fdset;
struct timeval tv, *tvp = NULL;
struct sockaddr_storage dest;
socklen_t destlen;
int ns, err;
char c_name_buf[BA_STR_SIZE];
FD_ZERO(&accept_fdset);
FD_SET(fd, &accept_fdset);
/* setup timeout */
if (timeout > 0) {
tv.tv_sec = timeout;
tv.tv_usec = 0;
tvp = &tv;
}
/* wait for an incoming connection */
err = select(fd + 1, &accept_fdset, NULL, NULL, tvp);
if (err <= 0) {
if (err < 0 && errno == EINTR)
continue;
if (err == 0)
warning(_("connection timed out"));
else
warning("select error: %s", strerror(errno));
return -1;
}
/* double check that the fd is actually set */
if (!FD_ISSET(fd, &accept_fdset))
continue;
destlen = sizeof(dest);
ns = accept(fd, (struct sockaddr *)&dest, &destlen);
if (ns < 0) {
warning("accept failed: %s", strerror(errno));
return -1;
}
/* get the name for this client */
if (verbose_mode())
getbluezname((struct sockaddr *)&dest,
hints->ai_protocol,
c_name_buf, sizeof(name_buf));
/* check if connections from this client are allowed */
if ((remote_address == NULL && remote_service == NULL) ||
(is_allowed((struct sockaddr *)&dest, destlen, hints,
remote_address, remote_service) == true))
{
if (verbose_mode()) {
warning(_("connect from %s"), c_name_buf);
}
callback(ns, SOCK_SEQPACKET, cdata);
if (max_accept > 0 && --max_accept == 0)
break;
} else {
close(ns);
if (verbose_mode()) {
warning(_("refused connect from %s"),
c_name_buf);
}
}
}
/* close the listening socket */
close(fd);
return 0;
}
static int getbluezaddr(const char *address, const char *service,
const struct addrinfo *hints, struct sockaddr *sa,
socklen_t *salen)
{
int psm;
switch (hints->ai_protocol) {
case BTPROTO_SCO:
assert(service == NULL);
*salen = sizeof(struct sockaddr_sco);
((struct sockaddr_sco *)sa)->sco_family = AF_BLUETOOTH;
if (address)
str2ba(address, &((struct sockaddr_sco *)sa)->sco_bdaddr);
break;
case BTPROTO_L2CAP:
assert(service != NULL && strlen(service) > 0);
*salen = sizeof(struct sockaddr_l2);
((struct sockaddr_l2 *)sa)->l2_family = AF_BLUETOOTH;
if (address)
str2ba(address, &((struct sockaddr_l2 *)sa)->l2_bdaddr);
if (safe_atoi(service, &psm) || psm < 0 || psm > USHRT_MAX) {
warning(_("invalid l2cap psm (service name)"));
return -1;
}
((struct sockaddr_l2 *)sa)->l2_psm = psm;
break;
default:
fatal_internal("invalid bluetooth protocol");
}
return 0;
}
static void getbluezname(const struct sockaddr *sa, int protocol,
char *str, size_t size)
{
char babuf[BA_MAXHOST];
/* check arguments */
assert(sa != NULL);
assert(str != NULL);
assert(size > 0);
switch (protocol) {
case BTPROTO_SCO:
safe_ba2str(&(((const struct sockaddr_sco *)sa)->sco_bdaddr),
babuf, sizeof(babuf));
strncpy(str, babuf, size);
break;
case BTPROTO_L2CAP:
safe_ba2str(&(((const struct sockaddr_l2 *)sa)->l2_bdaddr),
babuf, sizeof(babuf));
snprintf(str, size, "%s psm %d", babuf,
((const struct sockaddr_l2 *)sa)->l2_psm);
break;
default:
fatal_internal("invalid bluetooth protocol");
}
}
static bool is_allowed(const struct sockaddr *sa, socklen_t salen,
const struct addrinfo *hints,
const char *address, const char *service)
{
struct sockaddr_storage ss;
socklen_t len;
if (getbluezaddr(address, service, hints, (struct sockaddr *)&ss, &len))
return false;
return sockaddr_compare(sa, salen, (const struct sockaddr *)&ss, len);
}
syntax highlighted by Code2HTML, v. 0.9.1