From e68b3c582cd657bbc75dcca0d5fddd79237987c2 Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Wed, 30 Jan 2008 14:25:35 +0000 Subject: [PATCH] Add rfcomm_sppd(1) - RFCOMM Serial Port Profile daemon. Obtained-from: NetBSD --- usr.sbin/Makefile | 3 +- usr.sbin/rfcomm_sppd/Makefile | 11 + usr.sbin/rfcomm_sppd/rfcomm_sdp.c | 298 +++++++++++++++++ usr.sbin/rfcomm_sppd/rfcomm_sdp.h | 40 +++ usr.sbin/rfcomm_sppd/rfcomm_sppd.1 | 220 ++++++++++++ usr.sbin/rfcomm_sppd/rfcomm_sppd.c | 520 +++++++++++++++++++++++++++++ 6 files changed, 1091 insertions(+), 1 deletion(-) create mode 100644 usr.sbin/rfcomm_sppd/Makefile create mode 100644 usr.sbin/rfcomm_sppd/rfcomm_sdp.c create mode 100644 usr.sbin/rfcomm_sppd/rfcomm_sdp.h create mode 100644 usr.sbin/rfcomm_sppd/rfcomm_sppd.1 create mode 100644 usr.sbin/rfcomm_sppd/rfcomm_sppd.c diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 4a6e368afb..7f58ee69dd 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -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 index 0000000000..397b5505d9 --- /dev/null +++ b/usr.sbin/rfcomm_sppd/Makefile @@ -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 diff --git a/usr.sbin/rfcomm_sppd/rfcomm_sdp.c b/usr.sbin/rfcomm_sppd/rfcomm_sdp.c new file mode 100644 index 0000000000..860425d46f --- /dev/null +++ b/usr.sbin/rfcomm_sppd/rfcomm_sdp.c @@ -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 + * 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 +#include +#include +#include + +#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 index 0000000000..7c941a915a --- /dev/null +++ b/usr.sbin/rfcomm_sppd/rfcomm_sdp.h @@ -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 index 0000000000..60c09e47d4 --- /dev/null +++ b/usr.sbin/rfcomm_sppd/rfcomm_sppd.1 @@ -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 +.\" 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 index 0000000000..2e6e9baa02 --- /dev/null +++ b/usr.sbin/rfcomm_sppd/rfcomm_sppd.c @@ -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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 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); +} -- 2.41.0