📄 smsc_cimd2.c
字号:
octstr_set_char(packet->data, 5, buf[1]); octstr_set_char(packet->data, 6, buf[2]); packet->seq = seq;}static struct packet *packet_encode_message(Msg *msg, Octstr *sender_prefix){ struct packet *packet; Octstr *text; int spaceleft; long truncated; int dcs = 0; gw_assert(msg != NULL); gw_assert(msg->type == sms); gw_assert(msg->sms.receiver != NULL); dcs = fields_to_dcs(msg, 0); if (msg->sms.sender == NULL) msg->sms.sender = octstr_create(""); if (!parm_valid_address(msg->sms.receiver)) { warning(0, "cimd2_submit_msg: non-digits in " "destination phone number '%s', discarded", octstr_get_cstr(msg->sms.receiver)); return NULL; } if (!parm_valid_address(msg->sms.sender)) { warning(0, "cimd2_submit_msg: non-digits in " "originating phone number '%s', discarded", octstr_get_cstr(msg->sms.sender)); return NULL; } packet = packet_create(SUBMIT_MESSAGE, BOGUS_SEQUENCE); packet_add_address_parm(packet, P_DESTINATION_ADDRESS, msg->sms.receiver); /* CIMD2 interprets the originating address as a sub-address to * our connection number (so if the connection is "400" and we * fill in "600" as the sender number, the user sees "400600"). * Since we have no way to ask what this number is, it has to * be configured. */ if (octstr_len(sender_prefix) == 0) { /* Speed up the default case */ packet_add_address_parm(packet, P_ORIGINATING_ADDRESS, msg->sms.sender); } else if (octstr_compare(sender_prefix, octstr_imm("never")) != 0) { if (octstr_ncompare(sender_prefix, msg->sms.sender, octstr_len(sender_prefix)) == 0) { Octstr *sender; sender = octstr_copy(msg->sms.sender, octstr_len(sender_prefix), octstr_len(msg->sms.sender)); packet_add_address_parm(packet, P_ORIGINATING_ADDRESS, sender); octstr_destroy(sender); } else { warning(0, "CIMD2: Sending message with originating address <%s>, " "which does not start with the sender-prefix.", octstr_get_cstr(msg->sms.sender)); } } /* Explicitly ask not to get status reports. * If we do not do this, the server's default might be to * send status reports in some cases, and we don't do anything * with those reports anyway. */ /* ask for the delivery reports if needed*/ if (msg->sms.dlr_mask & 0x03) { packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 14); } else packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 0); truncated = 0; spaceleft = 140; if (octstr_len(msg->sms.udhdata)) { /* udhdata will be truncated and warned about if * it does not fit. */ packet_add_hex_parm(packet, P_USER_DATA_HEADER, msg->sms.udhdata); spaceleft -= octstr_len(msg->sms.udhdata); } if (msg->sms.coding == DC_7BIT) spaceleft = spaceleft * 8 / 7; if (spaceleft < 0) spaceleft = 0; text = octstr_duplicate(msg->sms.msgdata); if (octstr_len(text) > 0 && spaceleft == 0) { warning(0, "CIMD2: message filled up with " "UDH, no room for message text"); } else if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) { if (octstr_len(text) > spaceleft) { truncated = octstr_len(text) - spaceleft; octstr_truncate(text, spaceleft); } packet_add_hex_parm(packet, P_USER_DATA_BINARY, text); } else {#if CIMD2_TRACE debug("bb.sms.cimd2", 0, "CIMD2 sending message. Text:"); octstr_dump(text, 0);#endif /* Going from latin1 to GSM to CIMD2 may seem like a * detour, but it's the only way to get all the escape * codes right. */ charset_latin1_to_gsm(text); truncated = charset_gsm_truncate(text, spaceleft); convert_gsm_to_cimd2(text);#if CIMD2_TRACE debug("bb.sms.cimd2", 0, "After CIMD2 encoding:"); octstr_dump(text, 0);#endif packet_add_sms_parm(packet, P_USER_DATA, text); } if (dcs != 0) packet_add_int_parm(packet, P_DATA_CODING_SCHEME, dcs); if (truncated > 0) { warning(0, "CIMD2: truncating message text to fit " "in %d characters.", spaceleft); } octstr_destroy(text); return packet;}/***************************************************************************//* Protocol functions. These implement various transactions. *//***************************************************************************//* Give this packet a proper sequence number for sending. */static void packet_set_send_sequence(struct packet *packet, SMSCenter *smsc){ gw_assert(smsc != NULL); /* Send sequence numbers are always odd, receiving are always even */ gw_assert(smsc->cimd2_send_seq % 2 == 1); packet_set_sequence(packet, smsc->cimd2_send_seq); smsc->cimd2_send_seq += 2; if (smsc->cimd2_send_seq > 256) smsc->cimd2_send_seq = 1;}static struct packet *cimd2_get_packet(SMSCenter *smsc, Octstr **ts){ struct packet *packet = NULL; gw_assert(smsc != NULL); /* If packet is already available, don't try to read anything */ packet = packet_extract(smsc->cimd2_inbuffer); while (packet == NULL) { if (read_available(smsc->socket, RESPONSE_TIMEOUT) != 1) { warning(0, "CIMD2 SMSCenter is not responding"); return NULL; } if (octstr_append_from_socket(smsc->cimd2_inbuffer, smsc->socket) <= 0) { error(0, "cimd2_get_packet: read failed"); return NULL; } packet = packet_extract(smsc->cimd2_inbuffer); } packet_check(packet); packet_check_can_receive(packet); if(ts) *ts = packet_get_parm(packet,P_MC_TIMESTAMP); if (smsc->keepalive > 0) smsc->cimd2_next_ping = time(NULL) + 60 * smsc->keepalive; return packet;}/* Acknowledge a request. The CIMD 2 spec only defines positive responses * to the server, because the server is perfect. */static void cimd2_send_response(struct packet *request, SMSCenter *smsc){ struct packet *response; gw_assert(request != NULL); gw_assert(request->operation < RESPONSE); response = packet_create(request->operation + RESPONSE, request->seq); packet_set_checksum(response); /* Don't check errors here because if there is something * wrong with the socket, the main loop will detect it. */ octstr_write_to_socket(smsc->socket, response->data); packet_destroy(response);}static Msg *cimd2_accept_message(struct packet *request){ Msg *message = NULL; Octstr *destination = NULL; Octstr *origin = NULL; Octstr *UDH = NULL; Octstr *text = NULL; int DCS; /* See GSM 03.38. The bit patterns we can handle are: * 000xyyxx Uncompressed text, yy indicates alphabet. * yy = 00, default alphabet * yy = 01, 8-bit data * yy = 10, UCS2 (can't handle yet) * yy = 11, reserved * 1111xyxx Data, y indicates alphabet. * y = 0, default alphabet * y = 1, 8-bit data */ DCS = packet_get_int_parm(request, P_DATA_CODING_SCHEME); destination = packet_get_address_parm(request, P_DESTINATION_ADDRESS); origin = packet_get_address_parm(request, P_ORIGINATING_ADDRESS); UDH = packet_get_hex_parm(request, P_USER_DATA_HEADER); /* Text is either in User Data or User Data Binary field. */ text = packet_get_sms_parm(request, P_USER_DATA); if (text != NULL) {#if CIMD2_TRACE debug("bb.sms.cimd2", 0, "CIMD2 received message. Text:"); octstr_dump(text, 0);#endif convert_cimd2_to_gsm(text); charset_gsm_to_latin1(text);#if CIMD2_TRACE debug("bb.sms.cimd", 0, "Text in latin1:"); octstr_dump(text, 0);#endif } else { text = packet_get_hex_parm(request, P_USER_DATA_BINARY);#if CIMD2_TRACE debug("bb.sms.cimd2", 0, "CIMD2 received message. Text:"); octstr_dump(text, 0);#endif } /* Code elsewhere in the gateway always expects the sender and * receiver fields to be filled, so we discard messages that * lack them. If they should not be discarded, then the code * handling sms messages should be reviewed. -- RB */ if (!destination || octstr_len(destination) == 0) { info(0, "CIMD2: Got SMS without receiver, discarding."); goto error; } if (!origin || octstr_len(origin) == 0) { info(0, "CIMD2: Got SMS without sender, discarding."); goto error; } if ((!text || octstr_len(text) == 0) && (!UDH || octstr_len(UDH) == 0)) { info(0, "CIMD2: Got empty SMS, ignoring."); goto error; } message = msg_create(sms); if (! dcs_to_fields(&message, DCS)) { /* XXX Should reject this message ? */ debug("CIMD2", 0, "Invalid DCS"); dcs_to_fields(&message, 0); } message->sms.sender = origin; message->sms.receiver = destination; if (UDH) { message->sms.udhdata = UDH; } message->sms.msgdata = text; return message;error: msg_destroy(message); octstr_destroy(destination); octstr_destroy(origin); octstr_destroy(UDH); octstr_destroy(text); return NULL;}/* Deal with a request from the CIMD2 server, and acknowledge it. */static void cimd2_handle_request(struct packet *request, SMSCenter *smsc){ Msg *message = NULL; /* TODO: Check if the sequence number of this request is what we * expected. */ if (request->operation == DELIVER_STATUS_REPORT) { message = cimd2_accept_delivery_report_message(request,smsc); if (message) list_append(smsc->cimd2_received, message); } else if (request->operation == DELIVER_MESSAGE) { message = cimd2_accept_message(request); if (message) list_append(smsc->cimd2_received, message); } cimd2_send_response(request, smsc);}/* Send a request and wait for the ack. If the other side responds with * an error code, attempt to correct and retry. * If other packets arrive while we wait for the ack, handle them. * * Return -1 if the SMSC refused the request. Return -2 for other * errors, such as being unable to send the request at all. If the * function returns -2, the caller would do well to try to reopen the * connection. * * The SMSCenter must be already open. * * TODO: This function has grown large and complex. Break it up * into smaller pieces. */static int cimd2_request(struct packet *request, SMSCenter *smsc, Octstr **ts){ int ret; struct packet *reply = NULL; int errorcode; int tries = 0; gw_assert(smsc != NULL); gw_assert(request != NULL); gw_assert(operation_can_send(request->operation)); if (smsc->socket < 0) { warning(0, "cimd2_request: socket not open."); goto io_error; }retransmit: packet_set_send_sequence(request, smsc); packet_set_checksum(request); ret = octstr_write_to_socket(smsc->socket, request->data); if (ret < 0) goto io_error;next_reply: /*reply = cimd2_get_packet(smsc,ts);*/ reply = cimd2_get_packet(smsc, NULL); if (!reply) goto io_error; errorcode = packet_display_error(reply); if (reply->operation == NACK) { warning(0, "CIMD2 received NACK"); octstr_dump(reply->data, 0); /* Correct sequence number if server says it was wrong, * but only if server's number is sane. */ if (reply->seq != request->seq && (reply->seq % 1) == 1) { warning(0, "correcting sequence number from %ld to %ld.", (long) smsc->cimd2_send_seq, (long) reply->seq); smsc->cimd2_send_seq = reply->seq; } goto retry; } if (reply->operation == GENERAL_ERROR_RESPONSE) { error(0, "CIMD2 received general error response"); goto io_error; } /* The server sent us a request. Handle it, then wait for * a new reply. */ if (reply->operation < RESPONSE) { cimd2_handle_request(reply, smsc); packet_destroy(reply); goto next_reply; } if (reply->seq != request->seq) { /* We got a response to a different request number than * what we send. Strange. */ warning(0, "CIMD2: response had unexpected sequence number; ignoring.\n"); goto next_reply; } if (reply->operation != request->operation + RESPONSE) { /* We got a response that didn't match our request */ Octstr *request_name = operation_name(request->operation); Octstr *reply_name = operation_name(reply->operation); warning(0, "CIMD2: %s request got a %s", octstr_get_cstr(request_name), octstr_get_cstr(reply_name)); octstr_destroy(request_name); octstr_destroy(reply_name); octstr_dump(reply->data, 0); goto retry; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -