[PATCH] enhancing the fakesmsc to support failure and buffered msgs in DLRs
Guillaume Cottenceau <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi,
Okay, here's a not trivial patch.
I really hope one of the commiters can have a look at it, because
I spent some time writing it, debugging it, thoroughly testing it
including under high network load, documenting it, I made special
caution to try and follow doc/CodingStyle, and I will spend time
now to explain what it does and why the important modifications
were necessary.
1. Motivation
We use Postgresql databases; and current Kannel performance when
DLR are enabled, using sdb/postgres, drops terribly. In order to
decide for the best approach (make sdb use persistent connection
in the postgres driver? write a native postgres driver in Kannel?
write dbpool support for sdb in Kannel? a combination of some?),
I decided I needed the fake SMSC to support more than just always
success DLRs (e.g. failures and buffered messages), so that we
can simulate a more real situation.
2. Fake SMSC protocol background
The fake SMSC and Kannel communicate via a (local) TCP
connection. Messages are made of one line ("\n" terminated),
containing space separated fields indicating the parameters of
the message. They are similar in both ways (MO fakesmsc->kannel
and MT kannel->fakesmsc). No answer is needed nor performed,
and in particular for MT messages Kannel will always report
DLR_SUCCESS when a DLR was requested.
3. Addition of more DLR statuses and impact on the protocol
Given that, adding failed messages, and even SMSC failures, would
be easy.
However, adding buffered messages is more complicated, since when
we eventually report the success (or failure) of the message, we
need something to uniquely identify the same message. Hence, the
need to make the protocol more complicated, by sending a reply
back to Kannel for each MT message, containing this ID (routinely
called a timestamp in Kannel).
This also requires that the timestamp used for the DLR inserted
when calling `dlr_add' is really a SMSC-provided timestamp, and
explains the need to completely change the location of `dlr_add'
in `gw/smsc/smsc_fake.c'.
Since the fake SMSC might have decided to send a MO message at
the same time Kannel sent a MT message requesting a DLR, we need
a synchronization mechanism, recognizing if a message from the
fake SMSC is a reply or a MO message; and in case we're waiting
for a reply and actually receive a MO message, we need to fill a
local buffer with this MO message and loop again until we receive
this damn reply. At next loop, before examining if MO messages
were received, this buffer is flushed. This is another important
point of the proposed implementation, explaining the need for
`List *in_buffer' added in `main_connection_loop', and why the
algorithm inside this function has complicated a bit.
4. Changes in fake SMSC
Changes in the fake SMSC are somewhat parallel to above described
changes in Kannel, of course. I added the possibility of
specifying the "dlr" keyword before "type" in the message, so
that the fake SMSC knows if it should reply with a DLR
(otherwise, it will also reply, so that the protocol and
synchronization stay the same, but always with the success
value). We can control on the commandline, with three new
parameters, the ratio of messages that should be replied to with
delivery failure, smsc failure, or buffered message. Each
buffered message will be kept in a list, a random value from 0 to
10 will decide in how many seconds the final success delivery
status should be delivered. Both of these (0..10 seconds; final
delivery status == success) are hardcoded, because I didn't think
it would be useful to change them, but of course these can be
parameterized with commandline in the future if needed.
5. Performance
Ok, that should be it for a basic overview of the changes. Now,
here are the results I could measure on my machine, which is a
fairly old Dell with a p3-500 and IDE disks. I test with
bearerbox, smsbox, fakesmsc, dlr receiver, get-url receiver, and
the database on the same machine. The test basically is to send
as many MO requests as possible with fakesmsc on the commandline,
and measure how many answers (MT messages) I can get on a
maximum. Please note that the sms-service is using a get-url.
Without any DLR, with this new protocol (which adds one network
transmission per MT message, the answer), the maximum is 35
messages per second.
Requesting a DLR, with 0.1 (that is 10%) of failed messages and
0.3 (that's 30%) of buffered messages (each one adding more
network transfers and database operations), using sdb/postgres,
that drops down to 7.5 messages per second. I can hear the
harddisk going a bit crazy :).
I added the support for persistent connections in the postgres
SDB driver (this has already been accepted by SDB author as his
0.5.3 release) (the Kannel documentation is incorrect about SDB
and persistent connections, I will submit a fix), and my figures
went up to 17 messages per second, which is already really nice I
think, if you consider the mandatory overhead of DLRs in terms of
additional network transfers and database operations.
If anyone has figures with/without DLR's using the native mysql
database, I'd be interested.
Here's the patch. Please consider it. Thanks!
--
Guillaume Cottenceau
fakesmsc-add-fail-and-buffered-messages.diff
(text/x-patch, 23.5 KB)
Index: doc/userguide/userguide.xml
===================================================================
RCS file: /home/cvs/gateway/doc/userguide/userguide.xml,v
retrieving revision 1.269
diff -u -r1.269 userguide.xml
--- doc/userguide/userguide.xml 21 Apr 2004 10:52:34 -0000 1.269
+++ doc/userguide/userguide.xml 23 Apr 2004 15:54:54 -0000
@@ -7843,7 +7843,8 @@
<title>Starting fake SMS center</title>
<para>Each fakesmsc is started from command line, with all sent
- messages after command name. If any options are used (see below),
+ messages (that is, fake MO - Mobile Originated - messages)
+ after command name. If any options are used (see below),
they are put between the command and the messages. The usage is as
follows:</para>
@@ -7896,6 +7897,36 @@
</sect3>
<sect3>
+<title>DLR support</title>
+<para>
+ The fake SMSC supports DLR (delivery reports). By default,
+ when a DLR is requested for a MT (Mobile Terminated) message,
+ the fakesmsc will reply with a short message
+ telling Kannel that the delivery was successful.
+</para>
+<para>
+ If you want to simulate delivery failure, SMSC failure, or
+ buffered messages, use the appropriate command line options
+ as described in next section.
+</para>
+<para>
+ Details about special DLR statuses:
+ <itemizedlist>
+ <listitem><para>Kannel will disconnect the fake
+ SMSC when receiving an SMSC failure notification (you may
+ then reconnect).</para>
+ </listitem>
+ <listitem><para>When the fake SMSC sends a notification of
+ buffered message, it will wait for a minimum random delay
+ of 0 to 10 seconds before sending the final successful
+ delivery notification.</para>
+ </listitem>
+ </itemizedlist>
+</para>
+
+</sect3>
+
+<sect3>
<title>Fakesmsc command line options</title>
<para>Fake SMS center can be started with various optional command
@@ -7941,6 +7972,37 @@
Using 0 can be useful to listen for messages sent via other
channels.
</entry></row>
+
+ <row><entry><literal>-f</literal></entry>
+ <entry><emphasis>smsc-fail-ratio</emphasis></entry>
+ <entry valign="bottom">
+ Ratio (a number between 0 and 1) of SMSC failure for answers
+ to MT messages requesting a DLR. Notice that Kannel will
+ <emphasis>disconnect</emphasis> the fake SMSC when it receives
+ such an asnwer.
+ </entry></row>
+
+ <row><entry><literal>-df</literal></entry>
+ <entry><emphasis>delivery-fail-ratio</emphasis></entry>
+ <entry valign="bottom">
+ Ratio (a number between 0 and 1) of delivery failure for answers
+ to MT messages requesting a DLR. This is just like an MT message
+ that would not be delivered for any good reason by a real SMSC
+ (e.g. when there is no such mobile subscriber at that operator, etc)
+ </entry></row>
+
+ <row><entry><literal>-b</literal></entry>
+ <entry><emphasis>buffered-messages-ratio</emphasis></entry>
+ <entry valign="bottom">
+ Ratio (a number between 0 and 1) of buffered messages for answers
+ to MT messages requesting a DLR. In that case, a unique timestamp
+ is sent back to Kannel for use in the DLR database, and the fake
+ SMSC keeps track of that timestamp as well; then, after a random
+ delay between 0 to 10 seconds, the fake SMSC sends another
+ message indicating that the message at the said timestamp was
+ finally delivered.
+ </entry></row>
+
</tbody>
</tgroup>
</table>
Index: gw/smsc/smsc_fake.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_fake.c,v
retrieving revision 1.13
diff -u -r1.13 smsc_fake.c
--- gw/smsc/smsc_fake.c 16 Feb 2004 19:41:26 -0000 1.13
+++ gw/smsc/smsc_fake.c 23 Apr 2004 15:54:54 -0000
@@ -58,6 +58,7 @@
* smsc_fake.c - interface to fakesmsc.c
*
* Uoti Urpala 2001
+ * Some DLR additions by Guillaume Cottenceau (failure, smsc failure, buffered)
*/
/* Doesn't support multi-send
@@ -120,6 +121,8 @@
line = octstr_duplicate(msg->sms.sender);
octstr_append_char(line, ' ');
octstr_append(line, msg->sms.receiver);
+ if (DLR_IS_ENABLED_DEVICE(msg->sms.dlr_mask))
+ octstr_append(line, octstr_imm(" dlr"));
if (octstr_len(msg->sms.udhdata)) {
octstr_append(line, octstr_imm(" udh "));
msgdata = octstr_duplicate(msg->sms.udhdata);
@@ -160,6 +163,41 @@
return 1;
}
+/* parse an answer message from the fake SMSC and fill up
+ * the timestamp (ts) and dlr status (dlrstat) accordingly
+ */
+static void dlr_discover_status(Octstr *smsc_msg, Octstr **ts, int * dlrstat)
+{
+ *ts = NULL;
+
+ if (octstr_search(smsc_msg, octstr_imm("ACKTS "), 0) == 0) {
+ *ts = octstr_copy(smsc_msg, strlen("ACKTS "), LONG_MAX);
+ *dlrstat = DLR_SUCCESS;
+ } else if (octstr_str_compare(smsc_msg, "ACK") == 0)
+ *dlrstat = DLR_SUCCESS;
+ else if (octstr_str_compare(smsc_msg, "FAIL") == 0)
+ *dlrstat = DLR_FAIL;
+ else if (octstr_search(smsc_msg, octstr_imm("BUFFERED "), 0) == 0) {
+ *ts = octstr_copy(smsc_msg, strlen("BUFFERED "), LONG_MAX);
+ *dlrstat = DLR_BUFFERED;
+ } else
+ *dlrstat = DLR_SMSC_FAIL;
+}
+
+/* call necessary bearerbox functions to update a DLR status */
+static void dlr_update_status(SMSCConn *conn, Octstr *ts,
+ Octstr *receiver, int dlrstat)
+{
+ debug("dlr", 0, "smsc_fake: DLR for TS %s and receiver %s is now %d",
+ octstr_get_cstr(ts), octstr_get_cstr(receiver), dlrstat);
+
+ Msg *dlrmsg = dlr_find(conn->id, ts, receiver, dlrstat);
+
+ if (dlrmsg != NULL)
+ bb_smscconn_receive(conn, dlrmsg);
+ else
+ error(0, "smsc_fake: got DLR but could not find message or was not interested in it");
+}
static void msg_to_bb(SMSCConn *conn, Octstr *line)
{
@@ -216,7 +254,22 @@
octstr_destroy(tmp);
msg->sms.msgdata = octstr_copy(line, p2 + 1, LONG_MAX);
}
- else
+ else if (!octstr_compare(type, octstr_imm("dlr"))) {
+ Octstr *ts;
+ int dlrstat;
+ Octstr *dlrmsg = octstr_copy(line, p + 1, LONG_MAX);
+ dlr_discover_status(dlrmsg, &ts, &dlrstat);
+ if (ts != NULL) {
+ dlr_update_status(conn, ts, msg->sms.receiver, dlrstat);
+ octstr_destroy(ts);
+ } else
+ error(0, "smsc_fake: protocol error, got dlr message "
+ "but no timestamp!");
+ octstr_destroy(dlrmsg);
+ octstr_destroy(line);
+ octstr_destroy(type);
+ return;
+ } else
goto error;
octstr_destroy(line);
octstr_destroy(type);
@@ -228,7 +281,8 @@
bb_smscconn_receive(conn, msg);
return;
error:
- warning(0, "smsc_fake: invalid message syntax from client, ignored");
+ warning(0, "smsc_fake: invalid message syntax from client, ignored: %s",
+ octstr_get_cstr(line));
msg_destroy(msg);
octstr_destroy(line);
octstr_destroy(type);
@@ -242,47 +296,89 @@
Octstr *line;
Msg *msg;
double delay = 0;
+ List *in_buffer;
if (conn->throughput) {
delay = 1.0 / conn->throughput;
}
+ in_buffer = list_create();
+
while (1) {
- while (!conn->is_stopped && !privdata->shutdown &&
- (line = conn_read_line(client)))
+ while (!conn->is_stopped && !privdata->shutdown
+ && ((line = list_extract_first(in_buffer)) != NULL
+ || (line = conn_read_line(client)) != NULL))
msg_to_bb(conn, line);
+
if (conn_error(client))
goto error;
if (conn_eof(client))
goto eof;
while ((msg = list_extract_first(privdata->outgoing_queue)) != NULL) {
+
if (sms_to_client(client, msg) == 1) {
+ Octstr *answer, *answer_real = NULL;
+
+ while (answer_real == NULL) {
+ /* read answer */
+ if ((answer = conn_read_line(client)) == NULL) {
+ do {
+ conn_wait(client, -1);
+ if (conn_error(client))
+ goto error;
+ if (conn_eof(client))
+ goto eof;
+ answer = conn_read_line(client);
+ } while (answer == NULL);
+ }
+
+ /* Verify we're really receiving an answer to the
+ * sent message; if SMSC was actually sending a
+ * MO message at the same time, keep it for further
+ * use, and loop again for reading the real answer
+ */
+ if (octstr_search(answer, octstr_imm("ANSWER "), 0) == 0)
+ answer_real =
+ octstr_copy(answer, strlen("ANSWER "), LONG_MAX);
+ else
+ list_produce(in_buffer, answer);
+ }
+
+ octstr_destroy(answer);
+
+ if (octstr_str_compare(answer_real, "SMSC_FAIL") == 0)
+ goto smsc_failure;
/*
* Now look for the DLR entry and pass it to the upper layer.
* There is no *real* DLR awaited from the fakesmsc.
*/
if (DLR_IS_ENABLED_DEVICE(msg->sms.dlr_mask)) {
- Msg *dlrmsg;
- Octstr *tmp;
- int dlrstat = DLR_SUCCESS;
- char id[UUID_STR_LEN + 1];
-
- uuid_unparse(msg->sms.id, id);
- tmp = octstr_create(id);
- dlrmsg = dlr_find(conn->id,
- tmp, /* smsc message id */
- msg->sms.receiver, /* destination */
- dlrstat);
- if (dlrmsg != NULL) {
- bb_smscconn_receive(conn, dlrmsg);
- } else {
- error(0,"smsc_fale: got DLR but could not find message or was not interested in it");
+ Octstr *ts;
+ int dlrstat;
+
+ dlr_discover_status(answer_real, &ts, &dlrstat);
+
+ /*
+ * Insert a DLR if desired. Note that if message is not
+ * buffered, it will be removed right after, thus
+ * this is only useful for debugging purpose.
+ */
+ if (!ts) {
+ char id[UUID_STR_LEN + 1];
+ uuid_unparse(msg->sms.id, id);
+ ts = octstr_format("%s", id);
}
- octstr_destroy(tmp);
+ dlr_add(conn->id, ts, msg);
+
+ dlr_update_status(conn, ts, msg->sms.receiver, dlrstat);
+
+ octstr_destroy(ts);
}
+ octstr_destroy(answer_real);
+
/*
* Actually no quarantee of it having been really sent,
* but I suppose that doesn't matter since this interface
@@ -291,6 +387,7 @@
bb_smscconn_sent(conn, msg, NULL);
} else {
+ smsc_failure:
bb_smscconn_send_failed(conn, msg,
SMSCCONN_FAILED_REJECTED, octstr_create("REJECTED"));
goto error;
@@ -304,9 +401,15 @@
if (privdata->shutdown) {
debug("bb.sms", 0, "smsc_fake shutting down, closing client socket");
conn_destroy(client);
+ list_destroy(in_buffer, NULL);
return;
}
- conn_wait(client, -1);
+
+ if ((line = conn_read_line(client)) != NULL)
+ list_produce(in_buffer, line);
+ else
+ conn_wait(client, -1);
+
if (conn_error(client))
goto error;
if (conn_eof(client))
@@ -315,10 +418,12 @@
error:
info(0, "IO error to fakesmsc client. Closing connection.");
conn_destroy(client);
+ list_destroy(in_buffer, NULL);
return;
eof:
info(0, "EOF from fakesmsc client. Closing connection.");
conn_destroy(client);
+ list_destroy(in_buffer, NULL);
return;
}
@@ -423,19 +528,6 @@
copy = msg_duplicate(sms);
list_produce(privdata->outgoing_queue, copy);
-
- /*
- * Send DLR if desired, which means first add the DLR entry
- * and then later find it and remove it
- */
- if (DLR_IS_ENABLED_DEVICE(sms->sms.dlr_mask)) {
- Octstr *tmp;
- char id[UUID_STR_LEN + 1];
- uuid_unparse(sms->sms.id, id);
- tmp = octstr_format("%s", id);
- dlr_add(conn->id, tmp, sms);
- octstr_destroy(tmp);
- }
gwthread_wakeup(privdata->connection_thread);
Index: gwlib/protected.c
===================================================================
RCS file: /home/cvs/gateway/gwlib/protected.c,v
retrieving revision 1.16
diff -u -r1.16 protected.c
--- gwlib/protected.c 6 Mar 2004 10:38:20 -0000 1.16
+++ gwlib/protected.c 23 Apr 2004 15:54:55 -0000
@@ -72,6 +72,7 @@
#undef localtime
#undef gmtime
#undef rand
+#undef drand48
#undef gethostbyname
#undef mktime
@@ -166,6 +167,16 @@
lock(RAND);
ret = rand();
+ unlock(RAND);
+ return ret;
+}
+
+double gw_drand48(void)
+{
+ double ret;
+
+ lock(RAND);
+ ret = drand48();
unlock(RAND);
return ret;
}
Index: gwlib/protected.h
===================================================================
RCS file: /home/cvs/gateway/gwlib/protected.h,v
retrieving revision 1.11
diff -u -r1.11 protected.h
--- gwlib/protected.h 5 Mar 2004 14:37:20 -0000 1.11
+++ gwlib/protected.h 23 Apr 2004 15:54:55 -0000
@@ -77,6 +77,7 @@
struct tm gw_gmtime(time_t t);
time_t gw_mktime(struct tm *tm);
int gw_rand(void);
+double gw_drand48(void);
int gw_gethostbyname(struct hostent *ret, const char *name, char **buff);
/*
@@ -94,6 +95,9 @@
#undef rand
#define rand() do_not_use_rand_directly
+
+#undef drand48
+#define drand48() do_not_use_drand48_directly
#undef gethostbyname
#define gethostbyname() do_not_use_gethostbyname_directly
Index: test/fakesmsc.c
===================================================================
RCS file: /home/cvs/gateway/test/fakesmsc.c,v
retrieving revision 1.24
diff -u -r1.24 fakesmsc.c
--- test/fakesmsc.c 16 Feb 2004 19:41:26 -0000 1.24
+++ test/fakesmsc.c 23 Apr 2004 15:54:55 -0000
@@ -68,17 +68,35 @@
* message in urlcoded form. Space is coded as '+'. If type == "udh",
* the following 2 fields are taken to be the UDH and normal portions
* in urlcoded form. Space is again coded as '+'.
+ * If a DLR is requested, the string "dlr" is to be added before the type.
+ *
* The server sends replies back in the same format.
*
+ * For each message sent by the client, the server replies with one
+ * line beginning with ANSWER (to be able to separate from answers and
+ * requests sent in the mean-time over the network) specifying the
+ * delivery status: ACK, FAIL, SMSC_FAIL or BUFFERED <ts> where ts
+ * is the timestamp for the message.
+ *
+ * Apart from normal messages, the server also sends final delivery
+ * acknowledgement, in the space-separated fields format described
+ * above, with type == "dlr", the rest of the line containing
+ * ACKTS, a space, and finally the timestamp.
+ *
* Lars Wirzenius, later edition by Kalle Marjola
* Largely rewritten by Uoti Urpala
+ * Some DLR additions by Guillaume Cottenceau (failure, smsc failure, buffered)
*/
static char usage[] = "\n\
-Usage: fakesmsc [-H host] [-p port] [-i interval] [-m max] [-r <type>] <msg> ... \n\
+Usage: fakesmsc [-H host] [-p port] [-i interval] [-m max] [-f smsc-fail-ratio] [-df delivery-fail-ratio] [-b buffered-messages-ratio] [-r <type>] <msg> ... \n\
\n\
* 'host' and 'port' define bearerbox connection (default localhost:10000),\n\
* 'interval' is time in seconds (floats allowed) between generated messages,\n\
+* failure and buffered messages ratios are float in 0..1 range,\n\
+* (buffered messages are scheduled for delivery in a random 0..30 secs)\n\
+* (note: client will need to specify dlr-mask and dlr-url or else\n\
+* DLRs won't be requested)\n\
* 'max' is the total number sent (-1, default, means unlimited),\n\
* <type> bitmask of which elements to add randomized numbers for MO messages,\n\
* 1: src no, 2: recv no, 4: last text element,\n\
@@ -95,6 +113,7 @@
fakesmsc -m 1 \"123 345 route smsbox1 message+data+here\"\n\
fakesmsc -i 0.01 -m 1000 \"123 345 text nop\" \"1 2 text another message here\"\n\
fakesmsc -r 7 -m 1000 \"123<rand> 345<rand> text nop <rand>\"\n\
+fakesmsc -m 10 -b 0.5 \"123 345 text nop\"\n\
\n\
Server replies are shown in the same message format.\n";
@@ -117,9 +136,20 @@
static Octstr *host;
static long max_send = LONG_MAX;
static double interval = 1.0;
+static double fail_ratio = 0;
+static double smsc_fail_ratio = 0;
+static double buffered_ratio = 0;
static int sigint_received;
static int rnd = 0;
+struct bufferedmsg {
+ double delivery_time;
+ Octstr *ts;
+ Octstr *contents;
+};
+#define MAX_BUFFERED 200
+static struct bufferedmsg buffered_msgs[MAX_BUFFERED];
+
static void signal_handler(int signum)
{
if (signum == SIGINT)
@@ -158,6 +188,58 @@
return (double) now.tv_sec + now.tv_usec / 1e6;
}
+/* Get current date. */
+static Octstr* get_current_date(void)
+{
+ struct tm lt;
+ char buf[50];
+ double time = get_current_time();
+ time_t seconds = (time_t)time;
+ lt = gw_localtime(seconds);
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", <);
+ return octstr_format("%s.%03d", buf, (int)(1000 * (time-seconds)));
+}
+
+
+static Octstr* add_buffered(Octstr *msg, int num_received)
+{
+ Octstr *preamble, *date, *ts;
+ long p;
+ int i = 0, delay;
+
+ while (buffered_msgs[i].contents != NULL && i < MAX_BUFFERED)
+ i++;
+
+ if (i == MAX_BUFFERED) {
+ warning(0, "fakesmsc: maximum number of buffered messages reached");
+ return NULL;
+ }
+
+ p = octstr_search_char(msg, ' ', 0);
+ if (p == -1)
+ panic(0, "fakesmsc: internal error in buffered msg");
+ p = octstr_search_char(msg, ' ', p + 1);
+ if (p == -1)
+ panic(0, "fakesmsc: internal error in buffered msg");
+ preamble = octstr_copy(msg, 0, p);
+
+ date = get_current_date();
+ delay = gw_rand() % 10;
+ ts = octstr_format("[%S] %d [%d]", date, num_received, delay);
+ octstr_destroy(date);
+
+ buffered_msgs[i].ts = ts;
+ buffered_msgs[i].contents = octstr_format("%S dlr ACKTS %S\n",
+ preamble, ts);
+ buffered_msgs[i].delivery_time = get_current_time() + delay;
+
+ debug("dlr", 0, "Adding buffered msg with ts <%s> (delay %d seconds)",
+ octstr_get_cstr(ts), delay);
+
+ octstr_destroy(preamble);
+ return ts;
+}
+
/* our arguments */
static int check_args(int i, int argc, char **argv)
{
@@ -170,6 +252,24 @@
if (max_send < 0)
max_send = LONG_MAX;
}
+ else if (strcmp(argv[i], "-f")==0
+ || strcmp(argv[i], "--smsc-fail-ratio")==0) {
+ smsc_fail_ratio = atof(argv[i+1]);
+ if (smsc_fail_ratio < 0 || smsc_fail_ratio > 1)
+ smsc_fail_ratio = 0;
+ }
+ else if (strcmp(argv[i], "-df")==0
+ || strcmp(argv[i], "--delivery-fail-ratio")==0) {
+ fail_ratio = atof(argv[i+1]);
+ if (fail_ratio < 0 || fail_ratio > 1)
+ fail_ratio = 0;
+ }
+ else if (strcmp(argv[i], "-b")==0
+ || strcmp(argv[i], "--buffered-messages-ratio")==0) {
+ buffered_ratio = atof(argv[i+1]);
+ if (buffered_ratio < 0 || buffered_ratio > 1)
+ buffered_ratio = 0;
+ }
else if (strcmp(argv[i], "-i")==0 || strcmp(argv[i], "--interval")==0)
interval = atof(argv[i+1]);
else if (strcmp(argv[i], "-r")==0 || strcmp(argv[i], "--randomize")==0) {
@@ -228,6 +328,9 @@
double first_sent_at, last_sent_at;
double start_time, end_time;
double delta;
+ Octstr *answer_ack;
+ Octstr *answer_fail;
+ Octstr *answer_smsc_fail;
gwlib_init();
setup_signal_handlers();
@@ -239,6 +342,13 @@
if (num_msgs <= 0)
panic(0, "%s", usage);
+ answer_ack = octstr_create("ANSWER ACK\n");
+ answer_fail = octstr_create("ANSWER FAIL\n");
+ answer_smsc_fail = octstr_create("ANSWER SMSC_FAIL\n");
+
+ for (i = 0; i < MAX_BUFFERED; i ++)
+ buffered_msgs[i].contents = NULL;
+
/* pre-allocate array */
msgs = gw_malloc(sizeof(Octstr *) * num_msgs);
for (i = 0; i < num_msgs; i ++) {
@@ -296,12 +406,31 @@
delta = 0;
if (num_sent >= max_send)
delta = -1;
+
+ for (i = 0; i < MAX_BUFFERED; i++) {
+ if (buffered_msgs[i].contents != NULL) {
+ if (buffered_msgs[i].delivery_time < get_current_time()) {
+ debug("dlr", 0,
+ "fakesmsc: delivering ack for buffered msg: %s",
+ octstr_get_cstr(buffered_msgs[i].ts));
+ if (conn_write(server, buffered_msgs[i].contents) ==-1)
+ panic(0, "write failed");
+ octstr_destroy(buffered_msgs[i].ts);
+ octstr_destroy(buffered_msgs[i].contents);
+ buffered_msgs[i].contents = NULL;
+ }
+ }
+ }
+
conn_wait(server, delta);
if (conn_error(server) || conn_eof(server) || sigint_received)
goto over;
/* read as much as the smsc module provides us */
while ((line = conn_read_line(server))) {
+ Octstr *answer = NULL;
+ long p;
+
last_received_at = get_current_time();
if (first_received_at == 0)
first_received_at = last_received_at;
@@ -313,7 +442,35 @@
debug("receive", 0, "Got message %ld: <%s>", num_received,
octstr_get_cstr(line));
}
+
+ p = octstr_search_char(line, ' ', 0);
+ if (p == -1)
+ panic(0, "fakesmsc: protocol error in received msg");
+ p = octstr_search_char(line, ' ', p + 1);
+ if (p == -1)
+ panic(0, "fakesmsc: protocol error in received msg");
+ if (octstr_search(line, octstr_imm("dlr"), p + 1) != -1) {
+ if (gw_drand48() < smsc_fail_ratio) {
+ debug("dlr", 0, "Simulating SMSC failure");
+ answer = octstr_duplicate(answer_smsc_fail);
+ } else if (gw_drand48() < fail_ratio) {
+ debug("dlr", 0, "Simulating delivery failure");
+ answer = octstr_duplicate(answer_fail);
+ } else if (gw_drand48() < buffered_ratio) {
+ Octstr *ts = add_buffered(line, num_received);
+ if (ts)
+ answer = octstr_format("ANSWER BUFFERED %S\n", ts);
+ }
+ }
+
+ if (answer == NULL)
+ answer = octstr_duplicate(answer_ack);
octstr_destroy(line);
+
+ if (conn_write(server, answer) == -1)
+ panic(0, "answer write failed");
+
+ octstr_destroy(answer);
}
} while (delta > 0 || num_sent >= max_send);