Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
test_http_server.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2016 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Kannel Group (http://www.kannel.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  * endorse or promote products derived from this software without
29  * prior written permission. For written permission, please
30  * contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  * nor may "Kannel" appear in their name, without prior written
34  * permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group. For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * test_http.c - a simple program to test the http library, server end
59  *
60  * Lars Wirzenius
61  */
62 
63 #include <string.h>
64 #include <stdlib.h>
65 #include <unistd.h>
66 #include <signal.h>
67 
68 #include "gwlib/gwlib.h"
69 #include "gwlib/http.h"
70 
71 #define MAX_THREADS 1024
72 
75 
77 int ssl = 0; /* indicate if SSL-enabled server should be used */
78 static volatile sig_atomic_t run;
79 static List *extra_headers = NULL;
80 
81 static void split_headers(Octstr *headers, List **split)
82 {
83  long start;
84  long pos;
85 
86  *split = gwlist_create();
87  start = 0;
88  for (pos = 0; pos < octstr_len(headers); pos++) {
89  if (octstr_get_char(headers, pos) == '\n') {
90  Octstr *line;
91 
92  if (pos == start) {
93  /* Skip empty lines */
94  start = pos + 1;
95  continue;
96  }
97  line = octstr_copy(headers, start, pos - start);
98  start = pos + 1;
99  gwlist_append(*split, line);
100  }
101  }
102 }
103 
104 static void client_thread(void *arg)
105 {
107  Octstr *body, *url, *ip;
108  List *headers, *resph, *cgivars;
109  HTTPCGIVar *v;
110  Octstr *reply_body, *reply_type;
111  unsigned long n = 0;
112  int status, i;
113 
114  while (run) {
115  client = http_accept_request(port, &ip, &url, &headers, &body, &cgivars);
116 
117  n++;
118  if (client == NULL)
119  break;
120 
121  info(0, "Request for <%s> from <%s>",
122  octstr_get_cstr(url), octstr_get_cstr(ip));
123  if (verbose)
124  debug("test.http", 0, "CGI vars were");
125 
126  /*
127  * Don't use gwlist_extract() here, otherwise we don't have a chance
128  * to re-use the cgivars later on.
129  */
130  for (i = 0; i < gwlist_len(cgivars); i++) {
131  if ((v = gwlist_get(cgivars, i)) != NULL && verbose) {
132  octstr_dump(v->name, 0);
133  octstr_dump(v->value, 0);
134  }
135  }
136 
137  if (arg == NULL) {
138  reply_body = octstr_duplicate(reply_text);
139  reply_type = octstr_create("Content-Type: text/plain; "
140  "charset=\"UTF-8\"");
141  } else {
142  reply_body = octstr_duplicate(arg);
143  reply_type = octstr_create("Content-Type: text/vnd.wap.wml");
144  }
145 
146  resph = gwlist_create();
147  gwlist_append(resph, reply_type);
148 
149  status = HTTP_OK;
150 
151  /* check for special URIs and handle those */
152  if (octstr_compare(url, octstr_imm("/quit")) == 0) {
153  run = 0;
154  } else if (octstr_compare(url, octstr_imm("/whitelist")) == 0) {
155  octstr_destroy(reply_body);
156  if (whitelist != NULL) {
157  if (verbose) {
158  debug("test.http.server", 0, "we send a white list");
159  octstr_dump(whitelist, 0);
160  }
161  reply_body = octstr_duplicate(whitelist);
162  } else {
163  reply_body = octstr_imm("");
164  }
165  } else if (octstr_compare(url, octstr_imm("/blacklist")) == 0) {
166  octstr_destroy(reply_body);
167  if (blacklist != NULL) {
168  if (verbose) {
169  debug("test.http.server", 0, "we send a blacklist");
170  octstr_dump(blacklist, 0);
171  }
172  reply_body = octstr_duplicate(blacklist);
173  } else {
174  reply_body = octstr_imm("");
175  }
176  } else if (octstr_compare(url, octstr_imm("/save")) == 0) {
177  /* safe the body into a temporary file */
178  pid_t pid = getpid();
179  FILE *f = fopen(octstr_get_cstr(octstr_format("/tmp/body.%ld.%ld", pid, n)), "w");
180  octstr_print(f, body);
181  fclose(f);
182  } else if (octstr_compare(url, octstr_imm("/redirect/")) == 0) {
183  /* provide us with a HTTP 302 redirection response
184  * will return /redirect/<pid> for the location header
185  * and will return /redirect/ if cgivar loop is set to allow looping
186  */
187  Octstr *redirect_header, *scheme, *uri, *l;
188  pid_t pid = getpid();
189 
190  uri = ((l = http_cgi_variable(cgivars, "loop")) != NULL) ?
191  octstr_format("%s?loop=%s", octstr_get_cstr(url),
192  octstr_get_cstr(l)) :
193  octstr_format("%s%ld", octstr_get_cstr(url), pid);
194 
195  octstr_destroy(reply_body);
196  reply_body = octstr_imm("Here you got a redirection URL that you should follow.");
197  scheme = ssl ? octstr_imm("https://") : octstr_imm("http://");
198  redirect_header = octstr_format("Location: %s%s%s",
199  octstr_get_cstr(scheme),
200  octstr_get_cstr(http_header_value(headers, octstr_imm("Host"))),
201  octstr_get_cstr(uri));
202  gwlist_append(resph, redirect_header);
203  status = HTTP_FOUND; /* will provide 302 */
204  octstr_destroy(uri);
205  } else if (octstr_compare(url, octstr_imm("/mmsc")) == 0) {
206  /* fake a M-Send.conf PDU which is using MMSEncapsulation as body */
207  pid_t pid = getpid();
208  FILE *f;
210  octstr_destroy(reply_body);
211  reply_type = octstr_create("Content-Type: application/vnd.wap.mms-message");
212  reply_body = octstr_create("");
213  octstr_append_from_hex(reply_body,
214  "8c81" /* X-Mms-Message-Type: m-send-conf */
215  "98632d3862343300" /* X-Mms-Transaction-ID: c-8b43 */
216  "8d90" /* X-Mms-MMS-Version: 1.0 */
217  "9280" /* Response-status: Ok */
218  "8b313331373939353434393639383434313731323400"
219  ); /* Message-Id: 13179954496984417124 */
220  resph = gwlist_create();
221  gwlist_append(resph, reply_type);
222  /* safe the M-Send.req body into a temporary file */
223  f = fopen(octstr_get_cstr(octstr_format("/tmp/mms-body.%ld.%ld", pid, n)), "w");
224  octstr_print(f, body);
225  fclose(f);
226  }
227 
228  if (verbose) {
229  debug("test.http", 0, "request headers were");
230  http_header_dump(headers);
231  if (body != NULL) {
232  debug("test.http", 0, "request body was");
233  octstr_dump(body, 0);
234  }
235  }
236 
237  if (extra_headers != NULL)
238  http_header_combine(resph, extra_headers);
239 
240  /* return response to client */
241  http_send_reply(client, status, resph, reply_body);
242 
243  octstr_destroy(ip);
244  octstr_destroy(url);
245  octstr_destroy(body);
246  octstr_destroy(reply_body);
247  http_destroy_cgiargs(cgivars);
250  }
251 
252  octstr_destroy(whitelist);
253  octstr_destroy(blacklist);
254  debug("test.http", 0, "Working thread 'client_thread' terminates");
256 }
257 
258 static void help(void) {
259  info(0, "Usage: test_http_server [options...]");
260  info(0, "where options are:");
261  info(0, "-t number");
262  info(0, " set number of working threads to use (default: 1)");
263  info(0, "-v number");
264  info(0, " set log level for stderr logging (default: 0 - debug)");
265  info(0, "-l logfile");
266  info(0, " log all output to a file");
267  info(0, "-f file");
268  info(0, " use a specific file content for the response body");
269  info(0, "-r reply_text");
270  info(0, " defines which static text to use for replies");
271  info(0, "-h");
272  info(0, " provides this usage help information");
273  info(0, "-q");
274  info(0, " don't be too verbose with output");
275  info(0, "-p port");
276  info(0, " bind server to a specific port");
277  info(0, "-s");
278  info(0, " be an SSL-enabled server");
279  info(0, "-c ssl_cert");
280  info(0, " file of the SSL certificate to use");
281  info(0, "-k ssl_key");
282  info(0, " file of the SSL private key to use");
283  info(0, "-w white_list");
284  info(0, " file that is used for whitelist");
285  info(0, "-b black_list");
286  info(0, " file that is used for blacklist");
287  info(0, "-H filename");
288  info(0, " read HTTP headers from file 'filename' and add them to");
289  info(0, " the request for url 'url'");
290  info(0, "specific URIs with special functions are:");
291  info(0, " /quite - shutdown the HTTP server");
292  info(0, " /whitelist - provides the -w whitelist as response");
293  info(0, " /blacklist - provides the -b blacklist as response");
294  info(0, " /save - save a HTTP POST request body to a file /tmp/body.<pid>.<n>");
295  info(0, " where <pid> is the process id and <n> is the received request number");
296  info(0, " /redirect/ - respond with HTTP 302 and the location /redirect/<pid>");
297  info(0, " where <pid> is the process id. if a cgivar loop=<something> is given");
298  info(0, " then HTTP responses will end up in a loop.");
299  info(0, " /mmsc - fake a MMSC HTTP interface for M-Send.req PDUs send by a");
300  info(0, " mobile MMS-capable device, responds with a M-Send.conf PDU and");
301  info(0, " saves the M-Send.req body to a file /tmp/mms-body.<pid>.<n> in");
302  info(0, " MMSEncapsulation encoded binary format");
303 
304 }
305 
306 static void sigterm(int signo) {
307  run = 0;
308  debug("test.gwlib", 0, "Signal %d received, quitting.", signo);
309 }
310 
311 int main(int argc, char **argv) {
312  int i, opt, use_threads;
313  struct sigaction act;
314  char *filename;
315  Octstr *log_filename;
316  Octstr *file_contents;
317 #ifdef HAVE_LIBSSL
318  Octstr *ssl_server_cert_file = NULL;
319  Octstr *ssl_server_key_file = NULL;
320 #endif
321  char *whitelist_name;
322  char *blacklist_name;
323  int white_asked,
324  black_asked;
325  long threads[MAX_THREADS];
326  FILE *fp;
327 
328  gwlib_init();
329 
330  act.sa_handler = sigterm;
331  sigemptyset(&act.sa_mask);
332  act.sa_flags = 0;
333  sigaction(SIGTERM, &act, NULL);
334  sigaction(SIGINT, &act, NULL);
335 
336  port = 8080;
337  use_threads = 1;
338  verbose = 1;
339  run = 1;
340  filename = NULL;
341  log_filename = NULL;
342  blacklist_name = NULL;
343  whitelist_name = NULL;
344  white_asked = 0;
345  black_asked = 0;
346 
347  reply_text = octstr_create("Sent.");
348 
349  while ((opt = getopt(argc, argv, "hqv:p:t:f:l:sc:k:b:w:r:H:")) != EOF) {
350  switch (opt) {
351  case 'v':
353  break;
354 
355  case 'q':
356  verbose = 0;
357  break;
358 
359  case 'h':
360  help();
361  exit(0);
362 
363  case 'p':
364  port = atoi(optarg);
365  break;
366 
367  case 't':
368  use_threads = atoi(optarg);
369  if (use_threads > MAX_THREADS)
370  use_threads = MAX_THREADS;
371  break;
372 
373  case 'c':
374 #ifdef HAVE_LIBSSL
375  octstr_destroy(ssl_server_cert_file);
376  ssl_server_cert_file = octstr_create(optarg);
377 #endif
378  break;
379 
380  case 'k':
381 #ifdef HAVE_LIBSSL
382  octstr_destroy(ssl_server_key_file);
383  ssl_server_key_file = octstr_create(optarg);
384 #endif
385  break;
386 
387  case 's':
388 #ifdef HAVE_LIBSSL
389  ssl = 1;
390 #endif
391  break;
392 
393  case 'f':
394  filename = optarg;
395  break;
396 
397  case 'l':
398  octstr_destroy(log_filename);
399  log_filename = octstr_create(optarg);
400  break;
401 
402  case 'w':
403  whitelist_name = optarg;
404  if (whitelist_name == NULL)
405  whitelist_name = "";
406  white_asked = 1;
407  break;
408 
409  case 'b':
410  blacklist_name = optarg;
411  if (blacklist_name == NULL)
412  blacklist_name = "";
413  black_asked = 1;
414  break;
415 
416  case 'r':
417  octstr_destroy(reply_text);
418  reply_text = octstr_create(optarg);
419  break;
420 
421  case 'H': {
422  Octstr *cont;
423 
424  fp = fopen(optarg, "a");
425  if (fp == NULL)
426  panic(0, "Cannot open header text file %s", optarg);
427  cont = octstr_read_file(optarg);
428  if (cont == NULL)
429  panic(0, "Cannot read header text");
430  debug("", 0, "headers are");
431  octstr_dump(cont, 0);
432  split_headers(cont, &extra_headers);
433  fclose(fp);
434  octstr_destroy(cont);
435  break;
436  }
437 
438  case '?':
439  default:
440  error(0, "Invalid option %c", opt);
441  help();
442  panic(0, "Stopping.");
443  }
444  }
445 
446  if (log_filename != NULL) {
447  log_open(octstr_get_cstr(log_filename), GW_DEBUG, GW_NON_EXCL);
448  octstr_destroy(log_filename);
449  }
450 
451  if (filename == NULL)
452  file_contents = NULL;
453  else
454  file_contents = octstr_read_file(filename);
455 
456  if (white_asked) {
457  whitelist = octstr_read_file(whitelist_name);
458  if (whitelist == NULL)
459  panic(0, "Cannot read the whitelist");
460  }
461 
462  if (black_asked) {
463  blacklist = octstr_read_file(blacklist_name);
464  if (blacklist == NULL)
465  panic(0, "Cannot read the blacklist");
466  }
467 
468 #ifdef HAVE_LIBSSL
469  /*
470  * check if we are doing a SSL-enabled server version here
471  * load the required cert and key file
472  */
473  if (ssl) {
474  if (ssl_server_cert_file != NULL && ssl_server_key_file != NULL) {
475  conn_use_global_server_certkey_file(ssl_server_cert_file, ssl_server_key_file);
476  octstr_destroy(ssl_server_cert_file);
477  octstr_destroy(ssl_server_key_file);
478  } else {
479  panic(0, "certificate and public key need to be given!");
480  }
481  }
482 #endif
483 
484  if (http_open_port(port, ssl) == -1)
485  panic(0, "http_open_server failed");
486 
487  /*
488  * Do the real work in a separate thread so that the main
489  * thread can catch signals safely.
490  */
491  for (i = 0; i < use_threads; ++i)
492  threads[i] = gwthread_create(client_thread, file_contents);
493 
494  /* wait for all working threads */
495  for (i = 0; i < use_threads; ++i)
496  gwthread_join(threads[i]);
497 
498  octstr_destroy(reply_text);
499  gwlist_destroy(extra_headers, octstr_destroy_item);
500 
501  debug("test.http", 0, "Program exiting normally.");
502  gwlib_shutdown();
503  return 0;
504 }
505 
506 
507 
508 
void error(int err, const char *fmt,...)
Definition: log.c:612
void info(int err, const char *fmt,...)
Definition: log.c:636
#define MAX_THREADS
Definition: http.c:1998
Octstr * value
Definition: http.h:213
int verbose
int threads
Definition: fakewap.c:239
static void help(void)
void octstr_append_from_hex(Octstr *ostr, char *hex)
Definition: octstr.c:449
int ssl
void gwlist_append(List *list, void *item)
Definition: list.c:179
Octstr * whitelist
static void client_thread(void *arg)
void gwthread_join(long thread)
long gwlist_len(List *list)
Definition: list.c:166
static void client(int port)
Definition: test_udp.c:77
void * gwlist_get(List *list, long pos)
Definition: list.c:292
void http_header_combine(List *old_headers, List *new_headers)
Definition: http.c:3045
int port
int octstr_print(FILE *f, Octstr *ostr)
Definition: octstr.c:1189
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
#define octstr_copy(ostr, from, len)
Definition: octstr.h:178
Octstr * http_cgi_variable(List *list, char *name)
Definition: http.c:2813
int getopt(int argc, char **argv, char *opts)
Definition: attgetopt.c:84
void http_destroy_cgiargs(List *args)
Definition: http.c:2795
void http_send_reply(HTTPClient *client, int status, List *headers, Octstr *body)
Definition: http.c:2671
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:281
Definition: http.h:142
Octstr * name
Definition: http.h:212
void log_set_output_level(enum output_level level)
Definition: log.c:217
static List * extra_headers
HTTPClient * http_accept_request(int port, Octstr **client_ip, Octstr **url, List **headers, Octstr **body, List **cgivars)
Definition: http.c:2574
Definition: log.h:69
static void sigterm(int signo)
#define octstr_duplicate(ostr)
Definition: octstr.h:187
#define octstr_dump(ostr, level,...)
Definition: octstr.h:564
int main(int argc, char **argv)
Octstr * octstr_format(const char *fmt,...)
Definition: octstr.c:2462
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:322
char filename[FILENAME_MAX+1]
Definition: log.c:135
#define gwthread_create(func, arg)
Definition: gwthread.h:90
#define octstr_create(cstr)
Definition: octstr.h:125
void octstr_destroy_item(void *os)
Definition: octstr.c:334
int http_open_port(int port, int ssl)
Definition: http.c:2498
Octstr * octstr_read_file(const char *filename)
Definition: octstr.c:1546
int log_open(char *filename, int level, enum excl_state excl)
Definition: log.c:339
void http_close_all_ports(void)
Definition: http.c:2515
Octstr * http_header_value(List *headers, Octstr *name)
Definition: http.c:2909
long octstr_len(const Octstr *ostr)
Definition: octstr.c:340
Octstr * reply_text
static void split_headers(Octstr *headers, List **split)
Definition: octstr.c:118
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:690
char * optarg
Definition: attgetopt.c:82
#define panic
Definition: log.h:87
static List * split
Definition: test_http.c:88
void gwlib_shutdown(void)
Definition: gwlib.c:94
#define gwlist_create()
Definition: list.h:136
static volatile sig_atomic_t run
void gwlib_init(void)
Definition: gwlib.c:78
static Octstr * url
Definition: test_xmlrpc.c:84
int octstr_get_char(const Octstr *ostr, long pos)
Definition: octstr.c:404
Definition: list.c:102
static int start
Octstr * blacklist
void http_header_dump(List *headers)
Definition: http.c:3404
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:869
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
Definition: list.c:145
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.