mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-26 21:54:33 +08:00
358614b07e
According to assigned number 0x110f is AV Remote Controller not VCP.
805 lines
23 KiB
C
805 lines
23 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2001-2002 Ricky Yuen <ryuen@qualcomm.com>
|
|
* Copyright (C) 2003-2011 Marcel Holtmann <marcel@holtmann.org>
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "parser.h"
|
|
#include "sdp.h"
|
|
|
|
#define SDP_ERROR_RSP 0x01
|
|
#define SDP_SERVICE_SEARCH_REQ 0x02
|
|
#define SDP_SERVICE_SEARCH_RSP 0x03
|
|
#define SDP_SERVICE_ATTR_REQ 0x04
|
|
#define SDP_SERVICE_ATTR_RSP 0x05
|
|
#define SDP_SERVICE_SEARCH_ATTR_REQ 0x06
|
|
#define SDP_SERVICE_SEARCH_ATTR_RSP 0x07
|
|
|
|
typedef struct {
|
|
uint8_t pid;
|
|
uint16_t tid;
|
|
uint16_t len;
|
|
} __attribute__ ((packed)) sdp_pdu_hdr;
|
|
#define SDP_PDU_HDR_SIZE 5
|
|
|
|
/* Data element type descriptor */
|
|
#define SDP_DE_NULL 0
|
|
#define SDP_DE_UINT 1
|
|
#define SDP_DE_INT 2
|
|
#define SDP_DE_UUID 3
|
|
#define SDP_DE_STRING 4
|
|
#define SDP_DE_BOOL 5
|
|
#define SDP_DE_SEQ 6
|
|
#define SDP_DE_ALT 7
|
|
#define SDP_DE_URL 8
|
|
|
|
/* Data element size index lookup table */
|
|
typedef struct {
|
|
int addl_bits;
|
|
int num_bytes;
|
|
} sdp_siz_idx_lookup_table_t;
|
|
|
|
static sdp_siz_idx_lookup_table_t sdp_siz_idx_lookup_table[] = {
|
|
{ 0, 1 }, /* Size index = 0 */
|
|
{ 0, 2 }, /* 1 */
|
|
{ 0, 4 }, /* 2 */
|
|
{ 0, 8 }, /* 3 */
|
|
{ 0, 16 }, /* 4 */
|
|
{ 1, 1 }, /* 5 */
|
|
{ 1, 2 }, /* 6 */
|
|
{ 1, 4 }, /* 7 */
|
|
};
|
|
|
|
/* UUID name lookup table */
|
|
typedef struct {
|
|
int uuid;
|
|
char* name;
|
|
} sdp_uuid_nam_lookup_table_t;
|
|
|
|
static sdp_uuid_nam_lookup_table_t sdp_uuid_nam_lookup_table[] = {
|
|
{ SDP_UUID_SDP, "SDP" },
|
|
{ SDP_UUID_UDP, "UDP" },
|
|
{ SDP_UUID_RFCOMM, "RFCOMM" },
|
|
{ SDP_UUID_TCP, "TCP" },
|
|
{ SDP_UUID_TCS_BIN, "TCS-BIN" },
|
|
{ SDP_UUID_TCS_AT, "TCS-AT" },
|
|
{ SDP_UUID_OBEX, "OBEX" },
|
|
{ SDP_UUID_IP, "IP" },
|
|
{ SDP_UUID_FTP, "FTP" },
|
|
{ SDP_UUID_HTTP, "HTTP" },
|
|
{ SDP_UUID_WSP, "WSP" },
|
|
{ SDP_UUID_L2CAP, "L2CAP" },
|
|
{ SDP_UUID_BNEP, "BNEP" }, /* PAN */
|
|
{ SDP_UUID_HIDP, "HIDP" }, /* HID */
|
|
{ SDP_UUID_AVCTP, "AVCTP" }, /* AVCTP */
|
|
{ SDP_UUID_AVDTP, "AVDTP" }, /* AVDTP */
|
|
{ SDP_UUID_CMTP, "CMTP" }, /* CIP */
|
|
{ SDP_UUID_UDI_C_PLANE, "UDI_C-Plane" }, /* UDI */
|
|
{ SDP_UUID_SERVICE_DISCOVERY_SERVER, "SDServer" },
|
|
{ SDP_UUID_BROWSE_GROUP_DESCRIPTOR, "BrwsGrpDesc" },
|
|
{ SDP_UUID_PUBLIC_BROWSE_GROUP, "PubBrwsGrp" },
|
|
{ SDP_UUID_SERIAL_PORT, "SP" },
|
|
{ SDP_UUID_LAN_ACCESS_PPP, "LAN" },
|
|
{ SDP_UUID_DIALUP_NETWORKING, "DUN" },
|
|
{ SDP_UUID_IR_MC_SYNC, "IRMCSync" },
|
|
{ SDP_UUID_OBEX_OBJECT_PUSH, "OBEXObjPush" },
|
|
{ SDP_UUID_OBEX_FILE_TRANSFER, "OBEXObjTrnsf" },
|
|
{ SDP_UUID_IR_MC_SYNC_COMMAND, "IRMCSyncCmd" },
|
|
{ SDP_UUID_HEADSET, "Headset" },
|
|
{ SDP_UUID_CORDLESS_TELEPHONY, "CordlessTel" },
|
|
{ SDP_UUID_AUDIO_SOURCE, "AudioSource" }, /* A2DP */
|
|
{ SDP_UUID_AUDIO_SINK, "AudioSink" }, /* A2DP */
|
|
{ SDP_UUID_AV_REMOTE_TARGET, "AVRemTarget" }, /* AVRCP */
|
|
{ SDP_UUID_ADVANCED_AUDIO, "AdvAudio" }, /* A2DP */
|
|
{ SDP_UUID_AV_REMOTE, "AVRemote" }, /* AVRCP */
|
|
{ SDP_UUID_AV_REMOTE_CONTROLLER, "AVRemCt" }, /* AVRCP */
|
|
{ SDP_UUID_INTERCOM, "Intercom" },
|
|
{ SDP_UUID_FAX, "Fax" },
|
|
{ SDP_UUID_HEADSET_AUDIO_GATEWAY, "Headset AG" },
|
|
{ SDP_UUID_WAP, "WAP" },
|
|
{ SDP_UUID_WAP_CLIENT, "WAP Client" },
|
|
{ SDP_UUID_PANU, "PANU" }, /* PAN */
|
|
{ SDP_UUID_NAP, "NAP" }, /* PAN */
|
|
{ SDP_UUID_GN, "GN" }, /* PAN */
|
|
{ SDP_UUID_DIRECT_PRINTING, "DirectPrint" }, /* BPP */
|
|
{ SDP_UUID_REFERENCE_PRINTING, "RefPrint" }, /* BPP */
|
|
{ SDP_UUID_IMAGING, "Imaging" }, /* BIP */
|
|
{ SDP_UUID_IMAGING_RESPONDER, "ImagingResp" }, /* BIP */
|
|
{ SDP_UUID_HANDSFREE, "Handsfree" },
|
|
{ SDP_UUID_HANDSFREE_AUDIO_GATEWAY, "Handsfree AG" },
|
|
{ SDP_UUID_DIRECT_PRINTING_REF_OBJS, "RefObjsPrint" }, /* BPP */
|
|
{ SDP_UUID_REFLECTED_UI, "ReflectedUI" }, /* BPP */
|
|
{ SDP_UUID_BASIC_PRINTING, "BasicPrint" }, /* BPP */
|
|
{ SDP_UUID_PRINTING_STATUS, "PrintStatus" }, /* BPP */
|
|
{ SDP_UUID_HUMAN_INTERFACE_DEVICE, "HID" }, /* HID */
|
|
{ SDP_UUID_HARDCOPY_CABLE_REPLACE, "HCRP" }, /* HCRP */
|
|
{ SDP_UUID_HCR_PRINT, "HCRPrint" }, /* HCRP */
|
|
{ SDP_UUID_HCR_SCAN, "HCRScan" }, /* HCRP */
|
|
{ SDP_UUID_COMMON_ISDN_ACCESS, "CIP" }, /* CIP */
|
|
{ SDP_UUID_UDI_MT, "UDI MT" }, /* UDI */
|
|
{ SDP_UUID_UDI_TA, "UDI TA" }, /* UDI */
|
|
{ SDP_UUID_AUDIO_VIDEO, "AudioVideo" }, /* VCP */
|
|
{ SDP_UUID_SIM_ACCESS, "SAP" }, /* SAP */
|
|
{ SDP_UUID_PHONEBOOK_ACCESS_PCE, "PBAP PCE" }, /* PBAP */
|
|
{ SDP_UUID_PHONEBOOK_ACCESS_PSE, "PBAP PSE" }, /* PBAP */
|
|
{ SDP_UUID_PHONEBOOK_ACCESS, "PBAP" }, /* PBAP */
|
|
{ SDP_UUID_PNP_INFORMATION, "PNPInfo" },
|
|
{ SDP_UUID_GENERIC_NETWORKING, "Networking" },
|
|
{ SDP_UUID_GENERIC_FILE_TRANSFER, "FileTrnsf" },
|
|
{ SDP_UUID_GENERIC_AUDIO, "Audio" },
|
|
{ SDP_UUID_GENERIC_TELEPHONY, "Telephony" },
|
|
{ SDP_UUID_UPNP_SERVICE, "UPNP" }, /* ESDP */
|
|
{ SDP_UUID_UPNP_IP_SERVICE, "UPNP IP" }, /* ESDP */
|
|
{ SDP_UUID_ESDP_UPNP_IP_PAN, "UPNP PAN" }, /* ESDP */
|
|
{ SDP_UUID_ESDP_UPNP_IP_LAP, "UPNP LAP" }, /* ESDP */
|
|
{ SDP_UUID_ESDP_UPNP_L2CAP, "UPNP L2CAP" }, /* ESDP */
|
|
{ SDP_UUID_VIDEO_SOURCE, "VideoSource" }, /* VDP */
|
|
{ SDP_UUID_VIDEO_SINK, "VideoSink" }, /* VDP */
|
|
{ SDP_UUID_VIDEO_DISTRIBUTION, "VideoDist" }, /* VDP */
|
|
{ SDP_UUID_APPLE_AGENT, "AppleAgent" },
|
|
};
|
|
|
|
#define SDP_UUID_NAM_LOOKUP_TABLE_SIZE \
|
|
(sizeof(sdp_uuid_nam_lookup_table)/sizeof(sdp_uuid_nam_lookup_table_t))
|
|
|
|
/* AttrID name lookup table */
|
|
typedef struct {
|
|
int attr_id;
|
|
char* name;
|
|
} sdp_attr_id_nam_lookup_table_t;
|
|
|
|
static sdp_attr_id_nam_lookup_table_t sdp_attr_id_nam_lookup_table[] = {
|
|
{ SDP_ATTR_ID_SERVICE_RECORD_HANDLE, "SrvRecHndl" },
|
|
{ SDP_ATTR_ID_SERVICE_CLASS_ID_LIST, "SrvClassIDList" },
|
|
{ SDP_ATTR_ID_SERVICE_RECORD_STATE, "SrvRecState" },
|
|
{ SDP_ATTR_ID_SERVICE_SERVICE_ID, "SrvID" },
|
|
{ SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST, "ProtocolDescList" },
|
|
{ SDP_ATTR_ID_BROWSE_GROUP_LIST, "BrwGrpList" },
|
|
{ SDP_ATTR_ID_LANGUAGE_BASE_ATTRIBUTE_ID_LIST, "LangBaseAttrIDList" },
|
|
{ SDP_ATTR_ID_SERVICE_INFO_TIME_TO_LIVE, "SrvInfoTimeToLive" },
|
|
{ SDP_ATTR_ID_SERVICE_AVAILABILITY, "SrvAvail" },
|
|
{ SDP_ATTR_ID_BLUETOOTH_PROFILE_DESCRIPTOR_LIST, "BTProfileDescList" },
|
|
{ SDP_ATTR_ID_DOCUMENTATION_URL, "DocURL" },
|
|
{ SDP_ATTR_ID_CLIENT_EXECUTABLE_URL, "ClientExeURL" },
|
|
{ SDP_ATTR_ID_ICON_URL, "IconURL" },
|
|
{ SDP_ATTR_ID_ADDITIONAL_PROTOCOL_DESC_LISTS, "AdditionalProtocolDescLists" },
|
|
{ SDP_ATTR_ID_SERVICE_NAME, "SrvName" },
|
|
{ SDP_ATTR_ID_SERVICE_DESCRIPTION, "SrvDesc" },
|
|
{ SDP_ATTR_ID_PROVIDER_NAME, "ProviderName" },
|
|
{ SDP_ATTR_ID_VERSION_NUMBER_LIST, "VersionNumList" },
|
|
{ SDP_ATTR_ID_GROUP_ID, "GrpID" },
|
|
{ SDP_ATTR_ID_SERVICE_DATABASE_STATE, "SrvDBState" },
|
|
{ SDP_ATTR_ID_SERVICE_VERSION, "SrvVersion" },
|
|
{ SDP_ATTR_ID_SECURITY_DESCRIPTION, "SecurityDescription"}, /* PAN */
|
|
{ SDP_ATTR_ID_SUPPORTED_DATA_STORES_LIST, "SuppDataStoresList" }, /* Synchronization */
|
|
{ SDP_ATTR_ID_SUPPORTED_FORMATS_LIST, "SuppFormatsList" }, /* OBEX Object Push */
|
|
{ SDP_ATTR_ID_NET_ACCESS_TYPE, "NetAccessType" }, /* PAN */
|
|
{ SDP_ATTR_ID_MAX_NET_ACCESS_RATE, "MaxNetAccessRate" }, /* PAN */
|
|
{ SDP_ATTR_ID_IPV4_SUBNET, "IPv4Subnet" }, /* PAN */
|
|
{ SDP_ATTR_ID_IPV6_SUBNET, "IPv6Subnet" }, /* PAN */
|
|
{ SDP_ATTR_ID_SUPPORTED_CAPABILITIES, "SuppCapabilities" }, /* Imaging */
|
|
{ SDP_ATTR_ID_SUPPORTED_FEATURES, "SuppFeatures" }, /* Imaging and Hansfree */
|
|
{ SDP_ATTR_ID_SUPPORTED_FUNCTIONS, "SuppFunctions" }, /* Imaging */
|
|
{ SDP_ATTR_ID_TOTAL_IMAGING_DATA_CAPACITY, "SuppTotalCapacity" }, /* Imaging */
|
|
{ SDP_ATTR_ID_SUPPORTED_REPOSITORIES, "SuppRepositories" }, /* PBAP */
|
|
};
|
|
|
|
#define SDP_ATTR_ID_NAM_LOOKUP_TABLE_SIZE \
|
|
(sizeof(sdp_attr_id_nam_lookup_table)/sizeof(sdp_attr_id_nam_lookup_table_t))
|
|
|
|
char* get_uuid_name(int uuid)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < SDP_UUID_NAM_LOOKUP_TABLE_SIZE; i++) {
|
|
if (sdp_uuid_nam_lookup_table[i].uuid == uuid)
|
|
return sdp_uuid_nam_lookup_table[i].name;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline char* get_attr_id_name(int attr_id)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < SDP_ATTR_ID_NAM_LOOKUP_TABLE_SIZE; i++)
|
|
if (sdp_attr_id_nam_lookup_table[i].attr_id == attr_id)
|
|
return sdp_attr_id_nam_lookup_table[i].name;
|
|
return 0;
|
|
}
|
|
|
|
static inline uint8_t parse_de_hdr(struct frame *frm, int *n)
|
|
{
|
|
uint8_t de_hdr = get_u8(frm);
|
|
uint8_t de_type = de_hdr >> 3;
|
|
uint8_t siz_idx = de_hdr & 0x07;
|
|
|
|
/* Get the number of bytes */
|
|
if (sdp_siz_idx_lookup_table[siz_idx].addl_bits) {
|
|
switch(sdp_siz_idx_lookup_table[siz_idx].num_bytes) {
|
|
case 1:
|
|
*n = get_u8(frm); break;
|
|
case 2:
|
|
*n = get_u16(frm); break;
|
|
case 4:
|
|
*n = get_u32(frm); break;
|
|
case 8:
|
|
*n = get_u64(frm); break;
|
|
}
|
|
} else
|
|
*n = sdp_siz_idx_lookup_table[siz_idx].num_bytes;
|
|
|
|
return de_type;
|
|
}
|
|
|
|
static inline void print_int(uint8_t de_type, int level, int n, struct frame *frm, uint16_t *psm, uint8_t *channel)
|
|
{
|
|
uint64_t val, val2;
|
|
|
|
switch(de_type) {
|
|
case SDP_DE_UINT:
|
|
printf(" uint");
|
|
break;
|
|
case SDP_DE_INT:
|
|
printf(" int");
|
|
break;
|
|
case SDP_DE_BOOL:
|
|
printf(" bool");
|
|
break;
|
|
}
|
|
|
|
switch(n) {
|
|
case 1: /* 8-bit */
|
|
val = get_u8(frm);
|
|
if (channel && de_type == SDP_DE_UINT)
|
|
if (*channel == 0)
|
|
*channel = val;
|
|
break;
|
|
case 2: /* 16-bit */
|
|
val = get_u16(frm);
|
|
if (psm && de_type == SDP_DE_UINT)
|
|
if (*psm == 0)
|
|
*psm = val;
|
|
break;
|
|
case 4: /* 32-bit */
|
|
val = get_u32(frm);
|
|
break;
|
|
case 8: /* 64-bit */
|
|
val = get_u64(frm);
|
|
break;
|
|
case 16:/* 128-bit */
|
|
get_u128(frm, &val, &val2);
|
|
printf(" 0x%jx", val2);
|
|
if (val < 0x1000000000000000LL)
|
|
printf("0");
|
|
printf("%jx", val);
|
|
return;
|
|
default: /* syntax error */
|
|
printf(" err");
|
|
frm->ptr += n;
|
|
frm->len -= n;
|
|
return;
|
|
}
|
|
|
|
printf(" 0x%jx", val);
|
|
}
|
|
|
|
static inline void print_uuid(int n, struct frame *frm, uint16_t *psm, uint8_t *channel)
|
|
{
|
|
uint32_t uuid = 0;
|
|
char* s;
|
|
int i;
|
|
|
|
switch(n) {
|
|
case 2: /* 16-bit UUID */
|
|
uuid = get_u16(frm);
|
|
s = "uuid-16";
|
|
break;
|
|
case 4: /* 32_bit UUID */
|
|
uuid = get_u32(frm);
|
|
s = "uuid-32";
|
|
break;
|
|
case 16: /* 128-bit UUID */
|
|
printf(" uuid-128 ");
|
|
for (i = 0; i < 16; i++) {
|
|
printf("%02x", ((unsigned char *) frm->ptr)[i]);
|
|
if (i == 3 || i == 5 || i == 7 || i == 9)
|
|
printf("-");
|
|
}
|
|
frm->ptr += 16;
|
|
frm->len -= 16;
|
|
return;
|
|
default: /* syntax error */
|
|
printf(" *err*");
|
|
frm->ptr += n;
|
|
frm->len -= n;
|
|
return;
|
|
}
|
|
|
|
if (psm && *psm > 0 && *psm != 0xffff) {
|
|
set_proto(frm->handle, *psm, 0, uuid);
|
|
*psm = 0xffff;
|
|
}
|
|
|
|
if (channel && *channel > 0 && *channel != 0xff) {
|
|
set_proto(frm->handle, *psm, *channel, uuid);
|
|
*channel = 0xff;
|
|
}
|
|
|
|
printf(" %s 0x%04x", s, uuid);
|
|
if ((s = get_uuid_name(uuid)))
|
|
printf(" (%s)", s);
|
|
}
|
|
|
|
static inline void print_string(int n, struct frame *frm, const char *name)
|
|
{
|
|
int i, hex = 0;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
if (i == (n - 1) && ((char *) frm->ptr)[i] == '\0')
|
|
break;
|
|
|
|
if (!isprint(((char *) frm->ptr)[i])) {
|
|
hex = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf(" %s", name);
|
|
if (hex) {
|
|
for (i = 0; i < n; i++)
|
|
printf(" %02x", ((unsigned char *) frm->ptr)[i]);
|
|
} else {
|
|
printf(" \"");
|
|
for (i = 0; i < n; i++)
|
|
printf("%c", ((char *) frm->ptr)[i]);
|
|
printf("\"");
|
|
}
|
|
|
|
frm->ptr += n;
|
|
frm->len -= n;
|
|
}
|
|
|
|
static inline void print_de(int, struct frame *frm, int *split, uint16_t *psm, uint8_t *channel);
|
|
|
|
static inline void print_des(uint8_t de_type, int level, int n, struct frame *frm, int *split, uint16_t *psm, uint8_t *channel)
|
|
{
|
|
int len = frm->len;
|
|
while (len - (int) frm->len < n && (int) frm->len > 0)
|
|
print_de(level, frm, split, psm, channel);
|
|
}
|
|
|
|
static inline void print_de(int level, struct frame *frm, int *split, uint16_t *psm, uint8_t *channel)
|
|
{
|
|
int n = 0;
|
|
uint8_t de_type = parse_de_hdr(frm, &n);
|
|
|
|
switch (de_type) {
|
|
case SDP_DE_NULL:
|
|
printf(" null");
|
|
break;
|
|
case SDP_DE_UINT:
|
|
case SDP_DE_INT:
|
|
case SDP_DE_BOOL:
|
|
print_int(de_type, level, n, frm, psm, channel);
|
|
break;
|
|
case SDP_DE_UUID:
|
|
if (split) {
|
|
/* Split output by uuids.
|
|
* Used for printing Protocol Desc List */
|
|
if (*split) {
|
|
printf("\n");
|
|
p_indent(level, NULL);
|
|
}
|
|
++*split;
|
|
}
|
|
print_uuid(n, frm, psm, channel);
|
|
break;
|
|
case SDP_DE_URL:
|
|
case SDP_DE_STRING:
|
|
print_string(n, frm, de_type == SDP_DE_URL? "url": "str");
|
|
break;
|
|
case SDP_DE_SEQ:
|
|
printf(" <");
|
|
print_des(de_type, level, n, frm, split, psm, channel);
|
|
printf(" >");
|
|
break;
|
|
case SDP_DE_ALT:
|
|
printf(" [");
|
|
print_des(de_type, level, n, frm, split, psm, channel);
|
|
printf(" ]");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline void print_srv_srch_pat(int level, struct frame *frm)
|
|
{
|
|
int len, n1 = 0, n2 = 0;
|
|
|
|
p_indent(level, frm);
|
|
printf("pat");
|
|
|
|
if (parse_de_hdr(frm, &n1) == SDP_DE_SEQ) {
|
|
len = frm->len;
|
|
while (len - (int) frm->len < n1 && (int) frm->len > 0) {
|
|
if (parse_de_hdr(frm, &n2) == SDP_DE_UUID) {
|
|
print_uuid(n2, frm, NULL, NULL);
|
|
} else {
|
|
printf("\nERROR: Unexpected syntax (UUID)\n");
|
|
raw_dump(level, frm);
|
|
}
|
|
}
|
|
printf("\n");
|
|
} else {
|
|
printf("\nERROR: Unexpected syntax (SEQ)\n");
|
|
raw_dump(level, frm);
|
|
}
|
|
}
|
|
|
|
static inline void print_attr_id_list(int level, struct frame *frm)
|
|
{
|
|
uint16_t attr_id;
|
|
uint32_t attr_id_range;
|
|
int len, n1 = 0, n2 = 0;
|
|
|
|
p_indent(level, frm);
|
|
printf("aid(s)");
|
|
|
|
if (parse_de_hdr(frm, &n1) == SDP_DE_SEQ) {
|
|
len = frm->len;
|
|
while (len - (int) frm->len < n1 && (int) frm->len > 0) {
|
|
/* Print AttributeID */
|
|
if (parse_de_hdr(frm, &n2) == SDP_DE_UINT) {
|
|
char *name;
|
|
switch(n2) {
|
|
case 2:
|
|
attr_id = get_u16(frm);
|
|
name = get_attr_id_name(attr_id);
|
|
if (!name)
|
|
name = "unknown";
|
|
printf(" 0x%04x (%s)", attr_id, name);
|
|
break;
|
|
case 4:
|
|
attr_id_range = get_u32(frm);
|
|
printf(" 0x%04x - 0x%04x",
|
|
(attr_id_range >> 16),
|
|
(attr_id_range & 0xFFFF));
|
|
break;
|
|
}
|
|
} else {
|
|
printf("\nERROR: Unexpected syntax\n");
|
|
raw_dump(level, frm);
|
|
}
|
|
}
|
|
printf("\n");
|
|
} else {
|
|
printf("\nERROR: Unexpected syntax\n");
|
|
raw_dump(level, frm);
|
|
}
|
|
}
|
|
|
|
static inline void print_attr_list(int level, struct frame *frm)
|
|
{
|
|
uint16_t attr_id, psm;
|
|
uint8_t channel;
|
|
int len, split, n1 = 0, n2 = 0;
|
|
|
|
if (parse_de_hdr(frm, &n1) == SDP_DE_SEQ) {
|
|
len = frm->len;
|
|
while (len - (int) frm->len < n1 && (int) frm->len > 0) {
|
|
/* Print AttributeID */
|
|
if (parse_de_hdr(frm, &n2) == SDP_DE_UINT && n2 == sizeof(attr_id)) {
|
|
char *name;
|
|
attr_id = get_u16(frm);
|
|
p_indent(level, 0);
|
|
name = get_attr_id_name(attr_id);
|
|
if (!name)
|
|
name = "unknown";
|
|
printf("aid 0x%04x (%s)\n", attr_id, name);
|
|
split = (attr_id != SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST);
|
|
psm = 0;
|
|
channel = 0;
|
|
|
|
/* Print AttributeValue */
|
|
p_indent(level + 1, 0);
|
|
print_de(level + 1, frm, split ? NULL: &split,
|
|
attr_id == SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST ? &psm : NULL,
|
|
attr_id == SDP_ATTR_ID_PROTOCOL_DESCRIPTOR_LIST ? &channel : NULL);
|
|
printf("\n");
|
|
} else {
|
|
printf("\nERROR: Unexpected syntax\n");
|
|
raw_dump(level, frm);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
printf("\nERROR: Unexpected syntax\n");
|
|
raw_dump(level, frm);
|
|
}
|
|
}
|
|
|
|
static inline void print_attr_lists(int level, struct frame *frm)
|
|
{
|
|
int n = 0, cnt = 0;
|
|
int count = frm->len;
|
|
|
|
if (parse_de_hdr(frm, &n) == SDP_DE_SEQ) {
|
|
while (count - (int) frm->len < n && (int) frm->len > 0) {
|
|
p_indent(level, 0);
|
|
printf("record #%d\n", cnt++);
|
|
print_attr_list(level + 2, frm);
|
|
}
|
|
} else {
|
|
printf("\nERROR: Unexpected syntax\n");
|
|
raw_dump(level, frm);
|
|
}
|
|
}
|
|
|
|
static inline void print_cont_state(int level, unsigned char *buf)
|
|
{
|
|
uint8_t cont = buf[0];
|
|
int i;
|
|
|
|
p_indent(level, 0);
|
|
printf("cont");
|
|
for (i = 0; i < cont + 1; i++)
|
|
printf(" %2.2X", buf[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static char *pid2str(uint8_t pid)
|
|
{
|
|
switch (pid) {
|
|
case SDP_ERROR_RSP:
|
|
return "Error Rsp";
|
|
case SDP_SERVICE_SEARCH_REQ:
|
|
return "SS Req";
|
|
case SDP_SERVICE_SEARCH_RSP:
|
|
return "SS Rsp";
|
|
case SDP_SERVICE_ATTR_REQ:
|
|
return "SA Req";
|
|
case SDP_SERVICE_ATTR_RSP:
|
|
return "SA Rsp";
|
|
case SDP_SERVICE_SEARCH_ATTR_REQ:
|
|
return "SSA Req";
|
|
case SDP_SERVICE_SEARCH_ATTR_RSP:
|
|
return "SSA Rsp";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
#define FRAME_TABLE_SIZE 10
|
|
|
|
static struct frame frame_table[FRAME_TABLE_SIZE];
|
|
|
|
static int frame_add(struct frame *frm, int count)
|
|
{
|
|
register struct frame *fr;
|
|
register unsigned char *data;
|
|
register int i, len = 0, pos = -1;
|
|
|
|
for (i = 0; i < FRAME_TABLE_SIZE; i++) {
|
|
if (frame_table[i].handle == frm->handle &&
|
|
frame_table[i].cid == frm->cid) {
|
|
pos = i;
|
|
len = frame_table[i].data_len;
|
|
break;
|
|
}
|
|
if (pos < 0 && !frame_table[i].handle)
|
|
pos = i;
|
|
}
|
|
|
|
if (pos < 0 || count <= 0)
|
|
return -EIO;
|
|
|
|
data = malloc(len + count);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
fr = &frame_table[pos];
|
|
|
|
if (len > 0) {
|
|
memcpy(data, fr->data, len);
|
|
memcpy(data + len, frm->ptr, count);
|
|
} else
|
|
memcpy(data, frm->ptr, count);
|
|
|
|
if (fr->data)
|
|
free(fr->data);
|
|
|
|
fr->data = data;
|
|
fr->data_len = len + count;
|
|
fr->len = fr->data_len;
|
|
fr->ptr = fr->data;
|
|
fr->dev_id = frm->dev_id;
|
|
fr->in = frm->in;
|
|
fr->ts = frm->ts;
|
|
fr->handle = frm->handle;
|
|
fr->cid = frm->cid;
|
|
fr->num = frm->num;
|
|
fr->channel = frm->channel;
|
|
fr->pppdump_fd = frm->pppdump_fd;
|
|
fr->audio_fd = frm->audio_fd;
|
|
|
|
return pos;
|
|
}
|
|
|
|
static struct frame *frame_get(struct frame *frm, int count)
|
|
{
|
|
register int pos;
|
|
|
|
pos = frame_add(frm, count);
|
|
if (pos < 0)
|
|
return frm;
|
|
|
|
frame_table[pos].handle = 0;
|
|
|
|
return &frame_table[pos];
|
|
}
|
|
|
|
void sdp_dump(int level, struct frame *frm)
|
|
{
|
|
sdp_pdu_hdr *hdr = frm->ptr;
|
|
uint16_t tid = ntohs(hdr->tid);
|
|
uint16_t len = ntohs(hdr->len);
|
|
uint16_t total, count;
|
|
uint8_t cont;
|
|
|
|
frm->ptr += SDP_PDU_HDR_SIZE;
|
|
frm->len -= SDP_PDU_HDR_SIZE;
|
|
|
|
p_indent(level, frm);
|
|
printf("SDP %s: tid 0x%x len 0x%x\n", pid2str(hdr->pid), tid, len);
|
|
|
|
switch (hdr->pid) {
|
|
case SDP_ERROR_RSP:
|
|
p_indent(level + 1, frm);
|
|
printf("code 0x%x info ", get_u16(frm));
|
|
if (frm->len > 0)
|
|
hex_dump(0, frm, frm->len);
|
|
else
|
|
printf("none\n");
|
|
break;
|
|
|
|
case SDP_SERVICE_SEARCH_REQ:
|
|
/* Parse ServiceSearchPattern */
|
|
print_srv_srch_pat(level + 1, frm);
|
|
|
|
/* Parse MaximumServiceRecordCount */
|
|
p_indent(level + 1, frm);
|
|
printf("max %d\n", get_u16(frm));
|
|
|
|
/* Parse ContinuationState */
|
|
print_cont_state(level + 1, frm->ptr);
|
|
break;
|
|
|
|
case SDP_SERVICE_SEARCH_RSP:
|
|
/* Parse TotalServiceRecordCount */
|
|
total = get_u16(frm);
|
|
|
|
/* Parse CurrentServiceRecordCount */
|
|
count = get_u16(frm);
|
|
p_indent(level + 1, frm);
|
|
if (count < total)
|
|
printf("count %d of %d\n", count, total);
|
|
else
|
|
printf("count %d\n", count);
|
|
|
|
/* Parse service record handle(s) */
|
|
if (count > 0) {
|
|
int i;
|
|
p_indent(level + 1, frm);
|
|
printf("handle%s", count > 1 ? "s" : "");
|
|
for (i = 0; i < count; i++)
|
|
printf(" 0x%x", get_u32(frm));
|
|
printf("\n");
|
|
}
|
|
|
|
/* Parse ContinuationState */
|
|
print_cont_state(level + 1, frm->ptr);
|
|
break;
|
|
|
|
case SDP_SERVICE_ATTR_REQ:
|
|
/* Parse ServiceRecordHandle */
|
|
p_indent(level + 1, frm);
|
|
printf("handle 0x%x\n", get_u32(frm));
|
|
|
|
/* Parse MaximumAttributeByteCount */
|
|
p_indent(level + 1, frm);
|
|
printf("max %d\n", get_u16(frm));
|
|
|
|
/* Parse ServiceSearchPattern */
|
|
print_attr_id_list(level + 1, frm);
|
|
|
|
/* Parse ContinuationState */
|
|
print_cont_state(level + 1, frm->ptr);
|
|
break;
|
|
|
|
case SDP_SERVICE_ATTR_RSP:
|
|
/* Parse AttributeByteCount */
|
|
count = get_u16(frm);
|
|
p_indent(level + 1, frm);
|
|
printf("count %d\n", count);
|
|
|
|
/* Parse ContinuationState */
|
|
cont = *(unsigned char *)(frm->ptr + count);
|
|
|
|
if (cont == 0) {
|
|
/* Parse AttributeList */
|
|
print_attr_list(level + 1, frame_get(frm, count));
|
|
} else
|
|
frame_add(frm, count);
|
|
|
|
print_cont_state(level + 1, frm->ptr + count);
|
|
break;
|
|
|
|
case SDP_SERVICE_SEARCH_ATTR_REQ:
|
|
/* Parse ServiceSearchPattern */
|
|
print_srv_srch_pat(level + 1, frm);
|
|
|
|
/* Parse MaximumAttributeByteCount */
|
|
p_indent(level + 1, frm);
|
|
printf("max %d\n", get_u16(frm));
|
|
|
|
/* Parse AttributeList */
|
|
print_attr_id_list(level + 1, frm);
|
|
|
|
/* Parse ContinuationState */
|
|
print_cont_state(level + 1, frm->ptr);
|
|
break;
|
|
|
|
case SDP_SERVICE_SEARCH_ATTR_RSP:
|
|
/* Parse AttributeByteCount */
|
|
count = get_u16(frm);
|
|
p_indent(level + 1, frm);
|
|
printf("count %d\n", count);
|
|
|
|
/* Parse ContinuationState */
|
|
cont = *(unsigned char *)(frm->ptr + count);
|
|
|
|
if (cont == 0) {
|
|
/* Parse AttributeLists */
|
|
print_attr_lists(level + 1, frame_get(frm, count));
|
|
} else
|
|
frame_add(frm, count);
|
|
|
|
print_cont_state(level + 1, frm->ptr + count);
|
|
break;
|
|
|
|
default:
|
|
raw_dump(level + 1, frm);
|
|
break;
|
|
}
|
|
}
|