fakewap.c
资源名称:gateway-1.2.1 [点击查看]
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:19k
源码类别:
手机WAP编程
开发平台:
WINDOWS
- /*
- * fakewap.c - simulate wap clients talking directly to wap gw.
- *
- * This module can be built also in Windows where you should
- * add unzipped ".wininc" to your include directories.
- *
- * The protocol:
- *
- *
- * A) Fakewap -> Gateway
- *
- * WTP: Invoke PDU
- * WSP: Connect PDU
- *
- * B) Gateway -> Fakewap
- *
- * WTP: Result PDU
- * WSP: ConnectReply PDU
- *
- * C) Fakewap -> Gateway
- *
- * WTP: Ack PDU
- *
- * D) Fakewap -> Gateway
- *
- * WTP: Invoke PDU
- * WSP: Get PDU (data: URL)
- *
- * E) Gateway -> Fakewap
- *
- * WTP: Result PDU (data: WML page)
- * WSP: Reply PDU
- *
- * F) Fakewap -> Gateway
- *
- * WTP: Ack PDU
- *
- * G) Fakewap -> Gateway
- *
- * WTP: Invoke PDU
- * WSP: Disconnect PDU
- *
- *
- * Packets A-C open a WAP session. Packets D-F fetch a WML page.
- * Packet G closes the session.
- *
- * The test terminates when all packets have been sent.
- *
- * Tid verification uses following protocol (at WTP level only):
- *
- * A) Fakewap -> Gateway
- *
- * Either WSP Connect PDU with tid_new flag set on or same PDU with a
- * *seriously* wrapped up tid (only WTP header affected). Seriously
- * means tid being out of the window:
- *
- * |----------------------------|
- * tid space
- *
- * |-------------|
- * wrapping up
- * tid window
- *
- * B) Gateway -> Fakewap
- *
- * Ack PDU, tid verification flag set on.
- *
- * C) Fakewap -> Gateway
- *
- * Ack PDU, tid verification flag set on (this means a positive
- * answer).
- *
- * Antti Saarenheimo for WapIT Ltd.
- */
- #define MAX_SEND (0)
- static char usage[] = "
- Usage: fakewap [options] url ...n
- n
- where options are:n
- n
- -h helpn
- -v verbosen
- -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
- -i interval interval between requests (default: 1.0 seconds)n
- -c threads number concurrent of clients simulated (default: 1)n
- -V protoversion protocol version field, as an integer (default: 0)n
- -T pdu-type PDU type, as an integer (default: 1)n
- -t tcl transaction class, as an integer (default: 2)n
- -n set tid_new flag in packets, forces gateway to flush cachen
- (default: off)n
- -s test separation, by concatenating ack and disconnect pdusn
- (default: off)n
- -d difference difference between successive tid numbers (default: 1)n
- -F Accept failure and continue rather than exitingn
- -w Write/print recieved data (experimental)n
- n
- The urls are fetched in random order.n
- ";
- #include <errno.h>
- #include <ctype.h>
- #include <math.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <unistd.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <sys/param.h>
- #include <math.h>
- #include <signal.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 WTP_PDU_INVOKE 1
- #define WTP_PDU_RESULT 2
- #define WTP_PDU_ACK 3
- #define WTP_PDU_ABORT 4
- /*
- ** Common parameters
- */
- char **urls;
- int num_urls;
- Octstr *hostname = NULL;
- Octstr *gateway_addr = NULL;
- double interval = 1.0;
- unsigned short port = 9201;
- int max_send = 1;
- unsigned short tid_addition = 1;
- Mutex *mutex;
- int threads = 1;
- int num_sent = 0;
- time_t start_time, end_time;
- double totaltime = 0, besttime = 1000000L, worsttime = 0;
- int verbose = 0;
- int nofailexit = 0;
- int writedata = 0;
- int test_separation = 0;
- /*
- * 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, 0x01, 0x10, 0x00,
- 0x00 };
- 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 };
- /* 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_Concat[] = {0x00, 0x03, 0x18, 0x00, 0x00, 0x05, 0x0E, 0x00, 0x00, 0x00, 0x05 };
- /*
- ** In this case it does not matter what is the byte order
- */
- #define SET_GTR( hdr ) hdr[0] |= 0x04
- #define SET_TID( hdr, tid)
- hdr[1] |= (0x7f & ((tid) >> 8));
- 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.");
- memcpy( dest, template, sizeof(template));
- SET_TID( dest, tid )
- static void set_tid(Octstr *hdr, int tid) {
- int c;
- c = octstr_get_char(hdr, 1);
- c |= 0x7f & (tid >> 8);
- octstr_set_char(hdr, 1, c);
- octstr_set_char(hdr, 2, (unsigned char) tid);
- }
- /* Use this only on Invoke packets, the others have no tid_new field */
- static void set_tid_new(Octstr *hdr) {
- int c;
- c = octstr_get_char(hdr, 3);
- c |= 0x40;
- octstr_set_char(hdr, 3, c);
- }
- #ifndef min
- #define min(a,b) (a < b ? a : b)
- #endif
- /*
- ** if -v option has been defined, function prints the trace message and
- ** the first bytes in the message header
- */
- static void print_msg( const char * trace, unsigned char * msg,
- int msg_len ) {
- int i;
- if (verbose) {
- mutex_lock( mutex );
- printf( "%s (len %d): ", trace, msg_len );
- for (i = 0; i < msg_len && i < 16; i++) printf( "%02X ", msg[i] );
- printf( "n");
- mutex_unlock( mutex );
- }
- }
- /*
- ** if -w option has been defined, 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) {
- mutex_lock( mutex );
- printf( "%s (len %d): ", trace, msg_len );
- for (i = 0; i < msg_len && i < msg_len; i++)
- printf( "%c", isprint(msg[i]) ? msg[i] : '_');
- printf( "n");
- mutex_unlock( mutex );
- }
- }
- /* Choose a random message from a table of messages. */
- static char *choose_message(char **urls, int num_urls) {
- /* the following doesn't give an even distribution, but who cares */
- return urls[gw_rand() % num_urls];
- }
- /* returns next tid, given current tid. Every thread has its own
- * port, so has its own tid space. */
- static unsigned short next_tid(unsigned short old_tid) {
- return (old_tid + tid_addition) % (1 << 15);
- }
- /*
- ** Function stores WAP/WSP variable length integer to buffer and returns
- ** actual len
- */
- static int StoreVarInt( unsigned char *buf, unsigned long varInt )
- {
- int i, len = 1, non_zero_bits = 7;
- /*
- ** Skip all zero high bits
- */
- while ((varInt >> non_zero_bits) != 0) {
- non_zero_bits += 7;
- len++;
- }
- /*
- ** Read the higest bits first.
- */
- for (i = 0; i < len; i++)
- {
- buf[i] = ((unsigned char)(varInt >> (non_zero_bits-7)) & 0x7f) | 0x80;
- non_zero_bits -= 7;
- }
- buf[len-1] &= 0x7f;
- return len;
- }
- /*
- ** Function length of WAP/WSP variable length integer in the buffer
- */
- static int ReadVarIntLen( const unsigned char *buf )
- {
- int len = 1;
- while (buf[len-1] & 0x80) len++;
- return len;
- }
- /*
- ** 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 )
- {
- int ret;
- Octstr *datagram;
- datagram = octstr_create("");
- if (hdr != NULL)
- octstr_append_data(datagram, hdr, 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_new(datagram);
- }
- if (data != NULL)
- octstr_append_data(datagram, data, data_len);
- #if 0
- debug("fakewap", 0, "Sending WDP datagram:");
- octstr_dump(datagram, 0);
- #endif
- ret = udp_sendto(fd, datagram, gateway_addr);
- if (ret == -1) {
- error(0, "Sending to socket failed");
- return -1;
- }
- if (verbose) {
- debug("", 0, "Sent packet:");
- octstr_dump(datagram, 0);
- }
- octstr_destroy(datagram);
- return ret;
- }
- /*
- ** Function receives a wap wtl/wsp message. If the headers has been
- ** given, it must match with the received message.
- ** Return value:
- ** > 0 => length of received data
- ** == 0 => got acknowlengement or abort but not the expected data
- ** < 0 => error,
- */
- static int
- wap_msg_recv( int fd, const char * hdr, int hdr_len,
- unsigned short tid, unsigned char * data, int data_len,
- int timeout )
- {
- int ret;
- unsigned char msg[1024*64];
- int msg_len = 0;
- int fResponderIsDead = 1; /* assume this by default */
- Octstr *datagram, *dummy;
- /*
- ** Loop until we get the expected response or do timeout
- */
- for (;;)
- {
- if (timeout != 0)
- {
- ret = read_available(fd, timeout * 1000 * 1000);
- if (ret <= 0) {
- info(0, "Timeout while receiving from socket.n");
- if(nofailexit){
- continue;
- }else{
- return fResponderIsDead ? -1 : 0;
- }
- /* continue if we got ack? */
- }
- }
- ret = udp_recvfrom(fd, &datagram, &dummy);
- if (ret == 0) {
- octstr_get_many_chars(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");
- return -1;
- }
- if (hdr != NULL) {
- /*
- ** Ignore extra header bits, WAP GWs return different values
- */
- 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;
- }
- /*
- ** Handle TID test, the answer is: Yes, we have an outstanding
- ** transaction with this tid. We must turn on TID_OK-flag, too.
- ** We have a separate tid verification PDU.
- */
- 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 );
- }
- else if (GET_WTP_PDU_TYPE(msg) == WTP_PDU_ABORT) {
- print_msg( "Received WTP Abort", msg, msg_len );
- }
- else {
- print_msg( "Received unexpected message", msg, msg_len );
- }
- fResponderIsDead = 0;
- }
- else {
- hdr_len = 0;
- break;
- }
- }
- print_msg( "Received packet", msg, msg_len );
- 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);
- }
- else data_len = 0;
- return data_len;
- }
- static int get_next_transaction(void) {
- int i_this;
- mutex_lock( mutex );
- i_this = num_sent + 1;
- if (max_send == MAX_SEND || num_sent < max_send) num_sent++;
- mutex_unlock( mutex );
- return i_this;
- }
- /*
- ** 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.
- */
- static void client_session( void * arg)
- {
- int fd;
- int ret;
- int url_len = 0, url_off = 0;
- double nowsec, lastsec, tmp, sleepTime;
- long uSleepTime;
- struct timeval now;
- struct timezone tz;
- char * url;
- unsigned char sid[20];
- 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;
- unsigned short old_tid;
- int tid_new = 0;
- int connection_retries = 0;
- int i_this;
- fd = udp_client_socket();
- if (fd == -1)
- panic(0, "Couldn't create socket.");
- /*
- ** Loop until all URLs have been requested
- */
- for (;;) {
- /*
- ** 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
- */
- 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 (ret == -1) panic(0, "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 > 2)
- {
- sid_len = ReadVarIntLen(buf);
- memcpy( sid, buf, sid_len);
- }
- /*
- ** 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
- */
- old_tid = tid;
- tid = next_tid(old_tid);
- tid_new = (tid < old_tid); /* Did we wrap? */
- url = choose_message(urls, num_urls);
- 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 );
- 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 );
- 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;
- mutex_lock( mutex );
- if (tmp < besttime) besttime = tmp;
- if (tmp > worsttime) worsttime = tmp;
- totaltime += tmp;
- mutex_unlock( mutex );
- if (verbose == 1)
- {
- info(0, "fakewap: finished session # %d", i_this);
- }
- /*
- ** If we've done all the requests, then don't bother to sleep
- */
- if (i_this >= max_send) break;
- if (tmp < (double)interval) {
- usleep( uSleepTime );
- }
- }
- close(fd);
- /* The last end_time stays */
- mutex_lock( mutex );
- time(&end_time);
- mutex_unlock( mutex );
- }
- static void help(void) {
- info(0, "n%s", usage);
- }
- /* The main program. */
- int main(int argc, char **argv)
- {
- int i, opt;
- double delta;
- int proto_version, pdu_type, tcl, tid_new;
- #ifdef SunOS
- struct sigaction alrm;
- alrm.sa_handler = SIG_IGN;
- sigaction(SIGALRM,&alrm,NULL);
- #endif
- gwlib_init();
- proto_version = 0;
- pdu_type = 1;
- tcl = 2;
- tid_new = 0;
- hostname = octstr_create("localhost");
- while ((opt = getopt(argc, argv, "Fhvc:g:p:P:m:i:t:V:T:t:nsd:w")) != EOF) {
- switch (opt) {
- case 'g':
- octstr_destroy(hostname);
- hostname = octstr_create(optarg);
- break;
- case 'p':
- port = atoi(optarg);
- break;
- case 'm':
- max_send = atoi(optarg);
- break;
- case 'i':
- interval = atof(optarg);
- break;
- case 'c':
- threads = atoi(optarg);
- break;
- case 'V':
- proto_version = atoi(optarg);
- break;
- case 'T':
- pdu_type = atoi(optarg);
- break;
- case 't':
- tcl = atoi(optarg);
- break;
- case 'n':
- tid_new = 1;
- break;
- case 's':
- test_separation = 1;
- break;
- case 'd':
- tid_addition = atoi(optarg);
- break;
- case 'v':
- verbose = 1;
- break;
- case 'h':
- help();
- exit(0);
- break;
- case 'F':
- nofailexit=1;
- break;
- case 'w':
- writedata = 1;
- break;
- case '?':
- default:
- error(0, "Unknown option %c", opt);
- help();
- panic(0, "Stopping.");
- }
- }
- time(&start_time);
- 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;
- gateway_addr = udp_create_address(hostname, port);
- urls = argv + optind;
- num_urls = argc - optind;
- srand((unsigned int) time(NULL));
- mutex = mutex_create();
- info(0, "fakewap starting");
- if (threads < 1) threads = 1;
- /*
- ** Start 'extra' client threads and finally execute the
- ** session of main thread
- */
- for (i = 1; i < threads; i++)
- gwthread_create(client_session, NULL);
- client_session(NULL);
- /* Wait for the other sessions to complete */
- gwthread_join_every(client_session);
- info(0, "fakewap complete.");
- info(0, "fakewap: %d client threads made total %d transactions.",
- threads, num_sent);
- delta = difftime(end_time, start_time);
- info( 0, "fakewap: total running time %.1f seconds", delta);
- info( 0, "fakewap: %.1f messages/seconds on average", num_sent / delta);
- info( 0, "fakewap: time of best, worst and average transaction: "
- "%.1f s, %.1f s, %.1f s",
- besttime, worsttime, totaltime / num_sent );
- octstr_destroy(hostname);
- octstr_destroy(gateway_addr);
- mutex_destroy(mutex);
- gwlib_shutdown();
- return 0;
- }