summaryrefslogtreecommitdiffstats
path: root/tcpip/ttylinkd.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-04-21 09:51:03 +0200
committerRalf Baechle <ralf@linux-mips.org>1999-04-21 09:51:03 +0200
commit17287576555a5c46fa23549e2e5f073660dccb70 (patch)
tree08be5f5005dad609a2803758b8b825170f6701cb /tcpip/ttylinkd.c
Import ax25-tools 0.0.1 from tarballax25-tools-0.0.1
Diffstat (limited to 'tcpip/ttylinkd.c')
-rw-r--r--tcpip/ttylinkd.c719
1 files changed, 719 insertions, 0 deletions
diff --git a/tcpip/ttylinkd.c b/tcpip/ttylinkd.c
new file mode 100644
index 0000000..e185c7e
--- /dev/null
+++ b/tcpip/ttylinkd.c
@@ -0,0 +1,719 @@
+/*
+ * ttylinkd: A ttylink daemon using the ntalkd protocol.
+ * Copyright (C) 1996,1197 Craig Small (csmall@gonzo.triode.net.au)
+ *
+ * This program is free software ; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have recieved a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge. MA 02139, USA.
+ */
+ /*
+ * Versions:
+ * 13/01/96 cs Original Version
+ * 29/01/96 cs Added AX.25 proper support, thanks Tomi OH2BNS!!
+ * 27/01/97 cs Added support for stdin/stdout.
+ * 04/03/97 cs Added config file, no more defines.
+ * 09/03/97 tjd Enhanced to allow specifying user@hostname.wherever.
+ * rather than be confined to localhost.
+ */
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define osockaddr sockaddr
+#include <protocols/talkd.h>
+
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <sys/socket.h>
+#include <netax25/ax25.h>
+#include <netrose/rose.h>
+
+#include "../pathnames.h"
+
+
+static char version[] = "ttylink daemon (Version 0.05, 9 March 1997) ready.\n";
+
+#define SYSOP_USER "root"
+
+/* TTY control characters from the sysop's talk screen */
+char erasec;
+char killc;
+char werasec;
+
+/* Users address family */
+int userfamily;
+
+#define ADDR_SIZE 256
+char sysop_addr[ADDR_SIZE];
+char *sysop_user=NULL, *sysop_host=NULL;
+char config_file[128];
+
+int send_control(int skt, struct in_addr addr, CTL_MSG msg, CTL_RESPONSE *resp);
+void alarm_handle(int i);
+void do_talk(int skt);
+void read_config_file(int dummy);
+
+/*static char *Commands[] = {
+ "leaving invitation",
+ "look-up",
+ "delete",
+ "announce" };
+*/
+static char *Responses[] = {
+ "success",
+ "sysop not logged on",
+ "failed for unknown reason",
+ "sysop's machine unknown",
+ "permission denied",
+ "unknown request",
+ "bad version",
+ "bad address",
+ "bad control address" };
+
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in sin, ctl_sin;
+ struct in_addr my_addr, rem_addr;
+ int ctl_skt, skt, new_skt;
+ int length;
+ CTL_RESPONSE resp;
+ CTL_MSG msg;
+ struct protoent *ppe;
+ struct hostent *phe, *rhe=NULL;
+ char hostname[256];
+ int local_id, remote_id;
+ char buf[256];
+ char user[NAME_SIZE];
+ struct sockaddr sa;
+ int sa_len, i;
+ struct sockaddr_in *peer_sin=NULL;
+ struct sockaddr_ax25 *peer_sax;
+ struct sockaddr_rose *peer_srose;
+
+ /* Open up the system logger */
+ openlog(argv[0], LOG_PID, LOG_DAEMON);
+
+ write(STDOUT_FILENO, version, strlen(version));
+
+ /* Work out who is calling us */
+ userfamily = AF_UNSPEC;
+ bzero(user, NAME_SIZE);
+ strcpy(sysop_addr, SYSOP_USER);
+ strcpy(config_file, CONF_TTYLINKD_FILE);
+ for(i=1 ; i < argc ; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'v':
+ return 0;
+ break;
+ case 'h':
+ printf("ttylinkd comes with ABSOLUTELY NO WARRANY, see the file COPYING\n");
+ printf("This is free software, and you are welcome to redistribute it\n");
+ printf("under the terms of the GNU General Public License.\n\n");
+ printf("Usage: %s [-hv] [-c <callsign>] [-f <config_file>]\n", argv[0]);
+ return 0;
+ break;
+ case 'f':
+ if (i+2 > argc)
+ {
+ printf("The -f flag needs a parameter.\n");
+ return 0;
+ break;
+ }
+ strncpy(config_file, argv[++i], 127);
+ break;
+ case 'c':
+ if ( i+2 > argc)
+ {
+ printf("The -c flag need a parameter.\n");
+ return 0;
+ break;
+ }
+ strncpy(user, argv[++i], NAME_SIZE);
+ break;
+ default:
+ fprintf(stderr, "%s: Unknown flag, type %s -h for help\n", argv[0], argv[0]);
+ return -1;
+ break;
+ } /*switch */
+ } /* - */
+ } /* for */
+ if (user[0] == '\0')
+ {
+ sa_len = sizeof(sa);
+ if (getpeername(STDOUT_FILENO, &sa, &sa_len) < 0)
+ {
+ fprintf(stderr, "%s: getpeername() failed, you must specify a callsign in stdin mode.\n", argv[0]);
+ syslog(LOG_CRIT | LOG_DAEMON, "main(): getpeername() failed.");
+ return 0;
+ } else {
+ userfamily = sa.sa_family;
+ switch(sa.sa_family) {
+ case AF_INET:
+ peer_sin = (struct sockaddr_in*)&sa;
+ write(STDOUT_FILENO, buf, strlen(buf));
+ sprintf(buf, "Please enter your callsign: ");
+ write(STDOUT_FILENO, buf, strlen(buf));
+ fflush(stdout);
+ if (fgets(user, NAME_SIZE-1, stdin) == NULL)
+ return 0;
+ for (i = 0; user[i] != '\0' && user[i] != '\n' && user[i] != '\r'; i++)
+ ;
+ user[i] = '\0';
+ if (strlen(user) < 1)
+ return 0;
+ userfamily = AF_INET;
+ break;
+ case AF_AX25:
+ case AF_NETROM:
+ peer_sax = (struct sockaddr_ax25*)&sa;
+ for(i=0 ; i < 6 ; i++)
+ {
+ user[i] = tolower(((peer_sax->sax25_call.ax25_call[i]) >>1)&0x7f);
+ if (user[i] == ' ')
+ break;
+ }
+ user[i] = '\0';
+ break;
+ case AF_ROSE:
+ peer_srose = (struct sockaddr_rose*)&sa;
+ for(i=0 ; i < 6 ; i++)
+ {
+ user[i] = tolower(((peer_srose->srose_call.ax25_call[i]) >>1)&0x7f);
+ if (user[i] == ' ')
+ break;
+ }
+ user[i] = '\0';
+ break;
+ default:
+ syslog(LOG_DAEMON | LOG_CRIT, "Unsupported address family.");
+ exit(1);
+ }
+
+ }
+ } /* argc */
+
+ /* Read the configuration file to find the System Operator. */
+ read_config_file(0);
+ /* ... and parse it into user@host */
+ sysop_user=strtok(sysop_addr,"@");
+ sysop_host=strtok(NULL,"@");
+
+ if ((ppe = getprotobyname("udp")) == 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "Cannot find udp protocol entry.");
+ exit(1);
+ }
+
+ /* Obtain our local hostname */
+ gethostname(hostname,256);
+
+ /* Get the remote address, or use ours if remote isn't specified */
+ if (sysop_host)
+ {
+ if ((rhe = gethostbyname(sysop_host)) == NULL)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): gethostbyname failed.");
+ exit(1);
+ }
+ }
+ else
+ {
+ if ((rhe = gethostbyname(hostname)) == NULL)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): gethostbyname failed.");
+ exit(1);
+ }
+ }
+ bcopy(rhe->h_addr, (char*)&rem_addr, rhe->h_length);
+
+ /* Get our local address */
+ if ((phe = gethostbyname(hostname)) == NULL)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): gethostbyname failed.");
+ exit(1);
+ }
+ bcopy(phe->h_addr, (char*)&my_addr, phe->h_length);
+
+ /* Create local data socket */
+ bzero((char*)&sin, sizeof(sin));
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(0);
+ bcopy(phe->h_addr, (char*)&sin.sin_addr, phe->h_length);
+
+ if ((skt = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): socket() failed.");
+ exit(1);
+ }
+
+ if (bind( skt, (struct sockaddr*)&sin, sizeof(sin)) != 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): bind() failed.");
+ exit(1);
+ }
+
+ length = sizeof(sin);
+ if (getsockname(skt, (struct sockaddr*)&sin, &length) < 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): getsockname() failed.");
+ exit(1);
+ }
+
+ /* Create local control socket */
+ bzero((char*)&ctl_sin, sizeof(ctl_sin));
+
+ ctl_sin.sin_family = AF_INET;
+ bcopy(phe->h_addr, (char*)&ctl_sin.sin_addr, phe->h_length);
+ ctl_sin.sin_port = htons(0);
+
+ if ((ctl_skt = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): socket() while attempting to create control socket.");
+ close(skt);
+ exit(1);
+ }
+
+ if (bind(ctl_skt, (struct sockaddr*)&ctl_sin, sizeof(ctl_sin)) != 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): Error when trying to bind() control socket.");
+ close(skt);
+ exit(1);
+ }
+
+ length = sizeof(ctl_sin);
+ if (getsockname(ctl_skt, (struct sockaddr*)&ctl_sin, &length) < 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT,"main(): Error when trying to getsockname() for control socket.");
+ close(skt);
+ close(ctl_skt);
+ exit(1);
+ }
+
+ /* Start talking to the talk daemon */
+ bzero((char*)&msg, sizeof(msg));
+ msg.vers = TALK_VERSION;
+ msg.id_num = htonl(0);
+ msg.addr.sa_family = ntohs(AF_INET);
+ msg.ctl_addr = *(struct sockaddr*)&ctl_sin;
+ msg.ctl_addr = *(struct sockaddr*)&ctl_sin;
+ msg.ctl_addr.sa_family = ntohs(AF_INET);
+ msg.pid = htonl(getpid());
+ strncpy(msg.l_name, user, NAME_SIZE-1);
+ strncpy(msg.r_name, sysop_user, NAME_SIZE-1);
+
+
+ /* Now look for an invite */
+ msg.type = LOOK_UP;
+ (void) send_control(ctl_skt, rem_addr, msg, &resp);
+
+ /* The person not there? Send an announce and wake him up */
+ msg.type = ANNOUNCE;
+ msg.addr = *(struct sockaddr*)&sin;
+ msg.addr.sa_family = htons(AF_INET);
+ msg.id_num = -1;
+ i = send_control(ctl_skt, rem_addr, msg, &resp);
+ if ( i != SUCCESS)
+ {
+ if (i > BADCTLADDR)
+ printf("Cannot talk to sysop errno=%d.\n",i);
+ else
+ printf("Cannot talk to sysop, reason: %s.\n",Responses[i]);
+
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ /* Update the ID number so that we both know what we're talking about. */
+ remote_id = resp.id_num;
+
+ /* Get the TCP port ready for a connection */
+ if (listen(skt, 5) != 0)
+ {
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): Error when trying to listen() on socket.");
+ exit(1);
+ }
+
+ /* Now we have to make an invitation for the other user */
+ msg.type = LEAVE_INVITE;
+ if (send_control(ctl_skt, my_addr, msg, &resp) != SUCCESS)
+ {
+ printf("Problem with leaving an invitation\n");
+ syslog(LOG_DAEMON | LOG_CRIT, "main(): Cannot leave invititation.");
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ local_id = resp.id_num;
+
+ sprintf(buf, "Paging sysop.\n");
+ write(STDOUT_FILENO, buf, strlen(buf));
+
+ /* Wait for the sysop to connect to us */
+ signal(SIGALRM, alarm_handle);
+ alarm(30);
+
+ if ((new_skt = accept(skt, 0, 0)) < 0)
+ {
+ if (errno == EINTR)
+ {
+ /* Delete invitations from both daemons */
+ msg.type = DELETE;
+ msg.id_num = htonl(local_id);
+ send_control(ctl_skt, my_addr, msg, &resp);
+ msg.id_num = htonl(remote_id);
+ send_control(ctl_skt, rem_addr, msg, &resp);
+
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ syslog(LOG_DAEMON | LOG_WARNING, "main(): accept() failed. (%m)");
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ close(skt);
+ skt = new_skt;
+
+ /* Delete invitations from both daemons */
+ msg.type = DELETE;
+ msg.id_num = htonl(local_id);
+ send_control(ctl_skt, my_addr, msg, &resp);
+ msg.id_num = htonl(remote_id);
+ send_control(ctl_skt, rem_addr, msg, &resp);
+
+ sprintf(buf, "Sysop has responded.\n");
+ write(STDOUT_FILENO, buf, strlen(buf));
+
+ /*
+ * A little thing that they don't mention anywhere is the fact that the
+ * first three characters on a connection are used to work out to erase,
+ * kill and word erase characters. Nice to know eh?
+ */
+ /* We send ours first, but then again we get data in cooked form, so
+ * we don't use this. */
+ buf[0] = '\0';
+ buf[1] = '\0';
+ buf[2] = '\0';
+ if (write(skt, buf, 3) != 3)
+ {
+ printf("Connnection lost\n");
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ /* Get their character stuff */
+ if (read(skt, buf, 3) != 3)
+ {
+ printf("Connection lost\n");
+ close(skt);
+ close(ctl_skt);
+ return 0;
+ }
+ erasec = buf[0];
+ killc = buf[1];
+ werasec = buf[2];
+
+ /* Tell the sysop who this person is */
+ if (sa.sa_family == AF_AX25)
+ {
+ sprintf(buf, "Incoming ttylink from %s.\n", user);
+ write(skt, buf, strlen(buf));
+ }
+ if (sa.sa_family == AF_INET)
+ {
+ sprintf(buf, "Incoming ttylink from %s@%s.\n", user, inet_ntoa(peer_sin->sin_addr));
+ write(skt, buf, strlen(buf));
+ }
+
+
+ do_talk(skt);
+
+ close(skt);
+ close(ctl_skt);
+ return 0;
+}
+
+/*
+ * Used to send control messages to our friendly local talk daemon
+ */
+int send_control(int skt, struct in_addr addr, CTL_MSG msg, CTL_RESPONSE *resp)
+{
+ fd_set fdvar;
+ struct timeval timeout;
+ struct sockaddr_in sin;
+ static int talk_port = 0;
+ struct servent *pse;
+
+ /* Look up talk port once only */
+ if (talk_port == 0)
+ {
+ if ((pse = getservbyname("ntalk", "udp")) == NULL)
+ {
+ perror("getservbyname, assuming 518");
+ talk_port = 518;
+ } else {
+ talk_port = pse->s_port;
+ }
+ }
+
+ /* Create the socket address */
+ bzero((char*)&sin, sizeof(sin));
+ sin.sin_addr = addr;
+ sin.sin_family = AF_INET;
+ sin.sin_port = talk_port;
+
+ if (sendto(skt, (char*)&msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) != sizeof(msg))
+ {
+ syslog(LOG_DAEMON | LOG_ERR, "send_control(): sendto failed (%m).");
+ return -1;
+ }
+
+ /* Wait for reply */
+ FD_ZERO(&fdvar);
+ FD_SET(skt, &fdvar);
+ timeout.tv_sec = RING_WAIT;
+ timeout.tv_usec = 0;
+
+ if (select(32, &fdvar, NULL, NULL, &timeout) < 0)
+ syslog(LOG_DAEMON | LOG_ERR, "send_control(): select failed. (%m)");
+
+ /*
+ * The server is ignoring us, see ya later
+ */
+ if (!(FD_ISSET(skt, &fdvar)))
+ {
+ printf("Talk server not responding after %d seconds, aborting.\n", RING_WAIT);
+ return -1;
+ }
+
+ /* Get the message */
+ if(recv(skt, resp, sizeof(resp),0) ==0)
+ syslog(LOG_DAEMON | LOG_ERR, "send_control(): recv failed. (%m)");
+
+ return resp->answer;
+}
+
+/* Used to process the data from the sysop */
+int send_sysop_data(char *buf, int len)
+{
+ static char outbuf[82];
+ static char *bptr = outbuf;
+ int i;
+
+ for(i = 0; i < len; i++)
+ {
+ /* Check for erase character */
+ if (buf[i] == erasec)
+ {
+ if (bptr > outbuf)
+ bptr--;
+ continue;
+ }
+
+ /* Check for kill character */
+ if (buf[i] == killc)
+ {
+ bptr = outbuf;
+ continue;
+ }
+
+ /* Check for word-erase character */
+ if (buf[i] == werasec)
+ {
+ while( (bptr > outbuf) && (*bptr != ' ') )
+ bptr--;
+ continue;
+ }
+
+ /* Check for newline character */
+ if (buf[i] == '\n')
+ {
+ if ( (userfamily == AF_AX25) || (userfamily == AF_NETROM) || (userfamily == AF_ROSE) )
+ {
+ *bptr = '\r';
+ bptr++;
+ } else {
+ *bptr = '\r';
+ bptr++;
+ *bptr = '\n';
+ bptr++;
+ }
+ } else {
+ *bptr = buf[i];
+ bptr++;
+ }
+
+ /* Check for carriage return, which means send it */
+ /* We also send if we have more than 80 characters */
+ if (buf[i] == '\n' || (bptr - outbuf) > 80 )
+ {
+ if (write(STDOUT_FILENO, outbuf, (bptr - outbuf) ) != (bptr - outbuf) )
+ {
+ return -1;
+ }
+ bptr = outbuf;
+ }
+ } /* for */
+ return 0;
+}
+
+/* Used to process the data from the user - len must not exceed 256 */
+int send_user_data(int skt, char *buf, int len)
+{
+ char outbuf[256];
+ char *bptr = outbuf;
+ int i;
+
+ for(i = 0; i < len; i++)
+ {
+ if (buf[i] == '\r')
+ {
+ if (userfamily == AF_INET)
+ {
+ *bptr = '\n';
+ bptr++;
+ /*
+ * check if this is a <CR><LF> or a <CR><NUL>
+ * and skip the <LF> or <NUL>.
+ */
+ if (buf[i + 1] == '\n' || buf[i + 1] == '\0')
+ {
+ i++;
+ }
+ } else
+ {
+ *bptr = '\n';
+ bptr++;
+ }
+ continue;
+ }
+ *bptr = buf[i];
+ bptr++;
+ } /* for */
+
+ if (write(skt, outbuf, bptr - outbuf) != bptr - outbuf)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+
+/* The main talking loop */
+void do_talk(int skt)
+{
+ fd_set fdvar;
+ char inbuf[256], outbuf[256];
+ struct timeval timeout;
+ int i;
+
+
+ while(1)
+ {
+ FD_ZERO(&fdvar);
+ FD_SET(skt, &fdvar);
+ FD_SET(STDIN_FILENO, &fdvar);
+
+ timeout.tv_sec = 30;
+ timeout.tv_usec = 0;
+ if (select(32, &fdvar, NULL, NULL, &timeout) == 0)
+ {
+ if (ioctl(STDIN_FILENO, FIONREAD, (struct sgttyb *)&i) < 0)
+ return;
+ if (ioctl(skt, FIONREAD, (struct sgttyb*)&i) < 0)
+ return;
+ }
+
+ if (FD_ISSET(skt, &fdvar))
+ {
+ if ((i = read(skt, inbuf, 256)) <= 0)
+ {
+ strcpy(inbuf,"Sysop closed connection.\n");
+ write(STDOUT_FILENO, inbuf, strlen(inbuf));
+ return;
+ }
+ if (send_sysop_data(inbuf, i) < 0)
+ {
+ strcpy(outbuf, "User closed connection.\n");
+ (void) write(skt, outbuf, strlen(outbuf));
+ return;
+ }
+ }
+ if (FD_ISSET(STDIN_FILENO, &fdvar))
+ {
+ if ((i = read(STDIN_FILENO, outbuf, 256)) <= 0)
+ {
+ strcpy(outbuf, "User closed connection.\n");
+ (void) write(skt, outbuf, strlen(outbuf));
+ return;
+ }
+ if (send_user_data(skt, outbuf, i) < 0)
+ {
+ strcpy(inbuf,"Sysop closed connection.\n");
+ write(STDOUT_FILENO, inbuf, strlen(inbuf));
+ return;
+ }
+ }
+ }
+}
+
+void alarm_handle(int i)
+{
+ char buf[256];
+
+ strcpy(buf, "Sysop not responding.\n");
+ write(STDOUT_FILENO, buf, strlen(buf));
+}
+
+void read_config_file(int dummy)
+{
+ char buf[128];
+ char param[20], value[108];
+ FILE *fp;
+
+ if ( (fp = fopen(config_file, "r")) == NULL) {
+ syslog(LOG_DAEMON | LOG_ERR, "Cannot find configuration file: %s (%m)\n",config_file);
+ return;
+ }
+ /* Reset any variables here */
+
+ while ( fgets(buf, 128, fp) != NULL) {
+ if ( buf[0] == '#')
+ continue;
+ if (sscanf(buf, "%[^=]%*c%[^\n]", param, value) == 2) {
+ /* A big wierdo switch */
+ if (strcasecmp(param, "sysop") == 0) {
+ strncpy(sysop_addr, value, ADDR_SIZE);
+ }
+ }
+ } /* while */
+ fclose(fp);
+ signal(SIGHUP, read_config_file);
+}