📄 smsc_cimd2.c
字号:
/* Driver for CIMD 2 SMS centres. * Copyright 2000 WapIT Oy Ltd. * Author: Richard Braakman *//* TODO: Check checksums on incoming packets *//* TODO: Leading or trailing spaces are not allowed on parameters * "user identity" and "password". Check this. *//* TODO: Try to use the "More messages to send" flag *//* This code is based on the CIMD 2 spec, version 2-0 en. * All USSD-specific parts have been left out, since we only want to * communicate with SMSC's. * * I found one contradiction in the spec: * * - The definition of Integer parameters specifies decimal digits only, * but at least one Integer parameter (Validity Period Relative) can * be negative. I assume that this means a leading - is valid. */#include <ctype.h>#include <time.h>#include <errno.h>#include <limits.h>#include <string.h>#include <unistd.h>#include "gwlib/gwlib.h"#include "smsc_p.h"#include "sms.h"#include "dlr.h"#ifndef CIMD2_TRACE#define CIMD2_TRACE 0#endif/* Microseconds before giving up on a request */#define RESPONSE_TIMEOUT (10 * 1000000)/* Textual names for the operation codes defined by the CIMD 2 spec. *//* If you make changes here, also change the operation table. */enum { /* Requests from client */ LOGIN = 1, LOGOUT = 2, SUBMIT_MESSAGE = 3, ENQUIRE_MESSAGE_STATUS = 4, DELIVERY_REQUEST = 5, CANCEL_MESSAGE = 6, SET_REQ = 8, GET_REQ = 9, /* Requests from server */ DELIVER_MESSAGE = 20, DELIVER_STATUS_REPORT = 23, /* Requests from either */ ALIVE = 40, /* Not a request; add to any request to make it a response */ RESPONSE = 50, /* Responses not related to requests */ GENERAL_ERROR_RESPONSE = 98, NACK = 99};/* Textual names for the parameters defined by the CIMD 2 spec. *//* If you make changes here, also change the parameter table. */enum { P_USER_IDENTITY = 10, P_PASSWORD = 11, P_DESTINATION_ADDRESS = 21, P_ORIGINATING_ADDRESS = 23, P_ORIGINATING_IMSI = 26, P_ORIGINATED_VISITED_MSC = 28, P_DATA_CODING_SCHEME = 30, P_USER_DATA_HEADER = 32, P_USER_DATA = 33, P_USER_DATA_BINARY = 34, P_MORE_MESSAGES_TO_SEND = 44, P_VALIDITY_PERIOD_RELATIVE = 50, P_VALIDITY_PERIOD_ABSOLUTE = 51, P_PROTOCOL_IDENTIFIER = 52, P_FIRST_DELIVERY_TIME_RELATIVE = 53, P_FIRST_DELIVERY_TIME_ABSOLUTE = 54, P_REPLY_PATH = 55, P_STATUS_REPORT_REQUEST = 56, P_CANCEL_ENABLED = 58, P_CANCEL_MODE = 59, P_MC_TIMESTAMP = 60, P_STATUS_CODE = 61, P_STATUS_ERROR_CODE = 62, P_DISCHARGE_TIME = 63, P_TARIFF_CLASS = 64, P_SERVICE_DESCRIPTION = 65, P_MESSAGE_COUNT = 66, P_PRIORITY = 67, P_DELIVERY_REQUEST_MODE = 68, P_GET_PARAMETER = 500, P_MC_TIME = 501, P_ERROR_CODE = 900, P_ERROR_TEXT = 901};/***************************************************************************//* Table of properties of the parameters defined by CIMD 2, and some *//* functions to look up fields. *//***************************************************************************//* Parameter types, internal. CIMD 2 spec considers P_TIME to be "Integer" * and P_SMS to be "User Data". */enum { P_INT, P_STRING, P_ADDRESS, P_TIME, P_HEX, P_SMS };/* Information about the parameters defined by the CIMD 2 spec. * Used for warning about invalid incoming messages, and for validating * outgoing messages. */static const struct{ unsigned char *name; int number; int maxlen; int type; /* P_ values */ int minval, maxval; /* For P_INT */}parameters[] = { { "user identity", P_USER_IDENTITY, 32, P_STRING }, { "password", P_PASSWORD, 32, P_STRING }, { "destination address", P_DESTINATION_ADDRESS, 20, P_ADDRESS }, { "originating address", P_ORIGINATING_ADDRESS, 20, P_ADDRESS }, /* IMSI is International Mobile Subscriber Identity number */ { "originating IMSI", P_ORIGINATING_IMSI, 20, P_ADDRESS }, { "originated visited MSC", P_ORIGINATED_VISITED_MSC, 20, P_ADDRESS }, { "data coding scheme", P_DATA_CODING_SCHEME, 3, P_INT, 0, 255 }, { "user data header", P_USER_DATA_HEADER, 280, P_HEX }, { "user data", P_USER_DATA, 480, P_SMS }, { "user data binary", P_USER_DATA_BINARY, 280, P_HEX }, { "more messages to send", P_MORE_MESSAGES_TO_SEND, 1, P_INT, 0, 1 }, { "validity period relative", P_VALIDITY_PERIOD_RELATIVE, 3, P_INT, -1, 255 }, { "validity period absolute", P_VALIDITY_PERIOD_ABSOLUTE, 12, P_TIME }, { "protocol identifier", P_PROTOCOL_IDENTIFIER, 3, P_INT, 0, 255 }, { "first delivery time relative", P_FIRST_DELIVERY_TIME_RELATIVE, 3, P_INT, -1, 255 }, { "first delivery time absolute", P_FIRST_DELIVERY_TIME_ABSOLUTE, 12, P_TIME }, { "reply path", P_REPLY_PATH, 1, P_INT, 0, 1 }, { "status report request", P_STATUS_REPORT_REQUEST, 2, P_INT, 0, 63 }, { "cancel enabled", P_CANCEL_ENABLED, 1, P_INT, 0, 1 }, { "cancel mode", P_CANCEL_MODE, 1, P_INT, 0, 2 }, { "service centre timestamp", P_MC_TIMESTAMP, 12, P_TIME }, { "status code", P_STATUS_CODE, 2, P_INT, 0, 9 }, { "status error code", P_STATUS_ERROR_CODE, 3, P_INT, 0, 999 }, { "discharge time", P_DISCHARGE_TIME, 12, P_TIME }, { "tariff class", P_TARIFF_CLASS, 2, P_INT, 0, 99 }, { "service description", P_SERVICE_DESCRIPTION, 1, P_INT, 0, 9 }, { "message count", P_MESSAGE_COUNT, 3, P_INT, 0, 999 }, { "priority", P_PRIORITY, 1, P_INT, 1, 9 }, { "delivery request mode", P_DELIVERY_REQUEST_MODE, 1, P_INT, 0, 2 }, { "get parameter", P_GET_PARAMETER, 3, P_INT, 501, 999 }, { "MC time", P_MC_TIME, 12, P_TIME }, { "error code", P_ERROR_CODE, 3, P_INT, 0, 999 }, { "error text", P_ERROR_TEXT, 64, P_STRING }, { NULL }};/* Return the index in the parameters array for this parameter id. * Return -1 if it is not found. */static const int parm_index(int parmno){ int i; for (i = 0; parameters[i].name != NULL; i++) { if (parameters[i].number == parmno) return i; } return -1;}#ifndef NO_GWASSERT/* Return the type of this parameter id. Return -1 if the id is unknown. */static const int parm_type(int parmno){ int i = parm_index(parmno); if (i < 0) return -1; return parameters[i].type;}#endif/* Return the max length for this parameter id. * Return -1 if the id is unknown. */static const int parm_maxlen(int parmno){ int i = parm_index(parmno); if (i < 0) return -1; return parameters[i].maxlen;}static const char *parm_name(int parmno){ int i = parm_index(parmno); if (i < 0) return NULL; return parameters[i].name;}#ifndef NO_GWASSERT/* Return 1 if the value for this (Integer) parameter is in range. * Return 0 otherwise. Return -1 if the parameter was not found. */static const int parm_in_range(int parmno, long value){ int i; i = parm_index(parmno); if (i < 0) return -1; return (value >= parameters[i].minval && value <= parameters[i].maxval);}#endif/* Helper function to check P_ADDRESS type */static int isphonedigit(int c){ return isdigit(c) || c == '+' || c == '-';}static const int parm_valid_address(Octstr *value){ return octstr_check_range(value, 0, octstr_len(value), isphonedigit);}/***************************************************************************//* Some functions to look up information about operation codes *//***************************************************************************/static int operation_find(int operation);static Octstr *operation_name(int operation);static const int operation_can_send(int operation);static const int operation_can_receive(int operation);static const struct{ unsigned char *name; int code; int can_send; int can_receive;}operations[] = { { "Login", LOGIN, 1, 0 }, { "Logout", LOGOUT, 1, 0 }, { "Submit message", SUBMIT_MESSAGE, 1, 0 }, { "Enquire message status", ENQUIRE_MESSAGE_STATUS, 1, 0 }, { "Delivery request", DELIVERY_REQUEST, 1, 0 }, { "Cancel message", CANCEL_MESSAGE, 1, 0 }, { "Set parameter", SET_REQ, 1, 0 }, { "Get parameter", GET_REQ, 1, 0 }, { "Deliver message", DELIVER_MESSAGE, 0, 1 }, { "Deliver status report", DELIVER_STATUS_REPORT, 0, 1 }, { "Alive", ALIVE, 1, 1 }, { "NACK", NACK, 1, 1 }, { "General error response", GENERAL_ERROR_RESPONSE, 0, 1 }, { NULL, 0, 0, 0 }};static int operation_find(int operation){ int i; for (i = 0; operations[i].name != NULL; i++) { if (operations[i].code == operation) return i; } return -1;}/* Return a human-readable representation of this operation code */static Octstr *operation_name(int operation){ int i; i = operation_find(operation); if (i >= 0) return octstr_create(operations[i].name); if (operation >= RESPONSE) { i = operation_find(operation - RESPONSE); if (i >= 0) { Octstr *name = octstr_create(operations[i].name); octstr_append_cstr(name, " response"); return name; } } /* Put the operation number here when we have octstr_format */ return octstr_create("(unknown)");}/* Return true if a CIMD2 client may send this operation */static const int operation_can_send(int operation){ int i = operation_find(operation); if (i >= 0) return operations[i].can_send; /* If we can receive the request, then we can send the response. */ if (operation >= RESPONSE) return operation_can_receive(operation - RESPONSE); return 0;}/* Return true if a CIMD2 server may send this operation */static const int operation_can_receive(int operation){ int i = operation_find(operation); if (i >= 0) return operations[i].can_receive; /* If we can send the request, then we can receive the response. */ if (operation >= RESPONSE) return operation_can_send(operation - RESPONSE); return 0;}/***************************************************************************//* Packet encoding/decoding functions. They handle packets at the octet *//* level, and know nothing of the network. *//***************************************************************************/struct packet{ /* operation and seq are -1 if their value could not be parsed */ int operation; int seq; /* Sequence number */ Octstr *data; /* Encoded packet */ /* CIMD 2 packet structure is so simple that packet information is * stored as a valid encoded packet, and decoded as necessary. * Exceptions: operation code and sequence number are also stored * as ints for speed, and the checksum is not added until the packet * is about to be sent. Since checksums are optional, the packet * is still valid without a checksum. * * The sequence number is kept at 0 until it's time to actually * send the packet, so that the send functions have control over * the sequence numbers. */};/* These are the separators defined by the CIMD 2 spec */#define STX 2 /* Start of packet */#define ETX 3 /* End of packet */#define TAB 9 /* End of parameter *//* The same separators, in string form */#define STX_str "\02"#define ETX_str "\03"#define TAB_str "\011"/* A reminder that packets are created without a valid sequence number */#define BOGUS_SEQUENCE 0static Msg *cimd2_accept_delivery_report_message(struct packet *request, SMSCenter *smsc);/* Look for the STX OO:SSS TAB header defined by CIMD 2, where OO is the * operation code in two decimals and SSS is the sequence number in three * decimals. Leave the results in the proper fields of the packet. * Try to make sense of headers that don't fit this pattern; validating * the packet format is not our job. */static void packet_parse_header(struct packet *packet){ int pos; long number; /* Set default values, in case we can't parse the fields */ packet->operation = -1; packet->seq = -1; pos = octstr_parse_long(&number, packet->data, 1, 10); if (pos < 0) return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -