Fixed assertion error if ACK is received before INVITE transaction sends final response (malicious?). Also fixed misc warnings, and stress-tested on Quad Xeon

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@657 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index b59365a..f76aebd 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -139,6 +139,7 @@
     puts  ("User Agent options:");
     puts  ("  --auto-answer=code  Automatically answer incoming calls with code (e.g. 200)");
     puts  ("  --max-calls=N       Maximum number of concurrent calls (default:4, max:255)");
+    puts  ("  --thread-cnt=N      Number of worker threads (default:1)");
     /*
     puts  ("  --duration=SEC      Set maximum call duration (default:no limit)");
     */
@@ -264,7 +265,7 @@
 	   OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
 	   OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL,
 	   OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS, 
-	   OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP,
+	   OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
     };
     struct pj_getopt_option long_options[] = {
 	{ "config-file",1, 0, OPT_CONFIG_FILE},
@@ -311,7 +312,8 @@
 	{ "next-account",0,0, OPT_NEXT_ACCOUNT},
 	{ "next-cred",	0, 0, OPT_NEXT_CRED},
 	{ "max-calls",	1, 0, OPT_MAX_CALLS},
-	{ "duration",1,0, OPT_DURATION},
+	{ "duration",	1, 0, OPT_DURATION},
+	{ "thread-cnt",	1, 0, OPT_THREAD_CNT},
 	{ NULL, 0, 0, 0}
     };
     pj_status_t status;
@@ -612,6 +614,15 @@
 	    break;
 	*/
 
+	case OPT_THREAD_CNT:
+	    cfg->cfg.thread_cnt = my_atoi(pj_optarg);
+	    if (cfg->cfg.thread_cnt > 128) {
+		PJ_LOG(1,(THIS_FILE,
+			  "Error: invalid --thread-cnt option"));
+		return -1;
+	    }
+	    break;
+
 	case OPT_PTIME:
 	    cfg->media_cfg.ptime = my_atoi(pj_optarg);
 	    if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
diff --git a/pjsip-apps/src/samples/pjsip-perf.c b/pjsip-apps/src/samples/pjsip-perf.c
index 6c1f729..791c1c8 100644
--- a/pjsip-apps/src/samples/pjsip-perf.c
+++ b/pjsip-apps/src/samples/pjsip-perf.c
@@ -158,6 +158,9 @@
     } client;
 
     struct {
+	pj_bool_t send_trying;
+	pj_bool_t send_ringing;
+	unsigned delay;
 	struct srv_state prev_state;
 	struct srv_state cur_state;
     } server;
@@ -168,7 +171,7 @@
 struct call
 {
     pjsip_inv_session	*inv;
-    pj_timer_entry	 timer;
+    pj_timer_entry	 ans_timer;
 };
 
 
@@ -343,6 +346,60 @@
 };
 
 
+static pj_status_t send_response(pjsip_inv_session *inv, 
+				 pjsip_rx_data *rdata,
+				 int code,
+				 pj_bool_t *has_initial)
+{
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    if (*has_initial) {
+	status = pjsip_inv_answer(inv, code, NULL, NULL, &tdata);
+    } else {
+	status = pjsip_inv_initial_answer(inv, rdata, code, 
+					  NULL, NULL, &tdata);
+    }
+
+    if (status != PJ_SUCCESS) {
+	if (*has_initial) {
+	    status = pjsip_inv_answer(inv, PJSIP_SC_NOT_ACCEPTABLE, 
+				      NULL, NULL, &tdata);
+	} else {
+	    status = pjsip_inv_initial_answer(inv, rdata, 
+					      PJSIP_SC_NOT_ACCEPTABLE,
+					      NULL, NULL, &tdata);
+	}
+
+	if (status == PJ_SUCCESS) {
+	    *has_initial = PJ_TRUE;
+	    pjsip_inv_send_msg(inv, tdata); 
+	} else {
+	    pjsip_inv_terminate(inv, 500, PJ_FALSE);
+	    return -1;
+	}
+    } else {
+	*has_initial = PJ_TRUE;
+
+	status = pjsip_inv_send_msg(inv, tdata); 
+	if (status != PJ_SUCCESS) {
+	    pjsip_tx_data_dec_ref(tdata);
+	    return status;
+	}
+    }
+
+    return status;
+}
+
+static void answer_timer_cb(pj_timer_heap_t *h, pj_timer_entry *entry)
+{
+    struct call *call = entry->user_data;
+    pj_bool_t has_initial = PJ_TRUE;
+
+    entry->id = 0;
+    send_response(call->inv, NULL, 200, &has_initial);
+}
+
 static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata)
 {
     const pj_str_t call_user = { "2", 1 };
@@ -352,6 +409,7 @@
     pjsip_dialog *dlg;
     pjmedia_sdp_session *sdp;
     pjsip_tx_data *tdata;
+    pj_bool_t has_initial = PJ_FALSE;
     pj_status_t status;
 
     uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
@@ -444,26 +502,39 @@
 	return PJ_TRUE;
     }
     
