⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dbus-sdp.c

📁 Linux的蓝牙操作工具。配合bluez-lib使用
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * *  BlueZ - Bluetooth protocol stack for Linux * *  Copyright (C) 2006-2007  Nokia Corporation *  Copyright (C) 2004-2007  Marcel Holtmann <marcel@holtmann.org> * * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA * */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/param.h>#include <sys/socket.h>#include <bluetooth/bluetooth.h>#include <bluetooth/hci.h>#include <bluetooth/hci_lib.h>#include <bluetooth/l2cap.h>#include <bluetooth/sdp.h>#include <bluetooth/sdp_lib.h>#include <netinet/in.h>#include <glib.h>#include <dbus/dbus.h>#include "dbus.h"#include "dbus-helper.h"#include "hcid.h"#include "textfile.h"#include "adapter.h"#include "dbus-hci.h"#include "dbus-common.h"#include "dbus-error.h"#include "error.h"#include "dbus-sdp.h"#include "sdp-xml.h"#define SESSION_TIMEOUT 2000#define DEFAULT_XML_BUF_SIZE	1024struct transaction_context {	DBusConnection	*conn;	DBusMessage	*rq;	sdp_session_t	*session;	GIOChannel	*io;	guint		io_id;	uuid_t		uuid;	GSList		*identifiers;};typedef int connect_cb_t(struct transaction_context *t);struct pending_connect {	DBusConnection *conn;	DBusMessage *rq;	char *dst;	sdp_session_t *session;	connect_cb_t *conn_cb;};/* FIXME:  move to a common file */typedef struct {	char            *name;	uint16_t        class;	char            *info_name;} sdp_service_t;struct cached_session {	sdp_session_t *session;	guint timeout_id;	guint io_id;};static GSList *cached_sessions = NULL;static gboolean session_timeout(gpointer user_data){	struct cached_session *s = user_data;	debug("sdp session timed out. closing");	cached_sessions = g_slist_remove(cached_sessions, s);	g_source_remove(s->io_id);	sdp_close(s->session);	g_free(s);	return FALSE;}gboolean idle_callback(GIOChannel *io, GIOCondition cond, gpointer user_data){	struct cached_session *s = user_data;	if (cond & G_IO_NVAL)		return FALSE;	if (cond & (G_IO_ERR | G_IO_HUP))		debug("idle_callback: session got disconnected");	if (cond & G_IO_IN)		debug("got unexpected input on idle SDP socket");	cached_sessions = g_slist_remove(cached_sessions, s);	g_source_remove(s->timeout_id);	sdp_close(s->session);	g_free(s);	return FALSE;}static void cache_sdp_session(sdp_session_t *sess, GIOChannel *io){	struct cached_session *s;	s = g_new0(struct cached_session, 1);	s->session = sess;	s->timeout_id = g_timeout_add(SESSION_TIMEOUT, session_timeout, s);	s->io_id = g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,					idle_callback, s);	cached_sessions = g_slist_append(cached_sessions, s);	debug("sdp session added to cache");}static int get_bdaddrs(int sock, bdaddr_t *sba, bdaddr_t *dba){	struct sockaddr_l2 a;	socklen_t len;	len = sizeof(a);	if (getsockname(sock, (struct sockaddr *) &a, &len) < 0) {		error("getsockname: %s (%d)", strerror(errno), errno);		return -1;	}		bacpy(sba, &a.l2_bdaddr);	len = sizeof(a);	if (getpeername(sock, (struct sockaddr *) &a, &len) < 0) {		error("getpeername: %s (%d)", strerror(errno), errno);		return -1;	}	bacpy(dba, &a.l2_bdaddr);	return 0;}static struct cached_session *get_cached_session(bdaddr_t *src, bdaddr_t *dst){	GSList *l;	for (l = cached_sessions; l != NULL; l = l->next) {		struct cached_session *s = l->data;		int sock = sdp_get_socket(s->session);		bdaddr_t sba, dba;		if (get_bdaddrs(sock, &sba, &dba) < 0)			continue;		if (bacmp(&sba, src) || bacmp(&dba, dst))			continue;		debug("found matching session, removing from list");		cached_sessions = g_slist_remove(cached_sessions, s);		return s;	}	return NULL;}static sdp_session_t *get_sdp_session(bdaddr_t *src, bdaddr_t *dst){	struct cached_session *s;	sdp_session_t *session;	s = get_cached_session(src, dst);	if (!s) {		debug("no matching session found. creating a new one");		return sdp_connect(src, dst, SDP_NON_BLOCKING);	}	session = s->session;	g_source_remove(s->timeout_id);	g_source_remove(s->io_id);	g_free(s);	return session;}static void append_and_grow_string(void *data, const char *str){	sdp_buf_t *buff = data;	int len;	len = strlen(str);	if (!buff->data) {		buff->data = malloc(DEFAULT_XML_BUF_SIZE);		if (!buff->data)			return;		buff->buf_size = DEFAULT_XML_BUF_SIZE;	}	/* Grow string */	while (buff->buf_size < (buff->data_size + len + 1)) {		void *tmp;		uint32_t new_size;		/* Grow buffer by a factor of 2 */		new_size = (buff->buf_size << 1);		tmp = realloc(buff->data, new_size);		if (!tmp)			return;		buff->data = tmp;		buff->buf_size = new_size;	}	/* Include the NULL character */	memcpy(buff->data + buff->data_size, str, len + 1);	buff->data_size += len;}/* FIXME:  move to a common file */sdp_service_t sdp_service[] = {	{ "vcp",	VIDEO_CONF_SVCLASS_ID,		"Video Conference"	},	{ "map",	0,				NULL			},	{ "pbap",	PBAP_SVCLASS_ID,		"Phone Book Access"	},	{ "sap",	SAP_SVCLASS_ID,			"SIM Access"		},	{ "ftp",	OBEX_FILETRANS_SVCLASS_ID,	"OBEX File Transfer"	},	{ "bpp",	BASIC_PRINTING_SVCLASS_ID,	"Printing"		},	{ "bip",	IMAGING_SVCLASS_ID,		"Imaging"		},	{ "synch",	IRMC_SYNC_SVCLASS_ID,		"Synchronization"	},	{ "dun",	DIALUP_NET_SVCLASS_ID,		"Dial-Up Networking"	},	{ "opp",	OBEX_OBJPUSH_SVCLASS_ID,	"OBEX Object Push"	},	{ "fax",	FAX_SVCLASS_ID,			"Fax"			},	{ "spp",	SERIAL_PORT_SVCLASS_ID,		"Serial Port"		},	{ "hsp",	HEADSET_SVCLASS_ID,		"Headset"		},	{ "hfp",	HANDSFREE_SVCLASS_ID,		"Handsfree"		},	{ NULL }};/* FIXME:  move to a common file */uint16_t sdp_str2svclass(const char *str){	sdp_service_t *s;	for (s = sdp_service; s->name; s++) {		if (strcasecmp(s->name, str) == 0)			return s->class;	}	return 0;}/* list of remote and local service records */static GSList *pending_connects  = NULL;static struct pending_connect *pending_connect_new(DBusConnection *conn, DBusMessage *msg,							const char *dst, connect_cb_t *cb){	struct pending_connect *c;	if (!dst)		return NULL;	c = g_new0(struct pending_connect, 1);	if (dst)		c->dst = g_strdup(dst);	c->conn = dbus_connection_ref(conn);	c->rq = dbus_message_ref(msg);	c->conn_cb = cb;	return c;}static void pending_connect_free(struct pending_connect *c){	if (!c)		return;	g_free(c->dst);	if (c->rq)		dbus_message_unref(c->rq);	if (c->conn)		dbus_connection_unref(c->conn);	g_free(c);}static struct pending_connect *find_pending_connect(const char *dst){	GSList *l;	for (l = pending_connects; l != NULL; l = l->next) {		struct pending_connect *pending = l->data;		if (!strcmp(dst, pending->dst))			return pending;	}	return NULL;}static const char *get_address_from_message(DBusConnection *conn, DBusMessage *msg){	struct adapter *adapter;	const char *path;	path = dbus_message_get_path(msg);	if (!path)		return NULL;	if (dbus_connection_get_object_user_data(conn, path, (void *) &adapter) == FALSE)		return NULL;	return adapter->address;}static int sdp_store_record(const char *src, const char *dst, uint32_t handle, uint8_t *buf, size_t size){	char filename[PATH_MAX + 1], key[28], *value;	int i, err;	create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp");	create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);	snprintf(key, sizeof(key), "%17s#%08X", dst, handle);	value = g_malloc0(size * 2 + 1);	for (i = 0; i < size; i++)		sprintf(value + (i * 2), "%02X", buf[i]);	err = textfile_put(filename, key, value);	g_free(value);	return err;}static void transaction_context_free(void *udata, gboolean cache){	struct transaction_context *ctxt = udata;	if (!ctxt)		return;	if (ctxt->conn)		dbus_connection_unref(ctxt->conn);	if (ctxt->rq)		dbus_message_unref(ctxt->rq);	if (ctxt->session && !ctxt->io)		sdp_close(ctxt->session);	if (ctxt->session && ctxt->io) {		g_source_remove(ctxt->io_id);		if (cache)			cache_sdp_session(ctxt->session, ctxt->io);		else			sdp_close(ctxt->session);		g_io_channel_unref(ctxt->io);	}	if (ctxt->identifiers) {		g_slist_foreach(ctxt->identifiers, (GFunc) g_free, NULL);		g_slist_free(ctxt->identifiers);	}	g_free(ctxt);}static gboolean search_process_cb(GIOChannel *chan,				GIOCondition cond, void *udata){	struct transaction_context *ctxt = udata;	int err = 0;	if (cond & G_IO_NVAL) {		g_io_channel_unref(chan);		return FALSE;	}	if (cond & (G_IO_ERR | G_IO_HUP)) {		err = EIO;		goto failed;	}	if (sdp_process(ctxt->session) < 0)		goto failed;	return TRUE;failed:	if (err) {		error_failed_errno(ctxt->conn, ctxt->rq, err);		transaction_context_free(ctxt, FALSE);	}	return TRUE;}static void remote_svc_rec_completed_cb(uint8_t type, uint16_t err,			uint8_t *rsp, size_t size, void *udata){	struct transaction_context *ctxt = udata;	sdp_record_t *rec;	DBusMessage *reply;	DBusMessageIter iter, array_iter;	const char *src, *dst;	int scanned;	if (!ctxt)		return;	if (err == 0xffff) {		/* Check for protocol error or I/O error */		int sdp_err = sdp_get_error(ctxt->session);		if (sdp_err < 0) {			error("search failed: Invalid session!");			error_failed_errno(ctxt->conn, ctxt->rq, EINVAL);			goto failed;		}		error("search failed: %s (%d)", strerror(sdp_err), sdp_err);		error_failed_errno(ctxt->conn, ctxt->rq, sdp_err);		goto failed;	}	if (type == SDP_ERROR_RSP) {		error_sdp_failed(ctxt->conn, ctxt->rq, err);		goto failed;	}	/* check response PDU ID */	if (type != SDP_SVC_ATTR_RSP) {		error("SDP error: %s (%d)", strerror(EPROTO), EPROTO);		error_failed_errno(ctxt->conn, ctxt->rq, EPROTO);		goto failed;	}	dbus_message_get_args(ctxt->rq, NULL,			DBUS_TYPE_STRING, &dst,			DBUS_TYPE_INVALID);	src = get_address_from_message(ctxt->conn, ctxt->rq);	reply = dbus_message_new_method_return(ctxt->rq);	dbus_message_iter_init_append(reply, &iter);	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,			DBUS_TYPE_BYTE_AS_STRING, &array_iter);	rec = sdp_extract_pdu(rsp, &scanned);	if (rec == NULL || size != scanned) {		error("Invalid service record!");		goto done;	}	sdp_store_record(src, dst, rec->handle, rsp, size);	sdp_record_free(rec);	dbus_message_iter_append_fixed_array(&array_iter,			DBUS_TYPE_BYTE, &rsp, size);done:	dbus_message_iter_close_container(&iter, &array_iter);	send_message_and_unref(ctxt->conn, reply);failed:	transaction_context_free(ctxt, TRUE);}static void remote_svc_rec_completed_xml_cb(uint8_t type, uint16_t err,						uint8_t *rsp, size_t size,						void *udata){	struct transaction_context *ctxt = udata;	sdp_record_t *rec;	DBusMessage *reply;	const char *src, *dst;	int scanned;	sdp_buf_t result;	if (!ctxt)		return;	if (err == 0xffff) {		/* Check for protocol error or I/O error */		int sdp_err = sdp_get_error(ctxt->session);		if (sdp_err < 0) {			error("search failed: Invalid session!");			error_failed_errno(ctxt->conn, ctxt->rq, EINVAL);			goto failed;		}		error("search failed: %s (%d)", strerror(sdp_err), sdp_err);		error_failed_errno(ctxt->conn, ctxt->rq, sdp_err);		goto failed;	}	if (type == SDP_ERROR_RSP) {		error_sdp_failed(ctxt->conn, ctxt->rq, err);		goto failed;	}	/* check response PDU ID */	if (type != SDP_SVC_ATTR_RSP) {		error("SDP error: %s (%d)", strerror(EPROTO), EPROTO);		error_failed_errno(ctxt->conn, ctxt->rq, EPROTO);		goto failed;	}	dbus_message_get_args(ctxt->rq, NULL,			DBUS_TYPE_STRING, &dst,			DBUS_TYPE_INVALID);	src = get_address_from_message(ctxt->conn, ctxt->rq);	reply = dbus_message_new_method_return(ctxt->rq);		rec = sdp_extract_pdu(rsp, &scanned);	if (rec == NULL || size != scanned) {		error("Invalid service record!");		goto done;	}	sdp_store_record(src, dst, rec->handle, rsp, size);	memset(&result, 0, sizeof(sdp_buf_t));	convert_sdp_record_to_xml(rec, &result, append_and_grow_string);	sdp_record_free(rec);	if (result.data) {		dbus_message_append_args(reply,				DBUS_TYPE_STRING, &result.data,				DBUS_TYPE_INVALID);		free(result.data);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -