RE: wap client with SAR

Piotr Galecki <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <2C515BE8694C6F4B9B6A578BCAC32E2F024E964C@MBX021-W3-CA-1.exch021.domain.local>
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_sar_post.patch (application/octet-stream, 23.2 KB)
Index: test/fakewap.c
===================================================================
--- test/fakewap.c	(revision 5026)
+++ test/fakewap.c	(working copy)
@@ -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,7 +131,13 @@
 
 #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
 static char usage[] = "\
+fakewap version 1.3\n\
 Usage: fakewap [options] url ...\n\
 \n\
 where options are:\n\
@@ -152,7 +158,10 @@
                 (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\
+-w out-file	Write received data to file\n\
+-P in-file	Post data from file\n\
+-C content-type Specify content type: text, mms\n\
+-I addr[:port]  Specify source address\n\
 \n\
 The urls are fetched in random order.\n\
 ";
@@ -174,17 +183,23 @@
 #include <math.h>
 #include <signal.h>
 
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #include "gwlib/gwlib.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
 
 /*
 **  Common parameters
@@ -207,6 +222,10 @@
 int nofailexit = 0;
 int writedata = 0;
 int test_separation = 0;
+char* infile = NULL;
+char* outfile = NULL;
+const char* content_type;
+struct sockaddr_in src_addr;
 
 /*
  * PDU type, version number and transaction class are supplied by a 
@@ -214,14 +233,17 @@
  * 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: *\* */
+                               /* 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: *\* */
+                               0xa7, 'f', 'a', 'k', 'e', 'w', 'a', 'p', 0, /* User-Agent: fakewap */
 				};
 unsigned char WSP_ConnectReply[] = {0x16, 0x80, 0x00, 0x02 };
 unsigned char WTP_Ack[] =          {0x18, 0x00, 0x00 };
@@ -233,6 +255,13 @@
 unsigned char WSP_Reply[] =        {0x16, 0x80, 0x00, 0x04, 0x20 };
 unsigned char WSP_Disconnect[] =   {0x0E, 0x00, 0x00, 0x00, 0x05 };
 unsigned char WSP_Concat[] = {0x00, 0x03, 0x18, 0x00, 0x00, 0x05, 0x0E, 0x00, 0x00, 0x00, 0x05 };
+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 WSP_Post[] = { /* wtp invoke */ 0x0E, /* tid */ 0x00, 0x00, /* ack/class2: */ 0x12,
+                             /* tti max-group: 4200 */ 0x13, 0x04, 0x10, 0x68, /* wsp post */ 0x60 };
+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 +272,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 )
 
@@ -294,7 +323,8 @@
                 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,6 +387,21 @@
     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
@@ -371,7 +416,7 @@
 
     datagram = octstr_create("");
     if (hdr != NULL)
-    	octstr_append_data(datagram, hdr, hdr_len);
+    	octstr_append_data(datagram, (char*)hdr, hdr_len);
 
     set_tid(datagram, tid);
     if (get_wtp_pdu_type(datagram) == WTP_PDU_INVOKE) {
@@ -384,7 +429,7 @@
     }
 
     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 +438,26 @@
     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 (verbose) {
-	debug("", 0, "Sent packet:");
+        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;
+        default:
+            debug("fakewap", 0, "Sent ??? packet");
+            break;
+        }
 	octstr_dump(datagram, 0);
     }
     octstr_destroy(datagram);
@@ -433,7 +492,7 @@
         {
 	    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{
@@ -452,7 +511,7 @@
 	octstr_destroy(dummy);
 
         if (ret == -1) {
-            error(0, "recv() from socket failed");
+            error(0, "fakewap: recv() from socket failed");
             return -1;
         }
 
@@ -461,7 +520,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) &&
+                (GET_WTP_PDU_TYPE(msg) == GET_WTP_PDU_TYPE(hdr) ||
+                 (GET_WTP_PDU_TYPE(hdr)==WTP_PDU_RESULT && GET_WTP_PDU_TYPE(msg)==WTP_PDU_SEGM_RESULT)) &&
                 (hdr_len <= 3 || !memcmp( msg+3, hdr+3, hdr_len-3 ))) {
                 break;
             }
@@ -482,6 +542,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 +559,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 +576,159 @@
     return i_this;
 }
 
+int send_post(int fd, unsigned short tid, int tid_new, char* url)
+{
+#define SAR_SEGM_SIZE   1200
+#define	SAR_GROUP_LEN   4
+    char msgbuf[1500];
+    char useragent[] = { 0xa9, 'a', 'n', 'f', 'a', 'k', 'e', 'w', 'a', 'p', 0 };
+    char contentlen_buf[8];
+    unsigned char reply_hdr[32];
+    long timeout = 10;  /* 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 (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 initial WSP POST message
+    Octstr *postmsg;
+    postmsg = octstr_create("");
+
+    memcpy(msgbuf, WSP_Post, sizeof(WSP_Post));
+    tpi = 1;
+    ttr = (nsegs==0) ? 1 : 0;
+    gtr = (ttr) ? 0 : 1;
+    msgbuf[0] = (tpi << 7) | (WTP_PDU_INVOKE << 3) | (gtr << 2) | (ttr << 1);
+    octstr_append_data(postmsg, msgbuf, sizeof(WSP_Post));
+
+    // add uri, content type, user agent, content length
+    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;
+    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+sizeof(useragent)+contentlen_len );
+    memcpy( &msgbuf[off], url, url_len );
+    off += url_len;
+    memcpy( &msgbuf[off], content_type, content_type_len );
+    off += content_type_len;
+    memcpy( &msgbuf[off], useragent, sizeof(useragent) );
+    off += sizeof(useragent);
+    memcpy( &msgbuf[off], contentlen_buf, contentlen_len );
+    off += contentlen_len;
+    octstr_append_data(postmsg, msgbuf, off);
+    
+    // add payload
+    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, &useragent[1], fstats.st_size );
+    
+    ret = wap_msg_send( fd, octstr_get_cstr(postmsg), octstr_len(postmsg),
+                        tid, tid_new, msgbuf, ret );
+    if (ret == -1)
+    {
+        error(0, "fakewap: failure sending 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 );
+    if (ret == -1)
+    {
+        error(0, "fakewap: failure receiving wtp ack");
+        goto send_post_exit;
+    }
+    debug("fakewap", 0, "Received WTP_ACK, psn 0");
+    
+    while (!ttr)
+    {
+        psn++;
+
+        // create message with WTP Segmented Invoke headers
+        Octstr *segm_msg;
+        segm_msg = octstr_create("");
+        memcpy(msgbuf, WTP_Segm_Invoke, sizeof(WTP_Segm_Invoke));
+        tpi = 0;
+        ttr = (nsegs==psn) ? 1 : 0;
+        gtr = ttr ? 0 : (psn+1)%SAR_GROUP_LEN ? 0 : 1;
+        msgbuf[0] = (tpi << 7) | (WTP_PDU_SEGM_INVOKE << 3) | (gtr << 2) | (ttr << 1);
+        msgbuf[3] = psn;
+        octstr_append_data(segm_msg, msgbuf, sizeof(WTP_Segm_Invoke));
+
+        // read payload from file
+        int payload_len = read(infd, msgbuf, SAR_SEGM_SIZE);
+        if (payload_len <= 0)
+            panic(0, "fakewap: input file read error, errno %d", errno);
+
+        // send WTP Segmented Invoke message
+        ret = wap_msg_send( fd, octstr_get_cstr(segm_msg), octstr_len(segm_msg),
+                            tid, tid_new, msgbuf, payload_len );
+        if (ret == -1)
+        {
+            error(0, "fakewap: failure sending wtp invoke");
+            goto send_post_exit;
+        }
+        debug("fakewap", 0, "Sending WTP_SEGM_INVOKE, psn %u, payload len %d", psn, payload_len);
+
+        // receive ack for the group
+        if (gtr)
+        {
+            // receive segm invoke ack
+            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 );
+            if (ret == -1)
+            {
+                error(0, "fakewap: failure receiving WTP Segm Ack, psn %u", psn);
+                octstr_destroy(segm_msg);
+                segm_msg = NULL;
+                break;
+            }
+            debug("fakewap", 0, "Received WTP_ACK, psn %d", psn);
+        }
+
+        octstr_destroy(segm_msg);
+        segm_msg = NULL;
+    }
+
+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.
@@ -540,8 +756,12 @@
 
     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
     */
@@ -567,25 +787,26 @@
         ret = wap_msg_send( fd, WSP_Connect, sizeof(WSP_Connect),
                             tid, tid_new, NULL, 0 );
 
-        if (ret == -1) panic(0, "Send WSP_Connect failed");
+        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 );
 
-        if (ret == -1) panic(0, "Receive WSP_ConnectReply failed");
+        if (ret == -1) panic(0, "fakewap: Receive WSP_ConnectReply failed");
 
         if (ret > 2)
         {
-            sid_len = ReadVarIntLen(buf);
-            memcpy( sid, buf, sid_len);
+            sid_len = ReadVarIntLen(&buf[4]);
+            memcpy( sid, &buf[4], sid_len);
+            debug("fakewap", 0, "Received WSP_ConnectReply, SessID %d", ReadVarIntVal(&buf[4]));
         }
         /*
         **  Send abort and continue if we get an unexpected reply
         */
         if (ret == 0)  {
             if (connection_retries++ > 3) {
-                panic(0, "Cannot connect WAP GW!");
+                panic(0, "fakewap: Cannot connect WAP GW!");
             }
             wap_msg_send( fd, WTP_Abort, sizeof(WTP_Abort), tid, tid_new,
                           NULL, 0 );
@@ -597,7 +818,7 @@
         ret = wap_msg_send( fd, WTP_Ack, sizeof(WTP_Ack), tid, tid_new,
                             NULL, 0 );
 
-        if (ret == -1) panic(0, "Send WTP_Ack failed");
+        if (ret == -1) panic(0, "fakewap: Send WTP_Ack failed");
 
         /*
         **  Request WML page with the given URL
@@ -609,32 +830,115 @@
         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;
 
-        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)
+        {
+            send_post(fd, tid, tid_new, url);
+        }
+        else // send WSP Get
+        {
+            ret = wap_msg_send( fd, WSP_Get, sizeof(WSP_Get), tid, tid_new,
+                                buf, url_len+url_off );
+            if (ret == -1) break;
+        }
 
-           if (ret == -1) break;
+        int gtr = 0, ttr = 0, psn, gtr_psn = -1;
+        unsigned char segment_data[256][1500];
+        int segment_len[256];
+        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 );
+            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;
+                debug("fakewap", 0, "Received WTP_RESULT pdu, gtr %d, ttr %d", gtr, ttr);
+            }
+            else // segmented result
+            {
+                psn = buf[3];
+                debug("fakewap", 0, "Received WTP_SEGM_RESULT pdu, psn %d, gtr %d, ttr %d", psn, gtr, ttr);
+            }
+            segment_len[psn] = ret;
+            if (ret > 1500)
+                panic(0, "fakewap: Segment 0 exceeds 1500 bytes!?!");
+            memcpy(segment_data[psn], buf, ret);
+            
+            if (gtr || ttr)
+                gtr_psn = psn;
+            if (gtr || ttr || (psn < gtr_psn))
+            {
+                // check for lost segments and send NACK
+                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
+                    WTP_Nack[3] = 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, WTP_Nack, sizeof(WTP_Nack), tid, tid_new, lost_segs, num_lost );
+                    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 );
+                }
+                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 );
+                }
+                if (ret == -1)
+                    break;
+                gtr = 0;
+            }
+        }
+        if (!ttr)
+        {
+            panic(0, "fakewap: Failed to receive entire message!?!");
+            break;
+        }
+        
+        // if output file specified write received msg to file
+        if (outfile)
+        {
+            int outfd = open(outfile, O_CREAT | O_WRONLY);
+            if (outfd < 0)
+                panic(0, "fakewap: failed to open output file %s, errno %d", outfile, errno);
+
+            int iter, size = 0;
+            for (iter = 0; iter < 256; iter++)
+            {
+                if (segment_len[iter] > 0)
+                {
+                    ret = write(outfd, segment_data[iter], segment_len[iter]);
+                    if (ret < 0)
+                        panic(0, "fakewap: failed to write to output file %s, errno %d", outfile, errno);
+                    size += segment_len[iter];
+                }
+            }
+            close(outfd);
+            debug("fakewap", 0, "Wrote %d bytes of response payload to output file %s", size, outfile);
+        }
+
         /*
         **  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 );
 
@@ -698,6 +1002,7 @@
 
     sigaction(SIGALRM,&alrm,NULL);
 #endif
+
     gwlib_init();
 
     proto_version = 0;
@@ -707,7 +1012,13 @@
 
     hostname = octstr_create("localhost");
 
-    while ((opt = getopt(argc, argv, "Fhvc:g:p:P:m:i:t:V:T:t:nsd:w")) != EOF) {
+    content_type = "text/plain";
+    src_addr.sin_family = AF_INET;
+    src_addr.sin_addr.s_addr = INADDR_ANY;
+    src_addr.sin_port = 0;
+
+    while ((opt = getopt(argc, argv, "Fhvc:g:p:m:i:t:V:T:t:nsd:w:P:C:I:")) != EOF)
+    {
 	switch (opt) {
 	case 'g':
 	    octstr_destroy(hostname);
@@ -769,13 +1080,37 @@
 
         case 'w':
             writedata = 1;
+	    outfile = optarg;
             break;
 
+	case 'P':
+	    infile = optarg;
+	    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 '?':
 	default:
-	    error(0, "Unknown option %c", opt);
+	    error(0, "fakewap: Unknown option %c", opt);
 	    help();
-	    panic(0, "Stopping.");
+	    panic(0, "fakewap: Stopping.");
 	}
     }
 
@@ -802,7 +1137,7 @@
 
     mutex = mutex_create();
 
-    info(0, "fakewap starting");
+    info(0, "fakewap: starting");
 
     if (threads < 1) threads = 1;
 
@@ -817,7 +1152,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 +1165,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.