⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smsc_emi2.c

📁 gnu的专业网关smpp协议支持源代码。
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * smsc_emi2.c - interface to EMI SMS centers * * Uoti Urpala 2001 *//* Doesn't warn about unrecognized configuration variables *//* The EMI specification doesn't document how connections should be * opened/used. The way they currently work might need to be changed. */#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <errno.h>#include <time.h>#include <limits.h>#include <float.h>#include "gwlib/gwlib.h"#include "smscconn.h"#include "smscconn_p.h"#include "bb_smscconn_cb.h"#include "msg.h"#include "sms.h"#include "emimsg.h"#include "dlr.h"#define EMI2_MAX_TRN 100typedef struct privdata {    Octstr	*name;    List	*outgoing_queue;    long	receiver_thread;    long	sender_thread;    int		retry;	  	/* Retry always to connect to smsc */    int		shutdown;	  /* Internal signal to shut down */    int		listening_socket; /* File descriptor */    int		send_socket;    int		port, alt_port;		  /* SMSC port */    int		our_port;	  /* Optional local port number in which to				   * bind our end of send connection */    int		rport;		  /* Receive-port to listen */    Octstr	*allow_ip, *deny_ip;    Octstr	*host, *alt_host, *username, *password, *our_host;    Octstr	*my_number;	/* My number if we want to force one */    int		unacked;	/* Sent messages not acked */    struct {	time_t	sendtime;	/* When we sent out a message with a given				 * TRN. Is 0 if the TRN slot is currently free. */	int     sendtype;	/* OT of message, undefined if time == 0 */	int	dlr;            /* dlr = DLR_SMSC_SUCCESS || DLR_SMSC_FAIL */	Msg     *sendmsg; 	/* Corresponding message for OT == 51 */    } slots[EMI2_MAX_TRN];    int		keepalive; 	/* Seconds to send a Keepalive Command (OT=31) */    int		flowcontrol;	/* 0=Windowing, 1=Stop-and-Wait */    int		waitack;	/* Seconds to wait to ack */    int		throughput;	/* Messages per second */    int		window;		/* In windowed flow-control, the window size */    int         can_write;      /* write = 1, read = 0, for stop-and-wait flow control */    int         priv_nexttrn;   /* next TRN, this should never be accessed directly.				 * use int emi2_next_trn (SMSCConn *conn) instead.				 */    time_t	last_activity_time; /* the last time something was sent over the main				     * SMSC connection				     */    time_t      check_time;    int         idle_timeout;   /* Seconds a Main connection to the SMSC is allowed to be idle.				   If 0, no idle timeout is in effect */    Octstr   *npid; /* Notification PID value */    Octstr   *nadc; /* Notification Address */} PrivData;typedef enum {    EMI2_SENDREQ,  /* somebody asked this driver to send a SMS message */    EMI2_SMSCREQ,  /* the SMSC wants something from us */    EMI2_CONNERR,  /* an error condition in the SMSC main connection */    EMI2_TIMEOUT,  /* timeout on the SMSC main connection */} EMI2Event;#define PRIVDATA(conn) ((PrivData *)((conn)->data))#define SLOTBUSY(conn,i) (PRIVDATA(conn)->slots[(i)].sendtime != 0)#define CONNECTIONIDLE(conn)								\((PRIVDATA(conn)->unacked == 0) &&							\ (PRIVDATA(conn)->idle_timeout ?							\  (PRIVDATA(conn)->last_activity_time + PRIVDATA(conn)->idle_timeout) <= time(0):0))#define emi2_can_send(conn)					\((PRIVDATA(conn)->can_write || !PRIVDATA(conn)->flowcontrol) &&	\ (PRIVDATA(conn)->unacked < PRIVDATA(conn)->window) &&		\ (!PRIVDATA(conn)->shutdown))#define emi2_needs_keepalive(conn)							\(emi2_can_send(conn) &&									\ (PRIVDATA(conn)->keepalive > 0) &&							\ (time(NULL) > (PRIVDATA(conn)->last_activity_time + PRIVDATA(conn)->keepalive)))/* * Send an EMI message and update the last_activity_time field. */static int emi2_emimsg_send(SMSCConn *conn, Connection *server, struct emimsg *emimsg){    int result = emimsg_send(server, emimsg, PRIVDATA(conn)->name);    if (result >= 0 && emimsg->or == 'O' && ( emimsg->ot == 31 || emimsg->ot == 51)) {	PRIVDATA(conn)->last_activity_time = time (NULL);    }    return result;}/* Wait for a message of type 'ot', sent with TRN 0, to be acked. * Timeout after 't' seconds. Any other packets received are ignored. * This function is meant for initial login packet(s) and testing. * Return 1 for positive ACK, 0 for timeout, -1 for broken/closed connection, * -2 for negative NACK. */static int wait_for_ack(PrivData *privdata, Connection *server, int ot, int t){    time_t timeout_time;    int time_left;    Octstr *str;    struct emimsg *emimsg;    timeout_time = time(NULL) + t;    while (1) {	str = conn_read_packet(server, 2, 3);	if (conn_eof(server)) {	    error(0, "EMI2[%s]: connection closed in wait_for_ack",		  octstr_get_cstr(privdata->name));	    return -1;	}	if (conn_read_error(server)) {	    error(0, "EMI2[%s]: connection error in wait_for_ack",		  octstr_get_cstr(privdata->name));	    return -1;	}	if (str) {	    emimsg = get_fields(str, privdata->name);	    if (emimsg == NULL) {		octstr_destroy(str);		continue;	    }	    if (emimsg->ot == ot && emimsg->trn == 0 && emimsg->or == 'R') {		octstr_destroy(str);		break;	    }	    warning(0, "EMI2[%s]: ignoring message %s while waiting for ack to"			"ot:%d trn:%d", octstr_get_cstr(privdata->name),			octstr_get_cstr(str), ot, 0);	    emimsg_destroy(emimsg);	    octstr_destroy(str);	}	time_left = timeout_time - time(NULL);	if (time_left < 0 || privdata->shutdown)	    return 0;	conn_wait(server, time_left);    }    if (octstr_get_char(emimsg->fields[0], 0) == 'N') {	emimsg_destroy(emimsg);	return -2;    }    emimsg_destroy(emimsg);    return 1;}static struct emimsg *make_emi31(PrivData *privdata, int trn){    struct emimsg *emimsg;    if(octstr_len(privdata->username) || octstr_len(privdata->my_number)) {        emimsg = emimsg_create_op(31, trn, privdata->name);        if(octstr_len(privdata->username)) {            emimsg->fields[0] = octstr_duplicate(privdata->username);	} else {            emimsg->fields[0] = octstr_duplicate(privdata->my_number);	}        emimsg->fields[1] = octstr_create("0539");        return emimsg;    } else {        return NULL;    }}static struct emimsg *make_emi60(PrivData *privdata){    struct emimsg *emimsg;    emimsg = emimsg_create_op(60, 0, privdata->name);    emimsg->fields[E60_OADC] = octstr_duplicate(privdata->username);    emimsg->fields[E60_OTON] = octstr_create("6");    emimsg->fields[E60_ONPI] = octstr_create("5");    emimsg->fields[E60_STYP] = octstr_create("1");    emimsg->fields[E60_PWD] = octstr_duplicate(privdata->password);    octstr_binary_to_hex(emimsg->fields[E60_PWD], 1);    emimsg->fields[E60_VERS] = octstr_create("0100");    return emimsg;}static Connection *open_send_connection(SMSCConn *conn){    PrivData *privdata = conn->data;    int result, wait, alt_host, do_alt_host;    struct emimsg *emimsg;    Connection *server;    Msg *msg;    do_alt_host = octstr_len(privdata->alt_host) != 0 || 	    privdata->alt_port != 0;    wait = 0;    alt_host = -1; /* to avoid waiting in first cicle */    mutex_lock(conn->flow_mutex);    conn->status = SMSCCONN_RECONNECTING;    mutex_unlock(conn->flow_mutex);    while (!privdata->shutdown) {	/* Change status only if the first attempt to form a	 * connection fails, as it's possible that the SMSC closed the	 * connection because of idle timeout and a new one will be	 * created quickly. */	if (wait) {	    while ((msg = list_extract_first(privdata->outgoing_queue))) {		bb_smscconn_send_failed(conn, msg,					SMSCCONN_FAILED_TEMPORARILY);	    }	    if(alt_host == 0) {		info(0, "EMI2[%s]: waiting for %d %s before trying to "			    "connect again", octstr_get_cstr(privdata->name), 			    (wait < 60 ? wait : wait/60), 			    (wait < 60 ? "seconds" : "minutes"));		gwthread_sleep(wait);		wait = wait > (privdata->retry ? 3600 : 600) ?		    (privdata->retry ? 3600 : 600) : wait * 2;	    }	}	else	    wait = 15; 	if(alt_host != 1) {	    info(0, "EMI2[%s]: connecting to Primary SMSC", 			    octstr_get_cstr(privdata->name));	    server = conn_open_tcp_with_port(privdata->host, privdata->port,					     privdata->our_port, 					     privdata->our_host);	    if(do_alt_host)		alt_host=1;	    else		alt_host=0;	} else {	    info(0, "EMI2[%s]: connecting to Alternate SMSC",			    octstr_get_cstr(privdata->name));	    /* use alt_host or/and alt_port if defined */	    server = conn_open_tcp_with_port(		(octstr_len(privdata->alt_host) ? privdata->alt_host : privdata->host),		(privdata->alt_port ? privdata->alt_port : privdata->port),		privdata->our_port, privdata->our_host);	    alt_host=0;	}	if (privdata->shutdown) {	    conn_destroy(server);	    return NULL;	}	if (server == NULL) {	    error(0, "EMI2[%s]: opening TCP connection to %s failed",		  octstr_get_cstr(privdata->name),		  octstr_get_cstr(privdata->host));	    continue;	}	if (privdata->username && privdata->password) {	    emimsg = make_emi60(privdata);	    emi2_emimsg_send(conn, server, emimsg);	    emimsg_destroy(emimsg);	    result = wait_for_ack(privdata, server, 60, 30);	    if (result == -2) {		/* Are SMSCs going to return any temporary errors? If so,		 * testing for those error codes should be added here. */		error(0, "EMI2[%s]: Server rejected our login, giving up",		      octstr_get_cstr(privdata->name));		conn_destroy(server);		if(! privdata->retry)  {		    conn->why_killed = SMSCCONN_KILLED_WRONG_PASSWORD;		    return NULL;		} else		    continue;	    }	    else if (result == 0) {		error(0, "EMI2[%s]: Got no reply to login attempt "		      "within 30 s", octstr_get_cstr(privdata->name));		conn_destroy(server);		continue;	    }	    else if (result == -1) { /* Broken connection, already logged */		conn_destroy(server);		continue;	    }	    privdata->last_activity_time = 0; /* to force keepalive after login */	    privdata->can_write = 1;	}	mutex_lock(conn->flow_mutex);	conn->status = SMSCCONN_ACTIVE;	conn->connect_time = time(NULL);	mutex_unlock(conn->flow_mutex);	bb_smscconn_connected(conn);	return server;    }    return NULL;}static void pack_7bit(Octstr *str){    Octstr *result;    int len, i;    int numbits, value;    result = octstr_create("0");    len = octstr_len(str);    value = 0;    numbits = 0;    for (i = 0; i < len; i++) {	value += octstr_get_char(str, i) << numbits;	numbits += 7;	if (numbits >= 8) {	    octstr_append_char(result, value & 0xff);	    value >>= 8;	    numbits -= 8;	}    }    if (numbits > 0)	octstr_append_char(result, value);    octstr_set_char(result, 0, (len * 7 + 3) / 4);    octstr_delete(str, 0, LONG_MAX);    octstr_append(str, result);    octstr_binary_to_hex(str, 1);    octstr_destroy(result);}static struct emimsg *msg_to_emimsg(Msg *msg, int trn, PrivData *privdata){    Octstr *str;    struct emimsg *emimsg;    int dcs;    struct tm tm;    char p[20];    emimsg = emimsg_create_op(51, trn, privdata->name);    str = octstr_duplicate(msg->sms.sender);    if(octstr_get_char(str,0) == '+') {    	/* either alphanum or international */    	if (!octstr_check_range(str, 1, 256, gw_isdigit)) {	    /* alphanumeric sender address with + in front*/	    charset_latin1_to_gsm(str);	    octstr_truncate(str, 11); /* max length of alphanumeric OaDC */	    emimsg->fields[E50_OTOA] = octstr_create("5039");	    pack_7bit(str);	}	else {	    /* international number. Set format and remove + */	    emimsg->fields[E50_OTOA] = octstr_create("1139");	    octstr_delete(str, 0, 1);	    octstr_truncate(str, 22);  /* max length of numeric OaDC */	}    }    else {	if (!octstr_check_range(str, 0, 256, gw_isdigit)) {	    /* alphanumeric sender address */            charset_latin1_to_gsm(str);	    octstr_truncate(str, 11); /* max length of alphanumeric OaDC */	    emimsg->fields[E50_OTOA] = octstr_create("5039");	    pack_7bit(str);	}    }     emimsg->fields[E50_OADC] = str;    /* set protocoll id */    if (msg->sms.pid != 0) {        emimsg->fields[E50_RPID] = octstr_format("%04d", msg->sms.pid);    }    /* set reply path indicator */    if (msg->sms.rpi != 0) {        emimsg->fields[E50_RPI] = octstr_create("1");    }    str = octstr_duplicate(msg->sms.receiver);    if(octstr_get_char(str,0) == '+') {    	 /* international number format */    	 /* EMI doesnt understand + so we have to replace it with something useful */    	 /* we try 00 here. Should really be done in the config instead so this */    	 /* is only a workaround to make wrong configs work  */	 octstr_delete(str, 0, 1);	 octstr_insert_data(str, 0, "00",2);    }    octstr_truncate(str, 16); /* max length of ADC */    emimsg->fields[E50_ADC] = str;       emimsg->fields[E50_XSER] = octstr_create("");    /* XSer1: UDH */    if (octstr_len(msg->sms.udhdata)) {	str = octstr_create("");	octstr_append_char(str, 1);	octstr_append_char(str, octstr_len(msg->sms.udhdata));	octstr_append(str, msg->sms.udhdata);	octstr_binary_to_hex(str, 1);	octstr_append(emimsg->fields[E50_XSER],str);        octstr_destroy(str);    }	    /* XSer2: DCS */    dcs = fields_to_dcs(msg, msg->sms.alt_dcs);    if (dcs != 0 && dcs != 4) {   	str = octstr_create("");	octstr_append_char(str, 2); 	octstr_append_char(str, 1); /* len 01 */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -