RE: wap client with SAR

Piotr Galecki <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <2C515BE8694C6F4B9B6A578BCAC32E2F025097C2@MBX021-W3-CA-1.exch021.domain.local>
Hi Stipe,

Attached is the latest patch for fakewap with additional functionality:
 - parse WSP message and save only the received payload to the output file
 - support for connectionless get/post
 - robustness fixes for Post (resend group segments if no ack), packet loss simulation

Usage:
[root@contentsim11-UE kannel]# ./fakewap 
2013-03-20 18:30:43 [11297] [0] PANIC: fakewap version 1.6
Usage: fakewap [options] url ...

where options are:

-h		help
-g hostname	hostname or IP number of gateway (default: localhost)
-p port		port number of gateway (default: 9201)
-m max		maximum number of requests fakewap will make (default: 1)
-i interval	interval between requests (default: 1.0 seconds)
-c threads	number of concurrent clients simulated (default: 1)
-V protoversion	protocol version field, as an integer (default: 0)
-T pdu-type	PDU type, as an integer (default: 1)
-t tcl		transaction class, as an integer (default: 2)
-n		set tid_new flag in packets, forces gateway to flush cache
                (default: off)
-s              test separation, by concatenating ack and disconnect pdus
                (default: off)
-d difference	difference between successive tid numbers (default: 1)
-F		Accept failure and continue rather than exiting
-A agent        user agent
-C content-type Specify content type: text, mms
-D level	debug level (0=none(default), 1=brief, 2=verbose
-I addr[:port]  Specify source address
-M mode         Transaction mode: 0=connectionless, 1=connection-oriented
-P in-file	Post data from file
-w out-file	Write received data to file
-l loss-precent Simulate packet loss

The urls are fetched in random order.

I have verified the following:

#1 Connectionless GET
./fakewap -g 10.32.35.61 -D 1 -M 0 http://10.48.1.231/sqa/file_1k.txt
2013-03-20 18:24:49 [11290] [0] ERROR: Error while gw_gethostbyname occurs.
2013-03-20 18:24:49 [11290] [0] ERROR: System error 2: No such file or directory
2013-03-20 18:24:49 [11290] [0] ERROR: Can't find out official hostname for this host, using `contentsim11-UE' instead.
2013-03-20 18:24:49 [11290] [0] INFO: fakewap: starting
2013-03-20 18:24:49 [11290] [0] DEBUG: Sent WSP_GET packet
2013-03-20 18:24:50 [11290] [0] INFO: fakewap: Received WSP Reply with status code 200OK
2013-03-20 18:24:50 [11290] [0] INFO: fakewap: finished session # 1
2013-03-20 18:24:50 [11290] [0] INFO: fakewap: complete.
2013-03-20 18:24:50 [11290] [0] INFO: fakewap: 1 client threads made total 1 transactions.
2013-03-20 18:24:50 [11290] [0] INFO: fakewap: total running time 1.0 seconds
2013-03-20 18:24:50 [11290] [0] INFO: fakewap: 1.0 messages/seconds on average
2013-03-20 18:24:50 [11290] [0] INFO: fakewap: time of best, worst and average transaction: 0.6 s, 0.6 s, 0.6 s
[root@contentsim11-UE kannel]#

#2 Connection-oriented GET
[root@contentsim11-UE kannel]# ./fakewap -g 10.32.35.61 -D 1 -M 1 http://10.48.1.231/sqa/file_2k.txt
2013-03-20 18:28:20 [11293] [0] ERROR: Error while gw_gethostbyname occurs.
2013-03-20 18:28:20 [11293] [0] ERROR: System error 2: No such file or directory
2013-03-20 18:28:20 [11293] [0] ERROR: Can't find out official hostname for this host, using `contentsim11-UE' instead.
2013-03-20 18:28:20 [11293] [0] INFO: fakewap: starting
2013-03-20 18:28:20 [11293] [0] DEBUG: Sent WSP_CONNECT packet
2013-03-20 18:28:21 [11293] [0] DEBUG: Received WSP_ConnectReply, SessID 14
2013-03-20 18:28:21 [11293] [0] DEBUG: Sent WTP_ACK packet
2013-03-20 18:28:21 [11293] [0] DEBUG: Sent WSP_GET packet
2013-03-20 18:28:21 [11293] [0] DEBUG: Received WTP_RESULT pdu, gtr 0, ttr 0, payload len 1380
2013-03-20 18:28:21 [11293] [0] DEBUG: Received WTP_SEGM_RESULT pdu, psn 1, gtr 0, ttr 1, payload len 703
2013-03-20 18:28:21 [11293] [0] DEBUG: Sending WTP_ACK pdu, gtr_psn 1
2013-03-20 18:28:21 [11293] [0] DEBUG: Sent WTP_ACK packet
2013-03-20 18:28:21 [11293] [0] INFO: fakewap: Received WSP Reply with status code 200OK
2013-03-20 18:28:21 [11293] [0] DEBUG: Sent WSP_DISCONNECT packet
2013-03-20 18:28:21 [11293] [0] INFO: fakewap: finished session # 1
2013-03-20 18:28:21 [11293] [0] INFO: fakewap: complete.
2013-03-20 18:28:21 [11293] [0] INFO: fakewap: 1 client threads made total 1 transactions.
2013-03-20 18:28:21 [11293] [0] INFO: fakewap: total running time 1.0 seconds
2013-03-20 18:28:21 [11293] [0] INFO: fakewap: 1.0 messages/seconds on average
2013-03-20 18:28:21 [11293] [0] INFO: fakewap: time of best, worst and average transaction: 0.2 s, 0.2 s, 0.2 s
[root@contentsim11-UE kannel]#

#3 Connection-oriented POST of MMS message via WAPGW to MMSC

[root@contentsim11-UE kannel]# ./fakewap -g 10.32.35.61 -P /root/mms/Picture1.mms -C mms -w /tmp/mms-confirm.txt -D 0 -M 1 http://10.6.1.89/usr1=passwd
2013-03-20 18:35:02 [11308] [0] ERROR: Error while gw_gethostbyname occurs.
2013-03-20 18:35:02 [11308] [0] ERROR: System error 2: No such file or directory
2013-03-20 18:35:02 [11308] [0] ERROR: Can't find out official hostname for this host, using `contentsim11-UE' instead.
2013-03-20 18:35:02 [11308] [0] INFO: fakewap: starting
2013-03-20 18:35:02 [11308] [0] INFO: fakewap: Received WSP Reply with status code 200OK
2013-03-20 18:35:02 [11308] [0] INFO: fakewap: complete.
2013-03-20 18:35:02 [11308] [0] INFO: fakewap: 1 client threads made total 1 transactions.
2013-03-20 18:35:02 [11308] [0] INFO: fakewap: total running time 0.0 seconds
2013-03-20 18:35:02 [11308] [0] INFO: fakewap: inf messages/seconds on average
2013-03-20 18:35:02 [11308] [0] INFO: fakewap: time of best, worst and average transaction: 0.6 s, 0.6 s, 0.6 s
[root@contentsim11-UE kannel]#

#4 Connectionless POSTs should also work fine

Could you integrate with the kannel codebase?

Thank you,
Piotr

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Piotr Galecki
Sent: Monday, March 04, 2013 11:32 PM
To: Stipe Tolj
Cc: [email protected]
Subject: RE: wap client with SAR

Hi Stipe,

Here is the patch with my latest changes:
 - added WSP GET result reassembly
 - added WSP POST - options -P, -C, -w
 - added source address (-I) option
 - send Nack for lost segments, reassembly fixes

Let me know your comments.

There are a few more changes coming:
- wtp segm invoke retry if wtp ack is not received
- do not write wsp header to the output file

Thanks,
Piotr

-----Original Message-----
From: Stipe Tolj [mailto:[email protected]]
Sent: Monday, March 04, 2013 5:54 PM
Cc: Piotr Galecki
Subject: Re: wap client with SAR

Am 09.01.2013 15:57, schrieb Stipe Tolj:
>
> thanks a lot for contributing to the Kannel WAP 1 stack and the SAR 
> part. We're definitely interested.
>
> Kindly please provide a patchset in 'diff -u' against current SVN 
> trunk to review and commit.
>
> It would be great if we can move the discussion to the devel@ list, 
> which is more suited for this. Kindly subscribe and send the patchset 
> to the devel@ list.

Hi Piotr,

I haven't seen any update here from your side. Kindly, please let us know if you can still provide the patch for the WAP client SAR support?

Kind Regards,
Stipe

--
-------------------------------------------------------------------
Kölner Landstrasse 419
40589 Düsseldorf, NRW, Germany

tolj.org system architecture      Kannel Software Foundation (KSF)
http://www.tolj.org/              http://www.kannel.org/

mailto:st_{at}_tolj.org           mailto:stolj_{at}_kannel.org
-------------------------------------------------------------------
fakewap.patch (application/octet-stream, 44.9 KB)
Index: gwlib/socket.c
===================================================================
--- gwlib/socket.c	(revision 5031)
+++ gwlib/socket.c	(working copy)
@@ -581,6 +581,12 @@
 
 int udp_recvfrom(int s, Octstr **datagram, Octstr **addr)
 {
+    return udp_recvfrom_flags(s, datagram, addr, 0);
+}
+
+
+int udp_recvfrom_flags(int s, Octstr **datagram, Octstr **addr, int sockrcvflags)
+{
     struct sockaddr_in sa;
     socklen_t salen;
     char *buf;
@@ -589,7 +595,7 @@
     buf = gw_malloc(UDP_PACKET_MAX_SIZE);
 
     salen = sizeof(sa);
-    bytes = recvfrom(s, buf, UDP_PACKET_MAX_SIZE, 0, (struct sockaddr *) &sa, &salen);
+    bytes = recvfrom(s, buf, UDP_PACKET_MAX_SIZE, sockrcvflags, (struct sockaddr *) &sa, &salen);
     if (bytes == -1) {
         if (errno != EAGAIN)
             error(errno, "Couldn't receive UDP packet");
Index: gwlib/socket.h
===================================================================
--- gwlib/socket.h	(revision 5031)
+++ gwlib/socket.h	(working copy)
@@ -165,6 +165,7 @@
 /*
  * Receive a UDP message from a client.
  */
+int udp_recvfrom_flags(int s, Octstr **datagram, Octstr **addr, int sockrcvflags);
 int udp_recvfrom(int s, Octstr **datagram, Octstr **addr);
 
 
Index: test/fakewap.c
===================================================================
--- test/fakewap.c	(revision 5031)
+++ test/fakewap.c	(working copy)
@@ -1,7 +1,7 @@
 /* ==================================================================== 
  * The Kannel Software License, Version 1.0 
  * 
- * Copyright (c) 2001-2012 Kannel Group  
+ * Copyright (c) 2001-2013 Kannel Group  
  * Copyright (c) 1998-2001 WapIT Ltd.   
  * All rights reserved. 
  * 
@@ -80,7 +80,7 @@
  *    D)    Fakewap -> Gateway
  *
  *        WTP: Invoke PDU
- *        WSP: Get PDU (data: URL)
+ *        WSP: Get/Post PDU (data: URL)
  *
  *    E)    Gateway -> Fakewap
  *
@@ -131,13 +131,22 @@
 
 #define MAX_SEND (0)
 
+/* Versions:
+ * v1.0 - added WSP GET result reassembly
+ * v1.1 - added WSP POST - options -P, -C, -w
+ * v1.2 - added source address (-I) option
+ * v1.3 - send Nack for lost segments, reassembly fixes
+ * v1.4 - parse WSP message and save only the received payload to the output file
+ * v1.5 - support for connectionless get/post
+ * v1.6 - robustness fixes for Post (resend group segments if no ack), packet loss simulation
+ */
 static char usage[] = "\
+fakewap version 1.6\n\
 Usage: fakewap [options] url ...\n\
 \n\
 where options are:\n\
 \n\
 -h		help\n\
--v		verbose\n\
 -g hostname	hostname or IP number of gateway (default: localhost)\n\
 -p port		port number of gateway (default: 9201)\n\
 -m max		maximum number of requests fakewap will make (default: 1)\n\
@@ -152,7 +161,14 @@
                 (default: off)\n\
 -d difference	difference between successive tid numbers (default: 1)\n\
 -F		Accept failure and continue rather than exiting\n\
--w		Write/print received data (experimental)\n\
+-A agent        user agent\n\
+-C content-type Specify content type: text, mms\n\
+-D level	debug level (0=none(default), 1=brief, 2=verbose\n\
+-I addr[:port]  Specify source address\n\
+-M mode         Transaction mode: 0=connectionless, 1=connection-oriented\n\
+-P in-file	Post data from file\n\
+-w out-file	Write received data to file\n\
+-l loss-precent Simulate packet loss\n\
 \n\
 The urls are fetched in random order.\n\
 ";
@@ -173,19 +189,40 @@
 #include <sys/param.h>
 #include <math.h>
 #include <signal.h>
+#include <fcntl.h>
+#include <sys/stat.h>
 
 #include "gwlib/gwlib.h"
+#include "wap/wsp_pdu.h"
 
-#define GET_WTP_PDU_TYPE(hdr)  (hdr[0] >> 3)
-static int get_wtp_pdu_type(Octstr *hdr) {
-	return octstr_get_char(hdr, 0) >> 3;
+#define GET_WTP_PDU_TYPE(hdr)  ((hdr[0] >> 3) & 0x0f)
+static int get_wtp_pdu_type(Octstr *hdr)
+{
+    return (octstr_get_char(hdr, 0) >> 3) & 0x0f;
 }
 
 #define WTP_PDU_INVOKE  1
 #define WTP_PDU_RESULT  2
 #define WTP_PDU_ACK     3
 #define WTP_PDU_ABORT   4
+#define WTP_PDU_SEGM_INVOKE 5
+#define WTP_PDU_SEGM_RESULT 6
+#define WTP_PDU_NACK        7
 
+#define WSP_PDU_CONNECT    1
+#define WSP_PDU_REPLY      4
+#define WSP_PDU_DISCONNECT 5
+#define WSP_PDU_GET        0x40
+#define WSP_PDU_POST       0x60
+
+#define TXN_MODE_CONNECTION_LESS      0
+#define TXN_MODE_CONNECTION_ORIENTED  1
+
+#define SAR_SEGM_SIZE            1200
+#define	SAR_GROUP_LEN            4
+#define SAR_MAX_RETRIES          3
+#define WAP_MSG_RECEIVE_TIMEOUT  10
+
 /*
 **  Common parameters
 */
@@ -195,7 +232,7 @@
 Octstr *hostname = NULL;
 Octstr *gateway_addr = NULL;
 double interval = 1.0;
-unsigned short port = 9201;
+unsigned short port = 0;
 int max_send = 1;
 unsigned short tid_addition = 1;
 Mutex *mutex;
@@ -203,37 +240,56 @@
 int num_sent = 0;
 time_t start_time, end_time;
 double totaltime = 0, besttime = 1000000L,  worsttime = 0;
+int brief; /* enable brief debug */
 int verbose = 0;
 int nofailexit = 0;
-int writedata = 0;
 int test_separation = 0;
+char* infile;
+char* outfile;
+const char* content_type;
+struct sockaddr_in src_addr;
+int transaction_mode;
+int packet_loss; /* packet loss rate 0-99 */
+Octstr *useragent;
 
 /*
  * PDU type, version number and transaction class are supplied by a 
  * command line argument. WSP_Concat is a concatenation of WTP_Ack and 
  * WSP_Disconnect PDUs.
  */
-unsigned char WSP_Connect[] = {0x06, 0x00, 0x00, 0x00, 
-				/* WSP part */
-				0x01, /* PDU type */
-				0x10, /* Version 1.0 */
-				0x00, /* Capability length */
-				0x02, /* Headers length = 2*/
-				/* Capabilities */
-				/* Headers */
-				0x80, 0x80 /* Accept: *\* */
+unsigned char WSP_Connect[] = { /* WSP part */
+                               0x01, /* PDU type */
+                               0x10, /* Version 1.0 */
+                               0x0a, /* Capability length */
+                               0x0d, /* Headers length = 13 */
+                               /* Capabilities */
+                               0x04, 0x80, 0xc0, 0x80, 0x00, /* Client SDU */
+                               0x04, 0x81, 0xc0, 0x80, 0x00, /* Server SDU */
+                               /* Headers */
+                               0x80, 0x80, /* Accept: *\* */
 				};
 unsigned char WSP_ConnectReply[] = {0x16, 0x80, 0x00, 0x02 };
-unsigned char WTP_Ack[] =          {0x18, 0x00, 0x00 };
-unsigned char WTP_TidVe[] =        {0x1C, 0x00, 0x00 };
-unsigned char WTP_Abort[] =        {0x20, 0x00, 0x00, 0x00 };
-unsigned char WSP_Get[] =          {0x0E, 0x00, 0x00, 0x02, 0x40 };
+unsigned char WSP_Get[] =          {0x40};
 /* This used to also expect a content-type of 0x94, but that's too difficult
  * to check now that Kannel does full header encoding. */
 unsigned char WSP_Reply[] =        {0x16, 0x80, 0x00, 0x04, 0x20 };
-unsigned char WSP_Disconnect[] =   {0x0E, 0x00, 0x00, 0x00, 0x05 };
+unsigned char WSP_Disconnect[] =   {0x05};
+unsigned char WSP_Post[] = { /* wsp post */ 0x60 };
 unsigned char WSP_Concat[] = {0x00, 0x03, 0x18, 0x00, 0x00, 0x05, 0x0E, 0x00, 0x00, 0x00, 0x05 };
 
+unsigned char WTP_Ack[] =          {0x18, 0x00, 0x00 };
+unsigned char WTP_TidVe[] =        {0x1C, 0x00, 0x00 };
+unsigned char WTP_Abort[] =        {0x20, 0x00, 0x00, 0x00 };
+unsigned char WTP_Invoke_Cl0[] = { /*wtp invoke,ttr*/ 0x0a, /*tid*/ 0x00, 0x00, /*ack+class0*/ 0x10 };
+unsigned char WTP_Invoke_Cl2[] = { /*wtp invoke,ttr*/ 0x0a, /*tid*/ 0x00, 0x00, /*ack+class2*/ 0x12 };
+unsigned char WTP_Invoke_Cl2MaxGrp[] = { /*wtp invoke*/ 0x0E, /*tid*/ 0x00, 0x00, /*ack/class2:*/ 0x12,
+                                         /*tti max-group: 4200*/ 0x13, 0x04, 0x10, 0x68 };
+unsigned char WTP_Result[] =  {0x10, 0x80, 0x00 };
+unsigned char WTP_Segm_Result[] =  {0x30, 0x80, 0x00 };
+unsigned char WTP_Segm_Ack[] = {0x98, 0x00, 0x00, 0x19, 0x00 };
+unsigned char WTP_Nack[] = { /* wtp nack */ 0x38, /* tid */ 0x00, 0x00, /*num missing*/ 0x00 };
+unsigned char WTP_Segm_Invoke[] = { /* wtp segm invoke*/ 0x28, /* tid */ 0x00, 0x00, /* psn */ 0x00 };
+
 /*
 **  In this case it does not matter what is the byte order
 */
@@ -243,7 +299,7 @@
 	hdr[2] = (char)(tid)
 #define GET_TID( hdr ) (((hdr[1] & 0x7f) << 8) + hdr[2])
 #define CONSTRUCT_EXPECTED_REPLY_HDR( dest, template, tid ) \
-    if (sizeof(dest) < sizeof(template)) panic(0,"buffer overflow.");\
+    if (sizeof(dest) < sizeof(template)) panic(0,"fakewap: buffer overflow.");\
     memcpy( dest, template, sizeof(template));\
     SET_TID( dest, tid )
 
@@ -287,14 +343,14 @@
     }
 }
 /*
-**  if -w option has been defined, function prints the trace message and
-**  the first bytes in the message header
+**  function prints the trace message and the first bytes in the message header
 */   
 static void print_data( const char * trace, unsigned char * msg,
                 int msg_len ) {
     int i;
 
-    if (verbose || writedata) {
+    if (verbose)
+    {
         mutex_lock( mutex );
         printf( "%s (len %d): ", trace, msg_len );
         for (i = 0; i < msg_len && i < msg_len; i++)
@@ -357,34 +413,52 @@
     return len;
 }
 
+/*
+**  Retrieve value of WAP/WSP variable length integer in the buffer
+*/
+static int ReadVarIntVal( const unsigned char *buf )
+{
+    int    len = 1;
+    int    value = buf[0] & 0x7F;
+    while (buf[len-1] & 0x80)
+    {
+        len++;
+        value <<= 7;
+        value |= buf[len-1] & 0x7F;
+    }
+    return value;
+}
 
 /*
 **  Function sends message to WAP GW
 */
 static int
-wap_msg_send( int fd, unsigned char * hdr,
-            int hdr_len, unsigned short tid, int tid_new, unsigned char * data,
-            int data_len )
+wap_msg_send( int fd, unsigned char* wtp_hdr, int wtp_hdr_len, unsigned short tid, int tid_new,
+              unsigned char* wsp_hdr, int wsp_hdr_len, unsigned char* data, int data_len )
 {
     int ret;
     Octstr *datagram;
 
     datagram = octstr_create("");
-    if (hdr != NULL)
-    	octstr_append_data(datagram, hdr, hdr_len);
+    if (wtp_hdr != NULL) {
+    	octstr_append_data(datagram, (char*)wtp_hdr, wtp_hdr_len);
 
-    set_tid(datagram, tid);
-    if (get_wtp_pdu_type(datagram) == WTP_PDU_INVOKE) {
-	/* request ack every time */
-	int c;
-	c = octstr_get_char(datagram, 3);
-	octstr_set_char(datagram, 3, c | 0x10);
-	if (tid_new)
+        set_tid(datagram, tid);
+        if (get_wtp_pdu_type(datagram) == WTP_PDU_INVOKE) {
+            /* request ack every time */
+            int c;
+            c = octstr_get_char(datagram, 3);
+            octstr_set_char(datagram, 3, c | 0x10);
+            if (tid_new)
 		set_tid_new(datagram);
+        }
     }
 
+    if (wsp_hdr != NULL)
+        octstr_append_data(datagram, (char*)wsp_hdr, wsp_hdr_len);
+
     if (data != NULL)
-	octstr_append_data(datagram, data, data_len);
+	octstr_append_data(datagram, (char*)data, data_len);
     
 #if 0
     debug("fakewap", 0, "Sending WDP datagram:");
@@ -393,12 +467,61 @@
     ret = udp_sendto(fd, datagram, gateway_addr);
 
     if (ret == -1) {
-        error(0, "Sending to socket failed");
+        error(0, "fakewap: Sending to socket failed");
         return -1;
     }
 
+    if (brief) {
+        if (wsp_hdr!=NULL) {
+            int wsp_pdu;
+            if (transaction_mode==TXN_MODE_CONNECTION_ORIENTED)
+                wsp_pdu = wsp_hdr[0];
+            else
+                wsp_pdu = wsp_hdr[1];
+            switch (wsp_pdu)
+            {
+            case WSP_PDU_CONNECT:
+                debug("fakewap", 0, "Sent WSP_CONNECT packet");
+                break;
+            case WSP_PDU_DISCONNECT:
+                debug("fakewap", 0, "Sent WSP_DISCONNECT packet");
+                break;
+            case WSP_PDU_GET:
+                debug("fakewap", 0, "Sent WSP_GET packet");
+                break;
+            case WSP_PDU_POST:
+                debug("fakewap", 0, "Sent WSP_POST packet");
+                break;
+            default:
+                debug("fakewap", 0, "Sent WSP ??? packet");
+                break;               
+            }
+        }
+        else {
+            switch (get_wtp_pdu_type(datagram))
+            {
+            case WTP_PDU_INVOKE:
+                debug("fakewap", 0, "Sent WTP_INVOKE packet");
+                break;
+            case WTP_PDU_ACK:
+                debug("fakewap", 0, "Sent WTP_ACK packet");
+                break;
+            case WTP_PDU_ABORT:
+                debug("fakewap", 0, "Sent WTP_ABORT packet");
+                break;
+            case WTP_PDU_SEGM_INVOKE:
+                debug("fakewap", 0, "Sent WTP_SEGM_INVOKE packet");
+                break;
+            case WTP_PDU_NACK:
+                debug("fakewap", 0, "Sent WTP_NACK packet");
+                break;
+            default:
+                debug("fakewap", 0, "Sent ??? packet");
+                break;
+            }
+        }
+    }
     if (verbose) {
-	debug("", 0, "Sent packet:");
 	octstr_dump(datagram, 0);
     }
     octstr_destroy(datagram);
@@ -414,9 +537,9 @@
 **      < 0  => error,
 */
 static int
-wap_msg_recv( int fd, const char * hdr, int hdr_len,
+wap_msg_recv( int fd, const unsigned char* hdr, int hdr_len,
               unsigned short tid, unsigned char * data, int data_len,
-              int timeout )
+              int timeout, int udp_flags )
 {
     int ret;
     unsigned char msg[1024*64];
@@ -429,11 +552,14 @@
     */
     for (;;)
     {
+        time_t calltime;
+        time(&calltime);
+
         if (timeout != 0)
         {
 	    ret = read_available(fd, timeout * 1000 * 1000);
 	    if (ret <= 0) {
-                info(0, "Timeout while receiving from socket.\n");
+                info(0, "fakewap: Timeout while receiving from socket.\n");
 		if(nofailexit){
 		    continue;
 		}else{
@@ -443,16 +569,29 @@
 	    }
         }
 	
-	ret = udp_recvfrom(fd, &datagram, &dummy);
+	ret = udp_recvfrom_flags(fd, &datagram, &dummy, udp_flags);
+
+        /* drop packet if packet loss simulation is enabled
+         */
+        if ((packet_loss > 0) && !(udp_flags&MSG_PEEK) && ((gw_rand() % 100) < packet_loss))
+        {
+            time_t currtime;
+            time(&currtime);
+            octstr_destroy(datagram);
+            octstr_destroy(dummy);
+            timeout -= (int)(currtime - calltime);
+            debug("fakewap", 0, "Dropped packet, new timeout %d", timeout);
+            continue;
+        }
 	if (ret == 0) {
-		octstr_get_many_chars(msg, datagram, 0, octstr_len(datagram));
+            octstr_get_many_chars((char*)msg, datagram, 0, octstr_len(datagram));
 		msg_len = octstr_len(datagram);
 	}
 	octstr_destroy(datagram);
 	octstr_destroy(dummy);
 
         if (ret == -1) {
-            error(0, "recv() from socket failed");
+            error(0, "fakewap: recv() from socket failed");
             return -1;
         }
 
@@ -460,8 +599,8 @@
             /*
             **  Ignore extra header bits, WAP GWs return different values
             */
-            if (msg_len >= hdr_len &&
-                GET_WTP_PDU_TYPE(msg) == GET_WTP_PDU_TYPE(hdr) &&
+            if ((msg_len >= hdr_len) &&
+                (GET_WTP_PDU_TYPE(msg) == GET_WTP_PDU_TYPE(hdr)) &&
                 (hdr_len <= 3 || !memcmp( msg+3, hdr+3, hdr_len-3 ))) {
                 break;
             }
@@ -473,8 +612,7 @@
             else if (GET_WTP_PDU_TYPE(msg) == WTP_PDU_ACK &&
                      GET_TID(msg) == tid) {
                 print_msg( "Received tid verification", msg, msg_len );
-                wap_msg_send( fd, WTP_TidVe, sizeof(WTP_TidVe), tid, 0,
-                              NULL, 0 );
+                wap_msg_send( fd, WTP_TidVe, sizeof(WTP_TidVe), tid, 0, NULL, 0, NULL, 0 );
             }
             else if (GET_WTP_PDU_TYPE(msg) == WTP_PDU_ABORT) {
                 print_msg( "Received WTP Abort", msg, msg_len );
@@ -482,6 +620,9 @@
             else if (GET_WTP_PDU_TYPE(msg) == WTP_PDU_RESULT) {
                break;
             }
+            else if (GET_WTP_PDU_TYPE(msg) == WTP_PDU_SEGM_RESULT) {
+               break;
+            }
             else {
                 print_msg( "Received unexpected message", msg, msg_len );
             }
@@ -496,8 +637,8 @@
     print_data( "Received data", msg, msg_len );
 
     if (data != NULL && msg_len > hdr_len) {
-        data_len = min( data_len, msg_len - hdr_len );
-        memcpy( data, msg+hdr_len, data_len);
+        data_len = min( data_len, msg_len );
+        memcpy( data, msg, data_len);
     }
     else  data_len = 0;
     return data_len;
@@ -513,6 +654,220 @@
     return i_this;
 }
 
+int send_post(int fd, unsigned short tid, int tid_new, char* url)
+{
+    unsigned char wtphdr[32];
+    unsigned char msgbuf[32*1024];
+    unsigned char contentlen_buf[8];
+    unsigned char reply_hdr[32];
+    long timeout = WAP_MSG_RECEIVE_TIMEOUT;  /* wap gw is broken if no input */
+
+    /* open post input file */
+    int infd = open(infile, O_RDONLY);
+    if (infd < 0)
+        panic(0, "fakewap: failed to open input file %s, errno %d", infile, errno);
+            
+    /* get size of input file */
+    struct stat fstats;
+    int ret = fstat(infd, &fstats);
+    if (ret)
+        panic(0, "fakewap: failed to get file stats, errno %d", errno);
+    if (fstats.st_size == 0)
+        panic(0, "fakewap: input file is empty");
+    if (((transaction_mode==TXN_MODE_CONNECTION_LESS) && (fstats.st_size > (int)sizeof(msgbuf))) ||
+        ((transaction_mode==TXN_MODE_CONNECTION_ORIENTED) && (fstats.st_size > 256*SAR_SEGM_SIZE))) {
+        panic(0, "fakewap: input file size (%ld) is too large", fstats.st_size);
+    }
+            
+    int nsegs = (fstats.st_size-1) / SAR_SEGM_SIZE;
+    int tpi, gtr, ttr;
+    int psn = 0;
+
+    /* build WTP header */
+    tpi = 1;
+    ttr = (nsegs==0) ? 1 : 0;
+    gtr = (ttr) ? 0 : 1;
+    memcpy(wtphdr, WTP_Invoke_Cl2MaxGrp, sizeof(WTP_Invoke_Cl2MaxGrp));
+    wtphdr[0] = (tpi << 7) | (WTP_PDU_INVOKE << 3) | (gtr << 2) | (ttr << 1);
+
+    /* build WSP POST message */
+    Octstr *postmsg;
+    postmsg = octstr_create("");
+    if (transaction_mode==TXN_MODE_CONNECTION_LESS) {
+        msgbuf[0] = tid;
+        octstr_append_data(postmsg, (char*)msgbuf, 1);
+    }
+    memcpy(msgbuf, WSP_Post, sizeof(WSP_Post));
+    octstr_append_data(postmsg, (char*)msgbuf, sizeof(WSP_Post));
+
+    /* add uri, content type, user agent, content length, accept */
+    int url_len = strlen(url);
+    int off = StoreVarInt( msgbuf, url_len );
+    int content_type_len = strlen(content_type) + 1; /* +1 for eos */
+    int contentlen_len;
+    char acceptAll[] = { 0x80, 0x80 };
+    contentlen_buf[0] = 0x8d; /* hdr type - content-length */
+    if (fstats.st_size <= 127)
+    {
+        contentlen_buf[1] = 0x80 | (char)fstats.st_size;
+        contentlen_len = 2;
+    } else
+    {
+        contentlen_buf[1] = 2;
+        contentlen_buf[2] = (char)(fstats.st_size >> 8);
+        contentlen_buf[3] = (char)(fstats.st_size);
+        contentlen_len = 4;
+    }
+    off += StoreVarInt( &msgbuf[off], content_type_len+octstr_len(useragent)+contentlen_len+sizeof(acceptAll) );
+    memcpy( &msgbuf[off], url, url_len );
+    off += url_len;
+    memcpy( &msgbuf[off], content_type, content_type_len );
+    off += content_type_len;
+    memcpy( &msgbuf[off], octstr_get_cstr(useragent), octstr_len(useragent) );
+    off += octstr_len(useragent);
+    memcpy( &msgbuf[off], contentlen_buf, contentlen_len );
+    off += contentlen_len;
+    memcpy( &msgbuf[off], acceptAll, sizeof(acceptAll) );
+    off += sizeof(acceptAll);
+    octstr_append_data(postmsg, (char*)msgbuf, off);
+    
+    /* add payload */
+    if (transaction_mode==TXN_MODE_CONNECTION_LESS) {
+        ret = read(infd, msgbuf, sizeof(msgbuf));
+    } else {
+        ret = read(infd, msgbuf, SAR_SEGM_SIZE);
+    }
+    if (ret <= 0)
+        panic(0, "fakewap: input file read error, errno %d", errno);
+    
+    debug("fakewap", 0, "Sending WSP_POST, url %s, Content-Type %s, User-Agent %s, Content-Length %lu",
+          url, content_type, octstr_get_cstr(useragent) + 1, fstats.st_size );
+    
+    if (transaction_mode==TXN_MODE_CONNECTION_LESS) {
+        ret = wap_msg_send( fd, NULL, 0, tid, tid_new,
+                            (unsigned char*)octstr_get_cstr(postmsg), octstr_len(postmsg), msgbuf, ret );
+        if (ret == -1)
+        {
+            error(0, "fakewap: failure sending connection-less wtp invoke");
+            goto send_post_exit;
+        }
+    } else {
+        int retry = 0;
+        while (retry++ < SAR_MAX_RETRIES)
+        {
+            ret = wap_msg_send( fd, wtphdr, sizeof(WTP_Invoke_Cl2MaxGrp), tid, tid_new,
+                                (unsigned char*)octstr_get_cstr(postmsg), octstr_len(postmsg), msgbuf, ret );
+            if (ret == -1)
+            {
+                error(0, "fakewap: failure sending connection-oriented wtp invoke");
+                goto send_post_exit;
+            }
+            /* receive invoke ack */
+            CONSTRUCT_EXPECTED_REPLY_HDR( reply_hdr, WTP_Ack, tid );
+            ret = wap_msg_recv( fd, reply_hdr, sizeof(WTP_Ack),
+                                tid, msgbuf, sizeof(msgbuf), timeout, 0 );
+            if (ret < 0)
+            {
+                if (retry>=SAR_MAX_RETRIES) {
+                    error(0, "fakewap: failure receiving WTP Ack, psn %u, giving up", psn);
+                    goto send_post_exit;
+                }
+                warning(0, "fakewap: timeout receiving WTP Ack, resend wtp invoke");
+                continue;
+            }
+            debug("fakewap", 0, "Received WTP_ACK, psn 0");
+            break;
+        }
+    }
+    
+    if (transaction_mode==TXN_MODE_CONNECTION_ORIENTED) {
+        
+        while (!ttr)
+        {
+            int  grppktnum = 0;   /* packet in the group */
+            unsigned char  grpwtphdr[SAR_GROUP_LEN][32];
+            unsigned char  grpmsgbuf[SAR_GROUP_LEN][SAR_SEGM_SIZE];
+            int  grpmsglen[SAR_GROUP_LEN];
+            int  grpsize = 0;
+
+            /* build segments for a new group */
+            gtr = 0;
+            while (!gtr && !ttr)
+            {
+                psn++;
+                
+                /* create wtp header */
+                memcpy(grpwtphdr[grppktnum], WTP_Segm_Invoke, sizeof(WTP_Segm_Invoke));
+                tpi = 0;
+                ttr = (nsegs==psn) ? 1 : 0;
+                gtr = ttr ? 0 : (psn+1)%SAR_GROUP_LEN ? 0 : 1;
+                grpwtphdr[grppktnum][0] = (tpi << 7) | (WTP_PDU_SEGM_INVOKE << 3) | (gtr << 2) | (ttr << 1);
+                grpwtphdr[grppktnum][3] = psn;
+
+                /* read payload from file */
+                grpmsglen[grppktnum] = read(infd, (char*)grpmsgbuf[grppktnum], SAR_SEGM_SIZE);
+                if (grpmsglen[grppktnum] <= 0)
+                    panic(0, "fakewap: input file read error, errno %d", errno);
+
+                grppktnum++;
+                grpsize++;
+            }
+            
+            int retry = 0;
+            while (retry++ < SAR_MAX_RETRIES)
+            {
+                /* send a group of WTP Segmented Invoke messages */
+                for (grppktnum = 0; grppktnum < grpsize; grppktnum++)
+                {
+                    debug("fakewap", 0, "Sending WTP_SEGM_INVOKE, psn %u, payload len %d",
+                          psn-grpsize+grppktnum+1, grpmsglen[grppktnum]);
+                    ret = wap_msg_send( fd, grpwtphdr[grppktnum], sizeof(WTP_Segm_Invoke), tid, tid_new,
+                                        NULL, 0, grpmsgbuf[grppktnum], grpmsglen[grppktnum] );
+                    if (ret == -1)
+                    {
+                        error(0, "fakewap: failure sending wtp invoke");
+                        goto send_post_exit;
+                    }
+                }
+                
+                if (ttr) {
+                    /* receive WTP Result */
+                    CONSTRUCT_EXPECTED_REPLY_HDR( reply_hdr, WTP_Result, tid );
+                    ret = wap_msg_recv( fd, reply_hdr, sizeof(WTP_Result),
+                                        tid, msgbuf, sizeof(msgbuf), timeout, MSG_PEEK );
+                } else {
+                    /* receive segm invoke ack for the group */
+                    CONSTRUCT_EXPECTED_REPLY_HDR( reply_hdr, WTP_Segm_Ack, tid );
+                    reply_hdr[4] = psn;
+                    ret = wap_msg_recv( fd, reply_hdr, sizeof(WTP_Segm_Ack),
+                                        tid, msgbuf, sizeof(msgbuf), timeout, 0 );
+                }
+                if (ret < 0)
+                {
+                    if (retry>=SAR_MAX_RETRIES) {
+                        error(0, "fakewap: failure receiving WTP Segm Ack, psn %u, giving up", psn);
+                        goto send_post_exit;
+                    }
+                    warning(0, "fakewap: failure receiving WTP Segm Ack, psn %u, retrying", psn);
+                    continue;
+                }
+
+                if (ttr) {
+                    debug("fakewap", 0, "Received WTP_RESULT");
+                } else {
+                    debug("fakewap", 0, "Received WTP_ACK, psn %d", psn);
+                }
+                break;
+            } /* while (retry) */
+        } /* while (!ttr) */
+    } /* if (transaction_mode==CONNECTION_ORIENTED) */
+
+send_post_exit:
+    octstr_destroy(postmsg);
+    close(infd);
+    return ret;
+}
+
 /*
 **  Function (or thread) sets up a dgram socket.  Then it loops: WTL/WSP
 **  Connect, Get a url and Disconnect until all requests are have been done.
@@ -531,8 +886,8 @@
     int            sid_len = 0;
     unsigned char  buf[64*1024];
     unsigned char reply_hdr[32];
-    long timeout = 10;  /* wap gw is broken if no input */
-    unsigned short tid = 0;
+    long timeout = WAP_MSG_RECEIVE_TIMEOUT;   /* wap gw is broken if no input */
+    static unsigned short tid = 0;
     unsigned short old_tid;
     int tid_new = 0;
     int connection_retries = 0;
@@ -540,67 +895,75 @@
 
     fd = udp_client_socket();
     if (fd == -1)
-        panic(0, "Couldn't create socket.");
+        panic(0, "fakewap: Couldn't create socket.");
 
+    if (src_addr.sin_addr.s_addr!=INADDR_ANY || src_addr.sin_port!=0)
+        if (bind(fd, (const struct sockaddr *)&src_addr, (int)sizeof(src_addr)) == -1)
+            panic(0, "fakewap: Couldn't bind socket, errno %d", errno);
+
     /*
     **  Loop until all URLs have been requested
     */
     for (;;) {
-		/*
-		 ** Get start time of this request
-		 */
+        /*
+         * Get start time of this request
+         */
     	gettimeofday(&now, &tz);
     	lastsec = (double) now.tv_sec + now.tv_usec / 1e6;
 
         /*
-        **  Get next transaction number or exit if too many transactions
-        */
+         *  Get next transaction number or exit if too many transactions
+         */
         i_this = get_next_transaction();
         if (max_send != MAX_SEND  && i_this > max_send) break;
 
-        /*
-        **  Connect, save sid from reply and finally ack the reply
-        */
-	old_tid = tid;
-        tid = next_tid(old_tid);
-	tid_new = (tid < old_tid);  /* Did we wrap? */
-        ret = wap_msg_send( fd, WSP_Connect, sizeof(WSP_Connect),
-                            tid, tid_new, NULL, 0 );
+        if (transaction_mode==TXN_MODE_CONNECTION_ORIENTED) {
+            /*
+             *  Connect, save sid from reply and finally ack the reply
+             */
+            old_tid = tid;
+            tid = next_tid(old_tid);
+            tid_new = (tid < old_tid);  /* Did we wrap? */
 
-        if (ret == -1) panic(0, "Send WSP_Connect failed");
+            WSP_Connect[3] = 2 + octstr_len(useragent); /* set header length */
+            memcpy( buf, WSP_Connect, sizeof(WSP_Connect));
+            memcpy( &buf[sizeof(WSP_Connect)], octstr_get_cstr(useragent), octstr_len(useragent) );
+            ret = wap_msg_send( fd, WTP_Invoke_Cl2, sizeof(WTP_Invoke_Cl2), tid, tid_new,
+                                buf, sizeof(WSP_Connect)+octstr_len(useragent), NULL, 0 );
+            if (ret == -1) panic(0, "fakewap: Send WSP_Connect failed");
+            
+            CONSTRUCT_EXPECTED_REPLY_HDR( reply_hdr, WSP_ConnectReply, tid );
+            ret = wap_msg_recv( fd, reply_hdr, sizeof(WSP_ConnectReply),
+                                tid, buf, sizeof(buf), timeout, 0 );
+            if (ret == -1) panic(0, "fakewap: Receive WSP_ConnectReply failed");
 
-        CONSTRUCT_EXPECTED_REPLY_HDR( reply_hdr, WSP_ConnectReply, tid );
-        ret = wap_msg_recv( fd, reply_hdr, sizeof(WSP_ConnectReply),
-                            tid, buf, sizeof(buf), timeout );
+            if (ret > 2)
+            {
+                sid_len = ReadVarIntLen(&buf[4]);
+                memcpy( sid, &buf[4], sid_len);
+                debug("fakewap", 0, "Received WSP_ConnectReply, SessID %d", ReadVarIntVal(&buf[4]));
+            }
 
-        if (ret == -1) panic(0, "Receive WSP_ConnectReply failed");
+            /*
+            **  Send abort and continue if we get an unexpected reply
+            */
+            if (ret == 0)  {
+                if (connection_retries++ > 3) {
+                    panic(0, "fakewap: Cannot connect WAP GW!");
+                }
+                wap_msg_send( fd, WTP_Abort, sizeof(WTP_Abort), tid, tid_new, NULL, 0, NULL, 0 );
+                continue;
+            }
+            else {
+                connection_retries = 0;
+            }
+            ret = wap_msg_send( fd, WTP_Ack, sizeof(WTP_Ack), tid, tid_new, NULL, 0, NULL, 0 );
 
-        if (ret > 2)
-        {
-            sid_len = ReadVarIntLen(buf);
-            memcpy( sid, buf, sid_len);
+            if (ret == -1) panic(0, "fakewap: Send WTP_Ack failed");
         }
-        /*
-        **  Send abort and continue if we get an unexpected reply
-        */
-        if (ret == 0)  {
-            if (connection_retries++ > 3) {
-                panic(0, "Cannot connect WAP GW!");
-            }
-            wap_msg_send( fd, WTP_Abort, sizeof(WTP_Abort), tid, tid_new,
-                          NULL, 0 );
-            continue;
-        }
-        else {
-            connection_retries = 0;
-        }
-        ret = wap_msg_send( fd, WTP_Ack, sizeof(WTP_Ack), tid, tid_new,
-                            NULL, 0 );
 
-        if (ret == -1) panic(0, "Send WTP_Ack failed");
-
         /*
-        **  Request WML page with the given URL
+        **  Send WSP Get or Post for a given URL
         */
 	old_tid = tid;
         tid = next_tid(old_tid);
@@ -609,46 +972,190 @@
         url_len = strlen(url);
         url_off = StoreVarInt( buf, url_len );
         memcpy( buf+url_off, url, url_len );
-        ret = wap_msg_send( fd, WSP_Get, sizeof(WSP_Get), tid, tid_new,
-			    buf, url_len+url_off );
-        if (ret == -1) break;
+        buf[url_len+url_off] = 0x80; /* Accept: *\* */
+        buf[url_len+url_off+1] = 0x80; /* Accept: *\* */
 
-        CONSTRUCT_EXPECTED_REPLY_HDR( reply_hdr, WSP_Reply, tid );
-        ret = wap_msg_recv( fd, reply_hdr, sizeof(WSP_Reply),
-                            tid, buf, sizeof(buf), timeout );
-        if (ret == -1) break;
-        /*
-	** If we are testing separation, we concatenate WTP_Ack and 
-        ** WSP_Disconnect messages.
-        */
-        if (test_separation){
-           ret = wap_msg_send(fd, WSP_Concat, sizeof(WSP_Concat), tid, tid_new,
-			     sid, sid_len);
-           
-           if (ret == -1) break;
-        } else {
-           ret = wap_msg_send( fd, WTP_Ack, sizeof(WTP_Ack), tid, tid_new,
-			    NULL, 0 );
+        /* send WSP Post if an input file is specified */
+        if (infile)
+        {
+            ret = send_post(fd, tid, tid_new, url);
+        }
+        else /* send WSP Get */
+        {
+            if (transaction_mode==TXN_MODE_CONNECTION_LESS) {
+                unsigned char wsphdr[32];
+                wsphdr[0] = tid;
+                memcpy(&wsphdr[1], WSP_Get, sizeof(WSP_Get));
+                ret = wap_msg_send( fd, NULL, 0, tid, tid_new,
+                                    wsphdr, sizeof(WSP_Get)+1, buf, url_len+url_off+2 );
+            } else {
+                ret = wap_msg_send( fd, WTP_Invoke_Cl2, sizeof(WTP_Invoke_Cl2), tid, tid_new,
+                                    WSP_Get, sizeof(WSP_Get), buf, url_len+url_off );
+            }
+            if (ret == -1) break;
+        }
 
-           if (ret == -1) break;
+        Octstr* wspreply = octstr_create("");
+        if (transaction_mode==TXN_MODE_CONNECTION_LESS) {
+            /* Connectionless - receive WSP Reply
+             */
+            reply_hdr[0] = tid;
+            ret = wap_msg_recv( fd, reply_hdr, 1,
+                                tid, buf, sizeof(buf), timeout, 0 );
+            if (ret == -1) break;
 
-        /*
-        **  Finally disconnect with the sid returned by connect reply
-        */
-           ret = wap_msg_send( fd, WSP_Disconnect, sizeof(WSP_Disconnect),
-		            tid, tid_new, sid, sid_len );
+            octstr_append_data(wspreply, (char*)&buf[1], ret-1);
+        }
+        else {
+            /* Connection-Oriented - reassemble WSP Reply from WTP segments
+             */
+            int gtr = 0, ttr = 0, psn, gtr_psn = -1, ttr_psn = -1;
+            unsigned char segment_data[256][1500];
+            int segment_len[256];
+            int data_offset;
+            memset(segment_len, 0, sizeof(segment_len));
+            while (!ttr)
+            {
+                CONSTRUCT_EXPECTED_REPLY_HDR( reply_hdr, WTP_Result, tid );
+                ret = wap_msg_recv( fd, reply_hdr, sizeof(WTP_Result),
+                                    tid, buf, sizeof(buf), timeout, 0 );
+                timeout = WAP_MSG_RECEIVE_TIMEOUT;
+                if (ret == -1) break;
 
-           if (ret == -1) break;
+                gtr = (buf[0] & 0x04) ? 1 : 0;
+                ttr = (buf[0] & 0x02) ? 1 : 0;
+                if (GET_WTP_PDU_TYPE(buf) == WTP_PDU_RESULT)
+                {
+                    psn = 0;
+                    data_offset = 3;
+                    debug("fakewap", 0, "Received WTP_RESULT pdu, gtr %d, ttr %d, payload len %d", gtr, ttr, ret - data_offset);
+                }
+                else /* segmented result */
+                {
+                    psn = buf[3];
+                    data_offset = 4;
+                    debug("fakewap", 0, "Received WTP_SEGM_RESULT pdu, psn %d, gtr %d, ttr %d, payload len %d", psn, gtr, ttr, ret - data_offset);
+                }
+                segment_len[psn] = ret - data_offset;
+                if (segment_len[psn] > 1500)
+                    panic(0, "fakewap: Segment %d exceeds 1500 bytes!?!", psn);
+                memcpy(segment_data[psn], &buf[data_offset], segment_len[psn]);
+                
+                if (gtr || ttr || (psn < gtr_psn))
+                {
+                    if (gtr || ttr)
+                        gtr_psn = psn;
+                    if (ttr)
+                        ttr_psn = psn;
+
+                    /* check for lost segments and send NACK */
+                    unsigned char lost_segs[256];
+                    int i, num_lost = 0;
+                    for (i = 0; i <= gtr_psn; i++)
+                        if (!segment_len[i])
+                        lost_segs[num_lost++] = i;
+                    if (num_lost > 0)
+                    {
+                        /* send NACK since some segments got lost */
+                        memcpy(buf, WTP_Nack, sizeof(WTP_Nack));
+                        buf[3] = num_lost;
+                        memcpy(buf+sizeof(WTP_Nack), lost_segs, num_lost);
+                        debug("fakewap", 0, "Sending WTP_NACK pdu, num_lost %d, lost_seg0 %d", num_lost, lost_segs[0]);
+                        ret = wap_msg_send( fd, buf, sizeof(WTP_Nack)+num_lost, tid, tid_new, NULL, 0, NULL, 0 );
+                        ttr = 0;
+                    }
+                    else if (psn > 0)
+                    {
+                        /* send WTP Ack for a group of segments */
+                        WTP_Segm_Ack[4] = gtr_psn;
+                        debug("fakewap", 0, "Sending WTP_ACK pdu, gtr_psn %d", gtr_psn);
+                        ret = wap_msg_send( fd, WTP_Segm_Ack, sizeof(WTP_Segm_Ack), tid, tid_new, NULL, 0, NULL, 0 );
+                    }
+                    else
+                    {
+                        /* send regular WTP Ack for first segment */
+                        debug("fakewap", 0, "Sending WTP_ACK pdu, psn 0");
+                        ret = wap_msg_send( fd, WTP_Ack, sizeof(WTP_Ack), tid, tid_new, NULL, 0, NULL, 0 );
+                    }
+                    if (ret == -1)
+                        break;
+                    if ((ttr_psn >= 0) && (num_lost==0)) {
+                        /* received all segments */
+                        ttr = 1;
+                    }
+                }
+            }
+            if (!ttr) {
+                panic(0, "fakewap: Failed to receive entire message!?!");
+                break;
+            }
+
+            for (psn = 0; psn <= gtr_psn; psn++)
+                octstr_append_data(wspreply, (char*)segment_data[psn], segment_len[psn]);
         }
+            
+        /* validate WSP Reply
+         */
+        WSP_PDU* wsppdu = wsp_pdu_unpack(wspreply);
+        if (!wsppdu)
+            panic(0, "fakewap: Failed to unpack wsp message!?!");
+        if (wsppdu->type != Reply) {
+            error(0, "fakewap: Received WSP message type %u is not Reply!?!", octstr_get_char(wspreply, 0));
+        }
+        else
+        {
+            struct Reply* wspreply = &wsppdu->u.Reply;
+            int status;
+            if (wspreply->status <= 0x4f)
+                status = (wspreply->status >> 4) * 100 + (wspreply->status & 0x0f);
+            else if ((wspreply->status & 0xf0) == 0x50)
+                status = 431 + (wspreply->status & 0x0f);
+            else
+                status = (wspreply->status >> 4) * 100 - 100 + (wspreply->status & 0x0f);
+            if (status != 200) {
+                warning(0, "fakewap: Warning - received reply with status %d", status);
+            } else {
+                info(0, "fakewap: Received WSP Reply with status code 200OK");
+            }
 
-		/*
-		 ** Get end time of the request
-		 */
+            /* if output file arg was specified write the received wsp payload to file */
+            if (outfile)
+            {
+                int outfd = creat(outfile, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+                if (outfd < 0) {
+                    panic(0, "fakewap: failed to create output file %s, errno %d", outfile, errno);
+                }
+
+                ret = write(outfd, octstr_get_cstr(wspreply->data), octstr_len(wspreply->data));
+                if (ret < 0) {
+                    panic(0, "fakewap: failed to write to output file %s, errno %d", outfile, errno);
+                }
+
+                close(outfd);
+                debug("fakewap", 0, "Wrote %d bytes of response payload to output file %s", ret, outfile);
+            }
+        }
+
+        if (transaction_mode==TXN_MODE_CONNECTION_ORIENTED) {
+            /*
+            **  Finally disconnect with the sid returned by connect reply
+            */
+            {
+                ret = wap_msg_send( fd, WTP_Invoke_Cl0, sizeof(WTP_Invoke_Cl0), tid, tid_new,
+                                    WSP_Disconnect, sizeof(WSP_Disconnect), sid, sid_len );
+                
+                if (ret == -1) break;
+            }
+        }
+
+        /*
+        ** Get end time of the request
+        */
         gettimeofday(&now, &tz);
         nowsec = (double) now.tv_sec + now.tv_usec / 1e6;
         tmp = nowsec - lastsec;	/* Duration of request */
-		sleepTime = interval-tmp;	/* Amount of time left to sleep */
-		uSleepTime = sleepTime * 1e6;
+        sleepTime = interval-tmp;	/* Amount of time left to sleep */
+        uSleepTime = sleepTime * 1e6;
 
         mutex_lock( mutex );
         if (tmp < besttime) besttime = tmp;
@@ -656,14 +1163,14 @@
         totaltime += tmp;
         mutex_unlock( mutex );
 
-		if (verbose == 1)
-		{
-			info(0, "fakewap: finished session # %d", i_this);
-		}
+        if (brief == 1)
+        {
+            info(0, "fakewap: finished session # %d", i_this);
+        }
 
-		/*
-		 ** If we've done all the requests, then don't bother to sleep
-		 */
+        /*
+        ** If we've done all the requests, then don't bother to sleep
+        */
         if (i_this >= max_send) break;
 
         if (tmp < (double)interval) {
@@ -680,7 +1187,7 @@
 
 
 static void help(void) {
-	info(0, "\n%s", usage);
+    info(0, "\n%s", usage);
 }
 
 
@@ -698,6 +1205,7 @@
 
     sigaction(SIGALRM,&alrm,NULL);
 #endif
+
     gwlib_init();
 
     proto_version = 0;
@@ -707,7 +1215,40 @@
 
     hostname = octstr_create("localhost");
 
-    while ((opt = getopt(argc, argv, "Fhvc:g:p:P:m:i:t:V:T:t:nsd:w")) != EOF) {
+    /* restart args scanning */
+    optind = 1;
+
+    /* reset globals */
+    interval = 1.0;
+    port = 0;
+    max_send = 1;
+    tid_addition = 1;
+    threads = 1;
+    num_sent = 0;
+    totaltime = 0;
+    besttime = 1000000L;
+    worsttime = 0;
+    brief = 0;
+    verbose = 0;
+    test_separation = 0;
+    infile = NULL;
+    outfile = NULL;
+    content_type = "text/plain";
+    src_addr.sin_family = AF_INET;
+    src_addr.sin_addr.s_addr = INADDR_ANY;
+    src_addr.sin_port = 0;
+    transaction_mode = TXN_MODE_CONNECTION_ORIENTED;
+    packet_loss = 0;
+
+    /* create default user agent header prepend with a9, and end with 0 */
+    const char firstchar[] = {0xa9, 0}; /* code value for user agent header */
+    Octstr* temp = octstr_create("fakewap");
+    useragent = octstr_create(firstchar);
+    octstr_append_data(useragent, octstr_get_cstr(temp), octstr_len(temp) );
+    octstr_append_data(useragent, "\0", 1 );
+
+    while ((opt = getopt(argc, argv, "Fhc:g:p:m:i:t:V:T:t:nsd:A:C:D:I:M:P:w:l:")) != EOF)
+    {
 	switch (opt) {
 	case 'g':
 	    octstr_destroy(hostname);
@@ -754,10 +1295,11 @@
 	    tid_addition = atoi(optarg);
 	    break;
 
-	case 'v':
-	    verbose = 1;
+	case 'D':
+	    brief = (atoi(optarg) >= 1);
+	    verbose = (atoi(optarg) >= 2);
 	    break;
-	    
+
 	case 'h':
 	    help();
 	    exit(0);
@@ -768,14 +1310,63 @@
 	    break;
 
         case 'w':
-            writedata = 1;
+	    outfile = optarg;
             break;
 
+	case 'A':
+	    octstr_destroy(useragent);
+            octstr_destroy(temp);
+            /* create user agent header prepend with a9, and end with 0 */
+	    useragent = octstr_create(firstchar);
+	    temp = octstr_create(optarg);
+    	    octstr_append_data(useragent, octstr_get_cstr(temp), octstr_len(temp) );
+    	    octstr_append_data(useragent, "\0", 1 );
+	    break;
+
+	case 'C':
+            if (!strcmp("mms", optarg))
+                content_type = "application/vnd.wap.mms-message";
+            else
+                content_type = "text/plain";
+	    break;
+
+        case 'I':
+        {
+            unsigned int  byte0, byte1, byte2, byte3, srcport;
+            int ret = sscanf(optarg, "%u.%u.%u.%u:%u", &byte0, &byte1, &byte2, &byte3, &srcport);
+            if (ret!=4 && ret!=5)
+                panic(0, "fakewap: invalid source address %s", optarg);
+            src_addr.sin_addr.s_addr = htonl(byte0<<24 | byte1<<16 | byte2<<8 | byte3);
+            if (ret==5)
+                src_addr.sin_port = htons(srcport);
+            break;
+        }
+
+	case 'M':
+	    transaction_mode = atoi(optarg);
+            if ((transaction_mode!=0) && (transaction_mode!=1)) {
+                error(0, "fakewap: invalid transaction mode %s", optarg);
+                return -1;
+            }
+	    break;
+
+	case 'P':
+	    infile = optarg;
+	    break;
+
+	case 'l':
+	    packet_loss = atoi(optarg);
+            if ((packet_loss < 0) && (packet_loss >=99)) {
+                error(0, "fakewap: invalid packet loss rate %s, expect 0-99", optarg);
+                return -1;
+            }
+	    break;
+
 	case '?':
 	default:
-	    error(0, "Unknown option %c", opt);
+	    error(0, "fakewap: Unknown option %c", opt);
 	    help();
-	    panic(0, "Stopping.");
+	    panic(0, "fakewap: Stopping.");
 	}
     }
 
@@ -784,15 +1375,23 @@
     if (optind >= argc)
         panic(0, "%s", usage);
 
-	if (verbose != 1)
-	{
-		log_set_output_level (GW_INFO);
-	}
-    WSP_Connect[3] += (proto_version&3)<<6;
-    WSP_Connect[0] += (pdu_type&15)<<3;
-    WSP_Connect[3] += tcl&3;
-    WSP_Connect[3] += (tid_new&1)<<5;
-    
+    if ((!brief) && (!verbose))
+    {
+        log_set_output_level (GW_INFO);
+    }
+
+    if (port==0) {
+        if (transaction_mode==TXN_MODE_CONNECTION_LESS)
+            port = 9200;
+        else
+            port = 9201;
+    }
+
+    WTP_Invoke_Cl2[3] |= (proto_version&3)<<6;
+    /* WTP_Invoke_Cl2[0] += (pdu_type&15)<<3; */
+    WTP_Invoke_Cl2[3] |= tcl&3;
+    WTP_Invoke_Cl2[3] |= (tid_new&1)<<5;
+
     gateway_addr = udp_create_address(hostname, port);
 
     urls = argv + optind;
@@ -800,9 +1399,9 @@
 
     srand((unsigned int) time(NULL));
 
-    mutex = mutex_create();
+    mutex = (Mutex*)mutex_create();
 
-    info(0, "fakewap starting");
+    info(0, "fakewap: starting");
 
     if (threads < 1) threads = 1;
 
@@ -817,7 +1416,7 @@
     /* Wait for the other sessions to complete */
     gwthread_join_every(client_session);
 
-    info(0, "fakewap complete.");
+    info(0, "fakewap: complete.");
     info(0, "fakewap: %d client threads made total %d transactions.", 
     	threads, num_sent);
     delta = difftime(end_time, start_time);
@@ -830,6 +1429,7 @@
     octstr_destroy(hostname);
     octstr_destroy(gateway_addr);
     mutex_destroy(mutex);
+
     gwlib_shutdown();
     return 0;
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.