Coverage Report

Created: 2026-10-28 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bluez/gobex/gobex-header.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 *
4
 *  OBEX library with GLib integration
5
 *
6
 *  Copyright (C) 2011  Intel Corporation. All rights reserved.
7
 *
8
 */
9
10
#ifdef HAVE_CONFIG_H
11
#include <config.h>
12
#endif
13
14
#include <string.h>
15
16
#include "gobex-header.h"
17
#include "gobex-debug.h"
18
#include "src/shared/util.h"
19
20
/* Header types */
21
4.68k
#define G_OBEX_HDR_ENC_UNICODE  (0 << 6)
22
1.25k
#define G_OBEX_HDR_ENC_BYTES  (1 << 6)
23
3.04k
#define G_OBEX_HDR_ENC_UINT8  (2 << 6)
24
4.87k
#define G_OBEX_HDR_ENC_UINT32 (3 << 6)
25
26
12.4k
#define G_OBEX_HDR_ENC(id)  ((id) & 0xc0)
27
28
struct _GObexHeader {
29
  guint8 id;
30
  gboolean extdata;
31
  gsize vlen;     /* Length of value */
32
  gsize hlen;     /* Length of full encoded header */
33
  union {
34
    char *string;   /* UTF-8 converted from UTF-16 */
35
    guint8 *data;   /* Own buffer */
36
    const guint8 *extdata;  /* Reference to external buffer */
37
    guint8 u8;
38
    guint32 u32;
39
  } v;
40
};
41
42
159
static glong utf8_to_utf16(gunichar2 **utf16, const char *utf8) {
43
159
  glong utf16_len;
44
159
  int i;
45
46
159
  if (*utf8 == '\0') {
47
93
    *utf16 = NULL;
48
93
    return 0;
49
93
  }
50
51
66
  *utf16 = g_utf8_to_utf16(utf8, -1, NULL, &utf16_len, NULL);
52
66
  if (*utf16 == NULL)
53
0
    return -1;
54
55
  /* g_utf8_to_utf16 produces host-byteorder UTF-16,
56
   * but OBEX requires network byteorder (big endian) */
57
705
  for (i = 0; i < utf16_len; i++)
58
639
    (*utf16)[i] = g_htons((*utf16)[i]);
59
60
66
  utf16_len = (utf16_len + 1) << 1;
61
62
66
  return utf16_len;
63
66
}
64
65
static glong utf16_to_utf8(char **utf8, const gunichar2 *utf16, guint16 len,
66
        GError **err)
67
1.69k
{
68
1.69k
  glong utf8_len;
69
1.69k
  guint16 utf16_len, i;
70
1.69k
  gunichar2 *buf;
71
72
1.69k
  if (*utf16 == '\0') {
73
172
    *utf8 = NULL;
74
172
    return 0;
75
172
  }
76
77
  /* OBEX requires network byteorder (big endian) UTF-16
78
   * but g_utf16_to_utf8 expects host-byteorder UTF-8
79
   */
80
1.51k
  utf16_len = len / sizeof(gunichar2);
81
1.51k
  buf = alloca(sizeof(gunichar2) * (utf16_len + 1));
82
267k
  for (i = 0; i < utf16_len; i++)
83
266k
    (buf)[i] = g_ntohs(utf16[i]);
84
1.51k
  buf[utf16_len] = '\0';
85
86
1.51k
  *utf8 = g_utf16_to_utf8(buf, -1, NULL, &utf8_len, err);
87
1.51k
  if (*utf8 == NULL)
88
5
    utf8_len = -1;
89
90
1.51k
  return utf8_len;
91
1.69k
}
92
93
static guint8 *put_bytes(guint8 *to, const void *from, gsize count)
94
1.17k
{
95
1.17k
  memcpy(to, from, count);
96
1.17k
  return (to + count);
97
1.17k
}
98
99
static const guint8 *get_bytes(void *to, const guint8 *from, gsize count)
100
10.4k
{
101
10.4k
  memcpy(to, from, count);
102
10.4k
  return (from + count);
103
10.4k
}
104
105
gssize g_obex_header_encode(GObexHeader *header, void *buf, gsize buf_len)
106
540
{
107
540
  guint8 *ptr = buf;
108
540
  guint16 u16;
109
540
  guint32 u32;
110
540
  gunichar2 *utf16;
111
540
  glong utf16_len;
112
113
540
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
114
540
            G_OBEX_HDR_ENC(header->id));
