static void server_register(void) { sdp_nap_profile_t p; int rv; if (server_ss == NULL) { server_ss = sdp_open_local(control_path); if (server_ss == NULL || sdp_error(server_ss) != 0) { log_err("failed to contact SDP server"); return; } } memset(&p, 0, sizeof(p)); p.psm = l2cap_psm; p.load_factor = server_avail; p.security_description = (l2cap_mode == 0 ? 0x0000 : 0x0001); if (server_handle) rv = sdp_change_service(server_ss, server_handle, (uint8_t *)&p, sizeof(p)); else rv = sdp_register_service(server_ss, service_class, &local_bdaddr, (uint8_t *)&p, sizeof(p), &server_handle); if (rv != 0) { errno = sdp_error(server_ss); log_err("%s: %m", service_name); exit(EXIT_FAILURE); } }
/* Execute commands */ static int do_sdp_command(bdaddr_p bdaddr, char const *control, int local, int argc, char **argv) { char *cmd = argv[0]; struct sdp_command *c = NULL; void *xs = NULL; int e, help; help = 0; if (strcasecmp(cmd, "help") == 0) { argc --; argv ++; if (argc <= 0) { fprintf(stdout, "Supported commands:\n"); print_sdp_command(sdp_commands); fprintf(stdout, "\nFor more information use " \ "'help command'\n"); return (OK); } help = 1; cmd = argv[0]; } c = find_sdp_command(cmd, sdp_commands); if (c == NULL) { fprintf(stdout, "Unknown command: \"%s\"\n", cmd); return (ERROR); } if (!help) { if (!local) { if (memcmp(bdaddr, NG_HCI_BDADDR_ANY, sizeof(*bdaddr)) == 0) usage(); xs = sdp_open(NG_HCI_BDADDR_ANY, bdaddr); } else xs = sdp_open_local(control); if (xs == NULL) errx(1, "Could not create SDP session object"); if (sdp_error(xs) == 0) e = (c->handler)(xs, -- argc, ++ argv); else e = ERROR; } else e = USAGE; switch (e) { case OK: case FAILED: break; case ERROR: fprintf(stdout, "Could not execute command \"%s\". %s\n", cmd, strerror(sdp_error(xs))); break; case USAGE: fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description); break; default: assert(0); break; } sdp_close(xs); return (e); } /* do_sdp_command */
/* Main */ int main(int argc, char *argv[]) { struct sockaddr_rfcomm sock_addr; char *label = NULL, *unit = NULL, *ep = NULL; bdaddr_t addr; int s, channel, detach, server, service, regdun, regsp; pid_t pid; memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)); channel = 0; detach = 1; server = 0; service = 0; regdun = 0; regsp = 0; /* Parse command line arguments */ while ((s = getopt(argc, argv, "a:cC:dDhl:sSu:")) != -1) { switch (s) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &addr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&addr, he->h_addr, sizeof(addr)); } break; case 'c': /* client */ server = 0; break; case 'C': /* RFCOMM channel */ channel = strtoul(optarg, &ep, 10); if (*ep != '\0') { channel = 0; switch (tolower(optarg[0])) { case 'd': /* DialUp Networking */ service = SDP_SERVICE_CLASS_DIALUP_NETWORKING; break; case 'l': /* LAN Access Using PPP */ service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP; break; } } break; case 'd': /* do not detach */ detach = 0; break; case 'D': /* Register DUN service as well as LAN service */ regdun = 1; break; case 'l': /* PPP label */ label = optarg; break; case 's': /* server */ server = 1; break; case 'S': /* Register SP service as well as LAN service */ regsp = 1; break; case 'u': /* PPP -unit option */ strtoul(optarg, &ep, 10); if (*ep != '\0') usage(); /* NOT REACHED */ unit = optarg; break; case 'h': default: usage(); /* NOT REACHED */ } } /* Check if we got everything we wanted */ if (label == NULL) errx(1, "Must specify PPP label"); if (!server) { if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0) errx(1, "Must specify server BD_ADDR"); /* Check channel, if was not set then obtain it via SDP */ if (channel == 0 && service != 0) if (rfcomm_channel_lookup(NULL, &addr, service, &channel, &s) != 0) errc(1, s, "Could not obtain RFCOMM channel"); } if (channel <= 0 || channel > 30) errx(1, "Invalid RFCOMM channel number %d", channel); openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER); if (detach && daemon(0, 0) < 0) { syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)", strerror(errno), errno); exit(1); } s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); if (s < 0) { syslog(LOG_ERR, "Could not create socket. %s (%d)", strerror(errno), errno); exit(1); } if (server) { struct sigaction sa; void *ss = NULL; sdp_lan_profile_t lan; /* Install signal handler */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sighandler; if (sigaction(SIGTERM, &sa, NULL) < 0) { syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)", strerror(errno), errno); exit(1); } if (sigaction(SIGHUP, &sa, NULL) < 0) { syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)", strerror(errno), errno); exit(1); } if (sigaction(SIGINT, &sa, NULL) < 0) { syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)", strerror(errno), errno); exit(1); } sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL) < 0) { syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)", strerror(errno), errno); exit(1); } /* bind socket and listen for incoming connections */ sock_addr.rfcomm_len = sizeof(sock_addr); sock_addr.rfcomm_family = AF_BLUETOOTH; memcpy(&sock_addr.rfcomm_bdaddr, &addr, sizeof(sock_addr.rfcomm_bdaddr)); sock_addr.rfcomm_channel = channel; if (bind(s, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { syslog(LOG_ERR, "Could not bind socket. %s (%d)", strerror(errno), errno); exit(1); } if (listen(s, 10) < 0) { syslog(LOG_ERR, "Could not listen on socket. %s (%d)", strerror(errno), errno); exit(1); } ss = sdp_open_local(NULL); if (ss == NULL) { syslog(LOG_ERR, "Unable to create local SDP session"); exit(1); } if (sdp_error(ss) != 0) { syslog(LOG_ERR, "Unable to open local SDP session. " \ "%s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); exit(1); } memset(&lan, 0, sizeof(lan)); lan.server_channel = channel; if (sdp_register_service(ss, SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP, &addr, (void *) &lan, sizeof(lan), NULL) != 0) { syslog(LOG_ERR, "Unable to register LAN service with " \ "local SDP daemon. %s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); exit(1); } /* * Register DUN (Dial-Up Networking) service on the same * RFCOMM channel if requested. There is really no good reason * to not to support this. AT-command exchange can be faked * with chat script in ppp.conf */ if (regdun) { sdp_dun_profile_t dun; memset(&dun, 0, sizeof(dun)); dun.server_channel = channel; if (sdp_register_service(ss, SDP_SERVICE_CLASS_DIALUP_NETWORKING, &addr, (void *) &dun, sizeof(dun), NULL) != 0) { syslog(LOG_ERR, "Unable to register DUN " \ "service with local SDP daemon. " \ "%s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); exit(1); } } /* * Register SP (Serial Port) service on the same RFCOMM channel * if requested. It appears that some cell phones are using so * called "callback mechanism". In this scenario user is trying * to connect his cell phone to the Internet, and, user's host * computer is acting as the gateway server. It seems that it * is not possible to tell the phone to just connect and start * using the LAN service. Instead the user's host computer must * "jump start" the phone by connecting to the phone's SP * service. What happens next is the phone kills the existing * connection and opens another connection back to the user's * host computer. The phone really wants to use LAN service, * but for whatever reason it looks for SP service on the * user's host computer. This brain damaged behavior was * reported for Nokia 6600 and Sony/Ericsson P900. Both phones * are Symbian-based phones. Perhaps this is a Symbian problem? */ if (regsp) { sdp_sp_profile_t sp; memset(&sp, 0, sizeof(sp)); sp.server_channel = channel; if (sdp_register_service(ss, SDP_SERVICE_CLASS_SERIAL_PORT, &addr, (void *) &sp, sizeof(sp), NULL) != 0) { syslog(LOG_ERR, "Unable to register SP " \ "service with local SDP daemon. " \ "%s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); exit(1); } } for (done = 0; !done; ) { socklen_t len = sizeof(sock_addr); int s1 = accept(s, (struct sockaddr *) &sock_addr, &len); if (s1 < 0) { syslog(LOG_ERR, "Could not accept connection " \ "on socket. %s (%d)", strerror(errno), errno); exit(1); } pid = fork(); if (pid == (pid_t) -1) { syslog(LOG_ERR, "Could not fork(). %s (%d)", strerror(errno), errno); exit(1); } if (pid == 0) { sdp_close(ss); close(s); /* Reset signal handler */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_DFL; sigaction(SIGTERM, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); /* Become daemon */ daemon(0, 0); /* * XXX Make sure user does not shoot himself * in the foot. Do not pass unit option to the * PPP when operating in the server mode. */ exec_ppp(s1, NULL, label); } else close(s1); } } else { sock_addr.rfcomm_len = sizeof(sock_addr); sock_addr.rfcomm_family = AF_BLUETOOTH; memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY, sizeof(sock_addr.rfcomm_bdaddr)); sock_addr.rfcomm_channel = 0; if (bind(s, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { syslog(LOG_ERR, "Could not bind socket. %s (%d)", strerror(errno), errno); exit(1); } memcpy(&sock_addr.rfcomm_bdaddr, &addr, sizeof(sock_addr.rfcomm_bdaddr)); sock_addr.rfcomm_channel = channel; if (connect(s, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { syslog(LOG_ERR, "Could not connect socket. %s (%d)", strerror(errno), errno); exit(1); } exec_ppp(s, unit, label); } exit(0); } /* main */
/* Main */ int main(int argc, char *argv[]) { struct sigaction sa; struct sockaddr_rfcomm ra; bdaddr_t addr; int n, background, channel, service, s, amaster, aslave, fd, doserver, dopty; fd_set rfd; char *tty = NULL, *ep = NULL, buf[SPPD_BUFFER_SIZE]; memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)); background = channel = 0; service = SDP_SERVICE_CLASS_SERIAL_PORT; doserver = 0; dopty = 0; /* Parse command line options */ while ((n = getopt(argc, argv, "a:bc:thS")) != -1) { switch (n) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &addr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(1, "%s: %s", optarg, hstrerror(h_errno)); memcpy(&addr, he->h_addr, sizeof(addr)); } break; case 'c': /* RFCOMM channel */ channel = strtoul(optarg, &ep, 10); if (*ep != '\0') { channel = 0; switch (tolower(optarg[0])) { case 'd': /* DialUp Networking */ service = SDP_SERVICE_CLASS_DIALUP_NETWORKING; break; case 'f': /* Fax */ service = SDP_SERVICE_CLASS_FAX; break; case 'l': /* LAN */ service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP; break; case 's': /* Serial Port */ service = SDP_SERVICE_CLASS_SERIAL_PORT; break; default: errx(1, "Unknown service name: %s", optarg); /* NOT REACHED */ } } break; case 'b': /* Run in background */ background = 1; break; case 't': /* Open pseudo TTY */ dopty = 1; break; case 'S': doserver = 1; break; case 'h': default: usage(); /* NOT REACHED */ } } /* Check if we have everything we need */ if (!doserver && memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0) usage(); /* NOT REACHED */ /* Set signal handlers */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sppd_sighandler; if (sigaction(SIGTERM, &sa, NULL) < 0) err(1, "Could not sigaction(SIGTERM)"); if (sigaction(SIGHUP, &sa, NULL) < 0) err(1, "Could not sigaction(SIGHUP)"); if (sigaction(SIGINT, &sa, NULL) < 0) err(1, "Could not sigaction(SIGINT)"); sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL) < 0) err(1, "Could not sigaction(SIGCHLD)"); /* Open TTYs */ if (dopty) { if (sppd_ttys_open(&tty, &amaster, &aslave) < 0) exit(1); fd = amaster; } else { if (background) usage(); amaster = STDIN_FILENO; fd = STDOUT_FILENO; } /* Open RFCOMM connection */ if (doserver) { struct sockaddr_rfcomm ma; bdaddr_t bt_addr_any; sdp_sp_profile_t sp; void *ss; uint32_t sdp_handle; int acceptsock, aaddrlen; acceptsock = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); if (acceptsock < 0) err(1, "Could not create socket"); memcpy(&bt_addr_any, NG_HCI_BDADDR_ANY, sizeof(bt_addr_any)); memset(&ma, 0, sizeof(ma)); ma.rfcomm_len = sizeof(ma); ma.rfcomm_family = AF_BLUETOOTH; memcpy(&ma.rfcomm_bdaddr, &bt_addr_any, sizeof(bt_addr_any)); ma.rfcomm_channel = channel; if (bind(acceptsock, (struct sockaddr *)&ma, sizeof(ma)) < 0) err(1, "Could not bind socket on channel %d", channel); if (listen(acceptsock, 10) != 0) err(1, "Could not listen on socket"); aaddrlen = sizeof(ma); if (getsockname(acceptsock, (struct sockaddr *)&ma, &aaddrlen) < 0) err(1, "Could not get socket name"); channel = ma.rfcomm_channel; ss = sdp_open_local(NULL); if (ss == NULL) errx(1, "Unable to create local SDP session"); if (sdp_error(ss) != 0) errx(1, "Unable to open local SDP session. %s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); memset(&sp, 0, sizeof(sp)); sp.server_channel = channel; if (sdp_register_service(ss, SDP_SERVICE_CLASS_SERIAL_PORT, &bt_addr_any, (void *)&sp, sizeof(sp), &sdp_handle) != 0) { errx(1, "Unable to register LAN service with " "local SDP daemon. %s (%d)", strerror(sdp_error(ss)), sdp_error(ss)); } s = -1; while (s < 0) { aaddrlen = sizeof(ra); s = accept(acceptsock, (struct sockaddr *)&ra, &aaddrlen); if (s < 0) err(1, "Unable to accept()"); if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) && memcmp(&addr, &ra.rfcomm_bdaddr, sizeof(addr))) { warnx("Connect from wrong client"); close(s); s = -1; } } sdp_unregister_service(ss, sdp_handle); sdp_close(ss); close(acceptsock); } else { /* Check channel, if was not set then obtain it via SDP */ if (channel == 0 && service != 0) if (rfcomm_channel_lookup(NULL, &addr, service, &channel, &n) != 0) errc(1, n, "Could not obtain RFCOMM channel"); if (channel <= 0 || channel > 30) errx(1, "Invalid RFCOMM channel number %d", channel); s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM); if (s < 0) err(1, "Could not create socket"); memset(&ra, 0, sizeof(ra)); ra.rfcomm_len = sizeof(ra); ra.rfcomm_family = AF_BLUETOOTH; if (bind(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) err(1, "Could not bind socket"); memcpy(&ra.rfcomm_bdaddr, &addr, sizeof(ra.rfcomm_bdaddr)); ra.rfcomm_channel = channel; if (connect(s, (struct sockaddr *) &ra, sizeof(ra)) < 0) err(1, "Could not connect socket"); } /* Became daemon if required */ if (background && daemon(0, 0) < 0) err(1, "Could not daemon()"); openlog(SPPD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "Starting on %s...", (tty != NULL)? tty : "stdin/stdout"); /* Print used tty on stdout for wrappers to pick up */ if (!background) fprintf(stdout, "%s\n", tty); for (done = 0; !done; ) { FD_ZERO(&rfd); FD_SET(amaster, &rfd); FD_SET(s, &rfd); n = select(max(amaster, s) + 1, &rfd, NULL, NULL, NULL); if (n < 0) { if (errno == EINTR) continue; syslog(LOG_ERR, "Could not select(). %s", strerror(errno)); exit(1); } if (n == 0) continue; if (FD_ISSET(amaster, &rfd)) { n = sppd_read(amaster, buf, sizeof(buf)); if (n < 0) { syslog(LOG_ERR, "Could not read master pty, " \ "fd=%d. %s", amaster, strerror(errno)); exit(1); } if (n == 0) break; /* XXX */ if (sppd_write(s, buf, n) < 0) { syslog(LOG_ERR, "Could not write to socket, " \ "fd=%d, size=%d. %s", s, n, strerror(errno)); exit(1); } } if (FD_ISSET(s, &rfd)) { n = sppd_read(s, buf, sizeof(buf)); if (n < 0) { syslog(LOG_ERR, "Could not read socket, " \ "fd=%d. %s", s, strerror(errno)); exit(1); } if (n == 0) break; if (sppd_write(fd, buf, n) < 0) { syslog(LOG_ERR, "Could not write to master " \ "pty, fd=%d, size=%d. %s", fd, n, strerror(errno)); exit(1); } } } syslog(LOG_INFO, "Completed on %s", (tty != NULL)? tty : "stdin/stdout"); closelog(); close(s); if (tty != NULL) { close(aslave); close(amaster); } return (0); }