summaryrefslogtreecommitdiffstats
path: root/gnu/libexec/uucp/libunix/tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/libexec/uucp/libunix/tcp.c')
-rw-r--r--gnu/libexec/uucp/libunix/tcp.c444
1 files changed, 444 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/libunix/tcp.c b/gnu/libexec/uucp/libunix/tcp.c
new file mode 100644
index 0000000..6ec39f2
--- /dev/null
+++ b/gnu/libexec/uucp/libunix/tcp.c
@@ -0,0 +1,444 @@
+/* tcp.c
+ Code to handle TCP connections.
+
+ Copyright (C) 1991, 1992, 1993, 1995 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ 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 received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char tcp_rcsid[] = "$Id: tcp.c,v 1.5 1995/06/21 19:20:46 ian Rel $";
+#endif
+
+#if HAVE_TCP
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "sysdep.h"
+#include "conn.h"
+#include "system.h"
+
+#include <errno.h>
+
+#if HAVE_SYS_TYPES_TCP_H
+#include <sys/types.tcp.h>
+#endif
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#endif
+
+#ifndef FD_CLOEXEC
+#define FD_CLOEXEC 1
+#endif
+
+/* This code handles TCP connections. It assumes a Berkeley socket
+ interface. */
+
+/* The normal "uucp" port number. */
+#define IUUCP_PORT (540)
+
+/* Local functions. */
+static void utcp_free P((struct sconnection *qconn));
+static boolean ftcp_open P((struct sconnection *qconn, long ibaud,
+ boolean fwait));
+static boolean ftcp_close P((struct sconnection *qconn,
+ pointer puuconf,
+ struct uuconf_dialer *qdialer,
+ boolean fsuccess));
+static boolean ftcp_dial P((struct sconnection *qconn, pointer puuconf,
+ const struct uuconf_system *qsys,
+ const char *zphone,
+ struct uuconf_dialer *qdialer,
+ enum tdialerfound *ptdialer));
+static int itcp_port_number P((const char *zport));
+
+/* The command table for a TCP connection. */
+static const struct sconncmds stcpcmds =
+{
+ utcp_free,
+ NULL, /* pflock */
+ NULL, /* pfunlock */
+ ftcp_open,
+ ftcp_close,
+ ftcp_dial,
+ fsysdep_conn_read,
+ fsysdep_conn_write,
+ fsysdep_conn_io,
+ NULL, /* pfbreak */
+ NULL, /* pfset */
+ NULL, /* pfcarrier */
+ fsysdep_conn_chat,
+ NULL /* pibaud */
+};
+
+/* Initialize a TCP connection. */
+
+boolean
+fsysdep_tcp_init (qconn)
+ struct sconnection *qconn;
+{
+ struct ssysdep_conn *q;
+
+ q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn));
+ q->o = -1;
+ q->ord = -1;
+ q->owr = -1;
+ q->zdevice = NULL;
+ q->iflags = -1;
+ q->iwr_flags = -1;
+ q->fterminal = FALSE;
+ q->ftli = FALSE;
+ q->ibaud = 0;
+
+ qconn->psysdep = (pointer) q;
+ qconn->qcmds = &stcpcmds;
+ return TRUE;
+}
+
+/* Free a TCP connection. */
+
+static void
+utcp_free (qconn)
+ struct sconnection *qconn;
+{
+ xfree (qconn->psysdep);
+}
+
+/* Open a TCP connection. If the fwait argument is TRUE, we are
+ running as a server. Otherwise we are just trying to reach another
+ system. */
+
+static boolean
+ftcp_open (qconn, ibaud, fwait)
+ struct sconnection *qconn;
+ long ibaud;
+ boolean fwait;
+{
+ struct ssysdep_conn *qsysdep;
+ struct sockaddr_in s;
+ const char *zport;
+ uid_t ieuid;
+ boolean fswap;
+
+ ulog_device ("TCP");
+
+ qsysdep = (struct ssysdep_conn *) qconn->psysdep;
+
+ qsysdep->o = socket (AF_INET, SOCK_STREAM, 0);
+ if (qsysdep->o < 0)
+ {
+ ulog (LOG_ERROR, "socket: %s", strerror (errno));
+ return FALSE;
+ }
+
+ if (fcntl (qsysdep->o, F_SETFD,
+ fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0)
+ {
+ ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
+ (void) close (qsysdep->o);
+ qsysdep->o = -1;
+ return FALSE;
+ }
+
+ qsysdep->iflags = fcntl (qsysdep->o, F_GETFL, 0);
+ if (qsysdep->iflags < 0)
+ {
+ ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
+ (void) close (qsysdep->o);
+ qsysdep->o = -1;
+ return FALSE;
+ }
+
+ /* We save our process ID in the qconn structure. This is checked
+ in ftcp_close. */
+ qsysdep->ipid = getpid ();
+
+ /* If we aren't waiting for a connection, we're done. */
+ if (! fwait)
+ return TRUE;
+
+ /* Run as a server and wait for a new connection. The code in
+ uucico.c has already detached us from our controlling terminal.
+ From this point on if the server gets an error we exit; we only
+ return if we have received a connection. It would be more robust
+ to respawn the server if it fails; someday. */
+ bzero ((pointer) &s, sizeof s);
+ s.sin_family = AF_INET;
+ zport = qconn->qport->uuconf_u.uuconf_stcp.uuconf_zport;
+ s.sin_port = itcp_port_number (zport);
+ s.sin_addr.s_addr = htonl (INADDR_ANY);
+
+ /* Swap to our real user ID when doing the bind call. This will
+ permit the server to use privileged TCP ports when invoked by
+ root. We only swap if our effective user ID is not root, so that
+ the program can also be made suid root in order to get privileged
+ ports when invoked by anybody. */
+ fswap = geteuid () != 0;
+ if (fswap)
+ {
+ if (! fsuser_perms (&ieuid))
+ {
+ (void) close (qsysdep->o);
+ qsysdep->o = -1;
+ return FALSE;
+ }
+ }
+
+ if (bind (qsysdep->o, (struct sockaddr *) &s, sizeof s) < 0)
+ {
+ if (fswap)
+ (void) fsuucp_perms ((long) ieuid);
+ ulog (LOG_FATAL, "bind: %s", strerror (errno));
+ }
+
+ /* Now swap back to the uucp user ID. */
+ if (fswap)
+ {
+ if (! fsuucp_perms ((long) ieuid))
+ ulog (LOG_FATAL, "Could not swap back to UUCP user permissions");
+ }
+
+ if (listen (qsysdep->o, 5) < 0)
+ ulog (LOG_FATAL, "listen: %s", strerror (errno));
+
+ while (! FGOT_SIGNAL ())
+ {
+ size_t clen;
+ int onew;
+ pid_t ipid;
+
+ DEBUG_MESSAGE0 (DEBUG_PORT,
+ "ftcp_open: Waiting for connections");
+
+ clen = sizeof s;
+ onew = accept (qsysdep->o, (struct sockaddr *) &s, &clen);
+ if (onew < 0)
+ ulog (LOG_FATAL, "accept: %s", strerror (errno));
+
+ DEBUG_MESSAGE0 (DEBUG_PORT,
+ "ftcp_open: Got connection; forking");
+
+ ipid = ixsfork ();
+ if (ipid < 0)
+ ulog (LOG_FATAL, "fork: %s", strerror (errno));
+ if (ipid == 0)
+ {
+ (void) close (qsysdep->o);
+ qsysdep->o = onew;
+
+ /* Now we fork and let our parent die, so that we become
+ a child of init. This lets the main server code wait
+ for its child and then continue without accumulating
+ zombie children. */
+ ipid = ixsfork ();
+ if (ipid < 0)
+ {
+ ulog (LOG_ERROR, "fork: %s", strerror (errno));
+ _exit (EXIT_FAILURE);
+ }
+
+ if (ipid != 0)
+ _exit (EXIT_SUCCESS);
+
+ ulog_id (getpid ());
+
+ return TRUE;
+ }
+
+ (void) close (onew);
+
+ /* Now wait for the child. */
+ (void) ixswait ((unsigned long) ipid, (const char *) NULL);
+ }
+
+ /* We got a signal. */
+ usysdep_exit (FALSE);
+
+ /* Avoid compiler warnings. */
+ return FALSE;
+}
+
+/* Close the port. */
+
+/*ARGSUSED*/
+static boolean
+ftcp_close (qconn, puuconf, qdialer, fsuccess)
+ struct sconnection *qconn;
+ pointer puuconf;
+ struct uuconf_dialer *qdialer;
+ boolean fsuccess;
+{
+ struct ssysdep_conn *qsysdep;
+ boolean fret;
+
+ qsysdep = (struct ssysdep_conn *) qconn->psysdep;
+ fret = TRUE;
+ if (qsysdep->o >= 0 && close (qsysdep->o) < 0)
+ {
+ ulog (LOG_ERROR, "close: %s", strerror (errno));
+ fret = FALSE;
+ }
+ qsysdep->o = -1;
+
+ /* If the current pid is not the one we used to open the port, then
+ we must have forked up above and we are now the child. In this
+ case, we are being called from within the fendless loop in
+ uucico.c. We return FALSE to force the loop to end and the child
+ to exit. This should be handled in a cleaner fashion. */
+ if (qsysdep->ipid != getpid ())
+ fret = FALSE;
+
+ return fret;
+}
+
+/* Dial out on a TCP port, so to speak: connect to a remote computer. */
+
+/*ARGSUSED*/
+static boolean
+ftcp_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialer)
+ struct sconnection *qconn;
+ pointer puuconf;
+ const struct uuconf_system *qsys;
+ const char *zphone;
+ struct uuconf_dialer *qdialer;
+ enum tdialerfound *ptdialer;
+{
+ struct ssysdep_conn *qsysdep;
+ const char *zhost;
+ struct hostent *q;
+ struct sockaddr_in s;
+ const char *zport;
+ char **pzdialer;
+
+ qsysdep = (struct ssysdep_conn *) qconn->psysdep;
+
+ *ptdialer = DIALERFOUND_FALSE;
+
+ zhost = zphone;
+ if (zhost == NULL)
+ {
+ if (qsys == NULL)
+ {
+ ulog (LOG_ERROR, "No address for TCP connection");
+ return FALSE;
+ }
+ zhost = qsys->uuconf_zname;
+ }
+
+ errno = 0;
+ q = gethostbyname ((char *) zhost);
+ if (q != NULL)
+ {
+ s.sin_family = q->h_addrtype;
+ memcpy (&s.sin_addr.s_addr, q->h_addr, (size_t) q->h_length);
+ }
+ else
+ {
+ if (errno != 0)
+ {
+ ulog (LOG_ERROR, "gethostbyname (%s): %s", zhost, strerror (errno));
+ return FALSE;
+ }
+
+ s.sin_family = AF_INET;
+ s.sin_addr.s_addr = inet_addr ((char *) zhost);
+ if ((long) s.sin_addr.s_addr == (long) -1)
+ {
+ ulog (LOG_ERROR, "%s: unknown host name", zhost);
+ return FALSE;
+ }
+ }
+
+ zport = qconn->qport->uuconf_u.uuconf_stcp.uuconf_zport;
+ s.sin_port = itcp_port_number (zport);
+
+ if (connect (qsysdep->o, (struct sockaddr *) &s, sizeof s) < 0)
+ {
+ ulog (LOG_ERROR, "connect: %s", strerror (errno));
+ return FALSE;
+ }
+
+ /* Handle the dialer sequence, if any. */
+ pzdialer = qconn->qport->uuconf_u.uuconf_stcp.uuconf_pzdialer;
+ if (pzdialer != NULL && *pzdialer != NULL)
+ {
+ if (! fconn_dial_sequence (qconn, puuconf, pzdialer, qsys, zphone,
+ qdialer, ptdialer))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Get the port number given a name. The argument will almost always
+ be "uucp" so we cache that value. The return value is always in
+ network byte order. This returns -1 on error. */
+
+static int
+itcp_port_number (zname)
+ const char *zname;
+{
+ boolean fuucp;
+ static int iuucp;
+ int i;
+ char *zend;
+ struct servent *q;
+
+ fuucp = strcmp (zname, "uucp") == 0;
+ if (fuucp && iuucp != 0)
+ return iuucp;
+
+ /* Try it as a number first. */
+ i = strtol ((char *) zname, &zend, 10);
+ if (i != 0 && *zend == '\0')
+ return htons (i);
+
+ q = getservbyname ((char *) zname, (char *) "tcp");
+ if (q == NULL)
+ {
+ /* We know that the "uucp" service should be 540, even if isn't
+ in /etc/services. */
+ if (fuucp)
+ {
+ iuucp = htons (IUUCP_PORT);
+ return iuucp;
+ }
+ ulog (LOG_ERROR, "getservbyname (%s): %s", zname, strerror (errno));
+ return -1;
+ }
+
+ if (fuucp)
+ iuucp = q->s_port;
+
+ return q->s_port;
+}
+
+#endif /* HAVE_TCP */
OpenPOWER on IntegriCloud