| /* $Id$ */ |
| /* |
| * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) |
| * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| #include <pjnath/stun_msg.h> |
| #include <pjnath/errno.h> |
| #include <pj/assert.h> |
| #include <pj/os.h> |
| #include <pj/string.h> |
| |
| #if PJ_LOG_MAX_LEVEL > 0 |
| |
| |
| #define APPLY() if (len < 1 || len >= (end-p)) \ |
| goto on_return; \ |
| p += len |
| |
| static int print_binary(char *buffer, unsigned length, |
| const pj_uint8_t *data, unsigned data_len) |
| { |
| unsigned i; |
| |
| if (length < data_len * 2 + 8) |
| return -1; |
| |
| pj_ansi_sprintf(buffer, ", data="); |
| buffer += 7; |
| |
| for (i=0; i<data_len; ++i) { |
| pj_ansi_sprintf(buffer, "%02x", (*data) & 0xFF); |
| buffer += 2; |
| data++; |
| } |
| |
| pj_ansi_sprintf(buffer, "\n"); |
| buffer++; |
| |
| return data_len * 2 + 8; |
| } |
| |
| static int print_attr(char *buffer, unsigned length, |
| const pj_stun_attr_hdr *ahdr) |
| { |
| char *p = buffer, *end = buffer + length; |
| const char *attr_name = pj_stun_get_attr_name(ahdr->type); |
| char attr_buf[32]; |
| int len; |
| |
| if (*attr_name == '?') { |
| pj_ansi_snprintf(attr_buf, sizeof(attr_buf), "Attr 0x%x", |
| ahdr->type); |
| attr_name = attr_buf; |
| } |
| |
| len = pj_ansi_snprintf(p, end-p, |
| " %s: length=%d", |
| attr_name, |
| (int)ahdr->length); |
| APPLY(); |
| |
| |
| switch (ahdr->type) { |
| case PJ_STUN_ATTR_MAPPED_ADDR: |
| case PJ_STUN_ATTR_RESPONSE_ADDR: |
| case PJ_STUN_ATTR_SOURCE_ADDR: |
| case PJ_STUN_ATTR_CHANGED_ADDR: |
| case PJ_STUN_ATTR_REFLECTED_FROM: |
| case PJ_STUN_ATTR_XOR_PEER_ADDR: |
| case PJ_STUN_ATTR_XOR_RELAYED_ADDR: |
| case PJ_STUN_ATTR_XOR_MAPPED_ADDR: |
| case PJ_STUN_ATTR_XOR_REFLECTED_FROM: |
| case PJ_STUN_ATTR_ALTERNATE_SERVER: |
| { |
| const pj_stun_sockaddr_attr *attr; |
| |
| attr = (const pj_stun_sockaddr_attr*)ahdr; |
| |
| if (attr->sockaddr.addr.sa_family == pj_AF_INET()) { |
| len = pj_ansi_snprintf(p, end-p, |
| ", IPv4 addr=%s:%d\n", |
| pj_inet_ntoa(attr->sockaddr.ipv4.sin_addr), |
| pj_ntohs(attr->sockaddr.ipv4.sin_port)); |
| |
| } else if (attr->sockaddr.addr.sa_family == pj_AF_INET6()) { |
| len = pj_ansi_snprintf(p, end-p, |
| ", IPv6 addr present\n"); |
| } else { |
| len = pj_ansi_snprintf(p, end-p, |
| ", INVALID ADDRESS FAMILY!\n"); |
| } |
| APPLY(); |
| } |
| break; |
| |
| case PJ_STUN_ATTR_CHANNEL_NUMBER: |
| { |
| const pj_stun_uint_attr *attr; |
| |
| attr = (const pj_stun_uint_attr*)ahdr; |
| len = pj_ansi_snprintf(p, end-p, |
| ", chnum=%u (0x%x)\n", |
| (int)PJ_STUN_GET_CH_NB(attr->value), |
| (int)PJ_STUN_GET_CH_NB(attr->value)); |
| APPLY(); |
| } |
| break; |
| |
| case PJ_STUN_ATTR_CHANGE_REQUEST: |
| case PJ_STUN_ATTR_LIFETIME: |
| case PJ_STUN_ATTR_BANDWIDTH: |
| case PJ_STUN_ATTR_REQ_ADDR_TYPE: |
| case PJ_STUN_ATTR_EVEN_PORT: |
| case PJ_STUN_ATTR_REQ_TRANSPORT: |
| case PJ_STUN_ATTR_TIMER_VAL: |
| case PJ_STUN_ATTR_PRIORITY: |
| case PJ_STUN_ATTR_FINGERPRINT: |
| case PJ_STUN_ATTR_REFRESH_INTERVAL: |
| case PJ_STUN_ATTR_ICMP: |
| { |
| const pj_stun_uint_attr *attr; |
| |
| attr = (const pj_stun_uint_attr*)ahdr; |
| len = pj_ansi_snprintf(p, end-p, |
| ", value=%u (0x%x)\n", |
| (pj_uint32_t)attr->value, |
| (pj_uint32_t)attr->value); |
| APPLY(); |
| } |
| break; |
| |
| case PJ_STUN_ATTR_USERNAME: |
| case PJ_STUN_ATTR_PASSWORD: |
| case PJ_STUN_ATTR_REALM: |
| case PJ_STUN_ATTR_NONCE: |
| case PJ_STUN_ATTR_SOFTWARE: |
| { |
| const pj_stun_string_attr *attr; |
| |
| attr = (pj_stun_string_attr*)ahdr; |
| len = pj_ansi_snprintf(p, end-p, |
| ", value=\"%.*s\"\n", |
| (int)attr->value.slen, |
| attr->value.ptr); |
| APPLY(); |
| } |
| break; |
| |
| case PJ_STUN_ATTR_ERROR_CODE: |
| { |
| const pj_stun_errcode_attr *attr; |
| |
| attr = (const pj_stun_errcode_attr*) ahdr; |
| len = pj_ansi_snprintf(p, end-p, |
| ", err_code=%d, reason=\"%.*s\"\n", |
| attr->err_code, |
| (int)attr->reason.slen, |
| attr->reason.ptr); |
| APPLY(); |
| } |
| break; |
| |
| case PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES: |
| { |
| const pj_stun_unknown_attr *attr; |
| unsigned j; |
| |
| attr = (const pj_stun_unknown_attr*) ahdr; |
| |
| len = pj_ansi_snprintf(p, end-p, |
| ", unknown list:"); |
| APPLY(); |
| |
| for (j=0; j<attr->attr_count; ++j) { |
| len = pj_ansi_snprintf(p, end-p, |
| " %d", |
| (int)attr->attrs[j]); |
| APPLY(); |
| } |
| } |
| break; |
| |
| case PJ_STUN_ATTR_MESSAGE_INTEGRITY: |
| { |
| const pj_stun_msgint_attr *attr; |
| |
| attr = (const pj_stun_msgint_attr*) ahdr; |
| len = print_binary(p, (unsigned)(end-p), attr->hmac, 20); |
| APPLY(); |
| } |
| break; |
| |
| case PJ_STUN_ATTR_DATA: |
| { |
| const pj_stun_binary_attr *attr; |
| |
| attr = (const pj_stun_binary_attr*) ahdr; |
| len = print_binary(p, (unsigned)(end-p), attr->data, attr->length); |
| APPLY(); |
| } |
| break; |
| case PJ_STUN_ATTR_ICE_CONTROLLED: |
| case PJ_STUN_ATTR_ICE_CONTROLLING: |
| case PJ_STUN_ATTR_RESERVATION_TOKEN: |
| { |
| const pj_stun_uint64_attr *attr; |
| pj_uint8_t data[8]; |
| int i; |
| |
| attr = (const pj_stun_uint64_attr*) ahdr; |
| |
| for (i=0; i<8; ++i) |
| data[i] = ((const pj_uint8_t*)&attr->value)[7-i]; |
| |
| len = print_binary(p, (unsigned)(end-p), data, 8); |
| APPLY(); |
| } |
| break; |
| case PJ_STUN_ATTR_USE_CANDIDATE: |
| case PJ_STUN_ATTR_DONT_FRAGMENT: |
| default: |
| len = pj_ansi_snprintf(p, end-p, "\n"); |
| APPLY(); |
| break; |
| } |
| |
| return (int)(p-buffer); |
| |
| on_return: |
| return len; |
| } |
| |
| |
| /* |
| * Dump STUN message to a printable string output. |
| */ |
| PJ_DEF(char*) pj_stun_msg_dump(const pj_stun_msg *msg, |
| char *buffer, |
| unsigned length, |
| unsigned *printed_len) |
| { |
| char *p, *end; |
| int len; |
| unsigned i; |
| |
| PJ_ASSERT_RETURN(msg && buffer && length, NULL); |
| |
| PJ_CHECK_STACK(); |
| |
| p = buffer; |
| end = buffer + length; |
| |
| len = pj_ansi_snprintf(p, end-p, "STUN %s %s\n", |
| pj_stun_get_method_name(msg->hdr.type), |
| pj_stun_get_class_name(msg->hdr.type)); |
| APPLY(); |
| |
| len = pj_ansi_snprintf(p, end-p, |
| " Hdr: length=%d, magic=%08x, tsx_id=%08x%08x%08x\n" |
| " Attributes:\n", |
| msg->hdr.length, |
| msg->hdr.magic, |
| *(pj_uint32_t*)&msg->hdr.tsx_id[0], |
| *(pj_uint32_t*)&msg->hdr.tsx_id[4], |
| *(pj_uint32_t*)&msg->hdr.tsx_id[8]); |
| APPLY(); |
| |
| for (i=0; i<msg->attr_count; ++i) { |
| len = print_attr(p, (unsigned)(end-p), msg->attr[i]); |
| APPLY(); |
| } |
| |
| on_return: |
| *p = '\0'; |
| if (printed_len) |
| *printed_len = (unsigned)(p-buffer); |
| return buffer; |
| |
| #undef APPLY |
| } |
| |
| |
| #endif /* PJ_LOG_MAX_LEVEL > 0 */ |
| |