36 QByteArray &xmlOutput,
int depth = 0)
39 static constexpr int kMaxSdpRecursionDepth = 64;
41 if (depth > kMaxSdpRecursionDepth)
45 const int length = indentation*2 + 1;
46 QByteArray indentString(length,
' ');
50 xmlOutput.append(indentString);
55 xmlOutput.append(
"<nil/>\n");
58 std::snprintf(snBuffer,
BUFFER_SIZE,
"<uint8 value=\"0x%02x\"/>\n", data->val.uint8);
59 xmlOutput.append(snBuffer);
62 std::snprintf(snBuffer,
BUFFER_SIZE,
"<uint16 value=\"0x%04x\"/>\n", data->val.uint16);
63 xmlOutput.append(snBuffer);
66 std::snprintf(snBuffer,
BUFFER_SIZE,
"<uint32 value=\"0x%08x\"/>\n", data->val.uint32);
67 xmlOutput.append(snBuffer);
70 std::snprintf(snBuffer,
BUFFER_SIZE,
"<uint64 value=\"0x%016llx\"/>\n",
71 qulonglong(data->val.uint64));
72 xmlOutput.append(snBuffer);
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");
82 std::snprintf(snBuffer,
BUFFER_SIZE,
"<int8 value=\"%d\"/>/n", data->val.int8);
83 xmlOutput.append(snBuffer);
86 std::snprintf(snBuffer,
BUFFER_SIZE,
"<int16 value=\"%d\"/>/n", data->val.int16);
87 xmlOutput.append(snBuffer);
90 std::snprintf(snBuffer,
BUFFER_SIZE,
"<int32 value=\"%d\"/>/n", data->val.int32);
91 xmlOutput.append(snBuffer);
95 qlonglong(data->val.int64));
96 xmlOutput.append(snBuffer);
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");
105 case SDP_UUID_UNSPEC:
109 xmlOutput.append(
"<uuid value=\"0x");
110 sdp_uuid2strn(&(data->val.uuid), snBuffer,
BUFFER_SIZE);
111 xmlOutput.append(snBuffer);
112 xmlOutput.append(
"\"/>\n");
115 xmlOutput.append(
"<uuid value=\"");
116 sdp_uuid2strn(&(data->val.uuid), snBuffer,
BUFFER_SIZE);
117 xmlOutput.append(snBuffer);
118 xmlOutput.append(
"\"/>\n");
120 case SDP_TEXT_STR_UNSPEC:
126 xmlOutput.append(
"<text ");
127 QByteArray text = QByteArray::fromRawData(data->val.str, data->unitSize);
129 bool hasNonPrintableChar =
false;
130 for (qsizetype i = 0; i < text.size(); ++i) {
131 if (text[i] ==
'\0') {
134 }
else if (!isprint(text[i])) {
135 hasNonPrintableChar =
true;
136 const auto firstNullIdx = text.indexOf(
'\0');
137 if (firstNullIdx > 0)
138 text.resize(firstNullIdx);
143 if (hasNonPrintableChar) {
144 xmlOutput.append(
"encoding=\"hex\" value=\"");
145 xmlOutput.append(text.toHex());
147 text.replace(
'&',
"&");
148 text.replace(
'<',
"<");
149 text.replace(
'>',
">");
150 text.replace(
'"',
""");
152 xmlOutput.append(
"value=\"");
153 xmlOutput.append(text);
156 xmlOutput.append(
"\"/>\n");
161 xmlOutput.append(
"<boolean value=\"true\"/>\n");
163 xmlOutput.append(
"<boolean value=\"false\"/>\n");
170 xmlOutput.append(
"<sequence>\n");
171 parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput, depth + 1);
172 xmlOutput.append(indentString);
173 xmlOutput.append(
"</sequence>\n");
180 xmlOutput.append(
"<alternate>\n");
181 parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput, depth + 1);
182 xmlOutput.append(indentString);
183 xmlOutput.append(
"</alternate>\n");
185 case SDP_URL_STR_UNSPEC:
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);
197 xmlOutput.append(url.toEncoded().replace(
'&',
"&"));
198 xmlOutput.append(
"\"/>\n");
202 fprintf(stderr,
"Unknown dtd type\n");
248 fprintf(stderr,
"SDP for %s %s\n", argv[1], argv[2]);
252 int result = str2ba(argv[1], &remote);
254 fprintf(stderr,
"Invalid remote address: %s\n", argv[1]);
258 result = str2ba(argv[2], &local);
260 fprintf(stderr,
"Invalid local address: %s\n", argv[2]);
264 bool showHumanReadable =
false;
265 std::vector<std::string> targetServices;
267 for (
int i = 3; i < argc; i++) {
268 if (argv[i][0] !=
'-') {
276 showHumanReadable =
true;
281 for ( ; i < argc && argv[i][0] ==
'{'; i++)
282 targetServices.push_back(argv[i]);
287 fprintf(stderr,
"Wrong argument: %s\n", argv[i]);
293 std::vector<uuid_t> uuids;
294 for (std::vector<std::string>::const_iterator iter = targetServices.cbegin();
295 iter != targetServices.cend(); ++iter) {
298 uint16_t field1, field2, field3, field5;
299 uint32_t field0, field4;
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()));
310 field0 = htonl(field0);
311 field4 = htonl(field4);
312 field1 = htons(field1);
313 field2 = htons(field2);
314 field3 = htons(field3);
315 field5 = htons(field5);
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);
326 sdp_uuid128_create(&sdpUuid, &temp128);
327 uuids.push_back(sdpUuid);
330 sdp_session_t *session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY);
333 session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY);
337 fprintf(stderr,
"Cannot establish sdp session\n");
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);
349 uint32_t attributeRange = 0x0000ffff;
350 sdp_list_t *attributes;
351 attributes = sdp_list_append(
nullptr, &attributeRange);
353 sdp_list_t *sdpResults, *sdpIter;
354 sdp_list_t *totalResults =
nullptr;
355 sdp_list_t* serviceFilter;
357 for (uuid_t &uuid : uuids) {
358 serviceFilter = sdp_list_append(
nullptr, &uuid);
359 result = sdp_service_search_attr_req(session, serviceFilter,
361 attributes, &sdpResults);
362 sdp_list_free(serviceFilter,
nullptr);
364 fprintf(stderr,
"sdp_service_search_attr_req failed\n");
365 sdp_list_free(attributes,
nullptr);
374 totalResults = sdpResults;
375 sdpIter = totalResults;
378 sdpIter->next = sdpResults;
381 while (sdpIter->next)
382 sdpIter = sdpIter->next;
384 sdp_list_free(attributes,
nullptr);
387 sdpResults = totalResults;
391 sdp_record_t *record = (sdp_record_t *) sdpResults->data;
393 const QByteArray xml = parseSdpRecord(record);
396 sdpIter = sdpResults;
397 sdpResults = sdpResults->next;
399 sdp_record_free(record);
402 if (!total.isEmpty()) {
403 if (showHumanReadable)
404 printf(
"%s", total.constData());
406 printf(
"%s", total.toBase64().constData());