Kannel: Open Source WAP and SMS gateway  svn-r5335
smsc_cgw.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  * smsc_cgw.c - Implements an interface to the Sonera ContentGateway software
59  *
60  * Anders Lindh, FlyerOne Ltd <alindh@flyerone.com>
61  *
62  *
63  * Changelog:
64  *
65  * 22/02/2002: Preliminary support for the Euro character (req. ProviderServer 2.5.2)
66  * 25/01/2002: Caught a potentially nasty bug
67  * 16/01/2002: Some code cleanup
68  * 10/01/2002: Fixed a bug in trn handling
69  * 16/11/2001: Some minor fixes (Thanks to Tuomas Luttinen)
70  * 12/11/2001: Delivery reports, better acking and numerous other small fixes
71  * 05/11/2001: Initial release. Based heavily on smsc_emi2 and smsc_at2.
72  *
73  *
74  * TO-DO: Do some real life testing
75  * Squash bugs
76  *
77  * Usage: add the following to kannel.conf:
78  *
79  * group = smsc
80  * smsc = cgw
81  * host = x.x.x.x <- CGW server host
82  * port = xxxx <- CGW server otp port (if omitted, defaults to 21772)
83  * receive-port = xxxx <- our port for incoming messages
84  * appname = xxxx <- Name of a "Send only" service. Defaults to "send".
85  * All outgoing messages are routed through this service.
86  *
87  * Configure ContentGateway software to use the above port as text-port
88  * (in provider.cnf). This is documented in their "Guide for Service
89  * development 2.5", page 80.
90  *
91  * Add a new "Receive only" service (with the Remote Control tool), and set
92  * the "Application for Incoming Message" to "text://kannelhost:receive-port".
93  *
94  *
95  * Note:
96  *
97  * Do NOT define the service as a "Query/Reply" service, as it expects response
98  * messages to have a matching session-id tag. Kannel does not store the
99  * relation between a query and a reply, so the response message will not have
100  * the session-id tag. Even though the messages are delivered successfully,
101  * billing of premium priced services will fail.
102  *
103  *
104  */
105 
106 
107 #include <sys/types.h>
108 #include <sys/socket.h>
109 #include <unistd.h>
110 #include <errno.h>
111 #include <time.h>
112 #include <limits.h>
113 
114 #include "gwlib/gwlib.h"
115 #include "smscconn.h"
116 #include "smscconn_p.h"
117 #include "bb_smscconn_cb.h"
118 #include "msg.h"
119 #include "sms.h"
120 #include "dlr.h"
121 
122 
123 #define CGW_DEFPORT 21772
124 #define CGW_EOL 0x0A
125 #define CGW_TRN_MAX 500 /* Size of our internal message buffer. */
126 
127 #define CGWOP_MAXARGS 10 /* max. number of name:value pairs in cgwop */
128 
129 /* Valid CGW operations */
130 
131 #define CGW_OP_NOP 0 /* this doesn't really exist.. */
132 #define CGW_OP_MSG 1
133 #define CGW_OP_OK 2
134 #define CGW_OP_ERR 3
135 #define CGW_OP_DELIVERY 4
136 #define CGW_OP_HELLO 5
137 #define CGW_OP_STATUS 6
138 
139 struct cgwop
140 {
141  int op; /* one of above */
143  int trn; /* transaction number, used to ACK messages */
144  Octstr **name; /* for storing name/value pairs */
146 };
147 
148 static char *cgw_ops[6] = {"nop", "msg", "ok", "err", "delivery", "hello"};
149 
150 
151 typedef struct privdata
152 {
156  int shutdown; /* Internal signal to shut down */
157  int listening_socket; /* File descriptor */
159  int port; /* SMSC port */
160  int rport; /* port for receiving messages*/
161  int our_port; /* Optional local port number in which to
162  * bind our end of send connection */
165  Octstr *appname; /* Application name as defined in Sonera Remote manager */
166 
169  int dlr[CGW_TRN_MAX]; /* dlr = DLR_SMSC_SUCCESS || DLR_SMSC_FAIL */
170  int unacked; /* Sent messages not acked */
171  int waitack; /* Seconds to wait to ack */
172  int nexttrn;
173  long check_time; /* last checked ack/nack status */
174 }
175 PrivData;
176 
177 
178 static int cgw_add_msg_cb(SMSCConn *conn, Msg *sms);
179 static int cgw_shutdown_cb(SMSCConn *conn, int finish_sending);
180 static void cgw_start_cb(SMSCConn *conn);
181 static long cgw_queued_cb(SMSCConn *conn);
182 static void cgw_sender(void *arg);
184 static int cgw_send_loop(SMSCConn *conn, Connection *server);
186 int cgw_wait_command(PrivData *privdata, SMSCConn *conn, Connection *server, int timeout);
188 static void cgw_listener(void *arg);
189 static void cgw_receiver(SMSCConn *conn, Connection *server);
190 static int cgw_handle_op(SMSCConn *conn, Connection *server, struct cgwop *cgwop);
191 struct cgwop *cgw_read_op(PrivData *privdata, SMSCConn *conn, Connection *server, time_t timeout);
192 
193 
194 
195 /******************************************************************************
196  * Functions for handling cgwop -structures
197  */
198 
199 static void cgwop_add(struct cgwop *cgwop, Octstr *name, Octstr *value)
200 {
202  {
205 
206  cgwop->num_fields++;
207  } else
208  {
209  info(0, "cgw: CGWOP_MAXARGS exceeded.");
210  }
211 }
212 
213 
214 static struct cgwop *cgwop_create(int op, int trn)
215 {
216  struct cgwop *ret;
217  Octstr *trnstr;
218 
219  ret = gw_malloc(sizeof(struct cgwop));
220 
221  ret->op = op;
222  ret->num_fields = 0;
223  ret->trn = trn;
224 
225  ret->name = gw_malloc(CGWOP_MAXARGS * sizeof(Octstr *));
226  ret->value = gw_malloc(CGWOP_MAXARGS * sizeof(Octstr *));
227 
228  if (trn != -1)
229  {
230  trnstr = octstr_create("");
231  octstr_append_decimal(trnstr, trn);
232  cgwop_add(ret, octstr_imm("client-id"), trnstr);
233  octstr_destroy(trnstr);
234  }
235 
236  return ret;
237 }
238 
239 static void cgwop_destroy(struct cgwop *cgwop)
240 {
241  int len;
242 
243  len = cgwop->num_fields;
244  while (--len >= 0)
245  {
246  octstr_destroy(cgwop->name[len]); /* octstr_destroy(NULL) is ok */
247  octstr_destroy(cgwop->value[len]);
248  }
249 
250  gw_free(cgwop->name);
251  gw_free(cgwop->value);
252  gw_free(cgwop);
253 }
254 
255 static Octstr *cgwop_get(struct cgwop *cgwop, Octstr *name)
256 {
257  int len = cgwop->num_fields;
258 
259  while (--len >= 0)
260  if (octstr_compare(name, cgwop->name[len]) == 0)
261  return cgwop->value[len];
262  return NULL;
263 }
264 
265 static Octstr *cgwop_tostr(struct cgwop *cgwop)
266 {
267  int len = cgwop->num_fields;
268  Octstr *str;
269 
270  if (cgw_ops[cgwop->op] == NULL) return NULL; /* invalid operation */
271 
272  str = octstr_create("");
273 
274  octstr_append(str, octstr_imm("op:"));
277 
278  while (--len >= 0)
279  {
280  octstr_append(str, cgwop->name[len]);
281  octstr_append_char(str, ':');
282  octstr_append(str, cgwop->value[len]);
284  }
285  octstr_append(str, octstr_imm("end:"));
288 
289  return str;
290 }
291 
292 static int cgwop_send(Connection *conn, struct cgwop *cgwop)
293 {
294  Octstr *dta = cgwop_tostr(cgwop);
295 
296  if (dta == NULL) return -1; /* couldn't convert to string */
297 
298  if (conn_write(conn, dta) == -1)
299  {
300  octstr_destroy(dta);
301  return -1;
302  }
303 
304  octstr_destroy(dta);
305  return 1;
306 }
307 
308 /******************************************************************************
309  * cgw_encode_msg - Encode a msg according to specifications
310  */
311 
313 {
314  int i;
315  char esc = 27;
316  char e = 'e';
317 
318  /* Euro char (0x80) -> ESC + e. We do this conversion as long as the message
319  length is under 160 chars (the checking could probably be done better) */
320 
321  while ((i = octstr_search_char(str, 0x80, 0)) != -1) {
322  octstr_delete(str, i, 1); /* delete Euro char */
323  if (octstr_len(str) < 160) {
324  octstr_insert_data(str, i, &esc, 1); /* replace with ESC + e */
325  octstr_insert_data(str, i+1, &e, 1);
326  } else {
327  octstr_insert_data(str, i, &e, 1); /* no room for ESC + e, just replace with an e */
328  }
329  }
330 
331 
332  /* Escape backslash characters */
333  while ((i = octstr_search_char(str, '\\', 0)) != -1) {
334  octstr_insert(str, octstr_imm("\\"), i);
335  }
336  /* Remove Line Feed characters */
337  while ((i = octstr_search_char(str, CGW_EOL, 0)) != -1) {
338  octstr_delete(str, i, 1); /* delete EOL char */
339  octstr_insert(str, octstr_imm("\\n"), i);
340  }
341  /* Remove Carriage return characters */
342  while ((i = octstr_search_char(str, 13, 0)) != -1) {
343  octstr_delete(str, i, 1); /* delete EOL char */
344  octstr_insert(str, octstr_imm("\\r"), i);
345  }
346 
347  return str;
348 }
349 
350 /******************************************************************************
351  * cgw_decode_msg - Decode an incoming msg
352  */
353 
355 {
356  int i;
357 
358  /* make \n -> linefeed */
359  while ((i = octstr_search(str, octstr_imm("\\n"), 0)) != -1) {
360  octstr_delete(str, i, 2); /* delete "\n" str */
361  octstr_insert(str, octstr_imm("\n"), i);
362  }
363  /* make \r -> carriage return */
364  while ((i = octstr_search(str, octstr_imm("\\r"), 0)) != -1) {
365  octstr_delete(str, i, 2); /* delete EOL char */
366  octstr_insert(str, octstr_imm("\r"), i);
367  }
368  /* remove double backslashes */
369  while ((i = octstr_search(str, octstr_imm("\\\\"), 0)) != -1) {
370  octstr_delete(str, i, 1);
371  }
372 
373  return str;
374 }
375 
376 /******************************************************************************
377  * msg_to_cgwop - Create a send cgwop from a message
378  */
379 
380 static struct cgwop *msg_to_cgwop(PrivData *privdata, Msg *msg, int trn)
381 {
382  struct cgwop *cgwop;
383  Octstr *sender, *udh, *dta;
384 
386 
387  if (cgwop == NULL) return NULL;
388 
389  if (!octstr_check_range(msg->sms.sender, 0, octstr_len(msg->sms.sender), gw_isdigit))
390  {
391  /* If alphanumeric, simply prefix sender with '$' char */
392  sender = octstr_create("$");
393  octstr_append(sender, msg->sms.sender);
394  } else sender = octstr_duplicate(msg->sms.sender);
395 
397  cgwop_add(cgwop, octstr_imm("from"), sender);
398  cgwop_add(cgwop, octstr_imm("to"), msg->sms.receiver);
399 
400  /* If delivery reports are asked, ask for them by adding a nrq:anything field */
401  if (DLR_IS_ENABLED_DEVICE(msg->sms.dlr_mask))
402  cgwop_add(cgwop, octstr_imm("nrq"), octstr_imm("true"));
403 
404  octstr_destroy(sender);
405 
406  if (octstr_len(msg->sms.udhdata))
407  {
408  udh = octstr_duplicate(msg->sms.udhdata);
409  octstr_binary_to_hex(udh, 1);
410  cgwop_add(cgwop, octstr_imm("udh"), udh);
411  octstr_destroy(udh);
412 
413  dta = octstr_duplicate(msg->sms.msgdata);
414  octstr_binary_to_hex(dta, 1);
415  cgwop_add(cgwop, octstr_imm("msg"), dta);
416  cgwop_add(cgwop, octstr_imm("type"), octstr_imm("bin"));
417  octstr_destroy(dta);
418  } else
419  {
420  cgwop_add(cgwop, octstr_imm("msg"), cgw_encode_msg(msg->sms.msgdata));
421  }
422 
423  return cgwop;
424 }
425 
426 
427 /******************************************************************************
428  * Called to create the SMSC. This is our entry point.
429  */
430 
432 {
434  Octstr *allow_ip, *deny_ip, *host, *appname;
435  long portno, our_port, waitack;
436  int i;
437 
438  privdata = gw_malloc(sizeof(PrivData));
441 
442  if (cfg_get_integer(&portno, cfg, octstr_imm("port")) == -1)
443  portno = 0;
444  privdata->port = portno;
445 
446  if (cfg_get_integer(&portno, cfg, octstr_imm("receive-port")) < 0)
447  portno = 0;
448  privdata->rport = portno;
449 
450  host = cfg_get(cfg, octstr_imm("host"));
451  appname = cfg_get(cfg, octstr_imm("appname"));
452 
453  if (cfg_get_integer(&our_port, cfg, octstr_imm("our-port")) == -1)
454  privdata->our_port = 0; /* 0 means use any port */
455  else
457 
458  allow_ip = cfg_get(cfg, octstr_imm("connect-allow-ip"));
459  if (allow_ip)
460  deny_ip = octstr_create("*.*.*.*");
461  else
462  deny_ip = NULL;
463 
464  if (cfg_get_integer(&waitack, cfg, octstr_imm("wait-ack")) < 0)
465  privdata->waitack = 60;
466  else
467  privdata->waitack = waitack;
468 
469  if (privdata->port <= 0 || privdata->port > 65535) {
470  info(1, "No port defined for cgw -> using default (%d)", CGW_DEFPORT);
472  }
473 
474 
475  if (host == NULL) {
476  error(0, "'host' missing in cgw configuration.");
477  goto error;
478  }
479 
480  if (appname == NULL)
481  appname = octstr_create("send");
482 
485  privdata->host = host;
486  privdata->appname = appname;
487  privdata->nexttrn = 0;
488  privdata->check_time = 0;
489 
490  for (i = 0; i < CGW_TRN_MAX; i++) {
491  privdata->sendtime[i] = 0;
492  privdata->dlr[i] = 0;
493  }
494 
495  if (privdata->rport > 0 && cgw_open_listening_socket(conn,privdata) < 0) {
496  gw_free(privdata);
497  privdata = NULL;
498  goto error;
499  }
500 
501 
502  conn->data = privdata;
503 
504  conn->name = octstr_format("CGW:%d", privdata->port);
505 
506  privdata->shutdown = 0;
507 
508  conn->status = SMSCCONN_CONNECTING;
509  conn->connect_time = time(NULL);
510 
511  if (privdata->rport > 0 && (privdata->receiver_thread = gwthread_create(cgw_listener, conn)) == -1)
512  goto error;
513 
514  if ((privdata->sender_thread = gwthread_create(cgw_sender, conn)) == -1) {
515  privdata->shutdown = 1;
516  goto error;
517  }
518 
519  conn->shutdown = cgw_shutdown_cb;
520  conn->queued = cgw_queued_cb;
521  conn->start_conn = cgw_start_cb;
522  conn->send_msg = cgw_add_msg_cb;
523 
524  return 0;
525 
526 error:
527  error(0, "Failed to create CGW smsc connection");
528  if (privdata != NULL)
530 
531  gw_free(privdata);
535  octstr_destroy(appname);
536 
538  conn->status = SMSCCONN_DEAD;
539  info(0, "exiting");
540  return -1;
541 }
542 
543 
544 /******************************************************************************
545  * Callbacks for startup, shutdown, incoming and outgoing messages
546  */
547 
548 
549 static int cgw_add_msg_cb(SMSCConn *conn, Msg *sms)
550 {
551  PrivData *privdata = conn->data;
552  Msg *copy;
553 
554  copy = msg_duplicate(sms);
557 
558  return 0;
559 }
560 
561 
562 static int cgw_shutdown_cb(SMSCConn *conn, int finish_sending)
563 {
564  PrivData *privdata = conn->data;
565 
566  debug("bb.sms", 0, "Shutting down SMSCConn CGW, %s",
567  finish_sending ? "slow" : "instant");
568 
569  /* Documentation claims this would have been done by smscconn.c,
570  but isn't when this code is being written. */
572  privdata->shutdown = 1; /* Separate from why_killed to avoid locking, as
573  * why_killed may be changed from outside? */
574 
575  if (finish_sending == 0) {
576  Msg *msg;
577  while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL) {
579  }
580  }
581 
582  if (privdata->rport > 0)
584  return 0;
585 }
586 
587 
588 static void cgw_start_cb(SMSCConn *conn)
589 {
590  PrivData *privdata = conn->data;
591 
592  /* in case there are messages in the buffer already */
593  if (privdata->rport > 0)
595  debug("smsc.cgw", 0, "smsc_cgw: start called");
596 }
597 
598 
599 static long cgw_queued_cb(SMSCConn *conn)
600 {
601  PrivData *privdata = conn->data;
602  long ret = gwlist_len(privdata->outgoing_queue);
603 
604  /* use internal queue as load, maybe something else later */
605 
606  conn->load = ret;
607  return ret;
608 }
609 
610 
611 /******************************************************************************
612  * This is the entry point for out sender thread. This function is responsible
613  * for sending and acking messages in queue
614  */
615 
616 static void cgw_sender(void *arg)
617 {
618  SMSCConn *conn = arg;
619  PrivData *privdata = conn->data;
620  Msg *msg = NULL;
621  Connection *server = NULL;
622  int l = 0;
623  int ret = 0;
624 
625  conn->status = SMSCCONN_CONNECTING;
626 
627  /* Make sure we log into our own log-file if defined */
628  log_thread_to(conn->log_idx);
629 
630  while (!privdata->shutdown) {
631 
632  /* check that connection is active */
633  if (conn->status != SMSCCONN_ACTIVE) {
634  if ((server = cgw_open_send_connection(conn)) == NULL) {
635  privdata->shutdown = 1;
636  error(0, "Unable to connect to CGW server");
637  return ;
638  }
639 
640  conn->status = SMSCCONN_ACTIVE;
641  bb_smscconn_connected(conn);
642  } else {
643  ret = 0;
645  if (l > 0)
646  ret = cgw_send_loop(conn, server); /* send any messages in queue */
647 
648  if (ret != -1) ret = cgw_wait_command(privdata, conn, server, 1); /* read ack's and delivery reports */
649  if (ret != -1) cgw_check_acks(privdata); /* check un-acked messages */
650 
651  if (ret == -1) {
652  mutex_lock(conn->flow_mutex);
654  mutex_unlock(conn->flow_mutex);
655  }
656  }
657  }
658 
660 
661  while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL)
663  mutex_lock(conn->flow_mutex);
664 
665  conn->status = SMSCCONN_DEAD;
666 
671 
672  gw_free(privdata);
673  conn->data = NULL;
674 
675  mutex_unlock(conn->flow_mutex);
676  debug("bb.sms", 0, "smsc_cgw connection has completed shutdown.");
678 }
679 
680 
682 {
683  PrivData *privdata = conn->data;
684  int wait;
686  Msg *msg;
687 
688  wait = 0;
689  while (!privdata->shutdown) {
690 
691  /* Change status only if the first attempt to form a
692  * connection fails, as it's possible that the SMSC closed the
693  * connection because of idle timeout and a new one will be
694  * created quickly. */
695  if (wait) {
696  if (conn->status == SMSCCONN_ACTIVE) {
697  mutex_lock(conn->flow_mutex);
699  mutex_unlock(conn->flow_mutex);
700  }
703  info(0, "smsc_cgw: waiting for %d minutes before trying to connect again", wait);
704  gwthread_sleep(wait * 60);
705  wait = wait > 5 ? 10 : wait * 2;
706  } else
707  wait = 1;
708 
710  privdata->our_port, conn->our_host);
711 
712  if (privdata->shutdown) {
714  return NULL;
715  }
716 
717  if (server == NULL) {
718  error(0, "smsc_cgw: opening TCP connection to %s failed", octstr_get_cstr(privdata->host));
719  continue;
720  }
721 
722  if (conn->status != SMSCCONN_ACTIVE) {
723  mutex_lock(conn->flow_mutex);
724  conn->status = SMSCCONN_ACTIVE;
725  conn->connect_time = time(NULL);
726  mutex_unlock(conn->flow_mutex);
727  bb_smscconn_connected(conn);
728  }
729  return server;
730  }
731  return NULL;
732 }
733 
734 
735 /******************************************************************************
736  * Send messages in queue.
737  */
738 
740 {
741  PrivData *privdata = conn->data;
742  struct cgwop *cgwop;
743  Msg *msg;
744  int firsttrn;
745 
746  /* Send messages in queue */
747  while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL) {
748  firsttrn = privdata->nexttrn;
749  while (privdata->sendtime[privdata->nexttrn] != 0) {
750  if (++privdata->nexttrn >= CGW_TRN_MAX) privdata->nexttrn = 0;
751  if (privdata->nexttrn == firsttrn) { /* no available trn */
752  /* this happens too many messages are sent, and old messages
753  * haven't been acked. In this case, increase size of
754  * CGW_TRN_MAX */
755  info(0, "cgw: Saturated, increase size of CGW_TRN_MAX!");
757  return 1; /* re-insert, and go check for acks */
758  }
759  }
760 
762 
763  if (cgwop == NULL) {
764  info(0, "cgw: cgwop == NULL");
765  return 0;
766  }
767 
769  privdata->sendtime[privdata->nexttrn] = time(NULL);
770 
771  if (cgwop_send(server, cgwop) == -1) {
773  info(0, "cgw: Unable to send (cgwop_send() == -1)");
774  return -1;
775  }
776 
777  privdata->unacked++;
778 
780  }
781  return 0;
782 }
783 
784 /* Check whether there are messages the server hasn't acked in a
785  * reasonable time */
786 
788 {
789  time_t current_time;
790  int i;
791 
792  current_time = time(NULL);
793  if (privdata->unacked && (current_time > privdata->check_time + 30)) {
794  privdata->check_time = current_time;
795  for (i = 0; i < CGW_TRN_MAX; i++)
796  if (privdata->sendtime[i] && privdata->sendtime[i] < (current_time - privdata->waitack)) {
797  privdata->sendtime[i] = 0;
798  privdata->unacked--;
799  warning(0, "smsc_cgw: received neither OK nor ERR for message %d "
800  "in %d seconds, resending message", i, privdata->waitack);
802  }
803  }
804 }
805 
806 
807 /******************************************************************************
808  * cgw_wait_command - Used by cgw_sender thread to read delivery reports
809  */
810 
812 {
813  int ret;
814  struct cgwop *cgwop;
815 
816  /* is there data to be read? */
818 
819  if (ret != -1) {
820  /* read all waiting ops */
821  cgwop = cgw_read_op(privdata, conn, server, timeout);
822  if (cgwop != NULL) {
823  do {
824  if (conn_eof(server)) {
825  info(0, "cgw: Connection closed by SMSC");
827  if (cgwop != NULL) cgwop_destroy(cgwop);
828  return -1;
829  }
830  if (conn_error(server)) {
831  error(0, "cgw: Error trying to read ACKs from SMSC");
832  if (cgwop != NULL) cgwop_destroy(cgwop);
833  return -1;
834  }
835 
836  cgw_handle_op(conn, server, cgwop);
838  } while ((cgwop = cgw_read_op(privdata, conn, server, timeout)) != NULL);
839  } else
840  conn_wait(server, 1); /* added because gwthread_pollfd
841  seems to always return 1. This will keep
842  the load on a reasonable level */
843  }
844 
845  return 0;
846 }
847 
848 
849 /******************************************************************************
850  * cgw_read_op - read an operation, and return it as a *cgwop structure
851  *
852  * This function will not lock and wait for data if none is available. It will
853  * however lock until a whole op has been read. Timeout not implemented yet.
854  */
855 
856 struct cgwop *cgw_read_op(PrivData *privdata, SMSCConn *conn, Connection *server, time_t timeout)
857 {
858  Octstr *line, *name, *value;
859  int finished = 0;
860  int c = 0;
861  struct cgwop *cgwop = NULL;
862 
863  int op = CGW_OP_NOP;
864 
865  if ((line = conn_read_line(server)) == NULL)
866  return NULL; /* don't block */
867 
868  do
869  {
870  while (line == NULL)
871  line = conn_read_line(server); /* wait for more data */
872 
873  c = octstr_search_char(line, ':', 0);
874  if (c != -1) {
875  name = octstr_copy(line, 0, c);
876  value = octstr_copy(line, c + 1, octstr_len(line) - (c + 1));
877 
878  if (octstr_compare(name, octstr_imm("hello")) == 0) {
879  /* A connection is started by CGW by sending a
880  * "hello: Provider Server..." line. */
881 
883  cgwop_add(cgwop, octstr_imm("hello"), value);
884 
887  octstr_destroy(line);
888 
889  return cgwop;
890  }
891 
892  if (octstr_compare(name, octstr_imm("op")) == 0) {
893  /* check different ops */
894  if (octstr_compare(value, octstr_imm("msg")) == 0)
895  op = CGW_OP_MSG;
896  else
897  if (octstr_compare(value, octstr_imm("ok")) == 0)
898  op = CGW_OP_OK;
899  else
900  if (octstr_compare(value, octstr_imm("delivery")) == 0)
902  else
903  if (octstr_compare(value, octstr_imm("err")) == 0)
904  op = CGW_OP_ERR;
905  else
906  if (octstr_compare(value, octstr_imm("status")) == 0)
907  op = CGW_OP_STATUS;
908  else
909  info(0, "CGW: Received unknown op: %s", octstr_get_cstr(value));
910 
911  if (cgwop == NULL)
912  cgwop = cgwop_create(op, 0);
913  else
914  info(0, "cgw: cgwop != null");
915  }
916 
917  if (op != CGW_OP_NOP) {
918  /* All commands have to be inside an op:xx ... end:xx statement */
919 
920  if (octstr_compare(name, octstr_imm("end")) == 0) { /* found end of op */
921  finished = 1;
922  } else {
923  /* store in name/value fields in cgwop */
924  if (cgwop != NULL) {
926  }
927  }
928  }
931  octstr_destroy(line);
932 
933  if (!finished) line = conn_read_line(server);
934  } else {
935  info(0, "cgw: Received invalid input: %s", octstr_get_cstr(line));
936  octstr_destroy(line);
937  finished = 1;
938  }
939 
940  } while (!finished);
941 
942  return cgwop;
943 }
944 
946 {
947  int s;
948 
949  if ((s = make_server_socket(privdata->rport, (conn->our_host ? octstr_get_cstr(conn->our_host) : NULL))) == -1) {
950  error(0, "smsc_cgw: could not create listening socket in port %d", privdata->rport);
951  return -1;
952  }
953  if (socket_set_blocking(s, 0) == -1) {
954  error(0, "smsc_cgw: couldn't make listening socket port %d non-blocking", privdata->rport);
955  close(s);
956  return -1;
957  }
959  return 0;
960 }
961 
962 /******************************************************************************
963  * This is the entry point for our receiver thread. Listens for incoming
964  * connections and handles operations.
965  */
966 
967 static void cgw_listener(void *arg)
968 {
969  SMSCConn *conn = arg;
970  PrivData *privdata = conn->data;
971  struct sockaddr_in server_addr;
972  socklen_t server_addr_len;
973  Octstr *ip;
975  int s, ret;
976 
977  /* Make sure we log into our own log-file if defined */
978  log_thread_to(conn->log_idx);
979 
980  while (!privdata->shutdown) {
981  server_addr_len = sizeof(server_addr);
982 
984  if (ret == -1) {
985  if (errno == EINTR)
986  continue;
987  error(0, "Poll for cgw smsc connections failed, shutting down");
988  break;
989  }
990 
991  if (privdata->shutdown)
992  break;
993  if (ret == 0) /* This thread was woken up from elsewhere, but
994  * if we're not shutting down nothing to do here. */
995  continue;
996  s = accept(privdata->listening_socket, (struct sockaddr *) & server_addr,
997  &server_addr_len);
998  if (s == -1) {
999  warning(errno, "cgw_listener: accept() failed, retrying...");
1000  continue;
1001  }
1002 
1003  ip = host_ip(server_addr);
1005  info(0, "CGW smsc connection tried from denied host <%s>, disconnected", octstr_get_cstr(ip));
1006  octstr_destroy(ip);
1007  close(s);
1008  continue;
1009  }
1010  server = conn_wrap_fd(s, 0);
1011  if (server == NULL) {
1012  error(0, "cgw_listener: conn_wrap_fd failed on accept()ed fd");
1013  octstr_destroy(ip);
1014  close(s);
1015  continue;
1016  }
1017  conn_claim(server);
1018  info(0, "cgw: smsc connected from %s", octstr_get_cstr(ip));
1019  octstr_destroy(ip);
1020 
1021  cgw_receiver(conn, server);
1023  }
1024  if (close(privdata->listening_socket) == -1)
1025  warning(errno, "smsc_cgw: couldn't close listening socket at shutdown");
1027 }
1028 
1030 {
1031  PrivData *privdata = conn->data;
1032  struct cgwop *cgwop;
1033 
1034  while (1) {
1035  if (conn_eof(server)) {
1036  info(0, "cgw: receive connection closed by SMSC");
1037  return ;
1038  }
1039  if (conn_error(server)) {
1040  error(0, "cgw: receive connection broken");
1041  return ;
1042  }
1043 
1044  cgwop = cgw_read_op(conn->data, conn, server, 0);
1045 
1046  if (cgwop != NULL) {
1047  cgw_handle_op(conn, server, cgwop);
1049  } else
1050  conn_wait(server, -1);
1051 
1052  if (privdata->shutdown)
1053  break;
1054  }
1055  return ;
1056 }
1057 
1058 /******************************************************************************
1059  * This function handles incoming operations. Used by both receiver and sender
1060  * threads (i.e. sender thread uses this function for delivery and ack
1061  * operations).
1062  * Returns 1 if successfull, otherwise 0
1063  */
1064 static int cgw_handle_op(SMSCConn *conn, Connection *server, struct cgwop *cgwop)
1065 {
1066  PrivData *privdata = conn->data;
1067  Msg *msg = NULL;
1068  Octstr *from, *sid, *to, *msgdata; /* for messages */
1069  Octstr *msid, *status, *txt; /* delivery reports */
1070  Octstr *clid; /* for acks */
1071  struct cgwop *reply = NULL;
1072  long trn, stat; /* transaction number for ack */
1073  Msg *dlrmsg = NULL, *origmsg = NULL;
1074  Octstr *ts;
1075 
1076  if (cgwop == NULL) return 0;
1077 
1078  from = cgwop_get(cgwop, octstr_imm("from"));
1079  sid = cgwop_get(cgwop, octstr_imm("session-id"));
1080  to = cgwop_get(cgwop, octstr_imm("to"));
1081  msgdata = cgwop_get(cgwop, octstr_imm("msg"));
1082  txt = cgwop_get(cgwop, octstr_imm("txt"));
1083 
1084  msid = cgwop_get(cgwop, octstr_imm("msid"));
1085  status = cgwop_get(cgwop, octstr_imm("status"));
1086  clid = cgwop_get(cgwop, octstr_imm("client-id"));
1087 
1088  if (clid != NULL)
1089  {
1090  octstr_parse_long(&trn, clid, 0, 10);
1091  if ((trn < 0) || (trn >= CGW_TRN_MAX)) { /* invalid transaction number */
1092  info(0, "cgw: Invalid transaction number: %d", (int) trn);
1093  trn = -1;
1094  return 0;
1095  }
1096  }
1097 
1098  switch (cgwop->op)
1099  {
1100  case CGW_OP_MSG:
1101  msg = msg_create(sms);
1102  time(&msg->sms.time);
1103  msg->sms.msgdata = cgw_decode_msg(octstr_duplicate(msgdata));
1104  msg->sms.sender = octstr_duplicate(from);
1105  msg->sms.receiver = octstr_duplicate(to);
1106  msg->sms.smsc_id = octstr_duplicate(conn->id);
1107  bb_smscconn_receive(conn, msg);
1108 
1109  reply = cgwop_create(CGW_OP_OK, -1);
1110  cgwop_add(reply, octstr_imm("session-id"), sid);
1111  cgwop_send(server, reply); /* send reply */
1112 
1114 
1115  break;
1116 
1117  case CGW_OP_DELIVERY:
1118  if (privdata->dlr[trn]) {
1119 
1120  octstr_parse_long(&stat, status, 0, 10);
1121  origmsg = privdata->sendmsg[trn];
1122 
1123  if (origmsg == NULL) break;
1124 
1125  ts = octstr_create("");
1126  octstr_append(ts, conn->id);
1127  octstr_append_char(ts, '-');
1129 
1130  switch (stat) {
1131  case 0: /* delivered */
1132  dlrmsg = dlr_find(conn->id,
1133  ts, /* timestamp */
1134  msid, /* destination */
1135  DLR_SUCCESS, 0);
1136  break;
1137  case 1: /* buffered */
1138  dlrmsg = dlr_find(conn->id,
1139  ts, /* timestamp */
1140  msid, /* destination */
1141  DLR_BUFFERED, 0);
1142  break;
1143  case 2: /* not delivered */
1144  dlrmsg = dlr_find(conn->id,
1145  ts, /* timestamp */
1146  msid, /* destination */
1147  DLR_FAIL, 0);
1148  break;
1149  }
1150 
1151  octstr_destroy(ts);
1152  if (dlrmsg != NULL) {
1153  dlrmsg->sms.msgdata = octstr_duplicate(txt);
1154  bb_smscconn_receive(conn, dlrmsg);
1155  }
1156  }
1157 
1158  break;
1159 
1160  case CGW_OP_OK:
1161  if (trn == -1) break; /* invalid transaction number */
1162  /* info(0, "cgw: Got ACK: %s", octstr_get_cstr(clid)); */
1163 
1164  privdata->sendtime[trn] = 0;
1165  privdata->unacked--;
1166 
1167  /* add delivery notification request if wanted */
1168 
1169  msg = privdata->sendmsg[trn];
1170 
1171  if (msg && msg->sms.dlr_url && DLR_IS_ENABLED_DEVICE(msg->sms.dlr_mask)) {
1172  Octstr *ts;
1173 
1174  ts = octstr_create("");
1175  octstr_append(ts, conn->id);
1176  octstr_append_char(ts, '-');
1178 
1179  dlr_add(conn->id, ts, msg, 0);
1180 
1181  octstr_destroy(ts);
1182  privdata->dlr[trn] = 1;
1183  } else {
1184  privdata->dlr[trn] = 0;
1185  }
1186 
1187  /* mark as successfully sent */
1188  bb_smscconn_sent(conn, msg, NULL);
1189 
1190  break;
1191 
1192  case CGW_OP_STATUS:
1193  info(0, "CGW: Warning: Got session status");
1194  /* op:status messages are sent by ProviderServer to tell if there are problems with
1195  the session status. These are not wanted, and should never occur, as the delivery is
1196  cancelled, and no end-user billing is done. */
1197 
1198  break;
1199 
1200 
1201  case CGW_OP_HELLO:
1202  info(0, "CGW: Server said: %s", octstr_get_cstr(cgwop_get(cgwop, octstr_imm("hello"))));
1203  break;
1204 
1205  case CGW_OP_ERR:
1206  if (trn == -1) break; /* invalid transaction number */
1207 
1208  info(0, "CGW: Received error: %s", octstr_get_cstr(txt));
1209 
1210  privdata->sendtime[trn] = 0;
1211  privdata->unacked--;
1212 
1215 
1216  break;
1217 
1218  default:
1219  info(0, "cgw: Unknown operation: %d", cgwop->op);
1220  return 0;
1221  }
1222 
1223  return 1;
1224 }
1225 
1226 
1227 
#define CGW_DEFPORT
Definition: smsc_cgw.c:123
int port
Definition: smsc_cgw.c:159
Octstr * name
Definition: smscconn_p.h:173
Octstr * conn_read_line(Connection *conn)
Definition: conn.c:1134
void error(int err, const char *fmt,...)
Definition: log.c:648
void info(int err, const char *fmt,...)
Definition: log.c:672
Octstr ** value
Definition: smsc_cgw.c:145
static long our_port
Definition: radius_acct.c:87
Msg * msg_duplicate(Msg *msg)
Definition: msg.c:111
int smsc_cgw_create(SMSCConn *conn, CfgGroup *cfg)
Definition: smsc_cgw.c:431
static void cgw_start_cb(SMSCConn *conn)
Definition: smsc_cgw.c:588
void bb_smscconn_connected(SMSCConn *conn)
Definition: bb_smscconn.c:192
int socket_set_blocking(int fd, int blocking)
Definition: socket.c:368
Msg * sendmsg[CGW_TRN_MAX]
Definition: smsc_cgw.c:167
static Connection * cgw_open_send_connection(SMSCConn *conn)
Definition: smsc_cgw.c:681
Octstr ** name
Definition: smsc_cgw.c:144
#define mutex_unlock(m)
Definition: thread.h:136
void bb_smscconn_killed(void)
Definition: bb_smscconn.c:199
int octstr_check_range(Octstr *ostr, long pos, long len, octstr_func_t filter)
Definition: octstr.c:814
void octstr_append(Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:1504
void gwlist_produce(List *list, void *item)
Definition: list.c:411
int send_socket
Definition: smsc_cgw.c:158
Octstr * id
Definition: smscconn_p.h:174
long gwlist_len(List *list)
Definition: list.c:166
void * data
Definition: smscconn_p.h:250
static void cgwop_add(struct cgwop *cgwop, Octstr *name, Octstr *value)
Definition: smsc_cgw.c:199
static int cgw_handle_op(SMSCConn *conn, Connection *server, struct cgwop *cgwop)
Definition: smsc_cgw.c:1064
int waitack
Definition: smsc_cgw.c:171
List * outgoing_queue
Definition: smsc_cgw.c:153
void octstr_append_char(Octstr *ostr, int ch)
Definition: octstr.c:1517
int log_idx
Definition: smscconn_p.h:197
int op
Definition: smsc_cgw.c:141
long octstr_search(const Octstr *haystack, const Octstr *needle, long pos)
Definition: octstr.c:1070
static Octstr * host
Definition: fakesmsc.c:122
#define cfg_get(grp, varname)
Definition: cfg.h:86
static int cgw_add_msg_cb(SMSCConn *conn, Msg *sms)
Definition: smsc_cgw.c:549
#define msg_create(type)
Definition: msg.h:136
static Octstr * cgwop_get(struct cgwop *cgwop, Octstr *name)
Definition: smsc_cgw.c:255
Octstr * our_host
Definition: smscconn_p.h:192
static Cfg * cfg
Definition: opensmppbox.c:95
void octstr_insert_data(Octstr *ostr, long pos, const char *data, long len)
Definition: octstr.c:1461
int conn_eof(Connection *conn)
Definition: conn.c:705
Msg * dlr_find(const Octstr *smsc, const Octstr *ts, const Octstr *dst, int typ, int use_dst)
Definition: dlr.c:387
void dlr_add(const Octstr *smsc, const Octstr *ts, Msg *msg, int use_dst)
Definition: dlr.c:330
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
#define octstr_copy(ostr, from, len)
Definition: octstr.h:178
void octstr_binary_to_hex(Octstr *ostr, int uppercase)
Definition: octstr.c:465
void(* start_conn)(SMSCConn *conn)
Definition: smscconn_p.h:246
long octstr_search_char(const Octstr *ostr, int ch, long pos)
Definition: octstr.c:1012
int cgw_wait_command(PrivData *privdata, SMSCConn *conn, Connection *server, int timeout)
Definition: smsc_cgw.c:811
static int cgw_send_loop(SMSCConn *conn, Connection *server)
Definition: smsc_cgw.c:739
struct privdata PrivData
void log_thread_to(int idx)
Definition: log.c:759
static void cgw_receiver(SMSCConn *conn, Connection *server)
Definition: smsc_cgw.c:1029
#define CGW_OP_HELLO
Definition: smsc_cgw.c:136
int is_allowed_ip(Octstr *allow_ip, Octstr *deny_ip, Octstr *ip)
Definition: utils.c:815
smscconn_killed_t why_killed
Definition: smscconn_p.h:153
void conn_claim(Connection *conn)
Definition: conn.c:671
static void cgwop_destroy(struct cgwop *cgwop)
Definition: smsc_cgw.c:239
#define DLR_SUCCESS
Definition: dlr.h:72
static Octstr * from
Definition: mtbatch.c:95
#define POLLIN
Definition: gwpoll.h:91
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:283
int conn_write(Connection *conn, Octstr *data)
Definition: conn.c:1051
Definition: msg.h:79
void octstr_insert(Octstr *ostr1, const Octstr *ostr2, long pos)
Definition: octstr.c:1303
static struct cgwop * cgwop_create(int op, int trn)
Definition: smsc_cgw.c:214
void * gwlist_extract_first(List *list)
Definition: list.c:305
static int cgw_shutdown_cb(SMSCConn *conn, int finish_sending)
Definition: smsc_cgw.c:562
void octstr_delete(Octstr *ostr1, long pos, long len)
Definition: octstr.c:1527
long bb_smscconn_receive(SMSCConn *conn, Msg *sms)
Definition: bb_smscconn.c:477
void conn_destroy(Connection *conn)
Definition: conn.c:627
time_t connect_time
Definition: smscconn_p.h:155
void cgw_check_acks(PrivData *privdata)
Definition: smsc_cgw.c:787
#define octstr_duplicate(ostr)
Definition: octstr.h:187
static Octstr * deny_ip
Definition: bb_udp.c:112
Mutex * flow_mutex
Definition: smscconn_p.h:157
static struct cgwop * msg_to_cgwop(PrivData *privdata, Msg *msg, int trn)
Definition: smsc_cgw.c:380
Octstr * allow_ip
Definition: smsc_cgw.c:164
#define CGW_OP_MSG
Definition: smsc_cgw.c:132
char * name
Definition: smsc_cimd2.c:212
Connection * conn_open_tcp_with_port(Octstr *host, int port, int our_port, Octstr *our_host)
Definition: conn.c:548
int gw_isdigit(int c)
Definition: utils.c:988
int make_server_socket(int port, const char *interface_name)
Definition: socket.c:93
void warning(int err, const char *fmt,...)
Definition: log.c:660
Octstr * host
Definition: smsc_cgw.c:163
static int cgwop_send(Connection *conn, struct cgwop *cgwop)
Definition: smsc_cgw.c:292
Octstr * octstr_format(const char *fmt,...)
Definition: octstr.c:2464
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:324
int rport
Definition: smsc_cgw.c:160
#define gwthread_create(func, arg)
Definition: gwthread.h:90
#define CGW_OP_OK
Definition: smsc_cgw.c:133
#define CGW_OP_DELIVERY
Definition: smsc_cgw.c:135
#define octstr_create(cstr)
Definition: octstr.h:125
long check_time
Definition: smsc_cgw.c:173
#define CGW_TRN_MAX
Definition: smsc_cgw.c:125
static void cgw_sender(void *arg)
Definition: smsc_cgw.c:616
void gwthread_sleep(double seconds)
static Octstr * allow_ip
Definition: bb_udp.c:111
int gwthread_pollfd(int fd, int events, double timeout)
int shutdown
Definition: smsc_cgw.c:156
int num_fields
Definition: smsc_cgw.c:142
static Octstr * cgwop_tostr(struct cgwop *cgwop)
Definition: smsc_cgw.c:265
static char * cgw_ops[6]
Definition: smsc_cgw.c:148
long sender_thread
Definition: smsc_cgw.c:155
long octstr_len(const Octstr *ostr)
Definition: octstr.c:342
#define CGW_OP_NOP
Definition: smsc_cgw.c:131
static long cgw_queued_cb(SMSCConn *conn)
Definition: smsc_cgw.c:599
void octstr_append_decimal(Octstr *ostr, long value)
Definition: octstr.c:1976
int trn
Definition: smsc_cgw.c:143
Definition: octstr.c:118
void bb_smscconn_sent(SMSCConn *conn, Msg *sms, Octstr *reply)
Definition: bb_smscconn.c:281
int conn_wait(Connection *conn, double seconds)
Definition: conn.c:904
int(* shutdown)(SMSCConn *conn, int finish_sending)
Definition: smscconn_p.h:230
struct cgwop * cgw_read_op(PrivData *privdata, SMSCConn *conn, Connection *server, time_t timeout)
Definition: smsc_cgw.c:856
Octstr * host_ip(struct sockaddr_in addr)
Definition: socket.c:615
#define CGW_OP_STATUS
Definition: smsc_cgw.c:137
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:726
int cfg_get_integer(long *n, CfgGroup *grp, Octstr *varname)
Definition: cfg.c:742
void gwthread_wakeup(long thread)
Definition: cfg.c:73
int socklen_t
Definition: socket.h:73
smscconn_status_t status
Definition: smscconn_p.h:151
long octstr_parse_long(long *nump, Octstr *ostr, long pos, int base)
Definition: octstr.c:749
#define gwlist_create()
Definition: list.h:136
static void server(int lport, int pport)
long(* queued)(SMSCConn *conn)
Definition: smscconn_p.h:241
int(* send_msg)(SMSCConn *conn, Msg *msg)
Definition: smscconn_p.h:236
long receiver_thread
Definition: smsc_cgw.c:154
#define CGW_EOL
Definition: smsc_cgw.c:124
int listening_socket
Definition: smsc_cgw.c:157
#define DLR_BUFFERED
Definition: dlr.h:74
void bb_smscconn_send_failed(SMSCConn *conn, Msg *sms, int reason, Octstr *reply)
Definition: bb_smscconn.c:329
int unacked
Definition: smsc_cgw.c:170
int conn_error(Connection *conn)
Definition: conn.c:716
Octstr * appname
Definition: smsc_cgw.c:165
static Octstr * cgw_decode_msg(Octstr *str)
Definition: smsc_cgw.c:354
time_t sendtime[CGW_TRN_MAX]
Definition: smsc_cgw.c:168
int our_port
Definition: smsc_cgw.c:161
static void cgw_listener(void *arg)
Definition: smsc_cgw.c:967
#define mutex_lock(m)
Definition: thread.h:130
#define DLR_IS_ENABLED_DEVICE(dlr)
Definition: dlr.h:82
Octstr * deny_ip
Definition: smsc_cgw.c:164
#define CGWOP_MAXARGS
Definition: smsc_cgw.c:127
Definition: list.c:102
static int cgw_open_listening_socket(SMSCConn *conn, PrivData *privdata)
Definition: smsc_cgw.c:945
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
#define DLR_FAIL
Definition: dlr.h:73
#define CGW_OP_ERR
Definition: smsc_cgw.c:134
static Octstr * cgw_encode_msg(Octstr *str)
Definition: smsc_cgw.c:312
int dlr[CGW_TRN_MAX]
Definition: smsc_cgw.c:169
Connection * conn_wrap_fd(int fd, int ssl)
Definition: conn.c:566
static void reply(HTTPClient *c, List *push_headers)
int nexttrn
Definition: smsc_cgw.c:172
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:871
int load
Definition: smscconn_p.h:152
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.