Add rfcomm_sppd(1) - RFCOMM Serial Port Profile daemon.
authorHasso Tepper <hasso@dragonflybsd.org>
Wed, 30 Jan 2008 14:25:35 +0000 (14:25 +0000)
committerHasso Tepper <hasso@dragonflybsd.org>
Wed, 30 Jan 2008 14:25:35 +0000 (14:25 +0000)
Obtained-from: NetBSD

usr.sbin/Makefile
usr.sbin/rfcomm_sppd/Makefile [new file with mode: 0644]
usr.sbin/rfcomm_sppd/rfcomm_sdp.c [new file with mode: 0644]
usr.sbin/rfcomm_sppd/rfcomm_sdp.h [new file with mode: 0644]
usr.sbin/rfcomm_sppd/rfcomm_sppd.1 [new file with mode: 0644]
usr.sbin/rfcomm_sppd/rfcomm_sppd.c [new file with mode: 0644]

index 4a6e368..7f58ee6 100644 (file)
@@ -1,6 +1,6 @@
 #      From: @(#)Makefile      5.20 (Berkeley) 6/12/93
 # $FreeBSD: src/usr.sbin/Makefile,v 1.183.2.14 2003/04/16 11:01:51 ru Exp $
-# $DragonFly: src/usr.sbin/Makefile,v 1.38 2008/01/30 14:10:19 hasso Exp $
+# $DragonFly: src/usr.sbin/Makefile,v 1.39 2008/01/30 14:25:35 hasso Exp $
 
 .include "../sys/platform/${MACHINE_PLATFORM}/Makefile.inc"
 
@@ -96,6 +96,7 @@ SUBDIR= 802_11 \
        rdate \
        repquota \
        resident \
+       rfcomm_sppd \
        rip6query \
        rmt \
        route6d \
diff --git a/usr.sbin/rfcomm_sppd/Makefile b/usr.sbin/rfcomm_sppd/Makefile
new file mode 100644 (file)
index 0000000..397b550
--- /dev/null
@@ -0,0 +1,11 @@
+# $NetBSD: Makefile,v 1.2 2007/05/28 12:06:29 tls Exp $
+# $DragonFly: src/usr.sbin/rfcomm_sppd/Attic/Makefile,v 1.1 2008/01/30 14:25:35 hasso Exp $
+
+PROG=          rfcomm_sppd
+SRCS=          rfcomm_sppd.c rfcomm_sdp.c
+
+CFLAGS+=       -I${.CURDIR}/../../sys
+DPADD+=                ${LIBBLUETOOTH} ${LIBSDP}
+LDADD+=                -lbluetooth -lsdp
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rfcomm_sppd/rfcomm_sdp.c b/usr.sbin/rfcomm_sppd/rfcomm_sdp.c
new file mode 100644 (file)
index 0000000..860425d
--- /dev/null
@@ -0,0 +1,298 @@
+/* $NetBSD: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $ */
+/* $DragonFly: src/usr.sbin/rfcomm_sppd/Attic/rfcomm_sdp.c,v 1.1 2008/01/30 14:25:35 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2006 Itronix Inc.
+ * 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. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+/*
+ * rfcomm_sdp.c
+ *
+ * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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.
+ *
+ * $Id: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $
+ * $FreeBSD: src/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sdp.c,v 1.2 2004/04/09 23:26:16 emax Exp $
+ */
+
+#include <bluetooth.h>
+#include <errno.h>
+#include <sdp.h>
+#include <stdio.h>
+
+#include "rfcomm_sdp.h"
+
+#undef PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE
+#define        PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE    256
+
+#undef PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE
+#define        PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE   12
+
+static int rfcomm_proto_list_parse (uint8_t const *start, uint8_t const *end,
+                                       uint8_t *channel, int *error);
+
+/*
+ * Lookup RFCOMM channel number in the Protocol Descriptor List
+ */
+
+#undef rfcomm_channel_lookup_exit
+#define        rfcomm_channel_lookup_exit(e) { \
+       if (error != NULL) \
+               *error = (e); \
+       if (ss != NULL) { \
+               sdp_close(ss); \
+               ss = NULL; \
+       } \
+       return (((e) == 0)? 0 : -1); \
+}
+
+int
+rfcomm_channel_lookup(bdaddr_t const *local, bdaddr_t const *remote,
+                       int service, uint8_t *channel, int *error)
+{
+       uint8_t          buffer[PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE];
+       void            *ss    = NULL;
+       uint16_t         serv  = (uint16_t) service;
+       uint32_t         attr  = SDP_ATTR_RANGE(
+                                       SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+                                       SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
+       sdp_attr_t       proto = { SDP_ATTR_INVALID,0,sizeof(buffer),buffer };
+       uint32_t         type, len;
+
+       if (local == NULL)
+               local = BDADDR_ANY;
+       if (remote == NULL || channel == NULL)
+               rfcomm_channel_lookup_exit(EINVAL);
+
+       if ((ss = sdp_open(local, remote)) == NULL)
+               rfcomm_channel_lookup_exit(ENOMEM);
+       if (sdp_error(ss) != 0)
+               rfcomm_channel_lookup_exit(sdp_error(ss));
+
+       if (sdp_search(ss, 1, &serv, 1, &attr, 1, &proto) != 0)
+               rfcomm_channel_lookup_exit(sdp_error(ss));
+       if (proto.flags != SDP_ATTR_OK)
+               rfcomm_channel_lookup_exit(ENOATTR);
+
+       sdp_close(ss);
+       ss = NULL;
+
+       /*
+        * If it is possible for more than one kind of protocol stack to be
+        * used to gain access to the service, the ProtocolDescriptorList
+        * takes the form of a data element alternative. We always use the
+        * first protocol stack.
+        *
+        * A minimal Protocol Descriptor List for RFCOMM based service would
+        * look like
+        *
+        * seq8 len8                    - 2 bytes
+        *      seq8 len8               - 2 bytes
+        *              uuid16 value16  - 3 bytes       L2CAP
+        *      seq8 len8               - 2 bytes
+        *              uuid16 value16  - 3 bytes       RFCOMM
+        *              uint8  value8   - 2 bytes       RFCOMM param #1
+        *                              =========
+        *                               14 bytes
+        *
+        * Lets not count first [seq8 len8] wrapper, so the minimal size of
+        * the Protocol Descriptor List (the data we are actually interested
+        * in) for RFCOMM based service would be 12 bytes.
+        */
+
+       if (proto.vlen < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
+               rfcomm_channel_lookup_exit(EINVAL);
+
+       SDP_GET8(type, proto.value);
+
+       if (type == SDP_DATA_ALT8) {
+               SDP_GET8(len, proto.value);
+       } else if (type == SDP_DATA_ALT16) {
+               SDP_GET16(len, proto.value);
+       } else if (type == SDP_DATA_ALT32) {
+               SDP_GET32(len, proto.value);
+       } else
+               len = 0;
+
+       if (len > 0)
+               SDP_GET8(type, proto.value);
+
+       switch (type) {
+       case SDP_DATA_SEQ8:
+               SDP_GET8(len, proto.value);
+               break;
+
+       case SDP_DATA_SEQ16:
+               SDP_GET16(len, proto.value);
+               break;
+
+       case SDP_DATA_SEQ32:
+               SDP_GET32(len, proto.value);
+               break;
+
+       default:
+               rfcomm_channel_lookup_exit(ENOATTR);
+               /* NOT REACHED */
+       }
+
+       if (len < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
+               rfcomm_channel_lookup_exit(EINVAL);
+
+       return (rfcomm_proto_list_parse(proto.value,
+                                       buffer + proto.vlen, channel, error));
+}
+
+/*
+ * Parse protocol descriptor list
+ *
+ * The ProtocolDescriptorList attribute describes one or more protocol
+ * stacks that may be used to gain access to the service described by
+ * the service record. If the ProtocolDescriptorList describes a single
+ * stack, it takes the form of a data element sequence in which each
+ * element of the sequence is a protocol descriptor.
+ */
+
+#undef rfcomm_proto_list_parse_exit
+#define        rfcomm_proto_list_parse_exit(e) { \
+       if (error != NULL) \
+               *error = (e); \
+       return (((e) == 0)? 0 : -1); \
+}
+
+static int
+rfcomm_proto_list_parse(uint8_t const *start, uint8_t const *end,
+                       uint8_t *channel, int *error)
+{
+       int     type, len, value;
+
+       while (start < end) {
+
+               /*
+                * Parse protocol descriptor
+                *
+                * A protocol descriptor identifies a communications protocol
+                * and provides protocol specific parameters. A protocol
+                * descriptor is represented as a data element sequence. The
+                * first data element in the sequence must be the UUID that
+                * identifies the protocol. Additional data elements optionally
+                * provide protocol specific information, such as the L2CAP
+                * protocol/service multiplexer (PSM) and the RFCOMM server
+                * channel number (CN).
+                */
+
+               /* We must have at least one byte (type) */
+               if (end - start < 1)
+                       rfcomm_proto_list_parse_exit(EINVAL)
+
+               SDP_GET8(type, start);
+               switch (type) {
+               case SDP_DATA_SEQ8:
+                       SDP_GET8(len, start);
+                       break;
+
+               case SDP_DATA_SEQ16:
+                       SDP_GET16(len, start);
+                       break;
+
+               case SDP_DATA_SEQ32:
+                       SDP_GET32(len, start);
+                       break;
+
+               default:
+                       rfcomm_proto_list_parse_exit(ENOATTR)
+                       /* NOT REACHED */
+               }
+
+               /* We must have at least 3 bytes (type + UUID16) */
+               if (end - start < 3)
+                       rfcomm_proto_list_parse_exit(EINVAL);
+
+               /* Get protocol UUID */
+               SDP_GET8(type, start); len -= sizeof(uint8_t);
+               switch (type) {
+               case SDP_DATA_UUID16:
+                       SDP_GET16(value, start); len -= sizeof(uint16_t);
+                       if (value != SDP_UUID_PROTOCOL_RFCOMM)
+                               goto next_protocol;
+                       break;
+
+               case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
+               case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
+               default:
+                       rfcomm_proto_list_parse_exit(ENOATTR);
+                       /* NOT REACHED */
+               }
+
+               /*
+                * First protocol specific parameter for RFCOMM procotol must
+                * be uint8 that represents RFCOMM channel number. So we must
+                * have at least two bytes.
+                */
+
+               if (end - start < 2)
+                       rfcomm_proto_list_parse_exit(EINVAL);
+
+               SDP_GET8(type, start);
+               if (type != SDP_DATA_UINT8)
+                       rfcomm_proto_list_parse_exit(ENOATTR);
+
+               SDP_GET8(*channel, start);
+
+               rfcomm_proto_list_parse_exit(0);
+               /* NOT REACHED */
+next_protocol:
+               start += len;
+       }
+
+       /*
+        * If we got here then it means we could not find RFCOMM protocol
+        * descriptor, but the reply format was actually valid.
+        */
+
+       rfcomm_proto_list_parse_exit(ENOATTR);
+}
diff --git a/usr.sbin/rfcomm_sppd/rfcomm_sdp.h b/usr.sbin/rfcomm_sppd/rfcomm_sdp.h
new file mode 100644 (file)
index 0000000..7c941a9
--- /dev/null
@@ -0,0 +1,40 @@
+/* $NetBSD: rfcomm_sdp.h,v 1.1 2006/06/19 15:44:56 gdamore Exp $ */
+/* $DragonFly: src/usr.sbin/rfcomm_sppd/Attic/rfcomm_sdp.h,v 1.1 2008/01/30 14:25:35 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2006 Itronix Inc.
+ * All rights reserved.
+ *
+ * Written by Iain Hibbert for Itronix Inc.
+ *
+ * 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. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+
+#ifndef _RFCOMM_SDP_H_
+#define _RFCOMM_SDP_H_
+
+int rfcomm_channel_lookup(bdaddr_t const *, bdaddr_t const *, int, uint8_t *, int *);
+
+#endif /* _RFCOMM_SDP_H */
diff --git a/usr.sbin/rfcomm_sppd/rfcomm_sppd.1 b/usr.sbin/rfcomm_sppd/rfcomm_sppd.1
new file mode 100644 (file)
index 0000000..60c09e4
--- /dev/null
@@ -0,0 +1,220 @@
+.\" $NetBSD: rfcomm_sppd.1,v 1.5 2007/04/21 06:15:23 plunky Exp $
+.\" $DragonFly: src/usr.sbin/rfcomm_sppd/Attic/rfcomm_sppd.1,v 1.1 2008/01/30 14:25:35 hasso Exp $
+.\"
+.\" Copyright (c) 2006 Itronix Inc.
+.\" 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. The name of Itronix Inc. may not be used to endorse
+.\"    or promote products derived from this software without specific
+.\"    prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+.\"
+.\"
+.\" Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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.
+.\"
+.Dd April 10, 2007
+.Dt RFCOMM_SPPD 1
+.Os
+.Sh NAME
+.Nm rfcomm_sppd
+.Nd RFCOMM Serial Port Profile daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar device
+.Op Fl m Ar mode
+.Op Fl s Ar service
+.Op Fl t Ar tty
+.Brq Fl a Ar address | Fl c Ar channel 
+.Sh DESCRIPTION
+The
+.Nm
+utility is a Serial Port Profile daemon, providing serial access over
+an RFCOMM connection to a remote device.
+.Nm
+can work in client or server mode.
+.Pp
+In client mode,
+.Nm
+initiates an RFCOMM connection to the
+.Ar service
+at the remote
+.Ar address .
+.Pp
+In server mode,
+.Nm
+registers the
+.Ar service
+with the local SDP server and listens on the specified RFCOMM
+.Ar channel
+for an incoming connection.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl c Ar channel"
+.It Fl a Ar address
+Client mode.
+Specify the address of the remote RFCOMM device.
+The address can be specified as BD_ADDR or name.
+If given as a name, then the
+.Nm
+utility will attempt to resolve the name via
+.Xr bt_gethostbyname 3 .
+.It Fl c Ar channel
+Server mode.
+Specify the RFCOMM channel number to listen on.
+.Nm
+will register the service with the local
+.Xr sdpd 8
+daemon.
+Note that registering services with
+.Xr sdpd 8
+is a privileged operation.
+.It Fl d Ar device
+Use the local device with the specifed address.
+The device can be specified by BD_ADDR or device name.
+See
+.Xr btconfig 8
+for a list of available devices.
+If no
+.Ar device
+is specified, the connection will be set up on a system determined device.
+.It Fl m Ar mode
+Set connection link mode.
+Supported modes are:
+.Pp
+.Bl -tag -compact -offset indent
+.It auth
+require devices be paired.
+.It encrypt
+auth, plus enable encryption.
+.It secure
+encryption, plus change of link key.
+.El
+.It Fl s Ar service
+This is the service class that will be searched for on the remote device.
+If no
+.Ar service
+is given, the default
+.Qq Serial Port
+service class will be used.
+Known service classes are:
+.Pp
+.Bl -tag -compact -offset indent
+.It DUN
+Dialup Networking
+.It LAN
+LAN access using PPP
+.It SP
+Serial Port
+.El
+.Pp
+In client mode, the service class may be given as a channel number, for instances
+where the remote device does not provide Service Discovery.
+.It Fl t Ar tty
+Slave pseudo tty name.
+If this option is given,
+.Nm
+will detach from the controlling process after the bluetooth connection is
+made, and operate over the named
+.Xr pty 4
+pair.
+Otherwise, stdin/stdout will be used.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh FILES
+.Bl -tag -width ".Pa /dev/tty[p-sP-S][0-9a-v]" -compact
+.It Pa /dev/pty[p-sP-S][0-9a-v]
+master pseudo terminals
+.It Pa /dev/tty[p-sP-S][0-9a-v]
+slave pseudo terminals
+.El
+.Sh EXAMPLES
+.Dl rfcomm_sppd -a 00:01:02:03:04:05 -s 1 -t /dev/ttyp1
+.Pp
+Will open an RFCOMM connection to the server at
+.Li 00:01:02:03:04:05
+on channel
+.Li 1 .
+Once the connection has been established,
+.Nm
+will detach and
+.Pa /dev/ttyp1
+can be used to communicate with the remote serial port on the
+server, e.g. with the use of
+.Pp
+.Dl cu -l /dev/ttyp1
+.Pp
+In order to use
+.Nm
+to automatically create a secured link for
+.Xr pppd 8 ,
+use
+.Dl pty Qo rfcomm_sppd -a 00:01:02:03:04:05 -s DUN -m secure Qc
+.Pp
+in your
+.Xr pppd 8
+configuration file.
+.Sh SEE ALSO
+.Xr bluetooth 3 ,
+.Xr bluetooth 4 ,
+.Xr pty 4 ,
+.Xr btconfig 8 ,
+.Xr pppd 8 ,
+.Xr sdpd 8
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Fx
+and was ported to
+.Nx 4.0
+by
+.An Iain Hibbert
+under the sponsorship of
+.An Itronix, Inc .
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com ,
+.An Iain Hibbert
+.Sh BUGS
+Please report if found.
diff --git a/usr.sbin/rfcomm_sppd/rfcomm_sppd.c b/usr.sbin/rfcomm_sppd/rfcomm_sppd.c
new file mode 100644 (file)
index 0000000..2e6e9ba
--- /dev/null
@@ -0,0 +1,520 @@
+/* $NetBSD: rfcomm_sppd.c,v 1.8 2007/04/21 10:39:30 dsl Exp $ */
+/* $DragonFly: src/usr.sbin/rfcomm_sppd/Attic/rfcomm_sppd.c,v 1.1 2008/01/30 14:25:35 hasso Exp $ */
+
+/*-
+ * Copyright (c) 2006 Itronix Inc.
+ * 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. The name of Itronix Inc. may not be used to endorse
+ *    or promote products derived from this software without specific
+ *    prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
+ */
+/*
+ * rfcomm_sppd.c
+ *
+ * Copyright (c) 2007 Iain Hibbert
+ * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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 <bluetooth.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <paths.h>
+#include <sdp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <netbt/rfcomm.h>
+
+#include "rfcomm_sdp.h"
+
+#define max(a, b)      ((a) > (b) ? (a) : (b))
+
+int open_tty(const char *);
+int open_client(bdaddr_t *, bdaddr_t *, int, const char *);
+int open_server(bdaddr_t *, uint8_t, int, const char *);
+void copy_data(int, int);
+void sighandler(int);
+void usage(void);
+void reset_tio(void);
+
+int done;              /* got a signal */
+struct termios tio;    /* stored termios for reset on exit */
+
+struct service {
+       const char      *name;
+       const char      *description;
+       uint16_t        class;
+       int             pdulen;
+} services[] = {
+       { "DUN",        "Dialup Networking",
+         SDP_SERVICE_CLASS_DIALUP_NETWORKING,
+         sizeof(struct sdp_dun_profile)
+       },
+       { "LAN",        "Lan access using PPP",
+         SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
+         sizeof(struct sdp_lan_profile)
+       },
+       { "SP",         "Serial Port",
+         SDP_SERVICE_CLASS_SERIAL_PORT,
+         sizeof(struct sdp_sp_profile)
+       },
+       { NULL,         NULL,
+         0,
+         0
+       }
+};
+
+int
+main(int argc, char *argv[])
+{
+       struct termios          t;
+       bdaddr_t                laddr, raddr;
+       fd_set                  rdset;
+       const char              *service;
+       char                    *ep, *tty;
+       int                     lm, n, rfcomm, tty_in, tty_out;
+       uint8_t                 channel;
+
+       bdaddr_copy(&laddr, BDADDR_ANY);
+       bdaddr_copy(&raddr, BDADDR_ANY);
+       service = "SP";
+       tty = NULL;
+       channel = 0;
+       lm = 0;
+
+       /* Parse command line options */
+       while ((n = getopt(argc, argv, "a:c:d:hm:s:t:")) != -1) {
+               switch (n) {
+               case 'a': /* remote device address */
+                       if (!bt_aton(optarg, &raddr)) {
+                               struct hostent  *he = NULL;
+
+                               if ((he = bt_gethostbyname(optarg)) == NULL)
+                                       errx(EXIT_FAILURE, "%s: %s", optarg,
+                                           hstrerror(h_errno));
+
+                               bdaddr_copy(&raddr, (bdaddr_t *)he->h_addr);
+                       }
+                       break;
+
+               case 'c': /* RFCOMM channel */
+                       channel = strtoul(optarg, &ep, 10);
+                       if (*ep != '\0' || channel < 1 || channel > 30)
+                               errx(EXIT_FAILURE, "Invalid channel: %s", optarg);
+
+                       break;
+
+               case 'd': /* local device address */
+                       if (!bt_devaddr(optarg, &laddr))
+                               err(EXIT_FAILURE, "%s", optarg);
+
+                       break;
+
+               case 'm': /* Link Mode */
+                       if (strcasecmp(optarg, "auth") == 0)
+                               lm = RFCOMM_LM_AUTH;
+                       else if (strcasecmp(optarg, "encrypt") == 0)
+                               lm = RFCOMM_LM_ENCRYPT;
+                       else if (strcasecmp(optarg, "secure") == 0)
+                               lm = RFCOMM_LM_SECURE;
+                       else
+                               errx(EXIT_FAILURE, "%s: unknown mode", optarg);
+
+                       break;
+
+               case 's': /* service class */
+                       service = optarg;
+                       break;
+
+               case 't': /* Slave TTY name */
+                       if (optarg[0] != '/')
+                               asprintf(&tty, "%s%s", _PATH_DEV, optarg);
+                       else
+                               tty = optarg;
+
+                       break;
+
+               case 'h':
+               default:
+                       usage();
+                       /* NOT REACHED */
+               }
+       }
+
+       /*
+        * validate options:
+        *      must have channel or remote address but not both
+        */
+       if ((channel == 0 && bdaddr_any(&raddr))
+           || (channel != 0 && !bdaddr_any(&raddr)))
+               usage();
+
+       /*
+        * grab ttys before we start the bluetooth
+        */
+       if (tty == NULL) {
+               tty_in = STDIN_FILENO;
+               tty_out = STDOUT_FILENO;
+       } else {
+               tty_in = open_tty(tty);
+               tty_out = tty_in;
+       }
+
+       /* open RFCOMM */
+       if (channel == 0)
+               rfcomm = open_client(&laddr, &raddr, lm, service);
+       else
+               rfcomm = open_server(&laddr, channel, lm, service);
+
+       /*
+        * now we are ready to go, so either detach or maybe turn
+        * off some input processing, so that rfcomm_sppd can
+        * be used directly with stdio
+        */
+       if (tty == NULL) {
+               if (tcgetattr(tty_in, &t) < 0)
+                       err(EXIT_FAILURE, "tcgetattr");
+
+               memcpy(&tio, &t, sizeof(tio));
+               t.c_lflag &= ~(ECHO | ICANON);
+               t.c_iflag &= ~(ICRNL);
+
+               if (memcmp(&tio, &t, sizeof(tio))) {
+                       if (tcsetattr(tty_in, TCSANOW, &t) < 0)
+                               err(EXIT_FAILURE, "tcsetattr");
+
+                       atexit(reset_tio);
+               }
+       } else {
+               if (daemon(0, 0) < 0)
+                       err(EXIT_FAILURE, "daemon() failed");
+       }
+
+       /* catch signals */
+       done = 0;
+       (void)signal(SIGHUP, sighandler);
+       (void)signal(SIGINT, sighandler);
+       (void)signal(SIGPIPE, sighandler);
+       (void)signal(SIGTERM, sighandler);
+
+       openlog(getprogname(), LOG_PERROR | LOG_PID, LOG_DAEMON);
+       syslog(LOG_INFO, "Starting on %s...", (tty ? tty : "stdio"));
+
+       n = max(tty_in, rfcomm) + 1;
+       while (!done) {
+               FD_ZERO(&rdset);
+               FD_SET(tty_in, &rdset);
+               FD_SET(rfcomm, &rdset);
+
+               if (select(n, &rdset, NULL, NULL, NULL) < 0) {
+                       if (errno == EINTR)
+                               continue;
+
+                       syslog(LOG_ERR, "select error: %m");
+                       exit(EXIT_FAILURE);
+               }
+
+               if (FD_ISSET(tty_in, &rdset))
+                       copy_data(tty_in, rfcomm);
+
+               if (FD_ISSET(rfcomm, &rdset))
+                       copy_data(rfcomm, tty_out);
+       }
+
+       syslog(LOG_INFO, "Completed on %s", (tty ? tty : "stdio"));
+       exit(EXIT_SUCCESS);
+}
+
+int
+open_tty(const char *tty)
+{
+       char             pty[PATH_MAX], *slash;
+       struct group    *gr = NULL;
+       gid_t            ttygid;
+       int              master;
+
+       /*
+        * Construct master PTY name. The slave tty name must be less then
+        * PATH_MAX characters in length, must contain '/' character and
+        * must not end with '/'.
+        */
+       if (strlen(tty) >= sizeof(pty))
+               errx(EXIT_FAILURE, ": tty name too long");
+
+       strlcpy(pty, tty, sizeof(pty));
+       slash = strrchr(pty, '/');
+       if (slash == NULL || slash[1] == '\0')
+               errx(EXIT_FAILURE, "%s: invalid tty", tty);
+
+       slash[1] = 'p';
+       if (strcmp(pty, tty) == 0)
+               errx(EXIT_FAILURE, "Master and slave tty are the same (%s)", tty);
+
+       if ((master = open(pty, O_RDWR, 0)) < 0)
+               err(EXIT_FAILURE, "%s", pty);
+
+       /*
+        * Slave TTY
+        */
+
+       if ((gr = getgrnam("tty")) != NULL)
+               ttygid = gr->gr_gid;
+       else
+               ttygid = (gid_t)-1;
+
+       (void)chown(tty, getuid(), ttygid);
+       (void)chmod(tty, S_IRUSR | S_IWUSR | S_IWGRP);
+       (void)revoke(tty);
+
+       return master;
+}
+
+int
+open_client(bdaddr_t *laddr, bdaddr_t *raddr, int lm, const char *service)
+{
+       struct sockaddr_bt sa;
+       struct service *s;
+       struct linger l;
+       char *ep;
+       int fd;
+       uint8_t channel;
+
+       for (s = services ; ; s++) {
+               if (s->name == NULL) {
+                       channel = strtoul(service, &ep, 10);
+                       if (*ep != '\0' || channel < 1 || channel > 30)
+                               errx(EXIT_FAILURE, "Invalid service: %s", service);
+
+                       break;
+               }
+
+               if (strcasecmp(s->name, service) == 0) {
+                       if (rfcomm_channel_lookup(laddr, raddr, s->class, &channel, &errno) < 0)
+                               err(EXIT_FAILURE, "%s", s->name);
+
+                       break;
+               }
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.bt_len = sizeof(sa);
+       sa.bt_family = AF_BLUETOOTH;
+       bdaddr_copy(&sa.bt_bdaddr, laddr);
+
+       fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (fd < 0)
+               err(EXIT_FAILURE, "socket()");
+
+       if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+               err(EXIT_FAILURE, "bind(%s)", bt_ntoa(laddr, NULL));
+
+       memset(&l, 0, sizeof(l));
+       l.l_onoff = 1;
+       l.l_linger = 5;
+       if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
+               err(EXIT_FAILURE, "linger()");
+
+       if (setsockopt(fd, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) < 0)
+               err(EXIT_FAILURE, "link mode");
+
+       sa.bt_channel = channel;
+       bdaddr_copy(&sa.bt_bdaddr, raddr);
+
+       if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+               err(EXIT_FAILURE, "connect(%s, %d)", bt_ntoa(raddr, NULL),
+                                                    channel);
+
+       return fd;
+}
+
+/*
+ * In all the profiles we currently support registering, the channel
+ * is the first octet in the PDU, and it seems all the rest can be
+ * zero, so we just use an array of uint8_t big enough to store the
+ * largest, currently LAN. See <sdp.h> for definitions..
+ */
+#define pdu_len                sizeof(struct sdp_lan_profile)
+
+int
+open_server(bdaddr_t *laddr, uint8_t channel, int lm, const char *service)
+{
+       struct sockaddr_bt sa;
+       struct linger l;
+       socklen_t len;
+       void *ss;
+       int sv, fd, n;
+       uint8_t pdu[pdu_len];
+
+       memset(&sa, 0, sizeof(sa));
+       sa.bt_len = sizeof(sa);
+       sa.bt_family = AF_BLUETOOTH;
+       bdaddr_copy(&sa.bt_bdaddr, laddr);
+       sa.bt_channel = channel;
+
+       sv = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (sv < 0)
+               err(EXIT_FAILURE, "socket()");
+
+       if (bind(sv, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+               err(EXIT_FAILURE, "bind(%s, %d)", bt_ntoa(laddr, NULL),
+                                                 channel);
+
+       if (setsockopt(sv, BTPROTO_RFCOMM, SO_RFCOMM_LM, &lm, sizeof(lm)) < 0)
+               err(EXIT_FAILURE, "link mode");
+
+       if (listen(sv, 1) < 0)
+               err(EXIT_FAILURE, "listen()");
+
+       /* Register service with SDP server */
+       for (n = 0 ; ; n++) {
+               if (services[n].name == NULL)
+                       usage();
+
+               if (strcasecmp(services[n].name, service) == 0)
+                       break;
+       }
+
+       memset(pdu, 0, pdu_len);
+       pdu[0] = channel;
+
+       ss = sdp_open_local(NULL);
+       if (ss == NULL || (errno = sdp_error(ss)) != 0)
+               err(EXIT_FAILURE, "sdp_open_local");
+
+       if (sdp_register_service(ss, services[n].class, laddr,
+                   pdu, services[n].pdulen, NULL) != 0) {
+               errno = sdp_error(ss);
+               err(EXIT_FAILURE, "sdp_register_service");
+       }
+
+       len = sizeof(sa);
+       fd = accept(sv, (struct sockaddr *)&sa, &len);
+       if (fd < 0)
+               err(EXIT_FAILURE, "accept");
+
+       memset(&l, 0, sizeof(l));
+       l.l_onoff = 1;
+       l.l_linger = 5;
+       if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0)
+               err(EXIT_FAILURE, "linger()");
+
+       close(sv);
+       return fd;
+}
+
+void
+copy_data(int src, int dst)
+{
+       static char     buf[BUFSIZ];
+       ssize_t         nr, nw, off;
+
+       while ((nr = read(src, buf, sizeof(buf))) == -1) {
+               if (errno != EINTR) {
+                       syslog(LOG_ERR, "read failed: %m");
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       if (nr == 0)    /* reached EOF */
+               done++;
+
+       for (off = 0 ; nr ; nr -= nw, off += nw) {
+               if ((nw = write(dst, buf + off, (size_t)nr)) == -1) {
+                       syslog(LOG_ERR, "write failed: %m");
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+void
+sighandler(int s)
+{
+
+       done++;
+}
+
+void
+reset_tio(void)
+{
+
+       tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
+}
+
+void
+usage(void)
+{
+       const char *cmd = getprogname();
+       struct service *s;
+
+       fprintf(stderr, "Usage: %s [-d device] [-m mode] [-s service] [-t tty]\n"
+                       "       %*s {-a bdaddr | -c channel}\n"
+                       "\n"
+                       "Where:\n"
+                       "\t-a bdaddr    remote device address\n"
+                       "\t-c channel   local RFCOMM channel\n"
+                       "\t-d device    local device address\n"
+                       "\t-m mode      link mode\n"
+                       "\t-s service   service class\n"
+                       "\t-t tty       run in background using pty\n"
+                       "\n", cmd, (int)strlen(cmd), "");
+
+       fprintf(stderr, "Known service classes:\n");
+       for (s = services ; s->name != NULL ; s++)
+               fprintf(stderr, "\t%-13s%s\n", s->name, s->description);
+
+       exit(EXIT_FAILURE);
+}