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