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