* $Id$
*/
+#include <sys/types.h>
#include <stdio.h>
#include <sys/time.h>
-#include <sys/types.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include "sel.h"
#include "cmsg.h"
+#include "chain.h"
#include "debug.h"
#define TEXT 1
#define MAXPATHLEN 256
#endif
-#define DEFPACLEN 128
+#define DEFPACLEN 236
#define MAXPACLEN 236
#define MAXCALLSIGN 9
#define DBUF 1
#define DMSG 2
+#define DSTS 4
+
+#define UC unsigned char
typedef struct
{
char nl = '\n'; /* line end character */
char mode = 1; /* 0 - ax25, 1 - normal telnet, 2 - nlonly telnet */
char ending = 0; /* set this to end the program */
-char send_Z = 1; /* set a Z record to the node on termination */
char echo = 1; /* echo characters on stdout from stdin */
char int_tabs = 0; /* interpret tabs -> spaces */
char *root = "/spider"; /* root of data tree, can be overridden by DXSPIDER_ROOT */
int paclen = DEFPACLEN; /* default buffer size for outgoing packets */
int tabsize = 8; /* default tabsize for text messages */
char *connsort = "local"; /* the connection variety */
-
+int state = 0; /* the current state of the connection */
+int laststate = 0; /* the last state we were in */
+char echocancel = 1; /* echo cancelling */
+reft echobase; /* the anti echo queue */
+int maxecho = 5; /* the depth of the anti echo queue */
+int echon; /* no of entries in the anti echo queue */
+
+#define CONNECTED 100
+#define WAITLOGIN 1
+#define WAITPASSWD 2
+#define WAITINPUT 10
+#define DOCHAT 20
myregex_t iscallreg[] = { /* regexes to determine whether this is a reasonable callsign */
{
"^[0-9]+[A-Z]+[0-9]+[A-Z]+[1-9]?$", 0 /* 2E0AAA 2E0AAA1 */
},
{
- "^[A-Z]+[0-9]+[A-Z]+-[1-9]$", 0 /* G1TLH-2 */
+ "^[A-Z]+[0-9]+[A-Z]+-[0-9]$", 0 /* G1TLH-2 */
},
{
- "^[0-9]+[A-Z]+[0-9]+[A-Z]+-[1-9]$", 0 /* 2E0AAA-2 */
+ "^[0-9]+[A-Z]+[0-9]+[A-Z]+-[0-9]$", 0 /* 2E0AAA-2 */
},
{
"^[A-Z]+[0-9]+[A-Z]+-1[0-5]$", 0 /* G1TLH-11 */
* utility routines - various
*/
+void chgstate(int new)
+{
+ laststate = state;
+ state = new;
+ dbg(DSTS, "chg state %d->%d", laststate, state);
+}
+
void die(char *s, ...)
{
char buf[2000];
return (strcmp(a, b) == 0);
}
-int xopen(char *dir, char *name, int mode)
+FILE *xopen(char *dir, char *name, char *mode)
{
char fn[MAXPATHLEN+1];
snprintf(fn, MAXPATHLEN, "%s/%s/%s", root, dir, name);
- return open(fn, mode);
+ return fopen(fn, mode);
}
int iscallsign(char *s)
return 0;
}
+void reaper(int i)
+{
+ pid_t mypid;
+ while ((mypid = waitpid(-1, 0, WNOHANG)) > 0) {
+ ;
+ }
+}
+
/*
* higher level send and receive routines
*/
void flush_text(fcb_t *f)
{
if (f->obuf) {
- cmsg_send(f->outq, f->obuf, 0);
+ /* save this onto the front of the echo chain */
+ cmsg_t *imp = f->obuf;
+ int size = imp->inp - imp->data;
+ cmsg_t *emp = cmsg_new(size, imp->sort, imp->portp);
+
+ emp->size = size;
+ memcpy(emp->data, imp->data, size);
+ emp->inp = emp->data + size; /* just in case */
+ if (echocancel) {
+ chain_add(&echobase, emp);
+ if (++echon > maxecho) {
+ emp = cmsg_prev(&echobase);
+ cmsg_free(emp);
+ }
+ }
+
+ /* queue it for sending */
+ cmsg_send(f->outq, imp, 0);
f->sp->flags |= SEL_OUTPUT;
f->obuf = 0;
}
}
-void send_text(fcb_t *f, char *s, int l)
+void send_text(fcb_t *f, char *s, int l, int nlreq)
{
cmsg_t *mp;
char *p;
flush_text(f);
f->obuf = mp = cmsg_new(paclen+1, f->sort, f);
}
- if (nl == '\r')
- *mp->inp++ = nl;
- else {
- if (mode != 2)
- *mp->inp++ = '\r';
- *mp->inp++ = '\n';
+ if (nlreq) {
+ if (nl == '\r')
+ *mp->inp++ = nl;
+ else {
+ if (mode != 2)
+ *mp->inp++ = '\r';
+ *mp->inp++ = '\n';
+ }
}
if (!f->buffer_it)
flush_text(f);
}
-void send_msg(fcb_t *f, char let, unsigned char *s, int l)
+void send_msg(fcb_t *f, char let, UC *s, int l)
{
cmsg_t *mp;
int ln;
mp->inp += strlen(call);
*mp->inp++ = '|';
if (l > 0) {
- unsigned char *p;
+ UC *p;
for (p = s; p < s+l; ++p) {
if (mp->inp >= mp->data + (myl - 4)) {
int off = mp->inp - mp->data;
f->sp->flags |= SEL_OUTPUT;
}
+/*
+ * send a file out to the user
+ */
+void send_file(char *name)
+{
+ int i;
+ char buf[MAXPACLEN+1];
+
+ FILE *f = xopen("data", name, "r");
+ if (f) {
+ while (fgets(buf, paclen, f)) {
+ i = strlen(buf);
+ if (i && buf[i-1] == '\n')
+ buf[--i] = 0;
+ send_text(in, buf, i, 1);
+ }
+ fclose(f);
+ }
+}
+
/*
* the callback (called by sel_run) that handles all the inputs and outputs
*/
{
fcb_t *f = sp->fcb;
cmsg_t *mp, *omp;
- unsigned char c;
+ UC c;
/* input modes */
if (ending == 0 && in) {
case EAGAIN:
goto lout;
default:
-/* if (f->sort == MSG)
- send_Z = 0; */
dbg(DBUF,"got errno %d in input", errno);
ending++;
return 0;
}
} else if (r == 0) {
-/* if (f->sort == MSG)
- send_Z = 0; */
dbg(DBUF, "ending normally");
ending++;
return 0;
strcpy(omp->inp, "\b \b");
omp->inp += strlen(omp->inp);
break;
+ case '\r':
+ break;
+ case '\n':
+ strcpy(omp->inp, "\r\n");
+ omp->inp += 2;
+ break;
default:
*omp->inp++ = *p;
}
default:
if (nl == '\n' && *p == '\r') { /* ignore \r in telnet mode (ugh) */
p++;
+ } else if (nl == '\r' && *p == '\n') { /* and ignore \n in ax25 mode (double ugh) */
+ p++;
} else if (*p == nl) {
if (mp->inp == mp->data)
*mp->inp++ = ' ';
case MSG:
p = buf;
while (r > 0 && p < &buf[r]) {
- unsigned char ch = *p++;
+ UC ch = *p++;
if (mp->inp >= mp->data + (MAXBUFL-1)) {
mp->state = 0;
c = (ch - '0') << 4;
else if (ch >= 'A' && ch <= 'F')
c = (ch - 'A' + 10) << 4;
- else if (ch >= 'a' && ch <= 'a')
- c = (ch - 'a' + 10) << 4;
else {
- dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state);
+ dbg(DMSG, "Illegal hex char (%c) received in state 1", ch);
mp->inp = mp->data;
mp->state = 0;
}
*mp->inp++ = c | (ch - '0');
else if (ch >= 'A' && ch <= 'F')
*mp->inp++ = c | (ch - 'A' + 10);
- else if (ch >= 'a' && ch <= 'a')
- *mp->inp++ = c | (ch - 'a' + 10);
else {
- dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state);
+ dbg(DMSG, "Illegal hex char (%c) received in state 2", ch);
mp->inp = mp->data;
}
mp->state = 0;
case EAGAIN:
goto lend;
default:
-/* if (f->sort == MSG)
- send_Z = 0; */
dbg(DBUF,"got errno %d in output", errno);
ending++;
return;
/*
* set up the various mode flags, NL endings and things
*/
-void setmode(char *m)
+void setconntype(char *m)
{
connsort = strlower(m);
if (eq(connsort, "telnet") || eq(connsort, "local") || eq(connsort, "nlonly")) {
}
}
+
+/*
+ * things to do with ongoing processing of inputs
+ */
+
+void process_stdin()
+{
+ cmsg_t *wmp, *mp = cmsg_next(in->inq);
+ char *p, hasa, hasn, i;
+ char callsign[MAXCALLSIGN+1];
+
+ if (mp) {
+ dbg(DMSG, "MSG size: %d", mp->size);
+
+ /* check for echos */
+ if (echocancel) {
+ for (wmp = 0; wmp = chain_get_next(&echobase, wmp); ) {
+ if (!memcmp(wmp->data, mp->data, wmp->size)) {
+ cmsg_callback(mp, 0);
+ return;
+ }
+ }
+ }
+
+ switch (state) {
+ case CONNECTED:
+ if (mp->size > 0) {
+ send_msg(node, 'I', mp->data, mp->size);
+ }
+ break;
+ case WAITLOGIN:
+ for (i = 0; i < mp->size; ++i) {
+ UC ch = mp->data[i];
+ if (i < MAXCALLSIGN) {
+ if (isalpha(ch))
+ ++hasa;
+ if (isdigit(ch))
+ ++hasn;
+ if (isalnum(ch) || ch == '-')
+ callsign[i] = ch;
+ else
+ die("invalid callsign");
+ } else
+ die("invalid callsign");
+ }
+ callsign[i]= 0;
+ if (strlen(callsign) < 3)
+ die("invalid callsign");
+ if (hasa && hasn)
+ ;
+ else
+ die("invalid callsign");
+ call = strupper(callsign);
+
+ /* check the callsign against the regexes */
+ if (!iscallsign(call)) {
+ die("Sorry, %s isn't a valid callsign", call);
+ }
+
+ /* strip off a '-0' at the end */
+ i = strlen(call);
+ if (call[i-1] == '0' && call[i-2] == '-')
+ call[i-2] = 0;
+
+ alarm(0);
+ signal(SIGALRM, SIG_IGN);
+
+ /* tell the cluster who I am */
+ send_msg(node, 'A', connsort, strlen(connsort));
+
+ chgstate(CONNECTED);
+ send_file("connected");
+ break;
+
+ case DOCHAT:
+
+ break;
+ }
+
+ cmsg_callback(mp, 0);
+
+ }
+}
+
+void process_node()
+{
+ cmsg_t *mp = cmsg_next(node->inq);
+ if (mp) {
+ dbg(DMSG, "MSG size: %d", mp->size);
+
+ if (mp->size > 0 && mp->inp > mp->data) {
+ char *p = strchr(mp->data, '|');
+ if (p)
+ p++;
+ switch (mp->data[0]) {
+ case 'Z':
+ ending++;
+ return;
+ case 'E':
+ if (isdigit(*p))
+ in->echo = *p - '0';
+ break;
+ case 'B':
+ if (isdigit(*p))
+ in->buffer_it = *p - '0';
+ break;
+ case 'D':
+ if (p) {
+ int l = mp->inp - (UC *) p;
+ send_text(in, p, l, 1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ cmsg_callback(mp, 0);
+ } else {
+ flush_text(in);
+ }
+}
+
+/*
+ * things to do with going away
+ */
+
+void term_timeout(int i)
+{
+ /* none of this is going to be reused so don't bother cleaning up properly */
+ if (in && in->t_set)
+ tcsetattr(0, TCSANOW, &in->t);
+ if (node) {
+ shutdown(node->cnum, 3);
+ close(node->cnum);
+ }
+ exit(i);
+}
+
+void terminate(int i)
+{
+ signal(SIGALRM, term_timeout);
+ alarm(10);
+
+ while ((in && !is_chain_empty(in->outq)) ||
+ (node && !is_chain_empty(node->outq))) {
+ sel_run();
+ }
+ if (in && in->t_set)
+ tcsetattr(0, TCSADRAIN, &in->t);
+ if (node) {
+ shutdown(node->cnum, 3);
+ close(node->cnum);
+ }
+ exit(i);
+}
+
+void login_timeout(int i)
+{
+ write(0, "Timed Out", 10);
+ write(0, &nl, 1);
+ sel_run(); /* force a coordination */
+ if (in && in->t_set)
+ tcsetattr(0, TCSANOW, &in->t);
+ exit(i);
+}
+
/*
* things to do with initialisation
*/
{
int i, c, err = 0;
- while ((c = getopt(argc, argv, "h:p:x:")) > 0) {
+ while ((c = getopt(argc, argv, "eh:l:p:x:")) > 0) {
switch (c) {
+ case 'e':
+ echocancel ^= 1;
+ break;
case 'h':
node_addr = optarg;
break;
lerr:
if (err) {
- die("usage: client [-x n|-h<host>|-p<port>|-l<paclen>] <call>|login [local|telnet|ax25]");
+ die("usage: client [-e|-x n|-h<host>|-p<port>|-l<paclen>] <call>|login [local|telnet|ax25]");
}
if (optind < argc) {
die("Must have at least a callsign (for now)");
if (optind < argc) {
- setmode(argv[optind]);
+ setconntype(argv[optind]);
} else {
- setmode("local");
+ setconntype("local");
}
/* this is kludgy, but hey so is the rest of this! */
struct hostent *hp, *gethostbyname();
struct sockaddr_in server;
int nodef;
+ int one = 1;
sel_t *sp;
-
+ struct linger lg;
+
if ((hp = gethostbyname(node_addr)) == 0)
die("Unknown host tcp host %s for printer", node_addr);
if (connect(nodef, (struct sockaddr *) &server, sizeof server) < 0) {
die("Error on connect to %s port %d (%d)", node_addr, node_port, errno);
}
- node = fcb_new(nodef, MSG);
- node->sp = sel_open(nodef, node, "Msg System", fcb_handler, MSG, SEL_INPUT);
-
-}
-
-/*
- * things to do with going away
- */
-void term_timeout(int i)
-{
- /* none of this is going to be reused so don't bother cleaning up properly */
- if (in && in->t_set)
- tcsetattr(0, TCSANOW, &in->t);
- if (node) {
- shutdown(node->cnum, 3);
- close(node->cnum);
+ memset(&lg, 0, sizeof lg);
+ if (setsockopt(nodef, SOL_SOCKET, SO_LINGER, &lg, sizeof lg) < 0) {
+ die("Error on SO_LINGER to %s port %d (%d)", node_addr, node_port, errno);
}
- exit(i);
-}
-
-void terminate(int i)
-{
- if (node && send_Z && call) {
- send_msg(node, 'Z', "bye", 3);
+ if (setsockopt(nodef, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one) < 0) {
+ die("Error on SO_KEEPALIVE to %s port %d (%d)", node_addr, node_port, errno);
}
- signal(SIGALRM, term_timeout);
- alarm(10);
+ node = fcb_new(nodef, MSG);
+ node->sp = sel_open(nodef, node, "Msg System", fcb_handler, MSG, SEL_INPUT);
- while ((in && !is_chain_empty(in->outq)) ||
- (node && !is_chain_empty(node->outq))) {
- sel_run();
- }
- if (in && in->t_set)
- tcsetattr(0, TCSADRAIN, &in->t);
- if (node) {
- shutdown(node->cnum, 3);
- close(node->cnum);
- }
- exit(i);
-}
-
-void login_timeout(int i)
-{
- write(0, "Timed Out", 10);
- write(0, &nl, 1);
- sel_run(); /* force a coordination */
- if (in && in->t_set)
- tcsetattr(0, TCSANOW, &in->t);
- exit(i);
}
/*
- * things to do with ongoing processing of inputs
+ * the program itself....
*/
-void process_stdin()
+main(int argc, char *argv[])
{
- cmsg_t *mp = cmsg_next(in->inq);
- if (mp) {
- dbg(DMSG, "MSG size: %d", mp->size);
-
- if (mp->size > 0 && mp->inp > mp->data) {
- send_msg(node, 'I', mp->data, mp->size);
+ /* compile regexes for iscallsign */
+ {
+ myregex_t *rp;
+ for (rp = iscallreg; rp->in; ++rp) {
+ regex_t reg;
+ int r = regcomp(®, rp->in, REG_EXTENDED|REG_ICASE|REG_NOSUB);
+ if (r)
+ die("regcomp returned %d for '%s'", r, rp->in);
+ rp->regex = malloc(sizeof(regex_t));
+ if (!rp->regex)
+ die("out of room - compiling regexes");
+ *rp->regex = reg;
}
- cmsg_callback(mp, 0);
}
-}
-
-void process_node()
-{
- cmsg_t *mp = cmsg_next(node->inq);
- if (mp) {
- dbg(DMSG, "MSG size: %d", mp->size);
- if (mp->size > 0 && mp->inp > mp->data) {
- char *p = strchr(mp->data, '|');
- if (p)
- p++;
- switch (mp->data[0]) {
- case 'Z':
- send_Z = 0;
- ending++;
- return;
- case 'E':
- if (isdigit(*p))
- in->echo = *p - '0';
- break;
- case 'B':
- if (isdigit(*p))
- in->buffer_it = *p - '0';
- break;
- case 'D':
- if (p) {
- int l = mp->inp - (unsigned char *) p;
- send_text(in, p, l);
- }
- break;
- default:
- break;
- }
- }
- cmsg_callback(mp, 0);
- } else {
- flush_text(in);
- }
-}
-
-/*
- * the program itself....
- */
-
-main(int argc, char *argv[])
-{
/* set up environment */
{
char *p = getenv("DXSPIDER_ROOT");
#ifdef SIGPWR
signal(SIGPWR, terminate);
#endif
-
- /* compile regexes for iscallsign */
- {
- myregex_t *rp;
- for (rp = iscallreg; rp->in; ++rp) {
- regex_t reg;
- int r = regcomp(®, rp->in, REG_EXTENDED|REG_ICASE|REG_NOSUB);
- if (r)
- die("regcomp returned %d for '%s'", r, rp->in);
- rp->regex = malloc(sizeof(regex_t));
- if (!rp->regex)
- die("out of room - compiling regexes");
- *rp->regex = reg;
- }
- }
- /* is this a login? */
- if (eq(call, "LOGIN") || eq(call, "login")) {
-
- char buf[MAXPACLEN+1];
- char callsign[MAXCALLSIGN+1];
- int r, i;
- int f = xopen("data", "issue", 0);
- if (f > 0) {
- while ((r = read(f, buf, paclen)) > 0) {
- if (nl != '\n') {
- char *p;
- for (p = buf; p < &buf[r]; ++p) {
- if (*p == '\n')
- *p = nl;
- }
- }
- write(0, buf, r);
- }
- close(f);
- }
- signal(SIGALRM, login_timeout);
- alarm(timeout);
- write(0, "login: ", 7);
- dbgdump(DBUF, "<-out", "login: ", 7);
- for (i = 0;;) {
- char *p;
- r = read(0, buf, 20);
- dbgdump(DBUF, "in ->", buf, r);
- if (r <= 0)
- die("No login or error (%d)", errno);
- write(0, buf, r);
- dbgdump(DBUF, "<-out", buf, r);
- for (p = buf; p < buf+r; ++p) {
- if (i < MAXCALLSIGN) {
- if (*p == '\r' || *p == '\n')
- goto lgotcall;
- else if (isalnum(*p) || *p == '-')
- callsign[i++] = *p;
- else
- die("%c is not a valid callsign character", *p);
- } else
- die("callsign entered is too long");
- }
- }
-lgotcall:
- signal(SIGALRM, SIG_IGN);
- alarm(0);
- callsign[i]= 0;
- call = strupper(callsign);
- }
+ /* init a few things */
+ chain_init(&echobase);
- /* check the callsign */
- if (!iscallsign(call)) {
- die("Sorry, %s isn't a valid callsign", call);
- }
-
/* connect up stdin */
in = fcb_new(0, TEXT);
in->sp = sel_open(0, in, "STDIN", fcb_handler, TEXT, SEL_INPUT);
} else {
struct termios t = in->t;
t.c_lflag &= ~(ECHO|ECHONL|ICANON);
+ t.c_oflag = 0;
if (tcsetattr(0, TCSANOW, &t) < 0)
die("tcsetattr (%d)", errno);
in->echo = echo;
/* connect up node */
connect_to_node();
- /* tell the cluster who I am */
- send_msg(node, 'A', connsort, strlen(connsort));
+ /* is this a login? */
+ if (eq(call, "LOGIN") || eq(call, "login")) {
+ send_file("issue");
+ signal(SIGALRM, login_timeout);
+ alarm(timeout);
+ send_text(in, "login: ", 7, 0);
+ chgstate(WAITLOGIN);
+ } else {
+ int i;
+
+ /* check the callsign against the regexes */
+ if (!iscallsign(call)) {
+ die("Sorry, %s isn't a valid callsign", call);
+ }
+
+ /* strip off a '-0' at the end */
+ i = strlen(call);
+ if (call[i-1] == '0' && call[i-2] == '-')
+ call[i-2] = 0;
+
+ /* tell the cluster who I am */
+ send_msg(node, 'A', connsort, strlen(connsort));
+
+ chgstate(CONNECTED);
+ send_file("connected");
+ }
+
/* main processing loop */
- while (!ending) {
+ while (ending == 0) {
sel_run();
- if (!ending) {
+ if (ending == 0) {
process_stdin();
process_node();
}