115
116
540
  if (buf_len < header->hlen)
117
0
    return -1;
118
119
540
  ptr = put_bytes(ptr, &header->id, sizeof(header->id));
120
121
540
  switch (G_OBEX_HDR_ENC(header->id)) {
122
159
  case G_OBEX_HDR_ENC_UNICODE:
123
159
    utf16_len = utf8_to_utf16(&utf16, header->v.string);
124
159
    if (utf16_len < 0 || (guint16) utf16_len > buf_len)
125
0
      return -1;
126
159
    g_assert_cmpuint(utf16_len + 3, ==, header->hlen);
127
159
    u16 = g_htons(utf16_len + 3);
128
159
    ptr = put_bytes(ptr, &u16, sizeof(u16));
129
159
    put_bytes(ptr, utf16, utf16_len);
130
159
    g_free(utf16);
131
159
    break;
132
75
  case G_OBEX_HDR_ENC_BYTES:
133
75
    u16 = g_htons(header->hlen);
134
75
    ptr = put_bytes(ptr, &u16, sizeof(u16));
135
75
    if (header->extdata)
136
75
      put_bytes(ptr, header->v.extdata, header->vlen);
137
0
    else
138
0
      put_bytes(ptr, header->v.data, header->vlen);
139
75
    break;
140
143
  case G_OBEX_HDR_ENC_UINT8:
141
143
    *ptr = header->v.u8;
142
143
    break;
143
163
  case G_OBEX_HDR_ENC_UINT32:
144
163
    u32 = g_htonl(header->v.u32);
145
163
    put_bytes(ptr, &u32, sizeof(u32));
146
163
    break;
147
0
  default:
148
0
    g_assert_not_reached();
149
540
  }
150
151
540
  return header->hlen;
152
540
}
153
154
GObexHeader *g_obex_header_decode(const void *data, gsize len,
155
        GObexDataPolicy data_policy, gsize *parsed,
156
        GError **err)
157
5.94k
{
158
5.94k
  GObexHeader *header;
159
5.94k
  const guint8 *ptr = data;
160
5.94k
  guint16 hdr_len;
161
5.94k
  glong str_len;
162
5.94k
  GError *conv_err = NULL;
163
164
5.94k
  if (len < 2) {
165
5
    if (!err)
166
0
      return NULL;
167
5
    g_set_error(err, G_OBEX_ERROR, G_OBEX_ERROR_PARSE_ERROR,
168
5
            "Too short header in packet");
169
5
    g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
170
5
    return NULL;
171
5
  }
172
173
5.93k
  header = g_new0(GObexHeader, 1);
174
175
5.93k
  ptr = get_bytes(&header->id, ptr, sizeof(header->id));
176
177
5.93k
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
178
5.93k
            G_OBEX_HDR_ENC(header->id));
179
180
5.93k
  switch (G_OBEX_HDR_ENC(header->id)) {
181
2.26k
  case G_OBEX_HDR_ENC_UNICODE:
182
2.26k
    if (len < 3) {
183
1
      g_set_error(err, G_OBEX_ERROR,
184
1
        G_OBEX_ERROR_PARSE_ERROR,
185
1
        "Not enough data for unicode header (0x%02x)",
186
1
        header->id);
187
1
      goto failed;
188
1
    }
189
2.26k
    ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len));
190
2.26k
    hdr_len = g_ntohs(hdr_len);
191
192
2.26k
    if (hdr_len == 3) {
193
534
      header->v.string = g_strdup("");
194
534
      header->vlen = 0;
195
534
      header->hlen = hdr_len;
196
534
      *parsed = hdr_len;
197
534
      break;
198
534
    }
