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