Index: pjproject/trunk/pjlib-util/src/pjstun-client/client_main.c
===================================================================
--- pjproject/trunk/pjlib-util/src/pjstun-client/client_main.c	(revision 1053)
+++ pjproject/trunk/pjlib-util/src/pjstun-client/client_main.c	(revision 1054)
@@ -22,6 +22,7 @@
 
 #define THIS_FILE	"client_main.c"
+#define LOCAL_PORT	1998
 #define BANDWIDTH	64		    /* -1 to disable */
-#define LIFETIME	30		    /* -1 to disable */
+#define LIFETIME	600		    /* -1 to disable */
 #define REQ_TRANSPORT	-1		    /* 0: udp, 1: tcp, -1: disable */
 #define REQ_PORT_PROPS	-1		    /* -1 to disable */
@@ -40,6 +41,8 @@
     pj_bool_t		 quit;
     pj_sockaddr_in	 peer_addr;
-    pj_sockaddr_in	 srv_addr;  /**< server addr */
-
+    pj_sockaddr_in	 srv_addr;
+    pj_sockaddr_in	 relay_addr;
+    char		 data_buf[256];
+    char		*data;
 } g;
 
@@ -52,6 +55,10 @@
     char    *password;
     char    *nonce;
+    char    *peer_addr;
     pj_bool_t use_fingerprint;
 } o;
+
+
+static pj_status_t parse_addr(const char *input, pj_sockaddr_in *addr);
 
 
@@ -88,5 +95,24 @@
 {
     if (status == PJ_SUCCESS) {
-	puts("Client transaction completes");
+	switch (response->hdr.type) {
+	case PJ_STUN_ALLOCATE_RESPONSE:
+	    {
+		pj_stun_relay_addr_attr *ar;
+
+		ar = (pj_stun_relay_addr_attr*)
+		     pj_stun_msg_find_attr(response, 
+					   PJ_STUN_ATTR_RELAY_ADDR, 0);
+		if (ar) {
+		    pj_memcpy(&g.relay_addr, &ar->addr.ipv4,
+			      sizeof(pj_sockaddr_in));
+		    PJ_LOG(3,(THIS_FILE, "Relay address is %s:%d",
+			      pj_inet_ntoa(g.relay_addr.sin_addr),
+			      (int)pj_ntohs(g.relay_addr.sin_port)));
+		} else {
+		    pj_memset(&g.relay_addr, 0, sizeof(g.relay_addr));
+		}
+	    }
+	    break;
+	}
     } else {
 	my_perror("Client transaction error", status);
@@ -120,10 +146,17 @@
 		addrlen = sizeof(addr);
 		rc = pj_sock_recvfrom(g.sock, buffer, &len, 0, &addr, &addrlen);
-		if (rc == PJ_SUCCESS && len > 0) {
+		if (rc != PJ_SUCCESS || len <= 0)
+		    continue;
+
+		if (pj_stun_msg_check(buffer, len, PJ_STUN_IS_DATAGRAM)==PJ_SUCCESS) {
 		    rc = pj_stun_session_on_rx_pkt(g.sess, buffer, len, 
-						   PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET, 
+						   0, 
 						   NULL, &addr, addrlen);
 		    if (rc != PJ_SUCCESS)
 			my_perror("Error processing packet", rc);
+
+		} else {
+		    buffer[len] = '\0';
+		    PJ_LOG(3,(THIS_FILE, "Received data: %s", (char*)buffer));
 		}
 	    }
@@ -139,4 +172,5 @@
     pj_sockaddr_in addr;
     pj_stun_session_cb stun_cb;
+    int len;
     pj_status_t status;
 
@@ -182,4 +216,18 @@
     status = pj_sockaddr_in_init(&addr, NULL, 0);
     pj_assert(status == PJ_SUCCESS);
+
+    addr.sin_port = pj_htons((pj_uint16_t)LOCAL_PORT);
+    status = pj_sock_bind(g.sock, &addr, sizeof(addr));
+    pj_assert(status == PJ_SUCCESS);
+
+    len = sizeof(addr);
+    status = pj_sock_getsockname(g.sock, &addr, &len);
+    pj_assert(status == PJ_SUCCESS);
+
+    PJ_LOG(3,(THIS_FILE, "Listening on port %d", (int)pj_ntohs(addr.sin_port)));
+
+    pj_memcpy(&g.peer_addr, &addr, sizeof(pj_sockaddr_in));
+    if (g.peer_addr.sin_addr.s_addr == 0)
+	pj_gethostip(&g.peer_addr.sin_addr);
 
     pj_memset(&stun_cb, 0, sizeof(stun_cb));
@@ -207,4 +255,9 @@
     } else {
 	puts("Credential not set");
+    }
+
+    if (o.peer_addr) {
+	if (parse_addr(o.peer_addr, &g.peer_addr)!=PJ_SUCCESS)
+	    return -1;
     }
 
@@ -297,5 +350,5 @@
 
 	    pj_sockaddr_in_init(&addr, pj_cstr(&tmp, REQ_IP), 0);
-	    pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg,
+	    pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
 					 PJ_STUN_ATTR_REQ_IP, PJ_FALSE,
 					 &addr, sizeof(addr));