199
200
1.72k
    if (hdr_len > len || hdr_len < 5) {
201
37
      g_set_error(err, G_OBEX_ERROR,
202
37
        G_OBEX_ERROR_PARSE_ERROR,
203
37
        "Invalid unicode header (0x%02x) length (%u)",
204
37
        header->id, hdr_len);
205
37
      goto failed;
206
37
    }
207
208
1.69k
    str_len = utf16_to_utf8(&header->v.string,
209
1.69k
          (const gunichar2 *) ptr,
210
1.69k
          hdr_len - 5,
211
1.69k
          &conv_err);
212
1.69k
    if (str_len < 0) {
213
5
      g_set_error(err, G_OBEX_ERROR,
214
5
          G_OBEX_ERROR_PARSE_ERROR,
215
5
          "UTF16 to UTF8 conversion failed: %s",
216
5
          conv_err->message);
217
5
      g_error_free(conv_err);
218
5
      goto failed;
219
5
    }
220
221
1.68k
    header->vlen = (gsize) str_len;
222
1.68k
    header->hlen = hdr_len;
223
224
1.68k
    *parsed = hdr_len;
225
226
1.68k
    break;
227
591
  case G_OBEX_HDR_ENC_BYTES:
228
591
    if (len < 3) {
229
2
      g_set_error(err, G_OBEX_ERROR,
230
2
          G_OBEX_ERROR_PARSE_ERROR,
231
2
          "Too short byte array header");
232
2
      goto failed;
233
2
    }
234
589
    ptr = get_bytes(&hdr_len, ptr, sizeof(hdr_len));
235
589
    hdr_len = g_ntohs(hdr_len);
236
589
    if (hdr_len > len) {
237
17
      g_set_error(err, G_OBEX_ERROR,
238
17
          G_OBEX_ERROR_PARSE_ERROR,
239
17
          "Too long byte array header");
240
17
      goto failed;
241
17
    }
242
243
572
    if (hdr_len < 3) {
244
9
      g_set_error(err, G_OBEX_ERROR,
245
9
          G_OBEX_ERROR_PARSE_ERROR,
246
9
          "Too small byte array length");
247
9
      goto failed;
248
9
    }
249
250
563
    header->vlen = hdr_len - 3;
251
563
    header->hlen = hdr_len;
252
253
563
    switch (data_policy) {
254
0
    case G_OBEX_DATA_COPY:
255
0
      header->v.data = util_memdup(ptr, header->vlen);
256
0
      break;
257
563
    case G_OBEX_DATA_REF:
258
563
      header->extdata = TRUE;
259
563
      header->v.extdata = ptr;
260
563
      break;
261
0
    case G_OBEX_DATA_INHERIT:
262
0
    default:
263
0
      g_set_error(err, G_OBEX_ERROR,
264
0
          G_OBEX_ERROR_INVALID_ARGS,
265
0
          "Invalid data policy");
266
0
      goto failed;
267
563
    }
268
269
563
    *parsed = hdr_len;
270
271
563
    break;
272
1.45k
  case G_OBEX_HDR_ENC_UINT8:
273
1.45k
    header->vlen = 1;
274
1.45k
    header->hlen = 2;
275
1.45k
    header->v.u8 = *ptr;
276
1.45k
    *parsed = 2;
277
1.45k
    break;
278
1.63k
  case G_OBEX_HDR_ENC_UINT32:
279
1.63k
    if (len < 5) {
280
3
      g_set_error(err, G_OBEX_ERROR,
281
3
          G_OBEX_ERROR_PARSE_ERROR,
282
3
          "Too short uint32 header");
283
3
      goto failed;
284
3
    }
285
1.62k
    header->vlen = 4;
286
1.62k
    header->hlen = 5;
287
1.62k
    get_bytes(&header->v.u32, ptr, sizeof(header->v.u32));
288
1.62k
    header->v.u32 = g_ntohl(header->v.u32);
289
1.62k
    *parsed = 5;
290
1.62k
    break;
291
0
  default:
292
0
    g_assert_not_reached();
293
5.93k
  }
294
295
5.86k
  return header;
296
297
74
failed:
298
74
  if (*err)
