1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2014 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  * Implementation of a SM/ASI SMSC module.
59  *
60  * Stipe Tolj <stolj@wapme.de>
61  *
62  * This module connects to a CriticalPath InVoke SMS Center which
63  * uses the SM/ASI protocol.
64  * The module is heavily based on the SMPP module design.
65  *
66  * TODO:
67  * 1. alt_dcs is not used. Instead, msg->sms.mclass is used as the SMASI
68  *    Class.
69  * 2. Numbers are not handled correctly, I guess. SMASI allows only(?)
70  *    international numbers without leading double zero. How to ensure
71  *    this?
72  * 3. Handling of npi and ton correct?
73  * 4. SubmitMulti PDUs not supported.
74  * 5. Replace PDUs not supported.
75  * 6. Status PDUs not supported.
76  * 7. Cancel PDUs not supported.
77  * 8. UserRes PDUs not supported.
78  * 9. Smsc PDUs not supported.
79  * 10. EnquireLink PDUs not supported.
80  */
81 
82 #include "gwlib/gwlib.h"
83 #include "msg.h"
84 #include "smsc_p.h"
85 #include "smasi_pdu.h"
86 #include "smscconn_p.h"
87 #include "bb_smscconn_cb.h"
88 #include "sms.h"
89 #include "dlr.h"
90 
91 #define DEBUG 1
92 
93 #ifndef DEBUG
dump_pdu(const char * msg,Octstr * id,SMASI_PDU * pdu)94 static void dump_pdu(const char *msg, Octstr *id, SMASI_PDU *pdu) { }
95 #else
dump_pdu(const char * msg,Octstr * id,SMASI_PDU * pdu)96 static void dump_pdu(const char *msg, Octstr *id, SMASI_PDU *pdu)
97 {
98     debug("bb.sms.smasi", 0, "SMASI[%s]: %s", octstr_get_cstr(id), msg);
99     smasi_pdu_dump(pdu);
100 }
101 #endif
102 
103 
104 /************************************************************************/
105 /* DEFAULT SETTINGS                                                     */
106 /************************************************************************/
107 
108 #define SMASI_DEFAULT_PORT          21500
109 #define SMASI_DEFAULT_PRIORITY      0
110 #define MAX_PENDING_SUBMITS         10
111 #define SMASI_THROTTLING_SLEEP_TIME 15
112 #define SMASI_ENQUIRE_LINK_INTERVAL  30.0
113 
114 
115 /************************************************************************/
116 /* OVERRIDE SETTINGS                                                    */
117 /************************************************************************/
118 
119 /* Set these to -1 if no override desired. Values carried in message will
120  * be used then. Or the defaults - if message has no values.
121  *
122  * Otherwise these values will be forced!
123  */
124 
125 #define SMASI_OVERRIDE_SOURCE_TON    1
126 #define SMASI_OVERRIDE_SOURCE_NPI    -1
127 #define SMASI_OVERRIDE_DEST_TON      -1
128 #define SMASI_OVERRIDE_DEST_NPI      -1
129 
130 
131 /************************************************************************/
132 /* SMASI STRUCTURE AND RELATED FUNCTIONS                                */
133 /************************************************************************/
134 
135 typedef struct {
136     SMSCConn * conn;                 /* connection to the bearerbox */
137     int thread_handle;               /* handle for the SMASI thread */
138     List *msgs_to_send;
139     Dict *sent_msgs;                 /* hash table for send, but yet not confirmed */
140     List *received_msgs;             /* list of received, but yet not processed */
141     Counter *message_id_counter;     /* sequence number */
142     Octstr *host;                    /* host or IP of the SMASI server */
143     long port;                       /* port to connect to */
144     Octstr *username;
145     Octstr * password;
146     Octstr * my_number;
147     long source_addr_ton;
148     long source_addr_npi;
149     long dest_addr_ton;
150     long dest_addr_npi;
151     long priority;
152     time_t throttling_err_time;
153     int quitting;
154     long enquire_link_interval;
155     int logged_off;
156 } SMASI;
157 
158 
smasi_create(SMSCConn * conn)159 static SMASI *smasi_create(SMSCConn *conn)
160 {
161 
162     SMASI *smasi = gw_malloc(sizeof(SMASI));
163 
164     smasi->conn = conn;
165 
166     smasi->thread_handle = -1;
167     smasi->msgs_to_send = gwlist_create();
168     smasi->sent_msgs = dict_create(16, NULL);
169     smasi->received_msgs = gwlist_create();
170     smasi->message_id_counter = counter_create();
171     smasi->host = NULL;
172     smasi->username = NULL;
173     smasi->password = NULL;
174     smasi->source_addr_ton = -1;
175     smasi->source_addr_npi = -1;
176     smasi->dest_addr_ton = -1;
177     smasi->dest_addr_npi = -1;
178     smasi->my_number = NULL;
179     smasi->port = 21500;
180     smasi->quitting = 0;
181     smasi->logged_off = 0;
182     smasi->priority = 0;
183     smasi->throttling_err_time = 0;
184     smasi->enquire_link_interval = 30;
185 
186     gwlist_add_producer(smasi->msgs_to_send);
187 
188     return smasi;
189 }
190 
191 
smasi_destroy(SMASI * smasi)192 static void smasi_destroy(SMASI *smasi)
193 {
194     if (smasi == NULL) return;
195 
196     gwlist_destroy(smasi->msgs_to_send, msg_destroy_item);
197     dict_destroy(smasi->sent_msgs);
198     gwlist_destroy(smasi->received_msgs, msg_destroy_item);
199     counter_destroy(smasi->message_id_counter);
200     octstr_destroy(smasi->host);
201     octstr_destroy(smasi->username);
202     octstr_destroy(smasi->password);
203     gw_free(smasi);
204 }
205 
206 
207 
208 /************************************************************************/
209 /* DATA ENCODING                                                        */
210 /************************************************************************/
211 
212 /* These values will be initialized on module startup. They contain the
213  * ASCII representation of the chars that need to be escaped in the message
214  * body before transmission. Example: "," (comma) will be represented by
215  * the octet string ":2c".
216  */
217 
218 static Octstr *colon = NULL;
219 static Octstr *assign = NULL;
220 static Octstr *comma = NULL;
221 static Octstr *cr = NULL;
222 static Octstr *lf = NULL;
223 
224 
225 /*
226  * Escapes outgoing message body data by replacing occurrences of "special"
227  * chars inside the octet string.
228  */
escape_data(Octstr * data)229 static void escape_data(Octstr *data)
230 {
231     long pos = 0;
232 
233     /* This one uses a different approach than the encode and decode
234      * functions. Because it is assumed, that only a fraction of the
235      * contained chars have to be escaped.
236      */
237     while (pos < octstr_len(data)) {
238         Octstr * escaped = NULL;
239         int check = octstr_get_char(data, pos);
240 
241         if (check == ':') escaped = colon;
242         else if (check == '=') escaped = assign;
243         else if (check == ',') escaped = comma;
244         else if (check == '\n') escaped = cr;
245         else if (check == '\r') escaped = lf;
246 
247         if (escaped != NULL) {
248             /* If the current char has to be escaped, delete the char from
249              * the source string, replace it with the escape sequence, and
250              * advance position until after the inserted sequence.
251              */
252             octstr_delete(data, pos, 1);
253             octstr_insert(data, escaped, pos);
254             pos += octstr_len(escaped);
255         } else {
256             /* If not escaped, simply skip the current char. */
257             pos++;
258         }
259     }
260 }
261 
262 
263 /*
264  * Unescapes incoming message body data by replacing occurrences of escaped
265  * chars with their original character representation.
266  */
unescape_data(Octstr * data)267 static void unescape_data(Octstr *data)
268 {
269     long pos = 0;
270 
271     /* Again, an inplace transformation is used. Because, again, it is
272      * assumed that only a fraction of chars has to be unescaped.
273      */
274     while (pos < octstr_len(data)) {
275         int check = octstr_get_char(data, pos);
276 
277         if (check == ':') {
278             char byte = 0;
279             int msb = octstr_get_char(data, pos + 1);
280             int lsb = octstr_get_char(data, pos + 2);
281 
282             if (msb == '0') msb = 0;
283             else if (msb >= '1' && msb <= '9') msb -= '1' + 1;
284             else msb -= 'a' + 10;
285 
286             if (lsb == '0') lsb = 0;
287             else if (lsb >= '1' && lsb <= '9') lsb -= '1' + 1;
288             else lsb -= 'a' + 10;
289 
290             byte = msb << 4 | lsb;
291 
292             /* Do inplace unescaping. */
293             octstr_delete(data, pos, 3);
294             octstr_insert_data(data, pos, &byte, 1);
295         }
296         pos++;
297     }
298 }
299 
300 
301 /*
302  * Will replace a binary data octet string (inplace) with a SMASI conform
303  * ASCII representation of the data.
304  */
encode_binary_data(Octstr * data)305 static void encode_binary_data(Octstr *data)
306 {
307     Octstr *result = octstr_create("");
308     long pos = 0;
309 
310     while (pos < octstr_len(data)) {
311         int encode = octstr_get_char(data, pos);
312         int msb = (encode & 0xf0) >> 4;
313         int lsb = (encode & 0x0f) >> 0;
314 
315         if (msb == 0) msb = '0';
316         else if (msb < 10) msb = '1' + msb - 1;
317         else msb = 'a' + msb - 10;
318 
319         if (lsb == 0) lsb = '0';
320         else if (lsb < 10) lsb = '1' + lsb - 1;
321         else lsb = 'a' + lsb - 10;
322 
323         octstr_append_char(result, ':');
324         octstr_append_char(result, msb);
325         octstr_append_char(result, lsb);
326 
327         pos++;
328     }
329     /* Replace binary data octet string with ASCII representation. */
330     octstr_delete(data, 0, octstr_len(data));
331     octstr_append(data, result);
332     octstr_destroy(result);
333 }
334 
335 
336 /*
337  * Re-escape SMASI ASCII representation of binary data with the
338  * original binary data octet string.
339  * XXX this may be done by the internal parser routines too.
340  */
decode_binary_data(Octstr * data)341 static void decode_binary_data(Octstr *data)
342 {
343     long pos = 0;
344 
345     while (pos < octstr_len(data)) {
346         int check = octstr_get_char(data, pos);
347 
348         if (check == ':') {
349             Octstr *byte;
350             int msb = octstr_get_char(data, pos + 1);
351             int lsb = octstr_get_char(data, pos + 2);
352 
353             if (msb != -1 && lsb != -1) {
354                 byte = octstr_create("");
355                 octstr_append_char(byte, msb);
356                 octstr_append_char(byte, lsb);
357 
358                 if (octstr_hex_to_binary(byte) != -1) {
359                     /* Do inplace unescaping. */
360                     octstr_delete(data, pos, 3);
361                     octstr_insert(data, byte, pos);
362                 } else {
363                     error(0, "Malformed binary encoded data.");
364                 }
365 
366                 octstr_destroy(byte);
367             }
368         }
369         pos++;
370     }
371 }
372 
373 
374 /************************************************************************/
375 /* MESSAGE PROCESSING                                                   */
376 /************************************************************************/
377 
get_ton_npi_value(int override,int message)378 static Octstr *get_ton_npi_value(int override, int message)
379 {
380     if (override != -1) {
381         debug("bb.sms.smasi", 0, "SMASI: Manually forced ton or npi to `%d'",
382               override);
383         return (octstr_format("%ld", override));
384     } else {
385         return (octstr_format("%ld", message));
386     }
387 }
388 
389 
390 /*
391  * Gets the value to be used as source_addr_ton. Will use override values
392  * if configured. Will use values from message otherwise. Or fall back to
393  * defaults if nothing given.
394  */
get_source_addr_ton(SMASI * smasi,Msg * msg)395 static Octstr *get_source_addr_ton(SMASI *smasi, Msg *msg)
396 {
397     return get_ton_npi_value(smasi->source_addr_ton,
398                              GSM_ADDR_TON_INTERNATIONAL);
399 }
400 
401 
402 /*
403  * Gets the value to be used as source_addr_npi. Will use override values
404  * if configured. Will use values from message otherwise. Or fall back to
405  * defaults if nothing given.
406  */
get_source_addr_npi(SMASI * smasi,Msg * msg)407 static Octstr *get_source_addr_npi(SMASI *smasi, Msg *msg)
408 {
409     return get_ton_npi_value(smasi->source_addr_npi,
410                              GSM_ADDR_NPI_E164);
411 }
412 
413 
414 /*
415  * Gets the value to be used as dest_addr_ton. Will use override values
416  * if configured. Will use values from message otherwise. Or fall back to
417  * defaults if nothing given.
418  */
get_dest_addr_ton(SMASI * smasi,Msg * msg)419 static Octstr *get_dest_addr_ton(SMASI *smasi, Msg *msg)
420 {
421     return get_ton_npi_value(smasi->dest_addr_ton,
422                              GSM_ADDR_TON_INTERNATIONAL);
423 }
424 
425 
426 /*
427  * Gets the value to be used as dest_addr_npi. Will use override values
428  * if configured. Will use values from message otherwise. Or fall back to
429  * defaults if nothing given.
430  */
get_dest_addr_npi(SMASI * smasi,Msg * msg)431 static Octstr *get_dest_addr_npi(SMASI *smasi, Msg *msg)
432 {
433     return get_ton_npi_value(smasi->dest_addr_npi,
434                              GSM_ADDR_NPI_E164);
435 }
436 
437 
438 /*
439  * Determine the originator (sender number) type based on the number. Will
440  * change the originator number if necessary.
441  */
get_originator_type(SMASI * smasi,Octstr * originator)442 static Octstr *get_originator_type(SMASI *smasi, Octstr *originator)
443 {
444     /* International or alphanumeric sender? */
445     if (octstr_get_char(originator, 0) == '+') {
446         if (!octstr_check_range(originator, 1, 256, gw_isdigit)) {
447             return octstr_format("%ld", GSM_ADDR_TON_ALPHANUMERIC);
448         } else {
449            /* Numeric sender address with + in front: The + has to be
450             * removed from this international number.
451             */
452            octstr_delete(originator, 0, 1);
453            return octstr_format("%ld", GSM_ADDR_TON_INTERNATIONAL);
454         }
455     } else if (!octstr_check_range(originator, 0, 256, gw_isdigit)) {
456        return octstr_format("%ld", GSM_ADDR_TON_ALPHANUMERIC);
457     }
458 
459     /* Return the default value. */
460     return octstr_format("%ld", GSM_ADDR_TON_INTERNATIONAL);
461 }
462 
463 
464 /*
465  * Creates a SubmitReq PDU from an outgoing message.
466  */
msg_to_pdu(SMASI * smasi,Msg * msg)467 static SMASI_PDU *msg_to_pdu(SMASI *smasi, Msg *msg)
468 {
469     SMASI_PDU *pdu = smasi_pdu_create(SubmitReq);
470 
471     pdu->u.SubmitReq.Destination = octstr_duplicate(msg->sms.receiver);
472     pdu->u.SubmitReq.Body = octstr_duplicate(msg->sms.msgdata);
473     pdu->u.SubmitReq.Originator = octstr_duplicate(msg->sms.sender);
474 
475     pdu->u.SubmitReq.OriginatorType =
476         get_originator_type(smasi, pdu->u.SubmitReq.Originator);
477 
478     pdu->u.SubmitReq.Sequence =
479         octstr_format("%ld", counter_increase(smasi->message_id_counter));
480 
481 
482     /* If its a international number starting with +, lets remove the +. */
483     if (octstr_get_char(pdu->u.SubmitReq.Destination, 0) == '+')
484         octstr_delete(pdu->u.SubmitReq.Destination, 0, 1);
485 
486     /* Do ton and npi override - if configured. Use values from message
487      * otherwise.
488      */
489     pdu->u.SubmitReq.OriginatorType = get_source_addr_ton(smasi, msg);
490     pdu->u.SubmitReq.OriginatorPlan = get_source_addr_npi(smasi, msg);
491     pdu->u.SubmitReq.DestinationType = get_dest_addr_ton(smasi, msg);
492     pdu->u.SubmitReq.DestinationPlan = get_dest_addr_npi(smasi, msg);
493 
494     /* Set priority. */
495     if (smasi->priority >= 0 && smasi->priority <= 3) {
496         pdu->u.SubmitReq.MqPriority = octstr_format("%ld", smasi->priority);
497     } else {
498         pdu->u.SubmitReq.MqPriority = octstr_format("%ld", 0);
499     }
500 
501     /* Set encoding. */
502     if (msg->sms.coding != DC_UNDEF) {
503         if (msg->sms.coding == DC_7BIT)
504             pdu->u.SubmitReq.MsEncoding = octstr_create("7bit");
505         else if (msg->sms.coding == DC_8BIT)
506             pdu->u.SubmitReq.MsEncoding = octstr_create("8bit");
507         else if (msg->sms.coding == DC_UCS2)
508             pdu->u.SubmitReq.MsEncoding = octstr_create("16bit");
509 
510         /* Everything else will default to 7bit. */
511     }
512 
513     /* Set messaging class - if within defined parameter range. */
514     if (msg->sms.mclass != MC_UNDEF)
515         pdu->u.SubmitReq.Class = octstr_format("%ld", msg->sms.mclass);
516 
517     /* Set Protocol ID. */
518     pdu->u.SubmitReq.ProtocolID = octstr_format("%ld",
519 	(msg->sms.pid == SMS_PARAM_UNDEFINED ? 0 : msg->sms.pid));
520 
521     /* Check if SMS is binary. */
522     if (msg->sms.udhdata && octstr_len(msg->sms.udhdata) > 0) {
523 
524         pdu->u.SubmitReq.UserDataHeader =
525           octstr_duplicate(msg->sms.udhdata);
526 
527         pdu->u.SubmitReq.BodyEncoding =
528           octstr_create("Data");
529 
530         if (pdu->u.SubmitReq.MsEncoding)
531           octstr_destroy(pdu->u.SubmitReq.MsEncoding);
532 
533         pdu->u.SubmitReq.MsEncoding =
534           octstr_create("transparent");
535 
536         /* Encode data. */
537         encode_binary_data(pdu->u.SubmitReq.UserDataHeader);
538         encode_binary_data(pdu->u.SubmitReq.Body);
539     } else {
540 
541         /* Otherwise do data escaping. */
542         escape_data(pdu->u.SubmitReq.Body);
543     }
544 
545     return pdu;
546 }
547 
548 
549 /*
550  * Create a message structure from an incoming DeliverReq PDU.
551  */
pdu_to_msg(SMASI_PDU * pdu)552 static Msg *pdu_to_msg(SMASI_PDU *pdu)
553 {
554     Msg *msg = NULL;
555 
556     gw_assert(pdu->type == DeliverReq);
557     gw_assert(pdu->u.DeliverReq.Originator);
558     gw_assert(pdu->u.DeliverReq.Destination);
559     gw_assert(pdu->u.DeliverReq.Body);
560 
561     msg = msg_create(sms);
562 
563     msg->sms.sender = octstr_duplicate(pdu->u.DeliverReq.Originator);
564     msg->sms.receiver = octstr_duplicate(pdu->u.DeliverReq.Destination);
565     msg->sms.msgdata = octstr_duplicate(pdu->u.DeliverReq.Body);
566 
567     /* Read priority. */
568     if (pdu->u.DeliverReq.ProtocolId)
569         if (octstr_parse_long(&msg->sms.pid,
570                               pdu->u.DeliverReq.ProtocolId, 0, 10) == -1)
571             msg->sms.pid = SMS_PARAM_UNDEFINED;
572 
573     /* Read Coding. */
574     if (pdu->u.DeliverReq.MsEncoding) {
575 
576         /* Use specified coding. */
577         if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "7bit") == 0)
578             msg->sms.coding = DC_7BIT;
579         else if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "8bit") == 0)
580             msg->sms.coding = DC_8BIT;
581         else if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "UCS2") == 0)
582             msg->sms.coding = DC_UCS2;
583         else if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "transparent") == 0)
584             msg->sms.coding = DC_8BIT;
585     } else {
586 
587         /* Determine specified coding according to udhdata presence. */
588         if (pdu->u.DeliverReq.UserDataHeader)
589             msg->sms.coding = DC_8BIT;
590         else
591             msg->sms.coding = DC_7BIT;
592     }
593 
594     /* Unescape (non-binary) or decode (binary) data. */
595     if (msg->sms.coding == DC_8BIT) {
596 
597         decode_binary_data(msg->sms.msgdata);
598         if (pdu->u.DeliverReq.UserDataHeader &&
599             octstr_len(pdu->u.DeliverReq.UserDataHeader) > 0) {
600             msg->sms.udhdata = octstr_duplicate(pdu->u.DeliverReq.UserDataHeader);
601             decode_binary_data(msg->sms.udhdata);
602         }
603 
604     } else {
605         unescape_data(msg->sms.msgdata);
606     }
607 
608     /* Read message class. */
609     if (pdu->u.DeliverReq.Class &&
610         octstr_parse_long(&msg->sms.mclass,
611                           pdu->u.DeliverReq.Class, 0, 10) == -1)
612             msg->sms.mclass = MC_UNDEF;    /* Set to unspecified. */
613 
614     /* Read protocol ID. */
615     if (pdu->u.DeliverReq.ProtocolId &&
616         octstr_parse_long(&msg->sms.pid,
617                           pdu->u.DeliverReq.ProtocolId, 0, 10) == -1)
618             msg->sms.pid = SMS_PARAM_UNDEFINED;
619 
620     return msg;
621 }
622 
623 
624 /************************************************************************/
625 /* PDU HANDLING                                                         */
626 /************************************************************************/
627 
send_logoff(SMASI * smasi,Connection * conn)628 static void send_logoff(SMASI *smasi, Connection *conn)
629 {
630     SMASI_PDU *pdu = NULL;
631     Octstr *os = NULL;
632 
633     counter_increase(smasi->message_id_counter);
634 
635     pdu = smasi_pdu_create(LogoffReq);
636     pdu->u.LogoffReq.Reason = octstr_create("Client shutting down");
637     dump_pdu("Sending !LogoffReq:", smasi->conn->id, pdu);
638 
639     os = smasi_pdu_pack(pdu);
640     conn_write(conn, os);
641     octstr_destroy(os);
642     smasi_pdu_destroy(pdu);
643 }
644 
645 
send_enquire_link(SMASI * smasi,Connection * conn,long * last_sent)646 static void send_enquire_link(SMASI *smasi, Connection *conn, long *last_sent)
647 {
648     SMASI_PDU *pdu = NULL;
649     Octstr *os = NULL;
650 
651     if (date_universal_now() - *last_sent < smasi->enquire_link_interval)
652         return;
653     *last_sent = date_universal_now();
654 
655     pdu = smasi_pdu_create(EnquireLinkReq);
656     dump_pdu("Sending EnquireLinkReq:", smasi->conn->id, pdu);
657     os = smasi_pdu_pack(pdu);
658     if (os)
659 	   conn_write(conn, os); /* Write errors checked by caller. */
660     octstr_destroy(os);
661     smasi_pdu_destroy(pdu);
662 }
663 
664 
send_pdu(Connection * conn,Octstr * id,SMASI_PDU * pdu)665 static int send_pdu(Connection *conn, Octstr *id, SMASI_PDU *pdu)
666 {
667     Octstr * os = NULL;
668     int ret = 0;
669 
670     dump_pdu("Sending PDU:", id, pdu);
671     os = smasi_pdu_pack(pdu);
672     if (os) ret = conn_write(conn, os);
673     else ret = -1;
674 
675     octstr_destroy(os);
676     return ret;
677 }
678 
679 
680 /*
681  * Try to read a SMASI PDU from a connection. Return -1 for error (caller
682  * should close the connection), 0 for no PDU ready yet, or 1 for PDU read
683  * and unpacked. Return a pointer to the PDU in `*pdu'.
684  */
read_pdu(SMASI * smasi,Connection * conn,SMASI_PDU ** pdu)685 static int read_pdu(SMASI *smasi, Connection *conn, SMASI_PDU **pdu)
686 {
687     Octstr *os;
688 
689     os = smasi_pdu_read(conn);
690     if (os == NULL) {
691         if (conn_eof(conn) || conn_error(conn))
692             return -1;
693         return 0;
694     }
695 
696     *pdu = smasi_pdu_unpack(os);
697     if (*pdu == NULL) {
698         error(0, "SMASI[%s]: PDU unpacking failed.",
699               octstr_get_cstr(smasi->conn->id));
700         debug("bb.sms.smasi", 0, "SMASI[%s]: Failed PDU follows.",
701               octstr_get_cstr(smasi->conn->id));
702         octstr_dump(os, 0);
703         octstr_destroy(os);
704         return -1;
705     }
706     octstr_destroy(os);
707     return 1;
708 }
709 
710 
handle_pdu(SMASI * smasi,Connection * conn,SMASI_PDU * pdu,long * pending_submits)711 static void handle_pdu(SMASI *smasi, Connection *conn,
712                        SMASI_PDU *pdu, long *pending_submits)
713 {
714     SMASI_PDU *resp = NULL;
715     Msg *msg = NULL;
716     long reason;
717 
718     switch (pdu->type) {
719 
720         case DeliverReq:
721             msg = pdu_to_msg(pdu);
722 
723             msg_dump(msg, 0);
724 
725             if (smasi->my_number && octstr_len(smasi->my_number)) {
726                 octstr_destroy(msg->sms.receiver);
727                 msg->sms.receiver = octstr_duplicate(smasi->my_number);
728             }
729 
730             time(&msg->sms.time);
731             msg->sms.smsc_id = octstr_duplicate(smasi->conn->id);
732             bb_smscconn_receive(smasi->conn, msg);
733             resp = smasi_pdu_create(DeliverConf);
734 
735             if (pdu->u.DeliverReq.Sequence)
736                 resp->u.DeliverConf.Sequence =
737                   octstr_duplicate(pdu->u.DeliverReq.Sequence);
738 
739             if (pdu->u.DeliverReq.MsgReference)
740                 resp->u.DeliverConf.MsgReference =
741                   octstr_duplicate(pdu->u.DeliverReq.MsgReference);
742             break;
743 
744         case SubmitConf:
745             if (pdu->u.SubmitConf.Sequence) {
746                 msg = dict_remove(smasi->sent_msgs,
747                                   pdu->u.SubmitConf.Sequence);
748             } else {
749                 msg = NULL;
750             }
751 
752             if (msg == NULL) {
753                 warning(0, "SMASI[%s]: SMSC sent SubmitConf for unknown message.",
754                         octstr_get_cstr(smasi->conn->id));
755             } else {
756                 debug("bb.sms.smasi",0,
757                       "SMSC[%s]: SMSC confirmed msg seq <%s> ref <%s>",
758                        octstr_get_cstr(smasi->conn->id),
759                        octstr_get_cstr(pdu->u.SubmitConf.Sequence),
760                        octstr_get_cstr(pdu->u.SubmitConf.MsgReference));
761 
762                 bb_smscconn_sent(smasi->conn, msg, NULL);
763 
764                 --(*pending_submits);
765             }
766             break;
767 
768         case SubmitRej:
769             if (pdu->u.SubmitRej.Sequence) {
770                 msg = dict_remove(smasi->sent_msgs,
771                                   pdu->u.SubmitRej.Sequence);
772             } else {
773                 msg = NULL;
774             }
775 
776             error(0, "SMASI[%s]: SMSC returned error code %s for "
777                   "message ref <%s>", octstr_get_cstr(smasi->conn->id),
778                   octstr_get_cstr(pdu->u.SubmitRej.RejectCode),
779                   octstr_get_cstr(pdu->u.SubmitRej.MsgReference));
780 
781             if (msg == NULL) {
782                warning(0, "SMASI[%s]: SMSC sent SubmitRej for unknown message.",
783                        octstr_get_cstr(smasi->conn->id));
784             } else {
785                 reason = SMSCCONN_FAILED_REJECTED;
786                 bb_smscconn_send_failed(smasi->conn, msg, reason,
787 		            octstr_create("REJECTED"));
788                 --(*pending_submits);
789             }
790             break;
791 
792         case LogonConf:
793             *pending_submits = 0;
794 
795             smasi->conn->status = SMSCCONN_ACTIVE;
796             smasi->conn->connect_time = time(NULL);
797 
798             bb_smscconn_connected(smasi->conn);
799 
800             info(0, "SMASI[%s]: connection to SMSC established.",
801                  octstr_get_cstr(smasi->conn->id));
802             break;
803 
804         case LogonRej:
805             if (octstr_len(pdu->u.LogonRej.Reason) > 0) {
806                 error(0, "SMASI[%s]: SMSC rejected login with reason <%s>",
807                       octstr_get_cstr(smasi->conn->id),
808                       octstr_get_cstr(pdu->u.LogonRej.Reason));
809             } else {
810                 error(0, "SMASI[%s]: SMSC rejected login without reason",
811                       octstr_get_cstr(smasi->conn->id));
812             }
813             break;
814 
815         case LogoffConf:
816             info(0, "SMASI[%s]: SMSC confirmed logoff.",
817                  octstr_get_cstr(smasi->conn->id));
818             smasi->logged_off = 1;
819             break;
820 
821         default:
822             warning(0, "SMASI[%s]: Unknown PDU type <%s>, ignored.",
823                     octstr_get_cstr(smasi->conn->id), pdu->type_name);
824             break;
825     }
826 
827     if (resp != NULL) {
828         send_pdu(conn, smasi->conn->id, resp);
829         smasi_pdu_destroy(resp);
830     }
831 }
832 
833 
834 /************************************************************************/
835 /* SMASI CONNECTION HANDLING                                            */
836 /************************************************************************/
837 
838 /*
839  * Open transmission connection to SMS center. Return NULL for error,
840  * open connection for OK. Caller must set smasi->conn->status correctly
841  * before calling this.
842  */
open_connection(SMASI * smasi)843 static Connection *open_connection(SMASI *smasi)
844 {
845     Connection *conn = conn_open_tcp_with_port(smasi->host, smasi->port, 0, smasi->conn->our_host);
846 
847     if (conn == NULL) {
848         error(0, "SMASI[%s]: Couldn't connect to server.",
849               octstr_get_cstr(smasi->conn->id));
850         return NULL;
851     } else {
852         SMASI_PDU *logon = smasi_pdu_create(LogonReq);
853 
854         logon->u.LogonReq.Name = octstr_duplicate(smasi->username);
855         logon->u.LogonReq.Password = octstr_duplicate(smasi->password);
856 
857         counter_increase(smasi->message_id_counter);
858 
859         send_pdu(conn, smasi->conn->id, logon);
860 
861         smasi_pdu_destroy(logon);
862     }
863 
864     return conn;
865 }
866 
867 
send_messages(SMASI * smasi,Connection * conn,long * pending_submits)868 static void send_messages(SMASI *smasi, Connection *conn,
869                           long *pending_submits)
870 {
871     double delay = 0;
872 
873     if (*pending_submits == -1) return;
874 
875     if (smasi->conn->throughput > 0) {
876         delay = 1.0 / smasi->conn->throughput;
877     }
878 
879     while (*pending_submits < MAX_PENDING_SUBMITS) {
880         SMASI_PDU *pdu = NULL;
881         /* Get next message, quit if none to be sent. */
882         Msg *msg = gwlist_extract_first(smasi->msgs_to_send);
883 
884         if (msg == NULL) break;
885 
886         /* Send PDU, record it as waiting for ack from SMSC. */
887         pdu = msg_to_pdu(smasi, msg);
888 
889         if (pdu->u.SubmitReq.Sequence)
890             dict_put(smasi->sent_msgs, pdu->u.SubmitReq.Sequence, msg);
891 
892         send_pdu(conn, smasi->conn->id, pdu);
893 
894         smasi_pdu_destroy(pdu);
895 
896         /* obey throughput speed limit, if any */
897         if (smasi->conn->throughput > 0)
898             gwthread_sleep(delay);
899 
900         ++(*pending_submits);
901     }
902 }
903 
904 
905 /*
906  * This is the main function for the background thread for doing I/O on
907  * one SMASI connection (the one for transmitting or receiving messages).
908  * It makes the initial connection to the SMASI server and re-connects
909  * if there are I/O errors or other errors that require it.
910  */
smasi_thread(void * arg)911 static void smasi_thread(void *arg)
912 {
913     long pending_submits;
914     SMASI_PDU *pdu;
915     SMASI *smasi;
916     int logoff_already_sent = 0;
917     int ret;
918     Connection *conn;
919     long last_enquire_sent;
920     double timeout;
921 
922     smasi = arg;
923 
924     /* Make sure we log into our own log-file if defined */
925     log_thread_to(smasi->conn->log_idx);
926 
927     while (!smasi->quitting) {
928 
929         conn = open_connection(smasi);
930         if (conn == NULL) {
931             error(0, "SMASI[%s]: Could not connect to SMSC center " \
932                   "(retrying in %ld seconds).",
933                   octstr_get_cstr(smasi->conn->id), smasi->conn->reconnect_delay);
934 
935             gwthread_sleep(smasi->conn->reconnect_delay);
936             smasi->conn->status = SMSCCONN_RECONNECTING;
937             continue;
938         }
939 
940         last_enquire_sent = date_universal_now();
941         pending_submits = -1;
942 
943         for (;;) {
944             timeout = last_enquire_sent + smasi->enquire_link_interval
945                         - date_universal_now();
946 
947             /* wait for activity */
948             if (conn_wait(conn, timeout) == -1) {
949                 error(0, "SMASI[%s]: I/O error or other error. Re-connecting.",
950                       octstr_get_cstr(smasi->conn->id));
951                 break;
952             }
953 
954             /* Send logoff request if module is shutting down. */
955             if (smasi->quitting && !logoff_already_sent) {
956                 send_logoff(smasi, conn);
957                 logoff_already_sent = 1;
958             }
959 
960             /* send an enquire link */
961             send_enquire_link(smasi, conn, &last_enquire_sent);
962 
963             /* Receive incoming PDUs. */
964             while ((ret = read_pdu(smasi, conn, &pdu)) == 1) {
965                 /* Deal with the PDU we just got */
966                 dump_pdu("Got PDU:", smasi->conn->id, pdu);
967 
968                 /* Process the received PDU. */
969                 handle_pdu(smasi, conn, pdu, &pending_submits);
970 
971                 smasi_pdu_destroy(pdu);
972 
973                 /* Bail out if logoff confirmed. */
974                 if (smasi->logged_off) break;
975 
976                 /* Make sure we send even if we read a lot. */
977                 if ((!smasi->throttling_err_time ||
978                     ((time(NULL) - smasi->throttling_err_time) >
979                      SMASI_THROTTLING_SLEEP_TIME
980                       && !(smasi->throttling_err_time = 0))))
981                     send_messages(smasi, conn, &pending_submits);
982             }
983 
984             /* Check if connection broken. */
985             if (ret == -1) {
986                 error(0, "SMASI[%s]: I/O error or other error. Re-connecting.",
987                       octstr_get_cstr(smasi->conn->id));
988                 break;
989             }
990 
991             /* Bail out if logoff confirmed. */
992             if (smasi->logged_off) break;
993 
994             if ((!smasi->throttling_err_time ||
995                 ((time(NULL) - smasi->throttling_err_time) >
996                  SMASI_THROTTLING_SLEEP_TIME
997                   && !(smasi->throttling_err_time = 0))))
998                 send_messages(smasi, conn, &pending_submits);
999 
1000         }
1001 
1002         conn_destroy(conn);
1003         conn = NULL;
1004     }
1005 }
1006 
1007 
1008 /************************************************************************/
1009 /* SMSCCONN INTERFACE                                                   */
1010 /************************************************************************/
1011 
queued_cb(SMSCConn * conn)1012 static long queued_cb(SMSCConn *conn)
1013 {
1014     SMASI *smasi = conn->data;
1015 
1016     conn->load = (smasi ? (conn->status != SMSCCONN_DEAD ?
1017                     gwlist_len(smasi->msgs_to_send) : 0) : 0);
1018 
1019     return conn->load;
1020 }
1021 
1022 
send_msg_cb(SMSCConn * conn,Msg * msg)1023 static int send_msg_cb(SMSCConn *conn, Msg *msg)
1024 {
1025     SMASI *smasi = conn->data;
1026 
1027     gwlist_produce(smasi->msgs_to_send, msg_duplicate(msg));
1028     gwthread_wakeup(smasi->thread_handle);
1029 
1030     return 0;
1031 }
1032 
1033 
shutdown_cb(SMSCConn * conn,int finish_sending)1034 static int shutdown_cb(SMSCConn *conn, int finish_sending)
1035 {
1036     SMASI *smasi = NULL;
1037 
1038     debug("bb.sms.smasi", 0, "Shutting down SMSCConn %s (%s)",
1039           octstr_get_cstr(conn->name), finish_sending ? "slow" : "instant");
1040 
1041     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
1042 
1043     smasi = conn->data;
1044     smasi->quitting = 1;
1045     gwthread_wakeup(smasi->thread_handle);
1046     gwthread_join(smasi->thread_handle);
1047     smasi_destroy(smasi);
1048 
1049     debug("bb.sms.smasi", 0, "SMSCConn %s shut down.",
1050           octstr_get_cstr(conn->name));
1051     conn->status = SMSCCONN_DEAD;
1052     bb_smscconn_killed();
1053 
1054     /* Clean up. */
1055     octstr_destroy(colon);
1056     octstr_destroy(assign);
1057     octstr_destroy(comma);
1058     octstr_destroy(cr);
1059     octstr_destroy(lf);
1060 
1061     return 0;
1062 }
1063 
1064 
1065 /*
1066  * Configures the SMASI structure according to the configuration.
1067  *
1068  * @return 0 on complete success. -1 if failed due to missing or invalid
1069  * configuration entry.
1070  */
init_configuration(SMASI * smasi,CfgGroup * config)1071 static int init_configuration(SMASI *smasi, CfgGroup *config)
1072 {
1073     /* Read mandatory entries. */
1074     smasi->host = cfg_get(config, octstr_imm("host"));
1075     smasi->username = cfg_get(config, octstr_imm("smsc-username"));
1076     smasi->password = cfg_get(config, octstr_imm("smsc-password"));
1077 
1078     /* Check configuration. */
1079     if (smasi->host == NULL) {
1080         error(0,"SMASI: Configuration file doesn't specify host");
1081         return -1;
1082     }
1083     if (smasi->username == NULL) {
1084         error(0, "SMASI: Configuration file doesn't specify username.");
1085         return -1;
1086     }
1087     if (smasi->password == NULL) {
1088         error(0, "SMASI: Configuration file doesn't specify password.");
1089         return -1;
1090     }
1091 
1092     /* Read optional entries. Set default values if not set. */
1093     smasi->my_number = cfg_get(config, octstr_imm("my-number"));
1094     if (cfg_get_integer(&smasi->port, config, octstr_imm("port")) == -1)
1095         smasi->port = SMASI_DEFAULT_PORT;
1096     if (cfg_get_integer(&smasi->source_addr_ton, config,
1097       octstr_imm("source-addr-ton")) == -1)
1098         smasi->source_addr_ton = SMASI_OVERRIDE_SOURCE_TON;
1099     if (cfg_get_integer(&smasi->source_addr_npi, config,
1100       octstr_imm("source-addr-npi")) == -1)
1101         smasi->source_addr_npi = SMASI_OVERRIDE_SOURCE_NPI;
1102     if (cfg_get_integer(&smasi->dest_addr_ton, config,
1103       octstr_imm("dest-addr-ton")) == -1)
1104         smasi->dest_addr_ton = SMASI_OVERRIDE_DEST_TON;
1105     if (cfg_get_integer(&smasi->dest_addr_npi, config,
1106       octstr_imm("dest-addr-npi")) == -1)
1107         smasi->dest_addr_npi = SMASI_OVERRIDE_DEST_NPI;
1108     if (cfg_get_integer(&smasi->priority, config,
1109       octstr_imm("priority")) == -1)
1110         smasi->priority = SMASI_DEFAULT_PRIORITY;
1111     if (cfg_get_integer(&smasi->enquire_link_interval, config,
1112       octstr_imm("enquire-link-interval")) == -1)
1113         smasi->enquire_link_interval = SMASI_ENQUIRE_LINK_INTERVAL;
1114 
1115     /* Configure SMSC connection. */
1116     smasi->conn->data = smasi;
1117     smasi->conn->name = octstr_format("SMASI:%S:%d:%S",
1118         smasi->host, smasi->port, smasi->username);
1119 
1120     smasi->conn->id = cfg_get(config, octstr_imm("smsc-id"));
1121 
1122     if (smasi->conn->id == NULL)
1123       smasi->conn->id = octstr_duplicate(smasi->conn->name);
1124 
1125     return 0;
1126 }
1127 
1128 
smsc_smasi_create(SMSCConn * conn,CfgGroup * config)1129 int smsc_smasi_create(SMSCConn *conn, CfgGroup *config)
1130 {
1131     SMASI *smasi = NULL;
1132 
1133     /* Initialize data encoding subsystem. */
1134     colon = octstr_create(":3a");
1135     assign = octstr_create(":3d");
1136     comma = octstr_create(":2c");
1137     cr = octstr_create(":0a");
1138     lf = octstr_create(":0d");
1139 
1140     /* Create main SMASI structure and initialize it with configuration
1141      * settings.
1142      */
1143     smasi = smasi_create(conn);
1144 
1145     if (init_configuration(smasi, config) != 0)
1146         panic(0, "SMASI SMSC module configuration invalid.");
1147 
1148     conn->status = SMSCCONN_CONNECTING;
1149 
1150     /* Port is always set to a configured value or defaults to 21500.
1151      * Therefore, threads are always started.
1152      */
1153     smasi->thread_handle = gwthread_create(smasi_thread, smasi);
1154 
1155     if (smasi->thread_handle == -1) {
1156         error(0, "SMASI[%s]: Couldn't start SMASI thread.",
1157               octstr_get_cstr(smasi->conn->id));
1158         smasi_destroy(conn->data);
1159         return -1;
1160     }
1161 
1162     /* Setup control function pointers. */
1163     conn->shutdown = shutdown_cb;
1164     conn->queued = queued_cb;
1165     conn->send_msg = send_msg_cb;
1166 
1167     return 0;
1168 }
1169 
1170