1/* tcp.c
2   Code to handle TCP connections.
3
4   Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor
5
6   This file is part of the Taylor UUCP package.
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21
22   The author of the program may be contacted at ian@airs.com.
23   */
24
25#include "uucp.h"
26
27#if USE_RCS_ID
28const char tcp_rcsid[] = "$Id: tcp.c,v 1.12 2002/03/05 19:10:42 ian Rel $";
29#endif
30
31#if HAVE_TCP
32
33#include "uudefs.h"
34#include "uuconf.h"
35#include "sysdep.h"
36#include "conn.h"
37#include "system.h"
38
39#include <errno.h>
40
41#if HAVE_SYS_TYPES_TCP_H
42#include <sys/types.tcp.h>
43#endif
44#include <sys/socket.h>
45#include <netdb.h>
46#include <netinet/in.h>
47#include <arpa/inet.h>
48
49#if HAVE_FCNTL_H
50#include <fcntl.h>
51#else
52#if HAVE_SYS_FILE_H
53#include <sys/file.h>
54#endif
55#endif
56
57#ifndef FD_CLOEXEC
58#define FD_CLOEXEC 1
59#endif
60
61#if HAVE_STRUCT_SOCKADDR_STORAGE
62typedef struct sockaddr_storage sockaddr_storage;
63#else
64typedef struct sockaddr_in sockaddr_storage;
65#endif
66
67/* This code handles TCP connections.  It assumes a Berkeley socket
68   interface.  */
69
70/* The normal "uucp" port number.  */
71#define IUUCP_PORT (540)
72#define ZUUCP_PORT ("540")
73
74/* Local functions.  */
75static void utcp_free P((struct sconnection *qconn));
76#if HAVE_GETADDRINFO
77static boolean ftcp_set_hints P((int iversion, struct addrinfo *qhints));
78#endif
79static boolean ftcp_set_flags P((struct ssysdep_conn *qsysdep));
80static boolean ftcp_open P((struct sconnection *qconn, long ibaud,
81			    boolean fwait, boolean fuser));
82static boolean ftcp_close P((struct sconnection *qconn,
83			     pointer puuconf,
84			     struct uuconf_dialer *qdialer,
85			     boolean fsuccess));
86static boolean ftcp_dial P((struct sconnection *qconn, pointer puuconf,
87			    const struct uuconf_system *qsys,
88			    const char *zphone,
89			    struct uuconf_dialer *qdialer,
90			    enum tdialerfound *ptdialer));
91static int itcp_port_number P((const char *zport));
92
93/* The command table for a TCP connection.  */
94static const struct sconncmds stcpcmds =
95{
96  utcp_free,
97  NULL, /* pflock */
98  NULL, /* pfunlock */
99  ftcp_open,
100  ftcp_close,
101  ftcp_dial,
102  fsysdep_conn_read,
103  fsysdep_conn_write,
104  fsysdep_conn_io,
105  NULL, /* pfbreak */
106  NULL, /* pfset */
107  NULL, /* pfcarrier */
108  fsysdep_conn_chat,
109  NULL /* pibaud */
110};
111
112/* Initialize a TCP connection.  */
113
114boolean
115fsysdep_tcp_init (qconn)
116     struct sconnection *qconn;
117{
118  struct ssysdep_conn *q;
119
120  q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn));
121  q->o = -1;
122  q->ord = -1;
123  q->owr = -1;
124  q->zdevice = NULL;
125  q->iflags = -1;
126  q->iwr_flags = -1;
127  q->fterminal = FALSE;
128  q->ftli = FALSE;
129  q->ibaud = 0;
130
131  qconn->psysdep = (pointer) q;
132  qconn->qcmds = &stcpcmds;
133  return TRUE;
134}
135
136/* Free a TCP connection.  */
137
138static void
139utcp_free (qconn)
140     struct sconnection *qconn;
141{
142  xfree (qconn->psysdep);
143}
144
145#if HAVE_GETADDRINFO
146
147/* Set the hints for an addrinfo structure from the IP version.  */
148
149static boolean
150ftcp_set_hints (iversion, qhints)
151     int iversion;
152     struct addrinfo *qhints;
153{
154  switch (iversion)
155    {
156    case 0:
157      qhints->ai_family = 0;
158      break;
159    case 4:
160      qhints->ai_family = PF_INET;
161      break;
162    case 6:
163#ifdef PF_INET6
164      qhints->ai_family = PF_INET6;
165#else
166      ulog (LOG_ERROR, "IPv6 requested but not supported");
167      return FALSE;
168#endif
169      break;
170    default:
171      ulog (LOG_ERROR, "Invalid IP version number %d", iversion);
172      return FALSE;
173    }
174  return TRUE;
175}
176
177#endif /* HAVE_GETADDRINFO */
178
179/* Set the close on exec flag for a socket.  */
180
181static boolean
182ftcp_set_flags (qsysdep)
183     struct ssysdep_conn *qsysdep;
184{
185  if (fcntl (qsysdep->o, F_SETFD,
186	     fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0)
187    {
188      ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
189      (void) close (qsysdep->o);
190      qsysdep->o = -1;
191      return FALSE;
192    }
193
194  qsysdep->iflags = fcntl (qsysdep->o, F_GETFL, 0);
195  if (qsysdep->iflags < 0)
196    {
197      ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
198      (void) close (qsysdep->o);
199      qsysdep->o = -1;
200      return FALSE;
201    }
202
203  return TRUE;
204}
205
206/* Open a TCP connection.  If the fwait argument is TRUE, we are
207   running as a server.  Otherwise we are just trying to reach another
208   system.  */
209
210static boolean
211ftcp_open (qconn, ibaud, fwait, fuser)
212     struct sconnection *qconn;
213     long ibaud ATTRIBUTE_UNUSED;
214     boolean fwait;
215     boolean fuser ATTRIBUTE_UNUSED;
216{
217  struct ssysdep_conn *qsysdep;
218  const char *zport;
219  uid_t ieuid;
220  gid_t iegid;
221  boolean fswap;
222#if HAVE_GETADDRINFO
223  struct addrinfo shints;
224  struct addrinfo *qres;
225  struct addrinfo *quse;
226  int ierr;
227#endif
228
229  ulog_device ("TCP");
230
231  qsysdep = (struct ssysdep_conn *) qconn->psysdep;
232
233  qsysdep->o = -1;
234
235  /* We save our process ID in the qconn structure.  This is checked
236     in ftcp_close.  */
237  qsysdep->ipid = getpid ();
238
239  /* If we aren't waiting for a connection, we're done.  */
240  if (! fwait)
241    return TRUE;
242
243  zport = qconn->qport->uuconf_u.uuconf_stcp.uuconf_zport;
244
245#if HAVE_GETADDRINFO
246  bzero ((pointer) &shints, sizeof shints);
247  if (! ftcp_set_hints (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion,
248			&shints))
249    return FALSE;
250  shints.ai_socktype = SOCK_STREAM;
251  shints.ai_flags = AI_PASSIVE;
252  ierr = getaddrinfo (NULL, zport, &shints, &qres);
253  if (ierr == EAI_SERVICE && strcmp (zport, "uucp") == 0)
254    ierr = getaddrinfo (NULL, ZUUCP_PORT, &shints, &qres);
255
256  /* If getaddrinfo failed (i.e., ierr != 0), just fall back on using
257     an IPv4 port.  Likewise if we can't create a socket using any of
258     the resulting addrinfo structures.  */
259
260  if (ierr != 0)
261    {
262      ulog (LOG_ERROR, "getaddrinfo: %s", gai_strerror (ierr));
263      qres = NULL;
264      quse = NULL;
265    }
266  else
267    {
268      for (quse = qres; quse != NULL; quse = quse->ai_next)
269	{
270	  qsysdep->o = socket (quse->ai_family, quse->ai_socktype,
271			       quse->ai_protocol);
272	  if (qsysdep->o >= 0)
273	    break;
274	}
275    }
276#endif /* HAVE_GETADDRINFO */
277
278  if (qsysdep->o < 0)
279    {
280      if (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion != 0
281	  && qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion != 4)
282	{
283#ifdef HAVE_GETADDRINFO
284	  ulog (LOG_ERROR, "Could not get IPv6 socket");
285#else
286	  ulog (LOG_ERROR, "Only IPv4 sockets are supported");
287#endif
288	  return FALSE;
289	}
290
291      qsysdep->o = socket (AF_INET, SOCK_STREAM, 0);
292      if (qsysdep->o < 0)
293	{
294	  ulog (LOG_ERROR, "socket: %s", strerror (errno));
295	  return FALSE;
296	}
297    }
298
299  if (! ftcp_set_flags (qsysdep))
300    return FALSE;
301
302#if HAVE_GETADDRINFO
303#ifdef IPV6_BINDV6ONLY
304  if (quse != NULL && quse->ai_family == AF_INET6)
305    {
306      int iflag;
307
308      if (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion == 0)
309	iflag = 0;
310      else
311	iflag = 1;
312      if (setsockopt (qsysdep->o, IPPROTO_IPV6, IPV6_BINDV6ONLY,
313		      (char *) &iflag, sizeof (iflag)) < 0)
314	{
315	  ulog (LOG_ERROR, "setsockopt (IPV6_BINDONLY): %s",
316		strerror (errno));
317	  (void) close (qsysdep->o);
318	  qsysdep->o = -1;
319	  freeaddrinfo (qres);
320	  return FALSE;
321	}
322    }
323#endif /* defined (IPV6_BINDV6ONLY) */
324#endif /* HAVE_GETADDRINFO */
325
326  /* Run as a server and wait for a new connection.  The code in
327     uucico.c has already detached us from our controlling terminal.
328     From this point on if the server gets an error we exit; we only
329     return if we have received a connection.  It would be more robust
330     to respawn the server if it fails; someday.  */
331
332  /* Swap to our real user ID when doing the bind call.  This will
333     permit the server to use privileged TCP ports when invoked by
334     root.  We only swap if our effective user ID is not root, so that
335     the program can also be made suid root in order to get privileged
336     ports when invoked by anybody.  */
337  fswap = geteuid () != 0;
338  if (fswap)
339    {
340      if (! fsuser_perms (&ieuid, &iegid))
341	{
342	  (void) close (qsysdep->o);
343	  qsysdep->o = -1;
344#ifdef HAVE_GETADDRINFO
345	  if (qres != NULL)
346	    freeaddrinfo (qres);
347#endif
348	  return FALSE;
349	}
350    }
351
352#if HAVE_GETADDRINFO
353  if (quse != NULL)
354    {
355      if (bind (qsysdep->o, quse->ai_addr, quse->ai_addrlen) < 0)
356	{
357	  if (fswap)
358	    (void) fsuucp_perms ((long) ieuid, (long) iegid);
359	  ulog (LOG_FATAL, "bind: %s", strerror (errno));
360	}
361    }
362  else
363#endif
364    {
365      struct sockaddr_in sin;
366
367      bzero ((pointer) &sin, sizeof sin);
368      sin.sin_family = AF_INET;
369      sin.sin_port = itcp_port_number (zport);
370      sin.sin_addr.s_addr = htonl (INADDR_ANY);
371
372      if (bind (qsysdep->o, (struct sockaddr *) &sin, sizeof sin) < 0)
373	{
374	  if (fswap)
375	    (void) fsuucp_perms ((long) ieuid, (long) iegid);
376	  ulog (LOG_FATAL, "bind: %s", strerror (errno));
377	}
378    }
379
380#if HAVE_GETADDRINFO
381  if (qres != NULL)
382    freeaddrinfo (qres);
383#endif
384
385  /* Now swap back to the uucp user ID.  */
386  if (fswap)
387    {
388      if (! fsuucp_perms ((long) ieuid, (long) iegid))
389	ulog (LOG_FATAL, "Could not swap back to UUCP user permissions");
390    }
391
392  if (listen (qsysdep->o, 5) < 0)
393    ulog (LOG_FATAL, "listen: %s", strerror (errno));
394
395  while (! FGOT_SIGNAL ())
396    {
397      sockaddr_storage speer;
398      size_t clen;
399      int onew;
400      pid_t ipid;
401
402      DEBUG_MESSAGE0 (DEBUG_PORT,
403		      "ftcp_open: Waiting for connections");
404
405      clen = sizeof speer;
406      onew = accept (qsysdep->o, (struct sockaddr *) &speer, &clen);
407      if (onew < 0)
408	ulog (LOG_FATAL, "accept: %s", strerror (errno));
409
410      DEBUG_MESSAGE0 (DEBUG_PORT,
411		      "ftcp_open: Got connection; forking");
412
413      ipid = ixsfork ();
414      if (ipid < 0)
415	ulog (LOG_FATAL, "fork: %s", strerror (errno));
416      if (ipid == 0)
417	{
418	  (void) close (qsysdep->o);
419	  qsysdep->o = onew;
420
421	  /* Now we fork and let our parent die, so that we become
422	     a child of init.  This lets the main server code wait
423	     for its child and then continue without accumulating
424	     zombie children.  */
425	  ipid = ixsfork ();
426	  if (ipid < 0)
427	    {
428	      ulog (LOG_ERROR, "fork: %s", strerror (errno));
429	      _exit (EXIT_FAILURE);
430	    }
431
432	  if (ipid != 0)
433	    _exit (EXIT_SUCCESS);
434
435	  ulog_id (getpid ());
436
437	  return TRUE;
438	}
439
440      (void) close (onew);
441
442      /* Now wait for the child.  */
443      (void) ixswait ((unsigned long) ipid, (const char *) NULL);
444    }
445
446  /* We got a signal.  */
447  usysdep_exit (FALSE);
448
449  /* Avoid compiler warnings.  */
450  return FALSE;
451}
452
453/* Close the port.  */
454
455/*ARGSUSED*/
456static boolean
457ftcp_close (qconn, puuconf, qdialer, fsuccess)
458     struct sconnection *qconn;
459     pointer puuconf ATTRIBUTE_UNUSED;
460     struct uuconf_dialer *qdialer ATTRIBUTE_UNUSED;
461     boolean fsuccess ATTRIBUTE_UNUSED;
462{
463  struct ssysdep_conn *qsysdep;
464  boolean fret;
465
466  qsysdep = (struct ssysdep_conn *) qconn->psysdep;
467  fret = TRUE;
468  if (qsysdep->o >= 0 && close (qsysdep->o) < 0)
469    {
470      ulog (LOG_ERROR, "close: %s", strerror (errno));
471      fret = FALSE;
472    }
473  qsysdep->o = -1;
474
475  /* If the current pid is not the one we used to open the port, then
476     we must have forked up above and we are now the child.  In this
477     case, we are being called from within the fendless loop in
478     uucico.c.  We return FALSE to force the loop to end and the child
479     to exit.  This should be handled in a cleaner fashion.  */
480  if (qsysdep->ipid != getpid ())
481    fret = FALSE;
482
483  return fret;
484}
485
486/* Dial out on a TCP port, so to speak: connect to a remote computer.  */
487
488/*ARGSUSED*/
489static boolean
490ftcp_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialer)
491     struct sconnection *qconn;
492     pointer puuconf;
493     const struct uuconf_system *qsys;
494     const char *zphone;
495     struct uuconf_dialer *qdialer;
496     enum tdialerfound *ptdialer;
497{
498  struct ssysdep_conn *qsysdep;
499  const char *zhost;
500  const char *zport;
501  char **pzdialer;
502#if HAVE_GETADDRINFO
503  struct addrinfo shints;
504  struct addrinfo *qres;
505  struct addrinfo *quse;
506  int ierr;
507#endif
508
509  qsysdep = (struct ssysdep_conn *) qconn->psysdep;
510
511  qsysdep->o = -1;
512
513  *ptdialer = DIALERFOUND_FALSE;
514
515  zhost = zphone;
516  if (zhost == NULL)
517    {
518      if (qsys == NULL)
519	{
520	  ulog (LOG_ERROR, "No address for TCP connection");
521	  return FALSE;
522	}
523      zhost = qsys->uuconf_zname;
524    }
525
526  zport = qconn->qport->uuconf_u.uuconf_stcp.uuconf_zport;
527
528#if HAVE_GETADDRINFO
529  bzero ((pointer) &shints, sizeof shints);
530  if (! ftcp_set_hints (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion,
531			&shints))
532    return FALSE;
533  shints.ai_socktype = SOCK_STREAM;
534  ierr = getaddrinfo (zhost, zport, &shints, &qres);
535  if (ierr == EAI_SERVICE && strcmp (zport, "uucp") == 0)
536    ierr = getaddrinfo (zhost, ZUUCP_PORT, &shints, &qres);
537
538  /* If getaddrinfo failed (i.e., ierr != 0), just fall back on using
539     an IPv4 port.  Likewise if we can't connect using any of the
540     resulting addrinfo structures.  */
541
542  if (ierr != 0)
543    {
544      ulog (LOG_ERROR, "getaddrinfo: %s", gai_strerror (ierr));
545      qres = NULL;
546      quse = NULL;
547      ierr = 0;
548    }
549  else
550    {
551      ierr = 0;
552      for (quse = qres; quse != NULL; quse = quse->ai_next)
553	{
554	  qsysdep->o = socket (quse->ai_family, quse->ai_socktype,
555			       quse->ai_protocol);
556	  if (qsysdep->o >= 0)
557	    {
558	      if (connect (qsysdep->o, quse->ai_addr, quse->ai_addrlen) >= 0)
559		break;
560	      ierr = errno;
561	      close (qsysdep->o);
562	      qsysdep->o = -1;
563	    }
564	}
565      if (qres != NULL)
566	freeaddrinfo (qres);
567    }
568#endif
569
570  if (qsysdep->o < 0)
571    {
572      struct hostent *qhost;
573      struct sockaddr_in sin;
574
575      if (qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion != 0
576	  && qconn->qport->uuconf_u.uuconf_stcp.uuconf_iversion != 4)
577	{
578#if HAVE_GETADDRINFO
579	  if (ierr != 0)
580	    ulog (LOG_ERROR, "connect: %s", strerror (ierr));
581	  else
582	    ulog (LOG_ERROR, "Could not get IPv6 address or socket");
583#else
584	  ulog (LOG_ERROR, "Only IPv4 sockets are supported");
585#endif
586	  return FALSE;
587	}
588
589      qsysdep->o = socket (AF_INET, SOCK_STREAM, 0);
590      if (qsysdep->o < 0)
591	{
592	  ulog (LOG_ERROR, "socket: %s", strerror (errno));
593	  return FALSE;
594	}
595
596      errno = 0;
597      bzero ((pointer) &sin, sizeof sin);
598      qhost = gethostbyname ((char *) zhost);
599      if (qhost != NULL)
600	{
601	  sin.sin_family = qhost->h_addrtype;
602	  memcpy (&sin.sin_addr.s_addr, qhost->h_addr,
603		  (size_t) qhost->h_length);
604	}
605      else
606	{
607	  if (errno != 0)
608	    {
609	      ulog (LOG_ERROR, "gethostbyname (%s): %s", zhost,
610		    strerror (errno));
611	      return FALSE;
612	    }
613
614	  sin.sin_family = AF_INET;
615	  sin.sin_addr.s_addr = inet_addr ((char *) zhost);
616	  if ((long) sin.sin_addr.s_addr == (long) -1)
617	    {
618	      ulog (LOG_ERROR, "%s: unknown host name", zhost);
619	      return FALSE;
620	    }
621	}
622
623      sin.sin_port = itcp_port_number (zport);
624
625      if (connect (qsysdep->o, (struct sockaddr *) &sin, sizeof sin) < 0)
626	{
627	  ulog (LOG_ERROR, "connect: %s", strerror (errno));
628	  (void) close (qsysdep->o);
629	  qsysdep->o = -1;
630	  return FALSE;
631	}
632    }
633
634  if (! ftcp_set_flags (qsysdep))
635    return FALSE;
636
637  /* Handle the dialer sequence, if any.  */
638  pzdialer = qconn->qport->uuconf_u.uuconf_stcp.uuconf_pzdialer;
639  if (pzdialer != NULL && *pzdialer != NULL)
640    {
641      if (! fconn_dial_sequence (qconn, puuconf, pzdialer, qsys, zphone,
642				 qdialer, ptdialer))
643	return FALSE;
644    }
645
646  return TRUE;
647}
648
649/* Get the port number given a name.  The argument will almost always
650   be "uucp" so we cache that value.  The return value is always in
651   network byte order.  This returns -1 on error.  */
652
653static int
654itcp_port_number (zname)
655     const char *zname;
656{
657  boolean fuucp;
658  static int iuucp;
659  int i;
660  char *zend;
661  struct servent *q;
662
663  fuucp = strcmp (zname, "uucp") == 0;
664  if (fuucp && iuucp != 0)
665    return iuucp;
666
667  /* Try it as a number first.  */
668  i = strtol ((char *) zname, &zend, 10);
669  if (i != 0 && *zend == '\0')
670    return htons (i);
671
672  q = getservbyname ((char *) zname, (char *) "tcp");
673  if (q == NULL)
674    {
675      /* We know that the "uucp" service should be 540, even if isn't
676	 in /etc/services.  */
677      if (fuucp)
678	{
679	  iuucp = htons (IUUCP_PORT);
680	  return iuucp;
681	}
682      ulog (LOG_ERROR, "getservbyname (%s): %s", zname, strerror (errno));
683      return -1;
684    }
685
686  if (fuucp)
687    iuucp = q->s_port;
688
689  return q->s_port;
690}
691
692#endif /* HAVE_TCP */
693