summaryrefslogtreecommitdiff
path: root/pjlib-util/src/pjlib-util-test
diff options
context:
space:
mode:
Diffstat (limited to 'pjlib-util/src/pjlib-util-test')
-rw-r--r--pjlib-util/src/pjlib-util-test/encryption.c766
-rw-r--r--pjlib-util/src/pjlib-util-test/http_client.c955
-rw-r--r--pjlib-util/src/pjlib-util-test/main.c63
-rw-r--r--pjlib-util/src/pjlib-util-test/main_rtems.c11
-rw-r--r--pjlib-util/src/pjlib-util-test/main_win32.c1
-rw-r--r--pjlib-util/src/pjlib-util-test/resolver_test.c1405
-rw-r--r--pjlib-util/src/pjlib-util-test/stun.c119
-rw-r--r--pjlib-util/src/pjlib-util-test/test.c112
-rw-r--r--pjlib-util/src/pjlib-util-test/test.h38
-rw-r--r--pjlib-util/src/pjlib-util-test/xml.c145
10 files changed, 3615 insertions, 0 deletions
diff --git a/pjlib-util/src/pjlib-util-test/encryption.c b/pjlib-util/src/pjlib-util-test/encryption.c
new file mode 100644
index 0000000..706a441
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/encryption.c
@@ -0,0 +1,766 @@
+/* $Id: encryption.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * 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"
+#include <pjlib-util.h>
+#include <pjlib.h>
+
+
+#if INCLUDE_ENCRYPTION_TEST
+
+/*
+ * Encryption algorithm tests.
+ */
+#define THIS_FILE "encryption.c"
+
+
+/*
+ * SHA1 test from the original sha1.c source file.
+ */
+static char *sha1_test_data[] = {
+ "abc",
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "A million repetitions of 'a'"
+};
+static char *sha1_test_results[] = {
+ "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
+ "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
+ "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"
+};
+
+
+static void digest_to_hex(const pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE],
+ char *output)
+{
+ int i,j;
+ char *c = output;
+
+ for (i = 0; i < PJ_SHA1_DIGEST_SIZE/4; i++) {
+ for (j = 0; j < 4; j++) {
+ sprintf(c,"%02X", digest[i*4+j]);
+ c += 2;
+ }
+ sprintf(c, " ");
+ c += 1;
+ }
+ *(c - 1) = '\0';
+}
+
+static int sha1_test1(void)
+{
+ enum { MILLION = 1000000 };
+ int k;
+ pj_sha1_context context;
+ pj_uint8_t digest[20];
+ char output[80];
+ pj_pool_t *pool;
+ pj_uint8_t *block;
+
+ PJ_LOG(3, (THIS_FILE, " SHA1 test vector 1 from sha1.c.."));
+
+ for (k = 0; k < 2; k++){
+ pj_sha1_init(&context);
+ pj_sha1_update(&context, (pj_uint8_t*)sha1_test_data[k],
+ pj_ansi_strlen(sha1_test_data[k]));
+ pj_sha1_final(&context, digest);
+ digest_to_hex(digest, output);
+
+ if (pj_ansi_strcmp(output, sha1_test_results[k])) {
+ PJ_LOG(3, (THIS_FILE, " incorrect hash result on k=%d", k));
+ return -10;
+ }
+ }
+
+ /* million 'a' vector we feed separately */
+ pj_sha1_init(&context);
+ for (k = 0; k < MILLION; k++)
+ pj_sha1_update(&context, (pj_uint8_t*)"a", 1);
+ pj_sha1_final(&context, digest);
+ digest_to_hex(digest, output);
+ if (strcmp(output, sha1_test_results[2])) {
+ PJ_LOG(3, (THIS_FILE, " incorrect hash result!"));
+ return -20;
+ }
+
+ /* million 'a' test, using block */
+ pool = pj_pool_create(mem, "sha1test", 256, 512, NULL);
+ block = (pj_uint8_t*)pj_pool_alloc(pool, MILLION);
+ pj_memset(block, 'a', MILLION);
+
+ pj_sha1_init(&context);
+ pj_sha1_update(&context, block, MILLION);
+ pj_sha1_final(&context, digest);
+ digest_to_hex(digest, output);
+ if (strcmp(output, sha1_test_results[2])) {
+ pj_pool_release(pool);
+ PJ_LOG(3, (THIS_FILE, " incorrect hash result for block update!"));
+ return -21;
+ }
+
+ /* verify that original buffer was not modified */
+ for (k=0; k<MILLION; ++k) {
+ if (block[k] != 'a') {
+ pj_pool_release(pool);
+ PJ_LOG(3, (THIS_FILE, " block was modified!"));
+ return -22;
+ }
+ }
+
+ pj_pool_release(pool);
+
+ /* success */
+ return(0);
+}
+
+
+/*
+ * SHA1 test from RFC 3174
+ */
+/*
+ * Define patterns for testing
+ */
+#define TEST1 "abc"
+#define TEST2a "abcdbcdecdefdefgefghfghighijhi"
+#define TEST2b "jkijkljklmklmnlmnomnopnopq"
+#define TEST2 TEST2a TEST2b
+#define TEST3 "a"
+#define TEST4a "01234567012345670123456701234567"
+#define TEST4b "01234567012345670123456701234567"
+ /* an exact multiple of 512 bits */
+#define TEST4 TEST4a TEST4b
+
+static char *testarray[4] =
+{
+ TEST1,
+ TEST2,
+ TEST3,
+ TEST4
+};
+static int repeatcount[4] = { 1, 1, 1000000, 10 };
+static char *resultarray[4] =
+{
+ "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
+ "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
+ "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F",
+ "DEA356A2 CDDD90C7 A7ECEDC5 EBB56393 4F460452"
+};
+
+static int sha1_test2(void)
+{
+ pj_sha1_context sha;
+ int i;
+ pj_uint8_t digest[20];
+ char char_digest[64];
+
+ PJ_LOG(3, (THIS_FILE, " SHA1 test vector 2 from rfc 3174.."));
+
+ for(i = 0; i < 4; ++i) {
+ int j;
+
+ pj_sha1_init(&sha);
+
+ for(j = 0; j < repeatcount[i]; ++j) {
+ pj_sha1_update(&sha,
+ (const pj_uint8_t *) testarray[i],
+ pj_ansi_strlen(testarray[i]));
+ }
+
+ pj_sha1_final(&sha, digest);
+
+ digest_to_hex(digest, char_digest);
+ if (pj_ansi_strcmp(char_digest, resultarray[i])) {
+ PJ_LOG(3, (THIS_FILE, " digest mismatch in test %d", i));
+ return -40;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * HMAC-MD5 and HMAC-SHA1 test vectors from RFC 2202
+ */
+struct rfc2202_test
+{
+ char *key;
+ unsigned key_len;
+ char *input;
+ unsigned input_len;
+ char *md5_digest;
+ char *sha1_digest;
+};
+
+
+struct rfc2202_test rfc2202_test_vector[] =
+{
+ {
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+ 16,
+ "Hi There",
+ 8,
+ "\x92\x94\x72\x7a\x36\x38\xbb\x1c\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d",
+ NULL
+ },
+ {
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+ 20,
+ "Hi There",
+ 8,
+ NULL,
+ "\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00"
+ },
+ {
+ "Jefe",
+ 4,
+ "what do ya want for nothing?",
+ 28,
+ "\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38",
+ "\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79"
+ },
+ {
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa",
+ 16,
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+ 50,
+ "\x56\xbe\x34\x52\x1d\x14\x4c\x88\xdb\xb8\xc7\x33\xf0\xe8\xb3\xf6",
+ NULL
+ },
+ {
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+ 20,
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+ 50,
+ NULL,
+ "\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3"
+ },
+ {
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19",
+ 25,
+ "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+ "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+ "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+ "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+ "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+ 50,
+ "\x69\x7e\xaf\x0a\xca\x3a\x3a\xea\x3a\x75\x16\x47\x46\xff\xaa\x79",
+ "\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda"
+ },
+ {
+ "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+ "\x0c\x0c\x0c\x0c\x0c\x0c",
+ 16,
+ "Test With Truncation",
+ 20,
+ "\x56\x46\x1e\xf2\x34\x2e\xdc\x00\xf9\xba\xb9\x95\x69\x0e\xfd\x4c",
+ NULL
+ },
+ {
+ "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+ "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+ 20,
+ "Test With Truncation",
+ 20,
+ NULL,
+ "\x4c\x1a\x03\x42\x4b\x55\xe0\x7f\xe7\xf2\x7b\xe1\xd5\x8b\xb9\x32\x4a\x9a\x5a\x04"
+ },
+ {
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+ 80,
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ 54,
+ "\x6b\x1a\xb7\xfe\x4b\xd7\xbf\x8f\x0b\x62\xe6\xce\x61\xb9\xd0\xcd",
+ "\xaa\x4a\xe5\xe1\x52\x72\xd0\x0e\x95\x70\x56\x37\xce\x8a\x3b\x55\xed\x40\x21\x12"
+ },
+ {
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+ 80,
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ 73,
+ "\x6f\x63\x0f\xad\x67\xcd\xa0\xee\x1f\xb1\xf5\x62\xdb\x3a\xa5\x3e",
+ "\xe8\xe9\x9d\x0f\x45\x23\x7d\x78\x6d\x6b\xba\xa7\x96\x5c\x78\x08\xbb\xff\x1a\x91"
+ }
+};
+
+
+static int rfc2202_test(void)
+{
+ unsigned i;
+
+ PJ_LOG(3, (THIS_FILE, " verifying test vectors from rfc 2202.."));
+
+ /* Verify that test vectors are valid */
+ for (i=0; i<PJ_ARRAY_SIZE(rfc2202_test_vector); ++i) {
+ PJ_ASSERT_RETURN(pj_ansi_strlen(rfc2202_test_vector[i].input) ==
+ rfc2202_test_vector[i].input_len, -50);
+ PJ_ASSERT_RETURN(pj_ansi_strlen(rfc2202_test_vector[i].key) ==
+ rfc2202_test_vector[i].key_len, -52);
+ PJ_ASSERT_RETURN(rfc2202_test_vector[i].md5_digest==NULL ||
+ pj_ansi_strlen(rfc2202_test_vector[i].md5_digest)<=16,
+ -54);
+ PJ_ASSERT_RETURN(rfc2202_test_vector[i].sha1_digest==NULL ||
+ pj_ansi_strlen(rfc2202_test_vector[i].sha1_digest)<=20,
+ -56);
+ }
+
+ /* Test HMAC-MD5 */
+ PJ_LOG(3, (THIS_FILE, " HMAC-MD5 test vectors from rfc 2202.."));
+ for (i=0; i<PJ_ARRAY_SIZE(rfc2202_test_vector); ++i) {
+ pj_uint8_t digest_buf[18], *digest;
+
+ if (rfc2202_test_vector[i].md5_digest == NULL)
+ continue;
+
+ digest_buf[0] = '\0';
+ digest_buf[17] = '\0';
+
+ digest = digest_buf+1;
+
+ pj_hmac_md5((pj_uint8_t*)rfc2202_test_vector[i].input,
+ rfc2202_test_vector[i].input_len,
+ (pj_uint8_t*)rfc2202_test_vector[i].key,
+ rfc2202_test_vector[i].key_len,
+ digest);
+
+ /* Check for overwrites */
+ if (digest_buf[0] != '\0' || digest_buf[17] != '\0') {
+ PJ_LOG(3, (THIS_FILE, " error: overwriting outside buffer on test %d", i));
+ return -60;
+ }
+
+ /* Compare digest */
+ if (pj_memcmp(rfc2202_test_vector[i].md5_digest, digest, 16)) {
+ PJ_LOG(3, (THIS_FILE, " error: digest mismatch on test %d", i));
+ return -65;
+ }
+ }
+
+ /* Test HMAC-SHA1 */
+ PJ_LOG(3, (THIS_FILE, " HMAC-SHA1 test vectors from rfc 2202.."));
+ for (i=0; i<PJ_ARRAY_SIZE(rfc2202_test_vector); ++i) {
+ pj_uint8_t digest_buf[22], *digest;
+
+ if (rfc2202_test_vector[i].sha1_digest == NULL)
+ continue;
+
+ digest_buf[0] = '\0';
+ digest_buf[21] = '\0';
+
+ digest = digest_buf+1;
+
+ pj_hmac_sha1((pj_uint8_t*)rfc2202_test_vector[i].input,
+ rfc2202_test_vector[i].input_len,
+ (pj_uint8_t*)rfc2202_test_vector[i].key,
+ rfc2202_test_vector[i].key_len,
+ digest);
+
+ /* Check for overwrites */
+ if (digest_buf[0] != '\0' || digest_buf[21] != '\0') {
+ PJ_LOG(3, (THIS_FILE, " error: overwriting outside buffer on test %d", i));
+ return -70;
+ }
+
+ /* Compare digest */
+ if (pj_memcmp(rfc2202_test_vector[i].sha1_digest, digest, 20)) {
+ PJ_LOG(3, (THIS_FILE, " error: digest mismatch on test %d", i));
+ return -75;
+ }
+ }
+
+
+ /* Success */
+ return 0;
+}
+
+/* CRC32 test data, generated from crc32 test on a Linux box */
+struct crc32_test_t
+{
+ 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.."));
+
+ /* testing pj_crc32_calc */
+ for (i=0; i<PJ_ARRAY_SIZE(crc32_test_data); ++i) {
+ pj_uint32_t crc;
+
+ crc = pj_crc32_calc((pj_uint8_t*)crc32_test_data[i].input,
+ pj_ansi_strlen(crc32_test_data[i].input));
+ if (crc != crc32_test_data[i].crc) {
+ PJ_LOG(3,(THIS_FILE, " error: crc mismatch on test %d", i));
+ return -80;
+ }
+ }
+
+ /* testing incremental CRC32 calculation */
+ for (i=0; i<PJ_ARRAY_SIZE(crc32_test_data); ++i) {
+ pj_crc32_context ctx;
+ pj_uint32_t crc0, crc1;
+ unsigned len;
+
+ len = pj_ansi_strlen(crc32_test_data[i].input);
+ crc0 = pj_crc32_calc((pj_uint8_t*)crc32_test_data[i].input, len);
+
+ pj_crc32_init(&ctx);
+ pj_crc32_update(&ctx, (pj_uint8_t*)crc32_test_data[i].input,
+ len / 2);
+
+ if (len/2 > 0) {
+ pj_crc32_update(&ctx, (pj_uint8_t*)crc32_test_data[i].input + len/2,
+ len - len/2);
+ }
+
+ crc1 = pj_crc32_final(&ctx);
+
+ if (crc0 != crc1) {
+ PJ_LOG(3,(THIS_FILE,
+ " error: crc algorithm error on test %d", i));
+ return -85;
+ }
+
+ }
+ return 0;
+}
+
+enum
+{
+ ENCODE = 1,
+ DECODE = 2,
+ ENCODE_DECODE = 3
+};
+
+/*
+ * Base64 test vectors (RFC 4648)
+ */
+static struct base64_test_vec
+{
+ const char *base256;
+ const char *base64;
+ unsigned flag;
+} base64_test_vec[] =
+{
+ {
+ "",
+ "",
+ ENCODE_DECODE
+ },
+ {
+ "f",
+ "Zg==",
+ ENCODE_DECODE
+ },
+ {
+ "fo",
+ "Zm8=",
+ ENCODE_DECODE
+ },
+ {
+ "foo",
+ "Zm9v",
+ ENCODE_DECODE
+ },
+ {
+ "foob",
+ "Zm9vYg==",
+ ENCODE_DECODE
+ },
+ {
+ "fooba",
+ "Zm9vYmE=",
+ ENCODE_DECODE
+ },
+ {
+ "foobar",
+ "Zm9vYmFy",
+ ENCODE_DECODE
+ },
+ {
+ "\x14\xfb\x9c\x03\xd9\x7e",
+ "FPucA9l+",
+ ENCODE_DECODE
+ },
+ {
+ "\x14\xfb\x9c\x03\xd9",
+ "FPucA9k=",
+ ENCODE_DECODE
+ },
+ {
+ "\x14\xfb\x9c\x03",
+ "FPucAw==",
+ ENCODE_DECODE
+ },
+ /* with whitespaces */
+ {
+ "foobar",
+ "Zm9v\r\nYmFy",
+ DECODE
+ },
+ {
+ "foobar",
+ "\nZ\r\nm 9\tv\nYm\nF\ny\n",
+ DECODE
+ },
+};
+
+
+static int base64_test(void)
+{
+ unsigned i;
+ char output[80];
+ pj_status_t rc;
+
+ PJ_LOG(3, (THIS_FILE, " base64 test.."));
+
+ for (i=0; i<PJ_ARRAY_SIZE(base64_test_vec); ++i) {
+ pj_str_t input;
+ int out_len;
+
+ /* Encode test */
+ if (base64_test_vec[i].flag & ENCODE) {
+ out_len = sizeof(output);
+ rc = pj_base64_encode((pj_uint8_t*)base64_test_vec[i].base256,
+ strlen(base64_test_vec[i].base256),
+ output, &out_len);
+ if (rc != PJ_SUCCESS)
+ return -90;
+
+ if (out_len != (int)strlen(base64_test_vec[i].base64))
+ return -91;
+
+ output[out_len] = '\0';
+ if (strcmp(output, base64_test_vec[i].base64) != 0)
+ return -92;
+ }
+
+ /* Decode test */
+ if (base64_test_vec[i].flag & DECODE) {
+ out_len = sizeof(output);
+ input.ptr = (char*)base64_test_vec[i].base64;
+ input.slen = strlen(base64_test_vec[i].base64);
+ rc = pj_base64_decode(&input, (pj_uint8_t*)output, &out_len);
+ if (rc != PJ_SUCCESS)
+ return -95;
+
+ if (out_len != (int)strlen(base64_test_vec[i].base256))
+ return -96;
+
+ output[out_len] = '\0';
+
+ if (strcmp(output, base64_test_vec[i].base256) != 0)
+ return -97;
+ }
+ }
+
+ return 0;
+}
+
+
+int encryption_test()
+{
+ int rc;
+
+ rc = base64_test();
+ if (rc != 0)
+ return rc;
+
+ rc = sha1_test1();
+ if (rc != 0)
+ return rc;
+
+ rc = sha1_test2();
+ if (rc != 0)
+ return rc;
+
+ rc = rfc2202_test();
+ if (rc != 0)
+ return rc;
+
+ rc = crc32_test();
+ if (rc != 0)
+ return rc;
+
+ return 0;
+}
+
+static void crc32_update(pj_crc32_context *c, const pj_uint8_t *data,
+ pj_size_t nbytes)
+{
+ pj_crc32_update(c, data, nbytes);
+}
+
+static void crc32_final(pj_crc32_context *ctx, pj_uint32_t *digest)
+{
+ *digest = pj_crc32_final(ctx);
+}
+
+int encryption_benchmark()
+{
+ pj_pool_t *pool;
+ pj_uint8_t *input;
+ union {
+ pj_md5_context md5_context;
+ pj_sha1_context sha1_context;
+ } context;
+ pj_uint8_t digest[32];
+ pj_size_t input_len;
+ struct algorithm
+ {
+ const char *name;
+ void (*init_context)(void*);
+ void (*update)(void*, const pj_uint8_t*, unsigned);
+ void (*final)(void*, void*);
+ pj_uint32_t t;
+ } algorithms[] =
+ {
+ {
+ "MD5 ",
+ (void (*)(void*))&pj_md5_init,
+ (void (*)(void*, const pj_uint8_t*, unsigned))&pj_md5_update,
+ (void (*)(void*, void*))&pj_md5_final
+ },
+ {
+ "SHA1 ",
+ (void (*)(void*))&pj_sha1_init,
+ (void (*)(void*, const pj_uint8_t*, unsigned))&pj_sha1_update,
+ (void (*)(void*, void*))&pj_sha1_final
+ },
+ {
+ "CRC32",
+ (void (*)(void*))&pj_crc32_init,
+ (void (*)(void*, const pj_uint8_t*, unsigned))&crc32_update,
+ (void (*)(void*, void*))&crc32_final
+ }
+ };
+#if defined(PJ_DEBUG) && PJ_DEBUG!=0
+ enum { LOOP = 1000 };
+#else
+ enum { LOOP = 10000 };
+#endif
+ unsigned i;
+ double total_len;
+
+ input_len = 2048;
+ total_len = input_len * LOOP;
+ pool = pj_pool_create(mem, "enc", input_len+256, 0, NULL);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ input = (pj_uint8_t*)pj_pool_alloc(pool, input_len);
+ pj_memset(input, '\xaa', input_len);
+
+ PJ_LOG(3, (THIS_FILE, " feeding %d Mbytes of data",
+ (unsigned)(total_len/1024/1024)));
+
+ /* Dry run */
+ for (i=0; i<PJ_ARRAY_SIZE(algorithms); ++i) {
+ algorithms[i].init_context(&context);
+ algorithms[i].update(&context, input, input_len);
+ algorithms[i].final(&context, digest);
+ }
+
+ /* Run */
+ for (i=0; i<PJ_ARRAY_SIZE(algorithms); ++i) {
+ int j;
+ pj_timestamp t1, t2;
+
+ pj_get_timestamp(&t1);
+ algorithms[i].init_context(&context);
+ for (j=0; j<LOOP; ++j) {
+ algorithms[i].update(&context, input, input_len);
+ }
+ algorithms[i].final(&context, digest);
+ pj_get_timestamp(&t2);
+
+ algorithms[i].t = pj_elapsed_usec(&t1, &t2);
+ }
+
+ /* Results */
+ for (i=0; i<PJ_ARRAY_SIZE(algorithms); ++i) {
+ double bytes;
+
+ bytes = (total_len * 1000000 / algorithms[i].t);
+ PJ_LOG(3, (THIS_FILE, " %s:%8d usec (%3d.%03d Mbytes/sec)",
+ algorithms[i].name, algorithms[i].t,
+ (unsigned)(bytes / 1024 / 1024),
+ ((unsigned)(bytes) % (1024 * 1024)) / 1024));
+ }
+
+ return 0;
+}
+
+
+
+#endif /* INCLUDE_ENCRYPTION_TEST */
+
diff --git a/pjlib-util/src/pjlib-util-test/http_client.c b/pjlib-util/src/pjlib-util-test/http_client.c
new file mode 100644
index 0000000..0e2e3f6
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/http_client.c
@@ -0,0 +1,955 @@
+/* $Id: http_client.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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"
+
+#if INCLUDE_HTTP_CLIENT_TEST
+
+#define THIS_FILE "test_http"
+//#define VERBOSE
+#define STR_PREC(s) (int)s.slen, s.ptr
+#define USE_LOCAL_SERVER
+
+#include <pjlib.h>
+#include <pjlib-util.h>
+
+#define ACTION_REPLY 0
+#define ACTION_IGNORE -1
+
+static struct server_t
+{
+ pj_sock_t sock;
+ pj_uint16_t port;
+ pj_thread_t *thread;
+
+ /* Action:
+ * 0: reply with the response in resp.
+ * -1: ignore query (to simulate timeout).
+ * other: reply with that error
+ */
+ int action;
+ pj_bool_t send_content_length;
+ unsigned data_size;
+ unsigned buf_size;
+} g_server;
+
+static pj_bool_t thread_quit;
+static pj_timer_heap_t *timer_heap;
+static pj_ioqueue_t *ioqueue;
+static pj_pool_t *pool;
+static pj_http_req *http_req;
+static pj_bool_t test_cancel = PJ_FALSE;
+static pj_size_t total_size;
+static pj_size_t send_size = 0;
+static pj_status_t sstatus;
+static pj_sockaddr_in addr;
+static int counter = 0;
+
+static int server_thread(void *p)
+{
+ struct server_t *srv = (struct server_t*)p;
+ char *pkt = (char*)pj_pool_alloc(pool, srv->buf_size);
+ pj_sock_t newsock = PJ_INVALID_SOCKET;
+
+ while (!thread_quit) {
+ pj_ssize_t pkt_len;
+ int rc;
+ pj_fd_set_t rset;
+ pj_time_val timeout = {0, 500};
+
+ while (!thread_quit) {
+ PJ_FD_ZERO(&rset);
+ PJ_FD_SET(srv->sock, &rset);
+ rc = pj_sock_select(srv->sock+1, &rset, NULL, NULL, &timeout);
+ if (rc != 1) {
+ continue;
+ }
+
+ rc = pj_sock_accept(srv->sock, &newsock, NULL, NULL);
+ if (rc == PJ_SUCCESS) {
+ break;
+ }
+ }
+
+ if (thread_quit)
+ break;
+
+ while (!thread_quit) {
+ PJ_FD_ZERO(&rset);
+ PJ_FD_SET(newsock, &rset);
+ rc = pj_sock_select(newsock+1, &rset, NULL, NULL, &timeout);
+ if (rc != 1) {
+ PJ_LOG(3,("http test", "client timeout"));
+ continue;
+ }
+
+ pkt_len = srv->buf_size;
+ rc = pj_sock_recv(newsock, pkt, &pkt_len, 0);
+ if (rc == PJ_SUCCESS) {
+ break;
+ }
+ }
+
+ if (thread_quit)
+ break;
+
+ /* Simulate network RTT */
+ pj_thread_sleep(50);
+
+ if (srv->action == ACTION_IGNORE) {
+ continue;
+ } else if (srv->action == ACTION_REPLY) {
+ unsigned send_size = 0, ctr = 0;
+ pj_ansi_sprintf(pkt, "HTTP/1.0 200 OK\r\n");
+ if (srv->send_content_length) {
+ pj_ansi_sprintf(pkt + pj_ansi_strlen(pkt),
+ "Content-Length: %d\r\n",
+ srv->data_size);
+ }
+ pj_ansi_sprintf(pkt + pj_ansi_strlen(pkt), "\r\n");
+ pkt_len = pj_ansi_strlen(pkt);
+ rc = pj_sock_send(newsock, pkt, &pkt_len, 0);
+ if (rc != PJ_SUCCESS) {
+ pj_sock_close(newsock);
+ continue;
+ }
+ while (send_size < srv->data_size) {
+ pkt_len = srv->data_size - send_size;
+ if (pkt_len > (signed)srv->buf_size)
+ pkt_len = srv->buf_size;
+ send_size += pkt_len;
+ pj_create_random_string(pkt, pkt_len);
+ pj_ansi_sprintf(pkt, "\nPacket: %d", ++ctr);
+ pkt[pj_ansi_strlen(pkt)] = '\n';
+ rc = pj_sock_send(newsock, pkt, &pkt_len, 0);
+ if (rc != PJ_SUCCESS)
+ break;
+ }
+ pj_sock_close(newsock);
+ }
+ }
+
+ return 0;
+}
+
+static void on_data_read(pj_http_req *hreq, void *data, pj_size_t size)
+{
+ PJ_UNUSED_ARG(hreq);
+ PJ_UNUSED_ARG(data);
+
+ PJ_LOG(5, (THIS_FILE, "\nData received: %d bytes", size));
+ if (size > 0) {
+#ifdef VERBOSE
+ printf("%.*s\n", (int)size, (char *)data);
+#endif
+ }
+}
+
+static void on_send_data(pj_http_req *hreq,
+ void **data, pj_size_t *size)
+{
+ char *sdata;
+ pj_size_t sendsz = 8397;
+
+ PJ_UNUSED_ARG(hreq);
+
+ if (send_size + sendsz > total_size) {
+ sendsz = total_size - send_size;
+ }
+ send_size += sendsz;
+
+ sdata = (char*)pj_pool_alloc(pool, sendsz);
+ pj_create_random_string(sdata, sendsz);
+ pj_ansi_sprintf(sdata, "\nSegment #%d\n", ++counter);
+ *data = sdata;
+ *size = sendsz;
+
+ PJ_LOG(5, (THIS_FILE, "\nSending data progress: %d out of %d bytes",
+ send_size, total_size));
+}
+
+
+static void on_complete(pj_http_req *hreq, pj_status_t status,
+ const pj_http_resp *resp)
+{
+ PJ_UNUSED_ARG(hreq);
+
+ if (status == PJ_ECANCELLED) {
+ PJ_LOG(5, (THIS_FILE, "Request cancelled"));
+ return;
+ } else if (status == PJ_ETIMEDOUT) {
+ PJ_LOG(5, (THIS_FILE, "Request timed out!"));
+ return;
+ } else if (status != PJ_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Error %d", status));
+ return;
+ }
+ PJ_LOG(5, (THIS_FILE, "\nData completed: %d bytes", resp->size));
+ if (resp->size > 0 && resp->data) {
+#ifdef VERBOSE
+ printf("%.*s\n", (int)resp->size, (char *)resp->data);
+#endif
+ }
+}
+
+static void on_response(pj_http_req *hreq, const pj_http_resp *resp)
+{
+ pj_size_t i;
+
+ PJ_UNUSED_ARG(hreq);
+ PJ_UNUSED_ARG(resp);
+ PJ_UNUSED_ARG(i);
+
+#ifdef VERBOSE
+ printf("%.*s, %d, %.*s\n", STR_PREC(resp->version),
+ resp->status_code, STR_PREC(resp->reason));
+ for (i = 0; i < resp->headers.count; i++) {
+ printf("%.*s : %.*s\n",
+ STR_PREC(resp->headers.header[i].name),
+ STR_PREC(resp->headers.header[i].value));
+ }
+#endif
+
+ if (test_cancel) {
+ /* Need to delay closing the client socket here, otherwise the
+ * server will get SIGPIPE when sending response.
+ */
+ pj_thread_sleep(100);
+ pj_http_req_cancel(hreq, PJ_TRUE);
+ test_cancel = PJ_FALSE;
+ }
+}
+
+
+pj_status_t parse_url(const char *url, pj_http_url *hurl)
+{
+ pj_str_t surl;
+ pj_status_t status;
+
+ pj_cstr(&surl, url);
+ status = pj_http_req_parse_url(&surl, hurl);
+#ifdef VERBOSE
+ if (!status) {
+ printf("URL: %s\nProtocol: %.*s\nHost: %.*s\nPort: %d\nPath: %.*s\n\n",
+ url, STR_PREC(hurl->protocol), STR_PREC(hurl->host),
+ hurl->port, STR_PREC(hurl->path));
+ } else {
+ }
+#endif
+ return status;
+}
+
+static int parse_url_test()
+{
+ struct test_data
+ {
+ char *url;
+ pj_status_t result;
+ const char *username;
+ const char *passwd;
+ const char *host;
+ int port;
+ const char *path;
+ } test_data[] =
+ {
+ /* Simple URL without '/' in the end */
+ {"http://www.pjsip.org", PJ_SUCCESS, "", "", "www.pjsip.org", 80, "/"},
+
+ /* Simple URL with port number but without '/' in the end */
+ {"http://pjsip.org:8080", PJ_SUCCESS, "", "", "pjsip.org", 8080, "/"},
+
+ /* URL with path */
+ {"http://127.0.0.1:280/Joomla/index.php?option=com_content&task=view&id=5&Itemid=6",
+ PJ_SUCCESS, "", "", "127.0.0.1", 280,
+ "/Joomla/index.php?option=com_content&task=view&id=5&Itemid=6"},
+
+ /* URL with port and path */
+ {"http://pjsip.org:81/about-us/", PJ_SUCCESS, "", "", "pjsip.org", 81, "/about-us/"},
+
+ /* unsupported protocol */
+ {"ftp://www.pjsip.org", PJ_ENOTSUP, "", "", "", 80, ""},
+
+ /* invalid format */
+ {"http:/pjsip.org/about-us/", PJLIB_UTIL_EHTTPINURL, "", "", "", 80, ""},
+
+ /* invalid port number */
+ {"http://pjsip.org:xyz/", PJLIB_UTIL_EHTTPINPORT, "", "", "", 80, ""},
+
+ /* with username and password */
+ {"http://user:pass@pjsip.org", PJ_SUCCESS, "user", "pass", "pjsip.org", 80, "/"},
+
+ /* password only*/
+ {"http://:pass@pjsip.org", PJ_SUCCESS, "", "pass", "pjsip.org", 80, "/"},
+
+ /* user only*/
+ {"http://user:@pjsip.org", PJ_SUCCESS, "user", "", "pjsip.org", 80, "/"},
+
+ /* empty username and passwd*/
+ {"http://:@pjsip.org", PJ_SUCCESS, "", "", "pjsip.org", 80, "/"},
+
+ /* '@' character in username and path */
+ {"http://user@pjsip.org/@", PJ_SUCCESS, "user", "", "pjsip.org", 80, "/@"},
+
+ /* '@' character in path */
+ {"http://pjsip.org/@", PJ_SUCCESS, "", "", "pjsip.org", 80, "/@"},
+
+ /* '@' character in path */
+ {"http://pjsip.org/one@", PJ_SUCCESS, "", "", "pjsip.org", 80, "/one@"},
+
+ /* Invalid URL */
+ {"http://:", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http://@", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http:/", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http://", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http:///", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http://@/", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http:///@", PJ_EINVAL, "", "", "", 0, ""},
+
+ /* Invalid URL */
+ {"http://:::", PJ_EINVAL, "", "", "", 0, ""},
+ };
+ unsigned i;
+
+ for (i=0; i<PJ_ARRAY_SIZE(test_data); ++i) {
+ struct test_data *ptd;
+ pj_http_url hurl;
+ pj_status_t status;
+
+ ptd = &test_data[i];
+
+ PJ_LOG(3, (THIS_FILE, ".. %s", ptd->url));
+ status = parse_url(ptd->url, &hurl);
+
+ if (status != ptd->result) {
+ PJ_LOG(3,(THIS_FILE, "%d", status));
+ return -11;
+ }
+ if (status != PJ_SUCCESS)
+ continue;
+ if (pj_strcmp2(&hurl.username, ptd->username))
+ return -12;
+ if (pj_strcmp2(&hurl.passwd, ptd->passwd))
+ return -13;
+ if (pj_strcmp2(&hurl.host, ptd->host))
+ return -14;
+ if (hurl.port != ptd->port)
+ return -15;
+ if (pj_strcmp2(&hurl.path, ptd->path))
+ return -16;
+ }
+
+ return 0;
+}
+
+/*
+ * GET request scenario 1: using on_response() and on_data_read()
+ * Server replies with content-length. Application cancels the
+ * request upon receiving the response, then start it again.
+ */
+int http_client_test1()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_data_read = &on_data_read;
+ hcb.on_response = &on_response;
+ pj_http_req_param_default(&param);
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -31;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -32;
+
+#ifdef USE_LOCAL_SERVER
+
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_REPLY;
+ g_server.send_content_length = PJ_TRUE;
+ g_server.data_size = 2970;
+ g_server.buf_size = 1024;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d/about-us/",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://www.teluu.com/about-us/");
+#endif
+
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ &param, &hcb, &http_req))
+ return -33;
+
+ test_cancel = PJ_TRUE;
+ if (pj_http_req_start(http_req))
+ return -35;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+ if (pj_http_req_start(http_req))
+ return -37;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * GET request scenario 2: using on_complete() to get the
+ * complete data. Server does not reply with content-length.
+ * Request timed out, application sets a longer timeout, then
+ * then restart the request.
+ */
+int http_client_test2()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ pj_time_val timeout;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_response = &on_response;
+ pj_http_req_param_default(&param);
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -41;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -42;
+
+#ifdef USE_LOCAL_SERVER
+
+ pj_cstr(&url, "http://127.0.0.1:380");
+ param.timeout.sec = 0;
+ param.timeout.msec = 2000;
+
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_IGNORE;
+ g_server.send_content_length = PJ_FALSE;
+ g_server.data_size = 4173;
+ g_server.buf_size = 1024;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://www.google.com.sg");
+ param.timeout.sec = 0;
+ param.timeout.msec = 50;
+#endif
+
+ pj_http_headers_add_elmt2(&param.headers, (char*)"Accept",
+ (char*)"image/gif, image/x-xbitmap, image/jpeg, "
+ "image/pjpeg, application/x-ms-application,"
+ " application/vnd.ms-xpsdocument, "
+ "application/xaml+xml, "
+ "application/x-ms-xbap, "
+ "application/x-shockwave-flash, "
+ "application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, "
+ "application/msword, */*");
+ pj_http_headers_add_elmt2(&param.headers, (char*)"Accept-Language",
+ (char*)"en-sg");
+ pj_http_headers_add_elmt2(&param.headers, (char*)"User-Agent",
+ (char*)"Mozilla/4.0 (compatible; MSIE 7.0; "
+ "Windows NT 6.0; SLCC1; "
+ ".NET CLR 2.0.50727; "
+ ".NET CLR 3.0.04506)");
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ &param, &hcb, &http_req))
+ return -43;
+
+ if (pj_http_req_start(http_req))
+ return -45;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ g_server.action = ACTION_REPLY;
+#endif
+
+ timeout.sec = 0; timeout.msec = 10000;
+ pj_http_req_set_timeout(http_req, &timeout);
+ if (pj_http_req_start(http_req))
+ return -47;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * PUT request scenario 1: sending the whole data at once
+ */
+int http_client_test_put1()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ char *data;
+ int length = 3875;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_data_read = &on_data_read;
+ hcb.on_response = &on_response;
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -51;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -52;
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_REPLY;
+ g_server.send_content_length = PJ_TRUE;
+ g_server.data_size = 0;
+ g_server.buf_size = 4096;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d/test/test.txt",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://127.0.0.1:280/test/test.txt");
+
+#endif
+
+ pj_http_req_param_default(&param);
+ pj_strset2(&param.method, (char*)"PUT");
+ data = (char*)pj_pool_alloc(pool, length);
+ pj_create_random_string(data, length);
+ pj_ansi_sprintf(data, "PUT test\n");
+ param.reqdata.data = data;
+ param.reqdata.size = length;
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ &param, &hcb, &http_req))
+ return -53;
+
+ if (pj_http_req_start(http_req))
+ return -55;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * PUT request scenario 2: using on_send_data() callback to
+ * sending the data in chunks
+ */
+int http_client_test_put2()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_send_data = &on_send_data;
+ hcb.on_data_read = &on_data_read;
+ hcb.on_response = &on_response;
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -51;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -52;
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_REPLY;
+ g_server.send_content_length = PJ_TRUE;
+ g_server.data_size = 0;
+ g_server.buf_size = 16384;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d/test/test2.txt",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://127.0.0.1:280/test/test2.txt");
+
+#endif
+
+ pj_http_req_param_default(&param);
+ pj_strset2(&param.method, (char*)"PUT");
+ total_size = 15383;
+ send_size = 0;
+ param.reqdata.total_size = total_size;
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ &param, &hcb, &http_req))
+ return -53;
+
+ if (pj_http_req_start(http_req))
+ return -55;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+int http_client_test_delete()
+{
+ pj_str_t url;
+ pj_http_req_callback hcb;
+ pj_http_req_param param;
+ char urlbuf[80];
+
+ pj_bzero(&hcb, sizeof(hcb));
+ hcb.on_complete = &on_complete;
+ hcb.on_response = &on_response;
+
+ /* Create pool, timer, and ioqueue */
+ pool = pj_pool_create(mem, NULL, 8192, 4096, NULL);
+ if (pj_timer_heap_create(pool, 16, &timer_heap))
+ return -61;
+ if (pj_ioqueue_create(pool, 16, &ioqueue))
+ return -62;
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_FALSE;
+ g_server.action = ACTION_REPLY;
+ g_server.send_content_length = PJ_TRUE;
+ g_server.data_size = 0;
+ g_server.buf_size = 1024;
+
+ sstatus = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0,
+ &g_server.sock);
+ if (sstatus != PJ_SUCCESS)
+ return -41;
+
+ pj_sockaddr_in_init(&addr, NULL, 0);
+
+ sstatus = pj_sock_bind(g_server.sock, &addr, sizeof(addr));
+ if (sstatus != PJ_SUCCESS)
+ return -43;
+
+ {
+ pj_sockaddr_in addr;
+ int addr_len = sizeof(addr);
+ sstatus = pj_sock_getsockname(g_server.sock, &addr, &addr_len);
+ if (sstatus != PJ_SUCCESS)
+ return -44;
+ g_server.port = pj_sockaddr_in_get_port(&addr);
+ pj_ansi_snprintf(urlbuf, sizeof(urlbuf),
+ "http://127.0.0.1:%d/test/test2.txt",
+ g_server.port);
+ url = pj_str(urlbuf);
+ }
+
+ sstatus = pj_sock_listen(g_server.sock, 8);
+ if (sstatus != PJ_SUCCESS)
+ return -45;
+
+ sstatus = pj_thread_create(pool, NULL, &server_thread, &g_server,
+ 0, 0, &g_server.thread);
+ if (sstatus != PJ_SUCCESS)
+ return -47;
+
+#else
+ pj_cstr(&url, "http://127.0.0.1:280/test/test2.txt");
+#endif
+
+ pj_http_req_param_default(&param);
+ pj_strset2(&param.method, (char*)"DELETE");
+ if (pj_http_req_create(pool, &url, timer_heap, ioqueue,
+ &param, &hcb, &http_req))
+ return -63;
+
+ if (pj_http_req_start(http_req))
+ return -65;
+
+ while (pj_http_req_is_running(http_req)) {
+ pj_time_val delay = {0, 50};
+ pj_ioqueue_poll(ioqueue, &delay);
+ pj_timer_heap_poll(timer_heap, NULL);
+ }
+
+#ifdef USE_LOCAL_SERVER
+ thread_quit = PJ_TRUE;
+ pj_thread_join(g_server.thread);
+ pj_sock_close(g_server.sock);
+#endif
+
+ pj_http_req_destroy(http_req);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+int http_client_test()
+{
+ int rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing URL parsing"));
+ rc = parse_url_test();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing GET request scenario 1"));
+ rc = http_client_test1();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing GET request scenario 2"));
+ rc = http_client_test2();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing PUT request scenario 1"));
+ rc = http_client_test_put1();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing PUT request scenario 2"));
+ rc = http_client_test_put2();
+ if (rc)
+ return rc;
+
+ PJ_LOG(3, (THIS_FILE, "..Testing DELETE request"));
+ rc = http_client_test_delete();
+ if (rc)
+ return rc;
+
+ return PJ_SUCCESS;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_http_client_test;
+#endif /* INCLUDE_HTTP_CLIENT_TEST */
diff --git a/pjlib-util/src/pjlib-util-test/main.c b/pjlib-util/src/pjlib-util-test/main.c
new file mode 100644
index 0000000..7cc9920
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/main.c
@@ -0,0 +1,63 @@
+/* $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 "test.h"
+#include <pj/string.h>
+
+#if defined(PJ_SUNOS) && PJ_SUNOS!=0
+#include <signal.h>
+static void init_signals()
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_IGN;
+
+ sigaction(SIGALRM, &act, NULL);
+}
+
+#else
+#define init_signals()
+#endif
+
+#define boost()
+
+int main(int argc, char *argv[])
+{
+ int rc;
+
+ PJ_UNUSED_ARG(argc);
+ PJ_UNUSED_ARG(argv);
+
+ boost();
+ init_signals();
+
+ rc = test_main();
+
+ if (argc==2 && pj_ansi_strcmp(argv[1], "-i")==0) {
+ char s[10];
+
+ puts("Press ENTER to quit");
+ if (fgets(s, sizeof(s), stdin) == NULL)
+ return rc;
+ }
+
+ return rc;
+}
+
diff --git a/pjlib-util/src/pjlib-util-test/main_rtems.c b/pjlib-util/src/pjlib-util-test/main_rtems.c
new file mode 100644
index 0000000..4b7b58d
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/main_rtems.c
@@ -0,0 +1,11 @@
+
+/*
+ * !! OIY OIY !!
+ *
+ * The purpose of this file is only to get pjlib-util-test linked. I haven't
+ * actually tried to run pjlib-util-test on RTEMS!!
+ *
+ */
+
+
+#include "../../pjlib/src/pjlib-test/main_rtems.c"
diff --git a/pjlib-util/src/pjlib-util-test/main_win32.c b/pjlib-util/src/pjlib-util-test/main_win32.c
new file mode 100644
index 0000000..3043a39
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/main_win32.c
@@ -0,0 +1 @@
+#include "../../pjlib/src/pjlib-test/main_win32.c"
diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c
new file mode 100644
index 0000000..f08f8b4
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/resolver_test.c
@@ -0,0 +1,1405 @@
+/* $Id: resolver_test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * 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 <pjlib-util.h>
+#include <pjlib.h>
+#include "test.h"
+
+
+#define THIS_FILE "srv_resolver_test.c"
+
+////////////////////////////////////////////////////////////////////////////
+/*
+ * TODO: create various invalid DNS packets.
+ */
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+#define ACTION_REPLY 0
+#define ACTION_IGNORE -1
+#define ACTION_CB -2
+
+static struct server_t
+{
+ pj_sock_t sock;
+ pj_uint16_t port;
+ pj_thread_t *thread;
+
+ /* Action:
+ * 0: reply with the response in resp.
+ * -1: ignore query (to simulate timeout).
+ * other: reply with that error
+ */
+ int action;
+
+ pj_dns_parsed_packet resp;
+ void (*action_cb)(const pj_dns_parsed_packet *pkt,
+ pj_dns_parsed_packet **p_res);
+
+ unsigned pkt_count;
+
+} g_server[2];
+
+static pj_pool_t *pool;
+static pj_dns_resolver *resolver;
+static pj_bool_t thread_quit;
+static pj_timer_heap_t *timer_heap;
+static pj_ioqueue_t *ioqueue;
+static pj_thread_t *poll_thread;
+static pj_sem_t *sem;
+static pj_dns_settings set;
+
+#define MAX_LABEL 32
+
+struct label_tab
+{
+ unsigned count;
+
+ struct {
+ unsigned pos;
+ pj_str_t label;
+ } a[MAX_LABEL];
+};
+
+static void write16(pj_uint8_t *p, pj_uint16_t val)
+{
+ p[0] = (pj_uint8_t)(val >> 8);
+ p[1] = (pj_uint8_t)(val & 0xFF);
+}
+
+static void write32(pj_uint8_t *p, pj_uint32_t val)
+{
+ val = pj_htonl(val);
+ pj_memcpy(p, &val, 4);
+}
+
+static int print_name(pj_uint8_t *pkt, int size,
+ pj_uint8_t *pos, const pj_str_t *name,
+ struct label_tab *tab)
+{
+ pj_uint8_t *p = pos;
+ const char *endlabel, *endname;
+ unsigned i;
+ pj_str_t label;
+
+ /* Check if name is in the table */
+ for (i=0; i<tab->count; ++i) {
+ if (pj_strcmp(&tab->a[i].label, name)==0)
+ break;
+ }
+
+ if (i != tab->count) {
+ write16(p, (pj_uint16_t)(tab->a[i].pos | (0xc0 << 8)));
+ return 2;
+ } else {
+ if (tab->count < MAX_LABEL) {
+ tab->a[tab->count].pos = (p-pkt);
+ tab->a[tab->count].label.ptr = (char*)(p+1);
+ tab->a[tab->count].label.slen = name->slen;
+ ++tab->count;
+ }
+ }
+
+ endlabel = name->ptr;
+ endname = name->ptr + name->slen;
+
+ label.ptr = (char*)name->ptr;
+
+ while (endlabel != endname) {
+
+ while (endlabel != endname && *endlabel != '.')
+ ++endlabel;
+
+ label.slen = (endlabel - label.ptr);
+
+ if (size < label.slen+1)
+ return -1;
+
+ *p = (pj_uint8_t)label.slen;
+ pj_memcpy(p+1, label.ptr, label.slen);
+
+ size -= (label.slen+1);
+ p += (label.slen+1);
+
+ if (endlabel != endname && *endlabel == '.')
+ ++endlabel;
+ label.ptr = (char*)endlabel;
+ }
+
+ if (size == 0)
+ return -1;
+
+ *p++ = '\0';
+
+ return p-pos;
+}
+
+static int print_rr(pj_uint8_t *pkt, int size, pj_uint8_t *pos,
+ const pj_dns_parsed_rr *rr, struct label_tab *tab)
+{
+ pj_uint8_t *p = pos;
+ int len;
+
+ len = print_name(pkt, size, pos, &rr->name, tab);
+ if (len < 0)
+ return -1;
+
+ p += len;
+ size -= len;
+
+ if (size < 8)
+ return -1;
+
+ pj_assert(rr->dnsclass == 1);
+
+ write16(p+0, (pj_uint16_t)rr->type); /* type */
+ write16(p+2, (pj_uint16_t)rr->dnsclass); /* class */
+ write32(p+4, rr->ttl); /* TTL */
+
+ p += 8;
+ size -= 8;
+
+ if (rr->type == PJ_DNS_TYPE_A) {
+
+ if (size < 6)
+ return -1;
+
+ /* RDLEN is 4 */
+ write16(p, 4);
+
+ /* Address */
+ pj_memcpy(p+2, &rr->rdata.a.ip_addr, 4);
+
+ p += 6;
+ size -= 6;
+
+ } else if (rr->type == PJ_DNS_TYPE_CNAME ||
+ rr->type == PJ_DNS_TYPE_NS ||
+ rr->type == PJ_DNS_TYPE_PTR) {
+
+ if (size < 4)
+ return -1;
+
+ len = print_name(pkt, size-2, p+2, &rr->rdata.cname.name, tab);
+ if (len < 0)
+ return -1;
+
+ write16(p, (pj_uint16_t)len);
+
+ p += (len + 2);
+ size -= (len + 2);
+
+ } else if (rr->type == PJ_DNS_TYPE_SRV) {
+
+ if (size < 10)
+ return -1;
+
+ write16(p+2, rr->rdata.srv.prio); /* Priority */
+ write16(p+4, rr->rdata.srv.weight); /* Weight */
+ write16(p+6, rr->rdata.srv.port); /* Port */
+
+ /* Target */
+ len = print_name(pkt, size-8, p+8, &rr->rdata.srv.target, tab);
+ if (len < 0)
+ return -1;
+
+ /* RDLEN */
+ write16(p, (pj_uint16_t)(len + 6));
+
+ p += (len + 8);
+ size -= (len + 8);
+
+ } else {
+ pj_assert(!"Not supported");
+ return -1;
+ }
+
+ return p-pos;
+}
+
+static int print_packet(const pj_dns_parsed_packet *rec, pj_uint8_t *pkt,
+ int size)
+{
+ pj_uint8_t *p = pkt;
+ struct label_tab tab;
+ int i, len;
+
+ tab.count = 0;
+
+#if 0
+ pj_enter_critical_section();
+ PJ_LOG(3,(THIS_FILE, "Sending response:"));
+ pj_dns_dump_packet(rec);
+ pj_leave_critical_section();
+#endif
+
+ pj_assert(sizeof(pj_dns_hdr)==12);
+ if (size < (int)sizeof(pj_dns_hdr))
+ return -1;
+
+ /* Initialize header */
+ write16(p+0, rec->hdr.id);
+ write16(p+2, rec->hdr.flags);
+ write16(p+4, rec->hdr.qdcount);
+ write16(p+6, rec->hdr.anscount);
+ write16(p+8, rec->hdr.nscount);
+ write16(p+10, rec->hdr.arcount);
+
+ p = pkt + sizeof(pj_dns_hdr);
+ size -= sizeof(pj_dns_hdr);
+
+ /* Print queries */
+ for (i=0; i<rec->hdr.qdcount; ++i) {
+
+ len = print_name(pkt, size, p, &rec->q[i].name, &tab);
+ if (len < 0)
+ return -1;
+
+ p += len;
+ size -= len;
+
+ if (size < 4)
+ return -1;
+
+ /* Set type */
+ write16(p+0, (pj_uint16_t)rec->q[i].type);
+
+ /* Set class (IN=1) */
+ pj_assert(rec->q[i].dnsclass == 1);
+ write16(p+2, rec->q[i].dnsclass);
+
+ p += 4;
+ }
+
+ /* Print answers */
+ for (i=0; i<rec->hdr.anscount; ++i) {
+ len = print_rr(pkt, size, p, &rec->ans[i], &tab);
+ if (len < 0)
+ return -1;
+
+ p += len;
+ size -= len;
+ }
+
+ /* Print NS records */
+ for (i=0; i<rec->hdr.nscount; ++i) {
+ len = print_rr(pkt, size, p, &rec->ns[i], &tab);
+ if (len < 0)
+ return -1;
+
+ p += len;
+ size -= len;
+ }
+
+ /* Print additional records */
+ for (i=0; i<rec->hdr.arcount; ++i) {
+ len = print_rr(pkt, size, p, &rec->arr[i], &tab);
+ if (len < 0)
+ return -1;
+
+ p += len;
+ size -= len;
+ }
+
+ return p - pkt;
+}
+
+
+static int server_thread(void *p)
+{
+ struct server_t *srv = (struct server_t*)p;
+
+ while (!thread_quit) {
+ pj_fd_set_t rset;
+ pj_time_val timeout = {0, 500};
+ pj_sockaddr_in src_addr;
+ pj_dns_parsed_packet *req;
+ char pkt[1024];
+ pj_ssize_t pkt_len;
+ int rc, src_len;
+
+ PJ_FD_ZERO(&rset);
+ PJ_FD_SET(srv->sock, &rset);
+
+ rc = pj_sock_select(srv->sock+1, &rset, NULL, NULL, &timeout);
+ if (rc != 1)
+ continue;
+
+ src_len = sizeof(src_addr);
+ pkt_len = sizeof(pkt);
+ rc = pj_sock_recvfrom(srv->sock, pkt, &pkt_len, 0,
+ &src_addr, &src_len);
+ if (rc != 0) {
+ app_perror("Server error receiving packet", rc);
+ continue;
+ }
+
+ PJ_LOG(5,(THIS_FILE, "Server %d processing packet", srv - &g_server[0]));
+ srv->pkt_count++;
+
+ rc = pj_dns_parse_packet(pool, pkt, pkt_len, &req);
+ if (rc != PJ_SUCCESS) {
+ app_perror("server error parsing packet", rc);
+ continue;
+ }
+
+ /* Verify packet */
+ pj_assert(req->hdr.qdcount == 1);
+ pj_assert(req->q[0].dnsclass == 1);
+
+ /* Simulate network RTT */
+ pj_thread_sleep(50);
+
+ if (srv->action == ACTION_IGNORE) {
+ continue;
+ } else if (srv->action == ACTION_REPLY) {
+ srv->resp.hdr.id = req->hdr.id;
+ pkt_len = print_packet(&srv->resp, (pj_uint8_t*)pkt, sizeof(pkt));
+ pj_sock_sendto(srv->sock, pkt, &pkt_len, 0, &src_addr, src_len);
+ } else if (srv->action == ACTION_CB) {
+ pj_dns_parsed_packet *resp;
+ (*srv->action_cb)(req, &resp);
+ resp->hdr.id = req->hdr.id;
+ pkt_len = print_packet(resp, (pj_uint8_t*)pkt, sizeof(pkt));
+ pj_sock_sendto(srv->sock, pkt, &pkt_len, 0, &src_addr, src_len);
+ } else if (srv->action > 0) {
+ req->hdr.flags |= PJ_DNS_SET_RCODE(srv->action);
+ pkt_len = print_packet(req, (pj_uint8_t*)pkt, sizeof(pkt));
+ pj_sock_sendto(srv->sock, pkt, &pkt_len, 0, &src_addr, src_len);
+ }
+ }
+
+ return 0;
+}
+
+static int poll_worker_thread(void *p)
+{
+ PJ_UNUSED_ARG(p);
+
+ while (!thread_quit) {
+ pj_time_val delay = {0, 100};
+ pj_timer_heap_poll(timer_heap, NULL);
+ pj_ioqueue_poll(ioqueue, &delay);
+ }
+
+ return 0;
+}
+
+static void destroy(void);
+
+static int init(void)
+{
+ pj_status_t status;
+ pj_str_t nameservers[2];
+ pj_uint16_t ports[2];
+ int i;
+
+ nameservers[0] = pj_str("127.0.0.1");
+ ports[0] = 5553;
+ nameservers[1] = pj_str("127.0.0.1");
+ ports[1] = 5554;
+
+ g_server[0].port = ports[0];
+ g_server[1].port = ports[1];
+
+ pool = pj_pool_create(mem, NULL, 2000, 2000, NULL);
+
+ status = pj_sem_create(pool, NULL, 0, 2, &sem);
+ pj_assert(status == PJ_SUCCESS);
+
+ for (i=0; i<2; ++i) {
+ pj_sockaddr_in addr;
+
+ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &g_server[i].sock);
+ if (status != PJ_SUCCESS)
+ return -10;
+
+ pj_sockaddr_in_init(&addr, NULL, (pj_uint16_t)g_server[i].port);
+
+ status = pj_sock_bind(g_server[i].sock, &addr, sizeof(addr));
+ if (status != PJ_SUCCESS)
+ return -20;
+
+ status = pj_thread_create(pool, NULL, &server_thread, &g_server[i],
+ 0, 0, &g_server[i].thread);
+ if (status != PJ_SUCCESS)
+ return -30;
+ }
+
+ status = pj_timer_heap_create(pool, 16, &timer_heap);
+ pj_assert(status == PJ_SUCCESS);
+
+ status = pj_ioqueue_create(pool, 16, &ioqueue);
+ pj_assert(status == PJ_SUCCESS);
+
+ status = pj_dns_resolver_create(mem, NULL, 0, timer_heap, ioqueue, &resolver);
+ if (status != PJ_SUCCESS)
+ return -40;
+
+ pj_dns_resolver_get_settings(resolver, &set);
+ set.good_ns_ttl = 20;
+ set.bad_ns_ttl = 20;
+ pj_dns_resolver_set_settings(resolver, &set);
+
+ status = pj_dns_resolver_set_ns(resolver, 2, nameservers, ports);
+ pj_assert(status == PJ_SUCCESS);
+
+ status = pj_thread_create(pool, NULL, &poll_worker_thread, NULL, 0, 0, &poll_thread);
+ pj_assert(status == PJ_SUCCESS);
+
+ return 0;
+}
+
+
+static void destroy(void)
+{
+ int i;
+
+ thread_quit = PJ_TRUE;
+
+ for (i=0; i<2; ++i) {
+ pj_thread_join(g_server[i].thread);
+ pj_sock_close(g_server[i].sock);
+ }
+
+ pj_thread_join(poll_thread);
+
+ pj_dns_resolver_destroy(resolver, PJ_FALSE);
+ pj_ioqueue_destroy(ioqueue);
+ pj_timer_heap_destroy(timer_heap);
+
+ pj_sem_destroy(sem);
+ pj_pool_release(pool);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+/* DNS A parser tests */
+static int a_parser_test(void)
+{
+ pj_dns_parsed_packet pkt;
+ pj_dns_a_record rec;
+ pj_status_t rc;
+
+ PJ_LOG(3,(THIS_FILE, " DNS A record parser tests"));
+
+ pkt.q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query);
+ pkt.ans = (pj_dns_parsed_rr*)
+ pj_pool_calloc(pool, 32, sizeof(pj_dns_parsed_rr));
+
+ /* Simple answer with direct A record, but with addition of
+ * a CNAME and another A to confuse the parser.
+ */
+ PJ_LOG(3,(THIS_FILE, " A RR with duplicate CNAME/A"));
+ pkt.hdr.flags = 0;
+ pkt.hdr.qdcount = 1;
+ pkt.q[0].type = PJ_DNS_TYPE_A;
+ pkt.q[0].dnsclass = 1;
+ pkt.q[0].name = pj_str("ahost");
+ pkt.hdr.anscount = 3;
+
+ /* This is the RR corresponding to the query */
+ pkt.ans[0].name = pj_str("ahost");
+ pkt.ans[0].type = PJ_DNS_TYPE_A;
+ pkt.ans[0].dnsclass = 1;
+ pkt.ans[0].ttl = 1;
+ pkt.ans[0].rdata.a.ip_addr.s_addr = 0x01020304;
+
+ /* CNAME to confuse the parser */
+ pkt.ans[1].name = pj_str("ahost");
+ pkt.ans[1].type = PJ_DNS_TYPE_CNAME;
+ pkt.ans[1].dnsclass = 1;
+ pkt.ans[1].ttl = 1;
+ pkt.ans[1].rdata.cname.name = pj_str("bhost");
+
+ /* DNS A RR to confuse the parser */
+ pkt.ans[2].name = pj_str("bhost");
+ pkt.ans[2].type = PJ_DNS_TYPE_A;
+ pkt.ans[2].dnsclass = 1;
+ pkt.ans[2].ttl = 1;
+ pkt.ans[2].rdata.a.ip_addr.s_addr = 0x0203;
+
+
+ rc = pj_dns_parse_a_response(&pkt, &rec);
+ pj_assert(rc == PJ_SUCCESS);
+ pj_assert(pj_strcmp2(&rec.name, "ahost")==0);
+ pj_assert(rec.alias.slen == 0);
+ pj_assert(rec.addr_count == 1);
+ pj_assert(rec.addr[0].s_addr == 0x01020304);
+
+ /* Answer with the target corresponds to a CNAME entry, but not
+ * as the first record, and with additions of some CNAME and A
+ * entries to confuse the parser.
+ */
+ PJ_LOG(3,(THIS_FILE, " CNAME RR with duplicate CNAME/A"));
+ pkt.hdr.flags = 0;
+ pkt.hdr.qdcount = 1;
+ pkt.q[0].type = PJ_DNS_TYPE_A;
+ pkt.q[0].dnsclass = 1;
+ pkt.q[0].name = pj_str("ahost");
+ pkt.hdr.anscount = 4;
+
+ /* This is the DNS A record for the alias */
+ pkt.ans[0].name = pj_str("ahostalias");
+ pkt.ans[0].type = PJ_DNS_TYPE_A;
+ pkt.ans[0].dnsclass = 1;
+ pkt.ans[0].ttl = 1;
+ pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202;
+
+ /* CNAME entry corresponding to the query */
+ pkt.ans[1].name = pj_str("ahost");
+ pkt.ans[1].type = PJ_DNS_TYPE_CNAME;
+ pkt.ans[1].dnsclass = 1;
+ pkt.ans[1].ttl = 1;
+ pkt.ans[1].rdata.cname.name = pj_str("ahostalias");
+
+ /* Another CNAME to confuse the parser */
+ pkt.ans[2].name = pj_str("ahost");
+ pkt.ans[2].type = PJ_DNS_TYPE_CNAME;
+ pkt.ans[2].dnsclass = 1;
+ pkt.ans[2].ttl = 1;
+ pkt.ans[2].rdata.cname.name = pj_str("ahostalias2");
+
+ /* Another DNS A to confuse the parser */
+ pkt.ans[3].name = pj_str("ahostalias2");
+ pkt.ans[3].type = PJ_DNS_TYPE_A;
+ pkt.ans[3].dnsclass = 1;
+ pkt.ans[3].ttl = 1;
+ pkt.ans[3].rdata.a.ip_addr.s_addr = 0x03030303;
+
+ rc = pj_dns_parse_a_response(&pkt, &rec);
+ pj_assert(rc == PJ_SUCCESS);
+ pj_assert(pj_strcmp2(&rec.name, "ahost")==0);
+ pj_assert(pj_strcmp2(&rec.alias, "ahostalias")==0);
+ pj_assert(rec.addr_count == 1);
+ pj_assert(rec.addr[0].s_addr == 0x02020202);
+
+ /*
+ * No query section.
+ */
+ PJ_LOG(3,(THIS_FILE, " No query section"));
+ pkt.hdr.qdcount = 0;
+ pkt.hdr.anscount = 0;
+
+ rc = pj_dns_parse_a_response(&pkt, &rec);
+ pj_assert(rc == PJLIB_UTIL_EDNSINANSWER);
+
+ /*
+ * No answer section.
+ */
+ PJ_LOG(3,(THIS_FILE, " No answer section"));
+ pkt.hdr.flags = 0;
+ pkt.hdr.qdcount = 1;
+ pkt.q[0].type = PJ_DNS_TYPE_A;
+ pkt.q[0].dnsclass = 1;
+ pkt.q[0].name = pj_str("ahost");
+ pkt.hdr.anscount = 0;
+
+ rc = pj_dns_parse_a_response(&pkt, &rec);
+ pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
+
+ /*
+ * Answer doesn't match query.
+ */
+ PJ_LOG(3,(THIS_FILE, " Answer doesn't match query"));
+ pkt.hdr.flags = 0;
+ pkt.hdr.qdcount = 1;
+ pkt.q[0].type = PJ_DNS_TYPE_A;
+ pkt.q[0].dnsclass = 1;
+ pkt.q[0].name = pj_str("ahost");
+ pkt.hdr.anscount = 1;
+
+ /* An answer that doesn't match the query */
+ pkt.ans[0].name = pj_str("ahostalias");
+ pkt.ans[0].type = PJ_DNS_TYPE_A;
+ pkt.ans[0].dnsclass = 1;
+ pkt.ans[0].ttl = 1;
+ pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202;
+
+ rc = pj_dns_parse_a_response(&pkt, &rec);
+ pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
+
+
+ /*
+ * DNS CNAME that doesn't have corresponding DNS A.
+ */
+ PJ_LOG(3,(THIS_FILE, " CNAME with no matching DNS A RR (1)"));
+ pkt.hdr.flags = 0;
+ pkt.hdr.qdcount = 1;
+ pkt.q[0].type = PJ_DNS_TYPE_A;
+ pkt.q[0].dnsclass = 1;
+ pkt.q[0].name = pj_str("ahost");
+ pkt.hdr.anscount = 1;
+
+ /* The CNAME */
+ pkt.ans[0].name = pj_str("ahost");
+ pkt.ans[0].type = PJ_DNS_TYPE_CNAME;
+ pkt.ans[0].dnsclass = 1;
+ pkt.ans[0].ttl = 1;
+ pkt.ans[0].rdata.cname.name = pj_str("ahostalias");
+
+ rc = pj_dns_parse_a_response(&pkt, &rec);
+ pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
+
+
+ /*
+ * DNS CNAME that doesn't have corresponding DNS A.
+ */
+ PJ_LOG(3,(THIS_FILE, " CNAME with no matching DNS A RR (2)"));
+ pkt.hdr.flags = 0;
+ pkt.hdr.qdcount = 1;
+ pkt.q[0].type = PJ_DNS_TYPE_A;
+ pkt.q[0].dnsclass = 1;
+ pkt.q[0].name = pj_str("ahost");
+ pkt.hdr.anscount = 2;
+
+ /* The CNAME */
+ pkt.ans[0].name = pj_str("ahost");
+ pkt.ans[0].type = PJ_DNS_TYPE_CNAME;
+ pkt.ans[0].dnsclass = 1;
+ pkt.ans[0].ttl = 1;
+ pkt.ans[0].rdata.cname.name = pj_str("ahostalias");
+
+ /* DNS A record, but the name doesn't match */
+ pkt.ans[1].name = pj_str("ahost");
+ pkt.ans[1].type = PJ_DNS_TYPE_A;
+ pkt.ans[1].dnsclass = 1;
+ pkt.ans[1].ttl = 1;
+ pkt.ans[1].rdata.a.ip_addr.s_addr = 0x01020304;
+
+ rc = pj_dns_parse_a_response(&pkt, &rec);
+ pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
+
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+/* Simple DNS test */
+#define IP_ADDR0 0x00010203
+
+static void dns_callback(void *user_data,
+ pj_status_t status,
+ pj_dns_parsed_packet *resp)
+{
+ PJ_UNUSED_ARG(user_data);
+
+ pj_sem_post(sem);
+
+ PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
+ PJ_ASSERT_ON_FAIL(resp, return);
+ PJ_ASSERT_ON_FAIL(resp->hdr.anscount == 1, return);
+ PJ_ASSERT_ON_FAIL(resp->ans[0].type == PJ_DNS_TYPE_A, return);
+ PJ_ASSERT_ON_FAIL(resp->ans[0].rdata.a.ip_addr.s_addr == IP_ADDR0, return);
+
+}
+
+
+static int simple_test(void)
+{
+ pj_str_t name = pj_str("helloworld");
+ pj_dns_parsed_packet *r;
+ pj_status_t status;
+
+ PJ_LOG(3,(THIS_FILE, " simple successful test"));
+
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ g_server[0].action = ACTION_REPLY;
+ r = &g_server[0].resp;
+ r->hdr.qdcount = 1;
+ r->hdr.anscount = 1;
+ r->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query);
+ r->q[0].type = PJ_DNS_TYPE_A;
+ r->q[0].dnsclass = 1;
+ r->q[0].name = name;
+ r->ans = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_rr);
+ r->ans[0].type = PJ_DNS_TYPE_A;
+ r->ans[0].dnsclass = 1;
+ r->ans[0].name = name;
+ r->ans[0].rdata.a.ip_addr.s_addr = IP_ADDR0;
+
+ g_server[1].action = ACTION_REPLY;
+ r = &g_server[1].resp;
+ r->hdr.qdcount = 1;
+ r->hdr.anscount = 1;
+ r->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query);
+ r->q[0].type = PJ_DNS_TYPE_A;
+ r->q[0].dnsclass = 1;
+ r->q[0].name = name;
+ r->ans = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_rr);
+ r->ans[0].type = PJ_DNS_TYPE_A;
+ r->ans[0].dnsclass = 1;
+ r->ans[0].name = name;
+ r->ans[0].rdata.a.ip_addr.s_addr = IP_ADDR0;
+
+ status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0,
+ &dns_callback, NULL, NULL);
+ if (status != PJ_SUCCESS)
+ return -1000;
+
+ pj_sem_wait(sem);
+ pj_thread_sleep(1000);
+
+
+ /* Both servers must get packet */
+ pj_assert(g_server[0].pkt_count == 1);
+ pj_assert(g_server[1].pkt_count == 1);
+
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+/* DNS nameserver fail-over test */
+
+static void dns_callback_1b(void *user_data,
+ pj_status_t status,
+ pj_dns_parsed_packet *resp)
+{
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(resp);
+
+ pj_sem_post(sem);
+
+ PJ_ASSERT_ON_FAIL(status==PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_RCODE_NXDOMAIN),
+ return);
+}
+
+
+
+
+/* DNS test */
+static int dns_test(void)
+{
+ pj_str_t name = pj_str("name00");
+ pj_status_t status;
+
+ PJ_LOG(3,(THIS_FILE, " simple error response test"));
+
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ g_server[0].action = PJ_DNS_RCODE_NXDOMAIN;
+ g_server[1].action = PJ_DNS_RCODE_NXDOMAIN;
+
+ status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0,
+ &dns_callback_1b, NULL, NULL);
+ if (status != PJ_SUCCESS)
+ return -1000;
+
+ pj_sem_wait(sem);
+ pj_thread_sleep(1000);
+
+ /* Now only server 0 should get packet, since both servers are
+ * in STATE_ACTIVE state
+ */
+ pj_assert((g_server[0].pkt_count == 1 && g_server[1].pkt_count == 0) ||
+ (g_server[1].pkt_count == 1 && g_server[0].pkt_count == 0));
+
+ /* Wait to allow probing period to complete */
+ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)",
+ set.good_ns_ttl));
+ pj_thread_sleep(set.good_ns_ttl * 1000);
+
+ /*
+ * Fail-over test
+ */
+ PJ_LOG(3,(THIS_FILE, " failing server0"));
+ g_server[0].action = ACTION_IGNORE;
+ g_server[1].action = PJ_DNS_RCODE_NXDOMAIN;
+
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ name = pj_str("name01");
+ status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0,
+ &dns_callback_1b, NULL, NULL);
+ if (status != PJ_SUCCESS)
+ return -1000;
+
+ pj_sem_wait(sem);
+
+ /*
+ * Check that both servers still receive requests, since they are
+ * in probing state.
+ */
+ PJ_LOG(3,(THIS_FILE, " checking both NS during probing period"));
+ g_server[0].action = ACTION_IGNORE;
+ g_server[1].action = PJ_DNS_RCODE_NXDOMAIN;
+
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ name = pj_str("name02");
+ status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0,
+ &dns_callback_1b, NULL, NULL);
+ if (status != PJ_SUCCESS)
+ return -1000;
+
+ pj_sem_wait(sem);
+ pj_thread_sleep(set.qretr_delay * set.qretr_count);
+
+ /* Both servers must get requests */
+ pj_assert(g_server[0].pkt_count >= 1);
+ pj_assert(g_server[1].pkt_count == 1);
+
+ /* Wait to allow probing period to complete */
+ PJ_LOG(3,(THIS_FILE, " waiting for probing state to end (%d sec)",
+ set.qretr_delay *
+ (set.qretr_count+2) / 1000));
+ pj_thread_sleep(set.qretr_delay * (set.qretr_count + 2));
+
+
+ /*
+ * Now only server 1 should get requests.
+ */
+ PJ_LOG(3,(THIS_FILE, " verifying only good NS is used"));
+ g_server[0].action = PJ_DNS_RCODE_NXDOMAIN;
+ g_server[1].action = PJ_DNS_RCODE_NXDOMAIN;
+
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ name = pj_str("name03");
+ status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0,
+ &dns_callback_1b, NULL, NULL);
+ if (status != PJ_SUCCESS)
+ return -1000;
+
+ pj_sem_wait(sem);
+ pj_thread_sleep(1000);
+
+ /* Both servers must get requests */
+ pj_assert(g_server[0].pkt_count == 0);
+ pj_assert(g_server[1].pkt_count == 1);
+
+ /* Wait to allow probing period to complete */
+ PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)",
+ set.good_ns_ttl));
+ pj_thread_sleep(set.good_ns_ttl * 1000);
+
+ /*
+ * Now fail server 1 to switch to server 0
+ */
+ g_server[0].action = PJ_DNS_RCODE_NXDOMAIN;
+ g_server[1].action = ACTION_IGNORE;
+
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ name = pj_str("name04");
+ status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0,
+ &dns_callback_1b, NULL, NULL);
+ if (status != PJ_SUCCESS)
+ return -1000;
+
+ pj_sem_wait(sem);
+
+ /* Wait to allow probing period to complete */
+ PJ_LOG(3,(THIS_FILE, " waiting for probing state (%d sec)",
+ set.qretr_delay * (set.qretr_count+2) / 1000));
+ pj_thread_sleep(set.qretr_delay * (set.qretr_count + 2));
+
+ /*
+ * Now only server 0 should get requests.
+ */
+ PJ_LOG(3,(THIS_FILE, " verifying good NS"));
+ g_server[0].action = PJ_DNS_RCODE_NXDOMAIN;
+ g_server[1].action = ACTION_IGNORE;
+
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ name = pj_str("name05");
+ status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0,
+ &dns_callback_1b, NULL, NULL);
+ if (status != PJ_SUCCESS)
+ return -1000;
+
+ pj_sem_wait(sem);
+ pj_thread_sleep(1000);
+
+ /* Only good NS should get request */
+ pj_assert(g_server[0].pkt_count == 1);
+ pj_assert(g_server[1].pkt_count == 0);
+
+
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+/* Resolver test, normal, with CNAME */
+#define IP_ADDR1 0x02030405
+#define PORT1 50061
+
+static void action1_1(const pj_dns_parsed_packet *pkt,
+ pj_dns_parsed_packet **p_res)
+{
+ pj_dns_parsed_packet *res;
+ char *target = "sip.somedomain.com";
+
+ res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet);
+
+ if (res->q == NULL) {
+ res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query);
+ }
+ if (res->ans == NULL) {
+ res->ans = (pj_dns_parsed_rr*)
+ pj_pool_calloc(pool, 4, sizeof(pj_dns_parsed_rr));
+ }
+
+ res->hdr.qdcount = 1;
+ res->q[0].type = pkt->q[0].type;
+ res->q[0].dnsclass = pkt->q[0].dnsclass;
+ res->q[0].name = pkt->q[0].name;
+
+ if (pkt->q[0].type == PJ_DNS_TYPE_SRV) {
+
+ pj_assert(pj_strcmp2(&pkt->q[0].name, "_sip._udp.somedomain.com")==0);
+
+ res->hdr.anscount = 1;
+ res->ans[0].type = PJ_DNS_TYPE_SRV;
+ res->ans[0].dnsclass = 1;
+ res->ans[0].name = res->q[0].name;
+ res->ans[0].ttl = 1;
+ res->ans[0].rdata.srv.prio = 1;
+ res->ans[0].rdata.srv.weight = 2;
+ res->ans[0].rdata.srv.port = PORT1;
+ res->ans[0].rdata.srv.target = pj_str(target);
+
+ } else if (pkt->q[0].type == PJ_DNS_TYPE_A) {
+ char *alias = "sipalias.somedomain.com";
+
+ pj_assert(pj_strcmp2(&res->q[0].name, target)==0);
+
+ res->hdr.anscount = 2;
+ res->ans[0].type = PJ_DNS_TYPE_CNAME;
+ res->ans[0].dnsclass = 1;
+ res->ans[0].ttl = 1000; /* resolver should select minimum TTL */
+ res->ans[0].name = res->q[0].name;
+ res->ans[0].rdata.cname.name = pj_str(alias);
+
+ res->ans[1].type = PJ_DNS_TYPE_A;
+ res->ans[1].dnsclass = 1;
+ res->ans[1].ttl = 1;
+ res->ans[1].name = pj_str(alias);
+ res->ans[1].rdata.a.ip_addr.s_addr = IP_ADDR1;
+ }
+
+ *p_res = res;
+}
+
+static void srv_cb_1(void *user_data,
+ pj_status_t status,
+ const pj_dns_srv_record *rec)
+{
+ PJ_UNUSED_ARG(user_data);
+
+ pj_sem_post(sem);
+
+ PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
+ PJ_ASSERT_ON_FAIL(rec->count == 1, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].priority == 1, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].weight == 2, return);
+ PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.name, "sip.somedomain.com")==0,
+ return);
+ PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias.somedomain.com")==0,
+ return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].s_addr == IP_ADDR1, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT1, return);
+
+
+}
+
+static void srv_cb_1b(void *user_data,
+ pj_status_t status,
+ const pj_dns_srv_record *rec)
+{
+ PJ_UNUSED_ARG(user_data);
+
+ pj_sem_post(sem);
+
+ PJ_ASSERT_ON_FAIL(status==PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_RCODE_NXDOMAIN),
+ return);
+ PJ_ASSERT_ON_FAIL(rec->count == 0, return);
+}
+
+static int srv_resolver_test(void)
+{
+ pj_status_t status;
+ pj_str_t domain = pj_str("somedomain.com");
+ pj_str_t res_name = pj_str("_sip._udp.");
+
+ /* Successful scenario */
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): success scenario"));
+
+ g_server[0].action = ACTION_CB;
+ g_server[0].action_cb = &action1_1;
+ g_server[1].action = ACTION_CB;
+ g_server[1].action_cb = &action1_1;
+
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE,
+ NULL, &srv_cb_1, NULL);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_sem_wait(sem);
+
+ /* Because of previous tests, only NS 1 should get the request */
+ pj_assert(g_server[0].pkt_count == 2); /* 2 because of SRV and A resolution */
+ pj_assert(g_server[1].pkt_count == 0);
+
+
+ /* Wait until cache expires and nameserver state moves out from STATE_PROBING */
+ PJ_LOG(3,(THIS_FILE, " waiting for cache to expire (~15 secs).."));
+ pj_thread_sleep(1000 +
+ ((set.qretr_count + 2) * set.qretr_delay));
+
+ /* Successful scenario */
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): parallel queries"));
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE,
+ NULL, &srv_cb_1, NULL);
+ pj_assert(status == PJ_SUCCESS);
+
+
+ status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE,
+ NULL, &srv_cb_1, NULL);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_sem_wait(sem);
+ pj_sem_wait(sem);
+
+ /* Only server one should get a query */
+ pj_assert(g_server[0].pkt_count == 2); /* 2 because of SRV and A resolution */
+ pj_assert(g_server[1].pkt_count == 0);
+
+ /* Since TTL is one, subsequent queries should fail */
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): cache expires scenario"));
+
+
+ pj_thread_sleep(1000);
+
+ g_server[0].action = PJ_DNS_RCODE_NXDOMAIN;
+ g_server[1].action = PJ_DNS_RCODE_NXDOMAIN;
+
+ status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE,
+ NULL, &srv_cb_1b, NULL);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_sem_wait(sem);
+
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+/* Fallback because there's no SRV in answer */
+#define TARGET "domain2.com"
+#define IP_ADDR2 0x02030405
+#define PORT2 50062
+
+static void action2_1(const pj_dns_parsed_packet *pkt,
+ pj_dns_parsed_packet **p_res)
+{
+ pj_dns_parsed_packet *res;
+
+ res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet);
+
+ res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query);
+ res->ans = (pj_dns_parsed_rr*)
+ pj_pool_calloc(pool, 4, sizeof(pj_dns_parsed_rr));
+
+ res->hdr.qdcount = 1;
+ res->q[0].type = pkt->q[0].type;
+ res->q[0].dnsclass = pkt->q[0].dnsclass;
+ res->q[0].name = pkt->q[0].name;
+
+ if (pkt->q[0].type == PJ_DNS_TYPE_SRV) {
+
+ pj_assert(pj_strcmp2(&pkt->q[0].name, "_sip._udp." TARGET)==0);
+
+ res->hdr.anscount = 1;
+ res->ans[0].type = PJ_DNS_TYPE_A; // <-- this will cause the fallback
+ res->ans[0].dnsclass = 1;
+ res->ans[0].name = res->q[0].name;
+ res->ans[0].ttl = 1;
+ res->ans[0].rdata.srv.prio = 1;
+ res->ans[0].rdata.srv.weight = 2;
+ res->ans[0].rdata.srv.port = PORT2;
+ res->ans[0].rdata.srv.target = pj_str("sip01." TARGET);
+
+ } else if (pkt->q[0].type == PJ_DNS_TYPE_A) {
+ char *alias = "sipalias01." TARGET;
+
+ pj_assert(pj_strcmp2(&res->q[0].name, TARGET)==0);
+
+ res->hdr.anscount = 2;
+ res->ans[0].type = PJ_DNS_TYPE_CNAME;
+ res->ans[0].dnsclass = 1;
+ res->ans[0].name = res->q[0].name;
+ res->ans[0].ttl = 1;
+ res->ans[0].rdata.cname.name = pj_str(alias);
+
+ res->ans[1].type = PJ_DNS_TYPE_A;
+ res->ans[1].dnsclass = 1;
+ res->ans[1].name = pj_str(alias);
+ res->ans[1].ttl = 1;
+ res->ans[1].rdata.a.ip_addr.s_addr = IP_ADDR2;
+ }
+
+ *p_res = res;
+}
+
+static void srv_cb_2(void *user_data,
+ pj_status_t status,
+ const pj_dns_srv_record *rec)
+{
+ PJ_UNUSED_ARG(user_data);
+
+ pj_sem_post(sem);
+
+ PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
+ PJ_ASSERT_ON_FAIL(rec->count == 1, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].priority == 0, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].weight == 0, return);
+ PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.name, TARGET)==0,
+ return);
+ PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias01." TARGET)==0,
+ return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].s_addr == IP_ADDR2, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT2, return);
+}
+
+static int srv_resolver_fallback_test(void)
+{
+ pj_status_t status;
+ pj_str_t domain = pj_str(TARGET);
+ pj_str_t res_name = pj_str("_sip._udp.");
+
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): fallback test"));
+
+ g_server[0].action = ACTION_CB;
+ g_server[0].action_cb = &action2_1;
+ g_server[1].action = ACTION_CB;
+ g_server[1].action_cb = &action2_1;
+
+ status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, PJ_TRUE,
+ NULL, &srv_cb_2, NULL);
+ if (status != PJ_SUCCESS) {
+ app_perror(" srv_resolve error", status);
+ pj_assert(status == PJ_SUCCESS);
+ }
+
+ pj_sem_wait(sem);
+
+ /* Subsequent query should just get the response from the cache */
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): cache test"));
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, PJ_TRUE,
+ NULL, &srv_cb_2, NULL);
+ if (status != PJ_SUCCESS) {
+ app_perror(" srv_resolve error", status);
+ pj_assert(status == PJ_SUCCESS);
+ }
+
+ pj_sem_wait(sem);
+
+ pj_assert(g_server[0].pkt_count == 0);
+ pj_assert(g_server[1].pkt_count == 0);
+
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+/* Too many SRV or A entries */
+#define DOMAIN3 "d3"
+#define SRV_COUNT3 (PJ_DNS_SRV_MAX_ADDR+1)
+#define A_COUNT3 (PJ_DNS_MAX_IP_IN_A_REC+1)
+#define PORT3 50063
+#define IP_ADDR3 0x03030303
+
+static void action3_1(const pj_dns_parsed_packet *pkt,
+ pj_dns_parsed_packet **p_res)
+{
+ pj_dns_parsed_packet *res;
+ unsigned i;
+
+ res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet);
+
+ if (res->q == NULL) {
+ res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query);
+ }
+
+ res->hdr.qdcount = 1;
+ res->q[0].type = pkt->q[0].type;
+ res->q[0].dnsclass = pkt->q[0].dnsclass;
+ res->q[0].name = pkt->q[0].name;
+
+ if (pkt->q[0].type == PJ_DNS_TYPE_SRV) {
+
+ pj_assert(pj_strcmp2(&pkt->q[0].name, "_sip._udp." DOMAIN3)==0);
+
+ res->hdr.anscount = SRV_COUNT3;
+ res->ans = (pj_dns_parsed_rr*)
+ pj_pool_calloc(pool, SRV_COUNT3, sizeof(pj_dns_parsed_rr));
+
+ for (i=0; i<SRV_COUNT3; ++i) {
+ char *target;
+
+ res->ans[i].type = PJ_DNS_TYPE_SRV;
+ res->ans[i].dnsclass = 1;
+ res->ans[i].name = res->q[0].name;
+ res->ans[i].ttl = 1;
+ res->ans[i].rdata.srv.prio = (pj_uint16_t)i;
+ res->ans[i].rdata.srv.weight = 2;
+ res->ans[i].rdata.srv.port = (pj_uint16_t)(PORT3+i);
+
+ target = (char*)pj_pool_alloc(pool, 16);
+ sprintf(target, "sip%02d." DOMAIN3, i);
+ res->ans[i].rdata.srv.target = pj_str(target);
+ }
+
+ } else if (pkt->q[0].type == PJ_DNS_TYPE_A) {
+
+ //pj_assert(pj_strcmp2(&res->q[0].name, "sip." DOMAIN3)==0);
+
+ res->hdr.anscount = A_COUNT3;
+ res->ans = (pj_dns_parsed_rr*)
+ pj_pool_calloc(pool, A_COUNT3, sizeof(pj_dns_parsed_rr));
+
+ for (i=0; i<A_COUNT3; ++i) {
+ res->ans[i].type = PJ_DNS_TYPE_A;
+ res->ans[i].dnsclass = 1;
+ res->ans[i].ttl = 1;
+ res->ans[i].name = res->q[0].name;
+ res->ans[i].rdata.a.ip_addr.s_addr = IP_ADDR3+i;
+ }
+ }
+
+ *p_res = res;
+}
+
+static void srv_cb_3(void *user_data,
+ pj_status_t status,
+ const pj_dns_srv_record *rec)
+{
+ unsigned i;
+
+ PJ_UNUSED_ARG(user_data);
+ PJ_UNUSED_ARG(status);
+ PJ_UNUSED_ARG(rec);
+
+ pj_assert(status == PJ_SUCCESS);
+ pj_assert(rec->count == PJ_DNS_SRV_MAX_ADDR);
+
+ for (i=0; i<PJ_DNS_SRV_MAX_ADDR; ++i) {
+ unsigned j;
+
+ pj_assert(rec->entry[i].priority == i);
+ pj_assert(rec->entry[i].weight == 2);
+ //pj_assert(pj_strcmp2(&rec->entry[i].server.name, "sip." DOMAIN3)==0);
+ pj_assert(rec->entry[i].server.alias.slen == 0);
+ pj_assert(rec->entry[i].port == PORT3+i);
+
+ pj_assert(rec->entry[i].server.addr_count == PJ_DNS_MAX_IP_IN_A_REC);
+
+ for (j=0; j<PJ_DNS_MAX_IP_IN_A_REC; ++j) {
+ pj_assert(rec->entry[i].server.addr[j].s_addr == IP_ADDR3+j);
+ }
+ }
+
+ pj_sem_post(sem);
+}
+
+static int srv_resolver_many_test(void)
+{
+ pj_status_t status;
+ pj_str_t domain = pj_str(DOMAIN3);
+ pj_str_t res_name = pj_str("_sip._udp.");
+
+ /* Successful scenario */
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): too many entries test"));
+
+ g_server[0].action = ACTION_CB;
+ g_server[0].action_cb = &action3_1;
+ g_server[1].action = ACTION_CB;
+ g_server[1].action_cb = &action3_1;
+
+ g_server[0].pkt_count = 0;
+ g_server[1].pkt_count = 0;
+
+ status = pj_dns_srv_resolve(&domain, &res_name, 1, pool, resolver, PJ_TRUE,
+ NULL, &srv_cb_3, NULL);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_sem_wait(sem);
+
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+int resolver_test(void)
+{
+ int rc;
+
+ rc = init();
+ if (rc != 0)
+ goto on_error;
+
+ rc = a_parser_test();
+ if (rc != 0)
+ goto on_error;
+
+ rc = simple_test();
+ if (rc != 0)
+ goto on_error;
+
+ rc = dns_test();
+ if (rc != 0)
+ goto on_error;
+
+ srv_resolver_test();
+ srv_resolver_fallback_test();
+ srv_resolver_many_test();
+
+ destroy();
+ return 0;
+
+on_error:
+ destroy();
+ return rc;
+}
+
+
diff --git a/pjlib-util/src/pjlib-util-test/stun.c b/pjlib-util/src/pjlib-util-test/stun.c
new file mode 100644
index 0000000..dbd2d17
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/stun.c
@@ -0,0 +1,119 @@
+/* $Id: stun.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * 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
+ */
+
+static int decode_test(void)
+{
+ /* Invalid message type */
+
+ /* Short message */
+
+ /* Long, random message */
+
+ /* Message length in header is shorter */
+
+ /* Message length in header is longer */
+
+ /* Invalid magic */
+
+ /* Attribute length is not valid */
+
+ /* Unknown mandatory attribute type should generate error */
+
+ /* Unknown but non-mandatory should be okay */
+
+ /* String/binary attribute length is larger than the message */
+
+ /* Valid message with MESSAGE-INTEGRITY */
+
+ /* Valid message with FINGERPRINT */
+
+ /* Valid message with MESSAGE-INTEGRITY and FINGERPRINT */
+
+ /* Another attribute not FINGERPRINT exists after MESSAGE-INTEGRITY */
+
+ /* Another attribute exists after FINGERPRINT */
+
+ return 0;
+}
+
+static int decode_verify(void)
+{
+ /* Decode all attribute types */
+ return 0;
+}
+
+static int auth_test(void)
+{
+ /* REALM and USERNAME is present, but MESSAGE-INTEGRITY is not present.
+ * For short term, must with reply 401 without REALM.
+ * For long term, must reply with 401 with REALM.
+ */
+
+ /* USERNAME is not present, server must respond with 432 (Missing
+ * Username).
+ */
+
+ /* If long term credential is wanted and REALM is not present, server
+ * must respond with 434 (Missing Realm)
+ */
+
+ /* If REALM doesn't match, server must respond with 434 (Missing Realm)
+ * too, containing REALM and NONCE attribute.
+ */
+
+ /* When long term authentication is wanted and NONCE is NOT present,
+ * server must respond with 435 (Missing Nonce), containing REALM and
+ * NONCE attribute.
+ */
+
+ /* Simulate 438 (Stale Nonce) */
+
+ /* Simulate 436 (Unknown Username) */
+
+ /* When server wants to use short term credential, but request has
+ * REALM, reject with .... ???
+ */
+
+ /* Invalid HMAC */
+
+ /* Valid static short term, without NONCE */
+
+ /* Valid static short term, WITH NONCE */
+
+ /* Valid static long term (with NONCE */
+
+ /* Valid dynamic short term (without NONCE) */
+
+ /* Valid dynamic short term (with NONCE) */
+
+ /* Valid dynamic long term (with NONCE) */
+
+ return 0;
+}
+
+
+int stun_test(void)
+{
+ decode_verify();
+ decode_test();
+ auth_test();
+ return 0;
+}
+
diff --git a/pjlib-util/src/pjlib-util-test/test.c b/pjlib-util/src/pjlib-util-test/test.c
new file mode 100644
index 0000000..feb2c01
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/test.c
@@ -0,0 +1,112 @@
+/* $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 "test.h"
+#include <pjlib.h>
+#include <pjlib-util.h>
+
+void app_perror(const char *msg, pj_status_t rc)
+{
+ char errbuf[256];
+
+ PJ_CHECK_STACK();
+
+ pj_strerror(rc, errbuf, sizeof(errbuf));
+ PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
+}
+
+#define DO_TEST(test) do { \
+ PJ_LOG(3, ("test", "Running %s...", #test)); \
+ rc = test; \
+ PJ_LOG(3, ("test", \
+ "%s(%d)", \
+ (char*)(rc ? "..ERROR" : "..success"), rc)); \
+ if (rc!=0) goto on_return; \
+ } while (0)
+
+
+pj_pool_factory *mem;
+
+int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME |
+ PJ_LOG_HAS_MICRO_SEC;
+
+static int test_inner(void)
+{
+ pj_caching_pool caching_pool;
+ int rc = 0;
+
+ mem = &caching_pool.factory;
+
+ pj_log_set_level(3);
+ pj_log_set_decor(param_log_decor);
+
+ rc = pj_init();
+ if (rc != 0) {
+ app_perror("pj_init() error!!", rc);
+ return rc;
+ }
+
+ rc = pjlib_util_init();
+ pj_assert(rc == 0);
+
+ pj_dump_config();
+ pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 );
+
+#if INCLUDE_XML_TEST
+ DO_TEST(xml_test());
+#endif
+
+#if INCLUDE_ENCRYPTION_TEST
+ DO_TEST(encryption_test());
+ DO_TEST(encryption_benchmark());
+#endif
+
+#if INCLUDE_STUN_TEST
+ DO_TEST(stun_test());
+#endif
+
+#if INCLUDE_RESOLVER_TEST
+ DO_TEST(resolver_test());
+#endif
+
+#if INCLUDE_HTTP_CLIENT_TEST
+ DO_TEST(http_client_test());
+#endif
+
+on_return:
+ return rc;
+}
+
+int test_main(void)
+{
+ PJ_USE_EXCEPTION;
+
+ PJ_TRY {
+ return test_inner();
+ }
+ PJ_CATCH_ANY {
+ int id = PJ_GET_EXCEPTION();
+ PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)",
+ id, pj_exception_id_name(id)));
+ }
+ PJ_END;
+
+ return -1;
+}
+
diff --git a/pjlib-util/src/pjlib-util-test/test.h b/pjlib-util/src/pjlib-util-test/test.h
new file mode 100644
index 0000000..37b1d90
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/test.h
@@ -0,0 +1,38 @@
+/* $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 <pj/types.h>
+
+#define INCLUDE_XML_TEST 1
+#define INCLUDE_ENCRYPTION_TEST 1
+#define INCLUDE_STUN_TEST 1
+#define INCLUDE_RESOLVER_TEST 1
+#define INCLUDE_HTTP_CLIENT_TEST 1
+
+extern int xml_test(void);
+extern int encryption_test();
+extern int encryption_benchmark();
+extern int stun_test();
+extern int test_main(void);
+extern int resolver_test(void);
+extern int http_client_test();
+
+extern void app_perror(const char *title, pj_status_t rc);
+extern pj_pool_factory *mem;
+
diff --git a/pjlib-util/src/pjlib-util-test/xml.c b/pjlib-util/src/pjlib-util-test/xml.c
new file mode 100644
index 0000000..19733a1
--- /dev/null
+++ b/pjlib-util/src/pjlib-util-test/xml.c
@@ -0,0 +1,145 @@
+/* $Id: xml.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * 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"
+
+
+#if INCLUDE_XML_TEST
+
+#include <pjlib-util/xml.h>
+#include <pjlib.h>
+
+#define THIS_FILE "xml_test"
+
+static const char *xml_doc[] =
+{
+" <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+" <p:pidf-full xmlns=\"urn:ietf:params:xml:ns:pidf\"\n"
+" xmlns:p=\"urn:ietf:params:xml:ns:pidf-diff\"\n"
+" xmlns:r=\"urn:ietf:params:xml:ns:pidf:rpid\"\n"
+" xmlns:c=\"urn:ietf:params:xml:ns:pidf:caps\"\n"
+" entity=\"pres:someone@example.com\"\n"
+" version=\"567\">\n"
+"\n"
+" <tuple id=\"sg89ae\">\n"
+" <status>\n"
+" <basic>open</basic>\n"
+" <r:relationship>assistant</r:relationship>\n"
+" </status>\n"
+" <c:servcaps>\n"
+" <c:audio>true</c:audio>\n"
+" <c:video>false</c:video>\n"
+" <c:message>true</c:message>\n"
+" </c:servcaps>\n"
+" <contact priority=\"0.8\">tel:09012345678</contact>\n"
+" </tuple>\n"
+"\n"
+" <tuple id=\"cg231jcr\">\n"
+" <status>\n"
+" <basic>open</basic>\n"
+" </status>\n"
+" <contact priority=\"1.0\">im:pep@example.com</contact>\n"
+" </tuple>\n"
+"\n"
+" <tuple id=\"r1230d\">\n"
+" <status>\n"
+" <basic>closed</basic>\n"
+" <r:activity>meeting</r:activity>\n"
+" </status>\n"
+" <r:homepage>http://example.com/~pep/</r:homepage>\n"
+" <r:icon>http://example.com/~pep/icon.gif</r:icon>\n"
+" <r:card>http://example.com/~pep/card.vcd</r:card>\n"
+" <contact priority=\"0.9\">sip:pep@example.com</contact>\n"
+" </tuple>\n"
+"\n"
+" <note xml:lang=\"en\">Full state presence document</note>\n"
+"\n"
+" <r:person>\n"
+" <r:status>\n"
+" <r:activities>\n"
+" <r:on-the-phone/>\n"
+" <r:busy/>\n"
+" </r:activities>\n"
+" </r:status>\n"
+" </r:person>\n"
+"\n"
+" <r:device id=\"urn:esn:600b40c7\">\n"
+" <r:status>\n"
+" <c:devcaps>\n"
+" <c:mobility>\n"
+" <c:supported>\n"
+" <c:mobile/>\n"
+" </c:supported>\n"
+" </c:mobility>\n"
+" </c:devcaps>\n"
+" </r:status>\n"
+" </r:device>\n"
+"\n"
+" </p:pidf-full>\n"
+}
+;
+
+static int xml_parse_print_test(const char *doc)
+{
+ pj_str_t msg;
+ pj_pool_t *pool;
+ pj_xml_node *root;
+ char *output;
+ int output_len;
+
+ pool = pj_pool_create(mem, "xml", 4096, 1024, NULL);
+ pj_strdup2(pool, &msg, doc);
+ root = pj_xml_parse(pool, msg.ptr, msg.slen);
+ if (!root) {
+ PJ_LOG(1, (THIS_FILE, " Error: unable to parse XML"));
+ return -10;
+ }
+
+ output = (char*)pj_pool_zalloc(pool, msg.slen + 512);
+ output_len = pj_xml_print(root, output, msg.slen+512, PJ_TRUE);
+ if (output_len < 1) {
+ PJ_LOG(1, (THIS_FILE, " Error: buffer too small to print XML file"));
+ return -20;
+ }
+ output[output_len] = '\0';
+
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+int xml_test()
+{
+ unsigned i;
+ for (i=0; i<sizeof(xml_doc)/sizeof(xml_doc[0]); ++i) {
+ int status;
+ if ((status=xml_parse_print_test(xml_doc[i])) != 0)
+ return status;
+ }
+ return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled.
+ */
+int dummy_xml_test;
+#endif /* INCLUDE_XML_TEST */
+
+