-
-    /* Create 200 response .*/
-    status = pjsip_inv_initial_answer(call->inv, rdata, 200, 
-				      NULL, NULL, &tdata);
-    if (status != PJ_SUCCESS) {
-	status = pjsip_inv_initial_answer(call->inv, rdata, 
-					  PJSIP_SC_NOT_ACCEPTABLE,
-					  NULL, NULL, &tdata);
-	if (status == PJ_SUCCESS)
-	    pjsip_inv_send_msg(call->inv, tdata); 
-	else
-	    pjsip_inv_terminate(call->inv, 500, PJ_FALSE);
-	return PJ_TRUE;
+    /* Send 100/Trying if needed */
+    if (app.server.send_trying) {
+	status = send_response(call->inv, rdata, 100, &has_initial);
+	if (status != PJ_SUCCESS)
+	    return PJ_TRUE;
     }
 
+    /* Send 180/Ringing if needed */
+    if (app.server.send_ringing) {
+	status = send_response(call->inv, rdata, 180, &has_initial);
+	if (status != PJ_SUCCESS)
+	    return PJ_TRUE;
+    }
 
-    /* Send the 200 response. */  
-    status = pjsip_inv_send_msg(call->inv, tdata); 
-    PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return PJ_TRUE);
+    /* Simulate call processing delay */
+    if (app.server.delay) {
+	pj_time_val delay;
 
+	call->ans_timer.id = 1;
+	call->ans_timer.user_data = call;
+	call->ans_timer.cb = &answer_timer_cb;
+	
+	delay.sec = 0;
+	delay.msec = app.server.delay;
+	pj_time_val_normalize(&delay);
+
+	pjsip_endpt_schedule_timer(app.sip_endpt, &call->ans_timer, &delay);
+
+    } else {
+	/* Send the 200 response immediately . */  
+	status = pjsip_inv_send_msg(call->inv, tdata); 
+	PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return PJ_TRUE);
+    }
 
     /* Done */
     app.server.cur_state.call_cnt++;
@@ -1062,6 +1133,9 @@
 	"                           client, you must add ;transport=tcp parameter to URL\n"
 	"                           [default: no]\n"
 	"   --thread-count=N        Set number of worker threads [default=1]\n"
+	"   --trying                Send 100/Trying response (server, default no)\n"
+	"   --ringing               Send 180/Ringing response (server, default no)\n"
+	"   --delay=MS, -d          Delay answering call by MS (server, default no)\n"
 	"\n"
 	"Misc options:\n"
 	"   --help, -h              Display this screen\n"
