view utils/smpp-test2.c @ 221:e1d7db9d734c

smpp-test2 program written
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 03 Aug 2023 10:00:33 -0800
parents utils/smpp-test1.c@c798a1762c7c
children
line wrap: on
line source

/*
 * This program connects to an SMPP server in the role of a client,
 * sends a bind_transceiver request, and then goes into a receiving loop,
 * dumping everything that comes back from the server.  Received packets
 * are also checked for some command opcodes to which we need to generate
 * a response, and simple auto-responses are generated.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

static int tcpsock;
static struct sockaddr_in server_sin;
static u_char bind_req[64];
static unsigned bind_req_len;
static char system_id[16], password[9];
static u_char rx_hdr[16];
static unsigned rx_pkt_len, rx_command_id;

static void
construct_bind_req()
{
	u_char *dp;
	unsigned slen;

	dp = bind_req + 4;	/* length will be filled last */
	/* command_id */
	*dp++ = 0;
	*dp++ = 0;
	*dp++ = 0;
	*dp++ = 0x09;		/* bind_transceiver */
	/* empty command_status */
	*dp++ = 0;
	*dp++ = 0;
	*dp++ = 0;
	*dp++ = 0;
	/* sequence_number */
	*dp++ = 0;
	*dp++ = 0;
	*dp++ = 0;
	*dp++ = 1;
	/* system_id */
	slen = strlen(system_id) + 1;
	bcopy(system_id, dp, slen);
	dp += slen;
	/* password */
	slen = strlen(password) + 1;
	bcopy(password, dp, slen);
	dp += slen;
	/* system_type */
	strcpy(dp, "SMPP");
	dp += 5;
	/* interface_version */
	*dp++ = 0x34;
	/* addr_ton */
	*dp++ = 0;
	/* addr_npi */
	*dp++ = 0;
	/* address_range */
	*dp++ = 0;
	bind_req_len = dp - bind_req;
	bind_req[0] = bind_req_len >> 24;
	bind_req[1] = bind_req_len >> 16;
	bind_req[2] = bind_req_len >> 8;
	bind_req[3] = bind_req_len;
}

static void
print_bind_req()
{
	unsigned off, chunk;
	int i, c;

	printf("Constructed bind request of %u bytes\n", bind_req_len);
	for (off = 0; off < bind_req_len; off += chunk) {
		chunk = bind_req_len - off;
		if (chunk > 16)
			chunk = 16;
		printf("%02X:  ", off);
		for (i = 0; i < 16; i++) {
			if (i < chunk)
				printf("%02X ", bind_req[off + i]);
			else
				fputs("   ", stdout);
			if (i == 7 || i == 15)
				putchar(' ');
		}
		for (i = 0; i < chunk; i++) {
			c = bind_req[off + i];
			if (c < ' ' || c > '~')
				c = '.';
			putchar(c);
		}
		putchar('\n');
	}
}

static void
init_stage()
{
	int rc;

	rc = connect(tcpsock, (struct sockaddr *) &server_sin,
		     sizeof(struct sockaddr_in));
	if (rc < 0) {
		perror("connect");
		exit(1);
	}
	rc = write(tcpsock, bind_req, bind_req_len);
	if (rc != bind_req_len) {
		perror("write");
		exit(1);
	}
}

static void
rx_bytes(buf, need_len)
	u_char *buf;
	unsigned need_len;
{
	int cc;
	unsigned remain;

	for (remain = need_len; remain; remain -= cc) {
		cc = read(tcpsock, buf, remain);
		if (cc <= 0) {
			perror("read");
			exit(1);
		}
	}
}

static void
print_hdr()
{
	int i, j, pos;

	fputs("Got header:", stdout);
	pos = 0;
	for (i = 0; i < 4; i++) {
		putchar(' ');
		for (j = 0; j < 4; j++)
			printf("%02X", rx_hdr[pos++]);
	}
	putchar('\n');
}

static void
preen_rx_hdr()
{
	rx_pkt_len = (rx_hdr[0] << 24) | (rx_hdr[1] << 16) | (rx_hdr[2] << 8) |
			rx_hdr[3];
	printf("Rx packet length: %u bytes\n", rx_pkt_len);
	if (rx_pkt_len < 16) {
		printf("Error: packet length is too short\n");
		exit(1);
	}
	rx_command_id = (rx_hdr[4] << 24) | (rx_hdr[5] << 16) |
			(rx_hdr[6] << 8) | rx_hdr[7];
}

