1114879Sjulian/* 2114879Sjulian * rfcomm_pppd.c 3177174Semax */ 4177174Semax 5177174Semax/*- 6177174Semax * Copyright (c) 2001-2008 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7114879Sjulian * All rights reserved. 8114879Sjulian * 9114879Sjulian * Redistribution and use in source and binary forms, with or without 10114879Sjulian * modification, are permitted provided that the following conditions 11114879Sjulian * are met: 12114879Sjulian * 1. Redistributions of source code must retain the above copyright 13114879Sjulian * notice, this list of conditions and the following disclaimer. 14114879Sjulian * 2. Redistributions in binary form must reproduce the above copyright 15114879Sjulian * notice, this list of conditions and the following disclaimer in the 16114879Sjulian * documentation and/or other materials provided with the distribution. 17114879Sjulian * 18114879Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19114879Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20114879Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21114879Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22114879Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23114879Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24114879Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25114879Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26114879Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27114879Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28114879Sjulian * SUCH DAMAGE. 29114879Sjulian * 30121054Semax * $Id: rfcomm_pppd.c,v 1.5 2003/09/07 18:32:11 max Exp $ 31114879Sjulian * $FreeBSD$ 32114879Sjulian */ 33114879Sjulian 34121054Semax#include <bluetooth.h> 35121054Semax#include <ctype.h> 36121054Semax#include <err.h> 37114879Sjulian#include <errno.h> 38114879Sjulian#include <fcntl.h> 39121054Semax#include <sdp.h> 40114879Sjulian#include <signal.h> 41114879Sjulian#include <stdarg.h> 42114879Sjulian#include <stdio.h> 43114879Sjulian#include <stdlib.h> 44114879Sjulian#include <string.h> 45114879Sjulian#include <syslog.h> 46114879Sjulian#include <unistd.h> 47114879Sjulian 48114879Sjulian#define RFCOMM_PPPD "rfcomm_pppd" 49114879Sjulian 50121054Semaxint rfcomm_channel_lookup (bdaddr_t const *local, 51121054Semax bdaddr_t const *remote, 52121054Semax int service, int *channel, int *error); 53121054Semax 54126169Semaxstatic void exec_ppp (int s, char *unit, char *label); 55114879Sjulianstatic void sighandler (int s); 56114879Sjulianstatic void usage (void); 57114879Sjulian 58114879Sjulianstatic int done; 59114879Sjulian 60114879Sjulian/* Main */ 61114879Sjulianint 62114879Sjulianmain(int argc, char *argv[]) 63114879Sjulian{ 64114879Sjulian struct sockaddr_rfcomm sock_addr; 65126169Semax char *label = NULL, *unit = NULL, *ep = NULL; 66114879Sjulian bdaddr_t addr; 67176857Semax int s, channel, detach, server, service, 68176857Semax regdun, regsp; 69114879Sjulian pid_t pid; 70114879Sjulian 71114879Sjulian memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)); 72114879Sjulian channel = 0; 73114879Sjulian detach = 1; 74114879Sjulian server = 0; 75121054Semax service = 0; 76176857Semax regdun = 0; 77132711Semax regsp = 0; 78114879Sjulian 79114879Sjulian /* Parse command line arguments */ 80176857Semax while ((s = getopt(argc, argv, "a:cC:dDhl:sSu:")) != -1) { 81114879Sjulian switch (s) { 82121054Semax case 'a': /* BDADDR */ 83121054Semax if (!bt_aton(optarg, &addr)) { 84121054Semax struct hostent *he = NULL; 85114879Sjulian 86121054Semax if ((he = bt_gethostbyname(optarg)) == NULL) 87121054Semax errx(1, "%s: %s", optarg, hstrerror(h_errno)); 88114879Sjulian 89121054Semax memcpy(&addr, he->h_addr, sizeof(addr)); 90121054Semax } 91121054Semax break; 92114879Sjulian 93114879Sjulian case 'c': /* client */ 94114879Sjulian server = 0; 95114879Sjulian break; 96114879Sjulian 97114879Sjulian case 'C': /* RFCOMM channel */ 98121054Semax channel = strtoul(optarg, &ep, 10); 99126169Semax if (*ep != '\0') { 100121054Semax channel = 0; 101121054Semax switch (tolower(optarg[0])) { 102121054Semax case 'd': /* DialUp Networking */ 103121054Semax service = SDP_SERVICE_CLASS_DIALUP_NETWORKING; 104121054Semax break; 105121054Semax 106121054Semax case 'l': /* LAN Access Using PPP */ 107121054Semax service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP; 108121054Semax break; 109121054Semax } 110121054Semax } 111114879Sjulian break; 112114879Sjulian 113114879Sjulian case 'd': /* do not detach */ 114114879Sjulian detach = 0; 115114879Sjulian break; 116114879Sjulian 117176857Semax case 'D': /* Register DUN service as well as LAN service */ 118176857Semax regdun = 1; 119176857Semax break; 120176857Semax 121114879Sjulian case 'l': /* PPP label */ 122114879Sjulian label = optarg; 123114879Sjulian break; 124114879Sjulian 125126169Semax case 's': /* server */ 126114879Sjulian server = 1; 127114879Sjulian break; 128114879Sjulian 129132711Semax case 'S': /* Register SP service as well as LAN service */ 130132711Semax regsp = 1; 131132711Semax break; 132132711Semax 133126169Semax case 'u': /* PPP -unit option */ 134126169Semax strtoul(optarg, &ep, 10); 135126169Semax if (*ep != '\0') 136126169Semax usage(); 137126169Semax /* NOT REACHED */ 138126169Semax 139126169Semax unit = optarg; 140126169Semax break; 141126169Semax 142114879Sjulian case 'h': 143114879Sjulian default: 144114879Sjulian usage(); 145114879Sjulian /* NOT REACHED */ 146114879Sjulian } 147114879Sjulian } 148114879Sjulian 149114879Sjulian /* Check if we got everything we wanted */ 150121054Semax if (label == NULL) 151121054Semax errx(1, "Must specify PPP label"); 152114879Sjulian 153121054Semax if (!server) { 154121054Semax if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0) 155121054Semax errx(1, "Must specify server BD_ADDR"); 156121054Semax 157121054Semax /* Check channel, if was not set then obtain it via SDP */ 158121054Semax if (channel == 0 && service != 0) 159121054Semax if (rfcomm_channel_lookup(NULL, &addr, service, 160121054Semax &channel, &s) != 0) 161121054Semax errc(1, s, "Could not obtain RFCOMM channel"); 162121054Semax } 163121054Semax 164121054Semax if (channel <= 0 || channel > 30) 165121054Semax errx(1, "Invalid RFCOMM channel number %d", channel); 166121054Semax 167114879Sjulian openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER); 168114879Sjulian 169188130Semax if (detach && daemon(0, 0) < 0) { 170188130Semax syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)", 171188130Semax strerror(errno), errno); 172188130Semax exit(1); 173114879Sjulian } 174114879Sjulian 175114879Sjulian s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); 176114879Sjulian if (s < 0) { 177114879Sjulian syslog(LOG_ERR, "Could not create socket. %s (%d)", 178114879Sjulian strerror(errno), errno); 179114879Sjulian exit(1); 180114879Sjulian } 181114879Sjulian 182114879Sjulian if (server) { 183126169Semax struct sigaction sa; 184126169Semax void *ss = NULL; 185126169Semax sdp_lan_profile_t lan; 186114879Sjulian 187114879Sjulian /* Install signal handler */ 188114879Sjulian memset(&sa, 0, sizeof(sa)); 189114879Sjulian sa.sa_handler = sighandler; 190114879Sjulian 191114879Sjulian if (sigaction(SIGTERM, &sa, NULL) < 0) { 192114879Sjulian syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)", 193114879Sjulian strerror(errno), errno); 194114879Sjulian exit(1); 195114879Sjulian } 196114879Sjulian 197114879Sjulian if (sigaction(SIGHUP, &sa, NULL) < 0) { 198114879Sjulian syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)", 199114879Sjulian strerror(errno), errno); 200114879Sjulian exit(1); 201114879Sjulian } 202114879Sjulian 203114879Sjulian if (sigaction(SIGINT, &sa, NULL) < 0) { 204114879Sjulian syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)", 205114879Sjulian strerror(errno), errno); 206114879Sjulian exit(1); 207114879Sjulian } 208114879Sjulian 209114879Sjulian sa.sa_handler = SIG_IGN; 210114879Sjulian sa.sa_flags = SA_NOCLDWAIT; 211114879Sjulian 212114879Sjulian if (sigaction(SIGCHLD, &sa, NULL) < 0) { 213114879Sjulian syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)", 214114879Sjulian strerror(errno), errno); 215114879Sjulian exit(1); 216114879Sjulian } 217114879Sjulian 218114879Sjulian /* bind socket and listen for incoming connections */ 219114879Sjulian sock_addr.rfcomm_len = sizeof(sock_addr); 220114879Sjulian sock_addr.rfcomm_family = AF_BLUETOOTH; 221114879Sjulian memcpy(&sock_addr.rfcomm_bdaddr, &addr, 222114879Sjulian sizeof(sock_addr.rfcomm_bdaddr)); 223114879Sjulian sock_addr.rfcomm_channel = channel; 224114879Sjulian 225114879Sjulian if (bind(s, (struct sockaddr *) &sock_addr, 226114879Sjulian sizeof(sock_addr)) < 0) { 227114879Sjulian syslog(LOG_ERR, "Could not bind socket. %s (%d)", 228114879Sjulian strerror(errno), errno); 229114879Sjulian exit(1); 230114879Sjulian } 231114879Sjulian 232114879Sjulian if (listen(s, 10) < 0) { 233114879Sjulian syslog(LOG_ERR, "Could not listen on socket. %s (%d)", 234114879Sjulian strerror(errno), errno); 235114879Sjulian exit(1); 236114879Sjulian } 237114879Sjulian 238126169Semax ss = sdp_open_local(NULL); 239126169Semax if (ss == NULL) { 240126169Semax syslog(LOG_ERR, "Unable to create local SDP session"); 241126169Semax exit(1); 242126169Semax } 243126169Semax 244126169Semax if (sdp_error(ss) != 0) { 245126169Semax syslog(LOG_ERR, "Unable to open local SDP session. " \ 246126169Semax "%s (%d)", strerror(sdp_error(ss)), 247126169Semax sdp_error(ss)); 248126169Semax exit(1); 249126169Semax } 250126169Semax 251126169Semax memset(&lan, 0, sizeof(lan)); 252126169Semax lan.server_channel = channel; 253126169Semax 254126169Semax if (sdp_register_service(ss, 255126169Semax SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP, 256126169Semax &addr, (void *) &lan, sizeof(lan), NULL) != 0) { 257126169Semax syslog(LOG_ERR, "Unable to register LAN service with " \ 258126169Semax "local SDP daemon. %s (%d)", 259126169Semax strerror(sdp_error(ss)), sdp_error(ss)); 260126169Semax exit(1); 261126169Semax } 262132711Semax 263132711Semax /* 264176857Semax * Register DUN (Dial-Up Networking) service on the same 265176857Semax * RFCOMM channel if requested. There is really no good reason 266176857Semax * to not to support this. AT-command exchange can be faked 267176857Semax * with chat script in ppp.conf 268176857Semax */ 269176857Semax 270176857Semax if (regdun) { 271176857Semax sdp_dun_profile_t dun; 272176857Semax 273176857Semax memset(&dun, 0, sizeof(dun)); 274176857Semax dun.server_channel = channel; 275176857Semax 276176857Semax if (sdp_register_service(ss, 277176857Semax SDP_SERVICE_CLASS_DIALUP_NETWORKING, 278176857Semax &addr, (void *) &dun, sizeof(dun), 279176857Semax NULL) != 0) { 280176857Semax syslog(LOG_ERR, "Unable to register DUN " \ 281176857Semax "service with local SDP daemon. " \ 282176857Semax "%s (%d)", strerror(sdp_error(ss)), 283176857Semax sdp_error(ss)); 284176857Semax exit(1); 285176857Semax } 286176857Semax } 287176857Semax 288176857Semax /* 289132711Semax * Register SP (Serial Port) service on the same RFCOMM channel 290132711Semax * if requested. It appears that some cell phones are using so 291132711Semax * called "callback mechanism". In this scenario user is trying 292132711Semax * to connect his cell phone to the Internet, and, user's host 293132711Semax * computer is acting as the gateway server. It seems that it 294132711Semax * is not possible to tell the phone to just connect and start 295132711Semax * using the LAN service. Instead the user's host computer must 296132711Semax * "jump start" the phone by connecting to the phone's SP 297132711Semax * service. What happens next is the phone kills the existing 298132711Semax * connection and opens another connection back to the user's 299132711Semax * host computer. The phone really wants to use LAN service, 300132711Semax * but for whatever reason it looks for SP service on the 301132711Semax * user's host computer. This brain damaged behavior was 302132711Semax * reported for Nokia 6600 and Sony/Ericsson P900. Both phones 303132711Semax * are Symbian-based phones. Perhaps this is a Symbian problem? 304132711Semax */ 305132711Semax 306132711Semax if (regsp) { 307132711Semax sdp_sp_profile_t sp; 308132711Semax 309132711Semax memset(&sp, 0, sizeof(sp)); 310132711Semax sp.server_channel = channel; 311132711Semax 312132711Semax if (sdp_register_service(ss, 313132711Semax SDP_SERVICE_CLASS_SERIAL_PORT, 314132711Semax &addr, (void *) &sp, sizeof(sp), 315132711Semax NULL) != 0) { 316132711Semax syslog(LOG_ERR, "Unable to register SP " \ 317132711Semax "service with local SDP daemon. " \ 318132711Semax "%s (%d)", strerror(sdp_error(ss)), 319132711Semax sdp_error(ss)); 320132711Semax exit(1); 321132711Semax } 322132711Semax } 323126169Semax 324114879Sjulian for (done = 0; !done; ) { 325162494Semax socklen_t len = sizeof(sock_addr); 326162494Semax int s1 = accept(s, (struct sockaddr *) &sock_addr, &len); 327114879Sjulian 328114879Sjulian if (s1 < 0) { 329114879Sjulian syslog(LOG_ERR, "Could not accept connection " \ 330114879Sjulian "on socket. %s (%d)", strerror(errno), 331114879Sjulian errno); 332114879Sjulian exit(1); 333114879Sjulian } 334114879Sjulian 335114879Sjulian pid = fork(); 336114879Sjulian if (pid == (pid_t) -1) { 337114879Sjulian syslog(LOG_ERR, "Could not fork(). %s (%d)", 338114879Sjulian strerror(errno), errno); 339114879Sjulian exit(1); 340114879Sjulian } 341114879Sjulian 342114879Sjulian if (pid == 0) { 343126169Semax sdp_close(ss); 344114879Sjulian close(s); 345114879Sjulian 346114879Sjulian /* Reset signal handler */ 347114879Sjulian memset(&sa, 0, sizeof(sa)); 348114879Sjulian sa.sa_handler = SIG_DFL; 349114879Sjulian 350114879Sjulian sigaction(SIGTERM, &sa, NULL); 351114879Sjulian sigaction(SIGHUP, &sa, NULL); 352114879Sjulian sigaction(SIGINT, &sa, NULL); 353114879Sjulian sigaction(SIGCHLD, &sa, NULL); 354114879Sjulian 355114879Sjulian /* Become daemon */ 356114879Sjulian daemon(0, 0); 357114879Sjulian 358126169Semax /* 359126169Semax * XXX Make sure user does not shoot himself 360126169Semax * in the foot. Do not pass unit option to the 361126169Semax * PPP when operating in the server mode. 362126169Semax */ 363126169Semax 364126169Semax exec_ppp(s1, NULL, label); 365114879Sjulian } else 366114879Sjulian close(s1); 367114879Sjulian } 368114879Sjulian } else { 369114879Sjulian sock_addr.rfcomm_len = sizeof(sock_addr); 370114879Sjulian sock_addr.rfcomm_family = AF_BLUETOOTH; 371114879Sjulian memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY, 372114879Sjulian sizeof(sock_addr.rfcomm_bdaddr)); 373114879Sjulian sock_addr.rfcomm_channel = 0; 374114879Sjulian 375114879Sjulian if (bind(s, (struct sockaddr *) &sock_addr, 376114879Sjulian sizeof(sock_addr)) < 0) { 377114879Sjulian syslog(LOG_ERR, "Could not bind socket. %s (%d)", 378114879Sjulian strerror(errno), errno); 379114879Sjulian exit(1); 380114879Sjulian } 381114879Sjulian 382114879Sjulian memcpy(&sock_addr.rfcomm_bdaddr, &addr, 383114879Sjulian sizeof(sock_addr.rfcomm_bdaddr)); 384114879Sjulian sock_addr.rfcomm_channel = channel; 385114879Sjulian 386114879Sjulian if (connect(s, (struct sockaddr *) &sock_addr, 387114879Sjulian sizeof(sock_addr)) < 0) { 388114879Sjulian syslog(LOG_ERR, "Could not connect socket. %s (%d)", 389114879Sjulian strerror(errno), errno); 390114879Sjulian exit(1); 391114879Sjulian } 392114879Sjulian 393126169Semax exec_ppp(s, unit, label); 394114879Sjulian } 395114879Sjulian 396114879Sjulian exit(0); 397114879Sjulian} /* main */ 398114879Sjulian 399114879Sjulian/* 400126169Semax * Redirects stdin/stdout to s, stderr to /dev/null and exec 401126169Semax * 'ppp -direct -quiet [-unit N] label'. Never returns. 402114879Sjulian */ 403114879Sjulian 404114879Sjulianstatic void 405126169Semaxexec_ppp(int s, char *unit, char *label) 406114879Sjulian{ 407114879Sjulian char ppp[] = "/usr/sbin/ppp"; 408126169Semax char *ppp_args[] = { ppp, "-direct", "-quiet", 409126169Semax NULL, NULL, NULL, NULL }; 410114879Sjulian 411114879Sjulian close(0); 412114879Sjulian if (dup(s) < 0) { 413114879Sjulian syslog(LOG_ERR, "Could not dup(0). %s (%d)", 414114879Sjulian strerror(errno), errno); 415114879Sjulian exit(1); 416114879Sjulian } 417114879Sjulian 418114879Sjulian close(1); 419114879Sjulian if (dup(s) < 0) { 420114879Sjulian syslog(LOG_ERR, "Could not dup(1). %s (%d)", 421114879Sjulian strerror(errno), errno); 422114879Sjulian exit(1); 423114879Sjulian } 424114879Sjulian 425114879Sjulian close(2); 426114879Sjulian open("/dev/null", O_RDWR); 427114879Sjulian 428126169Semax if (unit != NULL) { 429126169Semax ppp_args[3] = "-unit"; 430126169Semax ppp_args[4] = unit; 431126169Semax ppp_args[5] = label; 432126169Semax } else 433126169Semax ppp_args[3] = label; 434126169Semax 435114879Sjulian if (execv(ppp, ppp_args) < 0) { 436126169Semax syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \ 437126169Semax "%s (%d)", ppp, (unit != NULL)? " -unit " : "", 438126169Semax (unit != NULL)? unit : "", label, 439126169Semax strerror(errno), errno); 440114879Sjulian exit(1); 441114879Sjulian } 442114879Sjulian} /* run_ppp */ 443114879Sjulian 444114879Sjulian/* Signal handler */ 445114879Sjulianstatic void 446114879Sjuliansighandler(int s) 447114879Sjulian{ 448114879Sjulian done = 1; 449114879Sjulian} /* sighandler */ 450114879Sjulian 451114879Sjulian/* Display usage and exit */ 452114879Sjulianstatic void 453114879Sjulianusage(void) 454114879Sjulian{ 455114879Sjulian fprintf(stdout, 456114879Sjulian"Usage: %s options\n" \ 457114879Sjulian"Where options are:\n" \ 458133178Semax"\t-a address Address to listen on or connect to (required for client)\n" \ 459114879Sjulian"\t-c Act as a clinet (default)\n" \ 460114879Sjulian"\t-C channel RFCOMM channel to listen on or connect to (required)\n" \ 461114879Sjulian"\t-d Run in foreground\n" \ 462177174Semax"\t-D Register Dial-Up Networking service (server mode only)\n" \ 463114879Sjulian"\t-l label Use PPP label (required)\n" \ 464114879Sjulian"\t-s Act as a server\n" \ 465132711Semax"\t-S Register Serial Port service (server mode only)\n" \ 466126169Semax"\t-u N Tell PPP to operate on /dev/tunN (client mode only)\n" \ 467114879Sjulian"\t-h Display this message\n", RFCOMM_PPPD); 468114879Sjulian 469114879Sjulian exit(255); 470114879Sjulian} /* usage */ 471114879Sjulian 472