40 const int length = indentation*2 + 1;
41 QByteArray indentString(length,
' ');
45 xmlOutput.append(indentString);
50 xmlOutput.append(
"<nil/>\n");
53 std::snprintf(snBuffer,
BUFFER_SIZE,
"<uint8 value=\"0x%02x\"/>\n", data->val.uint8);
54 xmlOutput.append(snBuffer);
57 std::snprintf(snBuffer,
BUFFER_SIZE,
"<uint16 value=\"0x%04x\"/>\n", data->val.uint16);
58 xmlOutput.append(snBuffer);
61 std::snprintf(snBuffer,
BUFFER_SIZE,
"<uint32 value=\"0x%08x\"/>\n", data->val.uint32);
62 xmlOutput.append(snBuffer);
65 std::snprintf(snBuffer,
BUFFER_SIZE,
"<uint64 value=\"0x%016llx\"/>\n",
66 qulonglong(data->val.uint64));
67 xmlOutput.append(snBuffer);
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");
77 std::snprintf(snBuffer,
BUFFER_SIZE,
"<int8 value=\"%d\"/>/n", data->val.int8);
78 xmlOutput.append(snBuffer);
81 std::snprintf(snBuffer,
BUFFER_SIZE,
"<int16 value=\"%d\"/>/n", data->val.int16);
82 xmlOutput.append(snBuffer);
85 std::snprintf(snBuffer,
BUFFER_SIZE,
"<int32 value=\"%d\"/>/n", data->val.int32);
86 xmlOutput.append(snBuffer);
90 qlonglong(data->val.int64));
91 xmlOutput.append(snBuffer);
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");
100 case SDP_UUID_UNSPEC:
104 xmlOutput.append(
"<uuid value=\"0x");
105 sdp_uuid2strn(&(data->val.uuid), snBuffer,
BUFFER_SIZE);
106 xmlOutput.append(snBuffer);
107 xmlOutput.append(
"\"/>\n");
110 xmlOutput.append(
"<uuid value=\"");
111 sdp_uuid2strn(&(data->val.uuid), snBuffer,
BUFFER_SIZE);
112 xmlOutput.append(snBuffer);
113 xmlOutput.append(
"\"/>\n");
115 case SDP_TEXT_STR_UNSPEC:
121 xmlOutput.append(
"<text ");
122 QByteArray text = QByteArray::fromRawData(data->val.str, data->unitSize);
124 bool hasNonPrintableChar =
false;
125 for (qsizetype i = 0; i < text.size(); ++i) {
126 if (text[i] ==
'\0') {
129 }
else if (!isprint(text[i])) {
130 hasNonPrintableChar =
true;
131 const auto firstNullIdx = text.indexOf(
'\0');
132 if (firstNullIdx > 0)
133 text.resize(firstNullIdx);
138 if (hasNonPrintableChar) {
139 xmlOutput.append(
"encoding=\"hex\" value=\"");
140 xmlOutput.append(text.toHex());
142 text.replace(
'&',
"&");
143 text.replace(
'<',
"<");
144 text.replace(
'>',
">");
145 text.replace(
'"',
""");
147 xmlOutput.append(
"value=\"");
148 xmlOutput.append(text);
151 xmlOutput.append(
"\"/>\n");
156 xmlOutput.append(
"<boolean value=\"true\"/>\n");
158 xmlOutput.append(
"<boolean value=\"false\"/>\n");
165 xmlOutput.append(
"<sequence>\n");
166 parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput);
167 xmlOutput.append(indentString);
168 xmlOutput.append(
"</sequence>\n");
175 xmlOutput.append(
"<alternate>\n");
176 parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput);
177 xmlOutput.append(indentString);
178 xmlOutput.append(
"</alternate>\n");
180 case SDP_URL_STR_UNSPEC:
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);
192 xmlOutput.append(url.toEncoded().replace(
'&',
"&"));
193 xmlOutput.append(
"\"/>\n");
197 fprintf(stderr,
"Unknown dtd type\n");
200 parseAttributeValues(data->next, indentation, xmlOutput);
242 fprintf(stderr,
"SDP for %s %s\n", argv[1], argv[2]);
246 int result = str2ba(argv[1], &remote);
248 fprintf(stderr,
"Invalid remote address: %s\n", argv[1]);
252 result = str2ba(argv[2], &local);
254 fprintf(stderr,
"Invalid local address: %s\n", argv[2]);
258 bool showHumanReadable =
false;
259 std::vector<std::string> targetServices;
261 for (
int i = 3; i < argc; i++) {
262 if (argv[i][0] !=
'-') {
270 showHumanReadable =
true;
275 for ( ; i < argc && argv[i][0] ==
'{'; i++)
276 targetServices.push_back(argv[i]);
281 fprintf(stderr,
"Wrong argument: %s\n", argv[i]);
287 std::vector<uuid_t> uuids;
288 for (std::vector<std::string>::const_iterator iter = targetServices.cbegin();
289 iter != targetServices.cend(); ++iter) {
292 uint16_t field1, field2, field3, field5;
293 uint32_t field0, field4;
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()));
304 field0 = htonl(field0);
305 field4 = htonl(field4);
306 field1 = htons(field1);
307 field2 = htons(field2);
308 field3 = htons(field3);
309 field5 = htons(field5);
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);
320 sdp_uuid128_create(&sdpUuid, &temp128);
321 uuids.push_back(sdpUuid);
324 sdp_session_t *session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY);
327 session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY);
331 fprintf(stderr,
"Cannot establish sdp session\n");
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);
343 uint32_t attributeRange = 0x0000ffff;
344 sdp_list_t *attributes;
345 attributes = sdp_list_append(
nullptr, &attributeRange);
347 sdp_list_t *sdpResults, *sdpIter;
348 sdp_list_t *totalResults =
nullptr;
349 sdp_list_t* serviceFilter;
351 for (uuid_t &uuid : uuids) {
352 serviceFilter = sdp_list_append(
nullptr, &uuid);
353 result = sdp_service_search_attr_req(session, serviceFilter,
355 attributes, &sdpResults);
356 sdp_list_free(serviceFilter,
nullptr);
358 fprintf(stderr,
"sdp_service_search_attr_req failed\n");
359 sdp_list_free(attributes,
nullptr);
368 totalResults = sdpResults;
369 sdpIter = totalResults;
372 sdpIter->next = sdpResults;
375 while (sdpIter->next)
376 sdpIter = sdpIter->next;
378 sdp_list_free(attributes,
nullptr);
381 sdpResults = totalResults;
385 sdp_record_t *record = (sdp_record_t *) sdpResults->data;
387 const QByteArray xml = parseSdpRecord(record);
390 sdpIter = sdpResults;
391 sdpResults = sdpResults->next;
393 sdp_record_free(record);
396 if (!total.isEmpty()) {
397 if (showHumanReadable)
398 printf(
"%s", total.constData());
400 printf(
"%s", total.toBase64().constData());