299
74
    g_obex_debug(G_OBEX_DEBUG_ERROR, "%s", (*err)->message);
300
74
  g_obex_header_free(header);
301
74
  return NULL;
302
5.93k
}
303
304
void g_obex_header_free(GObexHeader *header)
305
5.93k
{
306
5.93k
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
307
5.93k
            G_OBEX_HDR_ENC(header->id));
308
309
5.93k
  switch (G_OBEX_HDR_ENC(header->id)) {
310
2.26k
  case G_OBEX_HDR_ENC_UNICODE:
311
2.26k
    g_free(header->v.string);
312
2.26k
    break;
313
591
  case G_OBEX_HDR_ENC_BYTES:
314
591
    if (!header->extdata)
315
28
      free(header->v.data);
316
591
    break;
317
1.45k
  case G_OBEX_HDR_ENC_UINT8:
318
3.08k
  case G_OBEX_HDR_ENC_UINT32:
319
3.08k
    break;
320
0
  default:
321
0
    g_assert_not_reached();
322
5.93k
  }
323
324
5.93k
  g_free(header);
325
5.93k
}
326
327
gboolean g_obex_header_get_unicode(GObexHeader *header, const char **str)
328
0
{
329
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
330
0
            G_OBEX_HDR_ENC(header->id));
331
332
0
  if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UNICODE)
333
0
    return FALSE;
334
335
0
  *str = header->v.string;
336
337
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", *str);
338
339
0
  return TRUE;
340
0
}
341
342
gboolean g_obex_header_get_bytes(GObexHeader *header, const guint8 **val,
343
                gsize *len)
344
0
{
345
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
346
0
            G_OBEX_HDR_ENC(header->id));
347
348
0
  if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_BYTES)
349
0
    return FALSE;
350
351
0
  *len = header->vlen;
352
353
0
  if (header->extdata)
354
0
    *val = header->v.extdata;
355
0
  else
356
0
    *val = header->v.data;
357
358
0
  return TRUE;
359
0
}
360
361
GObexApparam *g_obex_header_get_apparam(GObexHeader *header)
362
0
{
363
0
  gboolean ret;
364
0
  const guint8 *val;
365
0
  gsize len;
366
367
0
  ret = g_obex_header_get_bytes(header, &val, &len);
368
0
  if (!ret)
369
0
    return NULL;
370
371
0
  return g_obex_apparam_decode(val, len);
372
0
}
373
374
gboolean g_obex_header_get_uint8(GObexHeader *header, guint8 *val)
375
0
{
376
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
377
0
            G_OBEX_HDR_ENC(header->id));
378
379
0
  if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UINT8)
380
0
    return FALSE;
381
382
0
  *val = header->v.u8;
383
384
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", *val);
385
386
0
  return TRUE;
387
0
}
388
389
gboolean g_obex_header_get_uint32(GObexHeader *header, guint32 *val)
390
0
{
391
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x",
392
0
            G_OBEX_HDR_ENC(header->id));
393
394
0
  if (G_OBEX_HDR_ENC(header->id) != G_OBEX_HDR_ENC_UINT32)
395
0
    return FALSE;
396
397
0
  *val = header->v.u32;
398
399
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", *val);
400
401
0
  return TRUE;
402
0
}
403
404
GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str)
405
0
{
406
0
  GObexHeader *header;
407
0
  gsize len;
408
409
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
410
411
0
  if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UNICODE)
412
0
    return NULL;
413
414
0
  header = g_new0(GObexHeader, 1);
415
416
0
  header->id = id;
417
418
0
  len = g_utf8_strlen(str, -1);
419
420
0
  header->vlen = len;
421
0
  header->hlen = len == 0 ? 3 : 3 + ((len + 1) * 2);
422
0
  header->v.string = g_strdup(str);
423
424
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", header->v.string);
425
426
0
  return header;
427
0
}
428
429
GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len)
430
0
{
431
0
  GObexHeader *header;
432
433
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
434
435
0
  if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_BYTES)
436
0
    return NULL;
437
438
0
  header = g_new0(GObexHeader, 1);
