/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * 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 "test.h" #define THIS_FILE "stun.c" static pj_stun_msg* create1(pj_pool_t*); static int verify1(pj_stun_msg*); static int verify2(pj_stun_msg*); static int verify5(pj_stun_msg*); static struct test { const char *title; char *pdu; unsigned pdu_len; pj_stun_msg* (*create)(pj_pool_t*); pj_status_t expected_status; int (*verify)(pj_stun_msg*); } tests[] = { { "Invalid message type", "\x11\x01\x00\x00\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 20, NULL, PJNATH_EINSTUNMSGTYPE, NULL }, { "Short message (1) (partial header)", "\x00\x01", 2, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Short message (2) (partial header)", "\x00\x01\x00\x00\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00", 16, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Short message (3), (missing attribute)", "\x00\x01\x00\x08\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 20, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Short message (4), (partial attribute header)", "\x00\x01\x00\x08\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\x28", 22, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Short message (5), (partial attribute header)", "\x00\x01\x00\x08\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\x28\x00", 23, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Short message (6), (partial attribute header)", "\x00\x01\x00\x08\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\x28\x00\x04", 24, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Short message (7), (partial attribute body)", "\x00\x01\x00\x08\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\x28\x00\x04\x00\x00\x00", 27, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Message length in header is too long", "\x00\x01\xff\xff\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\x28\x00\x04\x00\x00\x00", 27, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Message length in header is shorter", "\x00\x01\x00\x04\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\x28\x00\x04\x00\x00\x00\x00", 28, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Invalid magic", "\x00\x01\x00\x08\x00\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\x28\x00\x04\x00\x00\x00\x00", 28, NULL, PJ_SUCCESS, NULL }, { "Character beyond message", "\x00\x01\x00\x08\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\x28\x00\x04\x00\x00\x00\x00\x0a", 29, NULL, PJNATH_EINSTUNMSGLEN, NULL }, { "Respond unknown mandatory attribute with 420 and " "UNKNOWN-ATTRIBUTES attribute", NULL, 0, &create1, 0, &verify1 }, { "Unknown but non-mandatory should be okay", "\x00\x01\x00\x08\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\xff\x00\x03\x00\x00\x00\x00", 28, NULL, PJ_SUCCESS, &verify2 }, { "String attr length larger than message", "\x00\x01\x00\x08\x00\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x06\x00\xff\x00\x00\x00\x00", 28, NULL, PJNATH_ESTUNINATTRLEN, NULL }, { "Attribute other than FINGERPRINT after MESSAGE-INTEGRITY is allowed", "\x00\x01\x00\x20\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x08\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // M-I "\x80\x24\x00\x04\x00\x00\x00\x00", // REFRESH-INTERVAL 52, NULL, PJ_SUCCESS, NULL }, { "Attribute between MESSAGE-INTEGRITY and FINGERPRINT is allowed", "\x00\x01\x00\x28\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x08\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // M-I "\x80\x24\x00\x04\x00\x00\x00\x00" // REFRESH-INTERVAL "\x80\x28\x00\x04\xc7\xde\xdd\x65", // FINGERPRINT 60, NULL, PJ_SUCCESS, &verify5 }, { "Attribute past FINGERPRINT is not allowed", "\x00\x01\x00\x10\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x80\x28\x00\x04\x00\x00\x00\x00" "\x80\x24\x00\x04\x00\x00\x00\x00", 36, NULL, PJNATH_ESTUNFINGERPOS, NULL } }; static const char *err(pj_status_t status) { static char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status, errmsg, sizeof(errmsg)); return errmsg; } static const pj_str_t USERNAME = {"user", 4}; static const pj_str_t PASSWORD = {"password", 8}; static int decode_test(void) { unsigned i; pj_pool_t *pool; int rc = 0; pool = pj_pool_create(mem, "decode_test", 1024, 1024, NULL); PJ_LOG(3,(THIS_FILE, " STUN decode test")); for (i=0; ititle)); if (t->pdu) { status = pj_stun_msg_decode(pool, (pj_uint8_t*)t->pdu, t->pdu_len, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, &msg, NULL, NULL); /* Check expected decode result */ if (t->expected_status != status) { PJ_LOG(1,(THIS_FILE, " expecting status %d, got %d", t->expected_status, status)); rc = -10; goto on_return; } } else { msg = t->create(pool); status = PJ_SUCCESS; } if (status != PJ_SUCCESS) continue; /* Try to encode message */ pj_stun_create_key(pool, &key, NULL, &USERNAME, PJ_STUN_PASSWD_PLAIN, &PASSWORD); status = pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, " encode error: %s", err(status))); rc = -40; goto on_return; } /* Try to decode it once more */ status = pj_stun_msg_decode(pool, buf, len, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, &msg2, NULL, NULL); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, " subsequent decoding failed: %s", err(status))); rc = -50; goto on_return; } /* Verify */ if (t->verify) { rc = t->verify(msg); if (rc != 0) { goto on_return; } } } on_return: pj_pool_release(pool); if (rc == 0) PJ_LOG(3,(THIS_FILE, "...success!")); return rc; } /* Create 420 response */ static pj_stun_msg* create1(pj_pool_t *pool) { char *pdu = "\x00\x01\x00\x08\x21\x12\xa4\x42" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\xff\x00\x04\x00\x00\x00\x00"; unsigned pdu_len = 28; pj_stun_msg *msg, *res; pj_status_t status; status = pj_stun_msg_decode(pool, (pj_uint8_t*)pdu, pdu_len, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, &msg, NULL, &res); pj_assert(status != PJ_SUCCESS); PJ_UNUSED_ARG(status); pj_assert(res != NULL); return res; } /* Error response MUST have ERROR-CODE attribute */ /* 420 response MUST contain UNKNOWN-ATTRIBUTES */ static int verify1(pj_stun_msg *msg) { pj_stun_errcode_attr *aerr; pj_stun_unknown_attr *aunk; if (!PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) { PJ_LOG(1,(THIS_FILE, " expecting error message")); return -100; } aerr = (pj_stun_errcode_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0); if (aerr == NULL) { PJ_LOG(1,(THIS_FILE, " missing ERROR-CODE attribute")); return -110; } if (aerr->err_code != 420) { PJ_LOG(1,(THIS_FILE, " expecting 420 error")); return -120; } aunk = (pj_stun_unknown_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, 0); if (aunk == NULL) { PJ_LOG(1,(THIS_FILE, " missing UNKNOWN-ATTRIBUTE attribute")); return -130; } if (aunk->attr_count != 1) { PJ_LOG(1,(THIS_FILE, " expecting one unknown attribute")); return -140; } if (aunk->attrs[0] != 0xff) { PJ_LOG(1,(THIS_FILE, " expecting 0xff as unknown attribute")); return -150; } return 0; } /* Attribute count should be zero since unknown attribute is not parsed */ static int verify2(pj_stun_msg *msg) { pj_stun_binary_attr *bin_attr; if (msg->attr_count != 1) { PJ_LOG(1,(THIS_FILE, " expecting one attribute count")); return -200; } bin_attr = (pj_stun_binary_attr*)msg->attr[0]; if (bin_attr->hdr.type != 0x80ff) { PJ_LOG(1,(THIS_FILE, " expecting attribute type 0x80ff")); return -210; } if (bin_attr->hdr.length != 3) { PJ_LOG(1,(THIS_FILE, " expecting attribute length = 4")); return -220; } if (bin_attr->magic != PJ_STUN_MAGIC) { PJ_LOG(1,(THIS_FILE, " expecting PJ_STUN_MAGIC for unknown attr")); return -230; } if (bin_attr->length != 3) { PJ_LOG(1,(THIS_FILE, " expecting data length 4")); return -240; } return 0; } /* Attribute between MESSAGE-INTEGRITY and FINGERPRINT is allowed */ static int verify5(pj_stun_msg *msg) { if (msg->attr_count != 3) { PJ_LOG(1,(THIS_FILE, " expecting 3 attribute count")); return -500; } if (msg->attr[0]->type != PJ_STUN_ATTR_MESSAGE_INTEGRITY) { PJ_LOG(1,(THIS_FILE, " expecting MESSAGE-INTEGRITY")); return -510; } if (msg->attr[1]->type != PJ_STUN_ATTR_REFRESH_INTERVAL) { PJ_LOG(1,(THIS_FILE, " expecting REFRESH-INTERVAL")); return -520; } if (msg->attr[2]->type != PJ_STUN_ATTR_FINGERPRINT) { PJ_LOG(1,(THIS_FILE, " expecting FINGERPRINT")); return -530; } return 0; } static int decode_verify(void) { /* Decode all attribute types */ return 0; } /* * Test vectors, from: * http://tools.ietf.org/html/draft-denis-behave-rfc3489bis-test-vectors-02 */ typedef struct test_vector test_vector; static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v); static pj_stun_msg* create_msgint2(pj_pool_t *pool, test_vector *v); static pj_stun_msg* create_msgint3(pj_pool_t *pool, test_vector *v); enum { USE_MESSAGE_INTEGRITY = 1, USE_FINGERPRINT = 2 }; static struct test_vector { unsigned msg_type; char *tsx_id; char *pdu; unsigned pdu_len; unsigned options; char *username; char *password; char *realm; char *nonce; pj_stun_msg* (*create)(pj_pool_t*, test_vector*); } test_vectors[] = { { PJ_STUN_BINDING_REQUEST, "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae", "\x00\x01\x00\x44\x21\x12\xa4\x42\xb7\xe7" "\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" "\x00\x24\x00\x04\x6e\x00\x01\xff\x80\x29" "\x00\x08\x93\x2f\xf9\xb1\x51\x26\x3b\x36" "\x00\x06\x00\x09\x65\x76\x74\x6a\x3a\x68" "\x36\x76\x59\x20\x20\x20\x00\x08\x00\x14" "\x62\x4e\xeb\xdc\x3c\xc9\x2d\xd8\x4b\x74" "\xbf\x85\xd1\xc0\xf5\xde\x36\x87\xbd\x33" "\x80\x28\x00\x04\xad\x8a\x85\xff", 88, USE_MESSAGE_INTEGRITY | USE_FINGERPRINT, "evtj:h6vY", "VOkJxbRl1RmTxUk/WvJxBt", "", "", &create_msgint1 } /* disabled: see http://trac.pjsip.org/repos/ticket/960 , { PJ_STUN_BINDING_RESPONSE, "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae", "\x01\x01\x00\x3c" "\x21\x12\xa4\x42" "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" "\x80\x22\x00\x0b" "\x74\x65\x73\x74\x20\x76\x65\x63\x74\x6f\x72\x20" "\x00\x20\x00\x08" "\x00\x01\xa1\x47\xe1\x12\xa6\x43" "\x00\x08\x00\x14" "\x2b\x91\xf5\x99\xfd\x9e\x90\xc3\x8c\x74\x89\xf9" "\x2a\xf9\xba\x53\xf0\x6b\xe7\xd7" "\x80\x28\x00\x04" "\xc0\x7d\x4c\x96", 80, USE_MESSAGE_INTEGRITY | USE_FINGERPRINT, "evtj:h6vY", "VOkJxbRl1RmTxUk/WvJxBt", "", "", &create_msgint2 } */ /* disabled: see http://trac.pjsip.org/repos/ticket/960 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0 , { PJ_STUN_BINDING_RESPONSE, "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae", "\x01\x01\x00\x48" // Response type and message length "\x21\x12\xa4\x42" // Message cookie "\xb7\xe7\xa7\x01" // } "\xbc\x34\xd6\x86" // } Transaction ID "\xfa\x87\xdf\xae" // } "\x80\x22\x00\x0b" // SOFTWARE, length=11 "\x74\x65\x73\x74" "\x20\x76\x65\x63" "\x74\x6f\x72\x20" "\x00\x20\x00\x14" // XOR-MAPPED-ADDRESS "\x00\x02\xa1\x47" "\x01\x13\xa9\xfa" "\xa5\xd3\xf1\x79" "\xbc\x25\xf4\xb5" "\xbe\xd2\xb9\xd9" "\x00\x08\x00\x14" // MESSAGE-INTEGRITY attribute header "\xa3\x82\x95\x4e" // } "\x4b\xe6\x7b\xf1" // } "\x17\x84\xc9\x7c" // } HMAC-SHA1 fingerprint "\x82\x92\xc2\x75" // } "\xbf\xe3\xed\x41" // } "\x80\x28\x00\x04" // FINGERPRINT attribute header "\xc8\xfb\x0b\x4c" // CRC32 fingerprint , 92, USE_MESSAGE_INTEGRITY | USE_FINGERPRINT, "evtj:h6vY", "VOkJxbRl1RmTxUk/WvJxBt", "", "", &create_msgint3 } #endif */ }; static char* print_binary(const pj_uint8_t *data, unsigned data_len) { static char buf[1500]; unsigned length = sizeof(buf); char *p = buf; unsigned i; for (i=0; ipdu, v->pdu_len))); /* Try to parse the reference message first */ status = pj_stun_msg_decode(pool, (pj_uint8_t*)v->pdu, v->pdu_len, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, &ref_msg, &parsed_len, NULL); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, " Error decoding reference message")); rc = -1010; goto on_return; } if (parsed_len != v->pdu_len) { PJ_LOG(1,(THIS_FILE, " Parsed len error")); rc = -1020; goto on_return; } /* Print the reference message */ pj_stun_msg_dump(ref_msg, print, sizeof(print), NULL); PJ_LOG(4,(THIS_FILE, "Reference message:\n%s", print)); /* Create our message */ msg = v->create(pool, v); if (msg == NULL) { PJ_LOG(1,(THIS_FILE, " Error creating stun message")); rc = -1030; goto on_return; } /* Encode message */ if (v->options & USE_MESSAGE_INTEGRITY) { pj_str_t s1, s2, r; pj_stun_create_key(pool, &key, pj_cstr(&r, v->realm), pj_cstr(&s1, v->username), PJ_STUN_PASSWD_PLAIN, pj_cstr(&s2, v->password)); pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len); } else { pj_stun_msg_encode(msg, buf, sizeof(buf), 0, NULL, &len); } /* Print our raw message */ PJ_LOG(4,(THIS_FILE, "Message PDU:\n%s", print_binary((pj_uint8_t*)buf, (unsigned)len))); /* Print our message */ pj_stun_msg_dump(msg, print, sizeof(print), NULL); PJ_LOG(4,(THIS_FILE, "Message is:\n%s", print)); /* Compare message length */ if (len != v->pdu_len) { PJ_LOG(1,(THIS_FILE, " Message length mismatch")); rc = -1050; goto on_return; } pos = cmp_buf(buf, (const pj_uint8_t*)v->pdu, (unsigned)len); if (pos != (unsigned)-1) { PJ_LOG(1,(THIS_FILE, " Message mismatch at byte %d", pos)); rc = -1060; goto on_return; } /* Authenticate the request/response */ if (v->options & USE_MESSAGE_INTEGRITY) { if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { pj_stun_auth_cred cred; pj_status_t status2; pj_bzero(&cred, sizeof(cred)); cred.type = PJ_STUN_AUTH_CRED_STATIC; cred.data.static_cred.realm = pj_str(v->realm); cred.data.static_cred.username = pj_str(v->username); cred.data.static_cred.data = pj_str(v->password); cred.data.static_cred.nonce = pj_str(v->nonce); status2 = pj_stun_authenticate_request(buf, (unsigned)len, msg, &cred, pool, NULL, NULL); if (status2 != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status2, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, " Request authentication failed: %s", errmsg)); rc = -1070; goto on_return; } } else if (PJ_STUN_IS_RESPONSE(msg->hdr.type)) { pj_status_t status2; status2 = pj_stun_authenticate_response(buf, (unsigned)len, msg, &key); if (status2 != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(status2, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, " Response authentication failed: %s", errmsg)); rc = -1080; goto on_return; } } } } on_return: pj_pool_release(pool); return rc; } static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_timestamp u64; pj_str_t s1; pj_status_t status; status = pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_PRIORITY, 0x6e0001ff); if (status != PJ_SUCCESS) goto on_error; u64.u32.hi = 0x932ff9b1; u64.u32.lo = 0x51263b36; status = pj_stun_msg_add_uint64_attr(pool, msg, PJ_STUN_ATTR_ICE_CONTROLLED, &u64); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME, pj_cstr(&s1, v->username)); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_msgint_attr(pool, msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); if (status != PJ_SUCCESS) goto on_error; return msg; on_error: app_perror(" error: create_msgint1()", status); return NULL; } static pj_stun_msg* create_msgint2(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_sockaddr_in mapped_addr; pj_str_t s1; pj_status_t status; status = pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, pj_cstr(&s1, "test vector")); if (status != PJ_SUCCESS) goto on_error; status = pj_sockaddr_in_init(&mapped_addr, pj_cstr(&s1, "192.0.2.1"), 32853); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &mapped_addr, sizeof(pj_sockaddr_in)); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_msgint_attr(pool, msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); if (status != PJ_SUCCESS) goto on_error; return msg; on_error: app_perror(" error: create_msgint2()", status); return NULL; } static pj_stun_msg* create_msgint3(pj_pool_t *pool, test_vector *v) { pj_stun_msg *msg; pj_sockaddr mapped_addr; pj_str_t s1; pj_status_t status; status = pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC, (pj_uint8_t*)v->tsx_id, &msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE, pj_cstr(&s1, "test vector")); if (status != PJ_SUCCESS) goto on_error; status = pj_sockaddr_init(pj_AF_INET6(), &mapped_addr, pj_cstr(&s1, "2001:db8:1234:5678:11:2233:4455:6677"), 32853); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, &mapped_addr, sizeof(pj_sockaddr)); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_msgint_attr(pool, msg); if (status != PJ_SUCCESS) goto on_error; status = pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0); if (status != PJ_SUCCESS) goto on_error; return msg; on_error: app_perror(" error: create_msgint3()", status); return NULL; } /* Compare two messages */ static int cmp_msg(const pj_stun_msg *msg1, const pj_stun_msg *msg2) { unsigned i; if (msg1->hdr.type != msg2->hdr.type) return -10; if (msg1->hdr.length != msg2->hdr.length) return -20; if (msg1->hdr.magic != msg2->hdr.magic) return -30; if (pj_memcmp(msg1->hdr.tsx_id, msg2->hdr.tsx_id, sizeof(msg1->hdr.tsx_id))) return -40; if (msg1->attr_count != msg2->attr_count) return -50; for (i=0; iattr_count; ++i) { const pj_stun_attr_hdr *a1 = msg1->attr[i]; const pj_stun_attr_hdr *a2 = msg2->attr[i]; if (a1->type != a2->type) return -60; if (a1->length != a2->length) return -70; } return 0; } /* Decode and authenticate message with unknown non-mandatory attribute */ static int handle_unknown_non_mandatory(void) { pj_pool_t *pool = pj_pool_create(mem, NULL, 1000, 1000, NULL); pj_stun_msg *msg0, *msg1, *msg2; pj_uint8_t data[] = { 1, 2, 3, 4, 5, 6}; pj_uint8_t packet[500]; pj_stun_auth_cred cred; pj_size_t len; pj_status_t rc; PJ_LOG(3,(THIS_FILE, " handling unknown non-mandatory attr")); PJ_LOG(3,(THIS_FILE, " encoding")); rc = pj_stun_msg_create(pool, PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, NULL, &msg0); rc += pj_stun_msg_add_string_attr(pool, msg0, PJ_STUN_ATTR_USERNAME, &USERNAME); rc += pj_stun_msg_add_binary_attr(pool, msg0, 0x80ff, data, sizeof(data)); rc += pj_stun_msg_add_msgint_attr(pool, msg0); rc += pj_stun_msg_encode(msg0, packet, sizeof(packet), 0, &PASSWORD, &len); #if 0 if (1) { unsigned i; puts(""); printf("{ "); for (i=0; i