[commit] r8321 - in freeswitch/trunk: . libs/iax src/mod/endpoints/mod_iax
Author: anthm Date: Thu May 8 19:25:36 2008 New Revision: 8321 Added: freeswitch/trunk/src/mod/endpoints/mod_iax/answer.h freeswitch/trunk/src/mod/endpoints/mod_iax/frame.h freeswitch/trunk/src/mod/endpoints/mod_iax/iax-client.h freeswitch/trunk/src/mod/endpoints/mod_iax/iax-mutex.c freeswitch/trunk/src/mod/endpoints/mod_iax/iax-mutex.h freeswitch/trunk/src/mod/endpoints/mod_iax/iax.c freeswitch/trunk/src/mod/endpoints/mod_iax/iax.h freeswitch/trunk/src/mod/endpoints/mod_iax/iax2-parser.c freeswitch/trunk/src/mod/endpoints/mod_iax/iax2-parser.h freeswitch/trunk/src/mod/endpoints/mod_iax/iax2.h freeswitch/trunk/src/mod/endpoints/mod_iax/jitterbuf.c freeswitch/trunk/src/mod/endpoints/mod_iax/jitterbuf.h freeswitch/trunk/src/mod/endpoints/mod_iax/md5.c freeswitch/trunk/src/mod/endpoints/mod_iax/md5.h freeswitch/trunk/src/mod/endpoints/mod_iax/winiphone.c freeswitch/trunk/src/mod/endpoints/mod_iax/winpoop.h Removed: freeswitch/trunk/libs/iax/ Modified: freeswitch/trunk/configure.in freeswitch/trunk/src/mod/endpoints/mod_iax/Makefile Log: slim down iax build Modified: freeswitch/trunk/configure.in ============================================================================== --- freeswitch/trunk/configure.in (original) +++ freeswitch/trunk/configure.in Thu May 8 19:25:36 2008 @@ -593,7 +593,6 @@ AC_CONFIG_SUBDIRS(libs/voipcodecs) AC_CONFIG_SUBDIRS(libs/codec/ilbc) AC_CONFIG_SUBDIRS(libs/curl) -AC_CONFIG_SUBDIRS(libs/iax) AC_CONFIG_SUBDIRS(libs/iksemel) AC_CONFIG_SUBDIRS(libs/js/nsprpub) AC_CONFIG_SUBDIRS(libs/js) Modified: freeswitch/trunk/src/mod/endpoints/mod_iax/Makefile ============================================================================== --- freeswitch/trunk/src/mod/endpoints/mod_iax/Makefile (original) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/Makefile Thu May 8 19:25:36 2008 @@ -1,11 +1,6 @@ BASE=../../../.. +LOCAL_CFLAGS=-I. -DNEWJB -DLIBIAX +LOCAL_OBJS=iax2-parser.o iax.o md5.o jitterbuf.o iax-mutex.o -IAX_DIR=$(BASE)/libs/iax -IAXLA=$(IAX_DIR)/src/libiax.la -LOCAL_CFLAGS=-I$(IAX_DIR)/src -LOCAL_LIBADD=$(IAXLA) include $(BASE)/build/modmake.rules -$(IAXLA): $(IAX_DIR) $(IAX_DIR)/.update - cd $(IAX_DIR) && $(MAKE) - Added: freeswitch/trunk/src/mod/endpoints/mod_iax/answer.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/answer.h Thu May 8 19:25:36 2008 @@ -0,0 +1,237 @@ +/* + * Signed 16-bit audio data + * + * Source: answer.raw + * + * Copyright (C) 1999, Mark Spencer and Linux Support Services + * + * Distributed under the terms of the GNU General Public License + * + */ + +static signed short answer[] = { +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 0x19b7, 0x0245, 0xeee5, 0xb875, 0xd9a4, 0x6018, 0x660a, 0xc3c6, +0x8741, 0xff55, 0x4c2e, 0x2146, 0xfed2, 0xf079, 0xcbd4, 0xe561, 0x3c41, 0x3166, +0xd425, 0xdc59, 0x2748, 0x087d, 0xc72b, 0xfe3a, 0x4681, 0x14c6, 0xcf45, 0xdd38, +0xf8dd, 0x0a39, 0x3a5a, 0x32b9, 0xbfec, 0x957f, 0x15a3, 0x70f4, 0x1d95, 0xbfc4, +0xd367, 0xfda0, 0x0dc0, 0x29eb, 0x1fc2, 0xd684, 0xcab1, 0x19c7, 0x29ef, 0xe679, +0xe9d0, 0x2b82, 0x151a, 0xca9f, 0xdb68, 0x1f4a, 0x271c, 0x0e2a, 0xfb32, 0xd1b2, +0xc8ff, 0x2382, 0x6380, 0x0a52, 0xa118, 0xccbf, 0x2ddc, 0x33fd, 0x0964, 0xf2a4, +0xdd81, 0xe092, 0x1a00, 0x325c, 0xf5e3, 0xd6a1, 0x0b6c, 0x1c75, 0xe4f8, 0xe07c, +0x2082, 0x2b3e, 0xf445, 0xdaa9, 0xea13, 0xff3c, 0x245c, 0x35c1, 0xf308, 0xab53, +0xdf59, 0x4698, 0x3f3b, 0xe7f7, 0xca84, 0xed4d, 0x0c3f, 0x1e94, 0x1c2d, 0xf06f, +0xd4df, 0xff34, 0x23d8, 0x001e, 0xe3f1, 0x0b15, 0x2113, 0xf3fd, 0xd768, 0xf9a0, +0x1d31, 0x1c6e, 0x0797, 0xe3a0, 0xce6c, 0xfd7b, 0x422a, 0x2c4c, 0xd364, 0xbf42, +0x0278, 0x303e, 0x1c51, 0xf737, 0xe25a, 0xe75f, 0x0a8f, 0x22ab, 0x05f4, 0xe3f9, +0xf8c4, 0x1705, 0x0162, 0xe49f, 0xfb8b, 0x1e2b, 0x13ac, 0xf044, 0xe07b, 0xf01a, +0x1567, 0x2cbf, 0x0b75, 0xd01b, 0xd206, 0x1563, 0x38d7, 0x0f2e, 0xdb32, 0xdc30, +0x023b, 0x1e44, 0x16eb, 0xf5f7, 0xe425, 0xfa33, 0x14d5, 0x0968, 0xeff2, 0xf762, +0x1137, 0x0e59, 0xf13a, 0xe651, 0xff41, 0x1d60, 0x18fd, 0xf1e6, 0xd75f, 0xf097, +0x20ec, 0x27fa, 0xfba4, 0xd5b8, 0xe68e, 0x1657, 0x2518, 0x04f6, 0xe5a3, 0xe976, +0x0578, 0x18fa, 0x0a92, 0xec0a, 0xef2a, 0x111f, 0x12f4, 0xeec3, 0xe95e, 0x0d3a, +0x18fd, 0xff72, 0xeefc, 0xf114, 0xfaaa, 0x14ee, 0x21db, 0xf56e, 0xcb49, 0xf621, +0x3323, 0x1947, 0xe017, 0xe7e9, 0x0819, 0x0707, 0x084c, 0x0f57, 0xf152, 0xdf92, +0x104a, 0x28eb, 0xedcc, 0xd4ad, 0x1415, 0x296d, 0xed9a, 0xdf57, 0x0cc2, 0x0d95, +0xf7b5, 0x0deb, 0x0b34, 0xd713, 0xea08, 0x38d6, 0x216d, 0xc727, 0xdc32, 0x2cd2, +0x1822, 0xe2d5, 0xfeb3, 0x106c, 0xe6e5, 0xf81e, 0x2fe8, 0x01af, 0xc180, 0x037a, +0x42d8, 0xf88d, 0xc344, 0x0a4f, 0x2c4e, 0xf19d, 0xebeb, 0x162c, 0xf9e9, 0xde93, +0x1b56, 0x2c60, 0xd8aa, 0xce3e, 0x2a41, 0x2eeb, 0xdab1, 0xde32, 0x1c32, 0x0aba, +0xeabe, 0x1008, 0x136d, 0xda2f, 0xec3b, 0x31dd, 0x1130, 0xca79, 0xf5b8, 0x3423, +0x0274, 0xd27d, 0x035e, 0x1e68, 0xf641, 0xf904, 0x1691, 0xef7d, 0xd57a, 0x1c3b, +0x3c23, 0xe881, 0xc274, 0x0af5, 0x2962, 0xfa34, 0xf676, 0x0f71, 0xefcc, 0xe01f, +0x19e7, 0x276f, 0xe694, 0xe134, 0x1c3a, 0x0e8b, 0xd8e7, 0xfa81, 0x2f8b, 0x07c5, +0xd904, 0xf6fa, 0x0ca5, 0xf9a2, 0x0dc7, 0x2623, 0xec54, 0xbe23, 0x02b6, 0x4296, +0x10cd, 0xda61, 0xf11c, 0x0103, 0xf41c, 0x10b4, 0x2a03, 0xf63c, 0xce1a, 0xfdbd, +0x1fb4, 0xfc51, 0xf727, 0x1c8a, 0x04ff, 0xcf41, 0xec05, 0x2913, 0x1ce8, 0xf70c, +0xf744, 0xede8, 0xdd77, 0x0d99, 0x43f1, 0x119c, 0xc14f, 0xd60e, 0x17cb, 0x1e19, +0x0d4e, 0x0c95, 0xeed1, 0xcdf4, 0xf7a5, 0x331f, 0x1cd0, 0xeb17, 0xf082, 0xfb19, +0xe899, 0xfdeb, 0x323c, 0x2036, 0xdad3, 0xd134, 0xfd03, 0x1345, 0x1c10, 0x2239, +0xf656, 0xbc22, 0xdc3f, 0x3392, 0x3d59, 0xfd77, 0xdb4d, 0xe23f, 0xedbe, 0x0f7e, +0x35cc, 0x1947, 0xd5dc, 0xd1bf, 0x035d, 0x16fc, 0x1174, 0x1675, 0x0249, 0xd2d4, +0xd851, 0x184d, 0x32fe, 0x0f91, 0xee14, 0xe1e6, 0xdf9b, 0x016b, 0x3668, 0x2b2b, +0xe20c, 0xc554, 0xf257, 0x1c05, 0x1fc5, 0x14f0, 0xf891, 0xd41c, 0xdf83, 0x1865, +0x2de1, 0x0b16, 0xed58, 0xea0c, 0xea79, 0xfbd9, 0x22af, 0x2732, 0xf62f, 0xd389, +0xe7d9, 0x0b39, 0x1cdc, 0x1de3, 0x038a, 0xd809, 0xd5f7, 0x0b55, 0x305e, 0x1910, +0xf02e, 0xe089, 0xe7c7, 0x0195, 0x2265, 0x21da, 0xf743, 0xd8f2, 0xe978, 0x09a1, +0x190a, 0x17c5, 0x045a, 0xe46d, 0xdd06, 0xffb2, 0x2293, 0x1cfe, 0xfd4d, 0xe4f9, +0xe310, 0xfaf1, 0x1d22, 0x2376, 0x0113, 0xde3a, 0xe21b, 0x0204, 0x1ba1, 0x1bd6, +0x0333, 0xe563, 0xe104, 0xfd51, 0x1bc1, 0x1ccf, 0x0285, 0xe757, 0xe35e, 0xfaf2, +0x185d, 0x1d46, 0x06b7, 0xec13, 0xe108, 0xef6e, 0x121d, 0x2a17, 0x16a6, 0xe32c, +0xc9a9, 0xf070, 0x2f48, 0x3788, 0xfa4e, 0xc32a, 0xd9c2, 0x1fa1, 0x36fe, 0x07fa, +0xd9e4, 0xe577, 0x0e5e, 0x1755, 0xfb53, 0xed71, 0x0540, 0x19e0, 0x0301, 0xdc97, +0xe391, 0x1937, 0x367c, 0x0bc9, 0xca4c, 0xc96b, 0x105d, 0x461f, 0x2416, 0xd481, +0xbc97, 0xf8b7, 0x39af, 0x2ec9, 0xecc6, 0xcb50, 0xeee3, 0x1ffe, 0x1e8e, 0xf700, +0xe66a, 0xff58, 0x149f, 0x02e5, 0xe792, 0xf2d8, 0x1a4d, 0x225a, 0xf642, 0xce7f, +0xe6a6, 0x25e2, 0x38f5, 0x01d0, 0xc50f, 0xd243, 0x19bd, 0x3fc6, 0x14f0, 0xd2d7, +0xcdb6, 0x069a, 0x2ffe, 0x1847, 0xe6f8, 0xdf0a, 0x0337, 0x1a90, 0x067a, 0xeb5b, +0xf541, 0x143b, 0x14f2, 0xf092, 0xdc02, 0xfb91, 0x28a3, 0x2274, 0xeaa8, 0xc9e7, +0xef48, 0x2d01, 0x322e, 0xf6d2, 0xc7cb, 0xe13b, 0x1fda, 0x3217, 0x0458, 0xd690, +0xe2bf, 0x11c4, 0x21d5, 0x0291, 0xe5c8, 0xf3a9, 0x12ba, 0x11aa, 0xf22b, 0xe627, +0x03ec, 0x219a, 0x1036, 0xe2f2, 0xd93f, 0x059c, 0x2ed6, 0x1b75, 0xe227, 0xce55, +0xfb19, 0x2de0, 0x2477, 0xed08, 0xd148, 0xf307, 0x21d4, 0x2002, 0xf543, 0xdeac, +0xf7f9, 0x18a9, 0x11d6, 0xf0ef, 0xe8e4, 0x05ea, 0x1ba5, 0x0727, 0xe448, 0xe748, +0x100e, 0x265e, 0x07fc, 0xdbae, 0xde78, 0x0efa, 0x2ce0, 0x0f94, 0xddf1, 0xd9ea, +0x0797, 0x28f6, 0x12eb, 0xe60c, 0xdf46, 0x0469, 0x1fbb, 0x0ced, 0xe9f6, 0xe95f, +0x09fe, 0x1ab9, 0x02cb, 0xe5a4, 0xef2a, 0x1327, 0x1d7b, 0xfd07, 0xde3d, 0xed9c, +0x17e5, 0x22e7, 0xfe3a, 0xdb38, 0xe9b9, 0x161a, 0x2416, 0x0175, 0xde3d, 0xe9de, +0x1294, 0x1fc9, 0x00ea, 0xe2a7, 0xeee2, 0x1298, 0x1a7d, 0xfc1d, 0xe3bb, 0xf47a, +0x1642, 0x185e, 0xf727, 0xe1af, 0xf709, 0x19c3, 0x18e7, 0xf50d, 0xe010, 0xf75b, +0x1a9c, 0x18d8, 0xf4c5, 0xe0c9, 0xf865, 0x1a1c, 0x16d5, 0xf3a6, 0xe257, 0xfaf2, +0x1a44, 0x14d5, 0xf34f, 0xe4b6, 0xfc77, 0x17d5, 0x0ff8, 0xf133, 0xe8b7, 0x0344, +0x1a37, 0x0ad5, 0xe95e, 0xe61a, 0x08a5, 0x227e, 0x0e33, 0xe4a7, 0xdd70, 0x03b0, +0x25f4, 0x17b2, 0xec0a, 0xdb4e, 0xf898, 0x1ba3, 0x18f6, 0xf973, 0xe87f, 0xf77a, +0x0b93, 0x096c, 0xfb0e, 0xfb03, 0x0896, 0x0940, 0xf51d, 0xe904, 0xfdc7, 0x1dda, +0x1bf9, 0xf29b, 0xd37f, 0xea1b, 0x1f37, 0x3175, 0x07eb, 0xd3f7, 0xd46b, 0x077d, +0x2eeb, 0x1e67, 0xeeae, 0xd8c7, 0xef85, 0x1119, 0x18d3, 0x088e, 0xf953, 0xf5ad, +0xf556, 0xf63d, 0x0234, 0x167a, 0x19a1, 0xfbf9, 0xd873, 0xdd4b, 0x0f06, 0x3748, +0x21e6, 0xe181, 0xc032, 0xe79a, 0x2bec, 0x3e76, 0x0b1b, 0xce41, 0xcb23, 0xff96, +0x2d79, 0x26d1, 0xfcc7, 0xdf8a, 0xe525, 0xfd83, 0x10f1, 0x16d7, 0x0f50, 0xfaea, +0xe3f1, 0xe20f, 0x0158, 0x27d9, 0x2866, 0xf96f, 0xcb34, 0xd563, 0x11d6, 0x3d25, +0x2424, 0xe254, 0xc2c9, 0xe7cd, 0x248d, 0x34f5, 0x0c42, 0xdcd0, 0xd827, 0xfa65, +0x19eb, 0x1b50, 0x0721, 0xf396, 0xeb9c, 0xefde, 0x0016, 0x1594, 0x1cc1, 0x0658, +0xe22b, 0xd852, 0xfb3e, 0x2923, 0x2c78, 0xfc87, 0xcdb5, 0xd69c, 0x0e3c, 0x3527, +0x201f, 0xe993, 0xcf9e, 0xeb21, 0x183f, 0x25ea, 0x0c93, 0xed4d, 0xe5f9, 0xf548, +0x07fb, 0x117c, 0x0ff2, 0x0398, 0xf08c, 0xe628, 0xf489, 0x143b, 0x2419, 0x0ccf, +0xe2cc, 0xd5a6, 0xf861, 0x2615, 0x2a1b, 0xfeb4, 0xd543, 0xdc53, 0x09b4, 0x2901, +0x19ff, 0xf24a, 0xde86, 0xeec4, 0x0b7b, 0x1733, 0x0d0a, 0xfc24, 0xf1bb, 0xf110, +0xfa03, 0x0a0f, 0x15d4, 0x0e21, 0xf435, 0xe17e, 0xee90, 0x1225, 0x2527, 0x0efa, +0xe61f, 0xd916, 0xf7b8, 0x1f50, 0x2326, 0x0099, 0xe01e, 0xe473, 0x0491, 0x1b37, +0x1360, 0xfb17, 0xecd9, 0xf20d, 0x0051, 0x0aec, 0x0d4a, 0x073d, 0xfa5a, 0xeeb8, +0xf165, 0x0516, 0x17dc, 0x12da, 0xf71b, 0xe213, 0xed85, 0x0eef, 0x20c8, 0x0e09, +0xebcc, 0xe0d4, 0xf848, 0x1637, 0x19d6, 0x026b, 0xec09, 0xed00, 0xff9b, 0x0e5a, +0x0d6b, 0x026c, 0xf865, 0xf4da, 0xf888, 0x025a, 0x0cbb, 0x0d53, 0xff96, 0xeefa, +0xee80, 0x021c, 0x15d6, 0x126a, 0xf9c1, 0xe724, 0xf017, 0x0aa1, 0x18b6, 0x0b4e, +0xf2d7, 0xea91, 0xf957, 0x0cac, 0x1061, 0x03f4, 0xf6ad, 0xf476, 0xfbdf, 0x0489, +0x08b1, 0x06df, 0xffcf, 0xf766, 0xf537, 0xfddf, 0x0ad4, 0x0e15, 0x01da, 0xf205, +0xf0a0, 0x0082, 0x1066, 0x0e41, 0xfc71, 0xef1b, 0xf4ad, 0x05cd, 0x0f32, 0x07ed, +0xf9c8, 0xf401, 0xfa93, 0x04af, 0x088c, 0x04a7, 0xfe15, 0xf9f1, 0xfa64, 0xff1e, +0x0539, 0x078c, 0x02af, 0xfa1a, 0xf69d, 0xfd09, 0x075b, 0x0a3d, 0x01f2, 0xf761, +0xf642, 0xffa7, 0x08f3, 0x0830, 0xff05, 0xf7db, 0xf9bc, 0x0174, 0x068b, 0x04b2, +0xfeff, 0xfb39, 0xfc1a, 000000, 0x0371, 0x03d7, 0x00fe, 0xfd37, 0xfbe0, 0xfe78, +0x02af, 0x044a, 0x0180, 0xfd43, 0xfc00, 0xfed1, 0x02aa, 0x0346, 0x00dd, 0xfde0, +0xfbfe, 0x0114, 0x0987, 0x04bc, 0xf49d, 0xf23a, 0x06ab, 0x162e, 0x0544, 0xe76b, +0xea25, 0x1015, 0x2474, 0x0431, 0xd7d3, 0xe1ec, 0x1923, 0x2df5, 0x01cd, 0xd386, +0xe3d9, 0x1b9d, 0x2c62, 0xfeb8, 0xd31a, 0xe6ba, 0x1dbd, 0x2abb, 0xfbab, 0xd2ed, +0xe9ab, 0x1fa7, 0x28ef, 0xf8b3, 0xd2f5, 0xeca5, 0x2160, 0x26fd, 0xf5d7, 0xd334, +0xefa1, 0x22e5, 0x24ea, 0xf31b, 0xd3a9, 0xf29f, 0x2435, 0x22b6, 0xf07e, 0xd44e, +0xf59b, 0x2551, 0x2067, 0xee08, 0xd527, 0xf88e, 0x2639, 0x1e00, 0xebb6, 0xd62d, +0xfb77, 0x26eb, 0x1b85, 0xe98b, 0xd75f, 0xfe51, 0x276b, 0x18f9, 0xe78e, 0xd8b9, +0x011a, 0x27b6, 0x1660, 0xe5bb, 0xda3a, 0x03cc, 0x27cf, 0x13bd, 0xe415, 0xdbdf, +0x066a, 0x27b7, 0x1117, 0xe29e, 0xdda5, 0x08ec, 0x276e, 0x0e6d, 0xe154, 0xdf89, +0x0b52, 0x26f6, 0x0bc7, 0xe039, 0xe185, 0x0d96, 0x2653, 0x0924, 0xdf4e, 0xe399, +0x0fb9, 0x2584, 0x068b, 0xde93, 0xe5c0, 0x11b8, 0x248e, 0x03fd, 0xde08, 0xe7f8, +0x1390, 0x2372, 0x0180, 0xddaa, 0xea3c, 0x1544, 0x2231, 0xff12, 0xdd7a, 0xec89, +0x16cf, 0x20d0, 0xfcb9, 0xdd77, 0xeedb, 0x1831, 0x1f52, 0xfa77, 0xdd9f, 0xf132, +0x1969, 0x1db7, 0xf850, 0xddf1, 0xf385, 0x1a75, 0x1c06, 0xf645, 0xde6b, 0xf5d7, +0x1b5b, 0x1a3f, 0xf457, 0xdf0d, 0xf820, 0x1c13, 0x1867, 0xf288, 0xdfd2, 0xfa5f, +0x1ca1, 0x167f, 0xf0db, 0xe0ba, 0xfc92, 0x1d06, 0x148b, 0xef50, 0xe1c1, 0xfeb5, +0x1d43, 0x1290, 0xede9, 0xe2e6, 0x00c6, 0x1d58, 0x108e, 0xeca7, 0xe426, 0x02c4, +0x1d45, 0x0e8a, 0xeb8a, 0xe57f, 0x04a9, 0x1d0e, 0x0c87, 0xea92, 0xe6ec, 0x0677, +0x1cb2, 0x0a87, 0xe9be, 0xe86e, 0x082a, 0x1c34, 0x088b, 0xe912, 0xe9fe, 0x09c1, +0x1b95, 0x069c, 0xe88c, 0xeb9c, 0x0b3a, 0x1ad9, 0x04b6, 0xe82a, 0xed43, 0x0c96, +0x1a00, 0x02df, 0xe7eb, 0xeef3, 0x0dd0, 0x190d, 0x0116, 0xe7d0, 0xf0a8, 0x0eec, +0x1804, 0xff61, 0xe7d8, 0xf25d, 0x0fe6, 0x16e3, 0xfdc0, 0xe800, 0xf412, 0x10bf, +0x15b1, 0xfc36, 0xe848, 0xf5c5, 0x1176, 0x146e, 0xfac2, 0xe8ad, 0xf771, 0x120d, +0x1320, 0xf969, 0xe92e, 0xf913, 0x1282, 0x11c4, 0xf828, 0xe9cb, 0xfaac, 0x12d8, +0x1062, 0xf703, 0xea7e, 0xfc38, 0x130e, 0x0efa, 0xf5fb, 0xeb49, 0xfdb5, 0x1325, +0x0d8e, 0xf50e, 0xec26, 0xff20, 0x131e, 0x0c21, 0xf43f, 0xed15, 0x007a, 0x12fa, +0x0ab6, 0xf38d, 0xee15, 0x01be, 0x12bd, 0x094f, 0xf2f9, 0xef22, 0x02ef, 0x1265, +0x07f0, 0xf283, 0xf037, 0x0408, 0x11f6, 0x0699, 0xf226, 0xf156, 0x050a, 0x1170, +0x054b, 0xf1e8, 0xf27a, 0x05f4, 0x10d8, 0x040c, 0xf1c5, 0xf3a3, 0x06c2, 0x102c, +0x02da, 0xf1bc, 0xf4cc, 0x0779, 0x0f71, 0x01b7, 0xf1cc, 0xf5f5, 0x0815, 0x0ea7, +0x00a8, 0xf1f4, 0xf719, 0x0899, 0x0dd2, 0xffab, 0xf233, 0xf839, 0x0902, 0x0cf4, +0xfec0, 0xf288, 0xf950, 0x0952, 0x0c0e, 0xfdec, 0xf2ee, 0xfa5d, 0x0989, 0x0b23, +0xfd2d, 0xf368, 0xfb62, 0x09a7, 0x0a35, 0xfc85, 0xf3f1, 0xfc58, 0x09af, 0x0946, +0xfbf2, 0xf488, 0xfd3f, 0x09a1, 0x0859, 0xfb77, 0xf52c, 0xfe17, 0x097d, 0x076f, +0xfb14, 0xf5d8, 0xfede, 0x0945, 0x068a, 0xfac6, 0xf68d, 0xff93, 0x08fb, 0x05ad, +0xfa8e, 0xf747, 0x0034, 0x08a1, 0x04da, 0xfa6f, 0xf805, 0x00c2, 0x0836, 0x0410, +0xfa63, 0xf8c6, 0x013c, 0x07bf, 0x0354, 0xfa6c, 0xf985, 0x01a1, 0x073b, 0x02a4, +0xfa8a, 0xfa43, 0x01f1, 0x06af, 0x0204, 0xfab9, 0xfafc, 0x022c, 0x0619, 0x0175, +0xfafa, 0xfbae, 0x0252, 0x057f, 0x00f6, 0xfb4b, 0xfc5a, 0x0263, 0x04e0, 0x008b, +0xfbaa, 0xfcfa, 0x0262, 0x0440, 0x0032, 0xfc16, 0xfd90, 0x024b, 0x03a0, 0xffec, +0xfc8c, 0xfe19, 0x0225, 0x0301, 0xffb9, 0xfd0c, 0xfe93, 0x01ea, 0x0267, 0xff9c, +0xfd95, 0xfefe, 0x01a0, 0x01d3, 0xff90, 0xfe22, 0xff5a, 0x0147, 0x0145, 0xff99, +0xfeb3, 0xffa1, 0x00e0, 0x00c3, 0xffb6, 0xff46, 0xffd9, 0x006d, 0x004b, 0xffe5, +0xffda, 0xfffc, 000000, 0xfffe, 000000, 0xffff, 000000, 0xffff, 0xffff, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, +000000 }; Added: freeswitch/trunk/src/mod/endpoints/mod_iax/frame.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/frame.h Thu May 8 19:25:36 2008 @@ -0,0 +1,134 @@ +/* + * libiax: An implementation of the Inter-Asterisk eXchange protocol + * + * Asterisk internal frame definitions. + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU Lesser General Public License. Other components of + * Asterisk are distributed under The GNU General Public License + * only. + */ + +#ifndef _LIBIAX_FRAME_H +#define _LIBIAX_FRAME_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* Frame types */ +#define AST_FRAME_DTMF 1 /* A DTMF digit, subclass is the digit */ +#define AST_FRAME_VOICE 2 /* Voice data, subclass is AST_FORMAT_* */ +#define AST_FRAME_VIDEO 3 /* Video frame, maybe?? :) */ +#define AST_FRAME_CONTROL 4 /* A control frame, subclass is AST_CONTROL_* */ +#define AST_FRAME_NULL 5 /* An empty, useless frame */ +#define AST_FRAME_IAX 6 /* Inter Aterisk Exchange private frame type */ +#define AST_FRAME_TEXT 7 /* Text messages */ +#define AST_FRAME_IMAGE 8 /* Image Frames */ +#define AST_FRAME_HTML 9 /* HTML Frames */ +#define AST_FRAME_CNG 10 /* Comfort Noise frame (subclass is level of CNG in -dBov) */ + +/* HTML subclasses */ +#define AST_HTML_URL 1 /* Sending a URL */ +#define AST_HTML_DATA 2 /* Data frame */ +#define AST_HTML_BEGIN 4 /* Beginning frame */ +#define AST_HTML_END 8 /* End frame */ +#define AST_HTML_LDCOMPLETE 16 /* Load is complete */ +#define AST_HTML_NOSUPPORT 17 /* Peer is unable to support HTML */ +#define AST_HTML_LINKURL 18 /* Send URL and track */ +#define AST_HTML_UNLINK 19 /* Request no more linkage */ +#define AST_HTML_LINKREJECT 20 /* Reject LINKURL */ + +/* Data formats for capabilities and frames alike */ +/*! G.723.1 compression */ +#define AST_FORMAT_G723_1 (1 << 0) + /*! GSM compression */ +#define AST_FORMAT_GSM (1 << 1) + /*! Raw mu-law data (G.711) */ +#define AST_FORMAT_ULAW (1 << 2) + /*! Raw A-law data (G.711) */ +#define AST_FORMAT_ALAW (1 << 3) + /*! ADPCM (G.726, 32kbps) */ +#define AST_FORMAT_G726 (1 << 4) + /*! ADPCM (IMA) */ +#define AST_FORMAT_ADPCM (1 << 5) + /*! Raw 16-bit Signed Linear (8000 Hz) PCM */ +#define AST_FORMAT_SLINEAR (1 << 6) + /*! LPC10, 180 samples/frame */ +#define AST_FORMAT_LPC10 (1 << 7) + /*! G.729A audio */ +#define AST_FORMAT_G729A (1 << 8) + /*! SpeeX Free Compression */ +#define AST_FORMAT_SPEEX (1 << 9) + /*! iLBC Free Compression */ +#define AST_FORMAT_ILBC (1 << 10) + /*! Maximum audio format */ +#define AST_FORMAT_MAX_AUDIO (1 << 15) + /*! JPEG Images */ +#define AST_FORMAT_JPEG (1 << 16) + /*! PNG Images */ +#define AST_FORMAT_PNG (1 << 17) + /*! H.261 Video */ +#define AST_FORMAT_H261 (1 << 18) + /*! H.263 Video */ +#define AST_FORMAT_H263 (1 << 19) + /*! Max one */ +#define AST_FORMAT_MAX_VIDEO (1 << 24) + +/* Control frame types */ +#define AST_CONTROL_HANGUP 1 /* Other end has hungup */ +#define AST_CONTROL_RING 2 /* Local ring */ +#define AST_CONTROL_RINGING 3 /* Remote end is ringing */ +#define AST_CONTROL_ANSWER 4 /* Remote end has answered */ +#define AST_CONTROL_BUSY 5 /* Remote end is busy */ +#define AST_CONTROL_TAKEOFFHOOK 6 /* Make it go off hook */ +#define AST_CONTROL_OFFHOOK 7 /* Line is off hook */ +#define AST_CONTROL_CONGESTION 8 /* Congestion (circuits busy) */ +#define AST_CONTROL_FLASH 9 /* Flash hook */ +#define AST_CONTROL_WINK 10 /* Wink */ +#define AST_CONTROL_OPTION 11 /* Set an option */ + +#define AST_FRIENDLY_OFFSET 64 /* Reserved header space */ + +struct ast_frame { + /*! Kind of frame */ + int frametype; + /*! Subclass, frame dependent */ + int subclass; + /*! Length of data */ + int datalen; + /*! Number of 8khz samples in this frame */ + int samples; + /*! Was the data malloc'd? i.e. should we free it when we discard the f +rame? */ + int mallocd; + /*! How far into "data" the data really starts */ + int offset; + /*! Optional source of frame for debugging */ + char *src; + /*! Pointer to actual data */ + void *data; + /*! Next/Prev for linking stand alone frames */ + struct ast_frame *prev; + /*! Next/Prev for linking stand alone frames */ + struct ast_frame *next; + /* Unused except + if debugging is turned on, but left + in the struct + so that it can be turned on without + requiring a r +ecompile of the whole thing */ +}; + + + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + + +#endif Added: freeswitch/trunk/src/mod/endpoints/mod_iax/iax-client.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/iax-client.h Thu May 8 19:25:36 2008 @@ -0,0 +1,244 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Implementation of Inter-Asterisk eXchange + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU Lesser General Public License (LGPL) + */ + +#ifndef _ASTERISK_IAX_CLIENT_H +#define _ASTERISK_IAX_CLIENT_H + +#ifdef WIN32_TIME_GET_TIME +#include <Mmsystem.h> +#endif + +#if defined(_MSC_VER) +/* disable zero-sized array in struct/union warning */ +#pragma warning(disable:4200) +#endif + +#if !defined(LINUX) && !defined(NETBSD) +#define socklen_t int +#endif + +#include "frame.h" +#include "iax2.h" +#include "iax2-parser.h" + +#define MAXSTRLEN 80 + +#define IAX_AUTHMETHOD_PLAINTEXT IAX_AUTH_PLAINTEXT +#define IAX_AUTHMETHOD_MD5 IAX_AUTH_MD5 + +extern char iax_errstr[]; + +struct iax_session; + + +#define IAX_EVENT_CONNECT 0 /* Connect a new call */ +#define IAX_EVENT_ACCEPT 1 /* Accept a call */ +#define IAX_EVENT_HANGUP 2 /* Hang up a call */ +#define IAX_EVENT_REJECT 3 /* Rejected call */ +#define IAX_EVENT_VOICE 4 /* Voice Data */ +#define IAX_EVENT_DTMF 5 /* A DTMF Tone */ +#define IAX_EVENT_TIMEOUT 6 /* Connection timeout... session will be + a pointer to free()'d memory! */ +#define IAX_EVENT_LAGRQ 7 /* Lag request -- Internal use only */ +#define IAX_EVENT_LAGRP 8 /* Lag Measurement. See event.lag */ +#define IAX_EVENT_RINGA 9 /* Announce we/they are ringing */ +#define IAX_EVENT_PING 10 /* Ping -- internal use only */ +#define IAX_EVENT_PONG 11 /* Pong -- internal use only */ +#define IAX_EVENT_BUSY 12 /* Report a line busy */ +#define IAX_EVENT_ANSWER 13 /* Answer the line */ + +#define IAX_EVENT_IMAGE 14 /* Send/Receive an image */ +#define IAX_EVENT_AUTHRQ 15 /* Authentication request */ +#define IAX_EVENT_AUTHRP 16 /* Authentication reply */ + +#define IAX_EVENT_REGREQ 17 /* Registration request */ +#define IAX_EVENT_REGACK 18 /* Registration reply */ +#define IAX_EVENT_URL 19 /* URL received */ +#define IAX_EVENT_LDCOMPLETE 20 /* URL loading complete */ + +#define IAX_EVENT_TRANSFER 21 /* Transfer has taken place */ + +#define IAX_EVENT_DPREQ 22 /* Dialplan request */ +#define IAX_EVENT_DPREP 23 /* Dialplan reply */ +#define IAX_EVENT_DIAL 24 /* Dial on a TBD call */ + +#define IAX_EVENT_QUELCH 25 /* Quelch Audio */ +#define IAX_EVENT_UNQUELCH 26 /* Unquelch Audio */ + +#define IAX_EVENT_UNLINK 27 /* Unlink */ +#define IAX_EVENT_LINKREJECT 28 /* Link Rejection */ +#define IAX_EVENT_TEXT 29 /* Text Frame :-) */ +#define IAX_EVENT_REGREJ 30 /* Registration reply */ +#define IAX_EVENT_LINKURL 31 /* Unlink */ +#define IAX_EVENT_CNG 32 /* Comfort-noise (almost silence) */ +#define IAX_EVENT_POKE 33 + +/* moved from iax.c to support attended transfer */ +#define IAX_EVENT_REREQUEST 999 +#define IAX_EVENT_TXREPLY 1000 +#define IAX_EVENT_TXREJECT 1001 +#define IAX_EVENT_TXACCEPT 1002 +#define IAX_EVENT_TXREADY 1003 + +#define IAX_SCHEDULE_FUZZ 0 /* ms of fuzz to drop */ + +#ifdef WIN32 +#if defined(_MSC_VER) +typedef int (__stdcall *sendto_t)(SOCKET, const char *, int, int, const struct sockaddr *, int); +typedef int (__stdcall *recvfrom_t)(SOCKET, char *, int, int, struct sockaddr *, int *); +#else +typedef int PASCAL (*sendto_t)(SOCKET, const char *, int, int, const struct sockaddr *, int); +typedef int PASCAL (*recvfrom_t)(SOCKET, char *buf, int len, int flags, struct sockaddr *from, int *fromlen); +#endif +#else +typedef int (*sendto_t)(int, const void *, size_t, int, const struct sockaddr *, socklen_t); +typedef int (*recvfrom_t)(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); +#endif + +struct iax_event { + int etype; /* Type of event */ + int subclass; /* Subclass data (event specific) */ + time_in_ms_t ts; /* Timestamp */ + struct iax_session *session; /* Applicable session */ + int datalen; /* Length of raw data */ + struct iax_ies ies; /* IE's for IAX2 frames */ + unsigned char data[]; /* Raw data if applicable */ +}; + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* All functions return 0 on success and -1 on failure unless otherwise + specified */ + +/* Called to initialize IAX structures and sockets. Returns actual + portnumber (which it will try preferred portno first, but if not + take what it can get */ +extern int iax_init(char *ip, int preferredportno); + +extern int iax_shutdown(void); + +/* Get filedescriptor for IAX to use with select or gtk_input_add */ +extern int iax_get_fd(void); + +/* Find out how many milliseconds until the next scheduled event */ +extern time_in_ms_t iax_time_to_next_event(void); + +/* Generate a new IAX session */ +extern struct iax_session *iax_session_new(void); + +/* Return exactly one iax event (if there is one pending). If blocking is + non-zero, IAX will block until some event is received */ +extern struct iax_event *iax_get_event(int blocking); + + +extern int iax_auth_reply(struct iax_session *session, char *password, + char *challenge, int methods); + +/* Free an event */ +extern void iax_event_free(struct iax_event *event); + +struct sockaddr_in; + +/* Front ends for sending events */ +extern int iax_send_dtmf(struct iax_session *session, char digit); +extern int iax_send_voice(struct iax_session *session, int format, unsigned char *data, int datalen, int samples); +extern int iax_send_cng(struct iax_session *session, int level, unsigned char *data, int datalen); +extern int iax_send_image(struct iax_session *session, int format, unsigned char *data, int datalen); +extern int iax_send_url(struct iax_session *session, char *url, int link); +extern int iax_send_text(struct iax_session *session, char *text); +extern int iax_send_ping(struct iax_session *session); +extern int iax_load_complete(struct iax_session *session); +extern int iax_reject(struct iax_session *session, const char *reason); +int iax_reject_registration(struct iax_session *session, char *reason); +int iax_ack_registration(struct iax_session *session); +int iax_auth_registration(struct iax_session *session); +extern int iax_busy(struct iax_session *session); +extern int iax_congestion(struct iax_session *session); +extern int iax_hangup(struct iax_session *session, char *byemsg); +extern int iax_call(struct iax_session *session, const char *cidnum, const char *cidname, char *ich, char *lang, int wait, int format, int capability); +extern int iax_accept(struct iax_session *session, int format); +extern int iax_answer(struct iax_session *session); +extern int iax_sendurl(struct iax_session *session, char *url); +extern int iax_send_unlink(struct iax_session *session); +extern int iax_send_link_reject(struct iax_session *session); +extern int iax_ring_announce(struct iax_session *session); +extern struct sockaddr_in iax_get_peer_addr(struct iax_session *session); +extern int iax_register(struct iax_session *session, char *hostname, char *peer, char *secret, int refresh); +extern int iax_lag_request(struct iax_session *session); +extern int iax_dial(struct iax_session *session, char *number); /* Dial on a TBD call */ +extern int iax_dialplan_request(struct iax_session *session, char *number); /* Request dialplan status for number */ +extern int iax_quelch(struct iax_session *session); +extern int iax_unquelch(struct iax_session * session); +extern int iax_transfer(struct iax_session *session, char *number); +extern int iax_quelch_moh(struct iax_session *session, int MOH); + +extern void iax_destroy(struct iax_session * session); + +extern void iax_enable_debug(void); +extern void iax_disable_debug(void); + +/* For attended transfer, application create a new session, + * make a call on the new session. + * On answer of the new session, call iax_setup_transfer and wait for + * IAX_EVENT_TXREADY when both sides are completed succefully or + * IAX_EVENT_TXREJECT for either side. + * If there are music on hold the it will be stopped by this library. + */ +extern int iax_setup_transfer(struct iax_session *s0, struct iax_session *s1); + +struct iax_netstat { + time_in_ms_t jitter; + int losspct; + int losscnt; + int packets; + time_in_ms_t delay; + int dropped; + int ooo; +}; +/* fills in rtt, and an iax_netstat structure for each of local/remote directions of call */ +extern int iax_get_netstats(struct iax_session *s, time_in_ms_t *rtt, struct iax_netstat *local, struct iax_netstat *remote); + + +extern void iax_set_private(struct iax_session *s, void *pvt); +extern void *iax_get_private(struct iax_session *s); +extern void iax_set_sendto(struct iax_session *s, sendto_t sendto); + +/* to use application networking instead of internal, set call this instead of iax_init, + * and pass in sendto and recvfrom replacements. blocking reads may not be implemented */ +extern void iax_set_networking(sendto_t st, recvfrom_t rf); + +/* destroy an iax session */ +extern void iax_session_destroy(struct iax_session **session); + +/* Handle externally received frames */ +struct iax_event *iax_net_process(unsigned char *buf, int len, struct sockaddr_in *sin); +extern unsigned int iax_session_get_capability(struct iax_session *s); +extern char iax_pref_codec_add(struct iax_session *session, unsigned int format); +extern void iax_pref_codec_del(struct iax_session *session, unsigned int format); +extern int iax_pref_codec_get(struct iax_session *session, unsigned int *array, int len); + +/* Fine tune jitterbuffer */ +extern void iax_set_jb_target_extra( long value ); + +extern char *iax_get_peer_ip(struct iax_session *session); +extern char *iax_event_get_apparent_ip(struct iax_event *event); +extern void iax_set_samplerate(struct iax_session *session, unsigned short samplemask); + +#if defined(__cplusplus) +} +#endif + +#endif /* _ASTERISK_IAX_CLIENT_H */ Added: freeswitch/trunk/src/mod/endpoints/mod_iax/iax-mutex.c ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/iax-mutex.c Thu May 8 19:25:36 2008 @@ -0,0 +1,124 @@ +/* + * Simple Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + +#define _XOPEN_SOURCE 600 +#include <stdlib.h> +#include "iax-mutex.h" + + +#ifdef WIN32 +#define _WIN32_WINNT 0x0400 +#include <windows.h> +struct mutex { + CRITICAL_SECTION mutex; +}; + +#else + +#include <pthread.h> +struct mutex { + pthread_mutex_t mutex; +}; + +#endif + + +mutex_status_t iax_mutex_create(mutex_t **mutex) +{ + mutex_status_t status = MUTEX_FAILURE; +#ifndef WIN32 + pthread_mutexattr_t attr; +#endif + mutex_t *check = NULL; + + check = (mutex_t *)malloc(sizeof(**mutex)); + if (!check) + goto done; +#ifdef WIN32 + InitializeCriticalSection(&check->mutex); +#else + if (pthread_mutexattr_init(&attr)) + goto done; + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) + goto fail; + + if (pthread_mutex_init(&check->mutex, &attr)) + goto fail; + + goto success; + +fail: + pthread_mutexattr_destroy(&attr); + goto done; + +success: +#endif + *mutex = check; + status = MUTEX_SUCCESS; + +done: + return status; +} + +mutex_status_t iax_mutex_destroy(mutex_t *mutex) +{ +#ifdef WIN32 + DeleteCriticalSection(&mutex->mutex); +#else + if (pthread_mutex_destroy(&mutex->mutex)) + return MUTEX_FAILURE; +#endif + free(mutex); + return MUTEX_SUCCESS; +} + +mutex_status_t iax_mutex_lock(mutex_t *mutex) +{ +#ifdef WIN32 + EnterCriticalSection(&mutex->mutex); +#else + if (pthread_mutex_lock(&mutex->mutex)) + return MUTEX_FAILURE; +#endif + return MUTEX_SUCCESS; +} + +mutex_status_t iax_mutex_trylock(mutex_t *mutex) +{ +#ifdef WIN32 + if (!TryEnterCriticalSection(&mutex->mutex)) + return MUTEX_FAILURE; +#else + if (pthread_mutex_trylock(&mutex->mutex)) + return MUTEX_FAILURE; +#endif + return MUTEX_SUCCESS; +} + +mutex_status_t iax_mutex_unlock(mutex_t *mutex) +{ +#ifdef WIN32 + LeaveCriticalSection(&mutex->mutex); +#else + if (pthread_mutex_unlock(&mutex->mutex)) + return MUTEX_FAILURE; +#endif + return MUTEX_SUCCESS; +} Added: freeswitch/trunk/src/mod/endpoints/mod_iax/iax-mutex.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/iax-mutex.h Thu May 8 19:25:36 2008 @@ -0,0 +1,37 @@ +/* + * Simple Mutex abstraction + * Copyright(C) 2007 Michael Jerris + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so. + * + * This work is provided under this license on an "as is" basis, without warranty of any kind, + * either expressed or implied, including, without limitation, warranties that the covered code + * is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire + * risk as to the quality and performance of the covered code is with you. Should any covered + * code prove defective in any respect, you (not the initial developer or any other contributor) + * assume the cost of any necessary servicing, repair or correction. This disclaimer of warranty + * constitutes an essential part of this license. No use of any covered code is authorized hereunder + * except under this disclaimer. + * + */ + + +#ifndef _SIMPLE_ABSTRACT_MUTEX_H +#define _SIMPLE_ABSTRACT_MUTEX_H + +typedef struct mutex mutex_t; + +typedef enum mutex_status { + MUTEX_SUCCESS, + MUTEX_FAILURE +} mutex_status_t; + +mutex_status_t iax_mutex_create(mutex_t **mutex); +mutex_status_t iax_mutex_destroy(mutex_t *mutex); +mutex_status_t iax_mutex_lock(mutex_t *mutex); +mutex_status_t iax_mutex_trylock(mutex_t *mutex); +mutex_status_t iax_mutex_unlock(mutex_t *mutex); + +#endif Added: freeswitch/trunk/src/mod/endpoints/mod_iax/iax.c ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/iax.c Thu May 8 19:25:36 2008 @@ -0,0 +1,3366 @@ + /* + * libiax: An implementation of Inter-Asterisk eXchange + * + * Copyright (C) 2001, Linux Support Services, Inc. + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU Lesser (Library) General Public License + */ + +#ifdef WIN32 +#undef __STRICT_ANSI__ //for strdup with ms + +#include <string.h> +#include <process.h> +#include <windows.h> +#include <winsock.h> +#include <time.h> +#include <stdlib.h> +#include <malloc.h> +#include <stdarg.h> +#include <stdio.h> +#include <fcntl.h> +#include <io.h> +#include <errno.h> +#include <limits.h> + + +#define snprintf _snprintf + +#if defined(_MSC_VER) +#define close _close +#define inline __inline +#define strdup _strdup +#endif + +void gettimeofday(struct timeval *tv, void /*struct timezone*/ *tz); +#include "winpoop.h" + +#else + +#define _BSD_SOURCE 1 +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/select.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <time.h> + +#endif + +#include "iax2.h" +#include "iax-client.h" +#include "md5.h" + +#ifdef NEWJB +#include "jitterbuf.h" +#endif +#include "iax-mutex.h" +/* + work around jitter-buffer shrinking in asterisk: + channels/chan_iax2.c:schedule_delivery() shrinks jitter buffer by 2. + this causes frames timestamped 1ms apart to ( sometimes ) be delivered + out of order, and results in garbled audio. our temporary fix is to increase + the minimum number of ( timestamped ) milliseconds between frames to 3 ( 2 + 1 ). +*/ +#define IAX_MIN_TIMESTAMP_INCREMENT 3 + +/* Define socket options for IAX2 sockets, based on platform + * availability of flags */ +#ifdef WIN32 + #define IAX_SOCKOPTS 0 +#else + #ifdef LINUX + #define IAX_SOCKOPTS MSG_DONTWAIT | MSG_NOSIGNAL + #else + #define IAX_SOCKOPTS MSG_DONTWAIT + #endif +#endif + + + +#ifdef SNOM_HACK +/* The snom phone seems to improperly execute memset in some cases */ +#include "../../snom_phonecore2/include/snom_memset.h" +#endif + +/* Voice TS Prediction causes libiax2 to clean up the timestamps on + * outgoing frames. It works best with either continuous voice, or + * callers who call iax_send_cng to indicate DTX for silence */ +#define USE_VOICE_TS_PREDICTION + +/* Define Voice Smoothing to try to make some judgements and adjust timestamps + on incoming packets to what they "ought to be" */ + +#define VOICE_SMOOTHING +#undef VOICE_SMOOTHING + +/* Define Drop Whole Frames to make IAX shrink its jitter buffer by dropping entire + frames rather than simply delivering them faster. Dropping encoded frames, + before they're decoded, usually leads to better results than dropping + decoded frames. */ + +#define DROP_WHOLE_FRAMES + +#define MIN_RETRY_TIME 10 +#define MAX_RETRY_TIME 4000 +#define MEMORY_SIZE 1000 + +#define TRANSFER_NONE 0 +#define TRANSFER_BEGIN 1 +#define TRANSFER_READY 2 +#define TRANSFER_REL 3 + +#ifndef NEWJB +/* No more than 4 seconds of jitter buffer */ +static int max_jitterbuffer = 4000; +/* No more than 50 extra milliseconds of jitterbuffer than needed */ +static int max_extra_jitterbuffer = 50; +/* To use or not to use the jitterbuffer */ +static int iax_use_jitterbuffer = 1; + +/* Dropcount (in per-MEMORY_SIZE) usually percent */ +static int iax_dropcount = 3; +#endif + +/* UDP Socket (file descriptor) */ +static int netfd = -1; + +/* Max timeouts */ +static int maxretries = 10; + +/* configurable jitterbuffer options */ +static long jb_target_extra = -1; + +static int do_shutdown = 0; + +/* external global networking replacements */ +static sendto_t iax_sendto = (sendto_t) sendto; +static recvfrom_t iax_recvfrom = (recvfrom_t) recvfrom; + +/* ping interval (seconds) */ +static int ping_time = 10; +static void send_ping(void *session); + +struct iax_session { + /* Private data */ + void *pvt; + /* session-local Sendto function */ + sendto_t sendto; + /* Is voice quelched (e.g. hold) */ + int quelch; + /* Codec Pref Order */ + char codec_order[32]; + /* Codec Pref Order Index*/ + int codec_order_len; + /* Last received voice format */ + int voiceformat; + /* Last transmitted voice format */ + int svoiceformat; + /* Per session capability */ + int capability; + /* Last received timestamp */ + time_in_ms_t last_ts; + /* Last transmitted timestamp */ + time_in_ms_t lastsent; + /* Last transmitted voice timestamp */ + unsigned int lastvoicets; + /* Next predicted voice ts */ + time_in_ms_t nextpred; + /* True if the last voice we transmitted was not silence/CNG */ + int notsilenttx; + /* Our last measured ping time */ + time_in_ms_t pingtime; + /* Address of peer */ + struct sockaddr_in peeraddr; + /* Our call number */ + int callno; + /* Peer's call number */ + int peercallno; + /* Our next outgoing sequence number */ + unsigned char oseqno; + /* Next sequence number they have not yet acknowledged */ + unsigned char rseqno; + /* Our last received incoming sequence number */ + unsigned char iseqno; + /* Last acknowledged sequence number */ + unsigned char aseqno; + /* Peer supported formats */ + int peerformats; + /* Time value that we base our transmission on */ + time_in_ms_t offset; + /* Time value we base our delivery on */ + time_in_ms_t rxcore; + /* History of lags */ + int history[MEMORY_SIZE]; + /* Current base jitterbuffer */ + int jitterbuffer; + /* Informational jitter */ + int jitter; + /* Measured lag */ + int lag; + /* Current link state */ + int state; + /* Peer name */ + char peer[MAXSTRLEN]; + /* Default Context */ + char context[MAXSTRLEN]; + /* Caller ID if available */ + char callerid[MAXSTRLEN]; + /* DNID */ + char dnid[MAXSTRLEN]; + /* Requested Extension */ + char exten[MAXSTRLEN]; + /* Expected Username */ + char username[MAXSTRLEN]; + /* Expected Secret */ + char secret[MAXSTRLEN]; + /* permitted authentication methods */ + char methods[MAXSTRLEN]; + /* MD5 challenge */ + char challenge[12]; +#ifdef VOICE_SMOOTHING + unsigned int lastts; +#endif + /* Refresh if applicable */ + int refresh; + + /* ping scheduler id */ + int pingid; + + /* Transfer stuff */ + struct sockaddr_in transfer; + int transferring; + int transfercallno; + int transferid; + int transferpeer; /* for attended transfer */ + int transfer_moh; /* for music on hold while performing attended transfer */ + +#ifdef NEWJB + jitterbuf *jb; +#endif + struct iax_netstat remote_netstats; + + unsigned short samplemask; + /* For linking if there are multiple connections */ + struct iax_session *next; +}; + +char iax_errstr[256]; + +static void destroy_session(struct iax_session *session); + +#define IAXERROR snprintf(iax_errstr, sizeof(iax_errstr), + +#ifdef DEBUG_SUPPORT + +#ifdef DEBUG_DEFAULT +static int debug = 1; +#else +static int debug = 0; +#endif + +void iax_enable_debug(void) +{ + debug = 1; +} + +void iax_disable_debug(void) +{ + debug = 0; +} + +/* This is a little strange, but to debug you call DEBU(G "Hello World!\n"); */ +#ifdef WIN32 +#define G __FILE__, __LINE__, __FUNCTION__, +#else +#define G __FILE__, __LINE__, (const char *)__func__, +#endif + +#define DEBU __debug +static int __debug(char *file, int lineno, const char *func, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (debug) { + fprintf(stderr, "%s line %d in %s: ", file, lineno, func); + vfprintf(stderr, fmt, args); + } + va_end(args); + return 0; +} + +#else /* No debug support */ + +#ifdef WIN32 +#define DEBU +#else +#define DEBU(...) (void)(0) +#endif +#define G +#endif + +typedef void (*sched_func)(void *); + +struct iax_sched { + /* These are scheduled things to be delivered */ + time_in_ms_t when; + /* If event is non-NULL then we're delivering an event */ + struct iax_event *event; + /* If frame is non-NULL then we're transmitting a frame */ + struct iax_frame *frame; + /* If func is non-NULL then we should call it */ + sched_func func; + /* and pass it this argument */ + void *arg; + /* Easy linking */ + struct iax_sched *next; +}; + +static mutex_t *sched_mutex = NULL; +static mutex_t *session_mutex = NULL; +static struct iax_sched *schedq = NULL; +static struct iax_session *sessions = NULL; +static int callnums = 1; +static int transfer_id = 1; /* for attended transfer */ +static int time_init = 0; + +static void init_time(void) +{ +#ifdef WIN32_TIME_GET_TIME + timeBeginPeriod(1); +#endif + time_init = 1; +} + +static void time_end(void) +{ +#ifdef WIN32_TIME_GET_TIME + timeEndPeriod(1); +#endif + time_init = 0; +} + +static time_in_ms_t current_time_in_ms(void) +{ +#ifdef WIN32_TIME_GET_TIME + return timeGetTime(); +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return ((tv.tv_sec * 1000) + (tv.tv_usec / 1000)); +#endif +} + +void iax_set_private(struct iax_session *s, void *ptr) +{ + s->pvt = ptr; +} + +void *iax_get_private(struct iax_session *s) +{ + return s->pvt; +} + +void iax_set_sendto(struct iax_session *s, sendto_t ptr) +{ + s->sendto = ptr; +} + + +unsigned int iax_session_get_capability(struct iax_session *s) +{ + return s->capability; +} + + +static int inaddrcmp(struct sockaddr_in *sin1, struct sockaddr_in *sin2) +{ + return (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) || (sin1->sin_port != sin2->sin_port); +} + +static int iax_sched_add(struct iax_event *event, struct iax_frame *frame, sched_func func, void *arg, time_in_ms_t ms) +{ + + /* Schedule event to be delivered to the client + in ms milliseconds from now, or a reliable frame to be retransmitted */ + struct iax_sched *sched, *cur, *prev = NULL; + + if (!event && !frame && !func) { + DEBU(G "No event, no frame, no func? what are we scheduling?\n"); + return -1; + } + + + sched = (struct iax_sched*)malloc(sizeof(struct iax_sched)); + if (sched) { + memset(sched, 0, sizeof(struct iax_sched)); + sched->when = current_time_in_ms() + ms; + sched->event = event; + sched->frame = frame; + sched->func = func; + sched->arg = arg; + /* Put it in the list, in order */ + iax_mutex_lock(sched_mutex); + cur = schedq; + while(cur && cur->when <= sched->when) { + prev = cur; + cur = cur->next; + } + sched->next = cur; + if (prev) { + prev->next = sched; + } else { + schedq = sched; + } + iax_mutex_unlock(sched_mutex); + return 0; + } else { + DEBU(G "Out of memory!\n"); + return -1; + } +} + +static int iax_sched_del(struct iax_event *event, struct iax_frame *frame, sched_func func, void *arg, int all) +{ + struct iax_sched *cur, *tmp, *prev = NULL; + int ret = 0; + + iax_mutex_lock(sched_mutex); + cur = schedq; + while (cur) { + if (cur->event == event && cur->frame == frame && cur->func == func && cur->arg == arg) { + if (prev) + prev->next = cur->next; + else + schedq = cur->next; + tmp = cur; + cur = cur->next; + free(tmp); + if (!all) { + ret = -1; + goto done; + } + } else { + prev = cur; + cur = cur->next; + } + } + done: + iax_mutex_unlock(sched_mutex); + + return 0; + +} + + +time_in_ms_t iax_time_to_next_event(void) +{ + struct iax_sched *cur = NULL; + time_in_ms_t minimum = 999999999; + + iax_mutex_lock(sched_mutex); + cur = schedq; + + /* If there are no pending events, we don't need to timeout */ + if (!cur) { + iax_mutex_unlock(sched_mutex); + return -1; + } + while(cur) { + if (cur->when < minimum) { + minimum = cur->when; + } + cur = cur->next; + } + iax_mutex_unlock(sched_mutex); + + if (minimum <= 0) { + return -1; + } + + + + return minimum - current_time_in_ms(); +} + +struct iax_session *iax_session_new(void) +{ + struct iax_session *s; + s = (struct iax_session *)malloc(sizeof(struct iax_session)); + if (s) { + memset(s, 0, sizeof(struct iax_session)); + /* Initialize important fields */ + s->voiceformat = -1; + s->svoiceformat = -1; + /* Default pingtime to 30 ms */ + s->pingtime = 30; + /* XXX Not quite right -- make sure it's not in use, but that won't matter + unless you've had at least 65k calls. XXX */ + s->callno = callnums++; + if (callnums > 32767) + callnums = 1; + s->peercallno = 0; + s->transferpeer = 0; /* for attended transfer */ + + s->sendto = iax_sendto; + s->pingid = -1; +#ifdef NEWJB + s->jb = jb_new(); + { + jb_conf jbconf; + jbconf.max_jitterbuf = 0; + jbconf.resync_threshold = 1000; + jbconf.max_contig_interp = 0; + jbconf.target_extra = jb_target_extra; + jb_setconf(s->jb, &jbconf); + } +#endif + iax_mutex_lock(session_mutex); + s->next = sessions; + sessions = s; + iax_mutex_unlock(session_mutex); + } + return s; +} + +static int iax_session_valid(struct iax_session *session) +{ + /* Return -1 on a valid iax session pointer, 0 on a failure */ + struct iax_session *cur; + + iax_mutex_lock(session_mutex); + cur = sessions; + while(cur) { + if (session == cur) { + iax_mutex_unlock(session_mutex); + return -1; + } + cur = cur->next; + } + iax_mutex_unlock(session_mutex); + + return 0; +} + +int iax_get_netstats(struct iax_session *session, time_in_ms_t *rtt, struct iax_netstat *local, struct iax_netstat *remote) { + + if(!iax_session_valid(session)) return -1; + + *rtt = session->pingtime; + + *remote = session->remote_netstats; + +#ifdef NEWJB + { + jb_info stats; + jb_getinfo(session->jb, &stats); + + local->jitter = stats.jitter; + /* XXX: should be short-term loss pct.. */ + if(stats.frames_in == 0) stats.frames_in = 1; + local->losspct = stats.losspct/1000; + local->losscnt = stats.frames_lost; + local->packets = stats.frames_in; + local->delay = stats.current - stats.min; + local->dropped = stats.frames_dropped; + local->ooo = stats.frames_ooo; + } +#endif + return 0; +} + +static time_in_ms_t calc_timestamp(struct iax_session *session, time_in_ms_t ts, struct ast_frame *f) +{ + time_in_ms_t ms; + time_in_ms_t time_in_ms; + int voice = 0; + int genuine = 0; + + if (f && f->frametype == AST_FRAME_VOICE) { + voice = 1; + } else if (!f || f->frametype == AST_FRAME_IAX) { + genuine = 1; + } + + /* If this is the first packet we're sending, get our + offset now. */ + if (!session->offset) { + session->offset = current_time_in_ms(); + } + /* If the timestamp is specified, just use their specified + timestamp no matter what. Usually this is done for + special cases. */ + if (ts) { + return ts; + } + /* Otherwise calculate the timestamp from the current time */ + time_in_ms = current_time_in_ms(); + + + /* Calculate the number of milliseconds since we sent the first packet */ + ms = time_in_ms - session->offset; + + if (ms < 0) { + ms = 0; + } + + if(voice) { +#ifdef USE_VOICE_TS_PREDICTION + + /* If we haven't most recently sent silence, and we're + * close in time, use predicted time */ + if(session->notsilenttx && iax_abs(ms - session->nextpred) <= 240) { + /* Adjust our txcore, keeping voice and non-voice + * synchronized */ + session->offset += (int)(ms - session->nextpred)/10; + + if(!session->nextpred) { + session->nextpred = ms; + } + ms = session->nextpred; + } else { + /* in this case, just use the actual time, since + * we're either way off (shouldn't happen), or we're + * ending a silent period -- and seed the next predicted + * time. Also, round ms to the next multiple of + * frame size (so our silent periods are multiples + * of frame size too) */ + time_in_ms_t diff = ms % (f->samples / 8); + if(diff) + ms += f->samples/8 - diff; + session->nextpred = ms; + } +#else + if(ms <= session->lastsent) + ms = session->lastsent + 3; +#endif + session->notsilenttx = 1; + } else { + /* On a dataframe, use last value + 3 (to accomodate jitter buffer shrinking) + if appropriate unless it's a genuine frame */ + if (genuine) { + if (ms <= session->lastsent) + ms = session->lastsent + 3; + } else if (iax_abs(ms - session->lastsent) <= 240) { + ms = session->lastsent + 3; + } + + } + + /* Record the last sent packet for future reference */ + /* unless an AST_FRAME_IAX */ + if (!genuine) + session->lastsent = ms; + +#ifdef USE_VOICE_TS_PREDICTION + /* set next predicted ts based on 8khz samples */ + if(voice) + session->nextpred = session->nextpred + f->samples / 8; +#endif + + return ms; +} + +#ifdef NEWJB +static unsigned char get_n_bits_at(unsigned char *data, int n, int bit) +{ + int byte = bit / 8; /* byte containing first bit */ + int rem = 8 - (bit % 8); /* remaining bits in first byte */ + unsigned char ret = 0; + + if (n <= 0 || n > 8) + return 0; + + if (rem < n) { + ret = (data[byte] << (n - rem)); + ret |= (data[byte + 1] >> (8 - n + rem)); + } else { + ret = (data[byte] >> (rem - n)); + } + + return (ret & (0xff >> (8 - n))); +} + +static int speex_get_wb_sz_at(unsigned char *data, int len, int bit) +{ + static int SpeexWBSubModeSz[] = { + 0, 36, 112, 192, + 352, 0, 0, 0 }; + int off = bit; + unsigned char c; + + /* skip up to two wideband frames */ + if (((len * 8 - off) >= 5) && + get_n_bits_at(data, 1, off)) { + c = get_n_bits_at(data, 3, off + 1); + off += SpeexWBSubModeSz[c]; + + if (((len * 8 - off) >= 5) && + get_n_bits_at(data, 1, off)) { + c = get_n_bits_at(data, 3, off + 1); + off += SpeexWBSubModeSz[c]; + + if (((len * 8 - off) >= 5) && + get_n_bits_at(data, 1, off)) { + /* too many in a row */ + DEBU(G "\tCORRUPT too many wideband streams in a row\n"); + return -1; + } + } + + } + return off - bit; +} + +static int speex_get_samples(unsigned char *data, int len) +{ + static int SpeexSubModeSz[] = { + 0, 43, 119, 160, + 220, 300, 364, 492, + 79, 0, 0, 0, + 0, 0, 0, 0 }; + static int SpeexInBandSz[] = { + 1, 1, 4, 4, + 4, 4, 4, 4, + 8, 8, 16, 16, + 32, 32, 64, 64 }; + int bit = 0; + int cnt = 0; + int off = 0; + unsigned char c; + + DEBU(G "speex_get_samples(%d)\n", len); + while ((len * 8 - bit) >= 5) { + /* skip wideband frames */ + off = speex_get_wb_sz_at(data, len, bit); + if (off < 0) { + DEBU(G "\tERROR reading wideband frames\n"); + break; + } + bit += off; + + if ((len * 8 - bit) < 5) { + DEBU(G "\tERROR not enough bits left after wb\n"); + break; + } + + /* get control bits */ + c = get_n_bits_at(data, 5, bit); + DEBU(G "\tCONTROL: %d at %d\n", c, bit); + bit += 5; + + if (c == 15) { + DEBU(G "\tTERMINATOR\n"); + break; + } else if (c == 14) { + /* in-band signal; next 4 bits contain signal id */ + c = get_n_bits_at(data, 4, bit); + bit += 4; + DEBU(G "\tIN-BAND %d bits\n", SpeexInBandSz[c]); + bit += SpeexInBandSz[c]; + } else if (c == 13) { + /* user in-band; next 5 bits contain msg len */ + c = get_n_bits_at(data, 5, bit); + bit += 5; + DEBU(G "\tUSER-BAND %d bytes\n", c); + bit += c * 8; + } else if (c > 8) { + DEBU(G "\tUNKNOWN sub-mode %d\n", c); + break; + } else { + /* skip number bits for submode (less the 5 control bits) */ + DEBU(G "\tSUBMODE %d %d bits\n", c, SpeexSubModeSz[c]); + bit += SpeexSubModeSz[c] - 5; + + cnt += 160; /* new frame */ + } + } + DEBU(G "\tSAMPLES: %d\n", cnt); + return cnt; +} + +static inline int get_interp_len(int format) +{ + return (format == AST_FORMAT_ILBC) ? 30 : 20; +} + +static int get_sample_cnt(struct iax_event *e) +{ + int cnt = 0; + + /* + * In the case of zero length frames, do not return a cnt of 0 + */ + if ( e->datalen == 0 ) { + return get_interp_len( e->subclass ) * 8; + } + + switch (e->subclass) { + case AST_FORMAT_SPEEX: + cnt = speex_get_samples(e->data, e->datalen); + break; + case AST_FORMAT_G723_1: + cnt = 240; /* FIXME Not always the case */ + break; + case AST_FORMAT_ILBC: + cnt = 240 * (e->datalen / 50); + break; + case AST_FORMAT_GSM: + cnt = 160 * (e->datalen / 33); + break; + case AST_FORMAT_G729A: + cnt = 160 * (e->datalen / 20); + break; + case AST_FORMAT_SLINEAR: + cnt = e->datalen / 2; + break; + case AST_FORMAT_LPC10: + cnt = 22 * 8 + (((char *)(e->data))[7] & 0x1) * 8; + break; + case AST_FORMAT_ULAW: + case AST_FORMAT_ALAW: + cnt = e->datalen; + break; + case AST_FORMAT_ADPCM: + case AST_FORMAT_G726: + cnt = e->datalen * 2; + break; + default: + return 0; + } + return cnt; +} +#endif + +static int iax_xmit_frame(struct iax_frame *f) +{ + /* Send the frame raw */ +#ifdef DEBUG_SUPPORT + struct ast_iax2_full_hdr *h = (f->data); + if (ntohs(h->scallno) & IAX_FLAG_FULL) + iax_showframe(f, NULL, 0, f->transfer ? + &(f->session->transfer) : + &(f->session->peeraddr), f->datalen - sizeof(struct ast_iax2_full_hdr)); +#endif + + return f->session->sendto(netfd, (const char *) f->data, f->datalen, + IAX_SOCKOPTS, + f->transfer ? + (struct sockaddr *)&(f->session->transfer) : + (struct sockaddr *)&(f->session->peeraddr), sizeof(f->session->peeraddr)); +} + +static int iax_reliable_xmit(struct iax_frame *f) +{ + struct iax_frame *fc; + struct ast_iax2_full_hdr *fh; + fh = (struct ast_iax2_full_hdr *) f->data; + if (!fh->type) { + DEBU(G "Asked to reliably transmit a non-packet. Crashing.\n"); + *((char *)0)=0; + } + fc = (struct iax_frame *)malloc(sizeof(struct iax_frame)); + if (fc) { + /* Make a copy of the frame */ + memcpy(fc, f, sizeof(struct iax_frame)); + /* And a copy of the data if applicable */ + if (!fc->data || !fc->datalen) { + IAXERROR "No frame data?"); + DEBU(G "No frame data?\n"); + return -1; + } else { + fc->data = (char *)malloc(fc->datalen); + if (!fc->data) { + DEBU(G "Out of memory\n"); + IAXERROR "Out of memory\n"); + return -1; + } + memcpy(fc->data, f->data, f->datalen); + iax_sched_add(NULL, fc, NULL, NULL, fc->retrytime); + return iax_xmit_frame(fc); + } + } else + return -1; +} + +void iax_set_networking(sendto_t st, recvfrom_t rf) +{ + + init_time(); + iax_sendto = st; + iax_recvfrom = rf; +} + +int __iax_shutdown(void) +{ + struct iax_sched *sp, *fp; + + /* Hangup Calls */ + if (sessions) { + struct iax_session *sp = NULL, *fp = NULL; + iax_mutex_lock(session_mutex); + for(sp = sessions; sp ;) { + iax_hangup(sp, "System Shutdown"); + fp = sp; + sp = sp->next; + destroy_session(fp); + } + iax_mutex_unlock(session_mutex); + } + + /* Clear Scheduler */ + iax_mutex_lock(sched_mutex); + for(sp = schedq; sp ;) { + fp = sp; + sp = sp->next; + free(fp); + } + iax_mutex_unlock(sched_mutex); + + if (netfd > -1) { + shutdown(netfd, 2); + close(netfd); + } + + time_end(); + + iax_mutex_destroy(sched_mutex); + iax_mutex_destroy(session_mutex); + + return 0; +} + +int iax_shutdown(void) +{ + return do_shutdown++; +} + +void iax_set_jb_target_extra( long value ) +{ + /* store in jb_target_extra, a static global */ + jb_target_extra = value ; +} + +int iax_init(char *ip, int preferredportno) +{ + int portno = preferredportno; + struct sockaddr_in sin; + unsigned int sinlen; + int flags; + + init_time(); + + iax_mutex_create(&sched_mutex); + iax_mutex_create(&session_mutex); + + if(iax_recvfrom == (recvfrom_t) recvfrom) { + if (netfd > -1) { + /* Sokay, just don't do anything */ + DEBU(G "Already initialized."); + return 0; + } + netfd = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (netfd < 0) { + DEBU(G "Unable to allocate UDP socket\n"); + IAXERROR "Unable to allocate UDP socket\n"); + return -1; + } + + if (preferredportno == 0) preferredportno = IAX_DEFAULT_PORTNO; + if (preferredportno < 0) preferredportno = 0; + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = 0; + sin.sin_port = htons((short)preferredportno); + if (bind(netfd, (struct sockaddr *) &sin, sizeof(sin)) < 0) + { +#if defined(WIN32) || defined(_WIN32_WCE) + if (WSAGetLastError() == WSAEADDRINUSE) +#else + if (errno == EADDRINUSE) +#endif + { + /*the port is already in use, so bind to a free port chosen by the IP stack*/ + DEBU(G "Unable to bind to preferred port - port is in use. Trying to bind to a free one"); + sin.sin_port = htons((short)0); + if (bind(netfd, (struct sockaddr *) &sin, sizeof(sin)) < 0) + { + IAXERROR "Unable to bind UDP socket\n"); + return -1; + } + } else + { + IAXERROR "Unable to bind UDP socket\n"); + return -1; + } + } + sinlen = sizeof(sin); + if (getsockname(netfd, (struct sockaddr *) &sin, &sinlen) < 0) { + close(netfd); + netfd = -1; + DEBU(G "Unable to figure out what I'm bound to."); + IAXERROR "Unable to determine bound port number."); + } +#ifdef WIN32 + flags = 1; + if (ioctlsocket(netfd,FIONBIO,(unsigned long *) &flags)) { + closesocket(netfd); + netfd = -1; + DEBU(G "Unable to set non-blocking mode."); + IAXERROR "Unable to set non-blocking mode."); + } + +#else + if ((flags = fcntl(netfd, F_GETFL)) < 0) { + close(netfd); + netfd = -1; + DEBU(G "Unable to retrieve socket flags."); + IAXERROR "Unable to retrieve socket flags."); + } + if (fcntl(netfd, F_SETFL, flags | O_NONBLOCK) < 0) { + close(netfd); + netfd = -1; + DEBU(G "Unable to set non-blocking mode."); + IAXERROR "Unable to set non-blocking mode."); + } +#endif + portno = ntohs(sin.sin_port); + } + srand((unsigned int)time(NULL)); + callnums = rand() % 32767 + 1; + transfer_id = rand() % 32767 + 1; + DEBU(G "Started on port %d\n", portno); + return portno; +} + + + +static void convert_reply(char *out, unsigned char *in) +{ + int x; + for (x=0;x<16;x++) + out += sprintf(out, "%2.2x", (int)in[x]); +} + +static unsigned char compress_subclass(int subclass) +{ + int x; + int power=-1; + /* If it's 128 or smaller, just return it */ + if (subclass < IAX_FLAG_SC_LOG) + return subclass; + /* Otherwise find its power */ + for (x = 0; x < IAX_MAX_SHIFT; x++) { + if (subclass & (1 << x)) { + if (power > -1) { + DEBU(G "Can't compress subclass %d\n", subclass); + return 0; + } else + power = x; + } + } + return power | IAX_FLAG_SC_LOG; +} + +static int iax_send(struct iax_session *pvt, struct ast_frame *f, time_in_ms_t ts, int seqno, int now, int transfer, int final) +{ + /* Queue a packet for delivery on a given private structure. Use "ts" for + timestamp, or calculate if ts is 0. Send immediately without retransmission + or delayed, with retransmission */ + struct ast_iax2_full_hdr *fh; + struct ast_iax2_mini_hdr *mh; + unsigned char buf[5120]; + struct iax_frame *fr; + int res; + int sendmini=0; + time_in_ms_t lastsent; + time_in_ms_t fts; + + if (!pvt) { + IAXERROR "No private structure for packet?\n"); + return -1; + } + + /* this must come before the next call to calc_timestamp() since + calc_timestamp() will change lastsent to the returned value */ + lastsent = pvt->lastsent; + + /* Calculate actual timestamp */ + fts = calc_timestamp(pvt, ts, f); + + if (((fts & 0xFFFF0000L) == (lastsent & 0xFFFF0000L)) + /* High two bits are the same on timestamp, or sending on a trunk */ && + (f->frametype == AST_FRAME_VOICE) + /* is a voice frame */ && + (f->subclass == pvt->svoiceformat) + /* is the same type */ ) { + /* Force immediate rather than delayed transmission */ + now = 1; + /* Mark that mini-style frame is appropriate */ + sendmini = 1; + } + /* Allocate an iax_frame */ + if (now) { + fr = (struct iax_frame *) buf; + } else + fr = iax_frame_new(DIRECTION_OUTGRESS, f->datalen); + if (!fr) { + IAXERROR "Out of memory\n"); + return -1; + } + /* Copy our prospective frame into our immediate or retransmitted wrapper */ + iax_frame_wrap(fr, f); + + fr->ts = fts; + if (!fr->ts) { + IAXERROR "timestamp is 0?\n"); + if (!now) + iax_frame_free(fr); + return -1; + } + + fr->callno = pvt->callno; + fr->transfer = transfer; + fr->final = final; + fr->session = pvt; + if (!sendmini) { + /* We need a full frame */ + if (seqno > -1) + fr->oseqno = seqno; + else + fr->oseqno = pvt->oseqno++; + fr->iseqno = pvt->iseqno; + fh = (struct ast_iax2_full_hdr *)(((char *)fr->af.data) - sizeof(struct ast_iax2_full_hdr)); + fh->scallno = htons(fr->callno | IAX_FLAG_FULL); + fh->ts = htonl((long)(fr->ts)); + fh->oseqno = fr->oseqno; + if (transfer) { + fh->iseqno = 0; + } else + fh->iseqno = fr->iseqno; + /* Keep track of the last thing we've acknowledged */ + pvt->aseqno = fr->iseqno; + fh->type = fr->af.frametype & 0xFF; + fh->csub = compress_subclass(fr->af.subclass); + if (transfer) { + fr->dcallno = pvt->transfercallno; + } else + fr->dcallno = pvt->peercallno; + fh->dcallno = htons(fr->dcallno); + fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_full_hdr); + fr->data = fh; + fr->retries = maxretries; + /* Retry after 2x the ping time has passed */ + fr->retrytime = pvt->pingtime * 2; + if (fr->retrytime < MIN_RETRY_TIME) + fr->retrytime = MIN_RETRY_TIME; + if (fr->retrytime > MAX_RETRY_TIME) + fr->retrytime = MAX_RETRY_TIME; + /* Acks' don't get retried */ + if ((f->frametype == AST_FRAME_IAX) && (f->subclass == IAX_COMMAND_ACK)) + fr->retries = -1; + if (f->frametype == AST_FRAME_VOICE) { + pvt->svoiceformat = f->subclass; + } + if (now) { + res = iax_xmit_frame(fr); + } else + res = iax_reliable_xmit(fr); + } else { + /* Mini-frames have no sequence number */ + fr->oseqno = -1; + fr->iseqno = -1; + /* Mini frame will do */ + mh = (struct ast_iax2_mini_hdr *)(((char *)fr->af.data) - sizeof(struct ast_iax2_mini_hdr)); + mh->callno = htons(fr->callno); + mh->ts = htons((short)(fr->ts & 0xFFFF)); + fr->datalen = fr->af.datalen + sizeof(struct ast_iax2_mini_hdr); + fr->data = mh; + fr->retries = -1; + res = iax_xmit_frame(fr); + } + if( !now && fr!=NULL ) + iax_frame_free( fr ); + return res; +} + +#if 0 +static int iax_predestroy(struct iax_session *pvt) +{ + if (!pvt) { + return -1; + } + if (!pvt->alreadygone) { + /* No more pings or lagrq's */ + if (pvt->pingid > -1) + ast_sched_del(sched, pvt->pingid); + if (pvt->lagid > -1) + ast_sched_del(sched, pvt->lagid); + if (pvt->autoid > -1) + ast_sched_del(sched, pvt->autoid); + if (pvt->initid > -1) + ast_sched_del(sched, pvt->initid); + pvt->pingid = -1; + pvt->lagid = -1; + pvt->autoid = -1; + pvt->initid = -1; + pvt->alreadygone = 1; + } + return 0; +} +#endif + +static int __send_command(struct iax_session *i, char type, int command, time_in_ms_t ts, unsigned char *data, int datalen, int seqno, + int now, int transfer, int final, int samples) +{ + struct ast_frame f; + f.frametype = type; + f.subclass = command; + f.datalen = datalen; + f.samples = samples; + f.mallocd = 0; + f.offset = 0; +#ifdef __GNUC__ + f.src = (char *) __FUNCTION__; +#else + f.src = (char *) __FILE__; +#endif + f.data = data; + return iax_send(i, &f, ts, seqno, now, transfer, final); +} + +static int send_command(struct iax_session *i, char type, int command, time_in_ms_t ts, unsigned char *data, int datalen, int seqno) +{ + return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0, 0); +} + +static int send_command_final(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen, int seqno) +{ +#if 0 + /* It is assumed that the callno has already been locked */ + iax_predestroy(i); +#endif + int r; + r = __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1, 0); + if (r >= 0) { + iax_mutex_lock(session_mutex); + destroy_session(i); + iax_mutex_unlock(session_mutex); + } + return r; +} + +static int send_command_immediate(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen, int seqno) +{ + return __send_command(i, type, command, ts, data, datalen, seqno, 1, 0, 0, 0); +} + +static int send_command_transfer(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen) +{ + return __send_command(i, type, command, ts, data, datalen, 0, 0, 1, 0, 0); +} + +static int send_command_samples(struct iax_session *i, char type, int command, unsigned int ts, unsigned char *data, int datalen, int seqno, int samples) +{ + return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0, samples); +} + + +int iax_transfer(struct iax_session *session, char *number) +{ + static int res; //Return Code + struct iax_ie_data ied; //IE Data Structure (Stuff To Send) + + // Clear The Memory Used For IE Buffer + memset(&ied, 0, sizeof(ied)); + + // Copy The Transfer Destination Into The IE Structure + iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, (unsigned char *) number); + + // Send The Transfer Command - Asterisk Will Handle The Rest! + res = send_command(session, AST_FRAME_IAX, IAX_COMMAND_TRANSFER, 0, ied.buf, ied.pos, -1); + + // Return Success + return 0; +} + +static void stop_transfer(struct iax_session *session) +{ + struct iax_sched *sch; + + iax_mutex_lock(sched_mutex); + sch = schedq; + while(sch) { + if (sch->frame && (sch->frame->session == session)) + sch->frame->retries = -1; + sch = sch->next; + } + iax_mutex_unlock(sched_mutex); +} /* stop_transfer */ + +static void complete_transfer(struct iax_session *session, int peercallno, int xfr2peer, int preserveSeq) +{ + session->peercallno = peercallno; + /* Change from transfer to session now */ + if (xfr2peer) { + memcpy(&session->peeraddr, &session->transfer, sizeof(session->peeraddr)); + memset(&session->transfer, 0, sizeof(session->transfer)); + session->transferring = TRANSFER_NONE; + session->transferpeer = 0; + session->transfer_moh = 0; + /* Force retransmission of a real voice packet, and reset all timing */ + session->svoiceformat = -1; + session->voiceformat = 0; + } + session->rxcore = session->offset = 0; + memset(&session->history, 0, sizeof(session->history)); +#ifdef NEWJB + { /* Reset jitterbuffer */ + jb_frame frame; + while(jb_getall(session->jb,&frame) == JB_OK) + iax_event_free(frame.data); + + jb_reset(session->jb); + } +#endif + session->jitterbuffer = 0; + session->jitter = 0; + session->lag = 0; + + if (! preserveSeq) + { + /* Reset sequence numbers */ + session->aseqno = 0; + session->oseqno = 0; + session->iseqno = 0; + } + + session->lastsent = 0; + session->last_ts = 0; + session->lastvoicets = 0; + session->pingtime = 30; + /* We have to dump anything we were going to (re)transmit now that we've been + transferred since they're all invalid and for the old host. */ + stop_transfer(session); +} /* complete_transfer */ + +int iax_setup_transfer(struct iax_session *org_session, struct iax_session *new_session) +{ + int res; + struct iax_ie_data ied0; + struct iax_ie_data ied1; + + struct iax_session *s0 = org_session; + struct iax_session *s1 = new_session; + + memset(&ied0, 0, sizeof(ied0)); + memset(&ied1, 0, sizeof(ied1)); + + /* reversed setup */ + iax_ie_append_addr(&ied0, IAX_IE_APPARENT_ADDR, &s1->peeraddr); + iax_ie_append_short(&ied0, IAX_IE_CALLNO, s1->peercallno); + iax_ie_append_int(&ied0, IAX_IE_TRANSFERID, transfer_id); + + iax_ie_append_addr(&ied1, IAX_IE_APPARENT_ADDR, &s0->peeraddr); + iax_ie_append_short(&ied1, IAX_IE_CALLNO, s0->peercallno); + iax_ie_append_int(&ied1, IAX_IE_TRANSFERID, transfer_id); + + s0->transfer = s1->peeraddr; + s1->transfer = s0->peeraddr; + + s0->transferid = transfer_id; + s1->transferid = transfer_id; + + s0->transfercallno = s0->peercallno; + s1->transfercallno = s1->peercallno; + + s0->transferring = TRANSFER_BEGIN; + s1->transferring = TRANSFER_BEGIN; + + s0->transferpeer = s1->callno; + s1->transferpeer = s0->callno; + + transfer_id++; + + if (transfer_id > 32767) + transfer_id = 1; + + res = send_command(s0, AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied0.buf, ied0.pos, -1); + if (res < 0) { + return -1; + } + + res = send_command(s1, AST_FRAME_IAX, IAX_COMMAND_TXREQ, 0, ied1.buf, ied1.pos, -1); + if (res < 0) { + return -1; + } + + return 0; +} + +static int iax_finish_transfer(struct iax_session *s, short new_peer) +{ + int res; + struct iax_ie_data ied; + + memset(&ied, 0, sizeof(ied)); + + iax_ie_append_short(&ied, IAX_IE_CALLNO, new_peer); + + res = send_command(s, AST_FRAME_IAX, IAX_COMMAND_TXREL, 0, ied.buf, ied.pos, -1); + + complete_transfer(s, new_peer, 0, 1); + + return res; + +} + +static struct iax_session *iax_find_session2(short callno) +{ + struct iax_session *cur; + + iax_mutex_lock(session_mutex); + cur = sessions; + while(cur) { + if (callno == cur->callno && callno != 0) { + iax_mutex_unlock(session_mutex); + return cur; + } + cur = cur->next; + } + iax_mutex_unlock(session_mutex); + + return NULL; +} + +static int iax_handle_txready(struct iax_session *s) +{ + struct iax_session *s0, *s1; + short s0_org_peer, s1_org_peer; + + if (s->transfer_moh) { + s->transfer_moh = 0; + iax_unquelch(s); + } + + complete_transfer(s, s->peercallno, 0, 1); + + s->transferring = TRANSFER_REL; + + s0 = s; + s1 = iax_find_session2(s0->transferpeer); + + if (s1 != NULL && + s1->callno == s0->transferpeer && + s0->transferring == TRANSFER_REL && + s1->transferring == TRANSFER_REL) { + + s0_org_peer = s0->peercallno; + s1_org_peer = s1->peercallno; + + iax_finish_transfer(s0, s1_org_peer); + iax_finish_transfer(s1, s0_org_peer); + return 1; + } + + return 0; +} + +static void iax_handle_txreject(struct iax_session *s) +{ + struct iax_session *s0, *s1; + + s0 = s; + s1 = iax_find_session2(s0->transferpeer); + if (s1 != NULL && + s0->transferpeer == s1->callno && + s1->transferring) { + if (s1->transfer_moh) { + s1->transfer_moh = 0; + send_command_immediate(s1, AST_FRAME_IAX, IAX_COMMAND_UNQUELCH, 0, NULL, 0, s1->iseqno); + } + } + if (s0->transfer_moh) { + s0->transfer_moh = 0; + send_command_immediate(s0, AST_FRAME_IAX, IAX_COMMAND_UNQUELCH, 0, NULL, 0, s0->iseqno); + } + + memset(&s->transfer, 0, sizeof(s->transfer)); + s->transferring = TRANSFER_NONE; + s->transferpeer = 0; + s->transfer_moh = 0; +} + +static void destroy_session(struct iax_session *session) +{ + struct iax_session *cur, *prev=NULL; + struct iax_sched *curs, *prevs=NULL, *nexts=NULL; + int loop_cnt=0; + + iax_mutex_lock(sched_mutex); + curs = schedq; + while(curs) { + nexts = curs->next; + if (curs->frame && curs->frame->session == session) { + /* Just mark these frames as if they've been sent */ + curs->frame->retries = -1; + } else if (curs->event && curs->event->session == session) { + if (prevs) + prevs->next = nexts; + else + schedq = nexts; + if (curs->event) + iax_event_free(curs->event); + free(curs); + } else { + prevs = curs; + } + curs = nexts; + loop_cnt++; + } + iax_mutex_unlock(sched_mutex); + + + cur = sessions; + while(cur) { + if (cur == session) { + if (prev) + prev->next = session->next; + else + sessions = session->next; +#ifdef NEWJB + { + jb_frame frame; + while(jb_getall(session->jb,&frame) == JB_OK) + iax_event_free(frame.data); + + jb_destroy(session->jb); + } +#endif + free(session); + return; + } + prev = cur; + cur = cur->next; + } +} + +static int iax_send_lagrp(struct iax_session *session, time_in_ms_t ts); +static int iax_send_pong(struct iax_session *session, time_in_ms_t ts); + +static struct iax_event *handle_event(struct iax_event *event) +{ + /* We have a candidate event to be delievered. Be sure + the session still exists. */ + if (event) { + if (iax_session_valid(event->session)) { + /* Lag requests are never actually sent to the client, but + other than that are handled as normal packets */ + switch(event->etype) { + /* the user on the outside may need to look at the session so we will not free + it here anymore we will test for hangup event in iax_event_free and do it + there. + */ + case IAX_EVENT_REJECT: + case IAX_EVENT_HANGUP: + return event; + case IAX_EVENT_LAGRQ: + event->etype = IAX_EVENT_LAGRP; + iax_send_lagrp(event->session, event->ts); + iax_event_free(event); + break; + case IAX_EVENT_PING: + event->etype = IAX_EVENT_PONG; + iax_send_pong(event->session, event->ts); + iax_event_free(event); + break; + case IAX_EVENT_POKE: + event->etype = IAX_EVENT_PONG; + iax_send_pong(event->session, event->ts); + iax_destroy(event->session); + iax_event_free(event); + break; + default: + return event; + } + } else + iax_event_free(event); + } + return NULL; +} + +static int iax2_vnak(struct iax_session *session) +{ + return send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_VNAK, 0, NULL, 0, session->iseqno); +} + +int iax_send_dtmf(struct iax_session *session, char digit) +{ + return send_command(session, AST_FRAME_DTMF, digit, 0, NULL, 0, -1); +} + +int iax_send_voice(struct iax_session *session, int format, unsigned char *data, int datalen, int samples) +{ + /* Send a (possibly compressed) voice frame */ + if (!session->quelch) + return send_command_samples(session, AST_FRAME_VOICE, format, 0, data, datalen, -1, samples); + return 0; +} + +int iax_send_cng(struct iax_session *session, int level, unsigned char *data, int datalen) +{ + session->notsilenttx = 0; + return send_command(session, AST_FRAME_CNG, level, 0, data, datalen, -1); +} + +int iax_send_image(struct iax_session *session, int format, unsigned char *data, int datalen) +{ + /* Send an image frame */ + return send_command(session, AST_FRAME_IMAGE, format, 0, data, datalen, -1); +} + +int iax_register(struct iax_session *session, char *server, char *peer, char *secret, int refresh) +{ + /* Send a registration request */ + char tmp[256]; + char *p; + int res; + int portno = IAX_DEFAULT_PORTNO; + struct iax_ie_data ied; + struct hostent *hp; + + tmp[255] = '\0'; + strncpy(tmp, server, sizeof(tmp) - 1); + p = strchr(tmp, ':'); + if (p) { + *p = '\0'; + portno = atoi(p+1); + } + + memset(&ied, 0, sizeof(ied)); + if (secret) + strncpy(session->secret, secret, sizeof(session->secret) - 1); + else + strcpy(session->secret, ""); + + /* Connect first */ + hp = gethostbyname(tmp); + if (!hp) { + snprintf(iax_errstr, sizeof(iax_errstr), "Invalid hostname: %s", tmp); + return -1; + } + memcpy(&session->peeraddr.sin_addr, hp->h_addr, sizeof(session->peeraddr.sin_addr)); + session->peeraddr.sin_port = htons(portno); + session->peeraddr.sin_family = AF_INET; + strncpy(session->username, peer, sizeof(session->username) - 1); + session->refresh = refresh; + iax_ie_append_str(&ied, IAX_IE_USERNAME, (unsigned char *) peer); + iax_ie_append_short(&ied, IAX_IE_REFRESH, refresh); + res = send_command(session, AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1); + return res; +} + +int iax_reject_registration(struct iax_session *session, char *reason) +{ + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + iax_ie_append_str(&ied, IAX_IE_CAUSE, reason ? (unsigned char *) reason : (unsigned char *) "Unspecified"); + return send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_REGREJ, 0, ied.buf, ied.pos, -1); +} + +int iax_ack_registration(struct iax_session *session) +{ + return send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, NULL, 0, -1); +} + +int iax_auth_registration(struct iax_session *session) +{ + return send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, NULL, 0, -1); +} + +int iax_reject(struct iax_session *session, const char *reason) +{ + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + iax_ie_append_str(&ied, IAX_IE_CAUSE, reason ? (unsigned char *) reason : (unsigned char *) "Unspecified"); + return send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied.buf, ied.pos, -1); +} + +int iax_hangup(struct iax_session *session, char *byemsg) +{ + struct iax_ie_data ied; + iax_sched_del(NULL, NULL, send_ping, (void *) session, 1); + memset(&ied, 0, sizeof(ied)); + iax_ie_append_str(&ied, IAX_IE_CAUSE, byemsg ? (unsigned char *) byemsg : (unsigned char *) "Normal clearing"); + return send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_HANGUP, 0, ied.buf, ied.pos, -1); +} + +int iax_sendurl(struct iax_session *session, char *url) +{ + return send_command(session, AST_FRAME_HTML, AST_HTML_URL, 0, (unsigned char *) url, (int)strlen(url), -1); +} + +int iax_ring_announce(struct iax_session *session) +{ + return send_command(session, AST_FRAME_CONTROL, AST_CONTROL_RINGING, 0, NULL, 0, -1); +} + +int iax_lag_request(struct iax_session *session) +{ + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_LAGRQ, 0, NULL, 0, -1); +} + +int iax_busy(struct iax_session *session) +{ + return send_command(session, AST_FRAME_CONTROL, AST_CONTROL_BUSY, 0, NULL, 0, -1); +} + +int iax_congestion(struct iax_session *session) +{ + return send_command(session, AST_FRAME_CONTROL, AST_CONTROL_CONGESTION, 0, NULL, 0, -1); +} + + +int iax_accept(struct iax_session *session, int format) +{ + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + iax_ie_append_int(&ied, IAX_IE_FORMAT, format); + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied.buf, ied.pos, -1); +} + +int iax_answer(struct iax_session *session) +{ + return send_command(session, AST_FRAME_CONTROL, AST_CONTROL_ANSWER, 0, NULL, 0, -1); +} + +int iax_load_complete(struct iax_session *session) +{ + return send_command(session, AST_FRAME_HTML, AST_HTML_LDCOMPLETE, 0, NULL, 0, -1); +} + +int iax_send_url(struct iax_session *session, char *url, int link) +{ + return send_command(session, AST_FRAME_HTML, link ? AST_HTML_LINKURL : AST_HTML_URL, 0, (unsigned char *) url, (int)strlen(url), -1); +} + +int iax_send_text(struct iax_session *session, char *text) +{ + return send_command(session, AST_FRAME_TEXT, 0, 0, (unsigned char *) text, (int)strlen(text) + 1, -1); +} + +int iax_send_unlink(struct iax_session *session) +{ + return send_command(session, AST_FRAME_HTML, AST_HTML_UNLINK, 0, NULL, 0, -1); +} + +int iax_send_link_reject(struct iax_session *session) +{ + return send_command(session, AST_FRAME_HTML, AST_HTML_LINKREJECT, 0, NULL, 0, -1); +} + +static int iax_send_pong(struct iax_session *session, time_in_ms_t ts) +{ + struct iax_ie_data ied; + + memset(&ied, 0, sizeof(ied)); +#ifdef NEWJB + { + jb_info stats; + jb_getinfo(session->jb, &stats); + + iax_ie_append_int(&ied,IAX_IE_RR_JITTER, (int)stats.jitter); + /* XXX: should be short-term loss pct.. */ + if(stats.frames_in == 0) stats.frames_in = 1; + iax_ie_append_int(&ied,IAX_IE_RR_LOSS, + ((0xff & (stats.losspct/1000)) << 24 | (stats.frames_lost & 0x00ffffff))); + iax_ie_append_int(&ied,IAX_IE_RR_PKTS, stats.frames_in); + iax_ie_append_short(&ied,IAX_IE_RR_DELAY, (unsigned short)(stats.current - stats.min)); + iax_ie_append_int(&ied,IAX_IE_RR_DROPPED, stats.frames_dropped); + iax_ie_append_int(&ied,IAX_IE_RR_OOO, stats.frames_ooo); + } +#else + iax_ie_append_int(&ied,IAX_IE_RR_JITTER, session->jitter); + /* don't know, don't send! iax_ie_append_int(&ied,IAX_IE_RR_LOSS, 0); */ + /* don't know, don't send! iax_ie_append_int(&ied,IAX_IE_RR_PKTS, stats.frames_in); */ + /* don't know, don't send! iax_ie_append_short(&ied,IAX_IE_RR_DELAY, stats.current - stats.min); */ +#endif + + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_PONG, ts, ied.buf, ied.pos, -1); +} + +/* external API; deprecated since we send pings ourselves now (finally) */ +int iax_send_ping(struct iax_session *session) +{ + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1); +} + +/* scheduled ping sender; sends ping, then reschedules */ +static void send_ping(void *s) +{ + struct iax_session *session = (struct iax_session *)s; + + /* important, eh? */ + if(!iax_session_valid(session)) return; + + send_command(session, AST_FRAME_IAX, IAX_COMMAND_PING, 0, NULL, 0, -1); + session->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)session, ping_time * 1000); + return; +} + +static int iax_send_lagrp(struct iax_session *session, time_in_ms_t ts) +{ + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_LAGRP, ts, NULL, 0, -1); +} + +static int iax_send_txcnt(struct iax_session *session) +{ + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + iax_ie_append_int(&ied, IAX_IE_TRANSFERID, session->transferid); + return send_command_transfer(session, AST_FRAME_IAX, IAX_COMMAND_TXCNT, 0, ied.buf, ied.pos); +} + +static int iax_send_txrej(struct iax_session *session) +{ + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + iax_ie_append_int(&ied, IAX_IE_TRANSFERID, session->transferid); + return send_command_transfer(session, AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, ied.buf, ied.pos); +} + +static int iax_send_txaccept(struct iax_session *session) +{ + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + iax_ie_append_int(&ied, IAX_IE_TRANSFERID, session->transferid); + return send_command_transfer(session, AST_FRAME_IAX, IAX_COMMAND_TXACC, 0, ied.buf, ied.pos); +} + +static int iax_send_txready(struct iax_session *session) +{ + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + /* see asterisk chan_iax2.c */ + iax_ie_append_short(&ied, IAX_IE_CALLNO, session->callno); + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_TXREADY, 0, ied.buf, ied.pos, -1); +} + +int iax_auth_reply(struct iax_session *session, char *password, char *challenge, int methods) +{ + char reply[16]; + struct MD5Context md5; + char realreply[256]; + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + if ((methods & IAX_AUTH_MD5) && challenge) { + MD5Init(&md5); + MD5Update(&md5, (const unsigned char *) challenge, (unsigned int)strlen(challenge)); + MD5Update(&md5, (const unsigned char *) password, (unsigned int)strlen(password)); + MD5Final((unsigned char *) reply, &md5); + memset(realreply, 0, sizeof(realreply)); + convert_reply(realreply, (unsigned char *) reply); + iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, (unsigned char *) realreply); + } else { + iax_ie_append_str(&ied, IAX_IE_PASSWORD, (unsigned char *) password); + } + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_AUTHREP, 0, ied.buf, ied.pos, -1); +} + +static int iax_regauth_reply(struct iax_session *session, char *password, char *challenge, int methods) +{ + char reply[16]; + struct MD5Context md5; + char realreply[256]; + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + iax_ie_append_str(&ied, IAX_IE_USERNAME, (unsigned char *) session->username); + iax_ie_append_short(&ied, IAX_IE_REFRESH, session->refresh); + if ((methods & IAX_AUTHMETHOD_MD5) && challenge) { + MD5Init(&md5); + MD5Update(&md5, (const unsigned char *) challenge, (unsigned int)strlen(challenge)); + MD5Update(&md5, (const unsigned char *) password, (unsigned int)strlen(password)); + MD5Final((unsigned char *) reply, &md5); + memset(realreply, 0, sizeof(realreply)); + convert_reply(realreply, (unsigned char *) reply); + iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, (unsigned char *) realreply); + } else { + iax_ie_append_str(&ied, IAX_IE_PASSWORD, (unsigned char *) password); + } + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1); +} + + +int iax_dial(struct iax_session *session, char *number) +{ + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, (unsigned char *) number); + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_DIAL, 0, ied.buf, ied.pos, -1); +} + +int iax_quelch(struct iax_session *session) +{ + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_QUELCH, 0, NULL, 0, -1); +} + +int iax_unquelch(struct iax_session *session) +{ + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_UNQUELCH, 0, NULL, 0, -1); +} + +int iax_dialplan_request(struct iax_session *session, char *number) +{ + struct iax_ie_data ied; + memset(&ied, 0, sizeof(ied)); + iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, (unsigned char *) number); + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_DPREQ, 0, ied.buf, ied.pos, -1); +} + +static inline int which_bit(unsigned int i) +{ + char x; + for(x = 0; x < 32; x++) { + if ((1 << x) == i) { + return x + 1; + } + } + return 0; +} + +char iax_pref_codec_add(struct iax_session *session, unsigned int format) +{ + int diff = (int) 'A'; + session->codec_order[session->codec_order_len++] = (which_bit(format)) + diff; + session->codec_order[session->codec_order_len] = '\0'; + return session->codec_order[session->codec_order_len-1]; +} + + +void iax_pref_codec_del(struct iax_session *session, unsigned int format) +{ + int diff = (int) 'A'; + size_t x; + char old[32]; + char remove = which_bit(format) + diff; + + strncpy(old, session->codec_order, sizeof(old)); + session->codec_order_len = 0; + + for (x = 0; x < strlen(old) ; x++) { + if (old[x] != remove) { + session->codec_order[session->codec_order_len++] = old[x]; + } + } + session->codec_order[session->codec_order_len] = '\0'; +} + +int iax_pref_codec_get(struct iax_session *session, unsigned int *array, int len) +{ + int diff = (int) 'A'; + int x; + + for (x = 0; x < session->codec_order_len && x < len; x++) { + array[x] = (1 << (session->codec_order[x] - diff - 1)); + } + + return x; +} + +void iax_set_samplerate(struct iax_session *session, unsigned short samplemask) +{ + session->samplemask = samplemask; +} + + +int iax_call(struct iax_session *session, const char *cidnum, const char *cidname, char *ich, char *lang, int wait, int formats, int capabilities) +{ + char tmp[256]=""; + char *part1, *part2; + int res; + int portno; + char *username, *hostname, *secret, *context, *exten, *dnid; + struct iax_ie_data ied; + struct hostent *hp; + /* We start by parsing up the temporary variable which is of the form of: + [user@]peer[:portno][/exten[@context]] */ + if (!ich) { + IAXERROR "Invalid IAX Call Handle\n"); + DEBU(G "Invalid IAX Call Handle\n"); + return -1; + } + memset(&ied, 0, sizeof(ied)); + strncpy(tmp, ich, sizeof(tmp) - 1); + + iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION); + if (session->samplemask) { + iax_ie_append_short(&ied, IAX_IE_SAMPLINGRATE, session->samplemask); + } + if (cidnum) + iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, (unsigned char *) cidnum); + if (cidname) + iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, (unsigned char *) cidname); + + if (session->codec_order_len) { + iax_ie_append_str(&ied, IAX_IE_CODEC_PREFS, (unsigned char *) session->codec_order); + } + + session->capability = capabilities; + session->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)session, 2 * 1000); + + /* XXX We should have a preferred format XXX */ + iax_ie_append_int(&ied, IAX_IE_FORMAT, formats); + iax_ie_append_int(&ied, IAX_IE_CAPABILITY, capabilities); + if (lang) + iax_ie_append_str(&ied, IAX_IE_LANGUAGE, (unsigned char *) lang); + + /* Part 1 is [user[:password]@]peer[:port] */ + part1 = strtok(tmp, "/"); + + /* Part 2 is exten[@context] if it is anything all */ + part2 = strtok(NULL, "/"); + + if (strchr(part1, '@')) { + username = strtok(part1, "@"); + hostname = strtok(NULL, "@"); + } else { + username = NULL; + hostname = part1; + } + + if (username && strchr(username, ':')) { + username = strtok(username, ":"); + secret = strtok(NULL, ":"); + } else + secret = NULL; + + if(username) + strncpy(session->username, username, sizeof(session->username) - 1); + + if(secret) + strncpy(session->secret, secret, sizeof(session->secret) - 1); + + if (strchr(hostname, ':')) { + strtok(hostname, ":"); + portno = atoi(strtok(NULL, ":")); + } else { + portno = IAX_DEFAULT_PORTNO; + } + if (part2) { + exten = strtok(part2, "@"); + dnid = exten; + context = strtok(NULL, "@"); + } else { + exten = NULL; + dnid = NULL; + context = NULL; + } + if (username) + iax_ie_append_str(&ied, IAX_IE_USERNAME, (unsigned char *) username); + if (exten && strlen(exten)) + iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, (unsigned char *) exten); + if (dnid && strlen(dnid)) + iax_ie_append_str(&ied, IAX_IE_DNID, (unsigned char *) dnid); + if (context && strlen(context)) + iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, (unsigned char *) context); + + /* Setup host connection */ + hp = gethostbyname(hostname); + if (!hp) { + snprintf(iax_errstr, sizeof(iax_errstr), "Invalid hostname: %s", hostname); + return -1; + } + memcpy(&session->peeraddr.sin_addr, hp->h_addr, sizeof(session->peeraddr.sin_addr)); + session->peeraddr.sin_port = htons(portno); + session->peeraddr.sin_family = AF_INET; + res = send_command(session, AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1); + if (res < 0) + return res; + if (wait) { + DEBU(G "Waiting not yet implemented\n"); + return -1; + } + return res; +} + +static time_in_ms_t calc_rxstamp(struct iax_session *session) +{ + time_in_ms_t time_in_ms; + + time_in_ms = current_time_in_ms(); + + if (!session->rxcore) { + session->rxcore = time_in_ms; + } + + return time_in_ms - session->rxcore; +} + +#ifdef notdef_cruft +static int match(struct sockaddr_in *sin, short callno, short dcallno, struct iax_session *cur) +{ + if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) && + (cur->peeraddr.sin_port == sin->sin_port)) { + /* This is the main host */ + if ((cur->peercallno == callno) || + ((dcallno == cur->callno) && !cur->peercallno)) { + /* That's us. Be sure we keep track of the peer call number */ + cur->peercallno = callno; + return 1; + } + } + if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) && + (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) { + /* We're transferring */ + if (dcallno == cur->callno) + return 1; + } + return 0; +} +#endif + +/* splitted match into 2 passes otherwise causing problem of matching + up the wrong session using the dcallno and the peercallno because + during a transfer (2 IAX channels on the same client/system) the + same peercallno (from two different asterisks) exist in more than + one session. + */ +static int forward_match(struct sockaddr_in *sin, short callno, short dcallno, struct iax_session *cur) +{ + if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) && + (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) { + /* We're transferring */ + if (dcallno == cur->callno) + { + return 1; + } + } + + if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) && + (cur->peeraddr.sin_port == sin->sin_port)) { + if (dcallno == cur->callno && dcallno != 0) { + /* That's us. Be sure we keep track of the peer call number */ + if (cur->peercallno == 0) { + cur->peercallno = callno; + } + return 1; + } + } + + return 0; +} + +static int reverse_match(struct sockaddr_in *sin, short callno, struct iax_session *cur) +{ + if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) && + (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) { + /* We're transferring */ + if (callno == cur->peercallno) { + return 1; + } + } + if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) && + (cur->peeraddr.sin_port == sin->sin_port)) { + if (callno == cur->peercallno) { + return 1; + } + } + + return 0; +} + +static struct iax_session *iax_find_session(struct sockaddr_in *sin, + short callno, + short dcallno, + int makenew) +{ + struct iax_session *cur; + + iax_mutex_lock(session_mutex); + cur = sessions; + while(cur) { + if (forward_match(sin, callno, dcallno, cur)) { + iax_mutex_unlock(session_mutex); + return cur; + } + cur = cur->next; + } + + cur = sessions; + while(cur) { + if (reverse_match(sin, callno, cur)) { + iax_mutex_unlock(session_mutex); + return cur; + } + cur = cur->next; + } + + iax_mutex_unlock(session_mutex); + + if (makenew && !dcallno) { + cur = iax_session_new(); + cur->peercallno = callno; + cur->peeraddr.sin_addr.s_addr = sin->sin_addr.s_addr; + cur->peeraddr.sin_port = sin->sin_port; + cur->peeraddr.sin_family = AF_INET; + cur->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)cur, 2 * 1000); + DEBU(G "Making new session, peer callno %d, our callno %d\n", callno, cur->callno); + } else { + DEBU(G "No session, peer = %d, us = %d\n", callno, dcallno); + } + + return cur; +} + +#ifdef EXTREME_DEBUG +static int display_time(int ms) +{ + static int oldms = -1; + if (oldms < 0) { + DEBU(G "First measure\n"); + oldms = ms; + return 0; + } + DEBU(G "Time from last frame is %d ms\n", ms - oldms); + oldms = ms; + return 0; +} +#endif + +#define FUDGE 1 + +#ifdef NEWJB +/* From chan_iax2/steve davies: need to get permission from steve or digium, I guess */ +static time_in_ms_t unwrap_timestamp(time_in_ms_t ts, time_in_ms_t last) +{ + time_in_ms_t x; + + if ( (ts & 0xFFFF0000) == (last & 0xFFFF0000) ) { + x = ts - last; + if (x < -50000) { + /* Sudden big jump backwards in timestamp: + What likely happened here is that miniframe timestamp has circled but we haven't + gotten the update from the main packet. We'll just pretend that we did, and + update the timestamp appropriately. */ + ts = ( (last & 0xFFFF0000) + 0x10000) | (ts & 0xFFFF); + DEBU(G "schedule_delivery: pushed forward timestamp\n"); + } + if (x > 50000) { + /* Sudden apparent big jump forwards in timestamp: + What's likely happened is this is an old miniframe belonging to the previous + top-16-bit timestamp that has turned up out of order. + Adjust the timestamp appropriately. */ + ts = ( (last & 0xFFFF0000) - 0x10000) | (ts & 0xFFFF); + DEBU(G "schedule_delivery: pushed back timestamp\n"); + } + } + return ts; +} +#endif + + +static struct iax_event *schedule_delivery(struct iax_event *e, time_in_ms_t ts, int updatehistory) +{ + /* + * This is the core of the IAX jitterbuffer delivery mechanism: + * Dynamically adjust the jitterbuffer and decide how time_in_ms_t to wait + * before delivering the packet. + */ +#ifndef NEWJB + int ms, x; + int drops[MEMORY_SIZE]; + int min, max=0, maxone=0, y, z, match; +#endif + +#ifdef EXTREME_DEBUG + DEBU(G "[%p] We are at %d, packet is for %d\n", e->session, calc_rxstamp(e->session), ts); +#endif + +#ifdef VOICE_SMOOTHING + if (e->etype == IAX_EVENT_VOICE) { + /* Smooth voices if we know enough about the format */ + switch(e->event.voice.format) { + case AST_FORMAT_GSM: + /* GSM frames are 20 ms long, although there could be periods of + silence. If the time is < 50 ms, assume it ought to be 20 ms */ + if (ts - e->session->lastts < 50) + ts = e->session->lastts + 20; +#ifdef EXTREME_DEBUG + display_time(ts); +#endif + break; + default: + /* Can't do anything */ + } + e->session->lastts = ts; + } +#endif + +#ifdef NEWJB + { + int type = JB_TYPE_CONTROL; + int len = 0; + + if(e->etype == IAX_EVENT_VOICE) { + type = JB_TYPE_VOICE; + len = get_sample_cnt(e) / 8; + } else if(e->etype == IAX_EVENT_CNG) { + type = JB_TYPE_SILENCE; + } + + /* unwrap timestamp */ + ts = unwrap_timestamp(ts,e->session->last_ts); + + /* move forward last_ts if it's greater. We do this _after_ unwrapping, because + * asterisk _still_ has cases where it doesn't send full frames when it ought to */ + if(ts > e->session->last_ts) { + e->session->last_ts = ts; + } + + + /* insert into jitterbuffer */ + /* TODO: Perhaps we could act immediately if it's not droppable and late */ + if(jb_put(e->session->jb, e, type, len, ts, calc_rxstamp(e->session)) == JB_DROP) { + iax_event_free(e); + } + + } +#else + + /* How many ms from now should this packet be delivered? (remember + this can be a negative number, too */ + ms = (int)(calc_rxstamp(e->session) - ts); + + /* + Drop voice frame if timestamp is way off + if ((e->etype == IAX_EVENT_VOICE) && ((ms > 65536) || (ms < -65536))) { + DEBU(G "Dropping a voice packet with odd ts (ts = %d; ms = %d)\n", ts, ms); + free(e); + return NULL; + } + */ + + /* Adjust if voice frame timestamp is off by a step */ + if (ms > 32768) { + /* What likely happened here is that our counter has circled but we haven't + gotten the update from the main packet. We'll just pretend that we did, and + update the timestamp appropriately. */ + ms -= 65536; + } + if (ms < -32768) { + /* We got this packet out of order. Lets add 65536 to it to bring it into our new + time frame */ + ms += 65536; + } + +#if 0 + printf("rxstamp is %d, timestamp is %d, ms is %d\n", calc_rxstamp(e->session), ts, ms); +#endif + /* Rotate history queue. Leading 0's are irrelevant. */ + if (updatehistory) { + for (x=0; x < MEMORY_SIZE - 1; x++) + e->session->history[x] = e->session->history[x+1]; + + /* Add new entry for this time */ + e->session->history[x] = ms; + } + + /* We have to find the maximum and minimum time delay we've had to deliver. */ + min = e->session->history[0]; + for (z=0;z < iax_dropcount + 1; z++) { + /* We drop the top iax_dropcount entries. iax_dropcount represents + a tradeoff between quality of voice and latency. 3% drop seems to + be unnoticable to the client and can significantly improve latency. + We add one more to our droplist, but that's the one we actually use, + and don't drop. */ + max = -99999999; + for (x=0;x<MEMORY_SIZE;x++) { + if (max < e->session->history[x]) { + /* New candidate value. Make sure we haven't dropped it. */ + match=0; + for(y=0;!match && (y<z); y++) + match |= (drops[y] == x); + /* If there is no match, this is our new maximum */ + if (!match) { + max = e->session->history[x]; + maxone = x; + } + } + if (!z) { + /* First pass, calcualte our minimum, too */ + if (min > e->session->history[x]) + min = e->session->history[x]; + } + } + drops[z] = maxone; + } + /* Again, just for reference. The "jitter buffer" is the max. The difference + is the perceived jitter correction. */ + e->session->jitter = max - min; + + /* If the jitter buffer is substantially too large, shrink it, slowly enough + that the client won't notice ;-) . */ + if (max < e->session->jitterbuffer - max_extra_jitterbuffer) { +#ifdef EXTREME_DEBUG + DEBU(G "Shrinking jitterbuffer (target = %d, current = %d...\n", max, e->session->jitterbuffer); +#endif + e->session->jitterbuffer -= 1; + } + + /* Keep the jitter buffer from becoming unreasonably large */ + if (max > min + max_jitterbuffer) { + DEBU(G "Constraining jitter buffer (min = %d, max = %d)...\n", min, max); + max = min + max_jitterbuffer; + } + + /* If the jitter buffer is too small, we immediately grow our buffer to + accomodate */ + if (max > e->session->jitterbuffer) + e->session->jitterbuffer = max; + + /* Start with our jitter buffer delay, and subtract the lateness (or earliness). + Remember these times are all relative to the first packet, so their absolute + values are really irrelevant. */ + ms = e->session->jitterbuffer - ms - IAX_SCHEDULE_FUZZ; + + /* If the jitterbuffer is disabled, always deliver immediately */ + if (!iax_use_jitterbuffer) + ms = 0; + + if (ms < 1) { +#ifdef EXTREME_DEBUG + DEBU(G "Calculated delay is only %d\n", ms); +#endif + if ((ms > -4) || (e->etype != IAX_EVENT_VOICE)) { + /* Return the event immediately if it's it's less than 3 milliseconds + too late, or if it's not voice (believe me, you don't want to + just drop a hangup frame because it's late, or a ping, or some such. + That kinda ruins retransmissions too ;-) */ + /* Queue for immediate delivery */ + iax_sched_add(e, NULL, NULL, NULL, 0); + return NULL; + //return e; + } + DEBU(G "(not so) Silently dropping a packet (ms = %d)\n", ms); + /* Silently discard this as if it were to be delivered */ + free(e); + return NULL; + } + /* We need this to be delivered in the future, so we use our scheduler */ + iax_sched_add(e, NULL, NULL, NULL, ms); +#ifdef EXTREME_DEBUG + DEBU(G "Delivering packet in %d ms\n", ms); +#endif +#endif /* NEWJB */ + return NULL; + +} + +static int uncompress_subclass(unsigned char csub) +{ + /* If the SC_LOG flag is set, return 2^csub otherwise csub */ + if (csub & IAX_FLAG_SC_LOG) + return 1 << (csub & ~IAX_FLAG_SC_LOG & IAX_MAX_SHIFT); + else + return csub; +} + +static inline char *extract(char *src, char *string) +{ + /* Extract and duplicate what we need from a string */ + char *s, *t; + s = strstr(src, string); + if (s) { + s += strlen(string); + s = strdup(s); + /* End at ; */ + t = strchr(s, ';'); + if (t) { + *t = '\0'; + } + } + return s; + +} + +static struct iax_event *iax_header_to_event(struct iax_session *session, + struct ast_iax2_full_hdr *fh, + int datalen, struct sockaddr_in *sin) +{ + struct iax_event *e; + struct iax_sched *sch; + unsigned int ts; + int subclass = uncompress_subclass(fh->csub); + time_in_ms_t nowts; + int updatehistory = 1; + ts = ntohl(fh->ts); + /* don't run last_ts backwards; i.e. for retransmits and the like */ + if (ts > session->last_ts && + (fh->type == AST_FRAME_IAX && + subclass != IAX_COMMAND_ACK && + subclass != IAX_COMMAND_PONG && + subclass != IAX_COMMAND_LAGRP)) { + session->last_ts = ts; + } + + +#ifdef DEBUG_SUPPORT + iax_showframe(NULL, fh, 1, sin, datalen); +#endif + + /* Get things going with it, timestamp wise, if we + haven't already. */ + + /* Handle implicit ACKing unless this is an INVAL, and only if this is + from the real peer, not the transfer peer */ + if (!inaddrcmp(sin, &session->peeraddr) && + (((subclass != IAX_COMMAND_INVAL)) || + (fh->type != AST_FRAME_IAX))) { + unsigned char x; + /* XXX This code is not very efficient. Surely there is a better way which still + properly handles boundary conditions? XXX */ + /* First we have to qualify that the ACKed value is within our window */ + for (x=session->rseqno; x != session->oseqno; x++) + if (fh->iseqno == x) + break; + if ((x != session->oseqno) || (session->oseqno == fh->iseqno)) { + /* The acknowledgement is within our window. Time to acknowledge everything + that it says to */ + for (x=session->rseqno; x != fh->iseqno; x++) { + /* Ack the packet with the given timestamp */ + DEBU(G "Cancelling transmission of packet %d\n", x); + iax_mutex_lock(sched_mutex); + sch = schedq; + while(sch) { + if (sch->frame && (sch->frame->session == session) && + (sch->frame->oseqno == x)) + sch->frame->retries = -1; + sch = sch->next; + } + iax_mutex_unlock(sched_mutex); + } + /* Note how much we've received acknowledgement for */ + session->rseqno = fh->iseqno; + } else + DEBU(G "Received iseqno %d not within window %d->%d\n", fh->iseqno, session->rseqno, session->oseqno); + } + + /* Check where we are */ + if ((ntohs(fh->dcallno) & IAX_FLAG_RETRANS) || (fh->type != AST_FRAME_VOICE)) + updatehistory = 0; + if ((session->iseqno != fh->oseqno) && + (session->iseqno || + ((subclass != IAX_COMMAND_TXREADY) && + (subclass != IAX_COMMAND_TXREL) && + (subclass != IAX_COMMAND_TXCNT) && + (subclass != IAX_COMMAND_TXACC)) || + (fh->type != AST_FRAME_IAX))) { + if ( + ((subclass != IAX_COMMAND_ACK) && + (subclass != IAX_COMMAND_INVAL) && + (subclass != IAX_COMMAND_TXREADY) && + (subclass != IAX_COMMAND_TXREL) && + (subclass != IAX_COMMAND_TXCNT) && + (subclass != IAX_COMMAND_TXACC) && + (subclass != IAX_COMMAND_VNAK)) || + (fh->type != AST_FRAME_IAX)) { + /* If it's not an ACK packet, it's out of order. */ + DEBU(G "Packet arrived out of order (expecting %d, got %d) (frametype = %d, subclass = %d)\n", + session->iseqno, fh->oseqno, fh->type, subclass); + if (session->iseqno > fh->oseqno) { + /* If we've already seen it, ack it XXX There's a border condition here XXX */ + if ((fh->type != AST_FRAME_IAX) || + ((subclass != IAX_COMMAND_ACK) && (subclass != IAX_COMMAND_INVAL))) { + DEBU(G "Acking anyway\n"); + /* XXX Maybe we should handle its ack to us, but then again, it's probably outdated anyway, and if + we have anything to send, we'll retransmit and get an ACK back anyway XXX */ + send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0,fh->iseqno); + } + } else { + /* Send a VNAK requesting retransmission */ + iax2_vnak(session); + } + return NULL; + } + } else { + /* Increment unless it's an ACK or VNAK */ + if (((subclass != IAX_COMMAND_ACK) && + (subclass != IAX_COMMAND_INVAL) && + (subclass != IAX_COMMAND_TXCNT) && + (subclass != IAX_COMMAND_TXACC) && + (subclass != IAX_COMMAND_VNAK)) || + (fh->type != AST_FRAME_IAX)) + session->iseqno++; + } + + e = (struct iax_event *)malloc(sizeof(struct iax_event) + datalen + 1); + + if (e) { + memset(e, 0, sizeof(struct iax_event) + datalen); + /* Set etype to some unknown value so do not inavertently + sending IAX_EVENT_CONNECT event, which is 0 to application. + */ + e->etype = -1; + e->session = session; + switch(fh->type) { + case AST_FRAME_DTMF: + e->etype = IAX_EVENT_DTMF; + e->subclass = subclass; + /* + We want the DTMF event deliver immediately so all I/O can be + terminate quickly in an IVR system. + e = schedule_delivery(e, ts, updatehistory); */ + break; + case AST_FRAME_VOICE: + e->etype = IAX_EVENT_VOICE; + e->subclass = subclass; + session->voiceformat = subclass; + if (datalen) { + memcpy(e->data, fh->iedata, datalen); + e->datalen = datalen; + } + e = schedule_delivery(e, ts, updatehistory); + break; + case AST_FRAME_CNG: + e->etype = IAX_EVENT_CNG; + e->subclass = subclass; + if (datalen) { + memcpy(e->data, fh->iedata, datalen); + e->datalen = datalen; + } + e = schedule_delivery(e, ts, updatehistory); + break; + case AST_FRAME_IAX: + /* Parse IE's */ + if (datalen) { + memcpy(e->data, fh->iedata, datalen); + e->datalen = datalen; + } + if (iax_parse_ies(&e->ies, e->data, e->datalen)) { + IAXERROR "Unable to parse IE's"); + free(e); + e = NULL; + break; + } + switch(subclass) { + case IAX_COMMAND_NEW: + /* This is a new, incoming call */ + /* save the capability for validation */ + session->capability = e->ies.capability; + if (e->ies.codec_prefs) { + strncpy(session->codec_order, e->ies.codec_prefs, sizeof(session->codec_order)); + session->codec_order_len = (int)strlen(session->codec_order); + } + e->etype = IAX_EVENT_CONNECT; + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_AUTHREQ: + /* This is a request for a call */ + e->etype = IAX_EVENT_AUTHRQ; + if (strlen(session->username) && !strcmp(e->ies.username, session->username) && + strlen(session->secret)) { + /* Hey, we already know this one */ + iax_auth_reply(session, session->secret, e->ies.challenge, e->ies.authmethods); + free(e); + e = NULL; + break; + } + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_HANGUP: + e->etype = IAX_EVENT_HANGUP; + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_INVAL: + e->etype = IAX_EVENT_HANGUP; + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_REJECT: + e->etype = IAX_EVENT_REJECT; + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_ACK: + free(e); + e = NULL; + break; + case IAX_COMMAND_LAGRQ: + /* Pass this atime_in_ms_t for later handling */ + e->etype = IAX_EVENT_LAGRQ; + e->ts = ts; + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_POKE: + e->etype = IAX_EVENT_POKE; + e->ts = ts; + break; + case IAX_COMMAND_PING: + /* PINGS and PONGS don't get scheduled; */ + e->etype = IAX_EVENT_PING; + e->ts = ts; + break; + case IAX_COMMAND_PONG: + e->etype = IAX_EVENT_PONG; + /* track weighted average of ping time */ + session->pingtime = ((2 * session->pingtime) + (calc_timestamp(session,0,NULL) - ts)) / 3; + session->remote_netstats.jitter = e->ies.rr_jitter; + session->remote_netstats.losspct = e->ies.rr_loss >> 24;; + session->remote_netstats.losscnt = e->ies.rr_loss & 0xffffff; + session->remote_netstats.packets = e->ies.rr_pkts; + session->remote_netstats.delay = e->ies.rr_delay; + session->remote_netstats.dropped = e->ies.rr_dropped; + session->remote_netstats.ooo = e->ies.rr_ooo; + break; + case IAX_COMMAND_ACCEPT: + if (e->ies.format & session->capability) { + e->etype = IAX_EVENT_ACCEPT; + } + else { + struct iax_ie_data ied; + /* Although this should not happen, we added this to make sure + the negotiation protocol is enforced. + For lack of event to notify the application we use the defined + REJECT event. + */ + memset(&ied, 0, sizeof(ied)); + iax_ie_append_str(&ied, IAX_IE_CAUSE, (unsigned char *) "Unable to negotiate codec"); + send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied.buf, ied.pos, -1); + e->etype = IAX_EVENT_REJECT; + } + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_REGREQ: + e->etype = IAX_EVENT_REGREQ; + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_REGREL: + e->etype = IAX_EVENT_REGREQ; + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_REGACK: + e->etype = IAX_EVENT_REGACK; + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_REGAUTH: + iax_regauth_reply(session, session->secret, e->ies.challenge, e->ies.authmethods); + free(e); + e = NULL; + break; + case IAX_COMMAND_REGREJ: + e->etype = IAX_EVENT_REGREJ; + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_LAGRP: + e->etype = IAX_EVENT_LAGRP; + nowts = calc_timestamp(session, 0, NULL); + e->ts = nowts - ts; + e->subclass = session->jitter; + /* Can't call schedule_delivery since timestamp is non-normal */ + break;; + case IAX_COMMAND_TXREQ: + /* added check for defensive programming + * - in case the asterisk server + * or another client does not send the + * apparent transfer address + */ + if (e->ies.apparent_addr != NULL) { + /* so a full voice frame is sent on the + next voice output */ + session->svoiceformat = -1; + session->transfer = *e->ies.apparent_addr; + session->transfer.sin_family = AF_INET; + session->transfercallno = e->ies.callno; + session->transferring = TRANSFER_BEGIN; + session->transferid = e->ies.transferid; + iax_send_txcnt(session); + } + free(e); + e = NULL; + break; + case IAX_COMMAND_DPREP: + /* Received dialplan reply */ + e->etype = IAX_EVENT_DPREP; + /* Return immediately, makes no sense to schedule */ + break; + case IAX_COMMAND_TXCNT: + if (session->transferring) { + session->transfer = *sin; + iax_send_txaccept(session); + } + free(e); + e = NULL; + break; + case IAX_COMMAND_TXACC: + if (session->transferring) { + stop_transfer(session); + session->transferring = TRANSFER_READY; + iax_send_txready(session); + } + free(e); + e = NULL; + break; + case IAX_COMMAND_TXREL: + /* Release the transfer */ + send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, fh->ts, NULL, 0, fh->iseqno); + if (session->transferring) { + complete_transfer(session, e->ies.callno, 1, 0); + } + else { + complete_transfer(session, session->peercallno, 0, 1); + } + e->etype = IAX_EVENT_TRANSFER; + /* notify that asterisk no longer sitting between peers */ + e = schedule_delivery(e, ts, updatehistory); + break; + case IAX_COMMAND_QUELCH: + e->etype = IAX_EVENT_QUELCH; + session->quelch = 1; + break; + case IAX_COMMAND_UNQUELCH: + e->etype = IAX_EVENT_UNQUELCH; + session->quelch = 0; + break; + case IAX_COMMAND_TXREJ: + e->etype = IAX_EVENT_TXREJECT; + iax_handle_txreject(session); + break; + + case IAX_COMMAND_TXREADY: + send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0, fh->iseqno); + if (iax_handle_txready(session)) { + e->etype = IAX_EVENT_TXREADY; + } + else { + free(e); + e = NULL; + } + break; + default: + DEBU(G "Don't know what to do with IAX command %d\n", subclass); + free(e); + e = NULL; + } + break; + case AST_FRAME_CONTROL: + switch(subclass) { + case AST_CONTROL_ANSWER: + e->etype = IAX_EVENT_ANSWER; + e = schedule_delivery(e, ts, updatehistory); + break; + case AST_CONTROL_CONGESTION: + case AST_CONTROL_BUSY: + e->etype = IAX_EVENT_BUSY; + e = schedule_delivery(e, ts, updatehistory); + break; + case AST_CONTROL_RINGING: + e->etype = IAX_EVENT_RINGA; + e = schedule_delivery(e, ts, updatehistory); + break; + default: + DEBU(G "Don't know what to do with AST control %d\n", subclass); + free(e); + return NULL; + } + break; + case AST_FRAME_IMAGE: + e->etype = IAX_EVENT_IMAGE; + e->subclass = subclass; + if (datalen) { + memcpy(e->data, fh->iedata, datalen); + } + e = schedule_delivery(e, ts, updatehistory); + break; + + case AST_FRAME_TEXT: + e->etype = IAX_EVENT_TEXT; + if (datalen) { + memcpy(e->data, fh->iedata, datalen); + } + e = schedule_delivery(e, ts, updatehistory); + break; + + case AST_FRAME_HTML: + switch(fh->csub) { + case AST_HTML_LINKURL: + e->etype = IAX_EVENT_LINKURL; + /* Fall through */ + case AST_HTML_URL: + if (e->etype == -1) + e->etype = IAX_EVENT_URL; + e->subclass = fh->csub; + e->datalen = datalen; + if (datalen) { + memcpy(e->data, fh->iedata, datalen); + } + e = schedule_delivery(e, ts, updatehistory); + break; + case AST_HTML_LDCOMPLETE: + e->etype = IAX_EVENT_LDCOMPLETE; + e = schedule_delivery(e, ts, updatehistory); + break; + case AST_HTML_UNLINK: + e->etype = IAX_EVENT_UNLINK; + e = schedule_delivery(e, ts, updatehistory); + break; + case AST_HTML_LINKREJECT: + e->etype = IAX_EVENT_LINKREJECT; + e = schedule_delivery(e, ts, updatehistory); + break; + default: + DEBU(G "Don't know how to handle HTML type %d frames\n", fh->csub); + free(e); + return NULL; + } + break; + default: + DEBU(G "Don't know what to do with frame type %d\n", fh->type); + free(e); + return NULL; + } + } else + DEBU(G "Out of memory\n"); + + /* Already ack'd iax frames */ + if (session->aseqno != session->iseqno) { + send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0, fh->iseqno); + } + return e; +} + +static struct iax_event *iax_miniheader_to_event(struct iax_session *session, + struct ast_iax2_mini_hdr *mh, + int datalen) +{ + struct iax_event *e; + time_in_ms_t ts; + int updatehistory = 1; + e = (struct iax_event *)malloc(sizeof(struct iax_event) + datalen); + if (e) { + if (session->voiceformat > 0) { + e->etype = IAX_EVENT_VOICE; + e->session = session; + e->subclass = session->voiceformat; + e->datalen = datalen; + if (datalen) { +#ifdef EXTREME_DEBUG + DEBU(G "%d bytes of voice\n", datalen); +#endif + memcpy(e->data, mh->data, datalen); + } + ts = (session->last_ts & 0xFFFF0000) | ntohs(mh->ts); + return schedule_delivery(e, ts, updatehistory); + } else { + DEBU(G "No last format received on session %d\n", session->callno); + free(e); + e = NULL; + } + } else + DEBU(G "Out of memory\n"); + return e; +} + +void iax_destroy(struct iax_session *session) +{ + iax_mutex_lock(session_mutex); + destroy_session(session); + iax_mutex_unlock(session_mutex); +} + +static struct iax_event *iax_net_read(void) +{ + unsigned char buf[65536]; + int res, sinlen; + struct sockaddr_in sin; + + sinlen = sizeof(sin); + res = iax_recvfrom(netfd, buf, sizeof(buf), 0, (struct sockaddr *) &sin, &sinlen); + + if (res < 0) { +#ifdef WIN32 + if (WSAGetLastError() != WSAEWOULDBLOCK) { + DEBU(G "Error on read: %d\n", WSAGetLastError()); + IAXERROR "Read error on network socket: %s", strerror(errno)); + } +#else + if (errno != EAGAIN) { + DEBU(G "Error on read: %s\n", strerror(errno)); + IAXERROR "Read error on network socket: %s", strerror(errno)); + } +#endif + return NULL; + } + return iax_net_process(buf, res, &sin); +} + +static struct iax_session *iax_txcnt_session(struct ast_iax2_full_hdr *fh, int datalen, + struct sockaddr_in *sin, short callno, short dcallno) +{ + int subclass = uncompress_subclass(fh->csub); + unsigned char buf[ 65536 ]; /* allocated on stack with same size as iax_net_read() */ + struct iax_ies ies; + struct iax_session *cur; + + if ((fh->type != AST_FRAME_IAX) || (subclass != IAX_COMMAND_TXCNT) || (!datalen)) { + return NULL; /* special handling for TXCNT only */ + } + memcpy(buf, fh->iedata, datalen); /* prepare local buf for iax_parse_ies() */ + + if (iax_parse_ies(&ies, buf, datalen)) { + return NULL; /* Unable to parse IE's */ + } + if (!ies.transferid) { + return NULL; /* TXCNT without proper IAX_IE_TRANSFERID */ + } + iax_mutex_lock(session_mutex); + for( cur=sessions; cur; cur=cur->next ) { + if ((cur->transferring) && (cur->transferid == ies.transferid) && + (cur->callno == dcallno) && (cur->transfercallno == callno)) { + /* We're transferring --- + * skip address/port checking which would fail while remote peer behind symmetric NAT + * verify transferid instead + */ + cur->transfer.sin_addr.s_addr = sin->sin_addr.s_addr; /* setup for further handling */ + cur->transfer.sin_port = sin->sin_port; + break; + } + } + iax_mutex_unlock(session_mutex); + return cur; +} + +struct iax_event *iax_net_process(unsigned char *buf, int len, struct sockaddr_in *sin) +{ + struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)buf; + struct ast_iax2_mini_hdr *mh = (struct ast_iax2_mini_hdr *)buf; + struct iax_session *session; + + if (ntohs(fh->scallno) & IAX_FLAG_FULL) { + int subclass = uncompress_subclass(fh->csub); + int makenew = 0; + + /* Full size header */ + if (len < sizeof(struct ast_iax2_full_hdr)) { + DEBU(G "Short header received from %s\n", inet_ntoa(sin->sin_addr)); + IAXERROR "Short header received from %s\n", inet_ntoa(sin->sin_addr)); + return NULL; + } + /* Only allow it to make new sessions on types where that makes sense */ + if ((fh->type == AST_FRAME_IAX) && ((subclass == IAX_COMMAND_NEW) || + (subclass == IAX_COMMAND_POKE) || + (subclass == IAX_COMMAND_REGREL) || + (subclass == IAX_COMMAND_REGREQ))) { + makenew = 1; + } + + /* We have a full header, process appropriately */ + session = iax_find_session(sin, ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, makenew); + if (!session) + session = iax_txcnt_session(fh, len-sizeof(struct ast_iax2_full_hdr), sin, ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS); + if (session) + return iax_header_to_event(session, fh, len - sizeof(struct ast_iax2_full_hdr), sin); + /* if we get here, the frame was invalid for some reason, we should probably send IAX_COMMAND_INVAL (as long as the subclass was not already IAX_COMMAND_INVAL) */ + DEBU(G "No session?\n"); + return NULL; + } else { + if (len < sizeof(struct ast_iax2_mini_hdr)) { + DEBU(G "Short header received from %s\n", inet_ntoa(sin->sin_addr)); + IAXERROR "Short header received from %s\n", inet_ntoa(sin->sin_addr)); + return NULL; + } + /* Miniature, voice frame */ + session = iax_find_session(sin, ntohs(fh->scallno), 0, 0); + if (session) + return iax_miniheader_to_event(session, mh, len - sizeof(struct ast_iax2_mini_hdr)); + DEBU(G "No session?\n"); + return NULL; + } +} + +static struct iax_sched *iax_get_sched(time_in_ms_t time_in_ms) +{ + struct iax_sched *cur, *prev=NULL; + iax_mutex_lock(sched_mutex); + cur = schedq; + /* Check the event schedule first. */ + while(cur) { + if (time_in_ms > cur->when) { + /* Take it out of the event queue */ + if (prev) { + prev->next = cur->next; + } else { + schedq = cur->next; + } + iax_mutex_unlock(sched_mutex); + return cur; + } + cur = cur->next; + } + iax_mutex_unlock(sched_mutex); + return NULL; +} + +struct iax_event *iax_get_event(int blocking) +{ + struct iax_event *event; + struct iax_frame *frame; + time_in_ms_t time_in_ms; + struct iax_sched *cur; + + if (do_shutdown) { + __iax_shutdown(); + do_shutdown = 0; + return NULL; + } + time_in_ms = current_time_in_ms(); + while((cur = iax_get_sched(time_in_ms))) { + + event = cur->event; + frame = cur->frame; + if (event) { + + /* See if this is an event we need to handle */ + event = handle_event(event); + if (event) { + free(cur); + return event; + } + } else if(frame) { + /* It's a frame, transmit it and schedule a retry */ + if (frame->retries < 0) { + /* It's been acked. No need to send it. Destroy the old + frame. If final, destroy the session. */ + if (frame->final) { + iax_mutex_lock(session_mutex); + destroy_session(frame->session); + iax_mutex_unlock(session_mutex); + } + if (frame->data) + free(frame->data); + free(frame); + } else if (frame->retries == 0) { + if (frame->transfer) { + /* Send a transfer reject since we weren't able to connect */ + iax_send_txrej(frame->session); + if (frame->data) + free(frame->data); + free(frame); + free(cur); + break; + } else { + /* We haven't been able to get an ACK on this packet. If a + final frame, destroy the session, otherwise, pass up timeout */ + if (frame->final) { + iax_mutex_lock(session_mutex); + destroy_session(frame->session); + iax_mutex_unlock(session_mutex); + if (frame->data) + free(frame->data); + free(frame); + } else { + event = (struct iax_event *)malloc(sizeof(struct iax_event)); + if (event) { + event->etype = IAX_EVENT_TIMEOUT; + event->session = frame->session; + if (frame->data) + free(frame->data); + free(frame); + free(cur); + return handle_event(event); + } + } + } + } else { + struct ast_iax2_full_hdr *fh; + /* Decrement remaining retries */ + frame->retries--; + /* Multiply next retry time by 4, not above MAX_RETRY_TIME though */ + frame->retrytime *= 4; + /* Keep under 1000 ms if this is a transfer packet */ + if (!frame->transfer) { + if (frame->retrytime > MAX_RETRY_TIME) + frame->retrytime = MAX_RETRY_TIME; + } else if (frame->retrytime > 1000) + frame->retrytime = 1000; + fh = (struct ast_iax2_full_hdr *)(frame->data); + fh->dcallno = htons(IAX_FLAG_RETRANS | frame->dcallno); + iax_xmit_frame(frame); + /* Schedule another retransmission */ + DEBU(G "Scheduling retransmission %d\n", frame->retries); + iax_sched_add(NULL, frame, NULL, NULL, frame->retrytime); + } + } else if (cur->func) { + cur->func(cur->arg); + } + free(cur); + } + +#ifdef NEWJB + /* get jitterbuffer-scheduled events */ + { + struct iax_session *cur; + jb_frame frame; + iax_mutex_lock(session_mutex); + for(cur=sessions; cur; cur=cur->next) { + int ret; + time_in_ms_t now; + time_in_ms_t next; + + now = time_in_ms - cur->rxcore; + if(now > (next = jb_next(cur->jb))) { + ret = jb_get(cur->jb,&frame,now,get_interp_len(cur->voiceformat)); + switch(ret) { + case JB_OK: + // if(frame.type == JB_TYPE_VOICE && next + 20 != jb_next(cur->jb)) fprintf(stderr, "NEXT %ld is not %ld+20!\n", jb_next(cur->jb), next); + event = frame.data; + event = handle_event(event); + if (event) { + iax_mutex_unlock(session_mutex); + return event; + } + break; + case JB_INTERP: + // if(next + 20 != jb_next(cur->jb)) fprintf(stderr, "NEXT %ld is not %ld+20!\n", jb_next(cur->jb), next); + /* create an interpolation frame */ + //fprintf(stderr, "Making Interpolation frame\n"); + event = (struct iax_event *)malloc(sizeof(struct iax_event)); + if (event) { + event->etype = IAX_EVENT_VOICE; + event->subclass = cur->voiceformat; + event->ts = now; /* XXX: ??? applications probably ignore this anyway */ + event->session = cur; + event->datalen = 0; + event = handle_event(event); + if(event) { + iax_mutex_unlock(session_mutex); + return event; + } + } + break; + case JB_DROP: + // if(next != jb_next(cur->jb)) fprintf(stderr, "NEXT %ld is not next %ld!\n", jb_next(cur->jb), next); + iax_event_free(frame.data); + break; + case JB_NOFRAME: + case JB_EMPTY: + /* do nothing */ + break; + default: + /* shouldn't happen */ + break; + } + } + } + iax_mutex_unlock(session_mutex); + } + +#endif + /* Now look for networking events */ + if (blocking) { + /* Block until there is data if desired */ + fd_set fds; + time_in_ms_t nextEventTime; + + FD_ZERO(&fds); + FD_SET(netfd, &fds); + + nextEventTime = iax_time_to_next_event(); + if(nextEventTime < 0 && blocking > 1) { + nextEventTime = blocking; + } + if(nextEventTime < 0) + select(netfd + 1, &fds, NULL, NULL, NULL); + else + { + struct timeval nextEvent; + + nextEvent.tv_sec = (long)(nextEventTime / 1000); + nextEvent.tv_usec = (long)((nextEventTime % 1000) * 1000); + + select(netfd + 1, &fds, NULL, NULL, &nextEvent); + } + + } + event = iax_net_read(); + + return handle_event(event); +} + +struct sockaddr_in iax_get_peer_addr(struct iax_session *session) +{ + return session->peeraddr; +} + +char *iax_get_peer_ip(struct iax_session *session) +{ + return inet_ntoa(session->peeraddr.sin_addr); +} + +char *iax_event_get_apparent_ip(struct iax_event *event) +{ + return inet_ntoa(event->ies.apparent_addr->sin_addr); +} + +void iax_session_destroy(struct iax_session **session) +{ + iax_mutex_lock(session_mutex); + destroy_session(*session); + *session = NULL; + iax_mutex_unlock(session_mutex); +} + +void iax_event_free(struct iax_event *event) +{ + /* + We gave the user a chance to play with the session now we need to destroy it + if you are not calling this function on every event you read you are now going + to leak sessions as well as events! + */ + switch(event->etype) { + case IAX_EVENT_REJECT: + case IAX_EVENT_HANGUP: + /* Destroy this session -- it's no longer valid */ + if (event->session) { /* maybe the user did it already */ + iax_mutex_lock(session_mutex); + destroy_session(event->session); + iax_mutex_unlock(session_mutex); + } + break; + } + free(event); +} + +int iax_get_fd(void) +{ + /* Return our network file descriptor. The client can select on this (probably with other + things, or can add it to a network add sort of gtk_input_add for example */ + return netfd; +} + +int iax_quelch_moh(struct iax_session *session, int MOH) +{ + + struct iax_ie_data ied; //IE Data Structure (Stuff To Send) + memset(&ied, 0, sizeof(ied)); + + // You can't quelch the quelched + if (session->quelch == 1) + return -1; + + if (MOH) { + iax_ie_append(&ied, IAX_IE_MUSICONHOLD); + session->transfer_moh = 1; + } + + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_QUELCH, 0, ied.buf, ied.pos, -1); +} Added: freeswitch/trunk/src/mod/endpoints/mod_iax/iax.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/iax.h Thu May 8 19:25:36 2008 @@ -0,0 +1,86 @@ +/* + * libIAX + * + * Implementation of Inter-IAXerisk eXchange + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU Lesser (Library) General Public License + */ + +#ifndef _IAX_H +#define _IAX_H + +/* Max version of IAX protocol we support */ +#define IAX_PROTO_VERSION 1 + +#define IAX_MAX_CALLS 32768 + +#define IAX_FLAG_FULL 0x8000 + +#define IAX_FLAG_SC_LOG 0x80 + +#define IAX_MAX_SHIFT 0x1F + +/* Maximum size of an IAX frame (max size of UDP frame) */ +#define IAX_MAX_BUF_SIZE 65536 + +/* Subclass for IAX_FRAME_IAX */ +#define IAX_COMMAND_NEW 1 +#define IAX_COMMAND_PING 2 +#define IAX_COMMAND_PONG 3 +#define IAX_COMMAND_ACK 4 +#define IAX_COMMAND_HANGUP 5 +#define IAX_COMMAND_REJECT 6 +#define IAX_COMMAND_ACCEPT 7 +#define IAX_COMMAND_AUTHREQ 8 +#define IAX_COMMAND_AUTHREP 9 +#define IAX_COMMAND_INVAL 10 +#define IAX_COMMAND_LAGRQ 11 +#define IAX_COMMAND_LAGRP 12 +#define IAX_COMMAND_REGREQ 13 /* Registration request */ +#define IAX_COMMAND_REGAUTH 14 /* Registration authentication required */ +#define IAX_COMMAND_REGACK 15 /* Registration accepted */ +#define IAX_COMMAND_REGREJ 16 /* Registration rejected */ +#define IAX_COMMAND_REGREL 17 /* Force release of registration */ +#define IAX_COMMAND_VNAK 18 /* If we receive voice before valid first voice frame, send this */ +#define IAX_COMMAND_DPREQ 19 /* Request status of a dialplan entry */ +#define IAX_COMMAND_DPREP 20 /* Request status of a dialplan entry */ +#define IAX_COMMAND_DIAL 21 /* Request a dial on channel brought up TBD */ +#define IAX_COMMAND_TXREQ 22 /* Transfer Request */ +#define IAX_COMMAND_TXCNT 23 /* Transfer Connect */ +#define IAX_COMMAND_TXACC 24 /* Transfer Accepted */ +#define IAX_COMMAND_TXREADY 25 /* Transfer ready */ +#define IAX_COMMAND_TXREL 26 /* Transfer release */ +#define IAX_COMMAND_TXREJ 27 /* Transfer reject */ +#define IAX_COMMAND_QUELCH 28 /* Stop audio/video transmission */ +#define IAX_COMMAND_UNQUELCH 29 /* Resume audio/video transmission */ + +#define IAX_DEFAULT_REG_EXPIRE 60 + +#define IAX_DEFAULT_PORTNO 5036 + +/* Full frames are always delivered reliably */ +struct iax_full_hdr { + short callno; /* Source call number -- high bit must be 1 */ + short dcallno; /* Destination call number */ + unsigned int ts; /* 32-bit timestamp in milliseconds */ + unsigned short seqno; /* Packet number */ + char type; /* Frame type */ + unsigned char csub; /* Compressed subclass */ + char data[]; +}; + +/* Mini header is used only for voice frames -- delivered unreliably */ +struct iax_mini_hdr { + short callno; /* Source call number -- high bit must be 0 */ + unsigned short ts; /* 16-bit Timestamp (high 16 bits from last IAX_full_hdr) */ + /* Frametype implicitly VOICE_FRAME */ + /* subclass implicit from last IAX_full_hdr */ + char data[]; +}; + +#endif Added: freeswitch/trunk/src/mod/endpoints/mod_iax/iax2-parser.c ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/iax2-parser.c Thu May 8 19:25:36 2008 @@ -0,0 +1,840 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Implementation of Inter-Asterisk eXchange + * + * Copyright (C) 2003-2004, Digium + * + * Mark Spencer <markster@digium.com> + * + * This program is free software, distributed under the terms of + * the GNU Lesser (Library) General Public License + */ + +#ifdef WIN32 +#include <winsock.h> +#define snprintf _snprintf +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#ifndef _MSC_VER +#include <unistd.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include "frame.h" +#include "iax2.h" +#include "iax2-parser.h" + +static int frames = 0; +static int iframes = 0; +static int oframes = 0; + +#ifdef ALIGN32 +static unsigned int get_uint32(unsigned char *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +static unsigned short get_uint16(unsigned char *p) +{ + return (p[0] << 8) | p[1] ; +} + +#else +#define get_uint32(p) (*((unsigned int *)(p))) +#define get_uint16(p) (*((unsigned short *)(p))) +#endif + + +static void internaloutput(const char *str) +{ + printf(str); +} + +static void internalerror(const char *str) +{ + fprintf(stderr, "WARNING: %s", str); +} + +static void (*outputf)(const char *str) = internaloutput; +static void (*errorf)(const char *str) = internalerror; + +static void dump_addr(char *output, int maxlen, void *value, int len) +{ + struct sockaddr_in sin; + if (len == sizeof(sin)) { + memcpy(&sin, value, len); + snprintf(output, maxlen, "IPV4 %s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + } else { + snprintf(output, maxlen, "Invalid Address"); + } +} + +static void dump_string(char *output, int maxlen, void *value, int len) +{ + maxlen--; + if (maxlen > len) + maxlen = len; + strncpy(output,value, maxlen); + output[maxlen] = '\0'; +} + +static void dump_int(char *output, int maxlen, void *value, int len) +{ + if (len == (int)sizeof(unsigned int)) + snprintf(output, maxlen, "%lu", (unsigned long)ntohl(get_uint32(value))); + else + snprintf(output, maxlen, "Invalid INT"); +} + +static void dump_short(char *output, int maxlen, void *value, int len) +{ + if (len == (int)sizeof(unsigned short)) + snprintf(output, maxlen, "%d", ntohs(get_uint16(value))); + else + snprintf(output, maxlen, "Invalid SHORT"); +} + +static void dump_byte(char *output, int maxlen, void *value, int len) +{ + if (len == (int)sizeof(unsigned char)) + snprintf(output, maxlen, "%d", *((unsigned char *)value)); + else + snprintf(output, maxlen, "Invalid BYTE"); +} + +#if 0 +static void dump_ipaddr(char *output, int maxlen, void *value, int len) +{ + struct sockaddr_in sin; + if (len == (int)sizeof(unsigned int)) { + memcpy(&sin.sin_addr, value, len); + snprintf(output, maxlen, "%s", inet_ntoa(sin.sin_addr)); + } else + snprintf(output, maxlen, "Invalid IPADDR"); +} + +static void dump_prov_flags(char *output, int maxlen, void *value, int len) +{ + if (len == (int)sizeof(unsigned int)) + snprintf(output, maxlen, "%lu (%s)", (unsigned long)ntohl(get_uint32(value)), + "PROVISION_PARSING_NOT_IMPLEMENTED"); + else + snprintf(output, maxlen, "Invalid INT"); +} +#endif + +static void dump_samprate(char *output, int maxlen, void *value, int len) +{ + char tmp[256]=""; + int sr; + if (len == (int)sizeof(unsigned short)) { + sr = ntohs(*((unsigned short *)value)); + if (sr & IAX_RATE_8KHZ) + strcat(tmp, ",8khz"); + if (sr & IAX_RATE_11KHZ) + strcat(tmp, ",11.025khz"); + if (sr & IAX_RATE_16KHZ) + strcat(tmp, ",16khz"); + if (sr & IAX_RATE_32KHZ) + strcat(tmp, ",32khz"); + if (sr & IAX_RATE_22KHZ) + strcat(tmp, ",22.05khz"); + if (sr & IAX_RATE_44KHZ) + strcat(tmp, ",44.1khz"); + if (sr & IAX_RATE_48KHZ) + strcat(tmp, ",48khz"); + if (strlen(tmp)) + strncpy(output, &tmp[1], maxlen - 1); + else + strncpy(output, "None specified!\n", maxlen - 1); + } else + snprintf(output, maxlen, "Invalid SHORT"); + +} + +static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len); +static void dump_prov(char *output, int maxlen, void *value, int len) +{ + dump_prov_ies(output, maxlen, value, len); +} + +static struct iax2_ie { + int ie; + char *name; + void (*dump)(char *output, int maxlen, void *value, int len); +} ies[] = { + { IAX_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string }, + { IAX_IE_CALLING_NUMBER, "CALLING NUMBER", dump_string }, + { IAX_IE_CALLING_ANI, "ANI", dump_string }, + { IAX_IE_CALLING_NAME, "CALLING NAME", dump_string }, + { IAX_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string }, + { IAX_IE_USERNAME, "USERNAME", dump_string }, + { IAX_IE_PASSWORD, "PASSWORD", dump_string }, + { IAX_IE_CAPABILITY, "CAPABILITY", dump_int }, + { IAX_IE_FORMAT, "FORMAT", dump_int }, + { IAX_IE_LANGUAGE, "LANGUAGE", dump_string }, + { IAX_IE_CODEC_PREFS, "CODEC_PREFS", dump_string }, + { IAX_IE_VERSION, "VERSION", dump_short }, + { IAX_IE_ADSICPE, "ADSICPE", dump_short }, + { IAX_IE_DNID, "DNID", dump_string }, + { IAX_IE_AUTHMETHODS, "AUTHMETHODS", dump_short }, + { IAX_IE_CHALLENGE, "CHALLENGE", dump_string }, + { IAX_IE_MD5_RESULT, "MD5 RESULT", dump_string }, + { IAX_IE_RSA_RESULT, "RSA RESULT", dump_string }, + { IAX_IE_APPARENT_ADDR, "APPARENT ADDRESS", dump_addr }, + { IAX_IE_REFRESH, "REFRESH", dump_short }, + { IAX_IE_DPSTATUS, "DIALPLAN STATUS", dump_short }, + { IAX_IE_CALLNO, "CALL NUMBER", dump_short }, + { IAX_IE_CAUSE, "CAUSE", dump_string }, + { IAX_IE_IAX_UNKNOWN, "UNKNOWN IAX CMD", dump_byte }, + { IAX_IE_MSGCOUNT, "MESSAGE COUNT", dump_short }, + { IAX_IE_AUTOANSWER, "AUTO ANSWER REQ" }, + { IAX_IE_TRANSFERID, "TRANSFER ID", dump_int }, + { IAX_IE_RDNIS, "REFERRING DNIS", dump_string }, + { IAX_IE_PROVISIONING, "PROVISIONING", dump_prov }, + { IAX_IE_AESPROVISIONING, "AES PROVISIONG" }, + { IAX_IE_DATETIME, "DATE TIME", dump_int }, + { IAX_IE_DEVICETYPE, "DEVICE TYPE", dump_string }, + { IAX_IE_SERVICEIDENT, "SERVICE IDENT", dump_string }, + { IAX_IE_FIRMWAREVER, "FIRMWARE VER", dump_short }, + { IAX_IE_FWBLOCKDESC, "FW BLOCK DESC", dump_int }, + { IAX_IE_FWBLOCKDATA, "FW BLOCK DATA" }, + { IAX_IE_PROVVER, "PROVISIONG VER", dump_int }, + { IAX_IE_CALLINGPRES, "CALLING PRESNTN", dump_byte }, + { IAX_IE_CALLINGTON, "CALLING TYPEOFNUM", dump_byte }, + { IAX_IE_CALLINGTNS, "CALLING TRANSITNET", dump_short }, + { IAX_IE_SAMPLINGRATE, "SAMPLINGRATE", dump_samprate }, + { IAX_IE_RR_JITTER, "RR_JITTER", dump_int }, + { IAX_IE_RR_LOSS, "RR_LOSS", dump_int }, + { IAX_IE_RR_PKTS, "RR_PKTS", dump_int }, + { IAX_IE_RR_DELAY, "RR_DELAY", dump_short }, + { IAX_IE_RR_DROPPED, "RR_DROPPED", dump_int }, + { IAX_IE_RR_OOO, "RR_OOO", dump_int }, +}; + +const char *iax_ie2str(int ie) +{ + int x; + for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) { + if (ies[x].ie == ie) + return ies[x].name; + } + return "Unknown IE"; +} + + +static void dump_prov_ies(char *output, int maxlen, unsigned char *iedata, int len) +{ + int ielen; + int ie; + int found; + char tmp[256]; + if (len < 2) + return; + strcpy(output, "\n"); + maxlen -= (int)strlen(output); output += strlen(output); + while(len > 2) { + ie = iedata[0]; + ielen = iedata[1]; + if (ielen + 2> len) { + snprintf(tmp, (int)sizeof(tmp), "Total Prov IE length of %d bytes exceeds remaining prov frame length of %d bytes\n", ielen + 2, len); + strncpy(output, tmp, maxlen - 1); + maxlen -= (int)strlen(output); output += strlen(output); + return; + } + found = 0; + if (!found) { + snprintf(tmp, (int)sizeof(tmp), " Unknown Prov IE %03d : Present\n", ie); + strncpy(output, tmp, maxlen - 1); + maxlen -= (int)strlen(output); output += strlen(output); + } + iedata += (2 + ielen); + len -= (2 + ielen); + } +} + +static void dump_ies(unsigned char *iedata, int len) +{ + int ielen; + int ie; + int x; + int found; + char interp[1024]; + char tmp[1024]; + if (len < 2) + return; + while(len > 2) { + ie = iedata[0]; + ielen = iedata[1]; + if (ielen + 2> len) { + snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len); + outputf(tmp); + return; + } + found = 0; + for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) { + if (ies[x].ie == ie) { + if (ies[x].dump) { + ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen); + snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", ies[x].name, interp); + outputf(tmp); + } else { + if (ielen) + snprintf(interp, (int)sizeof(interp), "%d bytes", ielen); + else + strcpy(interp, "Present"); + snprintf(tmp, (int)sizeof(tmp), " %-15.15s : %s\n", ies[x].name, interp); + outputf(tmp); + } + found++; + } + } + if (!found) { + snprintf(tmp, (int)sizeof(tmp), " Unknown IE %03d : Present\n", ie); + outputf(tmp); + } + iedata += (2 + ielen); + len -= (2 + ielen); + } + outputf("\n"); +} + +void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen) +{ + const char *frames[] = { + "(0?)", + "DTMF ", + "VOICE ", + "VIDEO ", + "CONTROL", + "NULL ", + "IAX ", + "TEXT ", + "IMAGE " }; + const char *iaxs[] = { + "(0?)", + "NEW ", + "PING ", + "PONG ", + "ACK ", + "HANGUP ", + "REJECT ", + "ACCEPT ", + "AUTHREQ", + "AUTHREP", + "INVAL ", + "LAGRQ ", + "LAGRP ", + "REGREQ ", + "REGAUTH", + "REGACK ", + "REGREJ ", + "REGREL ", + "VNAK ", + "DPREQ ", + "DPREP ", + "DIAL ", + "TXREQ ", + "TXCNT ", + "TXACC ", + "TXREADY", + "TXREL ", + "TXREJ ", + "QUELCH ", + "UNQULCH", + "POKE", + "PAGE", + "MWI", + "UNSUPPORTED", + "TRANSFER", + "PROVISION", + "FWDOWNLD", + "FWDATA" + }; + const char *cmds[] = { + "(0?)", + "HANGUP ", + "RING ", + "RINGING", + "ANSWER ", + "BUSY ", + "TKOFFHK ", + "OFFHOOK" }; + struct ast_iax2_full_hdr *fh; + char retries[20]; + char class2[20]; + char subclass2[20]; + const char *class; + const char *subclass; + char tmp[256]; + + if (f) { + fh = f->data; + snprintf(retries, (int)sizeof(retries), "%03d", f->retries); + } else { + fh = fhi; + if (ntohs(fh->dcallno) & IAX_FLAG_RETRANS) + strcpy(retries, "Yes"); + else + strcpy(retries, " No"); + } + if (!(ntohs(fh->scallno) & IAX_FLAG_FULL)) { + /* Don't mess with mini-frames */ + return; + } + if (fh->type > (int)sizeof(frames)/(int)sizeof(char *)) { + snprintf(class2, (int)sizeof(class2), "(%d?)", fh->type); + class = class2; + } else { + class = frames[(int)fh->type]; + } + if (fh->type == AST_FRAME_DTMF) { + sprintf(subclass2, "%c", fh->csub); + subclass = subclass2; + } else if (fh->type == AST_FRAME_IAX) { + if (fh->csub >= (int)sizeof(iaxs)/(int)sizeof(iaxs[0])) { + snprintf(subclass2, (int)sizeof(subclass2), "(%d?)", fh->csub); + subclass = subclass2; + } else { + subclass = iaxs[(int)fh->csub]; + } + } else if (fh->type == AST_FRAME_CONTROL) { + if (fh->csub > (int)sizeof(cmds)/(int)sizeof(char *)) { + snprintf(subclass2, (int)sizeof(subclass2), "(%d?)", fh->csub); + subclass = subclass2; + } else { + subclass = cmds[(int)fh->csub]; + } + } else { + snprintf(subclass2, (int)sizeof(subclass2), "%d", fh->csub); + subclass = subclass2; + } +snprintf(tmp, (int)sizeof(tmp), +"%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s Subclass: %s\n", + (rx ? "Rx" : "Tx"), + retries, fh->oseqno, fh->iseqno, class, subclass); + outputf(tmp); +snprintf(tmp, (int)sizeof(tmp), +" Timestamp: %05lums SCall: %5.5d DCall: %5.5d [%s:%d]\n", + (unsigned long)ntohl(fh->ts), + ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, + inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + outputf(tmp); + if (fh->type == AST_FRAME_IAX) + dump_ies(fh->iedata, datalen); +} + +int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen) +{ + char tmp[256]; + if (datalen > ((int)sizeof(ied->buf) - ied->pos)) { + snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", iax_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos); + errorf(tmp); + return -1; + } + ied->buf[ied->pos++] = ie; + ied->buf[ied->pos++] = datalen; + memcpy(ied->buf + ied->pos, data, datalen); + ied->pos += datalen; + return 0; +} + +int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, struct sockaddr_in *sin) +{ + return iax_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in)); +} + +int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value) +{ + unsigned int newval; + newval = htonl(value); + return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval)); +} + +int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value) +{ + unsigned short newval; + newval = htons(value); + return iax_ie_append_raw(ied, ie, &newval, (int)sizeof(newval)); +} + +int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, const unsigned char *str) +{ + return iax_ie_append_raw(ied, ie, str, (int)strlen((char *) str)); +} + +int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat) +{ + return iax_ie_append_raw(ied, ie, &dat, 1); +} + +int iax_ie_append(struct iax_ie_data *ied, unsigned char ie) +{ + return iax_ie_append_raw(ied, ie, NULL, 0); +} + +void iax_set_output(void (*func)(const char *)) +{ + outputf = func; +} + +void iax_set_error(void (*func)(const char *)) +{ + errorf = func; +} + +int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen) +{ + /* Parse data into information elements */ + int len; + int ie; + char tmp[256]; + memset(ies, 0, (int)sizeof(struct iax_ies)); + ies->msgcount = -1; + ies->firmwarever = -1; + ies->calling_ton = -1; + ies->calling_tns = -1; + ies->calling_pres = -1; + ies->samprate = IAX_RATE_8KHZ; + while(datalen >= 2) { + ie = data[0]; + len = data[1]; + if (len > datalen - 2) { + errorf("Information element length exceeds message size\n"); + return -1; + } + switch(ie) { + case IAX_IE_CALLED_NUMBER: + ies->called_number = (char *) data + 2; + break; + case IAX_IE_CALLING_NUMBER: + ies->calling_number = (char *) data + 2; + break; + case IAX_IE_CALLING_ANI: + ies->calling_ani = (char *) data + 2; + break; + case IAX_IE_CALLING_NAME: + ies->calling_name = (char *) data + 2; + break; + case IAX_IE_CALLED_CONTEXT: + ies->called_context = (char *) data + 2; + break; + case IAX_IE_USERNAME: + ies->username = (char *) data + 2; + break; + case IAX_IE_PASSWORD: + ies->password = (char *) data + 2; + break; + case IAX_IE_CAPABILITY: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting capability to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else + ies->capability = ntohl(get_uint32(data + 2)); + break; + case IAX_IE_FORMAT: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting format to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else + ies->format = ntohl(get_uint32(data + 2)); + break; + case IAX_IE_LANGUAGE: + ies->language = (char *) data + 2; + break; + case IAX_IE_CODEC_PREFS: + ies->codec_prefs = (char *) data + 2; + break; + case IAX_IE_VERSION: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->version = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_ADSICPE: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting adsicpe to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->adsicpe = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_SAMPLINGRATE: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting samplingrate to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->samprate = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_DNID: + ies->dnid = (char *) data + 2; + break; + case IAX_IE_RDNIS: + ies->rdnis = (char *) data + 2; + break; + case IAX_IE_AUTHMETHODS: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting authmethods to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->authmethods = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_CHALLENGE: + ies->challenge = (char *) data + 2; + break; + case IAX_IE_MD5_RESULT: + ies->md5_result = (char *) data + 2; + break; + case IAX_IE_RSA_RESULT: + ies->rsa_result = (char *) data + 2; + break; + case IAX_IE_APPARENT_ADDR: + ies->apparent_addr = ((struct sockaddr_in *)(data + 2)); + break; + case IAX_IE_REFRESH: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting refresh to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->refresh = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_DPSTATUS: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting dpstatus to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->dpstatus = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_CALLNO: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting callno to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->callno = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_CAUSE: + ies->cause = (char *) data + 2; + break; + case IAX_IE_CAUSECODE: + if (len != 1) { + snprintf(tmp, (int)sizeof(tmp), "Expecting causecode to be single byte but was %d\n", len); + errorf(tmp); + } else { + ies->causecode = data[2]; + } + break; + case IAX_IE_IAX_UNKNOWN: + if (len == 1) + ies->iax_unknown = data[2]; + else { + snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len); + errorf(tmp); + } + break; + case IAX_IE_MSGCOUNT: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting msgcount to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->msgcount = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_AUTOANSWER: + ies->autoanswer = 1; + break; + case IAX_IE_MUSICONHOLD: + ies->musiconhold = 1; + break; + case IAX_IE_TRANSFERID: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting transferid to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else + ies->transferid = ntohl(get_uint32(data + 2)); + break; + case IAX_IE_DATETIME: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting date/time to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else + ies->datetime = ntohl(get_uint32(data + 2)); + break; + case IAX_IE_FIRMWAREVER: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting firmwarever to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->firmwarever = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_DEVICETYPE: + ies->devicetype = (char *) data + 2; + break; + case IAX_IE_SERVICEIDENT: + ies->serviceident = (char *) data + 2; + break; + case IAX_IE_FWBLOCKDESC: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected block desc to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else + ies->fwdesc = ntohl(get_uint32(data + 2)); + break; + case IAX_IE_FWBLOCKDATA: + ies->fwdata = data + 2; + ies->fwdatalen = len; + break; + case IAX_IE_PROVVER: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected provisioning version to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->provverpres = 1; + ies->provver = ntohl(get_uint32(data + 2)); + } + break; + case IAX_IE_CALLINGPRES: + if (len == 1) + ies->calling_pres = data[2]; + else { + snprintf(tmp, (int)sizeof(tmp), "Expected single byte callingpres, but was %d long\n", len); + errorf(tmp); + } + break; + case IAX_IE_CALLINGTON: + if (len == 1) + ies->calling_ton = data[2]; + else { + snprintf(tmp, (int)sizeof(tmp), "Expected single byte callington, but was %d long\n", len); + errorf(tmp); + } + break; + case IAX_IE_CALLINGTNS: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expecting callingtns to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else + ies->calling_tns = ntohs(get_uint16(data + 2)); + break; + case IAX_IE_RR_JITTER: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected jitter rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_jitter = ntohl(get_uint32(data + 2)); + } + break; + case IAX_IE_RR_LOSS: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_loss = ntohl(get_uint32(data + 2)); + } + break; + case IAX_IE_RR_PKTS: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_pkts = ntohl(get_uint32(data + 2)); + } + break; + case IAX_IE_RR_DELAY: + if (len != (int)sizeof(unsigned short)) { + snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len); + errorf(tmp); + } else { + ies->rr_delay = ntohs(get_uint16(data + 2)); + } + break; + case IAX_IE_RR_DROPPED: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_dropped = ntohl(get_uint32(data + 2)); + } + break; + case IAX_IE_RR_OOO: + if (len != (int)sizeof(unsigned int)) { + snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len); + errorf(tmp); + } else { + ies->rr_ooo = ntohl(get_uint32(data + 2)); + } + break; + default: + snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len); + outputf(tmp); + } + /* Overwrite information element with 0, to null terminate previous portion */ + data[0] = 0; + datalen -= (len + 2); + data += (len + 2); + } + /* Null-terminate last field */ + *data = '\0'; + if (datalen) { + errorf("Invalid information element contents, strange boundary\n"); + return -1; + } + return 0; +} + +void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f) +{ + fr->af.frametype = f->frametype; + fr->af.subclass = f->subclass; + fr->af.mallocd = 0; /* Our frame is static relative to the container */ + fr->af.datalen = f->datalen; + fr->af.samples = f->samples; + fr->af.offset = AST_FRIENDLY_OFFSET; + fr->af.src = f->src; + fr->af.data = fr->afdata; + if (fr->af.datalen) + memcpy(fr->af.data, f->data, fr->af.datalen); +} + +struct iax_frame *iax_frame_new(int direction, int datalen) +{ + struct iax_frame *fr; + fr = malloc((int)sizeof(struct iax_frame) + datalen); + if (fr) { + fr->direction = direction; + fr->retrans = -1; + frames++; + if (fr->direction == DIRECTION_INGRESS) + iframes++; + else + oframes++; + } + return fr; +} + +void iax_frame_free(struct iax_frame *fr) +{ + /* Note: does not remove from scheduler! */ + if (fr->direction == DIRECTION_INGRESS) + iframes--; + else if (fr->direction == DIRECTION_OUTGRESS) + oframes--; + else { + errorf("Attempt to double free frame detected\n"); + return; + } + fr->direction = 0; + free(fr); + frames--; +} + +int iax_get_frames(void) { return frames; } +int iax_get_iframes(void) { return iframes; } +int iax_get_oframes(void) { return oframes; } Added: freeswitch/trunk/src/mod/endpoints/mod_iax/iax2-parser.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/iax2-parser.h Thu May 8 19:25:36 2008 @@ -0,0 +1,145 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Implementation of Inter-Asterisk eXchange + * + * Copyright (C) 2003, Digium + * + * Mark Spencer <markster@digium.com> + * + * This program is free software, distributed under the terms of + * the GNU Lesser (Library) General Public License + */ + +#ifndef _IAX2_PARSER_H +#define _IAX2_PARSER_H + +struct iax_ies { + char *called_number; + char *calling_number; + char *calling_ani; + char *calling_name; + int calling_ton; + int calling_tns; + int calling_pres; + char *called_context; + char *username; + char *password; + unsigned int capability; + unsigned int format; + char *codec_prefs; + char *language; + int version; + unsigned short adsicpe; + char *dnid; + char *rdnis; + unsigned int authmethods; + char *challenge; + char *md5_result; + char *rsa_result; + struct sockaddr_in *apparent_addr; + unsigned short refresh; + unsigned short dpstatus; + unsigned short callno; + char *cause; + unsigned char causecode; + unsigned char iax_unknown; + int msgcount; + int autoanswer; + int musiconhold; + unsigned int transferid; + unsigned int datetime; + char *devicetype; + char *serviceident; + int firmwarever; + unsigned int fwdesc; + unsigned char *fwdata; + unsigned char fwdatalen; + unsigned int provver; + unsigned short samprate; + unsigned int provverpres; + unsigned int rr_jitter; + unsigned int rr_loss; + unsigned int rr_pkts; + unsigned short rr_delay; + unsigned int rr_dropped; + unsigned int rr_ooo; +}; + +#define DIRECTION_INGRESS 1 +#define DIRECTION_OUTGRESS 2 + +struct iax_frame { +#ifdef LIBIAX + struct iax_session *session; + struct iax_event *event; +#endif + + /* /Our/ call number */ + unsigned short callno; + /* /Their/ call number */ + unsigned short dcallno; + /* Start of raw frame (outgoing only) */ + void *data; + /* Length of frame (outgoing only) */ + int datalen; + /* How many retries so far? */ + int retries; + /* Outgoing relative timestamp (ms) */ + time_in_ms_t ts; + /* How long to wait before retrying */ + time_in_ms_t retrytime; + /* Are we received out of order? */ + int outoforder; + /* Have we been sent at all yet? */ + int sentyet; + /* Outgoing Packet sequence number */ + int oseqno; + /* Next expected incoming packet sequence number */ + int iseqno; + /* Non-zero if should be sent to transfer peer */ + int transfer; + /* Non-zero if this is the final message */ + int final; + /* Ingress or outgres */ + int direction; + /* Retransmission ID */ + int retrans; + /* Easy linking */ + struct iax_frame *next; + struct iax_frame *prev; + /* Actual, isolated frame header */ + struct ast_frame af; + unsigned char unused[AST_FRIENDLY_OFFSET]; + unsigned char afdata[]; /* Data for frame */ +}; + +struct iax_ie_data { + unsigned char buf[1024]; + int pos; +}; + +/* Choose a different function for output */ +extern void iax_set_output(void (*output)(const char *data)); +/* Choose a different function for errors */ +extern void iax_set_error(void (*output)(const char *data)); +extern void iax_showframe(struct iax_frame *f, struct ast_iax2_full_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen); + +extern const char *iax_ie2str(int ie); + +extern int iax_ie_append_raw(struct iax_ie_data *ied, unsigned char ie, const void *data, int datalen); +extern int iax_ie_append_addr(struct iax_ie_data *ied, unsigned char ie, struct sockaddr_in *sin); +extern int iax_ie_append_int(struct iax_ie_data *ied, unsigned char ie, unsigned int value); +extern int iax_ie_append_short(struct iax_ie_data *ied, unsigned char ie, unsigned short value); +extern int iax_ie_append_str(struct iax_ie_data *ied, unsigned char ie, const unsigned char *str); +extern int iax_ie_append_byte(struct iax_ie_data *ied, unsigned char ie, unsigned char dat); +extern int iax_ie_append(struct iax_ie_data *ied, unsigned char ie); +extern int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen); + +extern int iax_get_frames(void); +extern int iax_get_iframes(void); +extern int iax_get_oframes(void); +extern void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f); +extern struct iax_frame *iax_frame_new(int direction, int datalen); +extern void iax_frame_free(struct iax_frame *fr); +#endif Added: freeswitch/trunk/src/mod/endpoints/mod_iax/iax2.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/iax2.h Thu May 8 19:25:36 2008 @@ -0,0 +1,236 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Implementation of Inter-Asterisk eXchange + * + * Copyright (C) 2003, Digium + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU Lesser (Library) General Public License + */ + +#ifndef _IAX2_H +#define _IAX2_H + +typedef long long time_in_ms_t; +#define iax_abs(x) ((x) >= 0 ? (x) : -(x)) + +/* Max version of IAX protocol we support */ +#define IAX_PROTO_VERSION 2 + +#define IAX_MAX_CALLS 32768 + +#define IAX_FLAG_FULL 0x8000 + +#define IAX_FLAG_RETRANS 0x8000 + +#define IAX_FLAG_SC_LOG 0x80 + +#define IAX_MAX_SHIFT 0x1F + +#define IAX_WINDOW 64 + +/* Subclass for AST_FRAME_IAX */ +#define IAX_COMMAND_NEW 1 +#define IAX_COMMAND_PING 2 +#define IAX_COMMAND_PONG 3 +#define IAX_COMMAND_ACK 4 +#define IAX_COMMAND_HANGUP 5 +#define IAX_COMMAND_REJECT 6 +#define IAX_COMMAND_ACCEPT 7 +#define IAX_COMMAND_AUTHREQ 8 +#define IAX_COMMAND_AUTHREP 9 +#define IAX_COMMAND_INVAL 10 +#define IAX_COMMAND_LAGRQ 11 +#define IAX_COMMAND_LAGRP 12 +#define IAX_COMMAND_REGREQ 13 /* Registration request */ +#define IAX_COMMAND_REGAUTH 14 /* Registration authentication required */ +#define IAX_COMMAND_REGACK 15 /* Registration accepted */ +#define IAX_COMMAND_REGREJ 16 /* Registration rejected */ +#define IAX_COMMAND_REGREL 17 /* Force release of registration */ +#define IAX_COMMAND_VNAK 18 /* If we receive voice before valid first voice frame, send this */ +#define IAX_COMMAND_DPREQ 19 /* Request status of a dialplan entry */ +#define IAX_COMMAND_DPREP 20 /* Request status of a dialplan entry */ +#define IAX_COMMAND_DIAL 21 /* Request a dial on channel brought up TBD */ +#define IAX_COMMAND_TXREQ 22 /* Transfer Request */ +#define IAX_COMMAND_TXCNT 23 /* Transfer Connect */ +#define IAX_COMMAND_TXACC 24 /* Transfer Accepted */ +#define IAX_COMMAND_TXREADY 25 /* Transfer ready */ +#define IAX_COMMAND_TXREL 26 /* Transfer release */ +#define IAX_COMMAND_TXREJ 27 /* Transfer reject */ +#define IAX_COMMAND_QUELCH 28 /* Stop audio/video transmission */ +#define IAX_COMMAND_UNQUELCH 29 /* Resume audio/video transmission */ +#define IAX_COMMAND_POKE 30 /* Like ping, but does not require an open connection */ +#define IAX_COMMAND_PAGE 31 /* Paging description */ +#define IAX_COMMAND_MWI 32 /* Stand-alone message waiting indicator */ +#define IAX_COMMAND_UNSUPPORT 33 /* Unsupported message received */ +#define IAX_COMMAND_TRANSFER 34 /* Request remote transfer */ +#define IAX_COMMAND_PROVISION 35 /* Provision device */ +#define IAX_COMMAND_FWDOWNL 36 /* Download firmware */ +#define IAX_COMMAND_FWDATA 37 /* Firmware Data */ + +#define IAX_DEFAULT_REG_EXPIRE 60 /* By default require re-registration once per minute */ + +#define IAX_LINGER_TIMEOUT 10 /* How long to wait before closing bridged call */ + +#define IAX_DEFAULT_PORTNO 4569 + +/* IAX Information elements */ +#define IAX_IE_CALLED_NUMBER 1 /* Number/extension being called - string */ +#define IAX_IE_CALLING_NUMBER 2 /* Calling number - string */ +#define IAX_IE_CALLING_ANI 3 /* Calling number ANI for billing - string */ +#define IAX_IE_CALLING_NAME 4 /* Name of caller - string */ +#define IAX_IE_CALLED_CONTEXT 5 /* Context for number - string */ +#define IAX_IE_USERNAME 6 /* Username (peer or user) for authentication - string */ +#define IAX_IE_PASSWORD 7 /* Password for authentication - string */ +#define IAX_IE_CAPABILITY 8 /* Actual codec capability - unsigned int */ +#define IAX_IE_FORMAT 9 /* Desired codec format - unsigned int */ +#define IAX_IE_LANGUAGE 10 /* Desired language - string */ +#define IAX_IE_VERSION 11 /* Protocol version - short */ +#define IAX_IE_ADSICPE 12 /* CPE ADSI capability - short */ +#define IAX_IE_DNID 13 /* Originally dialed DNID - string */ +#define IAX_IE_AUTHMETHODS 14 /* Authentication method(s) - short */ +#define IAX_IE_CHALLENGE 15 /* Challenge data for MD5/RSA - string */ +#define IAX_IE_MD5_RESULT 16 /* MD5 challenge result - string */ +#define IAX_IE_RSA_RESULT 17 /* RSA challenge result - string */ +#define IAX_IE_APPARENT_ADDR 18 /* Apparent address of peer - struct sockaddr_in */ +#define IAX_IE_REFRESH 19 /* When to refresh registration - short */ +#define IAX_IE_DPSTATUS 20 /* Dialplan status - short */ +#define IAX_IE_CALLNO 21 /* Call number of peer - short */ +#define IAX_IE_CAUSE 22 /* Cause - string */ +#define IAX_IE_IAX_UNKNOWN 23 /* Unknown IAX command - byte */ +#define IAX_IE_MSGCOUNT 24 /* How many messages waiting - short */ +#define IAX_IE_AUTOANSWER 25 /* Request auto-answering -- none */ +#define IAX_IE_MUSICONHOLD 26 /* Request musiconhold with QUELCH -- none or string */ +#define IAX_IE_TRANSFERID 27 /* Transfer Request Identifier -- int */ +#define IAX_IE_RDNIS 28 /* Referring DNIS -- string */ +#define IAX_IE_PROVISIONING 29 /* Provisioning info */ +#define IAX_IE_AESPROVISIONING 30 /* AES Provisioning info */ +#define IAX_IE_DATETIME 31 /* Date/Time */ +#define IAX_IE_DEVICETYPE 32 /* Device Type -- string */ +#define IAX_IE_SERVICEIDENT 33 /* Service Identifier -- string */ +#define IAX_IE_FIRMWAREVER 34 /* Firmware revision -- u16 */ +#define IAX_IE_FWBLOCKDESC 35 /* Firmware block description -- u32 */ +#define IAX_IE_FWBLOCKDATA 36 /* Firmware block of data -- raw */ +#define IAX_IE_PROVVER 37 /* Provisioning Version (u32) */ +#define IAX_IE_CALLINGPRES 38 /* Calling presentation (u8) */ +#define IAX_IE_CALLINGTON 39 /* Calling type of number (u8) */ +#define IAX_IE_CALLINGTNS 40 /* Calling transit network select (u16) */ +#define IAX_IE_SAMPLINGRATE 41 /* Supported sampling rates (u16) */ +#define IAX_IE_CAUSECODE 42 /* Hangup cause (u8) */ +#define IAX_IE_ENCRYPTION 43 /* Encryption format (u16) */ +#define IAX_IE_ENCKEY 44 /* Encryption key (raw) */ +#define IAX_IE_CODEC_PREFS 45 /* Codec Negotiation */ + +#define IAX_IE_RR_JITTER 46 /* Received jitter (as in RFC1889) u32 */ +#define IAX_IE_RR_LOSS 47 /* Received loss (high byte loss pct, low 24 bits loss count, as in rfc1889 */ +#define IAX_IE_RR_PKTS 48 /* Received frames (total frames received) u32 */ +#define IAX_IE_RR_DELAY 49 /* Max playout delay for received frames (in ms) u16 */ +#define IAX_IE_RR_DROPPED 50 /* Dropped frames (presumably by jitterbuf) u32 */ +#define IAX_IE_RR_OOO 51 /* Frames received Out of Order u32 */ + + + +#define IAX_AUTH_PLAINTEXT (1 << 0) +#define IAX_AUTH_MD5 (1 << 1) +#define IAX_AUTH_RSA (1 << 2) + +#define IAX_META_TRUNK 1 /* Trunk meta-message */ +#define IAX_META_VIDEO 2 /* Video frame */ + +#define IAX_RATE_8KHZ (1 << 0) /* 8khz sampling (default if absent) */ +#define IAX_RATE_11KHZ (1 << 1) /* 11.025khz sampling */ +#define IAX_RATE_16KHZ (1 << 2) /* 16khz sampling */ +#define IAX_RATE_22KHZ (1 << 3) /* 22.05khz sampling */ +#define IAX_RATE_44KHZ (1 << 4) /* 44.1khz sampling */ +#define IAX_RATE_48KHZ (1 << 5) /* 48khz sampling */ +#define IAX_RATE_32KHZ (1 << 6) /* 32khz sampling */ + +#define IAX_DPSTATUS_EXISTS (1 << 0) +#define IAX_DPSTATUS_CANEXIST (1 << 1) +#define IAX_DPSTATUS_NONEXISTANT (1 << 2) +#define IAX_DPSTATUS_IGNOREPAT (1 << 14) +#define IAX_DPSTATUS_MATCHMORE (1 << 15) + +#if defined __GNUC__ +#define __PACKED __attribute__ ((__packed__)) +#else +#if defined (_MSC_VER) +#pragma pack(push,1) +#define __PACKED +#else +#pragma pack(1) +#define __PACKED +#endif +#endif + +/* Full frames are always delivered reliably */ +struct ast_iax2_full_hdr { + unsigned short scallno; /* Source call number -- high bit must be 1 */ + unsigned short dcallno; /* Destination call number -- high bit is 1 if retransmission */ + unsigned int ts; /* 32-bit timestamp in milliseconds (from 1st transmission) */ + unsigned char oseqno; /* Packet number (outgoing) */ + unsigned char iseqno; /* Packet number (next incoming expected) */ + char type; /* Frame type */ + unsigned char csub; /* Compressed subclass */ + unsigned char iedata[]; +} __PACKED; + +/* Mini header is used only for voice frames -- delivered unreliably */ +struct ast_iax2_mini_hdr { + unsigned short callno; /* Source call number -- high bit must be 0, rest must be non-zero */ + unsigned short ts; /* 16-bit Timestamp (high 16 bits from last ast_iax2_full_hdr) */ + /* Frametype implicitly VOICE_FRAME */ + /* subclass implicit from last ast_iax2_full_hdr */ + unsigned char data[]; +} __PACKED; + +struct ast_iax2_meta_hdr { + unsigned short zeros; /* Zeros field -- must be zero */ + unsigned char metacmd; /* Meta command */ + unsigned char cmddata; /* Command Data */ + unsigned char data[]; +} __PACKED; + +struct ast_iax2_video_hdr { + unsigned short zeros; /* Zeros field -- must be zero */ + unsigned short callno; /* Video call number */ + unsigned short ts; /* Timestamp and mark if present */ + unsigned char data[]; +} __PACKED; + +struct ast_iax2_meta_trunk_hdr { + unsigned int ts; /* 32-bit timestamp for all messages */ + unsigned char data[]; +} __PACKED; + +struct ast_iax2_meta_trunk_entry { + unsigned short callno; /* Call number */ + unsigned short len; /* Length of data for this callno */ +} __PACKED; + +#define IAX_FIRMWARE_MAGIC 0x69617879 + +struct ast_iax2_firmware_header { + unsigned int magic; /* Magic number */ + unsigned short version; /* Software version */ + unsigned char devname[16]; /* Device */ + unsigned int datalen; /* Data length of file beyond header */ + unsigned char chksum[16]; /* Checksum of all data */ + unsigned char data[]; +} __PACKED; + + +#ifdef _MSC_VER +#pragma pack(pop) +#else +#ifndef __GNUC__ +#pragma pack() +#endif +#endif + +#undef __PACKED + +#endif Added: freeswitch/trunk/src/mod/endpoints/mod_iax/jitterbuf.c ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/jitterbuf.c Thu May 8 19:25:36 2008 @@ -0,0 +1,834 @@ +/* + * jitterbuf: an application-independent jitterbuffer + * + * Copyrights: + * Copyright (C) 2004-2005, Horizon Wimba, Inc. + * + * Contributors: + * Steve Kann <stevek@stevek.com> + * + * This program is free software, distributed under the terms of + * the GNU Lesser (Library) General Public License + * + * Copyright on this file is disclaimed to Digium for inclusion in Asterisk + */ + +#include "iax2.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include "jitterbuf.h" + +/* define these here, just for ancient compiler systems */ +#define JB_LONGMAX 2147483647L +#define JB_LONGMIN (-JB_LONGMAX - 1L) + +/* MS VC can't do __VA_ARGS__ */ +#if (defined(WIN32) || defined(_WIN32_WCE)) && defined(_MSC_VER) +#define jb_warn if (warnf) warnf +#define jb_err if (errf) errf +#define jb_dbg if (dbgf) dbgf + +#ifdef DEEP_DEBUG + #define jb_dbg2 if (dbgf) dbgf +#else + #define jb_dbg2 if (0) dbgf +#endif + +#else + +#define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0) +#define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0) +#define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0) + +#ifdef DEEP_DEBUG +#define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0) +#else +#define jb_dbg2(...) ((void)0) +#endif + +#endif + +static jb_output_function_t warnf, errf, dbgf; + +void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg) +{ + errf = err; + warnf = warn; + dbgf = dbg; +} + +static void increment_losspct(jitterbuf *jb) +{ + jb->info.losspct = (100000 + 499 * jb->info.losspct)/500; +} + +static void decrement_losspct(jitterbuf *jb) +{ + jb->info.losspct = (499 * jb->info.losspct)/500; +} + +void jb_reset(jitterbuf *jb) +{ + /* only save settings */ + jb_conf s = jb->info.conf; + memset(jb, 0, sizeof(*jb)); + jb->info.conf = s; + + /* initialize length, using the configured value */ + jb->info.current = jb->info.target = jb->info.conf.target_extra; + jb->info.silence_begin_ts = -1; +} + +jitterbuf * jb_new() +{ + jitterbuf *jb; + + if (!(jb = (jitterbuf *)malloc(sizeof(*jb)))) + return NULL; + + jb->info.conf.target_extra = JB_TARGET_EXTRA; + + jb_reset(jb); + + jb_dbg2("jb_new() = %x\n", jb); + return jb; +} + +void jb_destroy(jitterbuf *jb) +{ + jb_frame *frame; + jb_dbg2("jb_destroy(%x)\n", jb); + + /* free all the frames on the "free list" */ + frame = jb->free; + while (frame != NULL) { + jb_frame *next = frame->next; + free(frame); + frame = next; + } + + /* free ourselves! */ + free(jb); +} + + + +#if 0 +static int longcmp(const void *a, const void *b) +{ + return *(long *)a - *(long *)b; +} +#endif + +/* simple history manipulation */ +/* maybe later we can make the history buckets variable size, or something? */ +/* drop parameter determines whether we will drop outliers to minimize + * delay */ +static int history_put(jitterbuf *jb, time_in_ms_t ts, time_in_ms_t now, long ms) +{ + time_in_ms_t delay = now - (ts - jb->info.resync_offset); + time_in_ms_t threshold = 2 * jb->info.jitter + jb->info.conf.resync_threshold; + time_in_ms_t kicked; + + /* don't add special/negative times to history */ + if (ts <= 0) + return 0; + + /* check for drastic change in delay */ + if (jb->info.conf.resync_threshold != -1) { + if (iax_abs(delay - jb->info.last_delay) > threshold) { + jb->info.cnt_delay_discont++; + if (jb->info.cnt_delay_discont > 3) { + /* resync the jitterbuffer */ + jb->info.cnt_delay_discont = 0; + jb->hist_ptr = 0; + jb->hist_maxbuf_valid = 0; + + jb_warn("Resyncing the jb. last_delay %ld, this delay %ld, threshold %ld, new offset %ld\n", jb->info.last_delay, delay, threshold, ts - now); + jb->info.resync_offset = ts - now; + jb->info.last_delay = delay = 0; /* after resync, frame is right on time */ + } else { + return -1; + } + } else { + jb->info.last_delay = delay; + jb->info.cnt_delay_discont = 0; + } + } + + kicked = jb->history[jb->hist_ptr % JB_HISTORY_SZ]; + + jb->history[(jb->hist_ptr++) % JB_HISTORY_SZ] = delay; + + /* optimization; the max/min buffers don't need to be recalculated, + * if this packet's entry doesn't change them. This happens if this + * packet is not involved, _and_ any packet that got kicked out of + * the history is also not involved. We do a number of comparisons, + * but it's probably still worthwhile, because it will usually + * succeed, and should be a lot faster than going through all 500 + * packets in history */ + if (!jb->hist_maxbuf_valid) + return 0; + + /* don't do this until we've filled history + * (reduces some edge cases below) */ + if (jb->hist_ptr < JB_HISTORY_SZ) + goto invalidate; + + /* if the new delay would go into min */ + if (delay < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1]) + goto invalidate; + + /* or max.. */ + if (delay > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1]) + goto invalidate; + + /* or the kicked delay would be in min */ + if (kicked <= jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1]) + goto invalidate; + + if (kicked >= jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1]) + goto invalidate; + + /* if we got here, we don't need to invalidate, 'cause this delay didn't + * affect things */ + return 0; + /* end optimization */ + + +invalidate: + jb->hist_maxbuf_valid = 0; + return 0; +} + +static void history_calc_maxbuf(jitterbuf *jb) +{ + int i,j; + + if (jb->hist_ptr == 0) + return; + + + /* initialize maxbuf/minbuf to the latest value */ + for (i=0;i<JB_HISTORY_MAXBUF_SZ;i++) { + /* + * jb->hist_maxbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ]; + * jb->hist_minbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ]; + */ + jb->hist_maxbuf[i] = JB_LONGMIN; + jb->hist_minbuf[i] = JB_LONGMAX; + } + + /* use insertion sort to populate maxbuf */ + /* we want it to be the top "n" values, in order */ + + /* start at the beginning, or JB_HISTORY_SZ frames ago */ + i = (jb->hist_ptr > JB_HISTORY_SZ) ? (jb->hist_ptr - JB_HISTORY_SZ) : 0; + + for (;i<jb->hist_ptr;i++) { + time_in_ms_t toins = jb->history[i % JB_HISTORY_SZ]; + + /* if the maxbuf should get this */ + if (toins > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1]) { + + /* insertion-sort it into the maxbuf */ + for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) { + /* found where it fits */ + if (toins > jb->hist_maxbuf[j]) { + /* move over */ + memmove(jb->hist_maxbuf + j + 1, jb->hist_maxbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_maxbuf[0])); + /* insert */ + jb->hist_maxbuf[j] = toins; + + break; + } + } + } + + /* if the minbuf should get this */ + if (toins < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1]) { + + /* insertion-sort it into the maxbuf */ + for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) { + /* found where it fits */ + if (toins < jb->hist_minbuf[j]) { + /* move over */ + memmove(jb->hist_minbuf + j + 1, jb->hist_minbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_minbuf[0])); + /* insert */ + jb->hist_minbuf[j] = toins; + + break; + } + } + } + +#if 0 + int k; + fprintf(stderr, "toins = %ld\n", toins); + fprintf(stderr, "maxbuf ="); + for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++) + fprintf(stderr, "%ld ", jb->hist_maxbuf[k]); + fprintf(stderr, "\nminbuf ="); + for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++) + fprintf(stderr, "%ld ", jb->hist_minbuf[k]); + fprintf(stderr, "\n"); +#endif + } + + jb->hist_maxbuf_valid = 1; +} + +static void history_get(jitterbuf *jb) +{ + time_in_ms_t max, min, jitter; + int index; + int count; + + if (!jb->hist_maxbuf_valid) + history_calc_maxbuf(jb); + + /* count is how many items in history we're examining */ + count = (jb->hist_ptr < JB_HISTORY_SZ) ? jb->hist_ptr : JB_HISTORY_SZ; + + /* index is the "n"ths highest/lowest that we'll look for */ + index = count * JB_HISTORY_DROPPCT / 100; + + /* sanity checks for index */ + if (index > (JB_HISTORY_MAXBUF_SZ - 1)) + index = JB_HISTORY_MAXBUF_SZ - 1; + + + if (index < 0) { + jb->info.min = 0; + jb->info.jitter = 0; + return; + } + + max = jb->hist_maxbuf[index]; + min = jb->hist_minbuf[index]; + + jitter = max - min; + + /* these debug stmts compare the difference between looking at the absolute jitter, and the + * values we get by throwing away the outliers */ + /* + fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", index, min, max, jitter); + fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", 0, jb->hist_minbuf[0], jb->hist_maxbuf[0], jb->hist_maxbuf[0]-jb->hist_minbuf[0]); + */ + + jb->info.min = min; + jb->info.jitter = jitter; +} + +/* returns 1 if frame was inserted into head of queue, 0 otherwise */ +static int queue_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, time_in_ms_t ts) +{ + jb_frame *frame; + jb_frame *p; + int head = 0; + time_in_ms_t resync_ts = ts - jb->info.resync_offset; + + if ((frame = jb->free)) { + jb->free = frame->next; + } else if (!(frame = (jb_frame *)malloc(sizeof(*frame)))) { + jb_err("cannot allocate frame\n"); + return 0; + } + + jb->info.frames_cur++; + + frame->data = data; + frame->ts = resync_ts; + frame->ms = ms; + frame->type = type; + + /* + * frames are a circular list, jb-frames points to to the lowest ts, + * jb->frames->prev points to the highest ts + */ + + if (!jb->frames) { /* queue is empty */ + jb->frames = frame; + frame->next = frame; + frame->prev = frame; + head = 1; + } else if (resync_ts < jb->frames->ts) { + frame->next = jb->frames; + frame->prev = jb->frames->prev; + + frame->next->prev = frame; + frame->prev->next = frame; + + /* frame is out of order */ + jb->info.frames_ooo++; + + jb->frames = frame; + head = 1; + } else { + p = jb->frames; + + /* frame is out of order */ + if (resync_ts < p->prev->ts) jb->info.frames_ooo++; + + while (resync_ts < p->prev->ts && p->prev != jb->frames) + p = p->prev; + + frame->next = p; + frame->prev = p->prev; + + frame->next->prev = frame; + frame->prev->next = frame; + } + return head; +} + +static time_in_ms_t queue_next(jitterbuf *jb) +{ + if (jb->frames) + return jb->frames->ts; + else + return -1; +} + +static time_in_ms_t queue_last(jitterbuf *jb) +{ + if (jb->frames) + return jb->frames->prev->ts; + else + return -1; +} + +static jb_frame *_queue_get(jitterbuf *jb, time_in_ms_t ts, int all) +{ + jb_frame *frame; + frame = jb->frames; + + if (!frame) + return NULL; + + /*jb_warn("queue_get: ASK %ld FIRST %ld\n", ts, frame->ts); */ + + if (all || ts >= frame->ts) { + /* remove this frame */ + frame->prev->next = frame->next; + frame->next->prev = frame->prev; + + if (frame->next == frame) + jb->frames = NULL; + else + jb->frames = frame->next; + + + /* insert onto "free" single-linked list */ + frame->next = jb->free; + jb->free = frame; + + jb->info.frames_cur--; + + /* we return the frame pointer, even though it's on free list, + * but caller must copy data */ + return frame; + } + + return NULL; +} + +static jb_frame *queue_get(jitterbuf *jb, time_in_ms_t ts) +{ + return _queue_get(jb,ts,0); +} + +static jb_frame *queue_getall(jitterbuf *jb) +{ + return _queue_get(jb,0,1); +} + +#if 0 +/* some diagnostics */ +static void jb_dbginfo(jitterbuf *jb) +{ + if (dbgf == NULL) + return; + + jb_dbg("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n", + jb->info.frames_in, jb->info.frames_out, jb->info.frames_late, jb->info.frames_lost, jb->info.frames_dropped, jb->info.frames_cur); + + jb_dbg("jitter=%ld current=%ld target=%ld min=%ld sil=%d len=%d len/fcur=%ld\n", + jb->info.jitter, jb->info.current, jb->info.target, jb->info.min, jb->info.silence_begin_ts, jb->info.current - jb->info.min, + jb->info.frames_cur ? (jb->info.current - jb->info.min)/jb->info.frames_cur : -8); + if (jb->info.frames_in > 0) + jb_dbg("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n", + jb->info.frames_lost * 100/(jb->info.frames_in + jb->info.frames_lost), + jb->info.frames_late * 100/jb->info.frames_in); + jb_dbg("jb info: queue %d -> %d. last_ts %d (queue len: %d) last_ms %d\n", + queue_next(jb), + queue_last(jb), + jb->info.next_voice_ts, + queue_last(jb) - queue_next(jb), + jb->info.last_voice_ms); +} +#endif + +#ifdef DEEP_DEBUG +static void jb_chkqueue(jitterbuf *jb) +{ + int i=0; + jb_frame *p = jb->frames; + + if (!p) { + return; + } + + do { + if (p->next == NULL) { + jb_err("Queue is BROKEN at item [%d]", i); + } + i++; + p=p->next; + } while (p->next != jb->frames); +} + +static void jb_dbgqueue(jitterbuf *jb) +{ + int i=0; + jb_frame *p = jb->frames; + + jb_dbg("queue: "); + + if (!p) { + jb_dbg("EMPTY\n"); + return; + } + + do { + jb_dbg("[%d]=%ld ", i++, p->ts); + p=p->next; + } while (p->next != jb->frames); + + jb_dbg("\n"); +} +#endif + +enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, time_in_ms_t ts, time_in_ms_t now) +{ + jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb, data, ms, ts, now); + + jb->info.frames_in++; + + if (type == JB_TYPE_VOICE) { + /* presently, I'm only adding VOICE frames to history and drift + * calculations; mostly because with the IAX integrations, I'm + * sending retransmitted control frames with their awkward + * timestamps through + */ + if (history_put(jb,ts,now,ms)) + return JB_DROP; + } + + /* if put into head of queue, caller needs to reschedule */ + if (queue_put(jb,data,type,ms,ts)) { + return JB_SCHED; + } + + return JB_OK; +} + + +static enum jb_return_code _jb_get(jitterbuf *jb, jb_frame *frameout, time_in_ms_t now, long interpl) +{ + jb_frame *frame; + time_in_ms_t diff; + + /*if ((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */ + /* get jitter info */ + history_get(jb); + + + /* target */ + jb->info.target = jb->info.jitter + jb->info.min + jb->info.conf.target_extra; + + /* if a hard clamp was requested, use it */ + if ((jb->info.conf.max_jitterbuf) && ((jb->info.target - jb->info.min) > jb->info.conf.max_jitterbuf)) { + jb_dbg("clamping target from %d to %d\n", (jb->info.target - jb->info.min), jb->info.conf.max_jitterbuf); + jb->info.target = jb->info.min + jb->info.conf.max_jitterbuf; + } + + diff = jb->info.target - jb->info.current; + + /* jb_warn("diff = %d lms=%d last = %d now = %d\n", diff, */ + /* jb->info.last_voice_ms, jb->info.last_adjustment, now); */ + + /* let's work on non-silent case first */ + if (!jb->info.silence_begin_ts) { + /* we want to grow */ + if ((diff > 0) && + /* we haven't grown in the delay length */ + (((jb->info.last_adjustment + JB_ADJUST_DELAY) < now) || + /* we need to grow more than the "length" we have left */ + (diff > queue_last(jb) - queue_next(jb)) ) ) { + /* grow by interp frame length */ + jb->info.current += interpl; + jb->info.next_voice_ts += interpl; + jb->info.last_voice_ms = interpl; + jb->info.last_adjustment = now; + jb->info.cnt_contig_interp++; + /* assume silence instead of continuing to interpolate */ + if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) { + jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current; + } + jb_dbg("G"); + return JB_INTERP; + } + + frame = queue_get(jb, jb->info.next_voice_ts - jb->info.current); + + /* not a voice frame; just return it. */ + if (frame && frame->type != JB_TYPE_VOICE) { + /* track start of silence */ + if (frame->type == JB_TYPE_SILENCE) { + jb->info.silence_begin_ts = frame->ts; + jb->info.cnt_contig_interp = 0; + } + + *frameout = *frame; + jb->info.frames_out++; + jb_dbg("o"); + return JB_OK; + } + + /* voice frame is later than expected */ + if (frame && frame->ts + jb->info.current < jb->info.next_voice_ts) { + if (frame->ts + jb->info.current > jb->info.next_voice_ts - jb->info.last_voice_ms) { + /* either we interpolated past this frame in the last jb_get */ + /* or the frame is still in order, but came a little too quick */ + *frameout = *frame; + /* reset expectation for next frame */ + jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms; + jb->info.frames_out++; + decrement_losspct(jb); + jb->info.cnt_contig_interp = 0; + jb_dbg("v"); + return JB_OK; + } else { + /* voice frame is late */ + *frameout = *frame; + jb->info.frames_out++; + decrement_losspct(jb); + jb->info.frames_late++; + jb->info.frames_lost--; + jb_dbg("l"); + /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb)); + jb_warninfo(jb); */ + return JB_DROP; + } + } + + /* keep track of frame sizes, to allow for variable sized-frames */ + if (frame && frame->ms > 0) { + jb->info.last_voice_ms = frame->ms; + } + + /* we want to shrink; shrink at 1 frame / 500ms */ + /* unless we don't have a frame, then shrink 1 frame */ + /* every 80ms (though perhaps we can shrink even faster */ + /* in this case) */ + if (diff < -jb->info.conf.target_extra && + ((!frame && jb->info.last_adjustment + 80 < now) || + (jb->info.last_adjustment + 500 < now))) { + + jb->info.last_adjustment = now; + jb->info.cnt_contig_interp = 0; + + if (frame) { + *frameout = *frame; + /* shrink by frame size we're throwing out */ + jb->info.current -= frame->ms; + jb->info.frames_out++; + decrement_losspct(jb); + jb->info.frames_dropped++; + jb_dbg("s"); + return JB_DROP; + } else { + /* shrink by last_voice_ms */ + jb->info.current -= jb->info.last_voice_ms; + jb->info.frames_lost++; + increment_losspct(jb); + jb_dbg("S"); + return JB_NOFRAME; + } + } + + /* lost frame */ + if (!frame) { + /* this is a bit of a hack for now, but if we're close to + * target, and we find a missing frame, it makes sense to + * grow, because the frame might just be a bit late; + * otherwise, we presently get into a pattern where we return + * INTERP for the lost frame, then it shows up next, and we + * throw it away because it's late */ + /* I've recently only been able to replicate this using + * iaxclient talking to app_echo on asterisk. In this case, + * my outgoing packets go through asterisk's (old) + * jitterbuffer, and then might get an unusual increasing delay + * there if it decides to grow?? */ + /* Update: that might have been a different bug, that has been fixed.. + * But, this still seemed like a good idea, except that it ended up making a single actual + * lost frame get interpolated two or more times, when there was "room" to grow, so it might + * be a bit of a bad idea overall */ + /*if (diff > -1 * jb->info.last_voice_ms) { + jb->info.current += jb->info.last_voice_ms; + jb->info.last_adjustment = now; + jb_warn("g"); + return JB_INTERP; + } */ + jb->info.frames_lost++; + increment_losspct(jb); + jb->info.next_voice_ts += interpl; + jb->info.last_voice_ms = interpl; + jb->info.cnt_contig_interp++; + /* assume silence instead of continuing to interpolate */ + if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) { + jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current; + } + jb_dbg("L"); + return JB_INTERP; + } + + /* normal case; return the frame, increment stuff */ + *frameout = *frame; + jb->info.next_voice_ts += frame->ms; + jb->info.frames_out++; + jb->info.cnt_contig_interp = 0; + decrement_losspct(jb); + jb_dbg("v"); + return JB_OK; + } else { + /* TODO: after we get the non-silent case down, we'll make the + * silent case -- basically, we'll just grow and shrink faster + * here, plus handle next_voice_ts a bit differently */ + + /* to disable silent special case altogether, just uncomment this: */ + /* jb->info.silence_begin_ts = 0; */ + + /* shrink interpl len every 10ms during silence */ + if (diff < -jb->info.conf.target_extra && + jb->info.last_adjustment + 10 <= now) { + jb->info.current -= interpl; + jb->info.last_adjustment = now; + } + + frame = queue_get(jb, now - jb->info.current); + if (!frame) { + return JB_NOFRAME; + } else if (frame->type != JB_TYPE_VOICE) { + /* normal case; in silent mode, got a non-voice frame */ + *frameout = *frame; + jb->info.frames_out++; + return JB_OK; + } + if (frame->ts < jb->info.silence_begin_ts) { + /* voice frame is late */ + *frameout = *frame; + jb->info.frames_out++; + decrement_losspct(jb); + jb->info.frames_late++; + jb->info.frames_lost--; + jb_dbg("l"); + /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb)); + jb_warninfo(jb); */ + return JB_DROP; + } else { + /* voice frame */ + /* try setting current to target right away here */ + jb->info.current = jb->info.target; + jb->info.silence_begin_ts = 0; + jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms; + jb->info.last_voice_ms = frame->ms; + jb->info.frames_out++; + decrement_losspct(jb); + *frameout = *frame; + jb_dbg("V"); + return JB_OK; + } + } +} + +time_in_ms_t jb_next(jitterbuf *jb) +{ + if (jb->info.silence_begin_ts) { + if (jb->frames) { + time_in_ms_t next = queue_next(jb); + history_get(jb); + /* shrink during silence */ + if (jb->info.target - jb->info.current < -jb->info.conf.target_extra) + return jb->info.last_adjustment + 10; + return next + jb->info.target; + } + else + return JB_LONGMAX; + } else { + return jb->info.next_voice_ts; + } +} + +enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frameout, time_in_ms_t now, long interpl) +{ + enum jb_return_code ret = _jb_get(jb, frameout, now, interpl); +#if 0 + static int lastts=0; + int thists = ((ret == JB_OK) || (ret == JB_DROP)) ? frameout->ts : 0; + jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb, frameout, now, ret, thists); + if (thists && thists < lastts) jb_warn("XXXX timestamp roll-back!!!\n"); + lastts = thists; +#endif + return ret; +} + +enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout) +{ + jb_frame *frame; + frame = queue_getall(jb); + + if (!frame) { + return JB_NOFRAME; + } + + *frameout = *frame; + return JB_OK; +} + + +enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats) +{ + history_get(jb); + + *stats = jb->info; + + return JB_OK; +} + +enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf) +{ + /* take selected settings from the struct */ + + jb->info.conf.max_jitterbuf = conf->max_jitterbuf; + jb->info.conf.resync_threshold = conf->resync_threshold; + jb->info.conf.max_contig_interp = conf->max_contig_interp; + + /* -1 indicates use of the default JB_TARGET_EXTRA value */ + jb->info.conf.target_extra = ( conf->target_extra == -1 ) + ? JB_TARGET_EXTRA + : conf->target_extra + ; + + /* update these to match new target_extra setting */ + jb->info.current = jb->info.conf.target_extra; + jb->info.target = jb->info.conf.target_extra; + + return JB_OK; +} + + Added: freeswitch/trunk/src/mod/endpoints/mod_iax/jitterbuf.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/jitterbuf.h Thu May 8 19:25:36 2008 @@ -0,0 +1,162 @@ +/* + * jitterbuf: an application-independent jitterbuffer + * + * Copyrights: + * Copyright (C) 2004-2005, Horizon Wimba, Inc. + * + * Contributors: + * Steve Kann <stevek@stevek.com> + * + * This program is free software, distributed under the terms of + * the GNU Lesser (Library) General Public License + * + * Copyright on this file is disclaimed to Digium for inclusion in Asterisk + */ + +#ifndef _JITTERBUF_H_ +#define _JITTERBUF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* configuration constants */ + /* Number of historical timestamps to use in calculating jitter and drift */ +#define JB_HISTORY_SZ 500 + /* what percentage of timestamps should we drop from the history when we examine it; + * this might eventually be something made configurable */ +#define JB_HISTORY_DROPPCT 3 + /* the maximum droppct we can handle (say it was configurable). */ +#define JB_HISTORY_DROPPCT_MAX 4 + /* the size of the buffer we use to keep the top and botton timestamps for dropping */ +#define JB_HISTORY_MAXBUF_SZ JB_HISTORY_SZ * JB_HISTORY_DROPPCT_MAX / 100 + /* amount of additional jitterbuffer adjustment */ +#define JB_TARGET_EXTRA 40 + /* ms between growing and shrinking; may not be honored if jitterbuffer runs out of space */ +#define JB_ADJUST_DELAY 40 + +enum jb_return_code { + /* return codes */ + JB_OK, /* 0 */ + JB_EMPTY, /* 1 */ + JB_NOFRAME, /* 2 */ + JB_INTERP, /* 3 */ + JB_DROP, /* 4 */ + JB_SCHED /* 5 */ +}; + +enum jb_frame_type { +/* frame types */ + JB_TYPE_CONTROL, /* 0 */ + JB_TYPE_VOICE, /* 1 */ + JB_TYPE_VIDEO, /* 2 - reserved */ + JB_TYPE_SILENCE /* 3 */ +}; + +typedef struct jb_conf { + /* settings */ + long max_jitterbuf; /* defines a hard clamp to use in setting the jitter buffer delay */ + long resync_threshold; /* the jb will resync when delay increases to (2 * jitter) + this param */ + long max_contig_interp; /* the max interp frames to return in a row */ + long target_extra; /* amount of additional jitterbuffer adjustment, overrides JB_TARGET_EXTRA */ +} jb_conf; + +typedef struct jb_info { + jb_conf conf; + + /* statistics */ + long frames_in; /* number of frames input to the jitterbuffer.*/ + long frames_out; /* number of frames output from the jitterbuffer.*/ + long frames_late; /* number of frames which were too late, and dropped.*/ + long frames_lost; /* number of missing frames.*/ + long frames_dropped; /* number of frames dropped (shrinkage) */ + long frames_ooo; /* number of frames received out-of-order */ + long frames_cur; /* number of frames presently in jb, awaiting delivery.*/ + time_in_ms_t jitter; /* jitter measured within current history interval*/ + time_in_ms_t min; /* minimum lateness within current history interval */ + time_in_ms_t current; /* the present jitterbuffer adjustment */ + time_in_ms_t target; /* the target jitterbuffer adjustment */ + long losspct; /* recent lost frame percentage (* 1000) */ + time_in_ms_t next_voice_ts; /* the ts of the next frame to be read from the jb - in receiver's time */ + long last_voice_ms; /* the duration of the last voice frame */ + time_in_ms_t silence_begin_ts; /* the time of the last CNG frame, when in silence */ + time_in_ms_t last_adjustment; /* the time of the last adjustment */ + time_in_ms_t last_delay; /* the last now added to history */ + long cnt_delay_discont; /* the count of discontinuous delays */ + time_in_ms_t resync_offset; /* the amount to offset ts to support resyncs */ + long cnt_contig_interp; /* the number of contiguous interp frames returned */ +} jb_info; + +typedef struct jb_frame { + void *data; /* the frame data */ + time_in_ms_t ts; /* the relative delivery time expected */ + long ms; /* the time covered by this frame, in sec/8000 */ + enum jb_frame_type type; /* the type of frame */ + struct jb_frame *next, *prev; +} jb_frame; + +typedef struct jitterbuf { + jb_info info; + + /* history */ + time_in_ms_t history[JB_HISTORY_SZ]; /* history */ + int hist_ptr; /* points to index in history for next entry */ + time_in_ms_t hist_maxbuf[JB_HISTORY_MAXBUF_SZ]; /* a sorted buffer of the max delays (highest first) */ + time_in_ms_t hist_minbuf[JB_HISTORY_MAXBUF_SZ]; /* a sorted buffer of the min delays (lowest first) */ + int hist_maxbuf_valid; /* are the "maxbuf"/minbuf valid? */ + + jb_frame *frames; /* queued frames */ + jb_frame *free; /* free frames (avoid malloc?) */ +} jitterbuf; + + +/* new jitterbuf */ +jitterbuf * jb_new(void); + +/* destroy jitterbuf */ +void jb_destroy(jitterbuf *jb); + +/* reset jitterbuf */ +/* NOTE: The jitterbuffer should be empty before you call this, otherwise + * you will leak queued frames, and some internal structures */ +void jb_reset(jitterbuf *jb); + +/* queue a frame data=frame data, timings (in ms): ms=length of frame (for voice), ts=ts (sender's time) + * now=now (in receiver's time) return value is one of + * JB_OK: Frame added. Last call to jb_next() still valid + * JB_DROP: Drop this frame immediately + * JB_SCHED: Frame added. Call jb_next() to get a new time for the next frame + */ +enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, time_in_ms_t ts, time_in_ms_t now); + +/* get a frame for time now (receiver's time) return value is one of + * JB_OK: You've got frame! + * JB_DROP: Here's an audio frame you should just drop. Ask me again for this time.. + * JB_NOFRAME: There's no frame scheduled for this time. + * JB_INTERP: Please interpolate an interpl-length frame for this time (either we need to grow, or there was a lost frame) + * JB_EMPTY: The jb is empty. + */ +enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frame, time_in_ms_t now, long interpl); + +/* unconditionally get frames from jitterbuf until empty */ +enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout); + +/* when is the next frame due out, in receiver's time (0=EMPTY) + * This value may change as frames are added (esp non-audio frames) */ +time_in_ms_t jb_next(jitterbuf *jb); + +/* get jitterbuf info: only "statistics" may be valid */ +enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats); + +/* set jitterbuf conf */ +enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf); + +typedef void (*jb_output_function_t)(const char *fmt, ...); +extern void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg); + +#ifdef __cplusplus +} +#endif + + +#endif Added: freeswitch/trunk/src/mod/endpoints/mod_iax/md5.c ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/md5.c Thu May 8 19:25:36 2008 @@ -0,0 +1,297 @@ +/* MD5 checksum routines used for authentication. Not covered by GPL, but + in the public domain as per the copyright below */ + +#ifdef FREEBSD +# include <machine/endian.h> +#elif defined(LINUX) +# include <endian.h> +# include <features.h> +# include <sys/types.h> +#elif defined(SOLARIS) + /* each solaris is different -- this won't work on 2.6 or 2.7 */ +# include <sys/isa_defs.h> /* Defines either _LITTLE_ENDIAN or _BIG_ENDIAN */ +# define __BIG_ENDIAN 4321 +# define __LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# define LITTLE_ENDIAN 1234 +# ifdef _LITTLE_ENDIAN +# define __BYTE_ORDER __LITTLE_ENDIAN +# define BYTE_ORDER LITTLE_ENDIAN +# else +# define __BYTE_ORDER __BIG_ENDIAN +# define BYTE_ORDER BIG_ENDIAN +# endif +#endif + +#ifdef _MSC_VER +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN 4321 +#endif +#ifndef __BYTE_ORDER +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN || BYTE_ORDER == BIG_ENDIAN +# define HIGHFIRST 1 +#elif __BYTE_ORDER == __LITTLE_ENDIAN || BYTE_ORDER == LITLE_ENDIAN +# undef HIGHFIRST +#else +# error "Please fix <bits/endian.h>" +#endif + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ +#include <string.h> /* for memcpy() */ +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *) ctx->in)[14] = ctx->bits[0]; + ((uint32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif Added: freeswitch/trunk/src/mod/endpoints/mod_iax/md5.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/md5.h Thu May 8 19:25:36 2008 @@ -0,0 +1,27 @@ +#ifndef MD5_H +#define MD5_H + +#if defined(__alpha) || defined(__x86_64) +typedef unsigned int uint32; +#else +typedef unsigned long uint32; +#endif + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(uint32 buf[4], uint32 const in[16]); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ +typedef struct MD5Context MD5_CTX; + +#endif /* !MD5_H */ Added: freeswitch/trunk/src/mod/endpoints/mod_iax/winiphone.c ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/winiphone.c Thu May 8 19:25:36 2008 @@ -0,0 +1,761 @@ +/* + * Miniphone: A simple, command line telephone + * + * IAX Support for talking to Asterisk and other Gnophone clients + * + * Copyright (C) 1999, Linux Support Services, Inc. + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +/* #define PRINTCHUCK /* enable this to indicate chucked incomming packets */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <io.h> +#include <conio.h> +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <process.h> +#include <windows.h> +#include <winsock.h> +#include <mmsystem.h> +#include <malloc.h> +#include "gsm.h" +#include "iax-client.h" +#include "frame.h" +#include "miniphone.h" + + +struct peer { + int time; + gsm gsmin; + gsm gsmout; + + struct iax_session *session; + struct peer *next; +}; + +static struct peer *peers; +static int answered_call = 0; + +/* stuff for wave audio device */ +HWAVEOUT wout; +HWAVEIN win; + +typedef struct whout { + WAVEHDR w; + short data[160]; + struct whout *next; +} WHOUT; + +WHOUT *outqueue = NULL; + +/* parameters for audio in */ +#define NWHIN 8 /* number of input buffer entries */ +/* NOTE the OUT_INTERVAL parameter *SHOULD* be more around 18 to 20 or so, since the packets should +be spaced by 20 milliseconds. However, in practice, especially in Windoze-95, setting it that high +caused underruns. 10 is just ever so slightly agressive, and the receiver has to chuck a packet +every now and then. Thats about the way it should be to be happy. */ +#define OUT_INTERVAL 10 /* number of ms to wait before sending more data to peer */ +/* parameters for audio out */ +#define OUT_DEPTH 12 /* number of outbut buffer entries */ +#define OUT_PAUSE_THRESHOLD 2 /* number of active entries needed to start output (for smoothing) */ + +/* audio input buffer headers */ +WAVEHDR whin[NWHIN]; +/* audio input buffers */ +char bufin[NWHIN][320]; + +/* initialize the sequence variables for the audio in stuff */ +unsigned int whinserial = 1,nextwhin = 1; + +static struct peer *find_peer(struct iax_session *); +static void parse_args(FILE *, unsigned char *); +void do_iax_event(FILE *); +void call(FILE *, char *); +void answer_call(void); +void reject_call(void); +static void handle_event(FILE *, struct iax_event *e, struct peer *p); +void parse_cmd(FILE *, int, char **); +void issue_prompt(FILE *); +void dump_array(FILE *, char **); + +static char *help[] = { +"Welcome to the miniphone telephony client, the commands are as follows:\n", +"Help\t\t-\tDisplays this screen.", +"Call <Number>\t-\tDials the number supplied.", +"Answer\t\t-\tAnswers an Inbound call.", +"Reject\t\t-\tRejects an Inbound call.", +"Dump\t\t-\tDumps (disconnects) the current call.", +"Dtmf <Digit>\t-\tSends specified DTMF digit.", +"Status\t\t-\tLists the current sessions and their current status.", +"Quit\t\t-\tShuts down the client.", +"", +0 +}; + +static struct peer *most_recent_answer; +static struct iax_session *newcall = 0; + +/* holder of the time, relative to startup in system ticks. See our +gettimeofday() implementation */ +time_t startuptime; + +/* routine called at exit to shutdown audio I/O and close nicely. +NOTE: If all this isnt done, the system doesnt not handle this +cleanly and has to be rebooted. What a pile of doo doo!! */ +void killem(void) +{ + waveInStop(win); + waveInReset(win); + waveInClose(win); + waveOutReset(wout); + waveOutClose(wout); + WSACleanup(); /* dont forget socket stuff too */ + return; +} + +/* Win-doze doenst have gettimeofday(). This sux. So, what we did is +provide some gettimeofday-like functionality that works for our purposes. +In the main(), we take a sample of the system tick counter (into startuptime). +This function returns the relative time since program startup, more or less, +which is certainly good enough for our purposes. */ +void gettimeofday(struct timeval *tv, struct timezone *tz) +{ + long l = startuptime + GetTickCount(); + + tv->tv_sec = l / 1000; + tv->tv_usec = (l % 1000) * 1000; + return; +} + + +static struct peer *find_peer(struct iax_session *session) +{ + struct peer *cur = peers; + while(cur) { + if (cur->session == session) + return cur; + cur = cur->next; + } + return NULL; +} + +void +parse_args(FILE *f, unsigned char *cmd) +{ + static char *argv[MAXARGS]; + unsigned char *parse = cmd; + int argc = 0, t = 0; + + // Don't mess with anything that doesn't exist... + if(!*parse) + return; + + memset(argv, 0, sizeof(argv)); + while(*parse) { + if(*parse < 33 || *parse > 128) { + *parse = 0, t++; + if(t > MAXARG) { + fprintf(f, "Warning: Argument exceeds maximum argument size, command ignored!\n"); + return; + } + } else if(t || !argc) { + if(argc == MAXARGS) { + fprintf(f, "Warning: Command ignored, too many arguments\n"); + return; + } + argv[argc++] = parse; + t = 0; + } + + parse++; + } + + if(argc) + parse_cmd(f, argc, argv); +} + +/* handle all network requests, and a pending scheduled event, if any */ +void service_network(int netfd, FILE *f) +{ + fd_set readfd; + struct timeval dumbtimer; + + /* set up a timer that falls-through */ + dumbtimer.tv_sec = 0; + dumbtimer.tv_usec = 0; + + + for(;;) /* suck everything outa network stuff */ + { + FD_ZERO(&readfd); + FD_SET(netfd, &readfd); + if (select(netfd + 1, &readfd, 0, 0, &dumbtimer) > 0) + { + if (FD_ISSET(netfd,&readfd)) + { + do_iax_event(f); + (void) iax_time_to_next_event(); + } else break; + } else break; + } + do_iax_event(f); /* do pending event if any */ +} + + +int +main(int argc, char *argv[]) +{ + int port; + int netfd; + int c, i; + FILE *f; + char rcmd[RBUFSIZE]; + gsm_frame fo; + WSADATA foop; + time_t t; + WAVEFORMATEX wf; + WHOUT *wh,*wh1,*wh2; + unsigned long lastouttick = 0; + + + + /* get time of day in milliseconds, offset by tick count (see our + gettimeofday() implementation) */ + time(&t); + startuptime = ((t % 86400) * 1000) - GetTickCount(); + + f = stdout; + _dup2(fileno(stdout),fileno(stderr)); + + /* start up the windoze-socket layer stuff */ + if (WSAStartup(0x0101,&foop)) { + fprintf(stderr,"Fatal error: Falied to startup windows sockets\n"); + return -1; + } + + + /* setup the format for opening audio channels */ + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = 1; + wf.nSamplesPerSec = 8000; + wf.nAvgBytesPerSec = 16000; + wf.nBlockAlign = 2; + wf.wBitsPerSample = 16; + wf.cbSize = 0; + /* open the audio out channel */ + if (waveOutOpen(&wout,0,&wf,0,0,CALLBACK_NULL) != MMSYSERR_NOERROR) + { + fprintf(stderr,"Fatal Error: Failed to open wave output device\n"); + return -1; + } + /* open the audio in channel */ + if (waveInOpen(&win,0,&wf,0,0,CALLBACK_NULL) != MMSYSERR_NOERROR) + { + fprintf(stderr,"Fatal Error: Failed to open wave input device\n"); + waveOutReset(wout); + waveOutClose(wout); + return -1; + } + /* activate the exit handler */ + atexit(killem); + /* initialize the audio in buffer structures */ + memset(&whin,0,sizeof(whin)); + + if ( (port = iax_init(0) < 0)) { + fprintf(stderr, "Fatal error: failed to initialize iax with port %d\n", port); + return -1; + } + + + iax_set_formats(AST_FORMAT_GSM); + netfd = iax_get_fd(); + + fprintf(f, "Text Based Telephony Client.\n\n"); + issue_prompt(f); + + /* main tight loop */ + while(1) { + /* service the network stuff */ + service_network(netfd,f); + if (outqueue) /* if stuff in audio output queue, free it up if its available */ + { + /* go through audio output queue */ + for(wh = outqueue,wh1 = wh2 = NULL,i = 0; wh != NULL; wh = wh->next) + { + service_network(netfd,f); /* service network here for better performance */ + /* if last one was removed from queue, zot it here */ + if (i && wh1) + { + free(wh1); + wh1 = wh2; + } + i = 0; /* reset "last one removed" flag */ + if (wh->w.dwFlags & WHDR_DONE) /* if this one is done */ + { + /* prepare audio header */ + if ((c = waveOutUnprepareHeader(wout,&wh->w,sizeof(WAVEHDR))) != MMSYSERR_NOERROR) + { + fprintf(stderr,"Cannot unprepare audio out header, error %d\n",c); + exit(255); + } + if (wh1 != NULL) /* if there was a last one */ + { + wh1->next = wh->next; + } + if (outqueue == wh) /* is first one, so set outqueue to next one */ + { + outqueue = wh->next; + } + i = 1; /* set 'to free' flag */ + } + wh2 = wh1; /* save old,old wh pointer */ + wh1 = wh; /* save the old wh pointer */ + } + } + /* go through all audio in buffers, and prepare and queue ones that are currently idle */ + for(i = 0; i < NWHIN; i++) + { + service_network(netfd,f); /* service network stuff here for better performance */ + if (!(whin[i].dwFlags & WHDR_PREPARED)) /* if not prepared, do so */ + { + /* setup this input buffer header */ + memset(&whin[i],0,sizeof(WAVEHDR)); + whin[i].lpData = bufin[i]; + whin[i].dwBufferLength = 320; + whin[i].dwUser = whinserial++; /* set 'user data' to current serial number */ + /* prepare the buffer */ + if (waveInPrepareHeader(win,&whin[i],sizeof(WAVEHDR))) + { + fprintf(stderr,"Unable to prepare header for input\n"); + return -1; + } + /* add it to device (queue) */ + if (waveInAddBuffer(win,&whin[i],sizeof(WAVEHDR))) + { + fprintf(stderr,"Unable to prepare header for input\n"); + return -1; + } + } + waveInStart(win); /* start it (if not already started) */ + } + + /* if key pressed, do command stuff */ + if(_kbhit()) + { + if ( ( fgets(&*rcmd, 256, stdin))) { + rcmd[strlen(rcmd)-1] = 0; + parse_args(f, &*rcmd); + } else fprintf(f, "Fatal error: failed to read data!\n"); + + issue_prompt(f); + } + /* do audio input stuff for buffers that have received data from audio in device already. Must + do them in serial number order (the order in which they were originally queued). */ + if(answered_call) /* send audio only if call answered */ + { + for(;;) /* loop until all are found */ + { + for(i = 0; i < NWHIN; i++) /* find an available one that's the one we are looking for */ + { + service_network(netfd,f); /* service network here for better performance */ + /* if not time to send any more, dont */ + if (GetTickCount() < (lastouttick + OUT_INTERVAL)) + { + i = NWHIN; /* set to value that WILL exit loop */ + break; + } + if ((whin[i].dwUser == nextwhin) && (whin[i].dwFlags & WHDR_DONE)) { /* if audio is ready */ + + /* must have read exactly 320 bytes */ + if (whin[i].dwBytesRecorded != whin[i].dwBufferLength) + { + fprintf(stderr,"Short audio read, got %d bytes, expected %d bytes\n", whin[i].dwBytesRecorded, + whin[i].dwBufferLength); + return -1; + } + if(!most_recent_answer->gsmout) + most_recent_answer->gsmout = gsm_create(); + + service_network(netfd,f); /* service network here for better performance */ + /* encode the audio from the buffer into GSM format */ + gsm_encode(most_recent_answer->gsmout, (short *) ((char *) whin[i].lpData), fo); + if(iax_send_voice(most_recent_answer->session, + AST_FORMAT_GSM, (char *)fo, sizeof(gsm_frame)) == -1) + puts("Failed to send voice!"); + lastouttick = GetTickCount(); /* save time of last output */ + + /* unprepare (free) the header */ + waveInUnprepareHeader(win,&whin[i],sizeof(WAVEHDR)); + /* initialize the buffer */ + memset(&whin[i],0,sizeof(WAVEHDR)); + /* bump the serial number to look for the next time */ + nextwhin++; + /* exit the loop so that we can start at lowest buffer again */ + break; + } + } + if (i >= NWHIN) break; /* if all found, get out of loop */ + } + } + + } + return 0; +} + +void +do_iax_event(FILE *f) { + int sessions = 0; + struct iax_event *e = 0; + struct peer *peer; + + while ( (e = iax_get_event(0))) { + peer = find_peer(e->session); + if(peer) { + handle_event(f, e, peer); + } else { + if(e->etype != IAX_EVENT_CONNECT) { + fprintf(stderr, "Huh? This is an event for a non-existant session?\n"); + } + sessions++; + + if(sessions >= MAX_SESSIONS) { + fprintf(f, "Missed a call... too many sessions open.\n"); + } + + + if(e->event.connect.callerid && e->event.connect.dnid) + fprintf(f, "Call from '%s' for '%s'", e->event.connect.callerid, + e->event.connect.dnid); + else if(e->event.connect.dnid) { + fprintf(f, "Call from '%s'", e->event.connect.dnid); + } else if(e->event.connect.callerid) { + fprintf(f, "Call from '%s'", e->event.connect.callerid); + } else printf("Call from"); + fprintf(f, " (%s)\n", inet_ntoa(iax_get_peer_addr(e->session).sin_addr)); + + if(most_recent_answer) { + fprintf(f, "Incoming call ignored, there's already a call waiting for answer... \ +please accept or reject first\n"); + iax_reject(e->session, "Too many calls, we're busy!"); + } else { + if ( !(peer = malloc(sizeof(struct peer)))) { + fprintf(f, "Warning: Unable to allocate memory!\n"); + return; + } + + peer->time = time(0); + peer->session = e->session; + peer->gsmin = 0; + peer->gsmout = 0; + + peer->next = peers; + peers = peer; + + iax_accept(peer->session); + iax_ring_announce(peer->session); + most_recent_answer = peer; + fprintf(f, "Incoming call!\n"); + } + iax_event_free(e); + issue_prompt(f); + } + } +} + +void +call(FILE *f, char *num) +{ + struct peer *peer; + + if(!newcall) + newcall = iax_session_new(); + else { + fprintf(f, "Already attempting to call somewhere, please cancel first!\n"); + return; + } + + if ( !(peer = malloc(sizeof(struct peer)))) { + fprintf(f, "Warning: Unable to allocate memory!\n"); + return; + } + + peer->time = time(0); + peer->session = newcall; + peer->gsmin = 0; + peer->gsmout = 0; + + peer->next = peers; + peers = peer; + + most_recent_answer = peer; + + iax_call(peer->session, num, 10); +} + +void +answer_call(void) +{ + if(most_recent_answer) + iax_answer(most_recent_answer->session); + printf("Answering call!\n"); + answered_call = 1; +} + +void +dump_call(void) +{ + if(most_recent_answer) + { + iax_hangup(most_recent_answer->session,""); + free(most_recent_answer); + } + printf("Dumping call!\n"); + answered_call = 0; + most_recent_answer = 0; + answered_call = 0; + peers = 0; + newcall = 0; +} + +void +reject_call(void) +{ + iax_reject(most_recent_answer->session, "Call rejected manually."); + most_recent_answer = 0; +} + +void +handle_event(FILE *f, struct iax_event *e, struct peer *p) +{ + int len,n; + WHOUT *wh,*wh1; + short fr[160]; + static paused_xmit = 0; + + + switch(e->etype) { + case IAX_EVENT_HANGUP: + iax_hangup(most_recent_answer->session, "Byeee!"); + fprintf(f, "Call disconnected by peer\n"); + free(most_recent_answer); + most_recent_answer = 0; + answered_call = 0; + peers = 0; + newcall = 0; + + break; + + case IAX_EVENT_REJECT: + fprintf(f, "Authentication was rejected\n"); + break; + case IAX_EVENT_ACCEPT: + fprintf(f, "Waiting for answer... RING RING\n"); + issue_prompt(f); + break; + case IAX_EVENT_ANSWER: + answer_call(); + break; + case IAX_EVENT_VOICE: + switch(e->event.voice.format) { + case AST_FORMAT_GSM: + if(e->event.voice.datalen % 33) { + fprintf(stderr, "Weird gsm frame, not a multiple of 33.\n"); + break; + } + + if (!p->gsmin) + p->gsmin = gsm_create(); + + len = 0; + while(len < e->event.voice.datalen) { + if(gsm_decode(p->gsmin, (char *) e->event.voice.data + len, fr)) { + fprintf(stderr, "Bad GSM data\n"); + break; + } else { /* its an audio packet to be output to user */ + + /* get count of pending items in audio output queue */ + n = 0; + if (outqueue) + { /* determine number of pending out queue items */ + for(wh = outqueue; wh != NULL; wh = wh->next) + { + if (!(wh->w.dwFlags & WHDR_DONE)) n++; + } + } + /* if not too many, send to user, otherwise chuck packet */ + if (n <= OUT_DEPTH) /* if not to chuck packet */ + { + /* malloc the memory for the queue item */ + wh = (WHOUT *) malloc(sizeof(WHOUT)); + if (wh == (WHOUT *) NULL) /* if error, bail */ + { + fprintf(stderr,"Outa memory!!!!\n"); + exit(255); + } + /* initialize the queue entry */ + memset(wh,0,sizeof(WHOUT)); + /* copy the PCM data from the gsm conversion buffer */ + memcpy((char *)wh->data,(char *)fr,sizeof(fr)); + /* set parameters for data */ + wh->w.lpData = (char *) wh->data; + wh->w.dwBufferLength = 320; + + /* prepare buffer for output */ + if (waveOutPrepareHeader(wout,&wh->w,sizeof(WAVEHDR))) + { + fprintf(stderr,"Cannot prepare header for audio out\n"); + exit(255); + } + /* if not currently transmitting, hold off a couple of packets for + smooth sounding output */ + if ((!n) && (!paused_xmit)) + { + /* pause output (before starting) */ + waveOutPause(wout); + /* indicate as such */ + paused_xmit = 1; + } + /* queue packet for output on audio device */ + if (waveOutWrite(wout,&wh->w,sizeof(WAVEHDR))) + { + fprintf(stderr,"Cannot output to wave output device\n"); + exit(255); + } + /* if we are paused, and we have enough packets, start audio */ + if ((n > OUT_PAUSE_THRESHOLD) && paused_xmit) + { + /* start the output */ + waveOutRestart(wout); + /* indicate as such */ + paused_xmit = 0; + } + /* insert it onto tail of outqueue */ + if (outqueue == NULL) /* if empty queue */ + outqueue = wh; /* point queue to new entry */ + else /* otherwise is non-empty queue */ + { + wh1 = outqueue; + while(wh1->next) wh1 = wh1->next; /* find last entry in queue */ + wh1->next = wh; /* point it to new entry */ + } + } +#ifdef PRINTCHUCK + else printf("Chucking packet!!\n"); +#endif + } + len += 33; + } + break; + default : + fprintf(f, "Don't know how to handle that format %d\n", e->event.voice.format); + } + break; + case IAX_EVENT_RINGA: + break; + default: + fprintf(f, "Unknown event: %d\n", e->etype); + break; + } +} + +void +parse_cmd(FILE *f, int argc, char **argv) +{ + _strupr(argv[0]); + if(!strcmp(argv[0], "HELP")) { + if(argc == 1) + dump_array(f, help); + else if(argc == 2) { + if(!strcmp(argv[1], "HELP")) + fprintf(f, "Help <Command>\t-\tDisplays general help or specific help on command if supplied an arguement\n"); + else if(!strcmp(argv[1], "QUIT")) + fprintf(f, "Quit\t\t-\tShuts down the miniphone\n"); + else fprintf(f, "No help available on %s\n", argv[1]); + } else { + fprintf(f, "Too many arguements for command help.\n"); + } + } else if(!strcmp(argv[0], "STATUS")) { + if(argc == 1) { + int c = 0; + struct peer *peerptr = peers; + + if(!peerptr) + fprintf(f, "No session matches found.\n"); + else while(peerptr) { + fprintf(f, "Listing sessions:\n\n"); + fprintf(f, "Session %d\n", ++c); + fprintf(f, "Session existed for %d seconds\n", (int)time(0)-peerptr->time); + if(answered_call) + fprintf(f, "Call answered.\n"); + else fprintf(f, "Call ringing.\n"); + + peerptr = peerptr->next; + } + } else fprintf(f, "Too many arguments for command status.\n"); + } else if(!strcmp(argv[0], "ANSWER")) { + if(argc > 1) + fprintf(f, "Too many arguements for command answer\n"); + else answer_call(); + } else if(!strcmp(argv[0], "REJECT")) { + if(argc > 1) + fprintf(f, "Too many arguements for command reject\n"); + else { + fprintf(f, "Rejecting current phone call.\n"); + reject_call(); + } + } else if(!strcmp(argv[0], "CALL")) { + if(argc > 2) + fprintf(f, "Too many arguements for command call\n"); + else { + call(f, argv[1]); + } + } else if(!strcmp(argv[0], "DUMP")) { + if(argc > 1) + fprintf(f, "Too many arguements for command dump\n"); + else { + dump_call(); + } + } else if(!strcmp(argv[0], "DTMF")) { + if(argc > 2) + { + fprintf(f, "Too many arguements for command dtmf\n"); + return; + } + if (argc < 1) + { + fprintf(f, "Too many arguements for command dtmf\n"); + return; + } + if(most_recent_answer) + iax_send_dtmf(most_recent_answer->session,*argv[1]); + } else if(!strcmp(argv[0], "QUIT")) { + if(argc > 1) + fprintf(f, "Too many arguements for command quit\n"); + else { + fprintf(f, "Good bye!\n"); + exit(1); + } + } else fprintf(f, "Unknown command of %s\n", argv[0]); +} + +void +issue_prompt(FILE *f) +{ + fprintf(f, "TeleClient> "); + fflush(f); +} + +void +dump_array(FILE *f, char **array) { + while(*array) + fprintf(f, "%s\n", *array++); +} Added: freeswitch/trunk/src/mod/endpoints/mod_iax/winpoop.h ============================================================================== --- (empty file) +++ freeswitch/trunk/src/mod/endpoints/mod_iax/winpoop.h Thu May 8 19:25:36 2008 @@ -0,0 +1,40 @@ +/* + * Functions Windows doesn't have... but should + * Copyright(C) 2001, Linux Support Services, Inc. + * + * Distributed under GNU LGPL. + * + * These are NOT fully compliant with BSD 4.3 and are not + * threadsafe. + * + */ + +#ifndef _winpoop_h +#define _winpoop_h + +#if defined(_MSC_VER) +#define INLINE __inline +#else +#define INLINE inline +#endif + +#include <winsock.h> + +void gettimeofday(struct timeval *tv, void /*struct timezone*/ *tz); + +static INLINE int inet_aton(char *cp, struct in_addr *inp) +{ + int a1, a2, a3, a4; + unsigned int saddr; + if (sscanf(cp, "%d.%d.%d.%d", &a1, &a2, &a3, &a4) != 4) + return 0; + a1 &= 0xff; + a2 &= 0xff; + a3 &= 0xff; + a4 &= 0xff; + saddr = (a1 << 24) | (a2 << 16) | (a3 << 8) | a4; + inp->s_addr = htonl(saddr); + return 1; +} + +#endif
participants (1)
-
Freeswitch SVN