Example #1
0
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);
	}
}
Example #2
0
static int
bt_query(struct l2cap_info *info, uint16_t service_class)
{
	sdp_attr_t values[BT_NUM_VALUES];
	uint8_t buffer[BT_NUM_VALUES][BT_BUF_SIZE];
	void *ss;
	int psm = -1;
	int n;

	memset(buffer, 0, sizeof(buffer));
	memset(values, 0, sizeof(values));

	ss = sdp_open(&info->laddr, &info->raddr);
	if (ss == NULL || sdp_error(ss) != 0) {
		DPRINTF("Could not open SDP\n");
		return (psm);
	}
	/* Initialize attribute values array */
	for (n = 0; n != BT_NUM_VALUES; n++) {
		values[n].flags = SDP_ATTR_INVALID;
		values[n].vlen = BT_BUF_SIZE;
		values[n].value = buffer[n];
	}

	/* Do SDP Service Search Attribute Request */
	n = sdp_search(ss, 1, &service_class, 1, bt_attrs, BT_NUM_VALUES, values);
	if (n != 0) {
		DPRINTF("SDP search failed\n");
		goto done;
	}
	/* Print attributes values */
	for (n = 0; n != BT_NUM_VALUES; n++) {
		if (values[n].flags != SDP_ATTR_OK)
			break;
		if (values[n].attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST)
			continue;
		psm = bt_find_psm(values[n].value, values[n].value + values[n].vlen);
		if (psm > -1)
			break;
	}
done:
	sdp_close(ss);
	return (psm);
}
Example #3
0
int cmd_browse(struct command *cmd)
{
	int	status = -1;
	uuid_t 	uuid;

	if (!__argv[optind]) {
		printf("server address is not given\n");
		return -1;
	}
	status = __sdp_connect(__argv[optind]);
	if (status) {
		printf("Connection to SDP server failed: %s\n", sdp_error(status));
		return status;
	}
	sdp_val2uuid16(&uuid, SDP_UUID_PUBLIC_BROWSE_GROUP);
	status = svcBrowse(&uuid);
	__sdp_close();
	return status;
}
Example #4
0
/* 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 */
Example #5
0
int cmd_pan_connect(struct command *cmd)
{
	int		err, role = AFFIX_PAN_NAP;
	BD_ADDR		bda;
	struct sockaddr_affix	saddr;

	__argv = &__argv[optind];

	if (!*__argv) {
		printf("Address missing\n");
		print_usage(cmd);
		return 1;
	}
	err = btdev_get_bda(&bda, *__argv);
	if (err) {
		printf("Incorrect address given\n");
		return 1;
	}
	if (*(++__argv)) {
		/* role */
		role = str2role(*__argv);
		if (!role) {
			fprintf(stderr, "invalid role: %s\n", *__argv);
			return 1;
		}
	}

	saddr.family = PF_AFFIX;
	saddr.bda = bda;
	saddr.devnum = HCIDEV_ANY;

#if defined(CONFIG_AFFIX_SDP)
	if (sdpmode) {
		uint16_t	ServiceID;
		uint16_t	count;
		slist_t		*searchList = NULL;
		slist_t		*attrList = NULL;
		slist_t		*svcList = NULL;
		sdpsvc_t	*svcRec;

		if (role == AFFIX_PAN_NAP)
			ServiceID = SDP_UUID_NAP;
		else if (role == AFFIX_PAN_GN)
			ServiceID = SDP_UUID_GN;
		else
			ServiceID = SDP_UUID_PANU;

		printf("Connecting to host %s ...\n", bda2str(&bda));

		/* search for service ServiceID */
		s_list_append_uuid16(&searchList, ServiceID);
		/* set attributes to find */
		s_list_append_uint(&attrList, SDP_ATTR_SERVICE_RECORD_HANDLE);
		s_list_append_uint(&attrList, SDP_ATTR_PROTO_DESC_LIST);
		err = __sdp_search_attr_req(&saddr, searchList, IndividualAttributes, attrList, 0xffff, &svcList, &count);
		s_list_destroy(&searchList);
		s_list_free(&attrList);
		if (err) {
			fprintf(stderr, "%s\n", sdp_error(err));
			return -1;
		}
		if (count == 0) {
			printf("services [%s] not found\n", val2str(sdp_service_map, ServiceID));
			return -1;
		}
		printf("Service found\n");
		svcRec = s_list_dequeue(&svcList);
		sdp_free_svc(svcRec);
		sdp_free_svclist(&svcList);
	}
#endif
	saddr.devnum = hci_devnum(btdev);
	err = affix_pan_connect(&saddr, role);
	if (err) {
		if (errno == EINVAL)
			fprintf(stderr, "Connection not allowed in this role.\n");
		else
			fprintf(stderr, "failed.\n");
	} else 
		fprintf(stderr, "connected.\n");
	return 0;
}
Example #6
0
static int find_service_channel(bdaddr_t *adapter, bdaddr_t *device, int only_gnapplet, uint16_t svclass_id)
{
	int i, channel = -1;
	char name[64];
	void *ss = NULL;
	uint32_t attrs[] = {
		SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
			SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
		SDP_ATTR_RANGE( SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
			SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET),
	};
	/* Buffer for the attributes */
	static uint8_t          buffer[NRECS * attrs_len][BSIZE];
	/* SDP attributes */
	static sdp_attr_t       values[NRECS * attrs_len];

	/* Initialize attribute values array */
	for (i = 0; i < values_len; i ++) {
		values[i].flags = SDP_ATTR_INVALID;
		values[i].attr = 0;
		values[i].vlen = BSIZE;
		values[i].value = buffer[i];
	}

	if ((ss = sdp_open(adapter, device)) == NULL)
		return -1;

	if (sdp_error(ss) != 0)
		goto end;

	if (sdp_search(ss, 1, &svclass_id, attrs_len, attrs, values_len, values) != 0)
		goto end;

	for (i = 0; i < values_len; i++) {
		union {
			uint8_t		uint8;
			uint16_t	uint16;
			uint32_t	uint32;
			uint64_t	uint64;
			int128_t	int128;
		} value;
		uint8_t *start, *end;
		uint32_t type, len;

		if (values[i].flags != SDP_ATTR_OK)
			break;

		start = values[i].value;
		end = values[i].value + values[i].vlen;

		switch (values[i].attr) {
		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
			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:
				goto end;
				break;
			}

			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:
				goto end;
			}

			while (start < end) {
				SDP_GET8(type, start);
				switch (type) {
				case SDP_DATA_UUID16:
					SDP_GET16(value.uint16, start);
					break;

				case SDP_DATA_UUID32:
					SDP_GET32(value.uint32, start);
					break;

				case SDP_DATA_UUID128:
					SDP_GET_UUID128(&value.int128, start);
					break;

				default:
					goto end;
				}
				if (value.uint16 == 3) {
					SDP_GET8(type, start);
					switch (type) {
					case SDP_DATA_UINT8:
					case SDP_DATA_INT8:
						SDP_GET8(value.uint8, start);
						channel = value.uint8;
						break;

					case SDP_DATA_UINT16:
					case SDP_DATA_INT16:
						SDP_GET16(value.uint16, start);
						channel = value.uint16;
						break;

					case SDP_DATA_UINT32:
					case SDP_DATA_INT32:
						SDP_GET32(value.uint32, start);
						channel = value.uint32;
						break;

					default:
						goto end;
					}
				} else {
					SDP_GET8(type, start);
					switch (type) {
					case SDP_DATA_SEQ8:
					case SDP_DATA_UINT8:
					case SDP_DATA_INT8:
					case SDP_DATA_BOOL:
						SDP_GET8(value.uint8, start);
						break;

					case SDP_DATA_SEQ16:
					case SDP_DATA_UINT16:
					case SDP_DATA_INT16:
					case SDP_DATA_UUID16:
						SDP_GET16(value.uint16, start);
						break;

					case SDP_DATA_SEQ32:
					case SDP_DATA_UINT32:
					case SDP_DATA_INT32:
					case SDP_DATA_UUID32:
						SDP_GET32(value.uint32, start);
						break;

					case SDP_DATA_UINT64:
					case SDP_DATA_INT64:
						SDP_GET64(value.uint64, start);
						break;

					case SDP_DATA_UINT128:
					case SDP_DATA_INT128:
						SDP_GET128(&value.int128, start);
						break;

					default:
						goto end;
					}
				}
			}
			start += len;
			break;

		case SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET:
			if (channel == -1)
				break;
			
			SDP_GET8(type, start);
			switch (type) {
				case SDP_DATA_STR8:
				case SDP_DATA_URL8:
					SDP_GET8(len, start);
					snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start);
					start += len;
					break;

				case SDP_DATA_STR16:
				case SDP_DATA_URL16:
					SDP_GET16(len, start);
					snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start);
					start += len;
					break;

				case SDP_DATA_STR32:
				case SDP_DATA_URL32:
					SDP_GET32(len, start);
					snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start);
					start += len;
					break;

				default:
					goto end;
			}
			if (name == NULL)
				break;

			if (only_gnapplet != 0) {
				if (strcmp(name, "gnapplet") == 0)
					goto end;
				else {
					channel = -1;
					break;
				}
			}
			
			if (strstr(name, "Nokia PC Suite") != NULL) {
				channel = -1;
				break;
			}

			if (strstr(name, "Bluetooth Serial Port") != NULL) {
				channel = -1;
				break;
			}

			if (strstr(name, "m-Router Connectivity") != NULL) {
				channel = -1;
				break;
			}

			goto end;
		}
	}

