00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066 #include <string.h>
00067 #include <stdlib.h>
00068 #include <unistd.h>
00069 #include <errno.h>
00070 #include <signal.h>
00071
00072 #include <sys/time.h>
00073 #include <sys/types.h>
00074 #include <sys/socket.h>
00075
00076 #include "gwlib/gwlib.h"
00077 #include "gw/msg.h"
00078
00079
00080 enum wsp_types {
00081 Bad_PDU = -1,
00082 Connect_PDU = 0x01,
00083 ConnectReply_PDU = 0x02,
00084 Redirect_PDU = 0x03,
00085 Reply_PDU = 0x04,
00086 Disconnect_PDU = 0x05,
00087 Push_PDU = 0x06,
00088 ConfirmedPush_PDU = 0x07,
00089 Suspend_PDU = 0x08,
00090 Resume_PDU = 0x09,
00091 Get_PDU = 0x40,
00092 Options_PDU = 0x41,
00093 Head_PDU = 0x42,
00094 Delete_PDU = 0x43,
00095 Trace_PDU = 0x44,
00096 Post_PDU = 0x60,
00097 Put_PDU = 0x61
00098 };
00099
00100 enum wtp_types {
00101 INVOKE = 1,
00102 RESULT = 2,
00103 ACK = 3
00104 };
00105
00106 #define WSP_VERSION 0x10
00107
00108 #define TIMEOUT 10.0
00109
00110 static long max_requests = 1;
00111 static long max_clients = 1;
00112 static long req_per_session = 1;
00113 static unsigned short http_port;
00114 static int wapbox_port = 30188;
00115 static Octstr *http_url = NULL;
00116
00117 static int verbose_debug = 0;
00118 static int user_ack = 0;
00119
00120 static long requests_complete = 0;
00121 static volatile sig_atomic_t dying = 0;
00122
00123 enum WTP_type {
00124 TR_Invoke = 1,
00125 TR_Result = 2,
00126 TR_Ack = 3,
00127 TR_Abort = 4
00128 };
00129
00130 struct client_status {
00131
00132 int wtp_invoked;
00133
00134
00135 int wtp_tid;
00136
00137
00138
00139 int wsp_connected;
00140
00141
00142 long wsp_session_id;
00143
00144
00145 int pages_fetched;
00146
00147
00148 unsigned short port;
00149 };
00150 typedef struct client_status Client;
00151
00152 static Client *clients;
00153 List *ready_clients;
00154
00155 static unsigned long get_varint(Octstr *pdu, int pos) {
00156 int c;
00157 long result = 0;
00158
00159 do {
00160 c = octstr_get_char(pdu, pos++);
00161 result = (result << 7) | (c & 0x7f);
00162 } while (c & 0x80);
00163
00164 return c;
00165 }
00166
00167 static void http_thread(void *arg) {
00168 HTTPClient *client;
00169 Octstr *ip;
00170 Octstr *url;
00171 List *headers;
00172 Octstr *body;
00173 List *cgivars;
00174 Octstr *reply_body = octstr_create(
00175 "<?xml version=\"1.0\"?>\n"
00176 "<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\"\n"
00177 " \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n"
00178 "<wml>\n"
00179 "<card id=\"main\" title=\"Hello, world\" newcontext=\"true\">\n"
00180 " <p>Hello, world.</p>\n"
00181 "</card></wml>\n");
00182 List *reply_headers = gwlist_create();
00183 int port;
00184
00185 port = *(int *) arg;
00186 gw_free(arg);
00187
00188 gwlist_append(reply_headers,
00189 octstr_create("Content-Type: text/vnd.wap.wml"));
00190
00191 for (;!dying;) {
00192 client = http_accept_request(port, &ip, &url, &headers,
00193 &body, &cgivars);
00194 if (client == NULL)
00195 break;
00196 http_send_reply(client, HTTP_OK, reply_headers, reply_body);
00197 http_destroy_headers(headers);
00198 octstr_destroy(ip);
00199 octstr_destroy(url);
00200 octstr_destroy(body);
00201 http_destroy_cgiargs(cgivars);
00202 }
00203
00204 octstr_destroy(reply_body);
00205 http_destroy_headers(reply_headers);
00206 }
00207
00208
00209 static long http_thread_id;
00210
00211 static int start_http_thread(void) {
00212 unsigned short port;
00213 int *port_copy;
00214 int ssl = 0;
00215
00216 for (port = 40000; port < 41000; port += 13) {
00217 if (http_open_port(port, ssl) != -1)
00218 break;
00219 }
00220 if (port == 41000)
00221 panic(0, "No ports available for http server");
00222
00223 port_copy = gw_malloc(sizeof(*port_copy));
00224 *port_copy = port;
00225 http_thread_id = gwthread_create(http_thread, port_copy);
00226 if (http_thread_id == -1)
00227 panic(0, "Cannot start http thread");
00228 return port;
00229 }
00230
00231 static Connection *start_wapbox(void) {
00232 int wap_socket;
00233 int wapbox;
00234
00235 wap_socket = make_server_socket(wapbox_port, NULL);
00236 if (wap_socket < 0)
00237 panic(0, "Couldn't make wapbox port\n");
00238
00239 wapbox = accept(wap_socket, NULL, NULL);
00240 if (wapbox < 0)
00241 panic(errno, "Wapbox could not connect\n");
00242
00243 close(wap_socket);
00244
00245 return conn_wrap_fd(wapbox, 0);
00246 }
00247
00248 static void initialize_clients(void) {
00249 long i;
00250
00251 ready_clients = gwlist_create();
00252
00253 clients = gw_malloc(max_clients * sizeof(*clients));
00254 for (i = 0; i < max_clients; i++) {
00255 clients[i].wtp_invoked = 0;
00256 clients[i].wtp_tid = 0;
00257 clients[i].wsp_connected = 0;
00258 clients[i].wsp_session_id = -1;
00259 clients[i].pages_fetched = 0;
00260 clients[i].port = i;
00261 gwlist_append(ready_clients, &clients[i]);
00262 }
00263 }
00264
00265 static void destroy_clients(void) {
00266 gw_free(clients);
00267 gwlist_destroy(ready_clients, NULL);
00268 }
00269
00270 static Client *find_client(unsigned short port) {
00271
00272 if (port >= max_clients)
00273 return NULL;
00274
00275 return clients + port;
00276 }
00277
00278 static void client_done(Client *client) {
00279 requests_complete++;
00280 gwlist_append(ready_clients, client);
00281 }
00282
00283 static void increment_tid(Client *client) {
00284 if (client->wtp_tid == 0x7fff)
00285 client->wtp_tid = 0;
00286 else
00287 client->wtp_tid++;
00288 }
00289
00290
00291 static void set_user_ack(Octstr *pdu) {
00292 octstr_set_bits(pdu, 3 * 8 + 3, 1, 1);
00293 }
00294
00295 static Octstr *wtp_invoke_create(int class) {
00296 Octstr *pdu;
00297
00298
00299 static unsigned char data[] = { 0x0e, 0x00, 0x00, 0x00 };
00300 gw_assert(class >= 0);
00301 gw_assert(class <= 2);
00302 pdu = octstr_create_from_data(data, sizeof(data));
00303 octstr_set_char(pdu, 3, class);
00304
00305 if (user_ack)
00306 set_user_ack(pdu);
00307
00308 return pdu;
00309 }
00310
00311 static Octstr *wtp_ack_create(void) {
00312 static unsigned char data[] = { 0x18, 0x00, 0x00 };
00313 return octstr_create_from_data(data, sizeof(data));
00314 }
00315
00316 static void add_wsp_connect(Octstr *pdu) {
00317 static unsigned char data[] = { Connect_PDU, WSP_VERSION, 68, 0x00,
00318 0x03, 0x80, 0x90, 0x00, 0x03, 0x81, 0x90, 0x00,
00319 0x02, 0x82, 0x30, 0x02, 0x83, 0x01, 0x02, 0x84,
00320 0x01, 0x28, 0x85, 0x50, 0x58, 0x2d, 0x55, 0x50,
00321 0x2d, 0x41, 0x47, 0x45, 0x54, 0x00, 0x51, 0x58,
00322 0x2d, 0x55, 0x50, 0x2d, 0x47, 0x45, 0x54, 0x4e,
00323 0x4f, 0x54, 0x49, 0x46, 0x59, 0x00, 0x70, 0x58,
00324 0x2d, 0x55, 0x50, 0x2d, 0x41, 0x50, 0x4f, 0x53,
00325 0x54, 0x00, 0x09, 0x86, 0x02, 0x78, 0x2d, 0x75,
00326 0x70, 0x2d, 0x31, 0x00 };
00327 octstr_append_data(pdu, data, sizeof(data));
00328 }
00329
00330 static void add_wsp_get(Octstr *pdu) {
00331 Octstr *urlbuf;
00332
00333 octstr_append_char(pdu, Get_PDU);
00334 if (http_url) {
00335 octstr_append_uintvar(pdu, octstr_len(http_url));
00336 octstr_append(pdu, http_url);
00337 } else {
00338 urlbuf = octstr_format("http://localhost:%ld/hello.wml",
00339 (long) http_port);
00340 octstr_append_uintvar(pdu, octstr_len(urlbuf));
00341 octstr_append(pdu, urlbuf);
00342 octstr_destroy(urlbuf);
00343 }
00344 }
00345
00346 static void add_wsp_disconnect(Octstr *pdu, long session_id) {
00347 octstr_append_char(pdu, Disconnect_PDU);
00348 octstr_append_uintvar(pdu, session_id);
00349 }
00350
00351 static void set_tid(Octstr *pdu, int tid) {
00352 int c;
00353
00354
00355 tid &= 0x7fff;
00356
00357 c = octstr_get_char(pdu, 1);
00358 c = (tid >> 8) | (c & 0x80);
00359 octstr_set_char(pdu, 1, c);
00360 octstr_set_char(pdu, 2, tid & 0xff);
00361 }
00362
00363 static int get_tid(Octstr *pdu) {
00364 return octstr_get_bits(pdu, 8, 16);
00365 }
00366
00367 static int wtp_type(Octstr *pdu) {
00368 return octstr_get_bits(pdu, 1, 4);
00369 }
00370
00371 static Msg *wdp_create(Octstr *data, Client *client) {
00372 Msg *msg;
00373
00374 msg = msg_create(wdp_datagram);
00375 msg->wdp_datagram.source_address = octstr_create("127.0.0.1");
00376 msg->wdp_datagram.source_port = client->port;
00377 msg->wdp_datagram.destination_address = octstr_create("127.0.0.1");
00378 msg->wdp_datagram.destination_port = 9201;
00379 msg->wdp_datagram.user_data = octstr_duplicate(data);
00380
00381 return msg;
00382 }
00383
00384 static void send_pdu(Octstr *pdu, Connection *boxc, Client *client) {
00385 Msg *msg;
00386 Octstr *data;
00387
00388 if (verbose_debug) {
00389 debug("test", 0, "Sending:");
00390 octstr_dump(pdu, 0);
00391 }
00392
00393 msg = wdp_create(pdu, client);
00394 data = msg_pack(msg);
00395 conn_write_withlen(boxc, data);
00396
00397 octstr_destroy(data);
00398 msg_destroy(msg);
00399 }
00400
00401 static void send_invoke_connect(Connection *boxc, Client *client) {
00402 Octstr *pdu;
00403
00404 gw_assert(client != NULL);
00405 gw_assert(client->wtp_invoked == 0);
00406 gw_assert(client->wsp_connected == 0);
00407
00408 pdu = wtp_invoke_create(2);
00409 set_tid(pdu, client->wtp_tid);
00410 add_wsp_connect(pdu);
00411
00412 send_pdu(pdu, boxc, client);
00413 octstr_destroy(pdu);
00414
00415 client->wtp_invoked = 1;
00416 }
00417
00418 static void send_invoke_get(Connection *boxc, Client *client) {
00419 Octstr *pdu;
00420
00421 gw_assert(client != NULL);
00422 gw_assert(client->wtp_invoked == 0);
00423 gw_assert(client->wsp_connected == 1);
00424
00425 pdu = wtp_invoke_create(2);
00426 set_tid(pdu, client->wtp_tid);
00427 add_wsp_get(pdu);
00428
00429 send_pdu(pdu, boxc, client);
00430 octstr_destroy(pdu);
00431
00432 client->wtp_invoked = 1;
00433 }
00434
00435 static void record_disconnect(Client *client) {
00436 client->wsp_connected = 0;
00437 client->wsp_session_id = -1;
00438 client->pages_fetched = 0;
00439 increment_tid(client);
00440 }
00441
00442 static void send_invoke_disconnect(Connection *boxc, Client *client) {
00443 Octstr *pdu;
00444
00445 gw_assert(client != NULL);
00446 gw_assert(client->wtp_invoked == 0);
00447 gw_assert(client->wsp_connected == 1);
00448
00449
00450 pdu = wtp_invoke_create(0);
00451 set_tid(pdu, client->wtp_tid);
00452 add_wsp_disconnect(pdu, client->wsp_session_id);
00453
00454 send_pdu(pdu, boxc, client);
00455 octstr_destroy(pdu);
00456
00457 record_disconnect(client);
00458 client_done(client);
00459 }
00460
00461 static void handle_connect_reply(Connection *boxc, Client *client, Octstr *pdu) {
00462 Octstr *ack;
00463
00464 gw_assert(client);
00465 gw_assert(client->wtp_invoked);
00466 gw_assert(!client->wsp_connected);
00467
00468 if (octstr_get_char(pdu, 3) != ConnectReply_PDU) {
00469 error(0, "Unexpected CONNECT reply");
00470 octstr_dump(pdu, 0);
00471 return;
00472 }
00473
00474 ack = wtp_ack_create();
00475 set_tid(ack, client->wtp_tid);
00476 send_pdu(ack, boxc, client);
00477 octstr_destroy(ack);
00478
00479 client->wtp_invoked = 0;
00480 increment_tid(client);
00481 client->wsp_connected = 1;
00482 client->wsp_session_id = get_varint(pdu, 4);
00483
00484 send_invoke_get(boxc, client);
00485 }
00486
00487 static void handle_get_reply(Connection *boxc, Client *client, Octstr *pdu) {
00488 Octstr *ack;
00489
00490 gw_assert(client);
00491 gw_assert(client->wtp_invoked);
00492 gw_assert(client->wsp_connected);
00493
00494 if (octstr_get_char(pdu, 3) != Reply_PDU) {
00495 error(0, "Unexpected GET reply");
00496 octstr_dump(pdu, 0);
00497 return;
00498 }
00499
00500 ack = wtp_ack_create();
00501 set_tid(ack, client->wtp_tid);
00502 send_pdu(ack, boxc, client);
00503 octstr_destroy(ack);
00504
00505 client->wtp_invoked = 0;
00506 increment_tid(client);
00507 client->pages_fetched++;
00508
00509 if (client->pages_fetched == req_per_session) {
00510 send_invoke_disconnect(boxc, client);
00511 } else {
00512 client_done(client);
00513 }
00514 }
00515
00516 static void handle_reply(Connection *boxc, Msg *reply) {
00517 Client *client;
00518 Octstr *wtp;
00519 int type;
00520 int dumped = 0;
00521
00522 gw_assert(reply != NULL);
00523 gw_assert(reply->type == wdp_datagram);
00524
00525 client = find_client(reply->wdp_datagram.destination_port);
00526 if (client == NULL)
00527 panic(0, "got packet for nonexisting client %ld",
00528 (long) reply->wdp_datagram.destination_port);
00529
00530 wtp = reply->wdp_datagram.user_data;
00531 type = wtp_type(wtp);
00532
00533 if (verbose_debug) {
00534 debug("test", 0, "Received:");
00535 octstr_dump(wtp, 0);
00536 dumped = 1;
00537 }
00538
00539 if (client->wtp_invoked == 0) {
00540 error(0, "Got packet for client that wasn't waiting");
00541 if (!dumped) {
00542 octstr_dump(wtp, 0);
00543 dumped = 1;
00544 }
00545 return;
00546 }
00547
00548
00549 if (get_tid(wtp) != (client->wtp_tid ^ 0x8000)) {
00550 error(0, "Got packet with wrong tid %d, expected %d.",
00551 get_tid(wtp), client->wtp_tid ^ 0x8000);
00552 if (!dumped) {
00553 octstr_dump(wtp, 0);
00554 dumped = 1;
00555 }
00556 return;
00557 }
00558
00559
00560
00561 if (client->wsp_connected == 0 && type == RESULT) {
00562 handle_connect_reply(boxc, client, wtp);
00563 } else if (client->wsp_connected == 1 && type == RESULT) {
00564 handle_get_reply(boxc, client, wtp);
00565 } else if (client->wsp_connected == 2 && type == ACK) {
00566 record_disconnect(client);
00567 client_done(client);
00568 } else {
00569 error(0, "Got unexpected packet");
00570 if (!dumped) {
00571 octstr_dump(wtp, 0);
00572 dumped = 1;
00573 }
00574 }
00575 }
00576
00577 static void start_request(Connection *boxc, Client *client) {
00578 gw_assert(client != NULL);
00579 gw_assert(client->wsp_connected != 2);
00580 gw_assert(client->wtp_invoked == 0);
00581
00582 if (client->wsp_connected == 0) {
00583 send_invoke_connect(boxc, client);
00584 } else {
00585 send_invoke_get(boxc, client);
00586 }
00587 }
00588
00589 static long run_requests(Connection *boxc) {
00590 int requests_sent;
00591 Octstr *data;
00592 Msg *msg;
00593 int ret;
00594
00595 requests_sent = 0;
00596 requests_complete = 0;
00597
00598 while (requests_complete < max_requests) {
00599 data = conn_read_withlen(boxc);
00600 if (!data) {
00601 Client *client;
00602
00603 if (requests_sent < max_requests
00604 && (client = gwlist_extract_first(ready_clients))) {
00605 start_request(boxc, client);
00606 requests_sent++;
00607 }
00608 ret = conn_wait(boxc, TIMEOUT);
00609 if (ret < 0 || conn_eof(boxc))
00610 panic(0, "Wapbox dead.");
00611 if (ret == 1)
00612 break;
00613 } else {
00614 msg = msg_unpack(data);
00615 if (!msg) {
00616 octstr_dump(data, 0);
00617 panic(0, "Received bad data from wapbox.");
00618 }
00619 if (msg->type == wdp_datagram)
00620 handle_reply(boxc, msg);
00621 msg_destroy(msg);
00622 }
00623 octstr_destroy(data);
00624 }
00625
00626 if (requests_complete < max_requests)
00627 info(0, "Timeout. %ld requests unsatisfied.",
00628 max_requests - requests_complete);
00629
00630 return requests_complete;
00631 }
00632
00633 static void help(void) {
00634 info(0, "Usage: drive_wapbox [options...]\n");
00635 info(0, " -r requests Stop after this many; default 1.");
00636 info(0, " -c clients # of concurrent clients; default 1.");
00637 info(0, " -w wapport Port wapbox should connect to; default 30188");
00638 info(0, " -u url Use this url instead of internal http server");
00639 info(0, " -g requests Number of requests per WSP session; default 1");
00640 info(0, " -U Set the User ack flag on all WTP transactions");
00641 }
00642
00643 int main(int argc, char **argv) {
00644 int opt;
00645 struct timeval start, end;
00646 Connection *boxc;
00647 long completed;
00648 double run_time;
00649
00650 gwlib_init();
00651
00652 while ((opt = getopt(argc, argv, "hv:r:c:w:du:Ug:")) != EOF) {
00653 switch (opt) {
00654 case 'v':
00655 log_set_output_level(atoi(optarg));
00656 break;
00657
00658 case 'r':
00659 max_requests = atol(optarg);
00660 break;
00661
00662 case 'c':
00663 max_clients = atol(optarg);
00664 break;
00665
00666 case 'w':
00667 wapbox_port = atoi(optarg);
00668 break;
00669
00670 case 'u':
00671 http_url = octstr_create(optarg);
00672 break;
00673
00674 case 'U':
00675 user_ack = 1;
00676 break;
00677
00678 case 'h':
00679 help();
00680 exit(0);
00681
00682 case 'd':
00683 verbose_debug = 1;
00684 break;
00685
00686 case 'g':
00687 req_per_session = atoi(optarg);
00688 break;
00689
00690 case '?':
00691 default:
00692 error(0, "Invalid option %c", opt);
00693 help();
00694 panic(0, "Stopping.");
00695 }
00696 }
00697
00698 if (!http_url)
00699 http_port = start_http_thread();
00700 boxc = start_wapbox();
00701
00702 initialize_clients();
00703
00704 if (gettimeofday(&start, NULL) < 0)
00705 panic(errno, "gettimeofday failed");
00706 completed = run_requests(boxc);
00707 if (gettimeofday(&end, NULL) < 0)
00708 panic(errno, "gettimeofday failed");
00709
00710 conn_destroy(boxc);
00711
00712 run_time = end.tv_sec - start.tv_sec;
00713 run_time += (double) (end.tv_usec - start.tv_usec) / 1000000.0;
00714
00715
00716 if (completed < max_requests)
00717 run_time -= TIMEOUT;
00718
00719 info(0, "%ld request%s in %0.1f seconds, %0.1f requests/s.",
00720 completed, completed != 1 ? "s" : "",
00721 run_time, max_requests / run_time);
00722
00723 dying = 1;
00724 http_close_all_ports();
00725 if (!http_url)
00726 gwthread_join(http_thread_id);
00727
00728 destroy_clients();
00729 octstr_destroy(http_url);
00730
00731 gwlib_shutdown();
00732
00733 return 0;
00734 }
See file LICENSE for details about the license agreement for using,
modifying, copying or deriving work from this software.