sdpd-server.c revision e0581b5e29c71c4a0b429ebad671e9bb5583f8e0
1/* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2001-2002 Nokia Corporation 6 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> 7 * Copyright (C) 2002-2008 Marcel Holtmann <marcel@holtmann.org> 8 * Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com> 9 * 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 * 25 */ 26 27#ifdef HAVE_CONFIG_H 28#include <config.h> 29#endif 30 31#include <stdio.h> 32#include <errno.h> 33#include <unistd.h> 34#include <stdlib.h> 35#include <sys/stat.h> 36#include <sys/socket.h> 37 38#include <bluetooth/bluetooth.h> 39#include <bluetooth/l2cap.h> 40#include <bluetooth/sdp.h> 41#include <bluetooth/sdp_lib.h> 42 43#include <sys/un.h> 44#include <netinet/in.h> 45 46#include <glib.h> 47 48#include "logging.h" 49#include "sdpd.h" 50 51static GIOChannel *l2cap_io = NULL, *unix_io = NULL; 52 53static int l2cap_sock, unix_sock; 54 55/* 56 * SDP server initialization on startup includes creating the 57 * l2cap and unix sockets over which discovery and registration clients 58 * access us respectively 59 */ 60static int init_server(uint16_t mtu, int master, int compat) 61{ 62 struct l2cap_options opts; 63 struct sockaddr_l2 l2addr; 64 struct sockaddr_un unaddr; 65 socklen_t optlen; 66 67 /* Register the public browse group root */ 68 register_public_browse_group(); 69 70 /* Register the SDP server's service record */ 71 register_server_service(); 72 73 /* Create L2CAP socket */ 74 l2cap_sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); 75 if (l2cap_sock < 0) { 76 error("opening L2CAP socket: %s", strerror(errno)); 77 return -1; 78 } 79 80 memset(&l2addr, 0, sizeof(l2addr)); 81 l2addr.l2_family = AF_BLUETOOTH; 82 bacpy(&l2addr.l2_bdaddr, BDADDR_ANY); 83 l2addr.l2_psm = htobs(SDP_PSM); 84 85 if (bind(l2cap_sock, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { 86 error("binding L2CAP socket: %s", strerror(errno)); 87 return -1; 88 } 89 90 if (master) { 91 int opt = L2CAP_LM_MASTER; 92 if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) { 93 error("setsockopt: %s", strerror(errno)); 94 return -1; 95 } 96 } 97 98 if (mtu > 0) { 99 memset(&opts, 0, sizeof(opts)); 100 optlen = sizeof(opts); 101 102 if (getsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) { 103 error("getsockopt: %s", strerror(errno)); 104 return -1; 105 } 106 107 opts.omtu = mtu; 108 opts.imtu = mtu; 109 110 if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) { 111 error("setsockopt: %s", strerror(errno)); 112 return -1; 113 } 114 } 115 116 listen(l2cap_sock, 5); 117 118 if (!compat) { 119 unix_sock = -1; 120 return 0; 121 } 122 123 /* Create local Unix socket */ 124 unix_sock = socket(PF_UNIX, SOCK_STREAM, 0); 125 if (unix_sock < 0) { 126 error("opening UNIX socket: %s", strerror(errno)); 127 return -1; 128 } 129 130 memset(&unaddr, 0, sizeof(unaddr)); 131 unaddr.sun_family = AF_UNIX; 132 strcpy(unaddr.sun_path, SDP_UNIX_PATH); 133 134 unlink(unaddr.sun_path); 135 136 if (bind(unix_sock, (struct sockaddr *) &unaddr, sizeof(unaddr)) < 0) { 137 error("binding UNIX socket: %s", strerror(errno)); 138 return -1; 139 } 140 141 listen(unix_sock, 5); 142 143 chmod(SDP_UNIX_PATH, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 144 145 return 0; 146} 147 148static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer data) 149{ 150 sdp_pdu_hdr_t hdr; 151 uint8_t *buf; 152 int sk, len, size; 153 154 if (cond & G_IO_NVAL) 155 return FALSE; 156 157 sk = g_io_channel_unix_get_fd(chan); 158 159 if (cond & (G_IO_HUP | G_IO_ERR)) { 160 sdp_svcdb_collect_all(sk); 161 return FALSE; 162 } 163 164 len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK); 165 if (len <= 0) { 166 sdp_svcdb_collect_all(sk); 167 return FALSE; 168 } 169 170 size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen); 171 buf = malloc(size); 172 if (!buf) 173 return TRUE; 174 175 len = recv(sk, buf, size, 0); 176 if (len <= 0) { 177 sdp_svcdb_collect_all(sk); 178 return FALSE; 179 } 180 181 handle_request(sk, buf, len); 182 183 return TRUE; 184} 185 186static gboolean io_accept_event(GIOChannel *chan, GIOCondition cond, gpointer data) 187{ 188 GIOChannel *io; 189 int nsk; 190 191 if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { 192 g_io_channel_unref(chan); 193 return FALSE; 194 } 195 196 if (data == &l2cap_sock) { 197 struct sockaddr_l2 addr; 198 socklen_t len = sizeof(addr); 199 200 nsk = accept(l2cap_sock, (struct sockaddr *) &addr, &len); 201 } else if (data == &unix_sock) { 202 struct sockaddr_un addr; 203 socklen_t len = sizeof(addr); 204 205 nsk = accept(unix_sock, (struct sockaddr *) &addr, &len); 206 } else 207 return FALSE; 208 209 if (nsk < 0) { 210 error("Can't accept connection: %s", strerror(errno)); 211 return TRUE; 212 } 213 214 io = g_io_channel_unix_new(nsk); 215 g_io_channel_set_close_on_unref(io, TRUE); 216 217 g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, 218 io_session_event, data); 219 220 g_io_channel_unref(io); 221 222 return TRUE; 223} 224 225int start_sdp_server(uint16_t mtu, const char *did, uint32_t flags) 226{ 227 int compat = flags & SDP_SERVER_COMPAT; 228 int master = flags & SDP_SERVER_MASTER; 229 230 info("Starting SDP server"); 231 232 if (init_server(mtu, master, compat) < 0) { 233 error("Server initialization failed"); 234 return -1; 235 } 236 237 if (did && strlen(did) > 0) { 238 const char *ptr = did; 239 uint16_t vid = 0x0000, pid = 0x0000, ver = 0x0000; 240 241 vid = (uint16_t) strtol(ptr, NULL, 16); 242 ptr = strchr(ptr, ':'); 243 if (ptr) { 244 pid = (uint16_t) strtol(ptr + 1, NULL, 16); 245 ptr = strchr(ptr + 1, ':'); 246 if (ptr) 247 ver = (uint16_t) strtol(ptr + 1, NULL, 16); 248 register_device_id(vid, pid, ver); 249 } 250 } 251 252 l2cap_io = g_io_channel_unix_new(l2cap_sock); 253 g_io_channel_set_close_on_unref(l2cap_io, TRUE); 254 255 g_io_add_watch(l2cap_io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, 256 io_accept_event, &l2cap_sock); 257 258 if (compat && unix_sock > fileno(stderr)) { 259 unix_io = g_io_channel_unix_new(unix_sock); 260 g_io_channel_set_close_on_unref(unix_io, TRUE); 261 262 g_io_add_watch(unix_io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, 263 io_accept_event, &unix_sock); 264 } 265 266 return 0; 267} 268 269void stop_sdp_server(void) 270{ 271 info("Stopping SDP server"); 272 273 sdp_svcdb_reset(); 274 275 if (unix_io) 276 g_io_channel_unref(unix_io); 277 278 if (l2cap_io) 279 g_io_channel_unref(l2cap_io); 280} 281