rfcomm_pppd.c revision 177174
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: head/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c 177174 2008-03-14 16:29:25Z emax $ 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 169114879Sjulian if (detach) { 170114879Sjulian pid = fork(); 171114879Sjulian if (pid == (pid_t) -1) { 172114879Sjulian syslog(LOG_ERR, "Could not fork(). %s (%d)", 173114879Sjulian strerror(errno), errno); 174114879Sjulian exit(1); 175114879Sjulian } 176114879Sjulian 177114879Sjulian if (pid != 0) 178114879Sjulian exit(0); 179114879Sjulian 180114879Sjulian if (daemon(0, 0) < 0) { 181114879Sjulian syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)", 182114879Sjulian strerror(errno), errno); 183114879Sjulian exit(1); 184114879Sjulian } 185114879Sjulian } 186114879Sjulian 187114879Sjulian s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); 188114879Sjulian if (s < 0) { 189114879Sjulian syslog(LOG_ERR, "Could not create socket. %s (%d)", 190114879Sjulian strerror(errno), errno); 191114879Sjulian exit(1); 192114879Sjulian } 193114879Sjulian 194114879Sjulian if (server) { 195126169Semax struct sigaction sa; 196126169Semax void *ss = NULL; 197126169Semax sdp_lan_profile_t lan; 198114879Sjulian 199114879Sjulian /* Install signal handler */ 200114879Sjulian memset(&sa, 0, sizeof(sa)); 201114879Sjulian sa.sa_handler = sighandler; 202114879Sjulian 203114879Sjulian if (sigaction(SIGTERM, &sa, NULL) < 0) { 204114879Sjulian syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)", 205114879Sjulian strerror(errno), errno); 206114879Sjulian exit(1); 207114879Sjulian } 208114879Sjulian 209114879Sjulian if (sigaction(SIGHUP, &sa, NULL) < 0) { 210114879Sjulian syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)", 211114879Sjulian strerror(errno), errno); 212114879Sjulian exit(1); 213114879Sjulian } 214114879Sjulian 215114879Sjulian if (sigaction(SIGINT, &sa, NULL) < 0) { 216114879Sjulian syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)", 217114879Sjulian strerror(errno), errno); 218114879Sjulian exit(1); 219114879Sjulian } 220114879Sjulian 221114879Sjulian sa.sa_handler = SIG_IGN; 222114879Sjulian sa.sa_flags = SA_NOCLDWAIT; 223114879Sjulian 224114879Sjulian if (sigaction(SIGCHLD, &sa, NULL) < 0) { 225114879Sjulian syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)", 226114879Sjulian strerror(errno), errno); 227114879Sjulian exit(1); 228114879Sjulian } 229114879Sjulian 230114879Sjulian /* bind socket and listen for incoming connections */ 231114879Sjulian sock_addr.rfcomm_len = sizeof(sock_addr); 232114879Sjulian sock_addr.rfcomm_family = AF_BLUETOOTH; 233114879Sjulian memcpy(&sock_addr.rfcomm_bdaddr, &addr, 234114879Sjulian sizeof(sock_addr.rfcomm_bdaddr)); 235114879Sjulian sock_addr.rfcomm_channel = channel; 236114879Sjulian 237114879Sjulian if (bind(s, (struct sockaddr *) &sock_addr, 238114879Sjulian sizeof(sock_addr)) < 0) { 239114879Sjulian syslog(LOG_ERR, "Could not bind socket. %s (%d)", 240114879Sjulian strerror(errno), errno); 241114879Sjulian exit(1); 242114879Sjulian } 243114879Sjulian 244114879Sjulian if (listen(s, 10) < 0) { 245114879Sjulian syslog(LOG_ERR, "Could not listen on socket. %s (%d)", 246114879Sjulian strerror(errno), errno); 247114879Sjulian exit(1); 248114879Sjulian } 249114879Sjulian 250126169Semax ss = sdp_open_local(NULL); 251126169Semax if (ss == NULL) { 252126169Semax syslog(LOG_ERR, "Unable to create local SDP session"); 253126169Semax exit(1); 254126169Semax } 255126169Semax 256126169Semax if (sdp_error(ss) != 0) { 257126169Semax syslog(LOG_ERR, "Unable to open local SDP session. " \ 258126169Semax "%s (%d)", strerror(sdp_error(ss)), 259126169Semax sdp_error(ss)); 260126169Semax exit(1); 261126169Semax } 262126169Semax 263126169Semax memset(&lan, 0, sizeof(lan)); 264126169Semax lan.server_channel = channel; 265126169Semax 266126169Semax if (sdp_register_service(ss, 267126169Semax SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP, 268126169Semax &addr, (void *) &lan, sizeof(lan), NULL) != 0) { 269126169Semax syslog(LOG_ERR, "Unable to register LAN service with " \ 270126169Semax "local SDP daemon. %s (%d)", 271126169Semax strerror(sdp_error(ss)), sdp_error(ss)); 272126169Semax exit(1); 273126169Semax } 274132711Semax 275132711Semax /* 276176857Semax * Register DUN (Dial-Up Networking) service on the same 277176857Semax * RFCOMM channel if requested. There is really no good reason 278176857Semax * to not to support this. AT-command exchange can be faked 279176857Semax * with chat script in ppp.conf 280176857Semax */ 281176857Semax 282176857Semax if (regdun) { 283176857Semax sdp_dun_profile_t dun; 284176857Semax 285176857Semax memset(&dun, 0, sizeof(dun)); 286176857Semax dun.server_channel = channel; 287176857Semax 288176857Semax if (sdp_register_service(ss, 289176857Semax SDP_SERVICE_CLASS_DIALUP_NETWORKING, 290176857Semax &addr, (void *) &dun, sizeof(dun), 291176857Semax NULL) != 0) { 292176857Semax syslog(LOG_ERR, "Unable to register DUN " \ 293176857Semax "service with local SDP daemon. " \ 294176857Semax "%s (%d)", strerror(sdp_error(ss)), 295176857Semax sdp_error(ss)); 296176857Semax exit(1); 297176857Semax } 298176857Semax } 299176857Semax 300176857Semax /* 301132711Semax * Register SP (Serial Port) service on the same RFCOMM channel 302132711Semax * if requested. It appears that some cell phones are using so 303132711Semax * called "callback mechanism". In this scenario user is trying 304132711Semax * to connect his cell phone to the Internet, and, user's host 305132711Semax * computer is acting as the gateway server. It seems that it 306132711Semax * is not possible to tell the phone to just connect and start 307132711Semax * using the LAN service. Instead the user's host computer must 308132711Semax * "jump start" the phone by connecting to the phone's SP 309132711Semax * service. What happens next is the phone kills the existing 310132711Semax * connection and opens another connection back to the user's 311132711Semax * host computer. The phone really wants to use LAN service, 312132711Semax * but for whatever reason it looks for SP service on the 313132711Semax * user's host computer. This brain damaged behavior was 314132711Semax * reported for Nokia 6600 and Sony/Ericsson P900. Both phones 315132711Semax * are Symbian-based phones. Perhaps this is a Symbian problem? 316132711Semax */ 317132711Semax 318132711Semax if (regsp) { 319132711Semax sdp_sp_profile_t sp; 320132711Semax 321132711Semax memset(&sp, 0, sizeof(sp)); 322132711Semax sp.server_channel = channel; 323132711Semax 324132711Semax if (sdp_register_service(ss, 325132711Semax SDP_SERVICE_CLASS_SERIAL_PORT, 326132711Semax &addr, (void *) &sp, sizeof(sp), 327132711Semax NULL) != 0) { 328132711Semax syslog(LOG_ERR, "Unable to register SP " \ 329132711Semax "service with local SDP daemon. " \ 330132711Semax "%s (%d)", strerror(sdp_error(ss)), 331132711Semax sdp_error(ss)); 332132711Semax exit(1); 333132711Semax } 334132711Semax } 335126169Semax 336114879Sjulian for (done = 0; !done; ) { 337162494Semax socklen_t len = sizeof(sock_addr); 338162494Semax int s1 = accept(s, (struct sockaddr *) &sock_addr, &len); 339114879Sjulian 340114879Sjulian if (s1 < 0) { 341114879Sjulian syslog(LOG_ERR, "Could not accept connection " \ 342114879Sjulian "on socket. %s (%d)", strerror(errno), 343114879Sjulian errno); 344114879Sjulian exit(1); 345114879Sjulian } 346114879Sjulian 347114879Sjulian pid = fork(); 348114879Sjulian if (pid == (pid_t) -1) { 349114879Sjulian syslog(LOG_ERR, "Could not fork(). %s (%d)", 350114879Sjulian strerror(errno), errno); 351114879Sjulian exit(1); 352114879Sjulian } 353114879Sjulian 354114879Sjulian if (pid == 0) { 355126169Semax sdp_close(ss); 356114879Sjulian close(s); 357114879Sjulian 358114879Sjulian /* Reset signal handler */ 359114879Sjulian memset(&sa, 0, sizeof(sa)); 360114879Sjulian sa.sa_handler = SIG_DFL; 361114879Sjulian 362114879Sjulian sigaction(SIGTERM, &sa, NULL); 363114879Sjulian sigaction(SIGHUP, &sa, NULL); 364114879Sjulian sigaction(SIGINT, &sa, NULL); 365114879Sjulian sigaction(SIGCHLD, &sa, NULL); 366114879Sjulian 367114879Sjulian /* Become daemon */ 368114879Sjulian daemon(0, 0); 369114879Sjulian 370126169Semax /* 371126169Semax * XXX Make sure user does not shoot himself 372126169Semax * in the foot. Do not pass unit option to the 373126169Semax * PPP when operating in the server mode. 374126169Semax */ 375126169Semax 376126169Semax exec_ppp(s1, NULL, label); 377114879Sjulian } else 378114879Sjulian close(s1); 379114879Sjulian } 380114879Sjulian } else { 381114879Sjulian sock_addr.rfcomm_len = sizeof(sock_addr); 382114879Sjulian sock_addr.rfcomm_family = AF_BLUETOOTH; 383114879Sjulian memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY, 384114879Sjulian sizeof(sock_addr.rfcomm_bdaddr)); 385114879Sjulian sock_addr.rfcomm_channel = 0; 386114879Sjulian 387114879Sjulian if (bind(s, (struct sockaddr *) &sock_addr, 388114879Sjulian sizeof(sock_addr)) < 0) { 389114879Sjulian syslog(LOG_ERR, "Could not bind socket. %s (%d)", 390114879Sjulian strerror(errno), errno); 391114879Sjulian exit(1); 392114879Sjulian } 393114879Sjulian 394114879Sjulian memcpy(&sock_addr.rfcomm_bdaddr, &addr, 395114879Sjulian sizeof(sock_addr.rfcomm_bdaddr)); 396114879Sjulian sock_addr.rfcomm_channel = channel; 397114879Sjulian 398114879Sjulian if (connect(s, (struct sockaddr *) &sock_addr, 399114879Sjulian sizeof(sock_addr)) < 0) { 400114879Sjulian syslog(LOG_ERR, "Could not connect socket. %s (%d)", 401114879Sjulian strerror(errno), errno); 402114879Sjulian exit(1); 403114879Sjulian } 404114879Sjulian 405126169Semax exec_ppp(s, unit, label); 406114879Sjulian } 407114879Sjulian 408114879Sjulian exit(0); 409114879Sjulian} /* main */ 410114879Sjulian 411114879Sjulian/* 412126169Semax * Redirects stdin/stdout to s, stderr to /dev/null and exec 413126169Semax * 'ppp -direct -quiet [-unit N] label'. Never returns. 414114879Sjulian */ 415114879Sjulian 416114879Sjulianstatic void 417126169Semaxexec_ppp(int s, char *unit, char *label) 418114879Sjulian{ 419114879Sjulian char ppp[] = "/usr/sbin/ppp"; 420126169Semax char *ppp_args[] = { ppp, "-direct", "-quiet", 421126169Semax NULL, NULL, NULL, NULL }; 422114879Sjulian 423114879Sjulian close(0); 424114879Sjulian if (dup(s) < 0) { 425114879Sjulian syslog(LOG_ERR, "Could not dup(0). %s (%d)", 426114879Sjulian strerror(errno), errno); 427114879Sjulian exit(1); 428114879Sjulian } 429114879Sjulian 430114879Sjulian close(1); 431114879Sjulian if (dup(s) < 0) { 432114879Sjulian syslog(LOG_ERR, "Could not dup(1). %s (%d)", 433114879Sjulian strerror(errno), errno); 434114879Sjulian exit(1); 435114879Sjulian } 436114879Sjulian 437114879Sjulian close(2); 438114879Sjulian open("/dev/null", O_RDWR); 439114879Sjulian 440126169Semax if (unit != NULL) { 441126169Semax ppp_args[3] = "-unit"; 442126169Semax ppp_args[4] = unit; 443126169Semax ppp_args[5] = label; 444126169Semax } else 445126169Semax ppp_args[3] = label; 446126169Semax 447114879Sjulian if (execv(ppp, ppp_args) < 0) { 448126169Semax syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \ 449126169Semax "%s (%d)", ppp, (unit != NULL)? " -unit " : "", 450126169Semax (unit != NULL)? unit : "", label, 451126169Semax strerror(errno), errno); 452114879Sjulian exit(1); 453114879Sjulian } 454114879Sjulian} /* run_ppp */ 455114879Sjulian 456114879Sjulian/* Signal handler */ 457114879Sjulianstatic void 458114879Sjuliansighandler(int s) 459114879Sjulian{ 460114879Sjulian done = 1; 461114879Sjulian} /* sighandler */ 462114879Sjulian 463114879Sjulian/* Display usage and exit */ 464114879Sjulianstatic void 465114879Sjulianusage(void) 466114879Sjulian{ 467114879Sjulian fprintf(stdout, 468114879Sjulian"Usage: %s options\n" \ 469114879Sjulian"Where options are:\n" \ 470133178Semax"\t-a address Address to listen on or connect to (required for client)\n" \ 471114879Sjulian"\t-c Act as a clinet (default)\n" \ 472114879Sjulian"\t-C channel RFCOMM channel to listen on or connect to (required)\n" \ 473114879Sjulian"\t-d Run in foreground\n" \ 474177174Semax"\t-D Register Dial-Up Networking service (server mode only)\n" \ 475114879Sjulian"\t-l label Use PPP label (required)\n" \ 476114879Sjulian"\t-s Act as a server\n" \ 477132711Semax"\t-S Register Serial Port service (server mode only)\n" \ 478126169Semax"\t-u N Tell PPP to operate on /dev/tunN (client mode only)\n" \ 479114879Sjulian"\t-h Display this message\n", RFCOMM_PPPD); 480114879Sjulian 481114879Sjulian exit(255); 482114879Sjulian} /* usage */ 483114879Sjulian 484