]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - gnu/libexec/uucp/libunix/tli.c
This commit was generated by cvs2svn to compensate for changes in r56545,
[FreeBSD/FreeBSD.git] / gnu / libexec / uucp / libunix / tli.c
1 /* tli.c
2    Code to handle TLI connections.
3
4    Copyright (C) 1992, 1993, 1994 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 or
23    c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144.
24    */
25
26 #include "uucp.h"
27
28 #if USE_RCS_ID
29 const char tli_rcsid[] = "$FreeBSD$";
30 #endif
31
32 #if HAVE_TLI
33
34 #include "sysdep.h"
35 #include "uudefs.h"
36 #include "uuconf.h"
37 #include "conn.h"
38 #include "system.h"
39
40 #include <errno.h>
41
42 #if HAVE_SYS_IOCTL_H
43 #include <sys/ioctl.h>
44 #endif
45
46 #if HAVE_TIUSER_H
47 #include <tiuser.h>
48 #else
49 #if HAVE_XTI_H
50 #include <xti.h>
51 #else
52 #if HAVE_SYS_TLI_H
53 #include <sys/tli.h>
54 #endif
55 #endif
56 #endif
57
58 #if HAVE_STROPTS_H
59 #include <stropts.h>
60 #endif
61
62 #if HAVE_FCNTL_H
63 #include <fcntl.h>
64 #else
65 #if HAVE_SYS_FILE_H
66 #include <sys/file.h>
67 #endif
68 #endif
69
70 #ifndef O_RDONLY
71 #define O_RDONLY 0
72 #define O_WRONLY 1
73 #define O_RDWR 2
74 #endif
75
76 #ifndef FD_CLOEXEC
77 #define FD_CLOEXEC 1
78 #endif
79
80 /* The arguments to t_alloca have two different names.  I want the
81    SVID ones, not the XPG3 ones.  */
82 #ifndef T_BIND
83 #define T_BIND T_BIND_STR
84 #endif
85 #ifndef T_CALL
86 #define T_CALL T_CALL_STR
87 #endif
88
89 /* Hopefully these externs will not cause any trouble.  This is how
90    they are shown in the SVID.  */
91 extern int t_errno;
92 extern char *t_errlist[];
93 extern int t_nerr;
94
95 #ifndef HAVE_TIUSER_H
96 #ifndef t_alloc
97 extern pointer t_alloc ();
98 #endif
99 #endif
100 \f
101 /* This code handles TLI connections.  It's Unix specific.  It's
102    largely based on code from Unix Network Programming, by W. Richard
103    Stevens.  */
104 \f
105 /* Local functions.  */
106 static const char *ztlierror P((void));
107 static void utli_free P((struct sconnection *qconn));
108 static boolean ftli_push P((struct sconnection *qconn));
109 static boolean ftli_open P((struct sconnection *qconn, long ibaud,
110                             boolean fwait));
111 static boolean ftli_close P((struct sconnection *qconn,
112                              pointer puuconf,
113                              struct uuconf_dialer *qdialer,
114                              boolean fsuccess));
115 static boolean ftli_dial P((struct sconnection *qconn, pointer puuconf,
116                             const struct uuconf_system *qsys,
117                             const char *zphone,
118                             struct uuconf_dialer *qdialer,
119                             enum tdialerfound *ptdialer));
120 \f
121 /* The command table for a TLI connection.  */
122 static const struct sconncmds stlicmds =
123 {
124   utli_free,
125   NULL, /* pflock */
126   NULL, /* pfunlock */
127   ftli_open,
128   ftli_close,
129   ftli_dial,
130   fsysdep_conn_read,
131   fsysdep_conn_write,
132   fsysdep_conn_io,
133   NULL, /* pfbreak */
134   NULL, /* pfset */
135   NULL, /* pfcarrier */
136   fsysdep_conn_chat,
137   NULL /* pibaud */
138 };
139 \f
140 /* Get a TLI error string.  */
141
142 static const char *
143 ztlierror ()
144 {
145   if (t_errno == TSYSERR)
146     return strerror (errno);
147   if (t_errno < 0 || t_errno >= t_nerr)
148     return "Unknown TLI error";
149   return t_errlist[t_errno];
150
151 \f
152 /* Initialize a TLI connection.  This may be called with qconn->qport
153    NULL, when opening standard input as a TLI connection.  */
154
155 boolean
156 fsysdep_tli_init (qconn)
157      struct sconnection *qconn;
158 {
159   struct ssysdep_conn *q;
160
161   q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn));
162   q->o = -1;
163   q->ord = -1;
164   q->owr = -1;
165   q->zdevice = NULL;
166   q->iflags = -1;
167   q->iwr_flags = -1;
168   q->fterminal = FALSE;
169   q->ftli = TRUE;
170   q->ibaud = 0;
171
172   qconn->psysdep = (pointer) q;
173   qconn->qcmds = &stlicmds;
174   return TRUE;
175 }
176
177 /* Free a TLI connection.  */
178
179 static void
180 utli_free (qconn)
181      struct sconnection *qconn;
182 {
183   xfree (qconn->psysdep);
184 }
185 \f
186 /* Push all desired modules onto a TLI stream.  If the user requests a
187    STREAMS connection without giving a list of modules, we just push
188    tirdwr.  If the I_PUSH ioctl is not defined on this system, we just
189    ignore any list of modules.  */
190
191 static boolean
192 ftli_push (qconn)
193      struct sconnection *qconn;
194 {
195 #ifdef I_PUSH
196
197   struct ssysdep_conn *qsysdep;
198
199   qsysdep = (struct ssysdep_conn *) qconn->psysdep;
200
201   if (qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush != NULL)
202     {
203       char **pz;
204
205       for (pz = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush;
206            *pz != NULL;
207            pz++)
208         {
209           if (ioctl (qsysdep->o, I_PUSH, *pz) < 0)
210             {
211               ulog (LOG_ERROR, "ioctl (I_PUSH, %s): %s", *pz,
212                     strerror (errno));
213               return FALSE;
214             }
215         }
216     }
217   else if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream)
218     {
219       if (ioctl (qsysdep->o, I_PUSH, "tirdwr") < 0)
220         {
221           ulog (LOG_ERROR, "ioctl (I_PUSH, tirdwr): %s",
222                 strerror (errno));
223           return FALSE;
224         }
225     }
226
227   /* If we have just put the connection into stream mode, we must turn
228      off the TLI flag to avoid using TLI calls on it.  */
229   if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream)
230     qsysdep->ftli = FALSE;
231
232 #endif /* defined (I_PUSH) */
233   
234   return TRUE;
235 }
236 \f
237 /* Open a TLI connection.  If the fwait argument is TRUE, we are
238    running as a server.  Otherwise we are just trying to reach another
239    system.  */
240
241 static boolean
242 ftli_open (qconn, ibaud, fwait)
243      struct sconnection *qconn;
244      long ibaud;
245      boolean fwait;
246 {
247   struct ssysdep_conn *qsysdep;
248   const char *zdevice;
249   char *zfreedev;
250   const char *zservaddr;
251   char *zfreeaddr;
252   uid_t ieuid;
253   boolean fswap;
254   struct t_bind *qtbind;
255   struct t_call *qtcall;
256
257   /* Unlike most other device types, we don't bother to call
258      ulog_device here, because fconn_open calls it with the name of
259      the port anyhow.  */
260
261   qsysdep = (struct ssysdep_conn *) qconn->psysdep;
262
263   zdevice = qconn->qport->uuconf_u.uuconf_stli.uuconf_zdevice;
264   if (zdevice == NULL)
265     zdevice = qconn->qport->uuconf_zname;
266
267   zfreedev = NULL;
268   if (*zdevice != '/')
269     {
270       zfreedev = zbufalc (sizeof "/dev/" + strlen (zdevice));
271       sprintf (zfreedev, "/dev/%s", zdevice);
272       zdevice = zfreedev;
273     }
274
275   /* If we are acting as a server, swap to our real user ID before
276      calling t_open.  This will permit the server to use privileged
277      TCP ports when invoked by root.  We only swap if our effective
278      user ID is not root, so that the program can also be made suid
279      root in order to get privileged ports when invoked by anybody.  */
280   fswap = fwait && geteuid () != 0;
281   if (fswap)
282     {
283       if (! fsuser_perms (&ieuid))
284         {
285           ubuffree (zfreedev);
286           return FALSE;
287         }
288     }
289
290   qsysdep->o = t_open (zdevice, O_RDWR, (struct t_info *) NULL);
291   if (qsysdep->o < 0)
292     {
293       if (fswap)
294         (void) fsuucp_perms ((long) ieuid);
295       ulog (LOG_ERROR, "t_open (%s): %s", zdevice, ztlierror ());
296       ubuffree (zfreedev);
297       return FALSE;
298     }
299
300   if (fcntl (qsysdep->o, F_SETFD,
301              fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0)
302     {
303       if (fswap)
304         (void) fsuucp_perms ((long) ieuid);
305       ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
306       ubuffree (zfreedev);
307       (void) t_close (qsysdep->o);
308       qsysdep->o = -1;
309       return FALSE;
310     }
311
312   qsysdep->iflags = fcntl (qsysdep->o, F_GETFL, 0);
313   if (qsysdep->iflags < 0)
314     {
315       if (fswap)
316         (void) fsuucp_perms ((long) ieuid);
317       ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
318       ubuffree (zfreedev);
319       (void) t_close (qsysdep->o);
320       qsysdep->o = -1;
321       return FALSE;
322     }
323
324   /* We save our process ID in the qconn structure.  This is checked
325      in ftli_close.  */
326   qsysdep->ipid = getpid ();
327
328   /* If we aren't waiting for a connection, we can bind to any local
329      address, and then we're finished.  */
330   if (! fwait)
331     {
332       /* fswap is known to be FALSE here.  */
333       ubuffree (zfreedev);
334       if (t_bind (qsysdep->o, (struct t_bind *) NULL,
335                   (struct t_bind *) NULL) < 0)
336         {
337           ulog (LOG_ERROR, "t_bind: %s", ztlierror ());
338           (void) t_close (qsysdep->o);
339           qsysdep->o = -1;
340           return FALSE;
341         }
342       return TRUE;
343     }
344
345   /* Run as a server and wait for a new connection.  The code in
346      uucico.c has already detached us from our controlling terminal.
347      From this point on if the server gets an error we exit; we only
348      return if we have received a connection.  It would be more robust
349      to respawn the server if it fails; someday.  */
350   qtbind = (struct t_bind *) t_alloc (qsysdep->o, T_BIND, T_ALL);
351   if (qtbind == NULL)
352     {
353       if (fswap)
354         (void) fsuucp_perms ((long) ieuid);
355       ulog (LOG_FATAL, "t_alloc (T_BIND): %s", ztlierror ());
356     }
357
358   zservaddr = qconn->qport->uuconf_u.uuconf_stli.uuconf_zservaddr;
359   if (zservaddr == NULL)
360     {
361       if (fswap)
362         (void) fsuucp_perms ((long) ieuid);
363       ulog (LOG_FATAL, "Can't run as TLI server; no server address");
364     }
365
366   zfreeaddr = zbufcpy (zservaddr);
367   qtbind->addr.len = cescape (zfreeaddr);
368   if (qtbind->addr.len > qtbind->addr.maxlen)
369     {
370       if (fswap)
371         (void) fsuucp_perms ((long) ieuid);
372       ulog (LOG_FATAL, "%s: TLI server address too long (max %d)",
373             zservaddr, qtbind->addr.maxlen);
374     }
375   memcpy (qtbind->addr.buf, zfreeaddr, qtbind->addr.len);
376   ubuffree (zfreeaddr);
377
378   qtbind->qlen = 5;
379
380   if (t_bind (qsysdep->o, qtbind, (struct t_bind *) NULL) < 0)
381     {
382       if (fswap)
383         (void) fsuucp_perms ((long) ieuid);
384       ulog (LOG_FATAL, "t_bind (%s): %s", zservaddr, ztlierror ());
385     }
386
387   if (fswap)
388     {
389       if (! fsuucp_perms ((long) ieuid))
390         ulog (LOG_FATAL, "Could not swap back to UUCP user permissions");
391     }
392
393   (void) t_free ((pointer) qtbind, T_BIND);
394
395   qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ALL);
396   if (qtcall == NULL)
397     ulog (LOG_FATAL, "t_alloc (T_CALL): %s", ztlierror ());
398
399   while (! FGOT_SIGNAL ())
400     {
401       int onew;
402       pid_t ipid;
403
404       DEBUG_MESSAGE0 (DEBUG_PORT,
405                       "ftli_open: Waiting for connections");
406
407       if (t_listen (qsysdep->o, qtcall) < 0)
408         ulog (LOG_FATAL, "t_listen: %s", ztlierror ());
409
410       onew = t_open (zdevice, O_RDWR, (struct t_info *) NULL);
411       if (onew < 0)
412         ulog (LOG_FATAL, "t_open (%s): %s", zdevice, ztlierror ());
413           
414       if (fcntl (onew, F_SETFD,
415                  fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0)
416         ulog (LOG_FATAL, "fcntl (FD_CLOEXEC): %s", strerror (errno));
417
418       if (t_bind (onew, (struct t_bind *) NULL, (struct t_bind *) NULL) < 0)
419         ulog (LOG_FATAL, "t_bind: %s", ztlierror ());
420
421       if (t_accept (qsysdep->o, onew, qtcall) < 0)
422         {
423           /* We may have received a disconnect.  */
424           if (t_errno != TLOOK)
425             ulog (LOG_FATAL, "t_accept: %s", ztlierror ());
426           if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0)
427             ulog (LOG_FATAL, "t_rcvdis: %s", ztlierror ());
428           (void) t_close (onew);
429           continue;
430         }
431
432       DEBUG_MESSAGE0 (DEBUG_PORT,
433                       "ftli_open: Got connection; forking");
434
435       ipid = ixsfork ();
436       if (ipid < 0)
437         ulog (LOG_FATAL, "fork: %s", strerror (errno));
438       if (ipid == 0)
439         {
440           ulog_close ();
441
442           (void) t_close (qsysdep->o);
443           qsysdep->o = onew;
444
445           /* Push any desired modules.  */
446           if (! ftli_push (qconn))
447             _exit (EXIT_FAILURE);
448
449           /* Now we fork and let our parent die, so that we become
450              a child of init.  This lets the main server code wait
451              for its child and then continue without accumulating
452              zombie children.  */
453           ipid = ixsfork ();
454           if (ipid < 0)
455             {
456               ulog (LOG_ERROR, "fork: %s", strerror (errno));
457               _exit (EXIT_FAILURE);
458             }
459               
460           if (ipid != 0)
461             _exit (EXIT_SUCCESS);
462
463           ulog_id (getpid ());
464
465           return TRUE;
466         }
467
468       (void) t_close (onew);
469
470       /* Now wait for the child.  */
471       (void) ixswait ((unsigned long) ipid, (const char *) NULL);
472     }
473
474   /* We got a signal.  */
475   usysdep_exit (FALSE);
476
477   /* Avoid compiler warnings.  */
478   return FALSE;
479 }
480 \f
481 /* Close the port.  */
482
483 /*ARGSUSED*/
484 static boolean
485 ftli_close (qconn, puuconf, qdialer, fsuccess)
486      struct sconnection *qconn;
487      pointer puuconf;
488      struct uuconf_dialer *qdialer;
489      boolean fsuccess;
490 {
491   struct ssysdep_conn *qsysdep;
492   boolean fret;
493
494   qsysdep = (struct ssysdep_conn *) qconn->psysdep;
495
496   fret = TRUE;
497   if (qsysdep->o >= 0)
498     {
499       if (qsysdep->ftli)
500         {
501           if (t_close (qsysdep->o) < 0)
502             {
503               ulog (LOG_ERROR, "t_close: %s", ztlierror ());
504               fret = FALSE;
505             }
506         }
507       else
508         {
509           if (close (qsysdep->o) < 0)
510             {
511               ulog (LOG_ERROR, "close: %s", strerror (errno));
512               fret = FALSE;
513             }
514         }
515
516       qsysdep->o = -1;
517     }
518
519   /* If the current pid is not the one we used to open the port, then
520      we must have forked up above and we are now the child.  In this
521      case, we are being called from within the fendless loop in
522      uucico.c.  We return FALSE to force the loop to end and the child
523      to exit.  This should be handled in a cleaner fashion.  */
524   if (qsysdep->ipid != getpid ())
525     fret = FALSE;
526
527   return fret;
528 }
529 \f
530 /* Dial out on a TLI port, so to speak: connect to a remote computer.  */
531
532 /*ARGSUSED*/
533 static boolean
534 ftli_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound)
535      struct sconnection *qconn;
536      pointer puuconf;
537      const struct uuconf_system *qsys;
538      const char *zphone;
539      struct uuconf_dialer *qdialer;
540      enum tdialerfound *ptdialerfound;
541 {
542   struct ssysdep_conn *qsysdep;
543   char **pzdialer;
544   const char *zaddr;
545   struct t_call *qtcall;
546   char *zescape;
547
548   qsysdep = (struct ssysdep_conn *) qconn->psysdep;
549
550   *ptdialerfound = DIALERFOUND_FALSE;
551
552   pzdialer = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzdialer;
553   if (*pzdialer == NULL)
554     pzdialer = NULL;
555
556   /* If the first dialer is "TLI" or "TLIS", we use the first token
557      (pzdialer[1]) as the address to connect to.  */
558   zaddr = zphone;
559   if (pzdialer != NULL
560       && (strcmp (pzdialer[0], "TLI") == 0
561           || strcmp (pzdialer[0], "TLIS") == 0))
562     {
563       if (pzdialer[1] == NULL)
564         ++pzdialer;
565       else
566         {
567           if (strcmp (pzdialer[1], "\\D") != 0
568               && strcmp (pzdialer[1], "\\T") != 0)
569             zaddr = pzdialer[1];
570           pzdialer += 2;
571         }
572     }
573   
574   if (zaddr == NULL)
575     {
576       ulog (LOG_ERROR, "No address for TLI connection");
577       return FALSE;
578     }
579
580   qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ADDR);
581   if (qtcall == NULL)
582     {
583       ulog (LOG_ERROR, "t_alloc (T_CALL): %s", ztlierror ());
584       return FALSE;
585     }
586
587   zescape = zbufcpy (zaddr);
588   qtcall->addr.len = cescape (zescape);
589   if (qtcall->addr.len > qtcall->addr.maxlen)
590     {
591       ulog (LOG_ERROR, "%s: TLI address too long (max %d)", zaddr,
592             qtcall->addr.maxlen);
593       ubuffree (zescape);
594       return FALSE;
595     }
596   memcpy (qtcall->addr.buf, zescape, qtcall->addr.len);
597   ubuffree (zescape);
598
599   if (t_connect (qsysdep->o, qtcall, (struct t_call *) NULL) < 0)
600     {
601       if (t_errno != TLOOK)
602         ulog (LOG_ERROR, "t_connect: %s", ztlierror ());
603       else
604         {
605           if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0)
606             ulog (LOG_ERROR, "t_rcvdis: %s", ztlierror ());
607           else
608             ulog (LOG_ERROR, "Connection refused");
609         }
610       return FALSE;
611     }
612
613   /* We've connected to the remote.  Push any desired modules.  */
614   if (! ftli_push (qconn))
615     return FALSE;      
616
617   /* Handle the rest of the dialer sequence.  */
618   if (pzdialer != NULL && *pzdialer != NULL)
619     {
620       if (! fconn_dial_sequence (qconn, puuconf, pzdialer, qsys, zphone,
621                                  qdialer, ptdialerfound))
622         return FALSE;
623     }
624
625   return TRUE;
626 }
627
628 #endif /* HAVE_TLI */