gattrib: Fix passing NULL to memcpy
[platform/upstream/bluez.git] / attrib / gattrib.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *
4  *  BlueZ - Bluetooth protocol stack for Linux
5  *
6  *  Copyright (C) 2010  Nokia Corporation
7  *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
8  *
9  *
10  */
11
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif
15
16 #include <stdio.h>
17 #include <stdint.h>
18 #include <stdbool.h>
19 #include <string.h>
20
21 #include <glib.h>
22
23 #include "lib/bluetooth.h"
24
25 #include "btio/btio.h"
26 #include "src/log.h"
27 #include "src/shared/util.h"
28 #include "src/shared/att.h"
29 #include "src/shared/queue.h"
30 #include "attrib/gattrib.h"
31
32 struct _GAttrib {
33         int ref_count;
34         struct bt_att *att;
35         GIOChannel *io;
36         GDestroyNotify destroy;
37         gpointer destroy_user_data;
38         struct queue *callbacks;
39         uint8_t *buf;
40         int buflen;
41         struct queue *track_ids;
42 };
43
44 struct attrib_callbacks {
45         unsigned int id;
46         GAttribResultFunc result_func;
47         GAttribNotifyFunc notify_func;
48         GDestroyNotify destroy_func;
49         gpointer user_data;
50         GAttrib *parent;
51         uint16_t notify_handle;
52 };
53
54 GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu, bool ext_signed)
55 {
56         gint fd;
57         GAttrib *attr;
58
59         if (!io)
60                 return NULL;
61
62         fd = g_io_channel_unix_get_fd(io);
63         attr = new0(GAttrib, 1);
64         if (!attr)
65                 return NULL;
66
67         g_io_channel_ref(io);
68         attr->io = io;
69
70         attr->att = bt_att_new(fd, ext_signed);
71         if (!attr->att)
72                 goto fail;
73
74         bt_att_set_close_on_unref(attr->att, true);
75         g_io_channel_set_close_on_unref(io, FALSE);
76
77         if (!bt_att_set_mtu(attr->att, mtu))
78                 goto fail;
79
80         attr->buf = malloc0(mtu);
81         attr->buflen = mtu;
82         if (!attr->buf)
83                 goto fail;
84
85         attr->callbacks = queue_new();
86         if (!attr->callbacks)
87                 goto fail;
88
89         attr->track_ids = queue_new();
90         if (!attr->track_ids)
91                 goto fail;
92
93         return g_attrib_ref(attr);
94
95 fail:
96         free(attr->buf);
97         bt_att_unref(attr->att);
98         g_io_channel_unref(io);
99         free(attr);
100         return NULL;
101 }
102
103 GAttrib *g_attrib_ref(GAttrib *attrib)
104 {
105         if (!attrib)
106                 return NULL;
107
108         __sync_fetch_and_add(&attrib->ref_count, 1);
109
110         DBG("%p: g_attrib_ref=%d ", attrib, attrib->ref_count);
111
112         return attrib;
113 }
114
115 static void attrib_callbacks_destroy(void *data)
116 {
117         struct attrib_callbacks *cb = data;
118
119         if (cb->destroy_func)
120                 cb->destroy_func(cb->user_data);
121
122         free(data);
123 }
124
125 static void attrib_callbacks_remove(void *data)
126 {
127         struct attrib_callbacks *cb = data;
128
129         if (!data || !queue_remove(cb->parent->callbacks, data))
130                 return;
131
132         attrib_callbacks_destroy(data);
133 }
134
135 void g_attrib_unref(GAttrib *attrib)
136 {
137         if (!attrib)
138                 return;
139
140         DBG("%p: g_attrib_unref=%d ", attrib, attrib->ref_count - 1);
141
142         if (__sync_sub_and_fetch(&attrib->ref_count, 1))
143                 return;
144
145         if (attrib->destroy)
146                 attrib->destroy(attrib->destroy_user_data);
147
148         bt_att_unref(attrib->att);
149
150         queue_destroy(attrib->callbacks, attrib_callbacks_destroy);
151         queue_destroy(attrib->track_ids, NULL);
152
153         free(attrib->buf);
154
155         g_io_channel_unref(attrib->io);
156
157         free(attrib);
158 }
159
160 GIOChannel *g_attrib_get_channel(GAttrib *attrib)
161 {
162         if (!attrib)
163                 return NULL;
164
165         return attrib->io;
166 }
167
168 struct bt_att *g_attrib_get_att(GAttrib *attrib)
169 {
170         if (!attrib)
171                 return NULL;
172
173         return attrib->att;
174 }
175
176 gboolean g_attrib_set_destroy_function(GAttrib *attrib, GDestroyNotify destroy,
177                                                         gpointer user_data)
178 {
179         if (!attrib)
180                 return FALSE;
181
182         attrib->destroy = destroy;
183         attrib->destroy_user_data = user_data;
184
185         return TRUE;
186 }
187
188
189 static uint8_t *construct_full_pdu(uint8_t opcode, const void *pdu,
190                                                                 uint16_t length)
191 {
192         uint8_t *buf = malloc0(length + 1);
193
194         if (!buf)
195                 return NULL;
196
197         buf[0] = opcode;
198
199         if (pdu && length)
200                 memcpy(buf + 1, pdu, length);
201
202         return buf;
203 }
204
205 static void attrib_callback_result(uint8_t opcode, const void *pdu,
206                                         uint16_t length, void *user_data)
207 {
208         uint8_t *buf;
209         struct attrib_callbacks *cb = user_data;
210         guint8 status = 0;
211
212         if (!cb)
213                 return;
214
215         buf = construct_full_pdu(opcode, pdu, length);
216         if (!buf)
217                 return;
218
219         if (opcode == BT_ATT_OP_ERROR_RSP) {
220                 /* Error code is the third byte of the PDU data */
221                 if (length < 4)
222                         status = BT_ATT_ERROR_UNLIKELY;
223                 else
224                         status = ((guint8 *)pdu)[3];
225         }
226
227         if (cb->result_func)
228                 cb->result_func(status, buf, length + 1, cb->user_data);
229
230         free(buf);
231 }
232
233 static void attrib_callback_notify(struct bt_att_chan *chan, uint8_t opcode,
234                                         const void *pdu, uint16_t length,
235                                         void *user_data)
236 {
237         uint8_t *buf;
238         struct attrib_callbacks *cb = user_data;
239
240         if (!cb || !cb->notify_func)
241                 return;
242
243         if (cb->notify_handle != GATTRIB_ALL_HANDLES && length < 2)
244                 return;
245
246         if (cb->notify_handle != GATTRIB_ALL_HANDLES &&
247                                         cb->notify_handle != get_le16(pdu))
248                 return;
249
250         buf = construct_full_pdu(opcode, pdu, length);
251         if (!buf)
252                 return;
253
254         cb->notify_func(buf, length + 1, cb->user_data);
255
256         free(buf);
257 }
258
259 guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
260                                 GAttribResultFunc func, gpointer user_data,
261                                 GDestroyNotify notify)
262 {
263         struct attrib_callbacks *cb = NULL;
264         bt_att_response_func_t response_cb = NULL;
265         bt_att_destroy_func_t destroy_cb = NULL;
266
267         if (!attrib)
268                 return 0;
269
270         if (!pdu || !len)
271                 return 0;
272
273         if (func || notify) {
274                 cb = new0(struct attrib_callbacks, 1);
275                 if (!cb)
276                         return 0;
277                 cb->result_func = func;
278                 cb->user_data = user_data;
279                 cb->destroy_func = notify;
280                 cb->parent = attrib;
281                 queue_push_head(attrib->callbacks, cb);
282                 response_cb = attrib_callback_result;
283                 destroy_cb = attrib_callbacks_remove;
284
285         }
286
287         if (id == 0)
288                 id = bt_att_send(attrib->att, pdu[0], (void *) pdu + 1,
289                                         len - 1, response_cb, cb, destroy_cb);
290         else {
291                 int err;
292
293                 err = bt_att_resend(attrib->att, id, pdu[0], (void *) pdu + 1,
294                                         len - 1, response_cb, cb, destroy_cb);
295                 if (err)
296                         return 0;
297         }
298
299         if (!id)
300                 return id;
301
302         /*
303          * If user what us to use given id, lets keep track on that so we give
304          * user a possibility to cancel ongoing request.
305          */
306         if (cb) {
307                 cb->id = id;
308                 queue_push_tail(attrib->track_ids, UINT_TO_PTR(id));
309         }
310
311         return id;
312 }
313
314 gboolean g_attrib_cancel(GAttrib *attrib, guint id)
315 {
316         if (!attrib)
317                 return FALSE;
318
319         return bt_att_cancel(attrib->att, id);
320 }
321
322 static void cancel_request(void *data, void *user_data)
323 {
324         unsigned int id = PTR_TO_UINT(data);
325         GAttrib *attrib = user_data;
326
327         bt_att_cancel(attrib->att, id);
328 }
329
330 gboolean g_attrib_cancel_all(GAttrib *attrib)
331 {
332         if (!attrib)
333                 return FALSE;
334
335         queue_foreach(attrib->track_ids, cancel_request, attrib);
336         queue_remove_all(attrib->track_ids, NULL, NULL, NULL);
337
338         return TRUE;
339 }
340
341 guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
342                                 GAttribNotifyFunc func, gpointer user_data,
343                                 GDestroyNotify notify)
344 {
345         struct attrib_callbacks *cb = NULL;
346
347         if (!attrib)
348                 return 0;
349
350         if (func || notify) {
351                 cb = new0(struct attrib_callbacks, 1);
352                 if (!cb)
353                         return 0;
354                 cb->notify_func = func;
355                 cb->notify_handle = handle;
356                 cb->user_data = user_data;
357                 cb->destroy_func = notify;
358                 cb->parent = attrib;
359                 queue_push_head(attrib->callbacks, cb);
360         }
361
362         if (opcode == GATTRIB_ALL_REQS)
363                 opcode = BT_ATT_ALL_REQUESTS;
364
365         return bt_att_register(attrib->att, opcode, attrib_callback_notify,
366                                                 cb, attrib_callbacks_remove);
367 }
368
369 uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
370 {
371         uint16_t mtu;
372
373         if (!attrib || !len)
374                 return NULL;
375
376         mtu = bt_att_get_mtu(attrib->att);
377
378         /*
379          * Clients of this expect a buffer to use.
380          *
381          * Pdu encoding in shared/att verifies if whole buffer fits the mtu,
382          * thus we should set the buflen also when mtu is reduced. But we
383          * need to reallocate the buffer only if mtu is larger.
384          */
385         if (mtu > attrib->buflen)
386                 attrib->buf = g_realloc(attrib->buf, mtu);
387
388         attrib->buflen = mtu;
389         *len = attrib->buflen;
390         return attrib->buf;
391 }
392
393 gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
394 {
395         if (!attrib)
396                 return FALSE;
397
398         /*
399          * Clients of this expect a buffer to use.
400          *
401          * Pdu encoding in sharred/att verifies if whole buffer fits the mtu,
402          * thus we should set the buflen also when mtu is reduced. But we
403          * need to reallocate the buffer only if mtu is larger.
404          */
405         if (mtu > attrib->buflen)
406                 attrib->buf = g_realloc(attrib->buf, mtu);
407
408         attrib->buflen = mtu;
409
410         return bt_att_set_mtu(attrib->att, mtu);
411 }
412
413 gboolean g_attrib_unregister(GAttrib *attrib, guint id)
414 {
415         if (!attrib)
416                 return FALSE;
417
418         return bt_att_unregister(attrib->att, id);
419 }
420
421 gboolean g_attrib_unregister_all(GAttrib *attrib)
422 {
423         if (!attrib)
424                 return false;
425
426         return bt_att_unregister_all(attrib->att);
427 }