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