@@ -1084,7 +1158,7 @@
 
 static pj_status_t init_options(int argc, char *argv[])
 {
-    enum { OPT_THREAD_COUNT = 1, OPT_REAL_SDP };
+    enum { OPT_THREAD_COUNT = 1, OPT_REAL_SDP, OPT_TRYING, OPT_RINGING };
     struct pj_getopt_option long_options[] = {
 	{ "local-port",	    1, 0, 'p' },
 	{ "count",	    1, 0, 'c' },
@@ -1097,6 +1171,9 @@
 	{ "verbose",        0, 0, 'v' },
 	{ "use-tcp",	    0, 0, 'T' },
 	{ "window",	    1, 0, 'w' },
+	{ "delay",	    1, 0, 'd' },
+	{ "trying",	    0, 0, OPT_TRYING},
+	{ "ringing",	    0, 0, OPT_RINGING},
 	{ NULL, 0, 0, 0 },
     };
     int c;
@@ -1114,7 +1191,7 @@
 
     /* Parse options */
     pj_optind = 0;
-    while((c=pj_getopt_long(argc,argv, "p:c:m:t:w:hsv", 
+    while((c=pj_getopt_long(argc,argv, "p:c:m:t:w:d:hsv", 
 			    long_options, &option_index))!=-1) 
     {
 	switch (c) {
@@ -1190,6 +1267,23 @@
 	    app.use_tcp = PJ_TRUE;
 	    break;
 
+	case 'd':
+	    app.server.delay = my_atoi(pj_optarg);
+	    if (app.server.delay > 3600) {
+		PJ_LOG(3,(THIS_FILE, "I think --delay %s is too long", 
+			  pj_optarg));
+		return -1;
+	    }
+	    break;
+
+	case OPT_TRYING:
+	    app.server.send_trying = 1;
+	    break;
+
+	case OPT_RINGING:
+	    app.server.send_ringing = 1;
+	    break;
+
 	default:
 	    PJ_LOG(1,(THIS_FILE, 
 		      "Invalid argument. Use --help to see help"));
diff --git a/pjsip-apps/src/samples/siprtp.c b/pjsip-apps/src/samples/siprtp.c
index 0da627b..c11b5cb 100644
--- a/pjsip-apps/src/samples/siprtp.c
+++ b/pjsip-apps/src/samples/siprtp.c
@@ -1061,7 +1061,6 @@
 static void boost_priority(void)
 {
 #define POLICY	SCHED_FIFO
-    pthread_t thread;
     struct sched_param tp;
     int max_prio;
     int policy;
diff --git a/pjsip-apps/src/samples/sndinfo.c b/pjsip-apps/src/samples/sndinfo.c
index 5ddc67a..e15a12c 100644
--- a/pjsip-apps/src/samples/sndinfo.c
+++ b/pjsip-apps/src/samples/sndinfo.c
@@ -117,7 +117,7 @@
 }
 
 static pj_status_t rec_cb(void *user_data, pj_uint32_t timestamp,
-			  const void *input, unsigned size)
+			  void *input, unsigned size)
 {
 
     PJ_UNUSED_ARG(size);
diff --git a/pjsip-apps/src/samples/sndtest.c b/pjsip-apps/src/samples/sndtest.c
index fc05d85..b2e8fe6 100644
--- a/pjsip-apps/src/samples/sndtest.c
+++ b/pjsip-apps/src/samples/sndtest.c
@@ -198,7 +198,7 @@
 }
 
 static pj_status_t rec_cb(void *user_data, pj_uint32_t timestamp,
-			  const void *input, unsigned size)
+			  void *input, unsigned size)
 {
 
     struct test_data *test_data = user_data;
diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
index e35f0ee..3294744 100644
--- a/pjsip/src/pjsip-ua/sip_inv.c
+++ b/pjsip/src/pjsip-ua/sip_inv.c
@@ -256,10 +256,18 @@
      */
     if (method->id == PJSIP_ACK_METHOD && inv) {
 
+	/* Ignore ACK if pending INVITE transaction has not finished. */
+	if (inv->invite_tsx && 
+	    inv->invite_tsx->state < PJSIP_TSX_STATE_COMPLETED)
+	{
+	    return PJ_TRUE;
+	}
+
 	/* Terminate INVITE transaction, if it's still present. */
 	if (inv->invite_tsx && 
 	    inv->invite_tsx->state <= PJSIP_TSX_STATE_COMPLETED)
 	{
+	    pj_assert(inv->invite_tsx->status_code >= 200);
 	    pjsip_tsx_terminate(inv->invite_tsx, 
 				inv->invite_tsx->status_code);
 	    inv->invite_tsx = NULL;
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index b7a23eb..211749f 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -1009,6 +1009,9 @@
 {
     pjsip_tsx_state_e prev_state = tsx->state;
 
+    /* New state must be greater than previous state */
+    pj_assert(state >= tsx->state);
+
     PJ_LOG(5, (tsx->obj_name, "State changed from %s to %s, event=%s",
 	       state_str[tsx->state], state_str[state], 
                pjsip_event_str(event_src_type)));
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index faafe2b..9c011b8 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -552,6 +552,10 @@
 	    if (status != PJ_SUCCESS)
 		goto on_error;
 	}
+	PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created", 
+		  pjsua_var.ua_cfg.thread_cnt));
+    } else {
+	PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
     }
 
     /* Done! */