Kannel: Open Source WAP and SMS gateway  svn-r5335
fakesmsc.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2018 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  * fakesmsc.c - simulate an SMS center, using a trivial protocol
59  *
60  * The protocol:
61  *
62  * Client sends each message on its own line (terminated with \r\n or \n).
63  * The line begins with 3 space-separated fields:
64  * sender's phone number, receiver's phone number,
65  * type of message. Type of message can be one of "text", "data", or
66  * "udh". If type == "text", the rest of the line is taken as the message.
67  * If type == "data", the next field is taken to be the text of the
68  * message in urlcoded form. Space is coded as '+'. If type == "udh",
69  * the following 2 fields are taken to be the UDH and normal portions
70  * in urlcoded form. Space is again coded as '+'.
71  * The server sends replies back in the same format.
72  *
73  * Lars Wirzenius, later edition by Kalle Marjola
74  * Largely rewritten by Uoti Urpala
75  */
76 
77 static char usage[] = "\n\
78 Usage: fakesmsc [-H host] [-r port] [-i interval] [-m max] [-z <type>] <msg> ... \n\
79 \n\
80 * 'host' and 'port' define bearerbox connection (default localhost:10000),\n\
81 * 'interval' is time in seconds (floats allowed) between generated messages,\n\
82 * 'max' is the total number sent (-1, default, means unlimited),\n\
83 * <type> bitmask of which elements to add randomized numbers for MO messages,\n\
84 * 1: src no, 2: recv no, 4: last text element,\n\
85 * where the given static elements in <msg> are used as constant prefixes,\n\
86 * <msg> is message to send, if several are given, they are sent randomly.\n\
87 \n\
88 msg format: \"sender receiver type(text|data|ucs2|udh-data|udh-text|route|dlr-mask) [udhdata|route|dlrmask] msgdata\"\n\
89 \n\
90 Type \"text\" means plaintext msgdata, \"data\" url-encoded, \"udh\" url-encoded udh+msg,\n\
91 \"utf8\" utf-8 url-encoded msgdata, \"ucs2\" unicode url-encoded msgdata \n\
92 and \"route\" means smsbox-id routed plaintext msgdata\n\
93 Examples: \n\
94 \n\
95 fakesmsc -m 1 \"123 345 udh %04udh%3f message+data+here\"\n\
96 fakesmsc -m 1 \"123 345 route smsbox1 message+data+here\"\n\
97 fakesmsc -i 0.01 -m 1000 \"123 345 text nop\" \"1 2 text another message here\"\n\
98 fakesmsc -z 7 -m 1000 \"123<rand> 345<rand> text nop <rand>\"\n\
99 \n\
100 Server replies are shown in the same message format.\n";
101 
102 #include <errno.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 <limits.h>
111 #include <signal.h>
112 
113 #include <sys/param.h>
114 
115 #include "gwlib/gwlib.h"
116 
117 #define IN_BUFSIZE 256 /* Buffer size for stdin */
118 #define IN_TIMEOUT 1 /* Timeout for stdin */
119 
120 
121 static int port = 10000;
122 static Octstr *host;
123 static long max_send = LONG_MAX;
124 static double interval = 1.0;
125 static int sigint_received;
126 static int rnd = 0;
127 
128 static void signal_handler(int signum)
129 {
130  if (signum == SIGINT)
131  sigint_received = 1;
132  else
133  panic(0, "Caught signal with no handler?!");
134 }
135 
136 
137 static void setup_signal_handlers(void)
138 {
139  struct sigaction act;
140 
141  act.sa_handler = signal_handler;
142  sigemptyset(&act.sa_mask);
143  act.sa_flags = 0;
144  sigaction(SIGINT, &act, NULL);
145 }
146 
147 
148 /* Choose a random message from a table of messages. */
149 static Octstr *choose_message(Octstr **msgs, int num_msgs)
150 {
151  /* the following doesn't give an even distribution, but who cares */
152  return msgs[gw_rand() % num_msgs];
153 }
154 
155 
156 /* Get current time, as double. */
157 static double get_current_time(void)
158 {
159  struct timezone tz;
160  struct timeval now;
161 
162  gettimeofday(&now, &tz);
163  return (double) now.tv_sec + now.tv_usec / 1e6;
164 }
165 
166 /* our arguments */
167 static int check_args(int i, int argc, char **argv)
168 {
169  if (strcmp(argv[i], "-r")==0 || strcmp(argv[i], "--port")==0)
170  port = atoi(argv[i+1]);
171  else if (!strcmp(argv[i], "-H") || !strcmp(argv[i], "--host"))
172  host = octstr_create(argv[i+1]);
173  else if (strcmp(argv[i], "-m")==0 || strcmp(argv[i], "--messages")==0) {
174  max_send = atoi(argv[i+1]);
175  if (max_send < 0)
176  max_send = LONG_MAX;
177  }
178  else if (strcmp(argv[i], "-i")==0 || strcmp(argv[i], "--interval")==0)
179  interval = atof(argv[i+1]);
180  else if (strcmp(argv[i], "-z")==0 || strcmp(argv[i], "--randomize")==0) {
181  rnd = atoi(argv[i+1]);
182  if (rnd < 0 || rnd > 7)
183  rnd = 0;
184  }
185  else {
186  panic(0, "%s", usage);
187  return 0;
188  }
189 
190  return 1;
191 }
192 
193 /* randomization of message elements */
194 static Octstr *randomize(Octstr *os)
195 {
196  Octstr *msg = octstr_create("");
197  List *words = octstr_split_words(os);
198  int i;
199 
200  /* randomize source and receiver number */
201  octstr_format_append(msg, "%S", gwlist_get(words, 0));
202  if (rnd & 0x1)
203  octstr_format_append(msg, "%d", gw_rand());
204 
205  octstr_format_append(msg, " %S", gwlist_get(words, 1));
206  if (rnd & 0x2)
207  octstr_format_append(msg, "%d", gw_rand());
208 
209  for (i = 2; i < gwlist_len(words); i++)
210  octstr_format_append(msg, " %S", gwlist_get(words, i));
211 
212  if (rnd & 0x4)
213  octstr_format_append(msg, " %d", gw_rand());
214 
215  octstr_append_char(msg, 10); /* End of line */
216 
218 
219  return msg;
220 }
221 
222 /* The main program. */
223 int main(int argc, char **argv)
224 {
226  Octstr *line;
227  Octstr **msgs;
228  int i;
229  int mptr, num_msgs;
230  long num_received, num_sent;
231  double first_received_at, last_received_at;
232  double first_sent_at, last_sent_at;
233  double start_time, end_time;
234  double delta;
235  int interactive, maxfd;
236  char *cptr;
237  char buffer[IN_BUFSIZE];
238  fd_set rset;
239  struct timeval alarm;
240  FILE *fp;
241 
242  gwlib_init();
244  host = octstr_create("localhost");
246 
247  mptr = get_and_set_debugs(argc, argv, check_args);
248  num_msgs = argc - mptr;
249 
250  interactive = 0;
251  msgs = NULL;
252  fp = NULL;
253  if (num_msgs <= 0) {
254  interactive = 1;
255  num_msgs = 0;
256  info(0, "Entering interactive mode. Type your message on the command line");
257  /* set up file pointer to stdin */
258  fp = stdin;
259  /* initialize set for select */
260  FD_ZERO(&rset);
261  } else {
262  msgs = gw_malloc(sizeof(Octstr *) * num_msgs);
263  for (i = 0; i < num_msgs; i ++) {
264  msgs[i] = octstr_create(argv[mptr + i]);
265  octstr_append_char(msgs[i], 10); /* End of line */
266  }
267  info(0, "Host %s Port %d interval %.3f max-messages %ld",
269 
270  srand((unsigned int) time(NULL));
271  }
272  info(0, "fakesmsc starting");
273  server = conn_open_tcp(host, port, NULL);
274  if (server == NULL)
275  panic(0, "Failed to open connection");
276 
277  num_sent = 0;
278  num_received = 0;
279 
280  first_received_at = 0;
281  first_sent_at = 0;
282  last_received_at = 0;
283  last_sent_at = 0;
284 
285  /* infinitely loop */
286  while (1) {
287  /* Are we on interactive mode? */
288  if (interactive == 1) {
289  /* Check if we need to clean things up beforehand */
290  if ( num_msgs > 0 ) {
291  for (i = 0; i < num_msgs; i ++)
292  octstr_destroy(msgs[i]);
293  gw_free(msgs);
294  num_msgs = 0;
295  }
296 
297  /* we want either the file pointer or timer */
298  FD_SET(fileno(fp), &rset);
299  /* get the largest file descriptor */
300  maxfd = fileno(fp) + 1;
301 
302  /* set timer to go off in 3 seconds */
303  alarm.tv_sec = IN_TIMEOUT;
304  alarm.tv_usec = 0;
305 
306  if (select(maxfd, &rset, NULL, NULL, &alarm) == -1)
307  goto over;
308  /* something went off, let's see if it's stdin */
309  if (FD_ISSET(fileno(fp), &rset)) { /* stdin is readable */
310  cptr = fgets(buffer, IN_BUFSIZE, stdin);
311  if (!cptr)
312  goto over;
313  if( strlen( cptr ) < 2 )
314  goto rcv;
315  } else { /* timer kicked in */
316  goto rcv;
317  }
318  num_msgs = 1;
319  msgs = gw_malloc(sizeof(Octstr*));
320  msgs[0] = octstr_create(cptr);
321  }
322  /* if we still have something to send as MO message */
323  if (num_sent < max_send) {
324  Octstr *os = choose_message(msgs, num_msgs);
325  Octstr *msg = rnd > 0 ? randomize(os) : os;
326 
327  if (conn_write(server, msg) == -1)
328  panic(0, "write failed");
329 
330  ++num_sent;
331  if (num_sent == max_send)
332  info(0, "fakesmsc: sent message %ld", num_sent);
333  else
334  debug("send", 0, "fakesmsc: sent message %ld", num_sent);
335 
336  if (rnd > 0)
338 
339  last_sent_at = get_current_time();
340  if (first_sent_at == 0)
341  first_sent_at = last_sent_at;
342  }
343 rcv:
344  do {
345  delta = interval * num_sent - (get_current_time() - first_sent_at);
346  if (delta < 0)
347  delta = 0;
348  if (num_sent >= max_send)
349  delta = -1;
350  conn_wait(server, delta);
352  goto over;
353 
354  /* read as much as the smsc module provides us */
355  while ((line = conn_read_line(server))) {
356  last_received_at = get_current_time();
357  if (first_received_at == 0)
358  first_received_at = last_received_at;
359  ++num_received;
360  if (num_received == max_send) {
361  info(0, "Got message %ld: <%s>", num_received,
362  octstr_get_cstr(line));
363  } else {
364  debug("receive", 0, "Got message %ld: <%s>", num_received,
365  octstr_get_cstr(line));
366  }
367  octstr_destroy(line);
368  }
369  } while (delta > 0 || num_sent >= max_send);
370  }
371 
372 over:
374 
375  /* destroy the MO messages */
376  for (i = 0; i < num_msgs; i ++)
377  octstr_destroy(msgs[i]);
378  gw_free(msgs);
379 
381 
382  info(0, "fakesmsc: %ld messages sent and %ld received", num_sent, num_received);
383  info(0, "fakesmsc: total running time %.1f seconds", end_time - start_time);
384  delta = last_sent_at - first_sent_at;
385  if (delta == 0)
386  delta = .01;
387  if (num_sent > 1)
388  info(0, "fakesmsc: from first to last sent message %.1f s, "
389  "%.1f msgs/s", delta, (num_sent - 1) / delta);
390  delta = last_received_at - first_received_at;
391  if (delta == 0)
392  delta = .01;
393  if (num_received > 1)
394  info(0, "fakesmsc: from first to last received message %.1f s, "
395  "%.1f msgs/s", delta, (num_received - 1) / delta);
396  info(0, "fakesmsc: terminating");
397 
398  return 0;
399 }
400 
Octstr * conn_read_line(Connection *conn)
Definition: conn.c:1134
void info(int err, const char *fmt,...)
Definition: log.c:672
#define IN_TIMEOUT
Definition: fakesmsc.c:118
Definition: http.c:2014
Connection * conn_open_tcp(Octstr *host, int port, Octstr *our_host)
Definition: conn.c:496
static Octstr * choose_message(Octstr **msgs, int num_msgs)
Definition: fakesmsc.c:149
static void setup_signal_handlers(void)
Definition: fakesmsc.c:137
static char usage[]
Definition: fakesmsc.c:77
long gwlist_len(List *list)
Definition: list.c:166
void * gwlist_get(List *list, long pos)
Definition: list.c:292
void octstr_append_char(Octstr *ostr, int ch)
Definition: octstr.c:1517
int main(int argc, char **argv)
Definition: fakesmsc.c:223
static Octstr * randomize(Octstr *os)
Definition: fakesmsc.c:194
static Octstr * host
Definition: fakesmsc.c:122
static double interval
Definition: fakesmsc.c:124
static void signal_handler(int signum)
Definition: fakesmsc.c:128
int conn_eof(Connection *conn)
Definition: conn.c:705
static long max_send
Definition: fakesmsc.c:123
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
static time_t start_time
Definition: bearerbox.c:148
int conn_write(Connection *conn, Octstr *data)
Definition: conn.c:1051
int num_sent
Definition: fakewap.c:240
void conn_destroy(Connection *conn)
Definition: conn.c:627
List * octstr_split_words(const Octstr *ostr)
Definition: octstr.c:1602
time_t end_time
Definition: fakewap.c:241
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:324
#define octstr_create(cstr)
Definition: octstr.h:125
void octstr_destroy_item(void *os)
Definition: octstr.c:336
static int check_args(int i, int argc, char **argv)
Definition: fakesmsc.c:167
Definition: octstr.c:118
int conn_wait(Connection *conn, double seconds)
Definition: conn.c:904
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:726
#define panic
Definition: log.h:87
#define IN_BUFSIZE
Definition: fakesmsc.c:117
void octstr_format_append(Octstr *os, const char *fmt,...)
Definition: octstr.c:2507
static void server(int lport, int pport)
static int rnd
Definition: fakesmsc.c:126
void gwlib_init(void)
Definition: gwlib.c:78
int conn_error(Connection *conn)
Definition: conn.c:716
static int sigint_received
Definition: fakesmsc.c:125
int get_and_set_debugs(int argc, char **argv, int(*find_own)(int index, int argc, char **argv))
Definition: utils.c:626
int gw_rand(void)
Definition: protected.c:174
Definition: list.c:102
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
static double get_current_time(void)
Definition: fakesmsc.c:157
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.