Upgrade bluez5_37 :Merge the code from private
[platform/upstream/bluez.git] / src / sdpd-server.c
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-2010  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 <errno.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <stdbool.h>
35 #include <sys/stat.h>
36 #include <sys/un.h>
37
38 #include <glib.h>
39
40 #include "lib/bluetooth.h"
41 #include "lib/l2cap.h"
42 #include "lib/sdp.h"
43 #include "lib/sdp_lib.h"
44
45 #include "log.h"
46 #include "sdpd.h"
47
48 static guint l2cap_id = 0, unix_id = 0;
49 static int l2cap_sock = -1, unix_sock = -1;
50
51 /*
52  * SDP server initialization on startup includes creating the
53  * l2cap and unix sockets over which discovery and registration clients
54  * access us respectively
55  */
56 static int init_server(uint16_t mtu, int master, int compat)
57 {
58         struct l2cap_options opts;
59         struct sockaddr_l2 l2addr;
60         struct sockaddr_un unaddr;
61         socklen_t optlen;
62
63         /* Register the public browse group root */
64         register_public_browse_group();
65
66         /* Register the SDP server's service record */
67         register_server_service();
68
69         /* Create L2CAP socket */
70         l2cap_sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
71         if (l2cap_sock < 0) {
72                 error("opening L2CAP socket: %s", strerror(errno));
73                 return -1;
74         }
75
76         memset(&l2addr, 0, sizeof(l2addr));
77         l2addr.l2_family = AF_BLUETOOTH;
78         bacpy(&l2addr.l2_bdaddr, BDADDR_ANY);
79         l2addr.l2_psm = htobs(SDP_PSM);
80
81         if (bind(l2cap_sock, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
82                 error("binding L2CAP socket: %s", strerror(errno));
83                 return -1;
84         }
85
86         if (master) {
87                 int opt = L2CAP_LM_MASTER;
88                 if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
89                         error("setsockopt: %s", strerror(errno));
90                         return -1;
91                 }
92         }
93
94         if (mtu > 0) {
95                 memset(&opts, 0, sizeof(opts));
96                 optlen = sizeof(opts);
97
98                 if (getsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
99                         error("getsockopt: %s", strerror(errno));
100                         return -1;
101                 }
102
103                 opts.omtu = mtu;
104                 opts.imtu = mtu;
105
106                 if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
107                         error("setsockopt: %s", strerror(errno));
108                         return -1;
109                 }
110         }
111
112         if (listen(l2cap_sock, 5) < 0) {
113                 error("listen: %s", strerror(errno));
114                 return -1;
115         }
116
117         if (!compat) {
118                 unix_sock = -1;
119                 return 0;
120         }
121
122         /* Create local Unix socket */
123         unix_sock = socket(PF_UNIX, SOCK_STREAM, 0);
124         if (unix_sock < 0) {
125                 error("opening UNIX socket: %s", strerror(errno));
126                 return -1;
127         }
128
129         memset(&unaddr, 0, sizeof(unaddr));
130         unaddr.sun_family = AF_UNIX;
131         strcpy(unaddr.sun_path, SDP_UNIX_PATH);
132
133         unlink(unaddr.sun_path);
134
135         if (bind(unix_sock, (struct sockaddr *) &unaddr, sizeof(unaddr)) < 0) {
136                 error("binding UNIX socket: %s", strerror(errno));
137                 return -1;
138         }
139
140         if (listen(unix_sock, 5) < 0) {
141                 error("listen UNIX socket: %s", strerror(errno));
142                 return -1;
143         }
144
145         chmod(SDP_UNIX_PATH, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
146
147         return 0;
148 }
149
150 static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer data)
151 {
152         sdp_pdu_hdr_t hdr;
153         uint8_t *buf;
154         int sk, len, size;
155
156         if (cond & G_IO_NVAL)
157                 return FALSE;
158
159         sk = g_io_channel_unix_get_fd(chan);
160
161         if (cond & (G_IO_HUP | G_IO_ERR)) {
162                 sdp_svcdb_collect_all(sk);
163                 return FALSE;
164         }
165
166         len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
167         if (len != sizeof(sdp_pdu_hdr_t)) {
168                 sdp_svcdb_collect_all(sk);
169                 return FALSE;
170         }
171
172         size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen);
173         buf = malloc(size);
174         if (!buf)
175                 return TRUE;
176
177         len = recv(sk, buf, size, 0);
178         /* Check here only that the received message is not empty.
179          * Incorrect length of message should be processed later
180          * inside handle_request() in order to produce ErrorResponse.
181          */
182         if (len <= 0) {
183                 sdp_svcdb_collect_all(sk);
184                 free(buf);
185                 return FALSE;
186         }
187
188         handle_request(sk, buf, len);
189
190         return TRUE;
191 }
192
193 static gboolean io_accept_event(GIOChannel *chan, GIOCondition cond, gpointer data)
194 {
195         GIOChannel *io;
196         int nsk;
197
198         if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
199                 return FALSE;
200
201         if (data == &l2cap_sock) {
202                 struct sockaddr_l2 addr;
203                 socklen_t len = sizeof(addr);
204
205                 nsk = accept(l2cap_sock, (struct sockaddr *) &addr, &len);
206         } else if (data == &unix_sock) {
207                 struct sockaddr_un addr;
208                 socklen_t len = sizeof(addr);
209
210                 nsk = accept(unix_sock, (struct sockaddr *) &addr, &len);
211         } else
212                 return FALSE;
213
214         if (nsk < 0) {
215                 error("Can't accept connection: %s", strerror(errno));
216                 return TRUE;
217         }
218
219         io = g_io_channel_unix_new(nsk);
220         g_io_channel_set_close_on_unref(io, TRUE);
221
222         g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
223                                         io_session_event, data);
224
225         g_io_channel_unref(io);
226
227         return TRUE;
228 }
229
230 int start_sdp_server(uint16_t mtu, uint32_t flags)
231 {
232         int compat = flags & SDP_SERVER_COMPAT;
233         int master = flags & SDP_SERVER_MASTER;
234         GIOChannel *io;
235
236         info("Starting SDP server");
237
238         if (init_server(mtu, master, compat) < 0) {
239                 error("Server initialization failed");
240                 return -1;
241         }
242
243         io = g_io_channel_unix_new(l2cap_sock);
244         g_io_channel_set_close_on_unref(io, TRUE);
245
246         l2cap_id = g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
247                                         io_accept_event, &l2cap_sock);
248         g_io_channel_unref(io);
249
250         if (compat && unix_sock > fileno(stderr)) {
251                 io = g_io_channel_unix_new(unix_sock);
252                 g_io_channel_set_close_on_unref(io, TRUE);
253
254                 unix_id = g_io_add_watch(io,
255                                         G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
256                                         io_accept_event, &unix_sock);
257                 g_io_channel_unref(io);
258         }
259
260         return 0;
261 }
262
263 void stop_sdp_server(void)
264 {
265         info("Stopping SDP server");
266
267         sdp_svcdb_reset();
268
269         if (unix_id > 0)
270                 g_source_remove(unix_id);
271
272         if (l2cap_id > 0)
273                 g_source_remove(l2cap_id);
274
275         l2cap_id = unix_id = 0;
276         l2cap_sock = unix_sock = -1;
277 }