Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

smsc_fake.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * smsc_fake.c - interface to fakesmsc.c
00059  *
00060  * Uoti Urpala 2001
00061  */
00062 
00063 /* Doesn't support multi-send
00064  * Doesn't warn about unrecognized configuration variables */
00065 
00066 #include <sys/types.h>
00067 #include <sys/socket.h>
00068 #include <unistd.h>
00069 #include <errno.h>
00070 #include <time.h>
00071 #include <limits.h>
00072 
00073 #include "gwlib/gwlib.h"
00074 #include "smscconn.h"
00075 #include "smscconn_p.h"
00076 #include "bb_smscconn_cb.h"
00077 #include "msg.h"
00078 #include "sms.h"
00079 #include "dlr.h"
00080 
00081 typedef struct privdata {
00082     List    *outgoing_queue;
00083     long    connection_thread;
00084     int     shutdown; /* Signal to the connection thread to shut down */
00085     int     listening_socket; /* File descriptor */
00086     int     port;         /* Port number to listen */
00087     Octstr  *allow_ip, *deny_ip;
00088 } PrivData;
00089 
00090 
00091 static int fake_open_connection(SMSCConn *conn, PrivData *privdata)
00092 {
00093     int s;
00094 
00095     if ((s = make_server_socket(privdata->port, (conn->our_host ? octstr_get_cstr(conn->our_host) : NULL))) == -1) {
00096         error(0, "smsc_fake: could not create listening socket in port %d",
00097               privdata->port);
00098         return -1;
00099     }
00100     if (socket_set_blocking(s, 0) == -1) {
00101         error(0, "smsc_fake: couldn't make listening socket port %d non-blocking",
00102               privdata->port);
00103         return -1;
00104     }
00105     privdata->listening_socket = s;
00106     return 0;
00107 }
00108 
00109 
00110 static int sms_to_client(Connection *client, Msg *msg)
00111 {
00112     Octstr *line;
00113     Octstr *msgdata = NULL; /* NULL to allow octstr_destroy */
00114     char *contents;
00115     int len;
00116 
00117     debug("bb.sms", 0, "smsc_fake: sending message to client");
00118 //    msg_dump(msg, 0);
00119 
00120     line = octstr_duplicate(msg->sms.sender);
00121     octstr_append_char(line, ' ');
00122     octstr_append(line, msg->sms.receiver);
00123     if (octstr_len(msg->sms.udhdata)) {
00124         octstr_append(line, octstr_imm(" udh "));
00125         msgdata = octstr_duplicate(msg->sms.udhdata);
00126         octstr_url_encode(msgdata);
00127         octstr_append(line, msgdata);
00128         octstr_destroy(msgdata);
00129         octstr_append_char(line, ' ');
00130         msgdata = octstr_duplicate(msg->sms.msgdata);
00131         octstr_url_encode(msgdata);
00132         octstr_append(line, msgdata);
00133     } else {
00134         contents = octstr_get_cstr(msg->sms.msgdata);
00135         len = octstr_len(msg->sms.msgdata);
00136         while (len > 0) {
00137             len--;
00138             if (contents[len] < 32 || contents[len] > 126) {
00139                 octstr_append(line, octstr_imm(" data "));
00140                 msgdata = octstr_duplicate(msg->sms.msgdata);
00141                 octstr_url_encode(msgdata);
00142                 octstr_append(line, msgdata);
00143                 goto notelse; /* C lacks "else" clause for while loops */
00144             }
00145         }
00146         octstr_append(line, octstr_imm(" text "));
00147         octstr_append(line, msg->sms.msgdata);
00148     }
00149 
00150 notelse:
00151     octstr_append_char(line, 10);
00152 
00153     if (conn_write(client, line) == -1) {
00154         octstr_destroy(msgdata);
00155         octstr_destroy(line);
00156         return -1;
00157     }
00158     octstr_destroy(msgdata);
00159     octstr_destroy(line);
00160     return 1;
00161 }
00162 
00163 
00164 static void msg_to_bb(SMSCConn *conn, Octstr *line)
00165 {
00166     long p, p2;
00167     Msg *msg;
00168     Octstr *type = NULL; /* might be destroyed after error before created */
00169 
00170     msg = msg_create(sms);
00171     p = octstr_search_char(line, ' ', 0);
00172     if (p == -1)
00173         goto error;
00174     msg->sms.sender = octstr_copy(line, 0, p);
00175     p2 = octstr_search_char(line, ' ', p + 1);
00176     if (p2 == -1)
00177         goto error;
00178     msg->sms.receiver = octstr_copy(line, p + 1, p2 - p - 1);
00179     p = octstr_search_char(line, ' ', p2 + 1);
00180     if (p == -1)
00181         goto error;
00182     type = octstr_copy(line, p2 + 1, p - p2 - 1);
00183     if (!octstr_compare(type, octstr_imm("text")))
00184         msg->sms.msgdata = octstr_copy(line, p + 1, LONG_MAX);
00185     else if (!octstr_compare(type, octstr_imm("data"))) {
00186         msg->sms.msgdata = octstr_copy(line, p + 1, LONG_MAX);
00187         if (octstr_url_decode(msg->sms.msgdata) == -1)
00188             warning(0, "smsc_fake: urlcoded data from client looks malformed");
00189     }
00190     else if (!octstr_compare(type, octstr_imm("route"))) {
00191         p2 = octstr_search_char(line, ' ', p + 1);
00192         if (p2 == -1)
00193             goto error;
00194         msg->sms.boxc_id = octstr_copy(line, p + 1, p2 - p - 1);
00195         msg->sms.msgdata = octstr_copy(line, p2 + 1, LONG_MAX);
00196     }
00197     else if (!octstr_compare(type, octstr_imm("udh"))) {
00198         p2 = octstr_search_char(line, ' ', p + 1);
00199         if (p2 == -1)
00200             goto error;
00201         msg->sms.udhdata = octstr_copy(line, p + 1, p2 - p - 1);
00202         msg->sms.msgdata = octstr_copy(line, p2 + 1, LONG_MAX);
00203         if (msg->sms.coding == DC_UNDEF)
00204             msg->sms.coding = DC_8BIT;;
00205         if (octstr_url_decode(msg->sms.msgdata) == -1 ||
00206             octstr_url_decode(msg->sms.udhdata) == -1)
00207             warning(0, "smsc_fake: urlcoded data from client looks malformed");
00208     }
00209     else if (!octstr_compare(type, octstr_imm("dlr-mask"))) {
00210         Octstr *tmp;
00211         p2 = octstr_search_char(line, ' ', p + 1);
00212         if (p2 == -1)
00213             goto error;
00214         tmp = octstr_copy(line, p + 1, p2 - p - 1);
00215         msg->sms.dlr_mask = atoi(octstr_get_cstr(tmp));
00216         octstr_destroy(tmp);
00217         msg->sms.msgdata = octstr_copy(line, p2 + 1, LONG_MAX);
00218     }
00219     else
00220         goto error;
00221     octstr_destroy(line);
00222     octstr_destroy(type);
00223     time(&msg->sms.time);
00224     msg->sms.smsc_id = octstr_duplicate(conn->id);
00225 
00226     debug("bb.sms", 0, "smsc_fake: new message received");
00227 //    msg_dump(msg, 0);
00228     bb_smscconn_receive(conn, msg);
00229     return;
00230 error:
00231     warning(0, "smsc_fake: invalid message syntax from client, ignored");
00232     msg_destroy(msg);
00233     octstr_destroy(line);
00234     octstr_destroy(type);
00235     return;
00236 }
00237 
00238 
00239 static void main_connection_loop(SMSCConn *conn, Connection *client)
00240 {
00241     PrivData *privdata = conn->data;
00242     Octstr *line;
00243     Msg *msg;
00244     double delay = 0;
00245 
00246     if (conn->throughput > 0) {
00247         delay = 1.0 / conn->throughput;
00248     }
00249 
00250     while (1) {
00251         while (!conn->is_stopped && !privdata->shutdown &&
00252                 (line = conn_read_line(client)))
00253             msg_to_bb(conn, line);
00254         if (conn_error(client))
00255             goto error;
00256         if (conn_eof(client))
00257             goto eof;
00258 
00259         /* 
00260          * We won't get DLRs from fakesmsc itself, due that we don't have
00261          * corresponding message IDs etc. We threat the DLR receiving here. So
00262          * DLR "originate" from the protocol layer towards abstraction layer.
00263          * This is all for pure debugging and testing.
00264          */
00265 
00266         while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL) {
00267 
00268             /* pass msg to fakesmsc daemon */            
00269             if (sms_to_client(client, msg) == 1) {
00270                 Msg *copy = msg_duplicate(msg);
00271                 
00272                 /* 
00273                  * Actually no quarantee of it having been really sent,
00274                  * but I suppose that doesn't matter since this interface
00275                  * is just for debugging anyway. The upper layer will send
00276                  * a SMSC success DLR if mask is set. Be aware that msg is
00277                  * destroyed in abstraction layer, that's why we use a copy
00278                  * afterwards to handle the final DLR. 
00279                  */
00280                 bb_smscconn_sent(conn, msg, NULL);
00281 
00282                 /* and now the final DLR */
00283                 if (DLR_IS_SUCCESS_OR_FAIL(copy->sms.dlr_mask)) {
00284                     Msg *dlrmsg;
00285                     Octstr *tmp;
00286                     int dlrstat = DLR_SUCCESS;
00287                     char id[UUID_STR_LEN + 1];
00288 
00289                     uuid_unparse(copy->sms.id, id);
00290                     tmp = octstr_create(id);
00291                     dlrmsg = dlr_find(conn->id,
00292                                       tmp, /* smsc message id */
00293                                       copy->sms.receiver, /* destination */
00294                                       dlrstat);
00295                     if (dlrmsg != NULL) {
00296                         /* XXX TODO: Provide a SMPP DLR text in msgdata */
00297                         bb_smscconn_receive(conn, dlrmsg);
00298                     } else {
00299                         error(0,"smsc_fale: got DLR but could not find message or was not interested in it");
00300                     }
00301                     octstr_destroy(tmp);
00302                 }
00303                 msg_destroy(copy);
00304 
00305             } else {
00306                 bb_smscconn_send_failed(conn, msg,
00307                     SMSCCONN_FAILED_REJECTED, octstr_create("REJECTED"));
00308                 goto error;
00309             }
00310 
00311             /* obey throughput speed limit, if any */
00312             if (conn->throughput > 0) {
00313                 gwthread_sleep(delay);
00314             }
00315         }
00316         if (privdata->shutdown) {
00317             debug("bb.sms", 0, "smsc_fake shutting down, closing client socket");
00318             conn_destroy(client);
00319             return;
00320         }
00321         conn_wait(client, -1);
00322         if (conn_error(client))
00323             goto error;
00324         if (conn_eof(client))
00325             goto eof;
00326     }
00327 error:
00328     info(0, "IO error to fakesmsc client. Closing connection.");
00329     conn_destroy(client);
00330     return;
00331 eof:
00332     info(0, "EOF from fakesmsc client. Closing connection.");
00333     conn_destroy(client);
00334     return;
00335 }
00336 
00337 
00338 static void fake_listener(void *arg)
00339 {
00340     SMSCConn *conn = arg;
00341     PrivData *privdata = conn->data;
00342     struct sockaddr_in client_addr;
00343     socklen_t client_addr_len;
00344     Octstr *ip;
00345     Connection *client;
00346     int s, ret;
00347     Msg *msg;
00348 
00349     /* Make sure we log into our own log-file if defined */
00350     log_thread_to(conn->log_idx);
00351 
00352     while (1) {
00353         client_addr_len = sizeof(client_addr);
00354         ret = gwthread_pollfd(privdata->listening_socket, POLLIN, -1);
00355         if (ret == -1) {
00356             if (errno == EINTR)
00357                 continue;
00358             error(0, "Poll for fakesmsc connections failed, shutting down");
00359             break;
00360         }
00361         if (privdata->shutdown)
00362             break;
00363         if (ret == 0) 
00364             /* 
00365              * This thread was woken up from elsewhere, but
00366              * if we're not shutting down nothing to do here. 
00367              */
00368             continue;
00369         s = accept(privdata->listening_socket, (struct sockaddr *)&client_addr,
00370                    &client_addr_len);
00371         if (s == -1) {
00372             warning(errno, "fake_listener: accept() failed, retrying...");
00373             continue;
00374         }
00375         ip = host_ip(client_addr);
00376         if (!is_allowed_ip(privdata->allow_ip, privdata->deny_ip, ip)) {
00377             info(0, "Fakesmsc connection tried from denied host <%s>,"
00378                  " disconnected", octstr_get_cstr(ip));
00379             octstr_destroy(ip);
00380             close(s);
00381             continue;
00382         }
00383         client = conn_wrap_fd(s, 0);
00384         if (client == NULL) {
00385             error(0, "fake_listener: conn_wrap_fd failed on accept()ed fd");
00386             octstr_destroy(ip);
00387             close(s);
00388             continue;
00389         }
00390         conn_claim(client);
00391         info(0, "Fakesmsc client connected from %s", octstr_get_cstr(ip));
00392         octstr_destroy(ip);
00393         mutex_lock(conn->flow_mutex);
00394         conn->status = SMSCCONN_ACTIVE;
00395         conn->connect_time = time(NULL);
00396         mutex_unlock(conn->flow_mutex);
00397         bb_smscconn_connected(conn);
00398 
00399         main_connection_loop(conn, client);
00400 
00401         if (privdata->shutdown)
00402             break;
00403         mutex_lock(conn->flow_mutex);
00404         conn->status = SMSCCONN_RECONNECTING;
00405         mutex_unlock(conn->flow_mutex);
00406         while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL) {
00407             bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_TEMPORARILY, NULL);
00408         }
00409     }
00410     if (close(privdata->listening_socket) == -1)
00411         warning(errno, "smsc_fake: couldn't close listening socket at shutdown");
00412     mutex_lock(conn->flow_mutex);
00413 
00414     conn->status = SMSCCONN_DEAD;
00415 
00416     while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL) {
00417         bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN, NULL);
00418     }
00419     gwlist_destroy(privdata->outgoing_queue, NULL);
00420     octstr_destroy(privdata->allow_ip);
00421     octstr_destroy(privdata->deny_ip);
00422     gw_free(privdata);
00423     conn->data = NULL;
00424 
00425     mutex_unlock(conn->flow_mutex);
00426     debug("bb.sms", 0, "smsc_fake connection has completed shutdown.");
00427     bb_smscconn_killed();
00428 }
00429 
00430 
00431 static int add_msg_cb(SMSCConn *conn, Msg *sms)
00432 {
00433     PrivData *privdata = conn->data;
00434     Msg *copy;
00435 
00436     copy = msg_duplicate(sms);
00437   
00438     /*  
00439      * Send DLR if desired, which means first add the DLR entry 
00440      * and then later find it and remove it. We need to ensure
00441      * that we put the DLR in first before producing the copy
00442      * to the list.
00443      */
00444     if (DLR_IS_ENABLED_DEVICE(sms->sms.dlr_mask)) {
00445         Octstr *tmp;
00446         char id[UUID_STR_LEN + 1];
00447         uuid_unparse(sms->sms.id, id);
00448         tmp = octstr_format("%s", id);
00449         dlr_add(conn->id, tmp, sms);
00450         octstr_destroy(tmp);
00451     }
00452     gwlist_produce(privdata->outgoing_queue, copy);
00453 
00454     gwthread_wakeup(privdata->connection_thread);
00455 
00456     return 0;
00457 }
00458 
00459 
00460 static int shutdown_cb(SMSCConn *conn, int finish_sending)
00461 {
00462     PrivData *privdata = conn->data;
00463 
00464     debug("bb.sms", 0, "Shutting down SMSCConn FAKE, %s",
00465           finish_sending ? "slow" : "instant");
00466 
00467     /* 
00468      * Documentation claims this would have been done by smscconn.c,
00469      * but isn't when this code is being written. 
00470      */
00471     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
00472     privdata->shutdown = 1; 
00473     /*
00474      * Separate from why_killed to avoid locking, as
00475      * why_killed may be changed from outside? 
00476      */
00477 
00478     if (finish_sending == 0) {
00479         Msg *msg;
00480         while((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL) {
00481             bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN, NULL);
00482         }
00483     }
00484 
00485     gwthread_wakeup(privdata->connection_thread);
00486     return 0;
00487 }
00488 
00489 
00490 static void start_cb(SMSCConn *conn)
00491 {
00492     PrivData *privdata = conn->data;
00493 
00494     /* in case there are messages in the buffer already */
00495     gwthread_wakeup(privdata->connection_thread);
00496     debug("bb.sms", 0, "smsc_fake: start called");
00497 }
00498 
00499 
00500 static long queued_cb(SMSCConn *conn)
00501 {
00502     PrivData *privdata = conn->data;
00503     long ret;
00504     
00505     ret = (privdata ? gwlist_len(privdata->outgoing_queue) : 0);
00506 
00507     /* use internal queue as load, maybe something else later */
00508 
00509     conn->load = ret;
00510     return ret;
00511 }
00512 
00513 
00514 int smsc_fake_create(SMSCConn *conn, CfgGroup *cfg)
00515 {
00516     PrivData *privdata = NULL;
00517     Octstr *allow_ip, *deny_ip;
00518     long portno;   /* has to be long because of cfg_get_integer */
00519 
00520     if (cfg_get_integer(&portno, cfg, octstr_imm("port")) == -1)
00521         portno = 0;
00522     allow_ip = cfg_get(cfg, octstr_imm("connect-allow-ip"));
00523     if (allow_ip)
00524         deny_ip = octstr_create("*.*.*.*");
00525     else
00526         deny_ip = NULL;
00527 
00528     if (portno == 0) {
00529         error(0, "'port' invalid in 'fake' record.");
00530         goto error;
00531     }
00532     privdata = gw_malloc(sizeof(PrivData));
00533     privdata->listening_socket = -1;
00534 
00535     privdata->port = portno;
00536     privdata->allow_ip = allow_ip;
00537     privdata->deny_ip = deny_ip;
00538 
00539     if (fake_open_connection(conn, privdata) < 0) {
00540         gw_free(privdata);
00541         privdata = NULL;
00542         goto error;
00543     }
00544 
00545     conn->data = privdata;
00546 
00547     conn->name = octstr_format("FAKE:%d", privdata->port);
00548 
00549     privdata->outgoing_queue = gwlist_create();
00550     privdata->shutdown = 0;
00551 
00552     conn->status = SMSCCONN_CONNECTING;
00553     conn->connect_time = time(NULL);
00554 
00555     if ((privdata->connection_thread = gwthread_create(fake_listener, conn)) == -1)
00556         goto error;
00557 
00558     conn->shutdown = shutdown_cb;
00559     conn->queued = queued_cb;
00560     conn->start_conn = start_cb;
00561     conn->send_msg = add_msg_cb;
00562 
00563     return 0;
00564 
00565 error:
00566     error(0, "Failed to create fake smsc connection");
00567     if (privdata != NULL) {
00568         gwlist_destroy(privdata->outgoing_queue, NULL);
00569         if (close(privdata->listening_socket == -1)) {
00570             error(errno, "smsc_fake: closing listening socket port %d failed",
00571                   privdata->listening_socket);
00572         }
00573     }
00574     gw_free(privdata);
00575     octstr_destroy(allow_ip);
00576     octstr_destroy(deny_ip);
00577     conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
00578     conn->status = SMSCCONN_DEAD;
00579     return -1;
00580 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.