| /* |
| * |
| * 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); |
| } |
| } |