summaryrefslogtreecommitdiffstats
path: root/src/tools
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@digia.com>2014-07-17 14:51:45 +0200
committerAlex Blasche <alexander.blasche@digia.com>2014-07-23 08:30:29 +0200
commit3ee0be10135f46fd482987a5229a0f199b7bf7c1 (patch)
tree29a569653cb1dba196b5f931c2c36960864f256f /src/tools
parent091322bcd89e8a68ac4cd660055ffa5c270df276 (diff)
Add small binary performing the SDP scan.
This is required to avoid tainting of QtBluetooth with GPL code from Bluez. Subsequent patches will remove GPL code from the new Bluez5 backend in the library and call this binary instead. Change-Id: Iff62ecb430d4a486a2d1f7382ba5dc48b229fea5 Reviewed-by: Aaron McCarthy <mccarthy.aaron@gmail.com>
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/sdpscanner/main.cpp328
-rw-r--r--src/tools/sdpscanner/sdpscanner.pro24
2 files changed, 352 insertions, 0 deletions
diff --git a/src/tools/sdpscanner/main.cpp b/src/tools/sdpscanner/main.cpp
new file mode 100644
index 00000000..20ef83f6
--- /dev/null
+++ b/src/tools/sdpscanner/main.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDebug>
+#include <stdio.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#define RETURN_SUCCESS 0
+#define RETURN_USAGE 1
+#define RETURN_INVALPARAM 2
+#define RETURN_SDP_ERROR 3
+
+void usage()
+{
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, "\tsdpscanner <remote bdaddr> <local bdaddr>\n\n");
+ fprintf(stderr, "Performs an SDP scan on remote device, using the SDP server\n"
+ "represented by the local Bluetooth device.\n");
+}
+
+#define BUFFER_SIZE 1024
+
+static void parseAttributeValues(sdp_data_t *data, int indentation, QByteArray &xmlOutput)
+{
+ if (!data)
+ return;
+
+ const int length = indentation*2 + 1;
+ QByteArray indentString(length, ' ');
+
+ char snBuffer[BUFFER_SIZE];
+
+ xmlOutput.append(indentString);
+
+ // deal with every dtd type
+ switch (data->dtd) {
+ case SDP_DATA_NIL:
+ xmlOutput.append("<nil/>\n");
+ break;
+ case SDP_UINT8:
+ qsnprintf(snBuffer, BUFFER_SIZE, "<uint8 value=\"0x%02x\"/>\n", data->val.uint8);
+ xmlOutput.append(snBuffer);
+ break;
+ case SDP_UINT16:
+ qsnprintf(snBuffer, BUFFER_SIZE, "<uint16 value=\"0x%04x\"/>\n", data->val.uint16);
+ xmlOutput.append(snBuffer);
+ break;
+ case SDP_UINT32:
+ qsnprintf(snBuffer, BUFFER_SIZE, "<uint32 value=\"0x%08x\"/>\n", data->val.uint32);
+ xmlOutput.append(snBuffer);
+ break;
+ case SDP_UINT64:
+ qsnprintf(snBuffer, BUFFER_SIZE, "<uint64 value=\"0x%016x\"/>\n", data->val.uint64);
+ xmlOutput.append(snBuffer);
+ break;
+ case SDP_UINT128:
+ xmlOutput.append("<uint128 value=\"0x");
+ for (int i = 0; i < 16; i++)
+ ::sprintf(&snBuffer[i * 2], "%02x", data->val.uint128.data[i]);
+ xmlOutput.append(snBuffer);
+ xmlOutput.append("\"/>\n");
+ break;
+ case SDP_INT8:
+ qsnprintf(snBuffer, BUFFER_SIZE, "<int8 value=\"%d\"/>/n", data->val.int8);
+ xmlOutput.append(snBuffer);
+ break;
+ case SDP_INT16:
+ qsnprintf(snBuffer, BUFFER_SIZE, "<int16 value=\"%d\"/>/n", data->val.int16);
+ xmlOutput.append(snBuffer);
+ break;
+ case SDP_INT32:
+ qsnprintf(snBuffer, BUFFER_SIZE, "<int32 value=\"%d\"/>/n", data->val.int32);
+ xmlOutput.append(snBuffer);
+ break;
+ case SDP_INT64:
+ qsnprintf(snBuffer, BUFFER_SIZE, "<int64 value=\"%d\"/>/n", data->val.int64);
+ xmlOutput.append(snBuffer);
+ break;
+ case SDP_INT128:
+ xmlOutput.append("<int128 value=\"0x");
+ for (int i = 0; i < 16; i++)
+ ::sprintf(&snBuffer[i * 2], "%02x", data->val.int128.data[i]);
+ xmlOutput.append(snBuffer);
+ xmlOutput.append("\"/>\n");
+ break;
+ case SDP_UUID_UNSPEC:
+ break;
+ case SDP_UUID16:
+ case SDP_UUID32:
+ xmlOutput.append("<uuid value=\"0x");
+ sdp_uuid2strn(&(data->val.uuid), snBuffer, BUFFER_SIZE);
+ xmlOutput.append(snBuffer);
+ xmlOutput.append("\"/>\n");
+ break;
+ case SDP_UUID128:
+ xmlOutput.append("<uuid value=\"");
+ sdp_uuid2strn(&(data->val.uuid), snBuffer, BUFFER_SIZE);
+ xmlOutput.append(snBuffer);
+ xmlOutput.append("\"/>\n");
+ break;
+ case SDP_TEXT_STR_UNSPEC:
+ break;
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ {
+ xmlOutput.append("<text ");
+ QByteArray text = QByteArray::fromRawData(data->val.str, data->unitSize);
+
+ bool hasNonPrintableChar = false;
+ for (int i = 0; i < text.count() && !hasNonPrintableChar; i++) {
+ if (!isprint(text[i])) {
+ hasNonPrintableChar = true;
+ break;
+ }
+ }
+
+ if (hasNonPrintableChar) {
+ xmlOutput.append("encoding=\"hex\" value=\"");
+ xmlOutput.append(text.toHex());
+ } else {
+ text.replace("&", "&amp");
+ text.replace("<", "&lt");
+ text.replace(">", "&gt");
+ text.replace("\"", "&quot");
+
+ xmlOutput.append("value=\"");
+ xmlOutput.append(text);
+ }
+
+ xmlOutput.append("\"/>\n");
+ break;
+ }
+ case SDP_BOOL:
+ if (data->val.uint8)
+ xmlOutput.append("<boolean value=\"true\"/>\n");
+ else
+ xmlOutput.append("<boolean value=\"false\"/>\n");
+ break;
+ case SDP_SEQ_UNSPEC:
+ break;
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ xmlOutput.append("<sequence>\n");
+ parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput);
+ xmlOutput.append(indentString);
+ xmlOutput.append("</sequence>\n");
+ break;
+ case SDP_ALT_UNSPEC:
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ xmlOutput.append("<alternate>\n");
+ parseAttributeValues(data->val.dataseq, indentation + 1, xmlOutput);
+ xmlOutput.append(indentString);
+ xmlOutput.append("</alternate>\n");
+ break;
+ case SDP_URL_STR_UNSPEC:
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ strncpy(snBuffer, data->val.str, data->unitSize - 1);
+ xmlOutput.append("<url value=\"");
+ xmlOutput.append(snBuffer);
+ xmlOutput.append("\"/>\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown dtd type\n");
+ }
+
+ parseAttributeValues(data->next, indentation, xmlOutput);
+}
+
+static void parseAttribute(void *value, void *extraData)
+{
+ sdp_data_t *data = (sdp_data_t *) value;
+ QByteArray *xmlOutput = static_cast<QByteArray *>(extraData);
+
+ char buffer[BUFFER_SIZE];
+
+ ::qsnprintf(buffer, BUFFER_SIZE, " <attribute id=\"0x%04x\">\n", data->attrId);
+ xmlOutput->append(buffer);
+
+ parseAttributeValues(data, 2, *xmlOutput);
+
+ xmlOutput->append(" </attribute>\n");
+}
+
+// the resulting xml output is based on the already used xml parser
+QByteArray parseSdpRecord(sdp_record_t *record)
+{
+ if (!record || !record->attrlist)
+ return QByteArray();
+
+ QByteArray xmlOutput;
+
+ xmlOutput.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<record>\n");
+
+ sdp_list_foreach(record->attrlist, parseAttribute, &xmlOutput);
+ xmlOutput.append("</record>");
+
+ return xmlOutput;
+}
+
+
+int main(int argc, char **argv)
+{
+ if (argc != 3) {
+ usage();
+ return RETURN_USAGE;
+ }
+
+ printf("SDP for %s %s\n", argv[1], argv[2]);
+
+ bdaddr_t remote;
+ bdaddr_t local;
+ int result = str2ba(argv[1], &remote);
+ if (result < 0) {
+ fprintf(stderr, "Invalid remote address: %s\n", argv[1]);
+ return RETURN_INVALPARAM;
+ }
+
+ result = str2ba(argv[2], &local);
+ if (result < 0) {
+ fprintf(stderr, "Invalid local address: %s\n", argv[2]);
+ return RETURN_INVALPARAM;
+ }
+
+ sdp_session_t *session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY);
+ if (!session) {
+ //try one more time if first time failed
+ session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY);
+ }
+
+ if (!session) {
+ fprintf(stderr, "Cannot establish sdp session\n");
+ return RETURN_SDP_ERROR;
+ }
+
+ // set the filter for service matches
+ uuid_t publicBrowseGroupUuid;
+ sdp_uuid16_create(&publicBrowseGroupUuid, PUBLIC_BROWSE_GROUP);
+ sdp_list_t *serviceFilter;
+ serviceFilter = sdp_list_append(0, &publicBrowseGroupUuid);
+
+ uint32_t attributeRange = 0x0000ffff; //all attributes
+ sdp_list_t *attributes;
+ attributes = sdp_list_append(0, &attributeRange);
+
+ sdp_list_t *sdpResults, *previous;
+ result = sdp_service_search_attr_req(session, serviceFilter,
+ SDP_ATTR_REQ_RANGE,
+ attributes, &sdpResults);
+ sdp_list_free(attributes, 0);
+ sdp_list_free(serviceFilter, 0);
+
+ if (result != 0) {
+ fprintf(stderr, "sdp_service_search_attr_req failed\n");
+ sdp_close(session);
+ return RETURN_SDP_ERROR;
+ }
+
+ QStringList xmlRecords;
+ while (sdpResults) {
+ sdp_record_t *record = (sdp_record_t *) sdpResults->data;
+
+ QByteArray xml = parseSdpRecord(record);
+ if (xml.isEmpty())
+ continue;
+
+ qDebug() << xml;
+ xmlRecords.append(QString::fromUtf8(xml));
+
+ previous = sdpResults;
+ sdpResults = sdpResults->next;
+ free(previous);
+ sdp_record_free(record);
+
+ }
+
+ sdp_close(session);
+
+ return RETURN_SUCCESS;
+}
diff --git a/src/tools/sdpscanner/sdpscanner.pro b/src/tools/sdpscanner/sdpscanner.pro
new file mode 100644
index 00000000..6bf0a96c
--- /dev/null
+++ b/src/tools/sdpscanner/sdpscanner.pro
@@ -0,0 +1,24 @@
+TEMPLATE = app
+TARGET = sdpscanner
+
+QT = core
+
+SOURCES = main.cpp
+
+CONFIG += link_pkgconfig
+PKGCONFIG_PRIVATE += bluez
+
+load(qt_tool)
+
+linux-*: {
+ # bluetooth.h is not standards compliant
+ contains(QMAKE_CXXFLAGS, -std=c++0x) {
+ QMAKE_CXXFLAGS -= -std=c++0x
+ QMAKE_CXXFLAGS += -std=gnu++0x
+ CONFIG -= c++11
+ }
+ c++11 {
+ CONFIG -= c++11
+ QMAKE_CXXFLAGS += -std=gnu++0x
+ }
+}