From bf839cd77520ad28437e55a73fd8167838023f42 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Mon, 26 Feb 2007 02:33:14 +0000 Subject: Added CRC32 code to pjlib-util, and implemented STUN FINGERPRINT and MESSAGE-INTEGRITY git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1002 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib-util/src/pjlib-util-test/encryption.c | 56 +++ pjlib-util/src/pjlib-util/crc32.c | 192 ++++++++++ pjlib-util/src/pjlib-util/stun_msg.c | 552 +++++++++++++++++++++++++--- pjlib-util/src/pjstun-client/stun_session.c | 9 +- pjlib-util/src/pjstun-srv/server_main.c | 41 +-- 5 files changed, 773 insertions(+), 77 deletions(-) create mode 100644 pjlib-util/src/pjlib-util/crc32.c (limited to 'pjlib-util/src') diff --git a/pjlib-util/src/pjlib-util-test/encryption.c b/pjlib-util/src/pjlib-util-test/encryption.c index 0c5de154..34e5ae59 100644 --- a/pjlib-util/src/pjlib-util-test/encryption.c +++ b/pjlib-util/src/pjlib-util-test/encryption.c @@ -383,6 +383,58 @@ static int rfc2202_test(void) return 0; } +/* CRC32 test data, generated from crc32 test on a Linux box */ +struct +{ + char *input; + pj_uint32_t crc; +} crc32_test_data[] = +{ + { + "", + 0x0 + }, + { + "Hello World", + 0x4a17b156 + }, + { + /* Something read from /dev/random */ + "\x21\x21\x98\x10\x62\x59\xbc\x58\x42\x24\xe5\xf3\x92\x0a\x68\x3c\xa7\x67\x73\xc3", + 0x506693be + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0xcab11777 + }, + { + "123456789", + 0xCBF43926 + } +}; + +/* + * CRC32 test + */ +static int crc32_test(void) +{ + unsigned i; + + PJ_LOG(3, (THIS_FILE, " crc32 test..")); + + for (i=0; i + +// crc.cpp - written and placed in the public domain by Wei Dai + +/* Table of CRC-32's of all single byte values (made by makecrc.c) */ +#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN != 0 + +#define CRC32_INDEX(c) (c & 0xff) +#define CRC32_SHIFTED(c) (c >> 8) + +static const pj_uint32_t crc_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + + +#elif defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN != 0 +#define CRC32_INDEX(c) (c >> 24) +#define CRC32_SHIFTED(c) (c << 8) + +static const pj_uint32_t crc_tab[] = { + 0x00000000L, 0x96300777L, 0x2c610eeeL, 0xba510999L, 0x19c46d07L, + 0x8ff46a70L, 0x35a563e9L, 0xa395649eL, 0x3288db0eL, 0xa4b8dc79L, + 0x1ee9d5e0L, 0x88d9d297L, 0x2b4cb609L, 0xbd7cb17eL, 0x072db8e7L, + 0x911dbf90L, 0x6410b71dL, 0xf220b06aL, 0x4871b9f3L, 0xde41be84L, + 0x7dd4da1aL, 0xebe4dd6dL, 0x51b5d4f4L, 0xc785d383L, 0x56986c13L, + 0xc0a86b64L, 0x7af962fdL, 0xecc9658aL, 0x4f5c0114L, 0xd96c0663L, + 0x633d0ffaL, 0xf50d088dL, 0xc8206e3bL, 0x5e10694cL, 0xe44160d5L, + 0x727167a2L, 0xd1e4033cL, 0x47d4044bL, 0xfd850dd2L, 0x6bb50aa5L, + 0xfaa8b535L, 0x6c98b242L, 0xd6c9bbdbL, 0x40f9bcacL, 0xe36cd832L, + 0x755cdf45L, 0xcf0dd6dcL, 0x593dd1abL, 0xac30d926L, 0x3a00de51L, + 0x8051d7c8L, 0x1661d0bfL, 0xb5f4b421L, 0x23c4b356L, 0x9995bacfL, + 0x0fa5bdb8L, 0x9eb80228L, 0x0888055fL, 0xb2d90cc6L, 0x24e90bb1L, + 0x877c6f2fL, 0x114c6858L, 0xab1d61c1L, 0x3d2d66b6L, 0x9041dc76L, + 0x0671db01L, 0xbc20d298L, 0x2a10d5efL, 0x8985b171L, 0x1fb5b606L, + 0xa5e4bf9fL, 0x33d4b8e8L, 0xa2c90778L, 0x34f9000fL, 0x8ea80996L, + 0x18980ee1L, 0xbb0d6a7fL, 0x2d3d6d08L, 0x976c6491L, 0x015c63e6L, + 0xf4516b6bL, 0x62616c1cL, 0xd8306585L, 0x4e0062f2L, 0xed95066cL, + 0x7ba5011bL, 0xc1f40882L, 0x57c40ff5L, 0xc6d9b065L, 0x50e9b712L, + 0xeab8be8bL, 0x7c88b9fcL, 0xdf1ddd62L, 0x492dda15L, 0xf37cd38cL, + 0x654cd4fbL, 0x5861b24dL, 0xce51b53aL, 0x7400bca3L, 0xe230bbd4L, + 0x41a5df4aL, 0xd795d83dL, 0x6dc4d1a4L, 0xfbf4d6d3L, 0x6ae96943L, + 0xfcd96e34L, 0x468867adL, 0xd0b860daL, 0x732d0444L, 0xe51d0333L, + 0x5f4c0aaaL, 0xc97c0dddL, 0x3c710550L, 0xaa410227L, 0x10100bbeL, + 0x86200cc9L, 0x25b56857L, 0xb3856f20L, 0x09d466b9L, 0x9fe461ceL, + 0x0ef9de5eL, 0x98c9d929L, 0x2298d0b0L, 0xb4a8d7c7L, 0x173db359L, + 0x810db42eL, 0x3b5cbdb7L, 0xad6cbac0L, 0x2083b8edL, 0xb6b3bf9aL, + 0x0ce2b603L, 0x9ad2b174L, 0x3947d5eaL, 0xaf77d29dL, 0x1526db04L, + 0x8316dc73L, 0x120b63e3L, 0x843b6494L, 0x3e6a6d0dL, 0xa85a6a7aL, + 0x0bcf0ee4L, 0x9dff0993L, 0x27ae000aL, 0xb19e077dL, 0x44930ff0L, + 0xd2a30887L, 0x68f2011eL, 0xfec20669L, 0x5d5762f7L, 0xcb676580L, + 0x71366c19L, 0xe7066b6eL, 0x761bd4feL, 0xe02bd389L, 0x5a7ada10L, + 0xcc4add67L, 0x6fdfb9f9L, 0xf9efbe8eL, 0x43beb717L, 0xd58eb060L, + 0xe8a3d6d6L, 0x7e93d1a1L, 0xc4c2d838L, 0x52f2df4fL, 0xf167bbd1L, + 0x6757bca6L, 0xdd06b53fL, 0x4b36b248L, 0xda2b0dd8L, 0x4c1b0aafL, + 0xf64a0336L, 0x607a0441L, 0xc3ef60dfL, 0x55df67a8L, 0xef8e6e31L, + 0x79be6946L, 0x8cb361cbL, 0x1a8366bcL, 0xa0d26f25L, 0x36e26852L, + 0x95770cccL, 0x03470bbbL, 0xb9160222L, 0x2f260555L, 0xbe3bbac5L, + 0x280bbdb2L, 0x925ab42bL, 0x046ab35cL, 0xa7ffd7c2L, 0x31cfd0b5L, + 0x8b9ed92cL, 0x1daede5bL, 0xb0c2649bL, 0x26f263ecL, 0x9ca36a75L, + 0x0a936d02L, 0xa906099cL, 0x3f360eebL, 0x85670772L, 0x13570005L, + 0x824abf95L, 0x147ab8e2L, 0xae2bb17bL, 0x381bb60cL, 0x9b8ed292L, + 0x0dbed5e5L, 0xb7efdc7cL, 0x21dfdb0bL, 0xd4d2d386L, 0x42e2d4f1L, + 0xf8b3dd68L, 0x6e83da1fL, 0xcd16be81L, 0x5b26b9f6L, 0xe177b06fL, + 0x7747b718L, 0xe65a0888L, 0x706a0fffL, 0xca3b0666L, 0x5c0b0111L, + 0xff9e658fL, 0x69ae62f8L, 0xd3ff6b61L, 0x45cf6c16L, 0x78e20aa0L, + 0xeed20dd7L, 0x5483044eL, 0xc2b30339L, 0x612667a7L, 0xf71660d0L, + 0x4d476949L, 0xdb776e3eL, 0x4a6ad1aeL, 0xdc5ad6d9L, 0x660bdf40L, + 0xf03bd837L, 0x53aebca9L, 0xc59ebbdeL, 0x7fcfb247L, 0xe9ffb530L, + 0x1cf2bdbdL, 0x8ac2bacaL, 0x3093b353L, 0xa6a3b424L, 0x0536d0baL, + 0x9306d7cdL, 0x2957de54L, 0xbf67d923L, 0x2e7a66b3L, 0xb84a61c4L, + 0x021b685dL, 0x942b6f2aL, 0x37be0bb4L, 0xa18e0cc3L, 0x1bdf055aL, + 0x8def022dL +}; + +#else +# error "Endianness not defined" +#endif + + +#define CRC32_NEGL 0xffffffffL + + +PJ_DEF(void) pj_crc32_init(pj_crc32_context *ctx) +{ + ctx->crc_state = 0; +} + +PJ_DEF(pj_uint32_t) pj_crc32_update(pj_crc32_context *ctx, + const pj_uint8_t *data, + pj_size_t nbytes) +{ + pj_uint32_t crc = ctx->crc_state ^ CRC32_NEGL; + + for( ; (((pj_uint32_t)data) & 0x03) && nbytes > 0; --nbytes) { + crc = crc_tab[CRC32_INDEX(crc) ^ *data++] ^ CRC32_SHIFTED(crc); + } + + while (nbytes >= 4) { + crc ^= *(const pj_uint32_t *)data; + crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); + crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); + crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); + crc = crc_tab[CRC32_INDEX(crc)] ^ CRC32_SHIFTED(crc); + nbytes -= 4; + data += 4; + } + + while (nbytes--) { + crc = crc_tab[CRC32_INDEX(crc) ^ *data++] ^ CRC32_SHIFTED(crc); + } + + ctx->crc_state = crc ^ CRC32_NEGL; + + return ctx->crc_state; +} + +PJ_DEF(pj_uint32_t) pj_crc32_final(pj_crc32_context *ctx) +{ + return ctx->crc_state; +} + +PJ_DEF(pj_uint32_t) pj_crc32_calc( const pj_uint8_t *data, + pj_size_t nbytes) +{ + pj_crc32_context ctx; + + pj_crc32_init(&ctx); + pj_crc32_update(&ctx, data, nbytes); + return pj_crc32_final(&ctx); +} + diff --git a/pjlib-util/src/pjlib-util/stun_msg.c b/pjlib-util/src/pjlib-util/stun_msg.c index fbe2223c..44c390d8 100644 --- a/pjlib-util/src/pjlib-util/stun_msg.c +++ b/pjlib-util/src/pjlib-util/stun_msg.c @@ -17,7 +17,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include +#include +#include #include #include #include @@ -25,8 +28,8 @@ #include #include -#define THIS_FILE "stun_msg.c" - +#define THIS_FILE "stun_msg.c" +#define STUN_XOR_FINGERPRINT 0x5354554eL static const char *stun_method_names[] = { @@ -547,6 +550,28 @@ pj_stun_generic_ip_addr_attr_create(pj_pool_t *pool, } +/* + * Create and add generic STUN IP address attribute to a STUN message. + */ +PJ_DEF(pj_status_t) +pj_stun_msg_add_generic_ip_addr_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + pj_bool_t xor_ed, + unsigned addr_len, + const pj_sockaddr_t *addr) +{ + pj_stun_generic_ip_addr_attr *attr; + pj_status_t status; + + status = pj_stun_generic_ip_addr_attr_create(pool, attr_type, xor_ed, + addr_len, addr, &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} + static pj_status_t decode_generic_ip_addr_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr) @@ -658,6 +683,27 @@ pj_stun_generic_string_attr_create(pj_pool_t *pool, } +/* + * Create and add STUN generic string attribute to the message. + */ +PJ_DEF(pj_status_t) +pj_stun_msg_add_generic_string_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + const pj_str_t *value) +{ + pj_stun_generic_string_attr *attr; + pj_status_t status; + + status = pj_stun_generic_string_attr_create(pool, attr_type, value, + &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} + + static pj_status_t decode_generic_string_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr) @@ -827,6 +873,22 @@ pj_stun_generic_uint_attr_create(pj_pool_t *pool, return PJ_SUCCESS; } +/* Create and add STUN generic 32bit value attribute to the message. */ +PJ_DEF(pj_status_t) +pj_stun_msg_add_generic_uint_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + pj_uint32_t value) +{ + pj_stun_generic_uint_attr *attr; + pj_status_t status; + + status = pj_stun_generic_uint_attr_create(pool, attr_type, value, &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} static pj_status_t decode_generic_uint_attr(pj_pool_t *pool, const pj_uint8_t *buf, @@ -1087,7 +1149,7 @@ static pj_status_t encode_error_code_attr(const void *a, pj_uint8_t *buf, PJ_DEF(pj_status_t) pj_stun_unknown_attr_create(pj_pool_t *pool, unsigned attr_cnt, - pj_uint16_t attr_array[], + const pj_uint16_t attr_array[], pj_stun_unknown_attr **p_attr) { pj_stun_unknown_attr *attr; @@ -1116,6 +1178,23 @@ pj_stun_unknown_attr_create(pj_pool_t *pool, } +/* Create and add STUN UNKNOWN-ATTRIBUTES attribute to the message. */ +PJ_DEF(pj_status_t) +pj_stun_msg_add_unknown_attr(pj_pool_t *pool, + pj_stun_msg *msg, + unsigned attr_cnt, + const pj_uint16_t attr_types[]) +{ + pj_stun_unknown_attr *attr; + pj_status_t status; + + status = pj_stun_unknown_attr_create(pool, attr_cnt, attr_types, &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} + static pj_status_t decode_unknown_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr) @@ -1193,6 +1272,8 @@ static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, PJ_DEF(pj_status_t) pj_stun_binary_attr_create(pj_pool_t *pool, int attr_type, + const pj_uint8_t *data, + unsigned length, pj_stun_binary_attr **p_attr) { pj_stun_binary_attr *attr; @@ -1202,12 +1283,38 @@ pj_stun_binary_attr_create(pj_pool_t *pool, attr = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_binary_attr); INIT_ATTR(attr, attr_type, sizeof(pj_stun_binary_attr)); + if (data && length) { + attr->length = length; + attr->data = pj_pool_alloc(pool, length); + pj_memcpy(attr->data, data, length); + } + *p_attr = attr; return PJ_SUCCESS; } +/* Create and add binary attr. */ +PJ_DEF(pj_status_t) +pj_stun_msg_add_binary_attr(pj_pool_t *pool, + pj_stun_msg *msg, + int attr_type, + const pj_uint8_t *data, + unsigned length) +{ + pj_stun_binary_attr *attr; + pj_status_t status; + + status = pj_stun_binary_attr_create(pool, attr_type, + data, length, &attr); + if (status != PJ_SUCCESS) + return status; + + return pj_stun_msg_add_attr(msg, &attr->hdr); +} + + static pj_status_t decode_binary_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr) @@ -1324,42 +1431,119 @@ PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, } +PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos) +{ + pj_uint16_t val = (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]); + return pj_ntohs(val); +} + +PJ_INLINE(pj_uint32_t) GET_VAL32(const pj_uint8_t *pdu, unsigned pos) +{ + pj_uint32_t val = (pdu[pos+0] << 24) + + (pdu[pos+1] << 16) + + (pdu[pos+2] << 8) + + (pdu[pos+3]); + return pj_ntohl(val); +} + + /* * Check that the PDU is potentially a valid STUN message. */ -PJ_DEF(pj_status_t) pj_stun_msg_check(const void *pdu, unsigned pdu_len, +PJ_DEF(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, unsigned pdu_len, unsigned options) { - pj_stun_msg_hdr *hdr; + unsigned msg_len; PJ_ASSERT_RETURN(pdu, PJ_EINVAL); if (pdu_len < sizeof(pj_stun_msg_hdr)) return PJLIB_UTIL_ESTUNINMSGLEN; - PJ_UNUSED_ARG(options); - - hdr = (pj_stun_msg_hdr*) pdu; - /* First byte of STUN message is always 0x00 or 0x01. */ - if ((*(const char*)pdu) != 0x00 && (*(const char*)pdu) != 0x01) + if (*pdu != 0x00 && *pdu != 0x01) return PJLIB_UTIL_ESTUNINMSGTYPE; /* If magic is set, then there is great possibility that this is * a STUN message. */ - if (pj_ntohl(hdr->magic) == PJ_STUN_MAGIC) - return PJ_SUCCESS; + if (GET_VAL32(pdu, 4) != PJ_STUN_MAGIC) + return PJLIB_UTIL_ESTUNNOTMAGIC; /* Check the PDU length */ - if (pj_ntohs(hdr->length) > pdu_len) + msg_len = GET_VAL16(pdu, 2); + if ((msg_len > pdu_len) || + ((options & PJ_STUN_IS_DATAGRAM) && msg_len != pdu_len)) + { return PJLIB_UTIL_ESTUNINMSGLEN; + } + + /* Check if FINGERPRINT attribute is present */ + if (GET_VAL16(pdu, msg_len + 20) == PJ_STUN_ATTR_FINGERPRINT) { + pj_uint16_t attr_len = GET_VAL16(pdu, msg_len + 22); + pj_uint32_t fingerprint = GET_VAL32(pdu, msg_len + 24); + pj_uint32_t crc; + + if (attr_len != 4) + return PJLIB_UTIL_ESTUNINATTRLEN; + + crc = pj_crc32_calc(pdu, msg_len + 20); + crc ^= STUN_XOR_FINGERPRINT; + + if (crc != fingerprint) + return PJLIB_UTIL_ESTUNFINGERPRINT; + } /* Could be a STUN message */ return PJ_SUCCESS; } +/* Create error response */ +PJ_DEF(pj_status_t) pj_stun_msg_create_response(pj_pool_t *pool, + const pj_stun_msg *req_msg, + unsigned err_code, + const pj_str_t *err_msg, + pj_stun_msg **p_response) +{ + unsigned msg_type = req_msg->hdr.type; + pj_stun_msg *response; + pj_stun_error_code_attr *err_attr; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && p_response, PJ_EINVAL); + + PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(msg_type), + PJLIB_UTIL_ESTUNINMSGTYPE); + + /* Create response or error response */ + if (err_code) + msg_type |= PJ_STUN_ERROR_RESPONSE_BIT; + else + msg_type |= PJ_STUN_RESPONSE_BIT; + + status = pj_stun_msg_create(pool, msg_type, req_msg->hdr.magic, + req_msg->hdr.tsx_id, &response); + if (status != PJ_SUCCESS) { + return status; + } + + /* Add error code attribute */ + if (err_code) { + status = pj_stun_error_code_attr_create(pool, err_code, err_msg, + &err_attr); + if (status != PJ_SUCCESS) { + return status; + } + + pj_stun_msg_add_attr(response, &err_attr->hdr); + } + + *p_response = response; + return PJ_SUCCESS; +} + + /* * Parse incoming packet into STUN message. */ @@ -1369,9 +1553,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, unsigned options, pj_stun_msg **p_msg, unsigned *p_parsed_len, - unsigned *p_err_code, - unsigned *p_uattr_cnt, - pj_uint16_t uattr[]) + pj_stun_msg **p_response) { pj_stun_msg *msg; @@ -1384,9 +1566,17 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, PJ_ASSERT_RETURN(pool && pdu && pdu_len && p_msg, PJ_EINVAL); PJ_ASSERT_RETURN(sizeof(pj_stun_msg_hdr) == 20, PJ_EBUG); - /* Application should have checked that this is a valid STUN msg */ - PJ_ASSERT_RETURN((status=pj_stun_msg_check(pdu, pdu_len, options)) - == PJ_SUCCESS, status); + if (p_parsed_len) + *p_parsed_len = 0; + if (p_response) + *p_response = NULL; + + /* Check if this is a STUN message, if necessary */ + if (options & PJ_STUN_CHECK_PACKET) { + status = pj_stun_msg_check(pdu, pdu_len, options); + if (status != PJ_SUCCESS) + return status; + } /* Create the message, copy the header, and convert to host byte order */ msg = PJ_POOL_ZALLOC_TYPE(pool, pj_stun_msg); @@ -1398,8 +1588,9 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, pdu += sizeof(pj_stun_msg_hdr); pdu_len -= sizeof(pj_stun_msg_hdr); - if (p_err_code) - *p_err_code = 0; + /* No need to create response if this is not a request */ + if (!PJ_STUN_IS_REQUEST(msg->hdr.type)) + p_response = NULL; /* Parse attributes */ uattr_cnt = 0; @@ -1415,8 +1606,25 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, attr_val_len = (attr_val_len + 3) & (~3); /* Check length */ - if (pdu_len < attr_val_len) + if (pdu_len < attr_val_len) { + pj_str_t err_msg; + char err_msg_buf[80]; + + err_msg.ptr = err_msg_buf; + err_msg.slen = pj_ansi_snprintf(err_msg_buf, sizeof(err_msg_buf), + "Attribute %s has invalid length", + pj_stun_get_attr_name(attr_type)); + + PJ_LOG(4,(THIS_FILE, "Error decoding message: %.*s", + (int)err_msg.slen, err_msg.ptr)); + + if (p_response) { + pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_BAD_REQUEST, + &err_msg, p_response); + } return PJLIB_UTIL_ESTUNINATTRLEN; + } /* Get the attribute descriptor */ adesc = find_attr_desc(attr_type); @@ -1427,38 +1635,71 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, PJ_LOG(4,(THIS_FILE, "Unrecognized attribute type %d", attr_type)); - /* Put to unrecognized attribute array */ - if (p_uattr_cnt && uattr && uattr_cnt < *p_uattr_cnt) { - uattr[uattr_cnt++] = (pj_uint16_t)attr_type; - } - /* Is this a fatal condition? */ if (attr_type <= 0x7FFF) { /* This is a mandatory attribute, we must return error * if we don't understand the attribute. */ - if (p_err_code && *p_err_code == 0) - *p_err_code = PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE; + if (p_response) { + unsigned err_code = PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE; + + status = pj_stun_msg_create_response(pool, msg, + err_code, NULL, + p_response); + if (status==PJ_SUCCESS) { + pj_uint16_t d = (pj_uint16_t)attr_type; + pj_stun_msg_add_unknown_attr(pool, *p_response, 1, &d); + } + } return PJLIB_UTIL_ESTUNUNKNOWNATTR; } } else { void *attr; + char err_msg1[PJ_ERR_MSG_SIZE], + err_msg2[PJ_ERR_MSG_SIZE]; /* Parse the attribute */ status = (adesc->decode_attr)(pool, pdu, &attr); if (status != PJ_SUCCESS) { + pj_strerror(status, err_msg1, sizeof(err_msg1)); + + if (p_response) { + pj_str_t e; + + e.ptr = err_msg2; + e.slen= pj_ansi_snprintf(err_msg2, sizeof(err_msg2), + "%s in %s", + err_msg1, + pj_stun_get_attr_name(attr_type)); + + pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_BAD_REQUEST, + &e, p_response); + } + PJ_LOG(4,(THIS_FILE, - "Error parsing STUN attribute type %d: status=%d", - attr_type, status)); + "Error parsing STUN attribute %s: %s", + pj_stun_get_attr_name(attr_type), + err_msg1)); + return status; } /* Make sure we have rooms for the new attribute */ - if (msg->attr_count >= PJ_STUN_MAX_ATTR) + if (msg->attr_count >= PJ_STUN_MAX_ATTR) { + if (p_response) { + pj_str_t e; + + e = pj_str("Too many attributes"); + pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_BAD_REQUEST, + &e, p_response); + } return PJLIB_UTIL_ESTUNTOOMANYATTR; + } /* Add the attribute */ msg->attr[msg->attr_count++] = (pj_stun_attr_hdr*)attr; @@ -1470,9 +1711,6 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, *p_msg = msg; - if (p_uattr_cnt) - *p_uattr_cnt = uattr_cnt; - if (p_parsed_len) *p_parsed_len = (pdu - start_pdu); @@ -1494,7 +1732,11 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, pj_stun_realm_attr *arealm = NULL; pj_stun_username_attr *auname = NULL; pj_stun_msg_integrity_attr *amsg_integrity = NULL; - unsigned i; + pj_stun_fingerprint_attr *afingerprint = NULL; + unsigned printed; + pj_status_t status; + unsigned i, length; + PJ_ASSERT_RETURN(msg && buf && buf_size, PJ_EINVAL); @@ -1519,8 +1761,6 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, for (i=0; iattr_count; ++i) { const struct attr_desc *adesc; const pj_stun_attr_hdr *attr_hdr; - unsigned printed; - pj_status_t status; attr_hdr = msg->attr[i]; @@ -1534,9 +1774,14 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, } else if (attr_hdr->type == PJ_STUN_ATTR_USERNAME) { pj_assert(auname == NULL); auname = (pj_stun_username_attr*) attr_hdr; + } else if (attr_hdr->type == PJ_STUN_ATTR_REALM) { pj_assert(arealm == NULL); arealm = (pj_stun_realm_attr*) attr_hdr; + + } else if (attr_hdr->type == PJ_STUN_ATTR_FINGERPRINT) { + afingerprint = (pj_stun_fingerprint_attr*) attr_hdr; + break; } adesc = find_attr_desc(attr_hdr->type); @@ -1550,16 +1795,128 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(const pj_stun_msg *msg, buf_size -= printed; } + /* Calculate message integrity, if present */ if (amsg_integrity != NULL) { - PJ_TODO(IMPLEMENT_MSG_INTEGRITY); + + pj_uint8_t md5_key_buf[16]; + pj_str_t key; + + /* MESSAGE-INTEGRITY must be the last attribute in the message, or + * the last attribute before FINGERPRINT. + */ + if (i < msg->attr_count-2) { + /* Should not happen for message generated by us */ + pj_assert(PJ_FALSE); + return PJLIB_UTIL_ESTUNMSGINT; + + } else if (i == msg->attr_count-2) { + if (msg->attr[i+1]->type != PJ_STUN_ATTR_FINGERPRINT) { + /* Should not happen for message generated by us */ + pj_assert(PJ_FALSE); + return PJLIB_UTIL_ESTUNMSGINT; + } else { + afingerprint = (pj_stun_fingerprint_attr*) msg->attr[i+1]; + } + } + + /* Must have USERNAME attribute */ + if (auname == NULL) { + /* Should not happen for message generated by us */ + pj_assert(PJ_FALSE); + return PJLIB_UTIL_ESTUNNOUSERNAME; + } + + /* Password must be specified */ + PJ_ASSERT_RETURN(password, PJ_EINVAL); + + /* Get the key to sign the message */ + if (arealm == NULL ) { + /* For short term credential, the key is the password */ + key = *password; + + } else { + /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking + * the MD5 hash of the result of concatenating the following five + * fields: (1) The username, with any quotes and trailing nulls + * removed, (2) A single colon, (3) The realm, with any quotes and + * trailing nulls removed, (4) A single colon, and (5) The + * password, with any trailing nulls removed. + */ + pj_md5_context ctx; + pj_str_t s; + + pj_md5_init(&ctx); + +#define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \ + s.ptr++, s.slen--; \ + if (s.slen && s.ptr[s.slen-1]=='"') \ + s.slen--; + + /* Add username */ + s = auname->value; + REMOVE_QUOTE(s); + pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); + + /* Add single colon */ + pj_md5_update(&ctx, (pj_uint8_t*)":", 1); + + /* Add realm */ + s = arealm->value; + REMOVE_QUOTE(s); + pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); + +#undef REMOVE_QUOTE + + /* Another colon */ + pj_md5_update(&ctx, (pj_uint8_t*)":", 1); + + /* Add password */ + pj_md5_update(&ctx, (pj_uint8_t*)password->ptr, password->slen); + + /* Done */ + pj_md5_final(&ctx, md5_key_buf); + key.ptr = (char*) md5_key_buf; + key.slen = 16; + } + + /* Calculate HMAC-SHA1 digest */ + pj_hmac_sha1((pj_uint8_t*)buf, buf-start, + (pj_uint8_t*)key.ptr, key.slen, + amsg_integrity->hmac); + + /* Put this attribute in the message */ + status = encode_msg_integrity_attr(amsg_integrity, buf, buf_size, + &printed); + if (status != PJ_SUCCESS) + return status; + + buf += printed; + buf_size -= printed; } + /* Calculate FINGERPRINT if present */ + if (afingerprint != NULL) { + afingerprint->value = pj_crc32_calc(start, buf-start); + afingerprint->value ^= STUN_XOR_FINGERPRINT; + + /* Put this attribute in the message */ + status = encode_generic_uint_attr(afingerprint, buf, buf_size, + &printed); + if (status != PJ_SUCCESS) + return status; + + buf += printed; + buf_size -= printed; + } /* Update the message length in the header. * Note that length is not including the 20 bytes header. */ - hdr->length = (pj_uint16_t)((buf - start) - 20); - hdr->length = pj_htons(hdr->length); + length = (pj_uint16_t)((buf - start) - 20); + /* hdr->length = pj_htons(length); */ + *(buf+2) = (pj_uint8_t)((length >> 8) & 0x00FF); + *(buf+3) = (pj_uint8_t)(length & 0x00FF); + /* Done */ if (p_msg_len) @@ -1587,3 +1944,116 @@ PJ_DEF(pj_stun_attr_hdr*) pj_stun_msg_find_attr( const pj_stun_msg *msg, return NULL; } + +/* Verify credential */ +PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_stun_msg *msg, + const pj_str_t *realm, + const pj_str_t *username, + const pj_str_t *password, + unsigned options, + pj_pool_t *pool, + pj_stun_msg **p_response) +{ + const pj_stun_msg_integrity_attr *amsgi; + const pj_stun_username_attr *auser; + const pj_stun_realm_attr *arealm; + + PJ_ASSERT_RETURN(msg && password, PJ_EINVAL); + PJ_ASSERT_RETURN(options==0, PJ_EINVAL); + PJ_UNUSED_ARG(options); + + if (p_response) + *p_response = NULL; + + /* First check that MESSAGE-INTEGRITY is present */ + amsgi = (const pj_stun_msg_integrity_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0); + if (amsgi == NULL) { + if (pool && p_response) { + pj_status_t rc; + + rc = pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_UNAUTHORIZED, + NULL, p_response); + if (rc==PJ_SUCCESS && realm) { + pj_stun_msg_add_generic_string_attr(pool, *p_response, + PJ_STUN_ATTR_REALM, + realm); + } + } + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_UNAUTHORIZED); + } + + /* Next check that USERNAME is present */ + auser = (const pj_stun_username_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); + if (auser == NULL) { + if (pool && p_response) { + pj_status_t rc; + + rc = pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_MISSING_USERNAME, + NULL, p_response); + if (rc==PJ_SUCCESS && realm) { + pj_stun_msg_add_generic_string_attr(pool, *p_response, + PJ_STUN_ATTR_REALM, + realm); + } + } + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_USERNAME); + } + + /* Check if username match */ + if (username && pj_stricmp(&auser->value, username) != 0) { + /* Username mismatch */ + if (pool && p_response) { + pj_status_t rc; + + rc = pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_WRONG_USERNAME, + NULL, p_response); + if (rc==PJ_SUCCESS && realm) { + pj_stun_msg_add_generic_string_attr(pool, *p_response, + PJ_STUN_ATTR_REALM, + realm); + } + } + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_WRONG_USERNAME); + } + + /* Next check that REALM is present */ + arealm = (const pj_stun_realm_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); + if (realm != NULL && arealm == NULL) { + /* Long term credential is required */ + if (pool && p_response) { + pj_status_t rc; + + rc = pj_stun_msg_create_response(pool, msg, + PJ_STUN_STATUS_MISSING_REALM, + NULL, p_response); + if (rc==PJ_SUCCESS) { + pj_stun_msg_add_generic_string_attr(pool, *p_response, + PJ_STUN_ATTR_REALM, + realm); + } + } + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_REALM); + + } else if (realm != NULL && arealm != NULL) { + + + } else if (realm == NULL && arealm != NULL) { + /* We want to use short term credential, but client uses long + * term credential. The draft doesn't mention anything about + * switching between long term and short term. + */ + PJ_TODO(SWITCHING_BETWEEN_SHORT_TERM_AND_LONG_TERM); + } + + PJ_TODO(CONTINUE_IMPLEMENTATION); + + return PJ_SUCCESS; +} + + diff --git a/pjlib-util/src/pjstun-client/stun_session.c b/pjlib-util/src/pjstun-client/stun_session.c index 101bb0b4..03dc6f19 100644 --- a/pjlib-util/src/pjstun-client/stun_session.c +++ b/pjlib-util/src/pjstun-client/stun_session.c @@ -484,7 +484,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, /* Encode message */ status = pj_stun_msg_encode(tdata->msg, tdata->pkt, tdata->max_len, - 0, &tdata->pkt_size); + 0, NULL, &tdata->pkt_size); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN encode() error", status); destroy_tdata(tdata); @@ -540,7 +540,7 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, pj_size_t pkt_size, unsigned *parsed_len) { - pj_stun_msg *msg; + pj_stun_msg *msg, *response; pj_pool_t *tmp_pool; char *dump; pj_status_t status; @@ -554,9 +554,12 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, /* Try to parse the message */ status = pj_stun_msg_decode(tmp_pool, (const pj_uint8_t*)packet, pkt_size, 0, &msg, parsed_len, - NULL, NULL, NULL); + &response); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN msg_decode() error", status); + if (response) { + PJ_TODO(SEND_RESPONSE); + } pj_pool_release(tmp_pool); return status; } diff --git a/pjlib-util/src/pjstun-srv/server_main.c b/pjlib-util/src/pjstun-srv/server_main.c index 5fd58ef9..46dc2752 100644 --- a/pjlib-util/src/pjstun-srv/server_main.c +++ b/pjlib-util/src/pjstun-srv/server_main.c @@ -51,31 +51,12 @@ static pj_status_t create_response(pj_pool_t *pool, { pj_uint32_t msg_type = req_msg->hdr.type; pj_stun_msg *response; - pj_stun_error_code_attr *err_attr; pj_status_t status; - /* Create response or error response */ - if (err_code) - msg_type |= PJ_STUN_ERROR_RESPONSE_BIT; - else - msg_type |= PJ_STUN_RESPONSE_BIT; - - status = pj_stun_msg_create(pool, msg_type, req_msg->hdr.magic, - req_msg->hdr.tsx_id, &response); - if (status != PJ_SUCCESS) { + status = pj_stun_msg_create_response(pool, req_msg, err_code, NULL, + &response); + if (status != PJ_SUCCESS) return status; - } - - /* Add error code attribute */ - if (err_code) { - status = pj_stun_error_code_attr_create(pool, err_code, NULL, - &err_attr); - if (status != PJ_SUCCESS) { - return status; - } - - pj_stun_msg_add_attr(response, &err_attr->hdr); - } /* Add unknown_attribute attributes if err_code is 420 */ if (err_code == PJ_STUN_STATUS_UNKNOWN_ATTRIBUTE) { @@ -110,7 +91,7 @@ static pj_status_t send_msg(struct service *svc, const pj_stun_msg *msg) /* Encode packet */ tx_pkt_len = sizeof(svc->tx_pkt); status = pj_stun_msg_encode(msg, svc->tx_pkt, tx_pkt_len, 0, - &tx_pkt_len); + NULL, &tx_pkt_len); if (status != PJ_SUCCESS) return status; @@ -223,10 +204,7 @@ static void on_read_complete(pj_ioqueue_key_t *key, { struct service *svc = (struct service *) pj_ioqueue_get_user_data(key); pj_pool_t *pool = NULL; - pj_stun_msg *rx_msg; - unsigned err_code; - unsigned uattr_cnt; - pj_uint16_t uattr_types[16]; + pj_stun_msg *rx_msg, *response; char dump[512]; pj_status_t status; @@ -235,16 +213,13 @@ static void on_read_complete(pj_ioqueue_key_t *key, pool = pj_pool_create(&server.cp.factory, "service", 4000, 4000, NULL); - err_code = 0; - uattr_cnt = PJ_ARRAY_SIZE(uattr_types); rx_msg = NULL; status = pj_stun_msg_decode(pool, svc->rx_pkt, bytes_read, 0, &rx_msg, - NULL, &err_code, &uattr_cnt, uattr_types); + NULL, &response); if (status != PJ_SUCCESS) { server_perror(THIS_FILE, "STUN msg_decode() error", status); - if (err_code != 0 && rx_msg && PJ_STUN_IS_REQUEST(rx_msg->hdr.type)) { - err_respond(svc, pool, rx_msg, err_code, - uattr_cnt, uattr_types); + if (response) { + send_msg(svc, response); } goto next_read; } -- cgit v1.2.3