439
440
0
  header->id = id;
441
0
  header->vlen = len;
442
0
  header->hlen = len + 3;
443
0
  header->v.data = util_memdup(data, len);
444
445
0
  return header;
446
0
}
447
448
GObexHeader *g_obex_header_new_tag(guint8 id, GObexApparam *apparam)
449
0
{
450
0
  guint8 buf[1024];
451
0
  gssize len;
452
453
0
  len = g_obex_apparam_encode(apparam, buf, sizeof(buf));
454
0
  if (len < 0)
455
0
    return NULL;
456
457
0
  return g_obex_header_new_bytes(id, buf, len);
458
0
}
459
460
GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam)
461
0
{
462
0
  return g_obex_header_new_tag(G_OBEX_HDR_APPARAM, apparam);
463
0
}
464
465
GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val)
466
0
{
467
0
  GObexHeader *header;
468
469
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
470
471
0
  if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UINT8)
472
0
    return NULL;
473
474
0
  header = g_new0(GObexHeader, 1);
475
476
0
  header->id = id;
477
0
  header->vlen = 1;
478
0
  header->hlen = 2;
479
0
  header->v.u8 = val;
480
481
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", header->v.u8);
482
483
0
  return header;
484
0
}
485
486
GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val)
487
0
{
488
0
  GObexHeader *header;
489
490
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
491
492
0
  if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UINT32)
493
0
    return NULL;
494
495
0
  header = g_new0(GObexHeader, 1);
496
497
0
  header->id = id;
498
0
  header->vlen = 4;
499
0
  header->hlen = 5;
500
0
  header->v.u32 = val;
501
502
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "%u", header->v.u32);
503
504
0
  return header;
505
0
}
506
507
guint8 g_obex_header_get_id(GObexHeader *header)
508
0
{
509
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x id 0x%02x",
510
0
        G_OBEX_HDR_ENC(header->id), header->id);
511
512
0
  return header->id;
513
0
}
514
515
guint16 g_obex_header_get_length(GObexHeader *header)
516
0
{
517
0
  g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x length %zu",
518
0
        G_OBEX_HDR_ENC(header->id), header->hlen);
519
520
0
  return header->hlen;
521
0
}
522
523
GSList *g_obex_header_create_list(guint8 first_hdr_id, va_list args,
524
              gsize *total_len)
525
142
{
526
142
  unsigned int id = first_hdr_id;
527
142
  GSList *l = NULL;
528
529
142
  g_obex_debug(G_OBEX_DEBUG_HEADER, "");
530
531
142
  *total_len = 0;
532
533
142
  while (id != G_OBEX_HDR_INVALID) {
534
0
    GObexHeader *hdr;
535
0
    const char *str;
536
0
    const void *bytes;
537
0
    unsigned int val;
538
0
    gsize len;
539
540
0
    switch (G_OBEX_HDR_ENC(id)) {
541
0
    case G_OBEX_HDR_ENC_UNICODE:
542
0
      str = va_arg(args, const char *);
543
0
      hdr = g_obex_header_new_unicode(id, str);
544
0
      break;
545
0
    case G_OBEX_HDR_ENC_BYTES:
546
0
      bytes = va_arg(args, void *);
547
0
      len = va_arg(args, gsize);
548
0
      hdr = g_obex_header_new_bytes(id, bytes, len);
549
0
      break;
550
0
    case G_OBEX_HDR_ENC_UINT8:
551
0
      val = va_arg(args, unsigned int);
552
0
      hdr = g_obex_header_new_uint8(id, val);
553
0
      break;
554
0
    case G_OBEX_HDR_ENC_UINT32:
555
0
      val = va_arg(args, unsigned int);
556
0
      hdr = g_obex_header_new_uint32(id, val);
557
0
      break;
558
0
    default:
559
0
      g_assert_not_reached();
560
0
    }
561
562
0
    l = g_slist_append(l, hdr);
563
0
    *total_len += hdr->hlen;
564
0
    id = va_arg(args, int);
565
0
  }
566
567
142
  return l;
568
142
}