Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include <QtCore/QByteArray>
5#include <QtCore/QDebug>
6#include <QtCore/QUrl>
7#include <stdio.h>
8#include <string>
9#include <bluetooth/bluetooth.h>
10#include <bluetooth/sdp.h>
11#include <bluetooth/sdp_lib.h>
12
13#include <cstdio>
14
15#define RETURN_SUCCESS 0
16#define RETURN_USAGE 1
17#define RETURN_INVALPARAM 2
18#define RETURN_SDP_ERROR 3
19
20void usage()
21{
22 fprintf(stderr, "Usage:\n");
23 fprintf(stderr, "\tsdpscanner <remote bdaddr> <local bdaddr> [Options] ({uuids})\n\n");
24 fprintf(stderr, "Performs an SDP scan on remote device, using the SDP server\n"
25 "represented by the local Bluetooth device.\n\n"
26 "Options:\n"
27 " -p Show scan results in human-readable form\n"
28 " -u [list of uuids] List of uuids which should be scanned for.\n"
29 " Each uuid must be enclosed in {}.\n"
30 " If the list is empty PUBLIC_BROWSE_GROUP scan is used.\n");
31}
32
33#define BUFFER_SIZE 1024
34
35static void parseAttributeValues(sdp_data_t *data, int indentation,
36 QByteArray &xmlOutput, int depth = 0)
37{
38 // same as in src/bluetooth/qbluetoothservicediscoveryagent_p.h
39 static constexpr int kMaxSdpRecursionDepth = 64;
40
41 if (depth > kMaxSdpRecursionDepth)
42 return;
43
44 while (data) {
45 const int length = indentation*2 + 1;
46 QByteArray indentString(length, ' ');
47
48 char snBuffer[BUFFER_SIZE];
49
50 xmlOutput.append(indentString);
51
52 // deal with every dtd type
53 switch (data->dtd) {
54 case SDP_DATA_NIL:
55 xmlOutput.append("<nil/>\n");
56 break;
57 case SDP_UINT8:
58 std::snprintf(snBuffer, BUFFER_SIZE, "<uint8 value=\"0x%02x\"/>\n", data->val.uint8);
59 xmlOutput.append(snBuffer);
60 break;
61 case SDP_UINT16:
62 std::snprintf(snBuffer, BUFFER_SIZE, "<uint16 value=\"0x%04x\"/>\n", data->val.uint16);
63 xmlOutput.append(snBuffer);
64 break;
65 case SDP_UINT32:
66 std::snprintf(snBuffer, BUFFER_SIZE, "<uint32 value=\"0x%08x\"/>\n", data->val.uint32);
67 xmlOutput.append(snBuffer);
68 break;
69 case SDP_UINT64:
70 std::snprintf(snBuffer, BUFFER_SIZE, "<uint64 value=\"0x%016llx\"/>\n",
71 qulonglong(data->val.uint64));
72 xmlOutput.append(snBuffer);
73 break;
74 case SDP_UINT128:
75 xmlOutput.append("<uint128 value=\"0x");
76 for (int i = 0; i < 16; i++)
77 std::sprintf(&snBuffer[i * 2], "%02x", data->val.uint128.data[i]);
78 xmlOutput.append(snBuffer);
79 xmlOutput.append("\"/>\n");
80 break;
81 case SDP_INT8:
82 std::snprintf(snBuffer, BUFFER_SIZE, "<int8 value=\"%d\"/>/n", data->val.int8);
83 xmlOutput.append(snBuffer);
84 break;
85 case SDP_INT16:
86 std::snprintf(snBuffer, BUFFER_SIZE, "<int16 value=\"%d\"/>/n", data->val.int16);
87 xmlOutput.append(snBuffer);
88 break;
89 case SDP_INT32:
90 std::snprintf(snBuffer, BUFFER_SIZE, "<int32 value=\"%d\"/>/n", data->val.int32);
91 xmlOutput.append(snBuffer);
92 break;
93 case SDP_INT64:
94 std::snprintf(snBuffer, BUFFER_SIZE, "<int64 value=\"%lld\"/>/n",
95 qlonglong(data->val.int64));
96 xmlOutput.append(snBuffer);
97 break;
98 case SDP_INT128:
99 xmlOutput.append("<int128 value=\"0x");
100 for (int i = 0; i < 16; i++)
101 std::sprintf(&snBuffer[i * 2], "%02x", data->val.int128.data[i]);
102 xmlOutput.append(snBuffer);
103 xmlOutput.append("\"/>\n");
104 break;
105 case SDP_UUID_UNSPEC:
106 break;
107 case SDP_UUID16:
108 case SDP_UUID32:
109 xmlOutput.append("<uuid value=\"0x");
110 sdp_uuid2strn(&(data->val.uuid), snBuffer, BUFFER_SIZE);
111 xmlOutput.append(snBuffer);
112 xmlOutput.append("\"/>\n");
113 break;
114 case SDP_UUID128:
115 xmlOutput.append("<uuid value=\"");
116 sdp_uuid2strn(&(data->val.uuid), snBuffer, BUFFER_SIZE);
117 xmlOutput.append(snBuffer);
118 xmlOutput.append("\"/>\n");
119 break;
120 case SDP_TEXT_STR_UNSPEC:
121 break;
122 case SDP_TEXT_STR8:
123 case SDP_TEXT_STR16:
124 case SDP_TEXT_STR32:
125 {
126 xmlOutput.append("<text ");
127 QByteArray text = QByteArray::fromRawData(data->val.str, data->unitSize);
128
129 bool hasNonPrintableChar = false;
130 for (qsizetype i = 0; i < text.size(); ++i) {
131 if (text[i] == '\0') {
132 text.resize(i); // cut trailing content
133 break;
134 } else if (!isprint(text[i])) {
135 hasNonPrintableChar = true;
136 const auto firstNullIdx = text.indexOf('\0');
137 if (firstNullIdx > 0)
138 text.resize(firstNullIdx); // cut trailing content
139 break;
140 }
141 }
142
143 if (hasNonPrintableChar) {
144 xmlOutput.append("encoding=\"hex\" value=\"");
145 xmlOutput.append(text.toHex());
146 } else {
147 text.replace('&', "&amp;");
148 text.replace('<', "&lt;");
149 text.replace('>', "&gt;");
150 text.replace('"', "&quot;");
151
152 xmlOutput.append("value=\"");
153 xmlOutput.append(text);
154 }
155
156 xmlOutput.append("\"/>\n");
157 break;
158 }
159 case SDP_BOOL:
160 if (data->val.uint8)
161 xmlOutput.append("<boolean value=\"true\"/>\n");
162 else
163 xmlOutput.append("<boolean value=\"false\"/>\n");
164 break;
165 case SDP_SEQ_UNSPEC:
166 break;
167 case SDP_SEQ8:
168 case SDP_SEQ16:
169 case SDP_SEQ32:
170 xmlOutput.append("<sequence>\n");
171 parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput, depth + 1);
172 xmlOutput.append(indentString);
173 xmlOutput.append("</sequence>\n");
174 break;
175 case SDP_ALT_UNSPEC:
176 break;
177 case SDP_ALT8:
178 case SDP_ALT16:
179 case SDP_ALT32:
180 xmlOutput.append("<alternate>\n");
181 parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput, depth + 1);
182 xmlOutput.append(indentString);
183 xmlOutput.append("</alternate>\n");
184 break;
185 case SDP_URL_STR_UNSPEC:
186 break;
187 case SDP_URL_STR8:
188 case SDP_URL_STR16:
189 case SDP_URL_STR32:
190 {
191 xmlOutput.append("<url value=\"");
192 const QByteArray urlData =
193 QByteArray::fromRawData(data->val.str, qstrnlen(data->val.str, data->unitSize));
194 const QUrl url = QUrl::fromEncoded(urlData);
195 // Encoded url %-encodes all of the XML special characters except '&',
196 // so we need to do that manually
197 xmlOutput.append(url.toEncoded().replace('&', "&amp;"));
198 xmlOutput.append("\"/>\n");
199 break;
200 }
201 default:
202 fprintf(stderr, "Unknown dtd type\n");
203 }
204
205 data = data->next;
206 }
207}
208
209static void parseAttribute(void *value, void *extraData)
210{
211 sdp_data_t *data = (sdp_data_t *) value;
212 QByteArray *xmlOutput = static_cast<QByteArray *>(extraData);
213
214 char buffer[BUFFER_SIZE];
215
216 std::snprintf(buffer, BUFFER_SIZE, " <attribute id=\"0x%04x\">\n", data->attrId);
217 xmlOutput->append(buffer);
218
219 parseAttributeValues(data, 2, *xmlOutput);
220
221 xmlOutput->append(" </attribute>\n");
222}
223
224// the resulting xml output is based on the already used xml parser
225QByteArray parseSdpRecord(sdp_record_t *record)
226{
227 if (!record || !record->attrlist)
228 return QByteArray();
229
230 QByteArray xmlOutput;
231
232 xmlOutput.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<record>\n");
233
234 sdp_list_foreach(record->attrlist, parseAttribute, &xmlOutput);
235 xmlOutput.append("</record>");
236
237 return xmlOutput;
238}
239
240
241int main(int argc, char **argv)
242{
243 if (argc < 3) {
244 usage();
245 return RETURN_USAGE;
246 }
247
248 fprintf(stderr, "SDP for %s %s\n", argv[1], argv[2]);
249
250 bdaddr_t remote;
251 bdaddr_t local;
252 int result = str2ba(argv[1], &remote);
253 if (result < 0) {
254 fprintf(stderr, "Invalid remote address: %s\n", argv[1]);
255 return RETURN_INVALPARAM;
256 }
257
258 result = str2ba(argv[2], &local);
259 if (result < 0) {
260 fprintf(stderr, "Invalid local address: %s\n", argv[2]);
261 return RETURN_INVALPARAM;
262 }
263
264 bool showHumanReadable = false;
265 std::vector<std::string> targetServices;
266
267 for (int i = 3; i < argc; i++) {
268 if (argv[i][0] != '-') {
269 usage();
270 return RETURN_USAGE;
271 }
272
273 switch (argv[i][1])
274 {
275 case 'p':
276 showHumanReadable = true;
277 break;
278 case 'u':
279 i++;
280
281 for ( ; i < argc && argv[i][0] == '{'; i++)
282 targetServices.push_back(argv[i]);
283
284 i--; // outer loop increments again
285 break;
286 default:
287 fprintf(stderr, "Wrong argument: %s\n", argv[i]);
288 usage();
289 return RETURN_USAGE;
290 }
291 }
292
293 std::vector<uuid_t> uuids;
294 for (std::vector<std::string>::const_iterator iter = targetServices.cbegin();
295 iter != targetServices.cend(); ++iter) {
296
297 uint128_t temp128;
298 uint16_t field1, field2, field3, field5;
299 uint32_t field0, field4;
300
301 fprintf(stderr, "Target scan for %s\n", (*iter).c_str());
302 if (sscanf((*iter).c_str(), "{%08x-%04hx-%04hx-%04hx-%08x%04hx}", &field0,
303 &field1, &field2, &field3, &field4, &field5) != 6) {
304 fprintf(stderr, "Skipping invalid uuid: %s\n", ((*iter).c_str()));
305 continue;
306 }
307
308 // we need uuid_t conversion based on
309 // http://www.spinics.net/lists/linux-bluetooth/msg20356.html
310 field0 = htonl(field0);
311 field4 = htonl(field4);
312 field1 = htons(field1);
313 field2 = htons(field2);
314 field3 = htons(field3);
315 field5 = htons(field5);
316
317 uint8_t* temp = (uint8_t*) &temp128;
318 memcpy(&temp[0], &field0, 4);
319 memcpy(&temp[4], &field1, 2);
320 memcpy(&temp[6], &field2, 2);
321 memcpy(&temp[8], &field3, 2);
322 memcpy(&temp[10], &field4, 4);
323 memcpy(&temp[14], &field5, 2);
324
325 uuid_t sdpUuid;
326 sdp_uuid128_create(&sdpUuid, &temp128);
327 uuids.push_back(sdpUuid);
328 }
329
330 sdp_session_t *session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY);
331 if (!session) {
332 //try one more time if first time failed
333 session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY);
334 }
335
336 if (!session) {
337 fprintf(stderr, "Cannot establish sdp session\n");
338 return RETURN_SDP_ERROR;
339 }
340
341 // set the filter for service matches
342 if (uuids.empty()) {
343 fprintf(stderr, "Using PUBLIC_BROWSE_GROUP for SDP search\n");
344 uuid_t publicBrowseGroupUuid;
345 sdp_uuid16_create(&publicBrowseGroupUuid, PUBLIC_BROWSE_GROUP);
346 uuids.push_back(publicBrowseGroupUuid);
347 }
348
349 uint32_t attributeRange = 0x0000ffff; //all attributes
350 sdp_list_t *attributes;
351 attributes = sdp_list_append(nullptr, &attributeRange);
352
353 sdp_list_t *sdpResults, *sdpIter;
354 sdp_list_t *totalResults = nullptr;
355 sdp_list_t* serviceFilter;
356
357 for (uuid_t &uuid : uuids) { // can't be const, d/t sdp_list_append signature
358 serviceFilter = sdp_list_append(nullptr, &uuid);
359 result = sdp_service_search_attr_req(session, serviceFilter,
360 SDP_ATTR_REQ_RANGE,
361 attributes, &sdpResults);
362 sdp_list_free(serviceFilter, nullptr);
363 if (result != 0) {
364 fprintf(stderr, "sdp_service_search_attr_req failed\n");
365 sdp_list_free(attributes, nullptr);
366 sdp_close(session);
367 return RETURN_SDP_ERROR;
368 }
369
370 if (!sdpResults)
371 continue;
372
373 if (!totalResults) {
374 totalResults = sdpResults;
375 sdpIter = totalResults;
376 } else {
377 // attach each new result list to the end of totalResults
378 sdpIter->next = sdpResults;
379 }
380
381 while (sdpIter->next) // skip to end of list
382 sdpIter = sdpIter->next;
383 }
384 sdp_list_free(attributes, nullptr);
385
386 // start XML generation from the front
387 sdpResults = totalResults;
388
389 QByteArray total;
390 while (sdpResults) {
391 sdp_record_t *record = (sdp_record_t *) sdpResults->data;
392
393 const QByteArray xml = parseSdpRecord(record);
394 total += xml;
395
396 sdpIter = sdpResults;
397 sdpResults = sdpResults->next;
398 free(sdpIter);
399 sdp_record_free(record);
400 }
401
402 if (!total.isEmpty()) {
403 if (showHumanReadable)
404 printf("%s", total.constData());
405 else
406 printf("%s", total.toBase64().constData());
407 }
408
409 sdp_close(session);
410
411 return RETURN_SUCCESS;
412}
void usage()
Definition main.cpp:20
#define RETURN_USAGE
Definition main.cpp:16
QByteArray parseSdpRecord(sdp_record_t *record)
Definition main.cpp:225
static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray &xmlOutput, int depth=0)
Definition main.cpp:35
#define BUFFER_SIZE
Definition main.cpp:33
#define RETURN_SUCCESS
Definition main.cpp:15
#define RETURN_INVALPARAM
Definition main.cpp:17
#define RETURN_SDP_ERROR
Definition main.cpp:18
static void parseAttribute(void *value, void *extraData)
Definition main.cpp:209
int main(int argc, char *argv[])
[ctor_close]