352 lines
6.6 KiB
C
352 lines
6.6 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2004-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 <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "parser.h"
|
|
|
|
static char *opcode2str(uint8_t opcode)
|
|
{
|
|
switch (opcode & 0x7f) {
|
|
case 0x00:
|
|
return "Connect";
|
|
case 0x01:
|
|
return "Disconnect";
|
|
case 0x02:
|
|
return "Put";
|
|
case 0x03:
|
|
return "Get";
|
|
case 0x04:
|
|
return "Reserved";
|
|
case 0x05:
|
|
return "SetPath";
|
|
case 0x06:
|
|
return "Action";
|
|
case 0x07:
|
|
return "Session";
|
|
case 0x7f:
|
|
return "Abort";
|
|
case 0x10:
|
|
return "Continue";
|
|
case 0x20:
|
|
return "Success";
|
|
case 0x21:
|
|
return "Created";
|
|
case 0x22:
|
|
return "Accepted";
|
|
case 0x23:
|
|
return "Non-authoritative information";
|
|
case 0x24:
|
|
return "No content";
|
|
case 0x25:
|
|
return "Reset content";
|
|
case 0x26:
|
|
return "Partial content";
|
|
case 0x30:
|
|
return "Multiple choices";
|
|
case 0x31:
|
|
return "Moved permanently";
|
|
case 0x32:
|
|
return "Moved temporarily";
|
|
case 0x33:
|
|
return "See other";
|
|
case 0x34:
|
|
return "Not modified";
|
|
case 0x35:
|
|
return "Use Proxy";
|
|
case 0x40:
|
|
return "Bad request";
|
|
case 0x41:
|
|
return "Unauthorized";
|
|
case 0x42:
|
|
return "Payment required";
|
|
case 0x43:
|
|
return "Forbidden";
|
|
case 0x44:
|
|
return "Not found";
|
|
case 0x45:
|
|
return "Method not allowed";
|
|
case 0x46:
|
|
return "Not acceptable";
|
|
case 0x47:
|
|
return "Proxy authentication required";
|
|
case 0x48:
|
|
return "Request timeout";
|
|
case 0x49:
|
|
return "Conflict";
|
|
case 0x4a:
|
|
return "Gone";
|
|
case 0x4b:
|
|
return "Length required";
|
|
case 0x4c:
|
|
return "Precondition failed";
|
|
case 0x4d:
|
|
return "Requested entity too large";
|
|
case 0x4e:
|
|
return "Requested URL too large";
|
|
case 0x4f:
|
|
return "Unsupported media type";
|
|
case 0x50:
|
|
return "Internal server error";
|
|
case 0x51:
|
|
return "Not implemented";
|
|
case 0x52:
|
|
return "Bad gateway";
|
|
case 0x53:
|
|
return "Service unavailable";
|
|
case 0x54:
|
|
return "Gateway timeout";
|
|
case 0x55:
|
|
return "HTTP version not supported";
|
|
case 0x60:
|
|
return "Database full";
|
|
case 0x61:
|
|
return "Database locked";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
static char *hi2str(uint8_t hi)
|
|
{
|
|
switch (hi & 0x3f) {
|
|
case 0x00:
|
|
return "Count";
|
|
case 0x01:
|
|
return "Name";
|
|
case 0x02:
|
|
return "Type";
|
|
case 0x03:
|
|
return "Length";
|
|
case 0x04:
|
|
return "Time";
|
|
case 0x05:
|
|
return "Description";
|
|
case 0x06:
|
|
return "Target";
|
|
case 0x07:
|
|
return "HTTP";
|
|
case 0x08:
|
|
return "Body";
|
|
case 0x09:
|
|
return "End of Body";
|
|
case 0x0a:
|
|
return "Who";
|
|
case 0x0b:
|
|
return "Connection ID";
|
|
case 0x0c:
|
|
return "App. Parameters";
|
|
case 0x0d:
|
|
return "Auth. Challenge";
|
|
case 0x0e:
|
|
return "Auth. Response";
|
|
case 0x0f:
|
|
return "Creator ID";
|
|
case 0x10:
|
|
return "WAN UUID";
|
|
case 0x11:
|
|
return "Object Class";
|
|
case 0x12:
|
|
return "Session Parameters";
|
|
case 0x13:
|
|
return "Session Sequence Number";
|
|
case 0x14:
|
|
return "Action ID";
|
|
case 0x15:
|
|
return "DestName";
|
|
case 0x16:
|
|
return "Permission";
|
|
case 0x17:
|
|
return "Single Response Mode";
|
|
case 0x18:
|
|
return "Single Response Mode Parameters";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
static void parse_headers(int level, struct frame *frm)
|
|
{
|
|
uint8_t hi, hv8;
|
|
uint16_t len;
|
|
uint32_t hv32;
|
|
|
|
while (frm->len > 0) {
|
|
hi = get_u8(frm);
|
|
|
|
p_indent(level, frm);
|
|
|
|
printf("%s (0x%02x)", hi2str(hi), hi);
|
|
switch (hi & 0xc0) {
|
|
case 0x00: /* Unicode */
|
|
if (frm->len < 2) {
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
len = get_u16(frm) - 3;
|
|
printf(" = Unicode length %d\n", len);
|
|
|
|
if (frm->len < len)
|
|
return;
|
|
|
|
raw_ndump(level, frm, len);
|
|
frm->ptr += len;
|
|
frm->len -= len;
|
|
break;
|
|
|
|
case 0x40: /* Byte sequence */
|
|
if (frm->len < 2) {
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
len = get_u16(frm) - 3;
|
|
printf(" = Sequence length %d\n", len);
|
|
|
|
if (frm->len < len)
|
|
return;
|
|
|
|
raw_ndump(level, frm, len);
|
|
frm->ptr += len;
|
|
frm->len -= len;
|
|
break;
|
|
|
|
case 0x80: /* One byte */
|
|
if (frm->len < 1) {
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
hv8 = get_u8(frm);
|
|
printf(" = %d\n", hv8);
|
|
break;
|
|
|
|
case 0xc0: /* Four bytes */
|
|
if (frm->len < 4) {
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
hv32 = get_u32(frm);
|
|
printf(" = %u\n", hv32);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void obex_dump(int level, struct frame *frm)
|
|
{
|
|
uint8_t last_opcode, opcode, status;
|
|
uint8_t version, flags, constants;
|
|
uint16_t length, pktlen;
|
|
|
|
frm = add_frame(frm);
|
|
|
|
while (frm->len > 2) {
|
|
opcode = get_u8(frm);
|
|
length = get_u16(frm);
|
|
status = opcode & 0x7f;
|
|
|
|
if ((int) frm->len < length - 3) {
|
|
frm->ptr -= 3;
|
|
frm->len += 3;
|
|
return;
|
|
}
|
|
|
|
p_indent(level, frm);
|
|
|
|
last_opcode = get_opcode(frm->handle, frm->dlci);
|
|
|
|
if (!(opcode & 0x70)) {
|
|
printf("OBEX: %s cmd(%c): len %d",
|
|
opcode2str(opcode),
|
|
opcode & 0x80 ? 'f' : 'c', length);
|
|
set_opcode(frm->handle, frm->dlci, opcode);
|
|
} else {
|
|
printf("OBEX: %s rsp(%c): status %x%02d len %d",
|
|
opcode2str(last_opcode),
|
|
opcode & 0x80 ? 'f' : 'c',
|
|
status >> 4, status & 0xf, length);
|
|
opcode = last_opcode;
|
|
}
|
|
|
|
if (get_status(frm->handle, frm->dlci) == 0x10)
|
|
printf(" (continue)");
|
|
|
|
set_status(frm->handle, frm->dlci, status);
|
|
|
|
if (frm->len == 0) {
|
|
printf("\n");
|
|
break;
|
|
}
|
|
|
|
switch (opcode & 0x7f) {
|
|
case 0x00: /* Connect */
|
|
if (frm->len < 4) {
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
version = get_u8(frm);
|
|
flags = get_u8(frm);
|
|
pktlen = get_u16(frm);
|
|
printf(" version %d.%d flags %d mtu %d\n",
|
|
version >> 4, version & 0xf, flags, pktlen);
|
|
break;
|
|
|
|
case 0x05: /* SetPath */
|
|
if (frm->len < 2) {
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
flags = get_u8(frm);
|
|
constants = get_u8(frm);
|
|
printf(" flags %d constants %d\n", flags, constants);
|
|
break;
|
|
|
|
default:
|
|
printf("\n");
|
|
break;
|
|
}
|
|
|
|
if ((status & 0x70) && (parser.flags & DUMP_VERBOSE)) {
|
|
p_indent(level, frm);
|
|
printf("Status %x%02d = %s\n",
|
|
status >> 4, status & 0xf,
|
|
opcode2str(status));
|
|
}
|
|
|
|
parse_headers(level, frm);
|
|
}
|
|
}
|