| /* $Id: stun.c 4537 2013-06-19 06:47:43Z riza $ */ |
| /* |
| * 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 "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; i<PJ_ARRAY_SIZE(tests); ++i) { |
| struct test *t = &tests[i]; |
| pj_stun_msg *msg, *msg2; |
| pj_uint8_t buf[1500]; |
| pj_str_t key; |
| pj_size_t len; |
| pj_status_t status; |
| |
| PJ_LOG(3,(THIS_FILE, " %s", t->title)); |
| |
| 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_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; i<data_len;) { |
| unsigned j; |
| |
| pj_ansi_snprintf(p, 1500-(p-buf), |
| "%04d-%04d ", |
| i, (i+20 < data_len) ? i+20 : data_len); |
| p += 12; |
| |
| for (j=0; j<20 && i<data_len && p<(buf+length-10); ++j, ++i) { |
| pj_ansi_sprintf(p, "%02x ", (*data) & 0xFF); |
| p += 3; |
| data++; |
| } |
| |
| pj_ansi_sprintf(p, "\n"); |
| p++; |
| } |
| |
| return buf; |
| } |
| |
| static int cmp_buf(const pj_uint8_t *s1, const pj_uint8_t *s2, unsigned len) |
| { |
| unsigned i; |
| for (i=0; i<len; ++i) { |
| if (s1[i] != s2[i]) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| static int fingerprint_test_vector() |
| { |
| pj_pool_t *pool; |
| pj_status_t status; |
| unsigned i; |
| int rc = 0; |
| |
| /* To avoid function not referenced warnings */ |
| (void)create_msgint2; |
| (void)create_msgint3; |
| |
| PJ_LOG(3,(THIS_FILE, " draft-denis-behave-rfc3489bis-test-vectors-02")); |
| |
| pool = pj_pool_create(mem, "fingerprint", 1024, 1024, NULL); |
| |
| for (i=0; i<PJ_ARRAY_SIZE(test_vectors); ++i) { |
| struct test_vector *v; |
| pj_stun_msg *ref_msg, *msg; |
| pj_size_t parsed_len; |
| pj_size_t len; |
| unsigned pos; |
| pj_uint8_t buf[1500]; |
| char print[1500]; |
| pj_str_t key; |
| |
| PJ_LOG(3,(THIS_FILE, " Running test %d/%d", i, |
| PJ_ARRAY_SIZE(test_vectors))); |
| |
| v = &test_vectors[i]; |
| |
| /* Print reference message */ |
| PJ_LOG(4,(THIS_FILE, "Reference message PDU:\n%s", |
| print_binary((pj_uint8_t*)v->pdu, 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 status; |
| |
| 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); |
| |
| status = pj_stun_authenticate_request(buf, (unsigned)len, msg, |
| &cred, pool, NULL, NULL); |
| if (status != PJ_SUCCESS) { |
| char errmsg[PJ_ERR_MSG_SIZE]; |
| pj_strerror(status, 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 status; |
| status = pj_stun_authenticate_response(buf, (unsigned)len, |
| msg, &key); |
| if (status != PJ_SUCCESS) { |
| char errmsg[PJ_ERR_MSG_SIZE]; |
| pj_strerror(status, 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; i<msg1->attr_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<len; ++i) printf("0x%02x, ", packet[i]); |
| puts(" }"); |
| } |
| #endif |
| |
| PJ_LOG(3,(THIS_FILE, " decoding")); |
| rc += pj_stun_msg_decode(pool, packet, len, PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET, |
| &msg1, NULL, NULL); |
| |
| rc += cmp_msg(msg0, msg1); |
| |
| pj_bzero(&cred, sizeof(cred)); |
| cred.type = PJ_STUN_AUTH_CRED_STATIC; |
| cred.data.static_cred.username = USERNAME; |
| cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; |
| cred.data.static_cred.data = PASSWORD; |
| |
| PJ_LOG(3,(THIS_FILE, " authenticating")); |
| rc += pj_stun_authenticate_request(packet, (unsigned)len, msg1, &cred, pool, |
| NULL, NULL); |
| |
| PJ_LOG(3,(THIS_FILE, " clone")); |
| msg2 = pj_stun_msg_clone(pool, msg1); |
| rc += cmp_msg(msg0, msg2); |
| |
| pj_pool_release(pool); |
| |
| return rc==0 ? 0 : -4410; |
| } |
| |
| |
| int stun_test(void) |
| { |
| int pad, rc; |
| |
| pad = pj_stun_set_padding_char(32); |
| |
| rc = decode_test(); |
| if (rc != 0) |
| goto on_return; |
| |
| rc = decode_verify(); |
| if (rc != 0) |
| goto on_return; |
| |
| rc = fingerprint_test_vector(); |
| if (rc != 0) |
| goto on_return; |
| |
| rc = handle_unknown_non_mandatory(); |
| if (rc != 0) |
| goto on_return; |
| |
| on_return: |
| pj_stun_set_padding_char(pad); |
| return rc; |
| } |
| |