📄 smsc_cimd2.c
字号:
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){ 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: packet without end marker"); 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){ 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 packet header in wrong format"); return -1; } return 0;}static int packet_check_parameter(struct packet *packet, long pos, long len){ 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 parameter at offset %ld in wrong format", 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 packet contains unknown parameter %ld", parm); return -1; } if (dlen > parameters[i].maxlen) { warning(0, "CIMD2 packet has '%s' parameter with length %ld, spec says max %d", 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, dlen - negative, gw_isdigit)) { warning(0, "CIMD2 packet has '%s' parameter with non-integer contents", parameters[i].name); errors++; } if (octstr_parse_long(&value, data, dpos, 10) >= 0 && (value < parameters[i].minval || value > parameters[i].maxval)) { warning(0, "CIMD2 packet has '%s' parameter out of range (value %ld, min %d, max %d)", parameters[i].name, value, parameters[i].minval, parameters[i].maxval); errors++; } break; case P_TIME: if (!octstr_check_range(data, dpos, dlen, gw_isdigit)) { warning(0, "CIMD2 packet has '%s' parameter with non-digit contents", parameters[i].name); errors++; } break; case P_ADDRESS: if (!octstr_check_range(data, dpos, dlen, isphonedigit)) { warning(0, "CIMD2 packet has '%s' parameter with non phone number contents", parameters[i].name); errors++; } break; case P_HEX: if (!octstr_check_range(data, dpos, dlen, gw_isxdigit)) { warning(0, "CIMD2 packet has '%s' parameter with non-hex contents", parameters[i].name); errors++; } if (dlen % 2 != 0) { warning(0, "CIMD2 packet has odd-length '%s' parameter", parameters[i].name); errors++; } break; case P_SMS: case P_STRING: /* nothing to check */ break; } if (errors > 0) return -1; return 0;}/* Check the packet against the CIMD 2 spec, generating log entries as * necessary. Return -1 if anything was wrong, otherwise 0. *//* TODO: Check if parameters found actually belong in the packet type */static int packet_check(struct packet *packet){ int errors = 0; long pos, len, next; Octstr *data; gw_assert(packet != NULL); data = packet->data; if (octstr_search_char(data, 0, 0) >= 0) { /* CIMD2 spec does not allow NUL bytes in a packet */ warning(0, "CIMD2 packet contains NULs"); errors++; } /* Assume the packet starts with STX and ends with ETX, * because we parsed it that way in the first place. */ errors += (packet_check_header(packet) < 0); /* Parameters are separated by tabs. After the last parameter * there is a tab, an optional two-digit checksum, and the ETX. * Check each parameter in turn, by skipping from tab to tab. */ len = octstr_len(data); /* Start at the first tab, wherever it is, so that we can still * check parameters if the header was weird. */ pos = octstr_search_char(data, TAB, 0); for ( ; pos >= 0; pos = next) { next = octstr_search_char(data, TAB, pos + 1); if (next >= 0) { errors += (packet_check_parameter(packet, pos, next - pos) < 0); } else { /* Check if the checksum has the right format. Don't * check the sum itself here, that will be done in a * separate call later. */ /* There are two valid formats: TAB ETX (no checksum) * and TAB digit digit ETX. We already know the TAB * and the ETX are there. */ if (!(octstr_len(data) - pos == 2 || (octstr_len(data) - pos == 4 && octstr_check_range(data, pos + 1, 2, gw_isxdigit)))) { warning(0, "CIMD2 packet checksum in wrong format"); errors++; } } } if (errors > 0) { octstr_dump(packet->data, 0); return -1; } return 0;}static void packet_check_can_receive(struct packet *packet){ gw_assert(packet != NULL); if (!operation_can_receive(packet->operation)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -