fakewap.c
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:19k
源码类别:

手机WAP编程

开发平台:

WINDOWS

  1. /*
  2.  * fakewap.c - simulate wap clients talking directly to wap gw.
  3.  *
  4.  * This module can be built also in Windows where you should
  5.  * add unzipped ".wininc" to your include directories.
  6.  *
  7.  * The protocol:
  8.  *
  9.  *
  10.  *    A)    Fakewap -> Gateway
  11.  *
  12.  *        WTP: Invoke PDU
  13.  *        WSP: Connect PDU
  14.  *
  15.  *    B)    Gateway -> Fakewap
  16.  *
  17.  *        WTP: Result PDU
  18.  *        WSP: ConnectReply PDU
  19.  *
  20.  *    C)    Fakewap -> Gateway
  21.  *
  22.  *        WTP: Ack PDU
  23.  *
  24.  *    D)    Fakewap -> Gateway
  25.  *
  26.  *        WTP: Invoke PDU
  27.  *        WSP: Get PDU (data: URL)
  28.  *
  29.  *    E)    Gateway -> Fakewap
  30.  *
  31.  *        WTP: Result PDU (data: WML page)
  32.  *        WSP: Reply PDU
  33.  *
  34.  *    F)    Fakewap -> Gateway
  35.  *
  36.  *        WTP: Ack PDU
  37.  *
  38.  *    G)    Fakewap -> Gateway
  39.  *
  40.  *        WTP: Invoke PDU
  41.  *        WSP: Disconnect PDU
  42.  *
  43.  *
  44.  *    Packets A-C open a WAP session. Packets D-F fetch a WML page.
  45.  *    Packet G closes the session.
  46.  *
  47.  * The test terminates when all packets have been sent.
  48.  *
  49.  * Tid verification uses following protocol (at WTP level only):
  50.  *
  51.  *    A)   Fakewap -> Gateway
  52.  *
  53.  *         Either WSP Connect PDU with tid_new flag set on or same PDU with a 
  54.  *         *seriously* wrapped up tid (only WTP header affected). Seriously
  55.  *         means tid being out of the window:
  56.  *
  57.  *         |----------------------------|
  58.  *                  tid space
  59.  *
  60.  *         |-------------|
  61.  *          wrapping up
  62.  *          tid window
  63.  *
  64.  *    B)   Gateway -> Fakewap
  65.  *
  66.  *         Ack PDU, tid verification flag set on.
  67.  *
  68.  *    C)   Fakewap -> Gateway
  69.  *
  70.  *         Ack PDU, tid verification flag set on (this means a positive 
  71.  *         answer). 
  72.  *
  73.  * Antti Saarenheimo for WapIT Ltd.
  74.  */
  75. #define MAX_SEND (0)
  76. static char usage[] = "
  77. Usage: fakewap [options] url ...n
  78. n
  79. where options are:n
  80. n
  81. -h helpn
  82. -v verbosen
  83. -g hostname hostname or IP number of gateway (default: localhost)n
  84. -p port port number of gateway (default: 9201)n
  85. -m max maximum number of requests fakewap will make (default: 1)n
  86. -i interval interval between requests (default: 1.0 seconds)n
  87. -c threads number concurrent of clients simulated (default: 1)n
  88. -V protoversion protocol version field, as an integer (default: 0)n
  89. -T pdu-type PDU type, as an integer (default: 1)n
  90. -t tcl transaction class, as an integer (default: 2)n
  91. -n set tid_new flag in packets, forces gateway to flush cachen
  92.                 (default: off)n
  93. -s              test separation, by concatenating ack and disconnect pdusn
  94.                 (default: off)n
  95. -d difference difference between successive tid numbers (default: 1)n
  96. -F Accept failure and continue rather than exitingn
  97. -w Write/print recieved data (experimental)n
  98. n
  99. The urls are fetched in random order.n
  100. ";
  101. #include <errno.h>
  102. #include <ctype.h>
  103. #include <math.h>
  104. #include <stdio.h>
  105. #include <stdlib.h>
  106. #include <string.h>
  107. #include <time.h>
  108. #include <unistd.h>
  109. #include <sys/time.h>
  110. #include <sys/types.h>
  111. #include <sys/socket.h>
  112. #include <netinet/in.h>
  113. #include <netdb.h>
  114. #include <sys/param.h>
  115. #include <math.h>
  116. #include <signal.h>
  117. #include "gwlib/gwlib.h"
  118. #define GET_WTP_PDU_TYPE(hdr)  (hdr[0] >> 3)
  119. static int get_wtp_pdu_type(Octstr *hdr) {
  120. return octstr_get_char(hdr, 0) >> 3;
  121. }
  122. #define WTP_PDU_INVOKE  1
  123. #define WTP_PDU_RESULT  2
  124. #define WTP_PDU_ACK     3
  125. #define WTP_PDU_ABORT   4
  126. /*
  127. **  Common parameters
  128. */
  129. char **urls;
  130. int num_urls;
  131. Octstr *hostname = NULL;
  132. Octstr *gateway_addr = NULL;
  133. double interval = 1.0;
  134. unsigned short port = 9201;
  135. int max_send = 1;
  136. unsigned short tid_addition = 1;
  137. Mutex *mutex;
  138. int threads = 1;
  139. int num_sent = 0;
  140. time_t start_time, end_time;
  141. double totaltime = 0, besttime = 1000000L,  worsttime = 0;
  142. int verbose = 0;
  143. int nofailexit = 0;
  144. int writedata = 0;
  145. int test_separation = 0;
  146. /*
  147.  * PDU type, version number and transaction class are supplied by a 
  148.  * command line argument. WSP_Concat is a concatenation of WTP_Ack and 
  149.  * WSP_Disconnect PDUs.
  150.  */
  151. unsigned char WSP_Connect[] = {0x06, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 
  152. 0x00 };
  153. unsigned char WSP_ConnectReply[] = {0x16, 0x80, 0x00, 0x02 };
  154. unsigned char WTP_Ack[] =          {0x18, 0x00, 0x00 };
  155. unsigned char WTP_TidVe[] =        {0x1C, 0x00, 0x00 };
  156. unsigned char WTP_Abort[] =        {0x20, 0x00, 0x00, 0x00 };
  157. unsigned char WSP_Get[] =          {0x0E, 0x00, 0x00, 0x02, 0x40 };
  158. /* This used to also expect a content-type of 0x94, but that's too difficult
  159.  * to check now that Kannel does full header encoding. */
  160. unsigned char WSP_Reply[] =        {0x16, 0x80, 0x00, 0x04, 0x20 };
  161. unsigned char WSP_Disconnect[] =   {0x0E, 0x00, 0x00, 0x00, 0x05 };
  162. unsigned char WSP_Concat[] = {0x00, 0x03, 0x18, 0x00, 0x00, 0x05, 0x0E, 0x00, 0x00, 0x00, 0x05 };
  163. /*
  164. **  In this case it does not matter what is the byte order
  165. */
  166. #define SET_GTR( hdr ) hdr[0] |= 0x04
  167. #define SET_TID( hdr, tid) 
  168. hdr[1] |= (0x7f & ((tid) >> 8)); 
  169. hdr[2] = (char)(tid)
  170. #define GET_TID( hdr ) (((hdr[1] & 0x7f) << 8) + hdr[2])
  171. #define CONSTRUCT_EXPECTED_REPLY_HDR( dest, template, tid ) 
  172.     if (sizeof(dest) < sizeof(template)) panic(0,"buffer overflow.");
  173.     memcpy( dest, template, sizeof(template));
  174.     SET_TID( dest, tid )
  175. static void set_tid(Octstr *hdr, int tid) {
  176. int c;
  177. c = octstr_get_char(hdr, 1);
  178. c |= 0x7f & (tid >> 8);
  179. octstr_set_char(hdr, 1, c);
  180. octstr_set_char(hdr, 2, (unsigned char) tid);
  181. }
  182. /* Use this only on Invoke packets, the others have no tid_new field */
  183. static void set_tid_new(Octstr *hdr) {
  184. int c;
  185. c = octstr_get_char(hdr, 3);
  186. c |= 0x40;
  187. octstr_set_char(hdr, 3, c);
  188. }
  189. #ifndef min
  190. #define min(a,b) (a < b ? a : b)
  191. #endif
  192. /*
  193. **  if -v option has been defined, function prints the trace message and
  194. **  the first bytes in the message header
  195. */
  196. static void print_msg( const char * trace, unsigned char * msg,
  197.                 int msg_len ) {
  198.     int i;
  199.     if (verbose) {
  200.         mutex_lock( mutex );
  201.         printf( "%s (len %d): ", trace, msg_len );
  202.         for (i = 0; i < msg_len && i < 16; i++) printf( "%02X ", msg[i] );
  203.         printf( "n");
  204.         mutex_unlock( mutex );
  205.     }
  206. }
  207. /*
  208. **  if -w option has been defined, function prints the trace message and
  209. **  the first bytes in the message header
  210. */   
  211. static void print_data( const char * trace, unsigned char * msg,
  212.                 int msg_len ) {
  213.     int i;
  214.     if (verbose || writedata) {
  215.         mutex_lock( mutex );
  216.         printf( "%s (len %d): ", trace, msg_len );
  217.         for (i = 0; i < msg_len && i < msg_len; i++)
  218.              printf( "%c", isprint(msg[i]) ? msg[i] : '_');
  219.         printf( "n");
  220.         mutex_unlock( mutex );
  221.     }
  222. }
  223. /* Choose a random message from a table of messages. */
  224. static char *choose_message(char **urls, int num_urls) {
  225.     /* the following doesn't give an even distribution, but who cares */
  226.     return urls[gw_rand() % num_urls];
  227. }
  228. /* returns next tid, given current tid.  Every thread has its own
  229.  * port, so has its own tid space. */
  230. static unsigned short next_tid(unsigned short old_tid) { 
  231.     return (old_tid + tid_addition) % (1 << 15);
  232. }
  233. /*
  234. **  Function stores WAP/WSP variable length integer to buffer and returns 
  235. **  actual len
  236. */
  237. static int StoreVarInt( unsigned char *buf, unsigned long varInt )
  238. {
  239.     int i, len = 1, non_zero_bits = 7;
  240.     /*
  241.     **    Skip all zero high bits
  242.     */
  243.     while ((varInt >> non_zero_bits) != 0) {
  244.         non_zero_bits += 7;
  245.         len++;
  246.     }
  247.     /*
  248.     **    Read the higest bits first.
  249.     */
  250.     for (i = 0; i < len; i++)
  251.     {
  252.         buf[i] = ((unsigned char)(varInt >> (non_zero_bits-7)) & 0x7f) | 0x80;
  253.         non_zero_bits -= 7;
  254.     }
  255.     buf[len-1] &= 0x7f;
  256.     return len;
  257. }
  258. /*
  259. **  Function length of WAP/WSP variable length integer in the buffer
  260. */
  261. static int ReadVarIntLen( const unsigned char *buf )
  262. {
  263.     int    len = 1;
  264.     while (buf[len-1] & 0x80) len++;
  265.     return len;
  266. }
  267. /*
  268. **  Function sends message to WAP GW
  269. */
  270. static int
  271. wap_msg_send( int fd, unsigned char * hdr,
  272.             int hdr_len, unsigned short tid, int tid_new, unsigned char * data,
  273.             int data_len )
  274. {
  275.     int ret;
  276.     Octstr *datagram;
  277.     datagram = octstr_create("");
  278.     if (hdr != NULL)
  279.      octstr_append_data(datagram, hdr, hdr_len);
  280.     set_tid(datagram, tid);
  281.     if (get_wtp_pdu_type(datagram) == WTP_PDU_INVOKE) {
  282. /* request ack every time */
  283. int c;
  284. c = octstr_get_char(datagram, 3);
  285. octstr_set_char(datagram, 3, c | 0x10);
  286. if (tid_new)
  287. set_tid_new(datagram);
  288.     }
  289.     if (data != NULL)
  290. octstr_append_data(datagram, data, data_len);
  291.     
  292. #if 0
  293.     debug("fakewap", 0, "Sending WDP datagram:");
  294.     octstr_dump(datagram, 0);
  295. #endif
  296.     ret = udp_sendto(fd, datagram, gateway_addr);
  297.     if (ret == -1) {
  298.         error(0, "Sending to socket failed");
  299.         return -1;
  300.     }
  301.     if (verbose) {
  302. debug("", 0, "Sent packet:");
  303. octstr_dump(datagram, 0);
  304.     }
  305.     octstr_destroy(datagram);
  306.     return ret;
  307. }
  308. /*
  309. **  Function receives a wap wtl/wsp message. If the headers has been
  310. **  given, it must match with the received message.
  311. **  Return value:
  312. **      >  0 => length of received data
  313. **      == 0 => got acknowlengement or abort but not the expected data
  314. **      < 0  => error,
  315. */
  316. static int
  317. wap_msg_recv( int fd, const char * hdr, int hdr_len,
  318.               unsigned short tid, unsigned char * data, int data_len,
  319.               int timeout )
  320. {
  321.     int ret;
  322.     unsigned char msg[1024*64];
  323.     int msg_len = 0;
  324.     int    fResponderIsDead = 1;  /* assume this by default */
  325.     Octstr *datagram, *dummy;
  326.     /*
  327.     **  Loop until we get the expected response or do timeout
  328.     */
  329.     for (;;)
  330.     {
  331.         if (timeout != 0)
  332.         {
  333.     ret = read_available(fd, timeout * 1000 * 1000);
  334.     if (ret <= 0) {
  335.                 info(0, "Timeout while receiving from socket.n");
  336. if(nofailexit){
  337.     continue;
  338. }else{
  339.     return fResponderIsDead ? -1 : 0;
  340. }
  341. /* continue if we got ack? */
  342.     }
  343.         }
  344. ret = udp_recvfrom(fd, &datagram, &dummy);
  345. if (ret == 0) {
  346. octstr_get_many_chars(msg, datagram, 0, octstr_len(datagram));
  347. msg_len = octstr_len(datagram);
  348. }
  349. octstr_destroy(datagram);
  350. octstr_destroy(dummy);
  351.         if (ret == -1) {
  352.             error(0, "recv() from socket failed");
  353.             return -1;
  354.         }
  355.         if (hdr != NULL) {
  356.             /*
  357.             **  Ignore extra header bits, WAP GWs return different values
  358.             */
  359.             if (msg_len >= hdr_len &&
  360.                 GET_WTP_PDU_TYPE(msg) == GET_WTP_PDU_TYPE(hdr) &&
  361.                 (hdr_len <= 3 || !memcmp( msg+3, hdr+3, hdr_len-3 ))) {
  362.                 break;
  363.             }
  364.             /*
  365.             **  Handle TID test, the answer is: Yes, we have an outstanding
  366.             **  transaction with this tid. We must turn on TID_OK-flag, too.
  367.             **  We have a separate tid verification PDU.
  368.             */
  369.             else if (GET_WTP_PDU_TYPE(msg) == WTP_PDU_ACK &&
  370.                      GET_TID(msg) == tid) {
  371.                 print_msg( "Received tid verification", msg, msg_len );
  372.                 wap_msg_send( fd, WTP_TidVe, sizeof(WTP_TidVe), tid, 0,
  373.                               NULL, 0 );
  374.             }
  375.             else if (GET_WTP_PDU_TYPE(msg) == WTP_PDU_ABORT) {
  376.                 print_msg( "Received WTP Abort", msg, msg_len );
  377.             }
  378.             else {
  379.                 print_msg( "Received unexpected message", msg, msg_len );
  380.             }
  381.             fResponderIsDead = 0;
  382.         }
  383.         else {
  384.             hdr_len = 0;
  385.             break;
  386.         }
  387.     }
  388.     print_msg( "Received packet", msg, msg_len );
  389.     print_data( "Received data", msg, msg_len );
  390.     if (data != NULL && msg_len > hdr_len) {
  391.         data_len = min( data_len, msg_len - hdr_len );
  392.         memcpy( data, msg+hdr_len, data_len);
  393.     }
  394.     else  data_len = 0;
  395.     return data_len;
  396. }
  397. static int get_next_transaction(void) {
  398.     int i_this;
  399.     mutex_lock( mutex );
  400.     i_this = num_sent + 1;
  401.     if (max_send == MAX_SEND || num_sent < max_send) num_sent++;
  402.     mutex_unlock( mutex );
  403.     return i_this;
  404. }
  405. /*
  406. **  Function (or thread) sets up a dgram socket.  Then it loops: WTL/WSP
  407. **  Connect, Get a url and Disconnect until all requests are have been done.
  408. */
  409. static void client_session( void * arg)
  410. {
  411.     int fd;
  412.     int ret;
  413.     int url_len = 0, url_off = 0;
  414.     double nowsec, lastsec, tmp, sleepTime;
  415.     long uSleepTime;
  416.     struct timeval now;
  417.     struct timezone tz;
  418.     char * url;
  419.     unsigned char  sid[20];
  420.     int            sid_len = 0;
  421.     unsigned char  buf[64*1024];
  422.     unsigned char reply_hdr[32];
  423.     long timeout = 10;  /* wap gw is broken if no input */
  424.     unsigned short tid = 0;
  425.     unsigned short old_tid;
  426.     int tid_new = 0;
  427.     int connection_retries = 0;
  428.     int i_this;
  429.     fd = udp_client_socket();
  430.     if (fd == -1)
  431.         panic(0, "Couldn't create socket.");
  432.     /*
  433.     **  Loop until all URLs have been requested
  434.     */
  435.     for (;;) {
  436. /*
  437.  ** Get start time of this request
  438.  */
  439.      gettimeofday(&now, &tz);
  440.      lastsec = (double) now.tv_sec + now.tv_usec / 1e6;
  441.         /*
  442.         **  Get next transaction number or exit if too many transactions
  443.         */
  444.         i_this = get_next_transaction();
  445.         if (max_send != MAX_SEND  && i_this > max_send) break;
  446.         /*
  447.         **  Connect, save sid from reply and finally ack the reply
  448.         */
  449. old_tid = tid;
  450.         tid = next_tid(old_tid);
  451. tid_new = (tid < old_tid);  /* Did we wrap? */
  452.         ret = wap_msg_send( fd, WSP_Connect, sizeof(WSP_Connect),
  453.                             tid, tid_new, NULL, 0 );
  454.         if (ret == -1) panic(0, "Send WSP_Connect failed");
  455.         CONSTRUCT_EXPECTED_REPLY_HDR( reply_hdr, WSP_ConnectReply, tid );
  456.         ret = wap_msg_recv( fd, reply_hdr, sizeof(WSP_ConnectReply),
  457.                             tid, buf, sizeof(buf), timeout );
  458.         if (ret == -1) panic(0, "Receive WSP_ConnectReply failed");
  459.         if (ret > 2)
  460.         {
  461.             sid_len = ReadVarIntLen(buf);
  462.             memcpy( sid, buf, sid_len);
  463.         }
  464.         /*
  465.         **  Send abort and continue if we get an unexpected reply
  466.         */
  467.         if (ret == 0)  {
  468.             if (connection_retries++ > 3) {
  469.                 panic(0, "Cannot connect WAP GW!");
  470.             }
  471.             wap_msg_send( fd, WTP_Abort, sizeof(WTP_Abort), tid, tid_new,
  472.                           NULL, 0 );
  473.             continue;
  474.         }
  475.         else {
  476.             connection_retries = 0;
  477.         }
  478.         ret = wap_msg_send( fd, WTP_Ack, sizeof(WTP_Ack), tid, tid_new,
  479.                             NULL, 0 );
  480.         if (ret == -1) panic(0, "Send WTP_Ack failed");
  481.         /*
  482.         **  Request WML page with the given URL
  483.         */
  484. old_tid = tid;
  485.         tid = next_tid(old_tid);
  486. tid_new = (tid < old_tid);  /* Did we wrap? */
  487.         url = choose_message(urls, num_urls);
  488.         url_len = strlen(url);
  489.         url_off = StoreVarInt( buf, url_len );
  490.         memcpy( buf+url_off, url, url_len );
  491.         ret = wap_msg_send( fd, WSP_Get, sizeof(WSP_Get), tid, tid_new,
  492.     buf, url_len+url_off );
  493.         if (ret == -1) break;
  494.         CONSTRUCT_EXPECTED_REPLY_HDR( reply_hdr, WSP_Reply, tid );
  495.         ret = wap_msg_recv( fd, reply_hdr, sizeof(WSP_Reply),
  496.                             tid, buf, sizeof(buf), timeout );
  497.         if (ret == -1) break;
  498.         /*
  499. ** If we are testing separation, we concatenate WTP_Ack and 
  500.         ** WSP_Disconnect messages.
  501.         */
  502.         if (test_separation){
  503.            ret = wap_msg_send(fd, WSP_Concat, sizeof(WSP_Concat), tid, tid_new,
  504.      sid, sid_len);
  505.            
  506.            if (ret == -1) break;
  507.         } else {
  508.            ret = wap_msg_send( fd, WTP_Ack, sizeof(WTP_Ack), tid, tid_new,
  509.     NULL, 0 );
  510.            if (ret == -1) break;
  511.         /*
  512.         **  Finally disconnect with the sid returned by connect reply
  513.         */
  514.            ret = wap_msg_send( fd, WSP_Disconnect, sizeof(WSP_Disconnect),
  515.             tid, tid_new, sid, sid_len );
  516.            if (ret == -1) break;
  517.         }
  518. /*
  519.  ** Get end time of the request
  520.  */
  521.         gettimeofday(&now, &tz);
  522.         nowsec = (double) now.tv_sec + now.tv_usec / 1e6;
  523.         tmp = nowsec - lastsec; /* Duration of request */
  524. sleepTime = interval-tmp; /* Amount of time left to sleep */
  525. uSleepTime = sleepTime * 1e6;
  526.         mutex_lock( mutex );
  527.         if (tmp < besttime) besttime = tmp;
  528.         if (tmp > worsttime) worsttime = tmp;
  529.         totaltime += tmp;
  530.         mutex_unlock( mutex );
  531. if (verbose == 1)
  532. {
  533. info(0, "fakewap: finished session # %d", i_this);
  534. }
  535. /*
  536.  ** If we've done all the requests, then don't bother to sleep
  537.  */
  538.         if (i_this >= max_send) break;
  539.         if (tmp < (double)interval) {
  540.             usleep( uSleepTime );
  541.         }
  542.     }
  543.     close(fd);
  544.     /* The last end_time stays */
  545.     mutex_lock( mutex );
  546.     time(&end_time);
  547.     mutex_unlock( mutex );
  548. }
  549. static void help(void) {
  550. info(0, "n%s", usage);
  551. }
  552. /* The main program. */
  553. int main(int argc, char **argv)
  554. {
  555.     int i, opt;
  556.     double delta;
  557.     int proto_version, pdu_type, tcl, tid_new;
  558. #ifdef SunOS
  559.     struct sigaction alrm;
  560.     alrm.sa_handler = SIG_IGN;
  561.     sigaction(SIGALRM,&alrm,NULL);
  562. #endif
  563.     gwlib_init();
  564.     proto_version = 0;
  565.     pdu_type = 1;
  566.     tcl = 2;
  567.     tid_new = 0;
  568.     hostname = octstr_create("localhost");
  569.     while ((opt = getopt(argc, argv, "Fhvc:g:p:P:m:i:t:V:T:t:nsd:w")) != EOF) {
  570. switch (opt) {
  571. case 'g':
  572.     octstr_destroy(hostname);
  573.     hostname = octstr_create(optarg);
  574.     break;
  575. case 'p':
  576.     port = atoi(optarg);
  577.     break;
  578. case 'm':
  579.     max_send = atoi(optarg);
  580.     break;
  581. case 'i':
  582.     interval = atof(optarg);
  583.     break;
  584. case 'c':
  585.     threads = atoi(optarg);
  586.     break;
  587. case 'V':
  588.     proto_version = atoi(optarg);
  589.     break;
  590. case 'T':
  591.     pdu_type = atoi(optarg);
  592.     break;
  593. case 't':
  594.     tcl = atoi(optarg);
  595.     break;
  596. case 'n':
  597.     tid_new = 1;
  598.     break;
  599.         case 's':
  600.     test_separation = 1;
  601.             break;
  602. case 'd':
  603.     tid_addition = atoi(optarg);
  604.     break;
  605. case 'v':
  606.     verbose = 1;
  607.     break;
  608.     
  609. case 'h':
  610.     help();
  611.     exit(0);
  612.     break;
  613. case 'F':
  614.     nofailexit=1;
  615.     break;
  616.         case 'w':
  617.             writedata = 1;
  618.             break;
  619. case '?':
  620. default:
  621.     error(0, "Unknown option %c", opt);
  622.     help();
  623.     panic(0, "Stopping.");
  624. }
  625.     }
  626.     time(&start_time);
  627.     if (optind >= argc)
  628.         panic(0, "%s", usage);
  629. if (verbose != 1)
  630. {
  631. log_set_output_level (GW_INFO);
  632. }
  633.     WSP_Connect[3] += (proto_version&3)<<6;
  634.     WSP_Connect[0] += (pdu_type&15)<<3;
  635.     WSP_Connect[3] += tcl&3;
  636.     WSP_Connect[3] += (tid_new&1)<<5;
  637.     
  638.     gateway_addr = udp_create_address(hostname, port);
  639.     urls = argv + optind;
  640.     num_urls = argc - optind;
  641.     srand((unsigned int) time(NULL));
  642.     mutex = mutex_create();
  643.     info(0, "fakewap starting");
  644.     if (threads < 1) threads = 1;
  645.     /*
  646.     **  Start 'extra' client threads and finally execute the
  647.     **  session of main thread
  648.     */
  649.     for (i = 1; i < threads; i++)
  650.         gwthread_create(client_session, NULL);
  651.     client_session(NULL);
  652.     /* Wait for the other sessions to complete */
  653.     gwthread_join_every(client_session);
  654.     info(0, "fakewap complete.");
  655.     info(0, "fakewap: %d client threads made total %d transactions.", 
  656.      threads, num_sent);
  657.     delta = difftime(end_time, start_time);
  658.     info( 0, "fakewap: total running time %.1f seconds", delta);
  659.     info( 0, "fakewap: %.1f messages/seconds on average", num_sent / delta);
  660.     info( 0, "fakewap: time of best, worst and average transaction: "
  661.              "%.1f s, %.1f s, %.1f s",
  662.          besttime, worsttime, totaltime / num_sent );
  663.     octstr_destroy(hostname);
  664.     octstr_destroy(gateway_addr);
  665.     mutex_destroy(mutex);
  666.     gwlib_shutdown();
  667.     return 0;
  668. }