@@ -321,9 +374,9 @@
     }
 
-    rc = pj_stun_session_create_req(g.sess, PJ_STUN_ALLOCATE_REQUEST, &tdata);
+    rc = pj_stun_session_create_req(g.sess, PJ_STUN_SET_ACTIVE_DESTINATION_REQUEST, &tdata);
     pj_assert(rc == PJ_SUCCESS);
 
     if (set) {
-	pj_stun_msg_add_ip_addr_attr(tdata->pool, tdata->msg,
+	pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
 				     PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
 				     &g.peer_addr, sizeof(g.peer_addr));
@@ -338,16 +391,115 @@
 static void send_send_ind(void)
 {
-}
-
-static void send_raw_data(void)
-{
+    pj_stun_tx_data *tdata;
+    int len;
+    pj_status_t rc;
+
+    if (g.peer_addr.sin_addr.s_addr == 0 ||
+	g.peer_addr.sin_port == 0)
+    {
+	puts("Error: peer address is not set");
+	return;
+    }
+
+    len = strlen(g.data);
+    if (len==0) {
+	puts("Error: data is not set");
+	return;
+    }
+
+    rc = pj_stun_session_create_ind(g.sess, PJ_STUN_SEND_INDICATION, &tdata);
+    pj_assert(rc == PJ_SUCCESS);
+
+    pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
+				  PJ_STUN_ATTR_REMOTE_ADDR, PJ_FALSE,
+				  &g.peer_addr, sizeof(g.peer_addr));
+    pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
+				PJ_STUN_ATTR_DATA, g.data, len);
+
+    rc = pj_stun_session_send_msg(g.sess, PJ_FALSE, 
+				  &g.srv_addr, sizeof(g.srv_addr),
+				  tdata);
+    pj_assert(rc == PJ_SUCCESS);
+
+}
+
+static void send_raw_data_to_srv(void)
+{
+    pj_ssize_t len;
+
+    if (g.srv_addr.sin_addr.s_addr == 0 ||
+	g.srv_addr.sin_port == 0)
+    {
+	puts("Error: server address is not set");
+	return;
+    }
+
+    len = strlen(g.data);
+    if (len==0) {
+	puts("Error: data is not set");
+	return;
+    }
+
+    len = strlen(g.data);
+    pj_sock_sendto(g.sock, g.data, &len, 0, &g.srv_addr, sizeof(g.srv_addr));
+}
+
+static void send_raw_data_to_relay(void)
+{
+    pj_ssize_t len;
+
+    if (g.relay_addr.sin_addr.s_addr == 0 ||
+	g.relay_addr.sin_port == 0)
+    {
+	puts("Error: relay address is not set");
+	return;
+    }
+
+    len = strlen(g.data);
+    if (len==0) {
+	puts("Error: data is not set");
+	return;
+    }
+
+    len = strlen(g.data);
+    pj_sock_sendto(g.sock, g.data, &len, 0, &g.relay_addr, sizeof(g.relay_addr));
+}
+
+static pj_status_t parse_addr(const char *input,
+			      pj_sockaddr_in *addr)
+{
+    const char *pos;
+    pj_str_t ip;
+    pj_uint16_t port;
+    pj_sockaddr_in tmp_addr;
+
+    pos = pj_ansi_strchr(input, ':');
+    if (pos==NULL) {
+	puts("Invalid format");
+	return -1;
+    }
+
+    ip.ptr = (char*)input;
+    ip.slen = pos - input;
+    port = (pj_uint16_t)atoi(pos+1);
+
+    if (port==0) {
+	puts("Invalid port");
+	return -1;
+    }
+
+    if (pj_sockaddr_in_init(&tmp_addr, &ip, port)!=PJ_SUCCESS) {
+	puts("Invalid address");
+	return -1;
+    }
+
+    pj_memcpy(addr, &tmp_addr, sizeof(tmp_addr));
+
+    return PJ_SUCCESS;
 }
 
 static void set_peer_addr(void)
 {
-    char ip_addr[64];
-    pj_str_t tmp;
-    pj_sockaddr_in addr;
-    int port;
+    char addr[64];
 
     printf("Current peer address: %s:%d\n", 
@@ -357,16 +509,10 @@
     printf("Input peer address in IP:PORT format: ");
     fflush(stdout);
-
-    if (scanf("%s:%d", ip_addr, &port) != 2) {
-	puts("Error.");
-	return;
-    }
-
-    if (pj_sockaddr_in_init(&addr, pj_cstr(&tmp,ip_addr), (pj_uint16_t)port) != PJ_SUCCESS) {
-	puts("Error: invalid address");
-	return;
-    }
-
-    g.peer_addr = addr;
+    gets(addr);
+
+    if (parse_addr(addr, &g.peer_addr) != PJ_SUCCESS) {
+	return;
+    }
+
 }
 
@@ -376,12 +522,13 @@
     printf("  pr      Set peer address (currently %s:%d)\n",
 	   pj_inet_ntoa(g.peer_addr.sin_addr), pj_ntohs(g.peer_addr.sin_port));
-    puts("");
+    printf("  dt      Set data (currently \"%s\")\n", g.data);
     puts("  br      Send Bind request");
     puts("  ar      Send Allocate request");
     puts("  dr      Send de-Allocate request");
-    puts("  sr      Send Set Active Indication request");
-    puts("  cr      Send clear Active Indication request");
+    puts("  sr      Send Set Active Destination request");
+    puts("  cr      Send clear Active Destination request");
     puts("  si      Send data with Send Indication");
-    puts("  rw      Send raw data");
+    puts("  rw      Send raw data to TURN server");
+    puts("  rW      Send raw data to relay address");
     puts("  q       Quit");
     puts("");
@@ -399,5 +546,14 @@
 	fgets(input, sizeof(input), stdin);
 	
-	if (input[0]=='b' && input[1]=='r') {
+	if (0) {
+
+	} else if (input[0]=='d' && input[1]=='t') {
+	    printf("Input data: ");
+	    gets(g.data);
+	    
+	} else if (input[0]=='p' && input[1]=='r') {
+	    set_peer_addr();
+	    
+	} else if (input[0]=='b' && input[1]=='r') {
 	    send_bind_request();
 	    
@@ -418,8 +574,8 @@
 	    
 	} else if (input[0]=='r' && input[1]=='w') {
-	    send_raw_data();
-	    
-	} else if (input[0]=='p' && input[1]=='r') {
-	    set_peer_addr();
+	    send_raw_data_to_srv();
+	    
+	} else if (input[0]=='r' && input[1]=='W') {
+	    send_raw_data_to_relay();
 	    
 	} else if (input[0]=='q') {
@@ -442,4 +598,6 @@
     puts(" --nonce, -N       Set NONCE");   
     puts(" --fingerprint, -F Use fingerprint for outgoing requests");
+    puts(" --peer, -P        Set peer address (address is in HOST:PORT format)");
+    puts(" --data, -D        Set data");
     puts(" --help, -h");
 }
@@ -453,4 +611,6 @@
 	{ "nonce",	1, 0, 'N'},
 	{ "fingerprint",0, 0, 'F'},
+	{ "peer",	1, 0, 'P'},
+	{ "data",	1, 0, 'D'},
 	{ "help",	0, 0, 'h'}
     };
@@ -459,4 +619,6 @@
     pj_status_t status;
 
+    g.data = g.data_buf;
+
     while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) {
 	switch (c) {
@@ -479,4 +641,11 @@
 	    o.use_fingerprint = PJ_TRUE;
 	    break;
+	case 'P':
+	    o.peer_addr = pj_optarg;
+	    break;
+	case 'D':
+	    g.data = pj_optarg;
+	    break;
+
 	default:
 	    printf("Argument \"%s\" is not valid. Use -h to see help",
