From 17287576555a5c46fa23549e2e5f073660dccb70 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 21 Apr 1999 09:51:03 +0200 Subject: Import ax25-tools 0.0.1 from tarball --- tcpip/ttylinkd.c | 719 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 719 insertions(+) create mode 100644 tcpip/ttylinkd.c (limited to 'tcpip/ttylinkd.c') 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define osockaddr sockaddr +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 ] [-f ]\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 or a + * and skip the or . + */ + 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); +} -- cgit v1.2.3