Path: blob/main/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c
104792 views
/*1* rfcomm_pppd.c2*/34/*-5* SPDX-License-Identifier: BSD-2-Clause6*7* Copyright (c) 2001-2008 Maksim Yevmenkin <[email protected]>8* All rights reserved.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND20* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE23* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL24* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS25* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT27* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY28* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF29* SUCH DAMAGE.30*31* $Id: rfcomm_pppd.c,v 1.5 2003/09/07 18:32:11 max Exp $32*/33#define L2CAP_SOCKET_CHECKED34#include <bluetooth.h>35#include <ctype.h>36#include <err.h>37#include <errno.h>38#include <fcntl.h>39#include <sdp.h>40#include <signal.h>41#include <stdarg.h>42#include <stdio.h>43#include <stdlib.h>44#include <string.h>45#include <syslog.h>46#include <unistd.h>4748#define RFCOMM_PPPD "rfcomm_pppd"4950int rfcomm_channel_lookup (bdaddr_t const *local,51bdaddr_t const *remote,52int service, int *channel, int *error);5354static void exec_ppp (int s, char *unit, char *label);55static void sighandler (int s);56static void usage (void);5758static int done;5960/* Main */61int62main(int argc, char *argv[])63{64struct sockaddr_rfcomm sock_addr;65char *label = NULL, *unit = NULL, *ep = NULL;66bdaddr_t addr;67int s, channel, detach, server, service,68regdun, regsp;69pid_t pid;7071memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));72channel = 0;73detach = 1;74server = 0;75service = 0;76regdun = 0;77regsp = 0;7879/* Parse command line arguments */80while ((s = getopt(argc, argv, "a:cC:dDhl:sSu:")) != -1) {81switch (s) {82case 'a': /* BDADDR */83if (!bt_aton(optarg, &addr)) {84struct hostent *he = NULL;8586if ((he = bt_gethostbyname(optarg)) == NULL)87errx(1, "%s: %s", optarg, hstrerror(h_errno));8889memcpy(&addr, he->h_addr, sizeof(addr));90}91break;9293case 'c': /* client */94server = 0;95break;9697case 'C': /* RFCOMM channel */98channel = strtoul(optarg, &ep, 10);99if (*ep != '\0') {100channel = 0;101switch (tolower(optarg[0])) {102case 'd': /* DialUp Networking */103service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;104break;105106case 'l': /* LAN Access Using PPP */107service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;108break;109}110}111break;112113case 'd': /* do not detach */114detach = 0;115break;116117case 'D': /* Register DUN service as well as LAN service */118regdun = 1;119break;120121case 'l': /* PPP label */122label = optarg;123break;124125case 's': /* server */126server = 1;127break;128129case 'S': /* Register SP service as well as LAN service */130regsp = 1;131break;132133case 'u': /* PPP -unit option */134strtoul(optarg, &ep, 10);135if (*ep != '\0')136usage();137/* NOT REACHED */138139unit = optarg;140break;141142case 'h':143default:144usage();145/* NOT REACHED */146}147}148149/* Check if we got everything we wanted */150if (label == NULL)151errx(1, "Must specify PPP label");152153if (!server) {154if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)155errx(1, "Must specify server BD_ADDR");156157/* Check channel, if was not set then obtain it via SDP */158if (channel == 0 && service != 0)159if (rfcomm_channel_lookup(NULL, &addr, service,160&channel, &s) != 0)161errc(1, s, "Could not obtain RFCOMM channel");162}163164if (channel <= 0 || channel > 30)165errx(1, "Invalid RFCOMM channel number %d", channel);166167openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);168169if (detach && daemon(0, 0) < 0) {170syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",171strerror(errno), errno);172exit(1);173}174175s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);176if (s < 0) {177syslog(LOG_ERR, "Could not create socket. %s (%d)",178strerror(errno), errno);179exit(1);180}181182if (server) {183struct sigaction sa;184void *ss = NULL;185sdp_lan_profile_t lan;186187/* Install signal handler */188memset(&sa, 0, sizeof(sa));189sa.sa_handler = sighandler;190191if (sigaction(SIGTERM, &sa, NULL) < 0) {192syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",193strerror(errno), errno);194exit(1);195}196197if (sigaction(SIGHUP, &sa, NULL) < 0) {198syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",199strerror(errno), errno);200exit(1);201}202203if (sigaction(SIGINT, &sa, NULL) < 0) {204syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",205strerror(errno), errno);206exit(1);207}208209sa.sa_handler = SIG_IGN;210sa.sa_flags = SA_NOCLDWAIT;211212if (sigaction(SIGCHLD, &sa, NULL) < 0) {213syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",214strerror(errno), errno);215exit(1);216}217218/* bind socket and listen for incoming connections */219sock_addr.rfcomm_len = sizeof(sock_addr);220sock_addr.rfcomm_family = AF_BLUETOOTH;221memcpy(&sock_addr.rfcomm_bdaddr, &addr,222sizeof(sock_addr.rfcomm_bdaddr));223sock_addr.rfcomm_channel = channel;224225if (bind(s, (struct sockaddr *) &sock_addr,226sizeof(sock_addr)) < 0) {227syslog(LOG_ERR, "Could not bind socket. %s (%d)",228strerror(errno), errno);229exit(1);230}231232if (listen(s, 10) < 0) {233syslog(LOG_ERR, "Could not listen on socket. %s (%d)",234strerror(errno), errno);235exit(1);236}237238ss = sdp_open_local(NULL);239if (ss == NULL) {240syslog(LOG_ERR, "Unable to create local SDP session");241exit(1);242}243244if (sdp_error(ss) != 0) {245syslog(LOG_ERR, "Unable to open local SDP session. " \246"%s (%d)", strerror(sdp_error(ss)),247sdp_error(ss));248exit(1);249}250251memset(&lan, 0, sizeof(lan));252lan.server_channel = channel;253254if (sdp_register_service(ss,255SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,256&addr, (void *) &lan, sizeof(lan), NULL) != 0) {257syslog(LOG_ERR, "Unable to register LAN service with " \258"local SDP daemon. %s (%d)",259strerror(sdp_error(ss)), sdp_error(ss));260exit(1);261}262263/*264* Register DUN (Dial-Up Networking) service on the same265* RFCOMM channel if requested. There is really no good reason266* to not to support this. AT-command exchange can be faked267* with chat script in ppp.conf268*/269270if (regdun) {271sdp_dun_profile_t dun;272273memset(&dun, 0, sizeof(dun));274dun.server_channel = channel;275276if (sdp_register_service(ss,277SDP_SERVICE_CLASS_DIALUP_NETWORKING,278&addr, (void *) &dun, sizeof(dun),279NULL) != 0) {280syslog(LOG_ERR, "Unable to register DUN " \281"service with local SDP daemon. " \282"%s (%d)", strerror(sdp_error(ss)),283sdp_error(ss));284exit(1);285}286}287288/*289* Register SP (Serial Port) service on the same RFCOMM channel290* if requested. It appears that some cell phones are using so291* called "callback mechanism". In this scenario user is trying292* to connect his cell phone to the Internet, and, user's host293* computer is acting as the gateway server. It seems that it294* is not possible to tell the phone to just connect and start295* using the LAN service. Instead the user's host computer must296* "jump start" the phone by connecting to the phone's SP297* service. What happens next is the phone kills the existing298* connection and opens another connection back to the user's299* host computer. The phone really wants to use LAN service,300* but for whatever reason it looks for SP service on the301* user's host computer. This brain damaged behavior was302* reported for Nokia 6600 and Sony/Ericsson P900. Both phones303* are Symbian-based phones. Perhaps this is a Symbian problem?304*/305306if (regsp) {307sdp_sp_profile_t sp;308309memset(&sp, 0, sizeof(sp));310sp.server_channel = channel;311312if (sdp_register_service(ss,313SDP_SERVICE_CLASS_SERIAL_PORT,314&addr, (void *) &sp, sizeof(sp),315NULL) != 0) {316syslog(LOG_ERR, "Unable to register SP " \317"service with local SDP daemon. " \318"%s (%d)", strerror(sdp_error(ss)),319sdp_error(ss));320exit(1);321}322}323324for (done = 0; !done; ) {325socklen_t len = sizeof(sock_addr);326int s1 = accept(s, (struct sockaddr *) &sock_addr, &len);327328if (s1 < 0) {329syslog(LOG_ERR, "Could not accept connection " \330"on socket. %s (%d)", strerror(errno),331errno);332exit(1);333}334335pid = fork();336if (pid == (pid_t) -1) {337syslog(LOG_ERR, "Could not fork(). %s (%d)",338strerror(errno), errno);339exit(1);340}341342if (pid == 0) {343sdp_close(ss);344close(s);345346/* Reset signal handler */347memset(&sa, 0, sizeof(sa));348sa.sa_handler = SIG_DFL;349350sigaction(SIGTERM, &sa, NULL);351sigaction(SIGHUP, &sa, NULL);352sigaction(SIGINT, &sa, NULL);353sigaction(SIGCHLD, &sa, NULL);354355/* Become daemon */356daemon(0, 0);357358/*359* XXX Make sure user does not shoot himself360* in the foot. Do not pass unit option to the361* PPP when operating in the server mode.362*/363364exec_ppp(s1, NULL, label);365} else366close(s1);367}368} else {369sock_addr.rfcomm_len = sizeof(sock_addr);370sock_addr.rfcomm_family = AF_BLUETOOTH;371memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,372sizeof(sock_addr.rfcomm_bdaddr));373sock_addr.rfcomm_channel = 0;374375if (bind(s, (struct sockaddr *) &sock_addr,376sizeof(sock_addr)) < 0) {377syslog(LOG_ERR, "Could not bind socket. %s (%d)",378strerror(errno), errno);379exit(1);380}381382memcpy(&sock_addr.rfcomm_bdaddr, &addr,383sizeof(sock_addr.rfcomm_bdaddr));384sock_addr.rfcomm_channel = channel;385386if (connect(s, (struct sockaddr *) &sock_addr,387sizeof(sock_addr)) < 0) {388syslog(LOG_ERR, "Could not connect socket. %s (%d)",389strerror(errno), errno);390exit(1);391}392393exec_ppp(s, unit, label);394}395396exit(0);397} /* main */398399/*400* Redirects stdin/stdout to s, stderr to /dev/null and exec401* 'ppp -direct -quiet [-unit N] label'. Never returns.402*/403404static void405exec_ppp(int s, char *unit, char *label)406{407char ppp[] = "/usr/sbin/ppp";408char *ppp_args[] = { ppp, "-direct", "-quiet",409NULL, NULL, NULL, NULL };410411close(0);412if (dup(s) < 0) {413syslog(LOG_ERR, "Could not dup(0). %s (%d)",414strerror(errno), errno);415exit(1);416}417418close(1);419if (dup(s) < 0) {420syslog(LOG_ERR, "Could not dup(1). %s (%d)",421strerror(errno), errno);422exit(1);423}424425close(2);426open("/dev/null", O_RDWR);427428if (unit != NULL) {429ppp_args[3] = "-unit";430ppp_args[4] = unit;431ppp_args[5] = label;432} else433ppp_args[3] = label;434435if (execv(ppp, ppp_args) < 0) {436syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \437"%s (%d)", ppp, (unit != NULL)? " -unit " : "",438(unit != NULL)? unit : "", label,439strerror(errno), errno);440exit(1);441}442} /* run_ppp */443444/* Signal handler */445static void446sighandler(int s)447{448done = 1;449} /* sighandler */450451/* Display usage and exit */452static void453usage(void)454{455fprintf(stdout,456"Usage: %s options\n" \457"Where options are:\n" \458"\t-a address Address to listen on or connect to (required for client)\n" \459"\t-c Act as a clinet (default)\n" \460"\t-C channel RFCOMM channel to listen on or connect to (required)\n" \461"\t-d Run in foreground\n" \462"\t-D Register Dial-Up Networking service (server mode only)\n" \463"\t-l label Use PPP label (required)\n" \464"\t-s Act as a server\n" \465"\t-S Register Serial Port service (server mode only)\n" \466"\t-u N Tell PPP to operate on /dev/tunN (client mode only)\n" \467"\t-h Display this message\n", RFCOMM_PPPD);468469exit(255);470} /* usage */471472473474