static void
read_and_dump_body()
{
	u_char buf[16];
	unsigned offset, chunk;
	int i, c;

	for (offset = 16; offset < rx_pkt_len; offset += chunk) {
		chunk = rx_pkt_len - offset;
		if (chunk > 16)
			chunk = 16;
		rx_bytes(buf, chunk);
		printf("%08X:  ", offset);
		for (i = 0; i < 16; i++) {
			if (i < chunk)
				printf("%02X ", buf[i]);
			else
				fputs("   ", stdout);
			if (i == 7 || i == 15)
				putchar(' ');
		}
		for (i = 0; i < chunk; i++) {
			c = buf[i];
			if (c < ' ' || c > '~')
				c = '.';
			putchar(c);
		}
		putchar('\n');
	}
}

static void
resp_hdr_only()
{
	u_char resp[16];
	int rc;

	/* command_length */
	resp[0] = 0;
	resp[1] = 0;
	resp[2] = 0;
	resp[3] = 16;
	/* command_id */
	resp[4] = rx_hdr[4] | 0x80;
	resp[5] = rx_hdr[5];
	resp[6] = rx_hdr[6];
	resp[7] = rx_hdr[7];
	/* command_status */
	resp[8] = 0;
	resp[9] = 0;
	resp[10] = 0;
	resp[11] = 0;
	/* sequence_number */
	resp[12] = rx_hdr[12];
	resp[13] = rx_hdr[13];
	resp[14] = rx_hdr[14];
	resp[15] = rx_hdr[15];
	/* good to go */
	rc = write(tcpsock, resp, 16);
	if (rc != 16) {
		perror("write");
		exit(1);
	}
}

static void
resp_dummy_msgid()
{
	u_char resp[17];
	int rc;

	/* command_length */
	resp[0] = 0;
	resp[1] = 0;
	resp[2] = 0;
	resp[3] = 17;
	/* command_id */
	resp[4] = rx_hdr[4] | 0x80;
	resp[5] = rx_hdr[5];
	resp[6] = rx_hdr[6];
	resp[7] = rx_hdr[7];
	/* command_status */
	resp[8] = 0;
	resp[9] = 0;
	resp[10] = 0;
	resp[11] = 0;
	/* sequence_number */
	resp[12] = rx_hdr[12];
	resp[13] = rx_hdr[13];
	resp[14] = rx_hdr[14];
	resp[15] = rx_hdr[15];
	/* empty message_id */
	resp[16] = 0;
	/* good to go */
	rc = write(tcpsock, resp, 17);
	if (rc != 17) {
		perror("write");
		exit(1);
	}
}

static void
auto_resp_logic()
{
	switch (rx_command_id) {
	case 0x005:
		printf("Got deliver_sm, responding with deliver_sm_resp\n");
		resp_dummy_msgid();
		return;
	case 0x103:
		printf("Got data_sm, responding with data_sm_resp\n");
		resp_dummy_msgid();
		return;
	case 0x015:
		printf("Got enquire_link, responding with enquire_link_resp\n");
		resp_hdr_only();
		return;
	}
}

main(argc, argv)
	char **argv;
{
	if (argc < 3 || argc > 4) {
		fprintf(stderr, "usage: %s server-ip system-id [password]\n",
			argv[0]);
		exit(1);
	}
	server_sin.sin_family = AF_INET;
	server_sin.sin_addr.s_addr = inet_addr(argv[1]);
	if (server_sin.sin_addr.s_addr == INADDR_NONE) {
		fprintf(stderr, "error: invalid IP address argument \"%s\"\n",
			argv[1]);
		exit(1);
	}
	server_sin.sin_port = htons(2775);
	if (strlen(argv[2]) > 15) {
		fprintf(stderr, "error: system-id string is too long\n");
		exit(1);
	}
	strcpy(system_id, argv[2]);
	if (argv[3]) {
		if (strlen(argv[3]) > 8) {
			fprintf(stderr, "error: password string is too long\n");
			exit(1);
		}
		strcpy(password, argv[3]);
	}
	construct_bind_req();
	setlinebuf(stdout);
	print_bind_req();
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
		perror("socket");
		exit(1);
	}
	init_stage();
	for (;;) {
		rx_bytes(rx_hdr, 16);
		print_hdr();
		preen_rx_hdr();
		read_and_dump_body();
		auto_resp_logic();
	}
}