end:
	sdp_close(ss);
	return channel;
}
Example #7
0
/* 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 */
Example #8
0
static void
client_query(void)
{
	uint8_t buffer[512];
	sdp_attr_t attr;
	uint32_t range;
	void *ss;
	int rv;
	uint8_t *seq0, *seq1;

	attr.flags = SDP_ATTR_INVALID;
	attr.attr = 0;
	attr.vlen = sizeof(buffer);
	attr.value = buffer;

	range = SDP_ATTR_RANGE(SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
			       SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);

	ss = sdp_open(&local_bdaddr, &remote_bdaddr);
	if (ss == NULL || (errno = sdp_error(ss)) != 0) {
		log_err("%s: %m", service_name);
		exit(EXIT_FAILURE);
	}

	log_info("Searching for %s service at %s",
	    service_name, bt_ntoa(&remote_bdaddr, NULL));

	rv = sdp_search(ss, 1, &service_class, 1, &range, 1, &attr);
	if (rv != 0) {
		log_err("%s: %s", service_name, strerror(sdp_error(ss)));
		exit(EXIT_FAILURE);
	}

	sdp_close(ss);

	if (attr.flags != SDP_ATTR_OK
	    || attr.attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST) {
		log_err("%s service not found", service_name);
		exit(EXIT_FAILURE);
	}

	/*
	 * we expect the following protocol descriptor list
	 *
	 *	seq len
	 *	  seq len
	 *	    uuid value == L2CAP
	 *	    uint16 value16 => PSM
	 *	  seq len
	 *	    uuid value == BNEP
	 */
	if (_sdp_get_seq(&attr.value, attr.value + attr.vlen, &seq0)
	    && _sdp_get_seq(&seq0, attr.value, &seq1)
	    && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_L2CAP)
	    && _sdp_get_uint16(&seq1, seq0, &l2cap_psm)
	    && _sdp_get_seq(&seq0, attr.value, &seq1)
	    && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_BNEP)) {
		log_info("Found PSM %d for service %s", l2cap_psm, service_name);
		return;
	}

	log_err("%s query failed", service_name);
	exit(EXIT_FAILURE);
}
Example #9
0
int
rfcomm_channel_lookup(bdaddr_t const *local, bdaddr_t const *remote,
			int service, int *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 = NG_HCI_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));
}
Example #10
0
static int32_t
hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
{
	void	*ss = NULL;
	uint8_t	*hid_descriptor = NULL;
	int32_t	 i, control_psm = -1, interrupt_psm = -1,
		 reconnect_initiate = -1,
		 normally_connectable = 0, battery_power = 0,
		 hid_descriptor_length = -1;

	if (local == NULL)
		local = NG_HCI_BDADDR_ANY;
	if (hd == NULL)
		hid_sdp_query_exit(EINVAL);

	for (i = 0; i < nvalues; i ++) {
		values[i].flags = SDP_ATTR_INVALID;
		values[i].attr = 0;
		values[i].vlen = sizeof(buffer[i]);
		values[i].value = buffer[i];
	}

	if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
		hid_sdp_query_exit(ENOMEM);
	if (sdp_error(ss) != 0)
		hid_sdp_query_exit(sdp_error(ss));
	if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
                hid_sdp_query_exit(sdp_error(ss));

        sdp_close(ss);
        ss = NULL;

	for (i = 0; i < nvalues; i ++) {
		if (values[i].flags != SDP_ATTR_OK)
			continue;

		switch (values[i].attr) {
		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
			break;

		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
			break;

		case 0x0205: /* HIDReconnectInitiate */
			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
			break;

		case 0x0206: /* HIDDescriptorList */
			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
				hid_descriptor = values[i].value;
				hid_descriptor_length = values[i].vlen;
			}
			break;

		case 0x0209: /* HIDBatteryPower */
			battery_power = hid_sdp_parse_boolean(&values[i]);
			break;

		case 0x020d: /* HIDNormallyConnectable */
			normally_connectable = hid_sdp_parse_boolean(&values[i]);
			break;
		}
	}

	if (control_psm == -1 || interrupt_psm == -1 ||
	    reconnect_initiate == -1 ||
	    hid_descriptor == NULL || hid_descriptor_length == -1)
		hid_sdp_query_exit(ENOATTR);

	hd->control_psm = control_psm;
	hd->interrupt_psm = interrupt_psm;
	hd->reconnect_initiate = reconnect_initiate? 1 : 0;
	hd->battery_power = battery_power? 1 : 0;
	hd->normally_connectable = normally_connectable? 1 : 0;
	hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
	if (hd->desc == NULL)
		hid_sdp_query_exit(ENOMEM);

	return (0);
}
Example #11
0
/* 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);
}