diff options
-rw-r--r-- | pjnath/include/pjnath/ice_strans.h | 11 | ||||
-rw-r--r-- | pjnath/include/pjnath/turn_session.h | 10 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice_strans.c | 21 | ||||
-rw-r--r-- | pjnath/src/pjnath/turn_session.c | 16 | ||||
-rw-r--r-- | pjnath/src/pjnath/turn_sock.c | 105 | ||||
-rw-r--r-- | pjsip-apps/src/samples/debug.c | 2 | ||||
-rw-r--r-- | pjsip-apps/src/samples/icedemo.c | 39 |
7 files changed, 173 insertions, 31 deletions
diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h index 43a837da..158c303c 100644 --- a/pjnath/include/pjnath/ice_strans.h +++ b/pjnath/include/pjnath/ice_strans.h @@ -622,6 +622,17 @@ PJ_DECL(pj_status_t) pj_ice_strans_get_ufrag_pwd(pj_ice_strans *ice_st, /** + * Get the number of local candidates for the specified component ID. + * + * @param ice_st The ICE stream transport. + * @param comp_id Component ID. + * + * @return The number of candidates. + */ +PJ_DECL(unsigned) pj_ice_strans_get_cands_count(pj_ice_strans *ice_st, + unsigned comp_id); + +/** * Enumerate the local candidates for the specified component. * * @param ice_st The ICE stream transport. diff --git a/pjnath/include/pjnath/turn_session.h b/pjnath/include/pjnath/turn_session.h index 64f12787..39e4cbf4 100644 --- a/pjnath/include/pjnath/turn_session.h +++ b/pjnath/include/pjnath/turn_session.h @@ -455,11 +455,19 @@ PJ_DECL(pj_status_t) pj_turn_session_shutdown(pj_turn_session *sess); * be notified about the client destruction. * * @param sess The TURN client session. + * @param last_err Optional error code to be set to the session, + * which would be returned back in the \a info + * parameter of #pj_turn_session_get_info(). If + * this argument value is PJ_SUCCESS, the error + * code will not be set. If the session already + * has an error code set, this function will not + * overwrite that error code either. * * @return PJ_SUCCESS if the operation has been successful, * or the appropriate error code on failure. */ -PJ_DECL(pj_status_t) pj_turn_session_destroy(pj_turn_session *sess); +PJ_DECL(pj_status_t) pj_turn_session_destroy(pj_turn_session *sess, + pj_status_t last_err); /** diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c index d702c656..b5608cb0 100644 --- a/pjnath/src/pjnath/ice_strans.c +++ b/pjnath/src/pjnath/ice_strans.c @@ -912,6 +912,27 @@ PJ_DEF(pj_status_t) pj_ice_strans_get_ufrag_pwd( pj_ice_strans *ice_st, } /* + * Get number of candidates + */ +PJ_DEF(unsigned) pj_ice_strans_get_cands_count(pj_ice_strans *ice_st, + unsigned comp_id) +{ + unsigned i, cnt; + + PJ_ASSERT_RETURN(ice_st && ice_st->ice && comp_id && + comp_id <= ice_st->comp_cnt, 0); + + cnt = 0; + for (i=0; i<ice_st->ice->lcand_cnt; ++i) { + if (ice_st->ice->lcand[i].comp_id != comp_id) + continue; + ++cnt; + } + + return cnt; +} + +/* * Enum candidates */ PJ_DEF(pj_status_t) pj_ice_strans_enum_cands(pj_ice_strans *ice_st, diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c index f3a33ea6..f0833b3f 100644 --- a/pjnath/src/pjnath/turn_session.c +++ b/pjnath/src/pjnath/turn_session.c @@ -461,10 +461,13 @@ PJ_DEF(pj_status_t) pj_turn_session_shutdown(pj_turn_session *sess) /** * Forcefully destroy the TURN session. */ -PJ_DEF(pj_status_t) pj_turn_session_destroy( pj_turn_session *sess) +PJ_DEF(pj_status_t) pj_turn_session_destroy( pj_turn_session *sess, + pj_status_t last_err) { PJ_ASSERT_RETURN(sess, PJ_EINVAL); + if (last_err != PJ_SUCCESS && sess->last_status == PJ_SUCCESS) + sess->last_status = last_err; set_state(sess, PJ_TURN_STATE_DEALLOCATED); sess_shutdown(sess, PJ_SUCCESS); return PJ_SUCCESS; @@ -959,12 +962,16 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr), PJ_FALSE, PJ_FALSE); if (ch && ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound) { + unsigned total_len; + /* Peer is assigned a channel number, we can use ChannelData */ pj_turn_channel_data *cd = (pj_turn_channel_data*)sess->tx_pkt; pj_assert(sizeof(*cd)==4); - if (pkt_len > sizeof(sess->tx_pkt)-sizeof(*cd)) { + /* Calculate total length, including paddings */ + total_len = (pkt_len + sizeof(*cd) + 3) & (~3); + if (total_len > sizeof(sess->tx_pkt)) { status = PJ_ETOOBIG; goto on_return; } @@ -975,7 +982,7 @@ PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess, pj_assert(sess->srv_addr != NULL); - status = sess->cb.on_send_pkt(sess, sess->tx_pkt, pkt_len+sizeof(*cd), + status = sess->cb.on_send_pkt(sess, sess->tx_pkt, total_len, sess->srv_addr, pj_sockaddr_get_len(sess->srv_addr)); @@ -1156,7 +1163,8 @@ PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess, goto on_return; } else { if (parsed_len) { - *parsed_len = cd.length + sizeof(cd); + /* Apply padding too */ + *parsed_len = ((cd.length + 3) & (~3)) + sizeof(cd); } } diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c index 287b0299..e2823ff3 100644 --- a/pjnath/src/pjnath/turn_sock.c +++ b/pjnath/src/pjnath/turn_sock.c @@ -273,14 +273,7 @@ static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e) static void show_err(pj_turn_sock *turn_sock, const char *title, pj_status_t status) { - char errmsg[PJ_ERR_MSG_SIZE]; - - if (status != PJ_SUCCESS) { - pj_strerror(status, errmsg, sizeof(errmsg)); - PJ_LOG(4,(turn_sock->obj_name, "%s: %s", title, errmsg)); - } else { - PJ_LOG(4,(turn_sock->obj_name, "%s", title, errmsg)); - } + PJ_PERROR(4,(turn_sock->obj_name, status, title)); } /* On error, terminate session */ @@ -288,8 +281,9 @@ static void sess_fail(pj_turn_sock *turn_sock, const char *title, pj_status_t status) { show_err(turn_sock, title, status); - if (turn_sock->sess) - pj_turn_session_destroy(turn_sock->sess); + if (turn_sock->sess) { + pj_turn_session_destroy(turn_sock->sess, status); + } } /* @@ -494,6 +488,49 @@ static pj_bool_t on_connect_complete(pj_activesock_t *asock, return PJ_TRUE; } +static pj_uint16_t GETVAL16H(const pj_uint8_t *buf, unsigned pos) +{ + return (pj_uint16_t) ((buf[pos + 0] << 8) | \ + (buf[pos + 1] << 0)); +} + +/* Quick check to determine if there is enough packet to process in the + * incoming buffer. Return the packet length, or zero if there's no packet. + */ +static unsigned has_packet(pj_turn_sock *turn_sock, const void *buf, pj_size_t bufsize) +{ + pj_bool_t is_stun; + + if (turn_sock->conn_type == PJ_TURN_TP_UDP) + return bufsize; + + /* Quickly check if this is STUN message, by checking the first two bits and + * size field which must be multiple of 4 bytes + */ + is_stun = ((((pj_uint8_t*)buf)[0] & 0xC0) == 0) && + ((GETVAL16H((const pj_uint8_t*)buf, 2) & 0x03)==0); + + if (is_stun) { + pj_size_t msg_len = GETVAL16H((const pj_uint8_t*)buf, 2); + return (msg_len+20 <= bufsize) ? msg_len+20 : 0; + } else { + /* This must be ChannelData. */ + pj_turn_channel_data cd; + + if (bufsize < 4) + return 0; + + /* Decode ChannelData packet */ + pj_memcpy(&cd, buf, sizeof(pj_turn_channel_data)); + cd.length = pj_ntohs(cd.length); + + if (bufsize >= cd.length+sizeof(cd)) + return (cd.length+sizeof(cd)+3) & (~3); + else + return 0; + } +} + /* * Notification from ioqueue when incoming UDP packet is received. */ @@ -504,21 +541,51 @@ static pj_bool_t on_data_read(pj_activesock_t *asock, pj_size_t *remainder) { pj_turn_sock *turn_sock; - pj_size_t parsed_len; pj_bool_t ret = PJ_TRUE; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); pj_lock_acquire(turn_sock->lock); if (status == PJ_SUCCESS && turn_sock->sess) { - /* Report incoming packet to TURN session */ - parsed_len = (unsigned)size; - pj_turn_session_on_rx_pkt(turn_sock->sess, data, size, &parsed_len); - if (parsed_len < (unsigned)size) { - *remainder = size - parsed_len; - pj_memmove(data, ((char*)data)+parsed_len, *remainder); - } else { - *remainder = 0; + /* Report incoming packet to TURN session, repeat while we have + * "packet" in the buffer (required for stream-oriented transports) + */ + unsigned pkt_len; + + //PJ_LOG(5,(turn_sock->pool->obj_name, + // "Incoming data, %lu bytes total buffer", size)); + + while ((pkt_len=has_packet(turn_sock, data, size)) != 0) { + pj_size_t parsed_len; + //const pj_uint8_t *pkt = (const pj_uint8_t*)data; + + //PJ_LOG(5,(turn_sock->pool->obj_name, + // "Packet start: %02X %02X %02X %02X", + // pkt[0], pkt[1], pkt[2], pkt[3])); + + //PJ_LOG(5,(turn_sock->pool->obj_name, + // "Processing %lu bytes packet of %lu bytes total buffer", + // pkt_len, size)); + + parsed_len = (unsigned)size; + pj_turn_session_on_rx_pkt(turn_sock->sess, data, size, &parsed_len); + + /* parsed_len may be zero if we have parsing error, so use our + * previous calculation to exhaust the bad packet. + */ + if (parsed_len == 0) + parsed_len = pkt_len; + + if (parsed_len < (unsigned)size) { + *remainder = size - parsed_len; + pj_memmove(data, ((char*)data)+parsed_len, *remainder); + } else { + *remainder = 0; + } + size = *remainder; + + //PJ_LOG(5,(turn_sock->pool->obj_name, + // "Buffer size now %lu bytes", size)); } } else if (status != PJ_SUCCESS && turn_sock->conn_type != PJ_TURN_TP_UDP) diff --git a/pjsip-apps/src/samples/debug.c b/pjsip-apps/src/samples/debug.c index b8b624d9..5417a79a 100644 --- a/pjsip-apps/src/samples/debug.c +++ b/pjsip-apps/src/samples/debug.c @@ -28,5 +28,5 @@ * E.g.: * #include "playfile.c" */ -#include "jbsim.c" +#include "icedemo.c" diff --git a/pjsip-apps/src/samples/icedemo.c b/pjsip-apps/src/samples/icedemo.c index f3f7c6c3..1f2a0208 100644 --- a/pjsip-apps/src/samples/icedemo.c +++ b/pjsip-apps/src/samples/icedemo.c @@ -47,6 +47,7 @@ static struct app_t pj_str_t turn_username; pj_str_t turn_password; pj_bool_t turn_fingerprint; + const char *log_file; } opt; /* Our global variables */ @@ -56,6 +57,7 @@ static struct app_t pj_bool_t thread_quit_flag; pj_ice_strans_cfg ice_cfg; pj_ice_strans *icest; + FILE *log_fhnd; /* Variables to store parsed remote ICE info */ struct rem_info @@ -109,6 +111,12 @@ static void err_exit(const char *title, pj_status_t status) pj_caching_pool_destroy(&icedemo.cp); pj_shutdown(); + + if (icedemo.log_fhnd) { + fclose(icedemo.log_fhnd); + icedemo.log_fhnd = NULL; + } + exit(status != PJ_SUCCESS); } @@ -216,11 +224,13 @@ static void cb_on_rx_data(pj_ice_strans *ice_st, PJ_UNUSED_ARG(src_addr_len); PJ_UNUSED_ARG(pkt); - ((char*)pkt)[size] = '\0'; + // Don't do this! It will ruin the packet buffer in case TCP is used! + //((char*)pkt)[size] = '\0'; - PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s: \"%s\"", + PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s: \"%.*s\"", comp_id, size, pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3), + (unsigned)size, (char*)pkt)); } @@ -236,8 +246,6 @@ static void cb_on_ice_complete(pj_ice_strans *ice_st, (op==PJ_ICE_STRANS_OP_INIT? "initialization" : (op==PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" : "unknown_op")); - PJ_UNUSED_ARG(ice_st); - if (status == PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, "ICE %s successful", opname)); } else { @@ -245,9 +253,18 @@ static void cb_on_ice_complete(pj_ice_strans *ice_st, pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(1,(THIS_FILE, "ICE %s failed: %s", opname, errmsg)); + pj_ice_strans_destroy(ice_st); + icedemo.icest = NULL; } } +/* log callback to write to file */ +static void log_func(int level, const char *data, int len) +{ + if (icedemo.log_fhnd) + fwrite(data, len, 1, icedemo.log_fhnd); + pj_log_write(level, data, len); +} /* * This is the main application initialization function. It is called @@ -258,6 +275,11 @@ static pj_status_t icedemo_init(void) { pj_status_t status; + if (icedemo.opt.log_file) { + icedemo.log_fhnd = fopen(icedemo.opt.log_file, "a"); + pj_log_set_log_func(&log_func); + } + /* Initialize the libraries before anything else */ CHECK( pj_init() ); CHECK( pjlib_util_init() ); @@ -1147,6 +1169,7 @@ static void icedemo_usage() puts(" resolution"); puts(" --max-host, -H N Set max number of host candidates to N"); puts(" --regular, -R Use regular nomination (default aggressive)"); + puts(" --log-file, -L FILE Save output to log FILE"); puts(" --help, -h Display this screen."); puts(""); puts("STUN related options:"); @@ -1182,7 +1205,8 @@ int main(int argc, char *argv[]) { "turn-username", 1, 0, 'u'}, { "turn-password", 1, 0, 'p'}, { "turn-fingerprint", 0, 0, 'F'}, - { "regular", 0, 0, 'R'} + { "regular", 0, 0, 'R'}, + { "log-file", 1, 0, 'L'}, }; int c, opt_id; pj_status_t status; @@ -1190,7 +1214,7 @@ int main(int argc, char *argv[]) icedemo.opt.comp_cnt = 1; icedemo.opt.max_host = -1; - while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:hTFR", long_options, &opt_id))!=-1) { + while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:L:hTFR", long_options, &opt_id))!=-1) { switch (c) { case 'c': icedemo.opt.comp_cnt = atoi(pj_optarg); @@ -1229,6 +1253,9 @@ int main(int argc, char *argv[]) case 'R': icedemo.opt.regular = PJ_TRUE; break; + case 'L': + icedemo.opt.log_file = pj_optarg; + break; default: printf("Argument \"%s\" is not valid. Use -h to see help", argv[pj_optind]); |