int check_pjlib_state(pj_stun_config *cfg, const struct pjlib_state *initial_st) { struct pjlib_state current_state; int rc = 0; capture_pjlib_state(cfg, ¤t_state); if (current_state.timer_cnt > initial_st->timer_cnt) { PJ_LOG(3,("", " error: possibly leaking timer")); rc |= ERR_TIMER_LEAK; #if PJ_TIMER_DEBUG pj_timer_heap_dump(cfg->timer_heap); #endif } if (current_state.pool_used_cnt > initial_st->pool_used_cnt) { PJ_LOG(3,("", " error: possibly leaking memory")); PJ_LOG(3,("", " dumping memory pool:")); pj_pool_factory_dump(mem, PJ_TRUE); rc |= ERR_MEMORY_LEAK; } return rc; }
static int destroy_test(pj_stun_config *stun_cfg, pj_bool_t with_dns_srv, pj_bool_t in_callback) { struct test_session_cfg test_cfg = { { /* Client cfg */ /* DNS SRV */ /* Destroy on state */ PJ_TRUE, 0xFFFF }, { /* Server cfg */ 0xFFFFFFFF, /* flags */ PJ_TRUE, /* respond to allocate */ PJ_TRUE /* respond to refresh */ } }; struct test_session *sess; int target_state; int rc; PJ_LOG(3,("", " destroy test %s %s", (in_callback? "in callback" : ""), (with_dns_srv? "with DNS srv" : "") )); test_cfg.client.enable_dns_srv = with_dns_srv; for (target_state=PJ_TURN_STATE_RESOLVING; target_state<=PJ_TURN_STATE_READY; ++target_state) { enum { TIMEOUT = 60 }; pjlib_state pjlib_state; pj_turn_session_info info; pj_time_val tstart; capture_pjlib_state(stun_cfg, &pjlib_state); PJ_LOG(3,("", " %s", pj_turn_state_name((pj_turn_state_t)target_state))); if (in_callback) test_cfg.client.destroy_on_state = target_state; rc = create_test_session(stun_cfg, &test_cfg, &sess); if (rc != 0) return rc; if (in_callback) { pj_gettimeofday(&tstart); rc = 0; while (sess->turn_sock) { pj_time_val now; poll_events(stun_cfg, 100, PJ_FALSE); pj_gettimeofday(&now); if (now.sec - tstart.sec > TIMEOUT) { rc = -7; break; } } } else { pj_gettimeofday(&tstart); rc = 0; while (sess->turn_sock) { pj_time_val now; poll_events(stun_cfg, 1, PJ_FALSE); pj_turn_sock_get_info(sess->turn_sock, &info); if (info.state >= target_state) { pj_turn_sock_destroy(sess->turn_sock); break; } pj_gettimeofday(&now); if (now.sec - tstart.sec > TIMEOUT) { rc = -8; break; } } } if (rc != 0) { PJ_LOG(3,("", " error: timeout")); return rc; } poll_events(stun_cfg, 1000, PJ_FALSE); destroy_session(sess); rc = check_pjlib_state(stun_cfg, &pjlib_state); if (rc != 0) { PJ_LOG(3,("", " error: memory/timer-heap leak detected")); return rc; } } return 0; }
static int state_progression_test(pj_stun_config *stun_cfg) { struct test_session_cfg test_cfg = { { /* Client cfg */ /* DNS SRV */ /* Destroy on state */ PJ_TRUE, 0xFFFF }, { /* Server cfg */ 0xFFFFFFFF, /* flags */ PJ_TRUE, /* respond to allocate */ PJ_TRUE /* respond to refresh */ } }; struct test_session *sess; unsigned i; int rc; PJ_LOG(3,("", " state progression tests")); for (i=0; i<=1; ++i) { enum { TIMEOUT = 60 }; pjlib_state pjlib_state; pj_turn_session_info info; struct test_result result; pj_time_val tstart; PJ_LOG(3,("", " %s DNS SRV resolution", (i==0? "without" : "with"))); capture_pjlib_state(stun_cfg, &pjlib_state); test_cfg.client.enable_dns_srv = i; rc = create_test_session(stun_cfg, &test_cfg, &sess); if (rc != 0) return rc; pj_bzero(&info, sizeof(info)); /* Wait until state is READY */ pj_gettimeofday(&tstart); while (sess->turn_sock) { pj_time_val now; poll_events(stun_cfg, 10, PJ_FALSE); rc = pj_turn_sock_get_info(sess->turn_sock, &info); if (rc!=PJ_SUCCESS) break; if (info.state >= PJ_TURN_STATE_READY) break; pj_gettimeofday(&now); if (now.sec - tstart.sec > TIMEOUT) { PJ_LOG(3,("", " timed-out")); break; } } if (info.state != PJ_TURN_STATE_READY) { PJ_LOG(3,("", " error: state is not READY")); destroy_session(sess); return -130; } /* Deallocate */ pj_turn_sock_destroy(sess->turn_sock); /* Wait for couple of seconds. * We can't poll the session info since the session may have * been destroyed */ poll_events(stun_cfg, 2000, PJ_FALSE); sess->turn_sock = NULL; pj_memcpy(&result, &sess->result, sizeof(result)); destroy_session(sess); /* Check the result */ if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVING)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_RESOLVING is not called")); return -140; } if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVED)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_RESOLVED is not called")); return -150; } if ((result.state_called & (1<<PJ_TURN_STATE_ALLOCATING)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_ALLOCATING is not called")); return -155; } if ((result.state_called & (1<<PJ_TURN_STATE_READY)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_READY is not called")); return -160; } if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATING)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_DEALLOCATING is not called")); return -170; } if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATED)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_DEALLOCATED is not called")); return -180; } if ((result.state_called & (1<<PJ_TURN_STATE_DESTROYING)) == 0) { PJ_LOG(3,("", " error: PJ_TURN_STATE_DESTROYING is not called")); return -190; } poll_events(stun_cfg, 500, PJ_FALSE); rc = check_pjlib_state(stun_cfg, &pjlib_state); if (rc != 0) { PJ_LOG(3,("", " error: memory/timer-heap leak detected")); return rc; } } return 0; }
static int perform_test(const char *title, pj_stun_config *stun_cfg, unsigned server_flag, struct test_cfg *caller_cfg, struct test_cfg *callee_cfg) { pjlib_state pjlib_state; struct test_sess *sess; int rc; PJ_LOG(3,("", INDENT "%s", title)); capture_pjlib_state(stun_cfg, &pjlib_state); rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg, &sess); if (rc != 0) return rc; #define ALL_READY (sess->caller.result.init_status!=PJ_EPENDING && \ sess->callee.result.init_status!=PJ_EPENDING) /* Wait until both ICE transports are initialized */ WAIT_UNTIL(30, ALL_READY, rc); if (!ALL_READY) { PJ_LOG(3,("", INDENT "err: init timed-out")); destroy_sess(sess, 500); return -100; } if (sess->caller.result.init_status != sess->caller.cfg.expected.init_status) { app_perror(INDENT "err: caller init", sess->caller.result.init_status); destroy_sess(sess, 500); return -102; } if (sess->callee.result.init_status != sess->callee.cfg.expected.init_status) { app_perror(INDENT "err: callee init", sess->callee.result.init_status); destroy_sess(sess, 500); return -104; } /* Failure condition */ if (sess->caller.result.init_status != PJ_SUCCESS || sess->callee.result.init_status != PJ_SUCCESS) { rc = 0; goto on_return; } /* Init ICE on caller */ rc = pj_ice_strans_init_ice(sess->caller.ice, sess->caller.cfg.role, &sess->caller.ufrag, &sess->caller.pass); if (rc != PJ_SUCCESS) { app_perror(INDENT "err: caller pj_ice_strans_init_ice()", rc); destroy_sess(sess, 500); return -100; } /* Init ICE on callee */ rc = pj_ice_strans_init_ice(sess->callee.ice, sess->callee.cfg.role, &sess->callee.ufrag, &sess->callee.pass); if (rc != PJ_SUCCESS) { app_perror(INDENT "err: callee pj_ice_strans_init_ice()", rc); destroy_sess(sess, 500); return -110; } /* Start ICE on callee */ rc = start_ice(&sess->callee, &sess->caller); if (rc != PJ_SUCCESS) { destroy_sess(sess, 500); return -120; } /* Wait for callee's answer_delay */ poll_events(stun_cfg, sess->callee.cfg.answer_delay, PJ_FALSE); /* Start ICE on caller */ rc = start_ice(&sess->caller, &sess->callee); if (rc != PJ_SUCCESS) { destroy_sess(sess, 500); return -130; } /* Wait until negotiation is complete on both endpoints */ #define ALL_DONE (sess->caller.result.nego_status!=PJ_EPENDING && \ sess->callee.result.nego_status!=PJ_EPENDING) WAIT_UNTIL(30, ALL_DONE, rc); if (!ALL_DONE) { PJ_LOG(3,("", INDENT "err: negotiation timed-out")); destroy_sess(sess, 500); return -140; } if (sess->caller.result.nego_status != sess->caller.cfg.expected.nego_status) { app_perror(INDENT "err: caller negotiation failed", sess->caller.result.nego_status); destroy_sess(sess, 500); return -150; } if (sess->callee.result.nego_status != sess->callee.cfg.expected.nego_status) { app_perror(INDENT "err: callee negotiation failed", sess->callee.result.nego_status); destroy_sess(sess, 500); return -160; } /* Verify that both agents have agreed on the same pair */ rc = check_pair(&sess->caller, &sess->callee, -170); if (rc != 0) { destroy_sess(sess, 500); return rc; } rc = check_pair(&sess->callee, &sess->caller, -180); if (rc != 0) { destroy_sess(sess, 500); return rc; } /* Looks like everything is okay */ /* Destroy ICE stream transports first to let it de-allocate * TURN relay (otherwise there'll be timer/memory leak, unless * we wait for long time in the last poll_events() below). */ if (sess->caller.ice) { pj_ice_strans_destroy(sess->caller.ice); sess->caller.ice = NULL; } if (sess->callee.ice) { pj_ice_strans_destroy(sess->callee.ice); sess->callee.ice = NULL; } on_return: /* Wait.. */ poll_events(stun_cfg, 500, PJ_FALSE); /* Now destroy everything */ destroy_sess(sess, 500); /* Flush events */ poll_events(stun_cfg, 100, PJ_FALSE); rc = check_pjlib_state(stun_cfg, &pjlib_state); if (rc != 0) { return rc; } return 0; }
static int perform_test2(const char *title, pj_stun_config *stun_cfg, unsigned server_flag, struct test_cfg *caller_cfg, struct test_cfg *callee_cfg, struct sess_param *test_param) { pjlib_state pjlib_state; struct test_sess *sess; unsigned i; int rc; PJ_LOG(3,(THIS_FILE, INDENT "%s", title)); capture_pjlib_state(stun_cfg, &pjlib_state); rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg, test_param, &sess); if (rc != 0) return rc; #define ALL_READY (sess->caller.result.init_status!=PJ_EPENDING && \ sess->callee.result.init_status!=PJ_EPENDING) /* Wait until both ICE transports are initialized */ WAIT_UNTIL(30000, ALL_READY, rc); if (!ALL_READY) { PJ_LOG(3,(THIS_FILE, INDENT "err: init timed-out")); destroy_sess(sess, 500); return -100; } if (sess->caller.result.init_status != sess->caller.cfg.expected.init_status) { app_perror(INDENT "err: caller init", sess->caller.result.init_status); destroy_sess(sess, 500); return -102; } if (sess->callee.result.init_status != sess->callee.cfg.expected.init_status) { app_perror(INDENT "err: callee init", sess->callee.result.init_status); destroy_sess(sess, 500); return -104; } /* Failure condition */ if (sess->caller.result.init_status != PJ_SUCCESS || sess->callee.result.init_status != PJ_SUCCESS) { rc = 0; goto on_return; } /* Init ICE on caller */ rc = pj_ice_strans_init_ice(sess->caller.ice, sess->caller.cfg.role, &sess->caller.ufrag, &sess->caller.pass); if (rc != PJ_SUCCESS) { app_perror(INDENT "err: caller pj_ice_strans_init_ice()", rc); destroy_sess(sess, 500); return -100; } /* Init ICE on callee */ rc = pj_ice_strans_init_ice(sess->callee.ice, sess->callee.cfg.role, &sess->callee.ufrag, &sess->callee.pass); if (rc != PJ_SUCCESS) { app_perror(INDENT "err: callee pj_ice_strans_init_ice()", rc); destroy_sess(sess, 500); return -110; } /* Start ICE on callee */ rc = start_ice(&sess->callee, &sess->caller); if (rc != PJ_SUCCESS) { destroy_sess(sess, 500); return -120; } /* Wait for callee's answer_delay */ poll_events(stun_cfg, sess->callee.cfg.answer_delay, PJ_FALSE); /* Start ICE on caller */ rc = start_ice(&sess->caller, &sess->callee); if (rc != PJ_SUCCESS) { destroy_sess(sess, 500); return -130; } for (i=0; i<sess->param->worker_cnt; ++i) { pj_status_t status; status = pj_thread_create(sess->pool, "worker_thread", worker_thread_proc, sess, 0, 0, &sess->worker_threads[i]); if (status != PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, INDENT "err: create thread")); destroy_sess(sess, 500); return -135; } } if (sess->param->destroy_after_create) goto on_destroy; if (sess->param->destroy_after_one_done) { while (sess->caller.result.init_status==PJ_EPENDING && sess->callee.result.init_status==PJ_EPENDING) { if (sess->param->worker_cnt) pj_thread_sleep(0); else poll_events(stun_cfg, 0, PJ_FALSE); } goto on_destroy; } WAIT_UNTIL(30000, ALL_DONE, rc); if (!ALL_DONE) { PJ_LOG(3,(THIS_FILE, INDENT "err: negotiation timed-out")); destroy_sess(sess, 500); return -140; } if (sess->caller.result.nego_status != sess->caller.cfg.expected.nego_status) { app_perror(INDENT "err: caller negotiation failed", sess->caller.result.nego_status); destroy_sess(sess, 500); return -150; } if (sess->callee.result.nego_status != sess->callee.cfg.expected.nego_status) { app_perror(INDENT "err: callee negotiation failed", sess->callee.result.nego_status); destroy_sess(sess, 500); return -160; } /* Verify that both agents have agreed on the same pair */ rc = check_pair(&sess->caller, &sess->callee, -170); if (rc != 0) { destroy_sess(sess, 500); return rc; } rc = check_pair(&sess->callee, &sess->caller, -180); if (rc != 0) { destroy_sess(sess, 500); return rc; } /* Looks like everything is okay */ on_destroy: /* Destroy ICE stream transports first to let it de-allocate * TURN relay (otherwise there'll be timer/memory leak, unless * we wait for long time in the last poll_events() below). */ if (sess->caller.ice) { pj_ice_strans_destroy(sess->caller.ice); sess->caller.ice = NULL; } if (sess->callee.ice) { pj_ice_strans_destroy(sess->callee.ice); sess->callee.ice = NULL; } on_return: /* Wait.. */ poll_events(stun_cfg, 200, PJ_FALSE); /* Now destroy everything */ destroy_sess(sess, 500); /* Flush events */ poll_events(stun_cfg, 100, PJ_FALSE); rc = check_pjlib_state(stun_cfg, &pjlib_state); if (rc != 0) { return rc; } return rc; }