michael@0: /* michael@0: Copyright (c) 2007, Adobe Systems, Incorporated michael@0: All rights reserved. michael@0: michael@0: Redistribution and use in source and binary forms, with or without michael@0: modification, are permitted provided that the following conditions are michael@0: met: michael@0: michael@0: * Redistributions of source code must retain the above copyright michael@0: notice, this list of conditions and the following disclaimer. michael@0: michael@0: * Redistributions in binary form must reproduce the above copyright michael@0: notice, this list of conditions and the following disclaimer in the michael@0: documentation and/or other materials provided with the distribution. michael@0: michael@0: * Neither the name of Adobe Systems, Network Resonance nor the names of its michael@0: contributors may be used to endorse or promote products derived from michael@0: this software without specific prior written permission. michael@0: michael@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: */ michael@0: michael@0: michael@0: static char *RCSSTRING __UNUSED__="$Id: stun_hint.c,v 1.2 2008/04/28 18:21:30 ekr Exp $"; michael@0: michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #ifdef WIN32 michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #else /* UNIX */ michael@0: #include michael@0: #endif /* end UNIX */ michael@0: #include michael@0: michael@0: #include "stun.h" michael@0: michael@0: michael@0: /* returns 0 if it's not a STUN message michael@0: * 1 if it's likely to be a STUN message michael@0: * 2 if it's super likely to be a STUN message michael@0: * 3 if it really is a STUN message */ michael@0: int michael@0: nr_is_stun_message(UCHAR *buf, int len) michael@0: { michael@0: const UINT4 cookie = htonl(NR_STUN_MAGIC_COOKIE); michael@0: const UINT4 cookie2 = htonl(NR_STUN_MAGIC_COOKIE2); michael@0: #if 0 michael@0: nr_stun_message msg; michael@0: #endif michael@0: UINT2 type; michael@0: nr_stun_encoded_attribute* attr; michael@0: unsigned int attrLen; michael@0: int atrType; michael@0: michael@0: if (sizeof(nr_stun_message_header) > len) michael@0: return 0; michael@0: michael@0: if ((buf[0] & (0x80|0x40)) != 0) michael@0: return 0; michael@0: michael@0: memcpy(&type, buf, 2); michael@0: type = ntohs(type); michael@0: michael@0: switch (type) { michael@0: case NR_STUN_MSG_BINDING_REQUEST: michael@0: case NR_STUN_MSG_BINDING_INDICATION: michael@0: case NR_STUN_MSG_BINDING_RESPONSE: michael@0: case NR_STUN_MSG_BINDING_ERROR_RESPONSE: michael@0: michael@0: #ifdef USE_TURN michael@0: case NR_STUN_MSG_ALLOCATE_REQUEST: michael@0: case NR_STUN_MSG_ALLOCATE_RESPONSE: michael@0: case NR_STUN_MSG_ALLOCATE_ERROR_RESPONSE: michael@0: case NR_STUN_MSG_REFRESH_REQUEST: michael@0: case NR_STUN_MSG_REFRESH_RESPONSE: michael@0: case NR_STUN_MSG_REFRESH_ERROR_RESPONSE: michael@0: case NR_STUN_MSG_PERMISSION_REQUEST: michael@0: case NR_STUN_MSG_PERMISSION_RESPONSE: michael@0: case NR_STUN_MSG_PERMISSION_ERROR_RESPONSE: michael@0: case NR_STUN_MSG_CHANNEL_BIND_REQUEST: michael@0: case NR_STUN_MSG_CHANNEL_BIND_RESPONSE: michael@0: case NR_STUN_MSG_CHANNEL_BIND_ERROR_RESPONSE: michael@0: case NR_STUN_MSG_SEND_INDICATION: michael@0: case NR_STUN_MSG_DATA_INDICATION: michael@0: #ifdef NR_STUN_MSG_CONNECT_REQUEST michael@0: case NR_STUN_MSG_CONNECT_REQUEST: michael@0: #endif michael@0: #ifdef NR_STUN_MSG_CONNECT_RESPONSE michael@0: case NR_STUN_MSG_CONNECT_RESPONSE: michael@0: #endif michael@0: #ifdef NR_STUN_MSG_CONNECT_ERROR_RESPONSE michael@0: case NR_STUN_MSG_CONNECT_ERROR_RESPONSE: michael@0: #endif michael@0: #ifdef NR_STUN_MSG_CONNECT_STATUS_INDICATION michael@0: case NR_STUN_MSG_CONNECT_STATUS_INDICATION: michael@0: #endif michael@0: #endif /* USE_TURN */ michael@0: michael@0: /* ok so far, continue */ michael@0: break; michael@0: default: michael@0: return 0; michael@0: break; michael@0: } michael@0: michael@0: if (!memcmp(&cookie2, &buf[4], sizeof(UINT4))) { michael@0: /* return here because if it's an old-style message then there will michael@0: * not be a fingerprint in the message */ michael@0: return 1; michael@0: } michael@0: michael@0: if (memcmp(&cookie, &buf[4], sizeof(UINT4))) michael@0: return 0; michael@0: michael@0: /* the magic cookie was right, so it's pretty darn likely that what we've michael@0: * got here is a STUN message */ michael@0: michael@0: attr = (nr_stun_encoded_attribute*)(buf + (len - 8)); michael@0: attrLen = ntohs(attr->length); michael@0: atrType = ntohs(attr->type); michael@0: michael@0: if (atrType != NR_STUN_ATTR_FINGERPRINT || attrLen != 4) michael@0: return 1; michael@0: michael@0: /* the fingerprint is in the right place and looks sane, so we can be quite michael@0: * sure we've got a STUN message */ michael@0: michael@0: #if 0 michael@0: /* nevermind this check ... there's a reasonable chance that a NAT has modified michael@0: * the message (and thus the fingerprint check will fail), but it's still an michael@0: * otherwise-perfectly-good STUN message, so skip the check since we're going michael@0: * to return "true" whether the check succeeds or fails */ michael@0: michael@0: if (nr_stun_parse_attr_UINT4(buf + (len - 4), attrLen, &msg.fingerprint)) michael@0: return 2; michael@0: michael@0: michael@0: if (nr_stun_compute_fingerprint(buf, len - 8, &computedFingerprint)) michael@0: return 2; michael@0: michael@0: if (msg.fingerprint.number != computedFingerprint) michael@0: return 2; michael@0: michael@0: /* and the fingerprint is good, so it's gotta be a STUN message */ michael@0: #endif michael@0: michael@0: return 3; michael@0: } michael@0: michael@0: int michael@0: nr_is_stun_request_message(UCHAR *buf, int len) michael@0: { michael@0: UINT2 type; michael@0: michael@0: if (sizeof(nr_stun_message_header) > len) michael@0: return 0; michael@0: michael@0: if (!nr_is_stun_message(buf, len)) michael@0: return 0; michael@0: michael@0: memcpy(&type, buf, 2); michael@0: type = ntohs(type); michael@0: michael@0: return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_REQUEST; michael@0: } michael@0: michael@0: int michael@0: nr_is_stun_indication_message(UCHAR *buf, int len) michael@0: { michael@0: UINT2 type; michael@0: michael@0: if (sizeof(nr_stun_message_header) > len) michael@0: return 0; michael@0: michael@0: if (!nr_is_stun_message(buf, len)) michael@0: return 0; michael@0: michael@0: memcpy(&type, buf, 2); michael@0: type = ntohs(type); michael@0: michael@0: return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_INDICATION; michael@0: } michael@0: michael@0: int michael@0: nr_is_stun_response_message(UCHAR *buf, int len) michael@0: { michael@0: UINT2 type; michael@0: michael@0: if (sizeof(nr_stun_message_header) > len) michael@0: return 0; michael@0: michael@0: if (!nr_is_stun_message(buf, len)) michael@0: return 0; michael@0: michael@0: memcpy(&type, buf, 2); michael@0: type = ntohs(type); michael@0: michael@0: return NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_RESPONSE michael@0: || NR_STUN_GET_TYPE_CLASS(type) == NR_CLASS_ERROR_RESPONSE; michael@0: } michael@0: michael@0: int michael@0: nr_has_stun_cookie(UCHAR *buf, int len) michael@0: { michael@0: static UINT4 cookie; michael@0: michael@0: cookie = htonl(NR_STUN_MAGIC_COOKIE); michael@0: michael@0: if (sizeof(nr_stun_message_header) > len) michael@0: return 0; michael@0: michael@0: if (memcmp(&cookie, &buf[4], sizeof(UINT4))) michael@0: return 0; michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: int michael@0: nr_stun_message_length(UCHAR *buf, int buf_len, int *msg_len) michael@0: { michael@0: nr_stun_message_header *hdr; michael@0: michael@0: if (!nr_is_stun_message(buf, buf_len)) michael@0: return(R_BAD_DATA); michael@0: michael@0: hdr = (nr_stun_message_header *)buf; michael@0: michael@0: *msg_len = ntohs(hdr->length); michael@0: michael@0: return(0); michael@0: } michael@0: michael@0: michael@0: