📄 dbus-sdp.c
字号:
/* * * BlueZ - Bluetooth protocol stack for Linux * * 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 "dbus-sdp.h"#include "sdp-xml.h"#define SESSION_TIMEOUT 2000#define MAX_IDENTIFIER_LEN 29 /* "XX:XX:XX:XX:XX:XX/0xYYYYYYYY\0" */#define DEFAULT_XML_BUF_SIZE 1024typedef struct { uint16_t dev_id; char *dst; void *search_data; get_record_cb_t *cb; void *data;} get_record_data_t;struct transaction_context { DBusConnection *conn; DBusMessage *rq; sdp_session_t *session; GIOChannel *io; guint io_id; uuid_t uuid; GSList *identifiers; /* Used for internal async get remote service record implementation */ get_record_data_t *call;};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; /* Used for internal async get remote service record implementation */ get_record_data_t *call;};/* 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), "%s#%08X", dst, handle); value = g_malloc(size * 2 + 1); value[0] = '\0'; 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 get_record_data_t *get_record_data_new(uint16_t dev_id, const char *dst, void *search_data, get_record_cb_t *cb, void *data){ get_record_data_t *n; n = g_new(get_record_data_t, 1); n->dst = g_strdup(dst); n->dev_id = dev_id; n->search_data = search_data; n->cb = cb; n->data = data; return n;}static void get_record_data_free(get_record_data_t *d){ g_free(d->search_data); g_free(d->dst); g_free(d);}static inline void get_record_data_call_cb(get_record_data_t *d, sdp_record_t *rec, int err){ d->cb(rec, d->data, err);}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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -