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;
}