/* ==================================================================== * The Kannel Software License, Version 1.0 * * Copyright (c) 2001-2005 Kannel Group * Copyright (c) 1998-2001 WapIT Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Kannel Group (http://www.kannel.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Kannel" and "Kannel Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact org@kannel.org. * * 5. Products derived from this software may not be called "Kannel", * nor may "Kannel" appear in their name, without prior written * permission of the Kannel Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Kannel Group. For more information on * the Kannel Group, please see . * * Portions of this software are based upon software originally written at * WapIT Ltd., Helsinki, Finland for the Kannel project. */ /* test_cimd2.c - fake cimd2 smsc * * This program pretends to be an CIMD 2 SMS center, accessible via IP. * It is used to test the Kannel smsc_cimd2 code. * * Richard Braakman */ /* Note: The CIMD2 parsing code was written as a prototype, and currently * its main use is to exercise the *real* CIMD2 code in gw/smsc_cimd2.c. * Please don't use this code for anything real. * Richard Braakman */ /* * TODO: If log level is high and activity level is low, there will be * "SND" log entries for packets that are not sent, which is confusing * and should be fixed. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gwlib/gwlib.h" enum { TIMESTAMP_MAXLEN = 13 }; unsigned char *progname; /* Set up a fake account for Kannel to use */ unsigned char *username = "foo"; unsigned char *password = "bar"; int port = 6789; /* This can be useful to get past protocol-ID checks when testing spew. */ unsigned char *intro = ""; enum ACT { ACT_listen = 0, ACT_reply = 1, ACT_deliver = 2, ACT_flood = 3 }; enum SPEW { SPEW_nothing = 0, SPEW_binary = 1, SPEW_characters = 2, SPEW_packets = 3 }; enum LOG { LOG_nothing = 0, LOG_data = 1, LOG_packets = 2, LOG_sms = 3 }; enum CHK { CHK_nothing = 0, CHK_packets = 1, CHK_sums = 2, CHK_protocol = 3, CHK_sms = 4 }; int activity = ACT_listen; int spew = SPEW_nothing; int logging = LOG_nothing; int checking = CHK_nothing; int max_deliveries = -1; int deliveries = 0; time_t start_time = 0; int sockfd = -1; Octstr *inbuffer; Octstr *outbuffer; /* Maximum reasonable outbuffer size. It can go above this, but we don't * deliberately add data when it's already more than this. */ enum { OUTBUFFER_LIMIT = 65536 }; /* Test dependencies on neatly-sized read and write chunks, by using * a deliberately evil buffer size. 1021 is the largest prime smaller * than 1024. */ enum { EVIL_BUFSIZE = 1021 }; enum CHARS { STX = 2, ETX = 3, TAB = 9, LF = 10, CR = 13 }; static void usage(FILE *out) { fprintf(out, "Usage: %s [options...]\n" " --help Print this message\n" " --user USER Allow clients to log in with username USER (default %s)\n" " --password PASS Allow clients to log in with password PASS (default %s)\n" " --intro INTRO Send INTRO string before anything else (default nothing)\n" " --port PORT TCP port to listen on (default %d)\n" " --activity ACT Activity level of test server (default %d)\n" " ACT = 0 send nothing, just listen\n" " ACT = 1 send valid replies, do not initiate any transactions\n" " ACT = 2 attempt to deliver a random SMS every few seconds (NI)\n" " ACT = 3 deliver many random SMSes, measure throughput (NI)\n" " --spew SPEW Flood client, overrides --activity (default %d)\n" " SPEW = 0 don't spew, use --activity instead\n" " SPEW = 1 spew random binary gunk at client\n" " SPEW = 2 spew random data of the right character set at client (NI)\n" " SPEW = 3 spew valid packets with random contents at client (NI)\n" " --logging LOG Log level of test server (default %d)\n" " LOG = 0 log nothing\n" " LOG = 1 log all data\n" " LOG = 2 log summaries of valid packets\n" " LOG = 3 log successfully sent and received SMSes (NI)\n" " --checking CHK Check level of test server (default %d)\n" " CHK = 0 check nothing\n" " CHK = 1 signal invalid packets (NI)\n" " CHK = 2 signal checksum errors (NI)\n" " CHK = 3 signal protocol errors (NI)\n" " CHK = 4 signal invalid SMS contents (NI)\n" " --max MAX With high activity values, stop after MAX deliveries\n" " NI means Not Implemented\n" , progname, username, password, port, activity, spew, logging, checking); } static void pretty_print(unsigned char *data, size_t length) { size_t i; int c; for (i = 0; i < length; i++) { c = data[i]; switch(c) { default: if (isprint(c)) putchar(c); else printf("<%d>", c); break; case TAB: fputs("", stdout); break; case LF: fputs("\n", stdout); break; case CR: fputs("", stdout); break; case STX: fputs("", stdout); break; case ETX: fputs("\n", stdout); break; } } fflush(stdout); } static void read_data(Octstr *in, int fd) { unsigned char buf[EVIL_BUFSIZE]; int ret; ret = read(fd, buf, sizeof(buf)); if (ret > 0) { octstr_append_data(in, buf, ret); if (logging == LOG_data) pretty_print(buf, ret); } else if (ret == 0) { fprintf(stderr, "Client closed socket\n"); exit(0); } else { if (errno == EINTR || errno == EAGAIN) return; error(errno, "read_data"); exit(1); } } static void write_data(Octstr *out, int fd) { unsigned char buf[EVIL_BUFSIZE]; int len; ssize_t ret; len = sizeof(buf); if (len > octstr_len(out)) len = octstr_len(out); if (len == 0) return; octstr_get_many_chars(buf, out, 0, len); ret = write(fd, buf, len); if (ret > 0) { if (logging == LOG_data) pretty_print(buf, ret); octstr_delete(out, 0, ret); } else if (ret == 0) { warning(0, "empty write"); } else { if (errno == EINTR || errno == EAGAIN) return; error(errno, "write_data"); exit(1); } } static void gen_message(Octstr *out); /* Return the minimum interval (in microseconds) after which we will * want to be called again. This value is only used if we _don't_ * generate data this time through. */ static long gen_data(Octstr *out) { unsigned char buf[EVIL_BUFSIZE]; size_t i; long interval = -1; static int last_sms; /* Used by ACT_deliver */ time_t now; if (max_deliveries < 0 || deliveries < max_deliveries) { switch (activity) { case ACT_deliver: now = time(NULL); if (last_sms == 0) last_sms = now; while (last_sms < now) { if (random() % 7 == 1) { gen_message(out); last_sms = now; } else last_sms++; } interval = 1000000; break; case ACT_flood: gen_message(out); break; } } switch (spew) { case SPEW_binary: for (i = 0; i < sizeof(buf); i++) { buf[i] = random() % 256; } octstr_append_data(out, buf, sizeof(buf)); break; } return interval; } /******************************* CIMD 2 specific code ************************/ int awaiting_response = 0; /* buf must be at least TIMESTAMP_MAXLEN bytes long. */ static void make_timestamp(unsigned char *buf, time_t fortime) { /* Is there a thread-safe version of gmtime? */ struct tm tm = gw_gmtime(fortime); sprintf(buf, "%02d%02d%02d%02d%02d%02d", tm.tm_year % 100, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } static void send_packet(Octstr *out, int opcode, int sequence, ...) { va_list ap; int parm; unsigned char *value; int checksum; int old_len, new_len; if (activity == ACT_listen) return; old_len = octstr_len(out); octstr_format_append(out, "%c%02d:%03d%c", STX, opcode, sequence, TAB); va_start(ap, sequence); for (parm = va_arg(ap, int); parm != 0; parm = va_arg(ap, int)) { value = va_arg(ap, unsigned char *); octstr_format_append(out, "%03d:%s\11", parm, value); } va_end(ap); /* Calculate checksum */ checksum = 0; for (new_len = octstr_len(out); old_len < new_len; old_len++) { checksum = (checksum + octstr_get_char(out, old_len)) & 0xff; } octstr_format_append(out, "%02X%c", checksum, ETX); } static void send_error(Octstr *out, int opcode, int sequence, unsigned char *errorcode, unsigned char *errortext) { if (logging == LOG_packets) printf("SND: ERROR, %s\n", errortext); send_packet(out, opcode, sequence, 900, errorcode, 901, errortext, 0); } static int eat_char(Octstr *packet, int ch) { if (octstr_get_char(packet, 0) == ch) { octstr_delete(packet, 0, 1); return 0; } return -1; } static Octstr *eat_string_parm(Octstr *packet, int parm, int maxlen) { long start, datastart; long tab; Octstr *result; Octstr *parmheader; parmheader = octstr_format("%c%03d:", TAB, parm); start = octstr_search(packet, parmheader, 0); if (start < 0) { octstr_destroy(parmheader); return NULL; } datastart = start + octstr_len(parmheader); tab = octstr_search_char(packet, TAB, datastart + 1); if (tab < 0) { tab = octstr_len(packet); } result = octstr_copy(packet, datastart, tab - datastart); octstr_delete(packet, start, tab - start); octstr_destroy(parmheader); return result; } static long eat_number(Octstr *ostr) { long result; long pos; pos = octstr_parse_long(&result, ostr, 0, 10); if (pos < 0) return INT_MIN; octstr_delete(ostr, 0, pos); return result; } static long eat_int_parm(Octstr *packet, int parm, int maxlen) { Octstr *value; long result; value = eat_string_parm(packet, parm, maxlen); if (!value) return INT_MIN; result = eat_number(value); if (octstr_len(value) > 0) result = INT_MIN; octstr_destroy(value); return result; } static void eat_checksum(Octstr *packet) { int len; int ch1, ch2, ch3; len = octstr_len(packet); if (len < 3) return; ch1 = octstr_get_char(packet, len - 3); ch2 = octstr_get_char(packet, len - 2); ch3 = octstr_get_char(packet, len - 1); if (isxdigit(ch3) && isxdigit(ch2) && ch1 == TAB) octstr_delete(packet, len - 3, 3); } static void handle_login(Octstr *packet, Octstr *out, int sequence) { Octstr *user = eat_string_parm(packet, 10, 32); Octstr *pass = eat_string_parm(packet, 11, 32); if (user == NULL) user = octstr_create(""); if (pass == NULL) pass = octstr_create(""); if (logging == LOG_packets) printf("RCV: Login user '%s', password '%s'\n", octstr_get_cstr(user), octstr_get_cstr(pass)); if (octstr_str_compare(user, username) == 0 && octstr_str_compare(pass, password) == 0) { if (logging == LOG_packets) printf("SND: Login OK\n"); send_packet(out, 51, sequence, 0); } else { send_error(out, 51, sequence, "100", "invalid login"); } octstr_destroy(user); octstr_destroy(pass); } static void handle_logout(Octstr *packet, Octstr *out, int sequence) { if (logging == LOG_packets) printf("RCV: Logout\n"); if (logging == LOG_packets) printf("SND: Logout OK\n"); send_packet(out, 52, sequence, 0); } static void handle_submit(Octstr *packet, Octstr *out, int sequence) { Octstr *dest_addr = eat_string_parm(packet, 21, 20); Octstr *orig_addr = eat_string_parm(packet, 23, 20); long DCS = eat_int_parm(packet, 30, 3); Octstr *UDH = eat_string_parm(packet, 32, 280); Octstr *text = eat_string_parm(packet, 33, 480); Octstr *textb = eat_string_parm(packet, 34, 280); long valid_rel = eat_int_parm(packet, 50, 3); Octstr *valid_abs = eat_string_parm(packet, 51, 12); long proto_id = eat_int_parm(packet, 52, 3); long delivery_rel = eat_int_parm(packet, 53, 3); Octstr *delivery_abs = eat_string_parm(packet, 54, 12); long reply_path = eat_int_parm(packet, 55, 1); long SRR = eat_int_parm(packet, 56, 2); long cancel = eat_int_parm(packet, 58, 1); long tariff_class = eat_int_parm(packet, 64, 2); long service_desc = eat_int_parm(packet, 65, 1); long priority = eat_int_parm(packet, 67, 1); List *other_dests = gwlist_create(); Octstr *tmp; while ((tmp = eat_string_parm(packet, 21, 20))) gwlist_append(other_dests, tmp); if (logging == LOG_packets) { int i; printf("RCV: Submit to %s", octstr_get_cstr(dest_addr)); for (i = 0; i < gwlist_len(other_dests); i++) { printf(", %s", octstr_get_cstr(gwlist_get(other_dests, i))); } printf("\n"); if (orig_addr) printf(" From: %s\n", octstr_get_cstr(orig_addr)); if (DCS > INT_MIN) printf(" Data coding: %ld\n", DCS); if (UDH) printf(" User data header: %s\n", octstr_get_cstr(UDH)); if (text) printf(" Text: %s\n", octstr_get_cstr(text)); if (textb) printf(" Text (binary): %s\n", octstr_get_cstr(textb)); if (valid_rel > INT_MIN) printf(" Validity period: %ld (relative)\n", valid_rel); if (valid_abs) printf(" Validity period: %s (absolute)\n", octstr_get_cstr(valid_abs)); if (proto_id > INT_MIN) printf(" Protocol ID: %ld\n", proto_id); if (delivery_rel > INT_MIN) printf(" First delivery: %ld (relative)\n", delivery_rel); if (delivery_abs) printf(" First delivery: %s (absolute)\n", octstr_get_cstr(delivery_abs)); if (reply_path == 0) printf(" Reply path disabled\n"); else if (reply_path == 1) printf(" Reply path enabled\n"); else if (reply_path > INT_MAX) printf(" Reply path: %ld\n", reply_path); if (SRR > INT_MAX) printf(" Status report flags: %ld\n", SRR); if (cancel == 0) printf(" Cancel disabled\n"); else if (cancel == 1) printf(" Cancel enabled\n"); else if (cancel > INT_MAX) printf(" Cancel enabled: %ld\n", cancel); if (tariff_class > INT_MAX) printf(" Tariff class: %ld\n", tariff_class); if (service_desc > INT_MAX) printf(" Service description: %ld\n", service_desc); if (priority > INT_MAX) printf(" Priority: %ld\n", priority); } if (!dest_addr) { send_error(out, 53, sequence, "300", "no destination"); } else if (gwlist_len(other_dests) > 0) { send_error(out, 53, sequence, "301", "too many destinations"); /* TODO: Report many other possible errors here */ } else { unsigned char buf[TIMESTAMP_MAXLEN]; make_timestamp(buf, time(NULL)); if (logging == LOG_packets) printf("SND: Submit OK\n"); send_packet(out, 53, sequence, 21, octstr_get_cstr(dest_addr), 60, buf, 0); } octstr_destroy(dest_addr); octstr_destroy(orig_addr); octstr_destroy(UDH); octstr_destroy(text); octstr_destroy(textb); octstr_destroy(valid_abs); octstr_destroy(delivery_abs); gwlist_destroy(other_dests, octstr_destroy_item); } static void handle_enquire(Octstr *packet, Octstr *out, int sequence) { Octstr *dest_addr = eat_string_parm(packet, 21, 20); Octstr *timestamp = eat_string_parm(packet, 60, 12); if (logging == LOG_packets) printf("RCV: Enquire status, dest='%s', time='%s'\n", dest_addr ? octstr_get_cstr(dest_addr) : "", timestamp ? octstr_get_cstr(timestamp) : ""); if (!dest_addr) { send_error(out, 54, sequence, "400", "no destination"); } else if (!timestamp) { send_error(out, 54, sequence, "401", "no timestamp"); } else { if (logging == LOG_packets) printf("SND: Respond: status unknown\n"); send_packet(out, 54, sequence, 21, octstr_get_cstr(dest_addr), 60, octstr_get_cstr(timestamp), 61, "0", 0); } octstr_destroy(dest_addr); octstr_destroy(timestamp); } static void handle_delivery_request(Octstr *packet, Octstr *out, int sequence) { long mode = eat_int_parm(packet, 68, 1); if (logging == LOG_packets) { switch (mode) { case 0: printf("RCV: Delivery request, messages waiting?\n"); break; case 1: printf("RCV: Delivery request, one message\n"); break; case 2: printf("RCV: Delivery request, all messages\n"); break; case INT_MIN: printf("RCV: Delivery request, no mode\n"); break; default: printf("RCV: Delivery request, mode %ld\n", mode); } } if (mode == INT_MIN) mode = 1; switch (mode) { case 0: if (logging == LOG_packets) printf("SND: Respond: 0 messages\n"); send_packet(out, 55, sequence, 66, "0", 0); break; case 1: send_error(out, 55, sequence, "500", "no messages available"); break; case 2: send_error(out, 55, sequence, "500", "no messages available"); break; default: send_error(out, 55, sequence, "501", "bad mode"); break; } } static void handle_cancel(Octstr *packet, Octstr *out, int sequence) { long mode = eat_int_parm(packet, 59, 1); Octstr *timestamp = eat_string_parm(packet, 60, 12); Octstr *destination = eat_string_parm(packet, 21, 20); if (logging == LOG_packets) { printf("RCV: Cancel"); if (mode != INT_MIN) printf(", mode %ld", mode); if (destination) printf(", dest '%s'", octstr_get_cstr(destination)); if (timestamp) printf(", time '%s'", octstr_get_cstr(timestamp)); printf("\n"); } if (mode < 0 || mode > 2) send_error(out, 56, sequence, "602", "bad mode"); else { if (logging == LOG_packets) printf("SND: OK\n"); send_packet(out, 56, sequence, 0); } } static void handle_set(Octstr *packet, Octstr *out, int sequence) { Octstr *pass = eat_string_parm(packet, 11, 32); if (pass) { if (logging == LOG_packets) printf("RCV: Set password to '%s'\n", octstr_get_cstr(pass)); send_error(out, 58, sequence, "801", "changing password not allowed"); } else { if (logging == LOG_packets) printf("RCV: Set, unknown parameters\n"); send_error(out, 58, sequence, "3", "cannot set"); } } static void handle_get(Octstr *packet, Octstr *out, int sequence) { long number = eat_int_parm(packet, 500, 3); if (logging == LOG_packets) printf("RCV: Get parameter #%ld\n", number); if (number == INT_MIN) { send_error(out, 59, sequence, "900", "missing parameter"); } else if (number == 501) { unsigned char buf[TIMESTAMP_MAXLEN]; make_timestamp(buf, time(NULL)); if (logging == LOG_packets) printf("SND: OK, SMSC timestamp is '%s'\n", buf); send_packet(out, 59, sequence, 501, buf, 0); } else { send_error(out, 59, sequence, "900", "unknown parameter"); } } static void handle_alive(Octstr *packet, Octstr *out, int sequence) { if (logging == LOG_packets) printf("RCV: Alive?\n"); if (logging == LOG_packets) printf("SND: Alive.\n"); send_packet(out, 90, sequence, 0); } static void handle_deliver_response(Octstr *packet, Octstr *out, int sequence) { awaiting_response = 0; if (logging == LOG_packets) printf("RCV: Deliver response\n"); deliveries++; if (max_deliveries > 0 && deliveries == max_deliveries) { time_t elapsed = time(NULL) - start_time; printf("LOG: %ld deliveries in %ld seconds\n", (long) max_deliveries, (long) elapsed); } /* No need to respond to a response */ } static void handle_deliver_status_report_response(Octstr *packet, Octstr *out, int sequence) { awaiting_response = 0; if (logging == LOG_packets) printf("RCV: Deliver status report response\n"); /* No need to respond to a response */ } static void handle_alive_response(Octstr *packet, Octstr *out, int sequence) { awaiting_response = 0; if (logging == LOG_packets) printf("RCV: Alive.\n"); /* No need to respond to a response */ } static void handle_nack(Octstr *packet, Octstr *out, int sequence) { awaiting_response = 0; if (logging == LOG_packets) printf("RCV: NACK\n"); /* TODO: We should retransmit if we get a nack, but there's * no record of what request we sent. */ } typedef void (*packet_handler)(Octstr *, Octstr *, int); struct { int opcode; packet_handler handler; } handlers[] = { { 1, handle_login }, { 2, handle_logout }, { 3, handle_submit }, { 4, handle_enquire }, { 5, handle_delivery_request }, { 6, handle_cancel }, { 8, handle_set }, { 9, handle_get }, { 40, handle_alive }, { 70, handle_deliver_response }, { 73, handle_deliver_status_report_response }, { 90, handle_alive_response }, { 99, handle_nack }, { -1, NULL }, }; static void parse_packet(Octstr *packet, Octstr *out) { int opcode, sequence; int i; eat_checksum(packet); opcode = eat_number(packet); if (opcode < 0 || eat_char(packet, ':') < 0) return; sequence = eat_number(packet); if (sequence < 0) return; for (i = 0; handlers[i].opcode >= 0; i++) { if (handlers[i].opcode == opcode) { (handlers[i].handler)(packet, out, sequence); break; } } if (handlers[i].opcode < 0) { /* Loop failed */ if (logging == LOG_packets) printf("RCV: unknown operation %ld\n", (long) handlers[i].opcode); send_error(out, 98, sequence, "1", "unexpected operation"); } } /* Parse the data stream for packets, and send out replies. */ static void parse_data(Octstr *in, Octstr *out) { int stx, etx; Octstr *packet; for (;;) { /* Look for start of packet. Delete everything up to the start * marker. (CIMD2 section 3.1 says we can ignore any data * transmitted between packets.) */ stx = octstr_search_char(in, STX, 0); if (stx < 0) octstr_delete(in, 0, octstr_len(in)); else if (stx > 0) octstr_delete(in, 0, stx); etx = octstr_search_char(in, ETX, 0); if (etx < 0) return; /* Incomplete packet; wait for more data. */ /* Copy the data between stx and etx */ packet = octstr_copy(in, 1, etx - 1); /* Then cut the packet (including stx and etx) from inbuffer */ octstr_delete(in, 0, etx + 1); parse_packet(packet, out); octstr_destroy(packet); } } static void random_address(unsigned char *buf, int size) { int len = random() % size; while (len--) { *buf++ = '0' + random() % 10; } *buf++ = '\0'; } static void random_message(unsigned char *buf, int size) { int len = random() % size; while (len--) { do { *buf = random() % 256; } while (*buf == STX || *buf == ETX || *buf == TAB); buf++; } *buf++ = '\0'; } static void random_hex(unsigned char *buf, int size) { int len = random() % size; /* Make even */ len -= (len % 2); while (len--) { int c = random() % 16; if (c < 10) *buf++ = c + '0'; else *buf++ = c - 10 + 'a'; } *buf++ = '\0'; } static void gen_message(Octstr *out) { static int send_seq = 0; unsigned char dest[21]; unsigned char orig[21]; unsigned char scts[TIMESTAMP_MAXLEN]; unsigned char message[481]; unsigned char udh[281]; if (awaiting_response == 1) return; random_address(dest, sizeof(dest)); random_address(orig, sizeof(orig)); make_timestamp(scts, time(NULL)); random_message(message, sizeof(message)); if (random() % 2 == 0) random_hex(udh, sizeof(udh)); else *udh = 0; if (logging == LOG_packets) printf("SND: Deliver message (random)\n"); if (*udh) { send_packet(out, 20, send_seq, 21, dest, 23, orig, 60, scts, 32, udh, 33, message, 0); } else { send_packet(out, 20, send_seq, 21, dest, 23, orig, 60, scts, 33, message, 0); } send_seq += 2; if (send_seq > 255) send_seq = 0; awaiting_response = 1; } /************************** CIMD 2 specific code ends ************************/ static void main_loop(void) { fd_set readfds, writefds; int n; static int reported_outfull = 0; int interval = -1; inbuffer = octstr_create(""); outbuffer = octstr_create(intro); start_time = time(NULL); for (;;) { if (octstr_len(outbuffer) < OUTBUFFER_LIMIT) { interval = gen_data(outbuffer); } else if (!reported_outfull) { warning(0, "outbuffer getting full; waiting..."); reported_outfull = 1; } FD_ZERO(&readfds); FD_SET(sockfd, &readfds); if (octstr_len(outbuffer) > 0) { FD_ZERO(&writefds); FD_SET(sockfd, &writefds); n = select(sockfd+1, &readfds, &writefds, NULL, NULL); } else { struct timeval tv; struct timeval *tvp; if (interval >= 0) { tv.tv_sec = 0; tv.tv_usec = interval; tvp = &tv; } else { tvp = NULL; } n = select(sockfd+1, &readfds, NULL, NULL, tvp); } if (n < 0) { if (errno == EINTR) { warning(errno, "main loop, select"); continue; } error(errno, "main loop, select"); sleep(1); continue; } if (n > 0) { if (FD_ISSET(sockfd, &readfds)) { read_data(inbuffer, sockfd); parse_data(inbuffer, outbuffer); } if (octstr_len(outbuffer) > 0 && FD_ISSET(sockfd, &writefds)) { write_data(outbuffer, sockfd); } if (octstr_len(outbuffer) < OUTBUFFER_LIMIT) { reported_outfull = 0; } } } } static struct { unsigned char *option; void *location; int number; } options[] = { { "--user", &username, 0 }, { "--password", &password, 0 }, { "--port", &port, 1 }, { "--intro", &intro, 0 }, { "--activity", &activity, 1 }, { "--spew", &spew, 1 }, { "--logging", &logging, 1 }, { "--checking", &checking, 1 }, { "--max", &max_deliveries, 1 }, { NULL, NULL, 0 }, }; static int wait_for_client(int port) { struct sockaddr_in sin; socklen_t addrlen; int listenfd; int clientfd; Octstr *addr; listenfd = make_server_socket(port, NULL); if (listenfd < 0) { fprintf(stderr, "%s: failed to open socket at port %d\n", progname, port); exit(1); } do { addrlen = sizeof(sin); clientfd = accept(listenfd, (struct sockaddr *)&sin, &addrlen); if (clientfd < 0) { error(errno, "failed to accept new connection"); } } while (clientfd < 0); if (socket_set_blocking(clientfd, 0) < 0) { panic(0, "failed to make client socket nonblocking"); } addr = gw_netaddr_to_octstr(AF_INET, &sin.sin_addr); info(0, "Accepted client from %s:%d", octstr_get_cstr(addr), ntohs(sin.sin_port)); octstr_destroy(addr); close(listenfd); return clientfd; } int main(int argc, char *argv[]) { int i; int opt; gwlib_init(); progname = argv[0]; srandom(0); /* Make "random" data reproducible */ for (i = 1; i < argc; i++) { for (opt = 0; options[opt].option; opt++) { if (strcmp(argv[i], options[opt].option) == 0) { if (i + 1 >= argc) { fprintf(stderr, "%s: missing argument to %s", progname, argv[i]); exit(2); } if (options[opt].number) { * (int *) options[opt].location = atoi(argv[i+1]); } else { * (char **) options[opt].location = argv[i+1]; } i++; break; } } if (options[opt].option) continue; if (strcmp(argv[i], "--help") == 0) { usage(stdout); exit(0); } if (argv[i][0] == '-') { fprintf(stderr, "%s: unknown option %s\n", progname, argv[i]); usage(stderr); exit(2); } } sockfd = wait_for_client(port); main_loop(); return 0; }