📄 smsc_cimd2.c
字号:
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, SMSCConn *conn);/* 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; packet->operation = number; if (octstr_get_char(packet->data, pos++) != ':') return; pos = octstr_parse_long(&number, packet->data, pos, 10); if (pos < 0) return; packet->seq = number;}/* Accept an Octstr containing one packet, build a struct packet around * it, and return that struct. The Octstr is stored in the struct. * No error checking is done here yet. */static struct packet *packet_parse(Octstr *packet_data){ struct packet *packet; packet = gw_malloc(sizeof(*packet)); packet->data = packet_data; /* Fill in packet->operation and packet->seq */ packet_parse_header(packet); return packet;}/* Deallocate this packet */static void packet_destroy(struct packet *packet){ if (packet != NULL) { octstr_destroy(packet->data); gw_free(packet); }}/* Find the first packet in "in", delete it from "in", and return it as * a struct. Return NULL if "in" contains no packet. Always delete * leading non-packet data from "in". (The CIMD 2 spec says we should * ignore any data between the packet markers). */static struct packet *packet_extract(Octstr *in, SMSCConn *conn){ int stx, etx; Octstr *packet; /* Find STX, and delete everything up to it */ stx = octstr_search_char(in, STX, 0); if (stx < 0) { octstr_delete(in, 0, octstr_len(in)); return NULL; } else { octstr_delete(in, 0, stx); } /* STX is now in position 0. Find ETX. */ etx = octstr_search_char(in, ETX, 1); if (etx < 0) return NULL; /* What shall we do with STX data... STX data... ETX? * Either skip to the second STX, or assume an ETX marker before * the STX. Doing the latter has a chance of succeeding, and * will at least allow good logging of the error. */ stx = octstr_search_char(in, STX, 1); if (stx >= 0 && stx < etx) { warning(0, "CIMD2[%s]: packet without end marker", octstr_get_cstr(conn->id)); packet = octstr_copy(in, 0, stx); octstr_delete(in, 0, stx); octstr_append_cstr(packet, ETX_str); } else { /* Normal case. Copy packet, and cut it from the source. */ packet = octstr_copy(in, 0, etx + 1); octstr_delete(in, 0, etx + 1); } return packet_parse(packet);}/* The get_parm functions always return the first parameter with the * correct id. There is only one case where the spec allows multiple * parameters with the same id, and that is when an SMS has multiple * destination addresses. We only support one destination address anyway. *//* Look for the first parameter with id 'parmno' and return its value. * Return NULL if the parameter was not found. */static Octstr *packet_get_parm(struct packet *packet, int parmno){ long pos, next; long valuepos; long number; gw_assert(packet != NULL); pos = octstr_search_char(packet->data, TAB, 0); if (pos < 0) return NULL; /* Bad packet, nothing we can do */ /* Parameters have a tab on each end. If we don't find the * closing tab, we're at the checksum, so we stop. */ for ( ; (next = octstr_search_char(packet->data, TAB, pos + 1)) >= 0; pos = next) { if (octstr_parse_long(&number, packet->data, pos + 1, 10) < 0) continue; if (number != parmno) continue; valuepos = octstr_search_char(packet->data, ':', pos + 1); if (valuepos < 0) continue; /* badly formatted parm */ valuepos++; /* skip the ':' */ /* Found the right parameter */ return octstr_copy(packet->data, valuepos, next - valuepos); } return NULL;}/* Look for an Integer parameter with id 'parmno' in the packet and * return its value. Return INT_MIN if the parameter was not found. * (Unfortunately, -1 is a valid parameter value for at least one * parameter.) */static long packet_get_int_parm(struct packet *packet, int parmno){ Octstr *valuestr = NULL; long value; /* Our code should never even try a bad parameter access. */ gw_assert(parm_type(parmno) == P_INT); valuestr = packet_get_parm(packet, parmno); if (!valuestr) goto error; if (octstr_parse_long(&value, valuestr, 0, 10) < 0) goto error; octstr_destroy(valuestr); return value;error: octstr_destroy(valuestr); return INT_MIN;}/* Look for a String parameter with id 'parmno' in the packet and * return its value. Return NULL if the parameter was not found. * No translations are done on the value. */static Octstr *packet_get_string_parm(struct packet *packet, int parmno){ /* Our code should never even try a bad parameter access. */ gw_assert(parm_type(parmno) == P_STRING); return packet_get_parm(packet, parmno);}/* Look for an Address parameter with id 'parmno' in the packet and * return its value. Return NULL if the parameter was not found. * No translations are done on the value. */static Octstr *packet_get_address_parm(struct packet *packet, int parmno){ /* Our code should never even try a bad parameter access. */ gw_assert(parm_type(parmno) == P_ADDRESS); return packet_get_parm(packet, parmno);}/* Look for an SMS parameter with id 'parmno' in the packet and return its * value. Return NULL if the parameter was not found. No translations * are done on the value, so it will be in the ISO-Latin-1 character set * with CIMD2-specific escapes. */static Octstr *packet_get_sms_parm(struct packet *packet, int parmno){ /* Our code should never even try a bad parameter access. */ gw_assert(parm_type(parmno) == P_SMS); return packet_get_parm(packet, parmno);}/* There is no packet_get_time_parm because the CIMD 2 timestamp * format is useless. It's in the local time of the MC, with * a 2-digit year and no DST information. We can do without. *//* Look for a Hex parameter with id 'parmno' in the packet and return * its value. Return NULL if the parameter was not found. The value * is de-hexed. */static Octstr *packet_get_hex_parm(struct packet *packet, int parmno){ Octstr *value = NULL; /* Our code should never even try a bad parameter access. */ gw_assert(parm_type(parmno) == P_HEX); value = packet_get_parm(packet, parmno); if (!value) goto error; if (octstr_hex_to_binary(value) < 0) goto error; return value;error: octstr_destroy(value); return NULL;}/* Check if the header is according to CIMD 2 spec, generating log * entries as necessary. Return -1 if anything was wrong, otherwise 0. */static int packet_check_header(struct packet *packet, SMSCConn *conn){ Octstr *data; gw_assert(packet != NULL); data = packet->data; /* The header must have a two-digit operation code, a colon, * and a three-digit sequence number, followed by a tab. * (CIMD2, 3.1) */ if (octstr_len(data) < 8 || !octstr_check_range(data, 1, 2, gw_isdigit) || octstr_get_char(data, 3) != ':' || !octstr_check_range(data, 4, 3, gw_isdigit) || octstr_get_char(data, 7) != TAB) { warning(0, "CIMD2[%s]: packet header in wrong format", octstr_get_cstr(conn->id)); return -1; } return 0;}static int packet_check_parameter(struct packet *packet, long pos, long len, SMSCConn *conn){ Octstr *data; long parm; long dpos, dlen; int negative; long value; int i; int errors = 0; gw_assert(packet != NULL); data = packet->data; /* The parameter header should be TAB, followed by a three-digit * parameter number, a colon, and the data. We already know about * the tab. */ if (len < 5 || !octstr_check_range(data, pos + 1, 3, gw_isdigit) || octstr_get_char(data, pos + 4) != ':') { warning(0, "CIMD2[%s]: parameter at offset %ld in wrong format", octstr_get_cstr(conn->id), pos); errors++; } /* If we can't parse a parameter number, there's nothing more * that we can check. */ dpos = octstr_parse_long(&parm, data, pos + 1, 10); if (dpos < 0) return -1; if (octstr_get_char(data, dpos) == ':') dpos++; dlen = len - (dpos - pos); /* dlen can not go negative because octstr_parse_long must have * been stopped by the TAB at the end of the parameter data. */ gw_assert(dlen >= 0); i = parm_index(parm); if (i < 0) { warning(0, "CIMD2[%s]: packet contains unknown parameter %ld", octstr_get_cstr(conn->id), parm); return -1; } if (dlen > parameters[i].maxlen) { warning(0, "CIMD2[%s]: packet has '%s' parameter with length %ld, spec says max %d", octstr_get_cstr(conn->id), parameters[i].name, len, parameters[i].maxlen); errors++; } switch (parameters[i].type) { case P_INT: /* Allow a leading - */ negative = (octstr_get_char(data, dpos) == '-'); if (!octstr_check_range(data, dpos + negative,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -