📄 smsc_cimd2.c
字号:
response = packet_create(request->operation + RESPONSE, request->seq); packet_set_checksum(response); debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending <%s>", octstr_get_cstr(pdata->conn->id), octstr_get_cstr(response->data)); /* Don't check errors here because if there is something * wrong with the socket, the main loop will detect it. */ octstr_write_to_socket(pdata->socket, response->data); packet_destroy(response);}static Msg *cimd2_accept_message(struct packet *request, SMSCConn *conn){ 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 * 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) { convert_cimd2_to_gsm(text,conn); charset_gsm_to_latin1(text); } else { /* * FIXME: If DCS indicates GSM charset, and we get it in binary, * then it's probably bit-packed. We'll have to undo it because * our "charset_gsm" means one gsm character per octet. This is * not currently supported. -- RB */ text = packet_get_hex_parm(request, P_USER_DATA_BINARY); } /* 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[%s]: Got SMS without receiver, discarding.", octstr_get_cstr(conn->id)); goto error; } if (!origin || octstr_len(origin) == 0) { info(0, "CIMD2[%s]: Got SMS without sender, discarding.", octstr_get_cstr(conn->id)); goto error; } if (!text && (!UDH || octstr_len(UDH) == 0)) { info(0, "CIMD2[%s]: Got empty SMS, ignoring.", octstr_get_cstr(conn->id)); goto error; } message = msg_create(sms); if (! dcs_to_fields(&message, DCS)) { /* XXX Should reject this message ? */ debug("bb.sms.cimd2", 0, "CIMD2[%s]: Invalid DCS", octstr_get_cstr(conn->id)); dcs_to_fields(&message, 0); } time(&message->sms.time); 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, SMSCConn *conn){ PrivData *pdata = conn->data; Msg *message = NULL; if ((request->seq == 254 && pdata->receive_seq == 0) || request->seq == pdata->receive_seq - 2) { warning(0, "CIMD2[%s]: request had same sequence number as previous.", octstr_get_cstr(conn->id)); } else { pdata->receive_seq = request->seq + 2; if (pdata->receive_seq > 254) pdata->receive_seq = 0; if (request->operation == DELIVER_STATUS_REPORT) { message = cimd2_accept_delivery_report_message(request, conn); if (message) list_append(pdata->received, message); } else if (request->operation == DELIVER_MESSAGE) { message = cimd2_accept_message(request,conn); if (message) list_append(pdata->received, message); } } cimd2_send_response(request, pdata);}/* 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, SMSCConn *conn, Octstr **ts){ PrivData *pdata = conn->data; int ret; struct packet *reply = NULL; int errorcode; int tries = 0; gw_assert(pdata != NULL); gw_assert(request != NULL); gw_assert(operation_can_send(request->operation)); if (pdata->socket < 0) { warning(0, "CIMD2[%s]: cimd2_request: socket not open.", octstr_get_cstr(conn->id)); return -2; } retransmit: packet_set_send_sequence(request, pdata); packet_set_checksum(request); debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending <%s>", octstr_get_cstr(conn->id), octstr_get_cstr(request->data)); ret = octstr_write_to_socket(pdata->socket, request->data); if (ret < 0) goto io_error;next_reply: packet_destroy(reply); /* destroy old, if any */ reply = cimd2_get_packet(pdata, ts); if (!reply) goto io_error; errorcode = packet_display_error(reply,conn); if (reply->operation == NACK) { warning(0, "CIMD2[%s]: received NACK", octstr_get_cstr(conn->id)); 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 % 2) == 1) { warning(0, "CIMD2[%s]: correcting sequence number from %ld to %ld.", octstr_get_cstr(conn->id), (long) pdata->send_seq, (long) reply->seq); pdata->send_seq = reply->seq; } goto retry; } if (reply->operation == GENERAL_ERROR_RESPONSE) { error(0, "CIMD2[%s]: received general error response", octstr_get_cstr(conn->id)); 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, conn); 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[%s]: response had unexpected sequence number; ignoring.", octstr_get_cstr(conn->id)); 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]: %s request got a %s", octstr_get_cstr(conn->id), 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; } if (errorcode > 0) goto error; /* The reply passed all the checks... looks like the SMSC accepted * our request! */ packet_destroy(reply); return 0;io_error: packet_destroy(reply); return -2;error: packet_destroy(reply); return -1;retry: if (++tries < 3) { warning(0, "CIMD2[%s]: Retransmitting (take %d)", octstr_get_cstr(conn->id), tries); goto retransmit; } warning(0, "CIMD2[%s]: Giving up.", octstr_get_cstr(conn->id)); goto io_error;}/* Close the SMSC socket without fanfare. */static void cimd2_close_socket(PrivData *pdata){ gw_assert(pdata != NULL); if (pdata->socket < 0) return; if (close(pdata->socket) < 0) warning(errno, "CIMD2[%s]: error closing socket", octstr_get_cstr(pdata->conn->id)); pdata->socket = -1;}/* Open a socket to the SMSC, send a login packet, and wait for ack. * This may block. Return 0 for success, or -1 for failure. *//* Make sure the socket is closed before calling this function, otherwise * we will leak fd's. */static int cimd2_login(SMSCConn *conn){ PrivData *pdata = conn->data; int ret; struct packet *packet = NULL; gw_assert(pdata != NULL); if (pdata->socket >= 0) { warning(0, "CIMD2[%s]: login: socket was already open; closing", octstr_get_cstr(conn->id)); cimd2_close_socket(pdata); } pdata->socket = tcpip_connect_to_server_with_port( octstr_get_cstr(pdata->host), pdata->port, pdata->our_port, (conn->our_host ? octstr_get_cstr(conn->our_host) : NULL)); if (pdata->socket != -1) { packet = packet_create(LOGIN, BOGUS_SEQUENCE); packet_add_string_parm(packet, P_USER_IDENTITY, pdata->username, conn); packet_add_string_parm(packet, P_PASSWORD, pdata->password, conn); ret = cimd2_request(packet, conn, NULL); if (ret >= 0) { packet_destroy(packet); info(0, "CIMD2[%s] logged in.", octstr_get_cstr(conn->id)); return 0; } } error(0, "CIMD2[%s] login failed.", octstr_get_cstr(conn->id)); cimd2_close_socket(pdata); packet_destroy(packet); return -1;}static void cimd2_logout(SMSCConn *conn){ struct packet *packet = NULL; int ret; packet = packet_create(LOGOUT, BOGUS_SEQUENCE); /* TODO: Don't wait very long for a response in this case. */ ret = cimd2_request(packet, conn, NULL); if (ret == 0) { info(0, "CIMD2[%s] logged out.", octstr_get_cstr(conn->id)); } packet_destroy(packet);}static int cimd2_send_alive(SMSCConn *conn){ struct packet *packet = NULL; int ret; packet = packet_create(ALIVE, BOGUS_SEQUENCE); ret = cimd2_request(packet, conn, NULL); packet_destroy(packet); if (ret < 0) warning(0, "CIMD2[%s]: SMSC not alive.", octstr_get_cstr(conn->id)); return ret;}static void cimd2_destroy(PrivData *pdata){ int discarded; if (pdata == NULL) return; octstr_destroy(pdata->host); octstr_destroy(pdata->username); octstr_destroy(pdata->password); octstr_destroy(pdata->inbuffer); octstr_destroy(pdata->my_number); discarded = list_len(pdata->received); if (discarded > 0) warning(0, "CIMD2[%s]: discarded %d received messages", octstr_get_cstr(pdata->conn->id), discarded); list_destroy(pdata->received, msg_destroy_item); list_destroy(pdata->outgoing_queue, NULL); list_destroy(pdata->stopped, NULL); gw_free(pdata);}static int cimd2_submit_msg(SMSCConn *conn, Msg *msg){ PrivData *pdata = conn->data; struct packet *packet; Octstr *ts = NULL; int ret; gw_assert(pdata != NULL); debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending message",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -