/* $NetBSD: scm.c,v 1.12 2001/09/18 04:36:28 itojun Exp $ */ /* * Copyright (c) 1992 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* * SUP Communication Module for 4.3 BSD * * SUP COMMUNICATION MODULE SPECIFICATIONS: * * IN THIS MODULE: * * CONNECTION ROUTINES * * FOR SERVER * servicesetup (port) establish TCP port connection * char *port; name of service * service () accept TCP port connection * servicekill () close TCP port in use by another process * serviceprep () close temp ports used to make connection * serviceend () close TCP port * * FOR CLIENT * request (port,hostname,retry) establish TCP port connection * char *port,*hostname; name of service and host * int retry; true if retries should be used * requestend () close TCP port * * HOST NAME CHECKING * p = remotehost () remote host name (if known) * char *p; * i = samehost () whether remote host is also this host * int i; * i = matchhost (name) whether remote host is same as name * int i; * char *name; * * RETURN CODES * All procedures return values as indicated above. Other routines * normally return SCMOK on success, SCMERR on error. * * COMMUNICATION PROTOCOL * * Described in scmio.c. * ********************************************************************** * HISTORY * 2-Oct-92 Mary Thompson (mrt) at Carnegie-Mellon University * Added conditional declarations of INADDR_NONE and INADDR_LOOPBACK * since Tahoe version of does not define them. * * Revision 1.13 92/08/11 12:05:35 mrt * Added changes from stump: * Allow for multiple interfaces, and for numeric addresses. * Changed to use builtin port for the "supfiledbg" * service when getservbyname() cannot find it. * Added forward static declatations, delinted. * Updated variable argument usage. * [92/08/08 mrt] * * Revision 1.12 92/02/08 19:01:11 mja * Add (struct sockaddr *) casts for HC 2.1. * [92/02/08 18:59:09 mja] * * Revision 1.11 89/08/03 19:49:03 mja * Updated to use v*printf() in place of _doprnt(). * [89/04/19 mja] * * 11-Feb-88 Glenn Marcy (gm0w) at Carnegie-Mellon University * Moved sleep into computeBackoff, renamed to dobackoff. * * 10-Feb-88 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added timeout to backoff. * * 27-Dec-87 Glenn Marcy (gm0w) at Carnegie-Mellon University * Removed nameserver support. * * 09-Sep-87 Glenn Marcy (gm0w) at Carnegie-Mellon University * Fixed to depend less upon having name of remote host. * * 25-May-87 Doug Philips (dwp) at Carnegie-Mellon Universtiy * Extracted backoff/sleeptime computation from "request" and * created "computeBackoff" so that I could use it in sup.c when * trying to get to nameservers as a group. * * 21-May-87 Chriss Stephens (chriss) at Carnegie Mellon University * Merged divergent CS and EE versions. * * 02-May-87 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added some bullet-proofing code around hostname calls. * * 31-Mar-87 Dan Nydick (dan) at Carnegie-Mellon University * Fixed for 4.3. * * 30-May-86 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added code to use known values for well-known ports if they are * not found in the host table. * * 19-Feb-86 Glenn Marcy (gm0w) at Carnegie-Mellon University * Changed setsockopt SO_REUSEADDR to be non-fatal. Added fourth * parameter as described in 4.3 manual entry. * * 15-Feb-86 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added call of readflush() to requestend() routine. * * 29-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University * Major rewrite for protocol version 4. All read/write and crypt * routines are now in scmio.c. * * 14-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added setsockopt SO_REUSEADDR call. * * 01-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University * Removed code to "gracefully" handle unexpected messages. This * seems reasonable since it didn't work anyway, and should be * handled at a higher level anyway by adhering to protocol version * number conventions. * * 26-Nov-85 Glenn Marcy (gm0w) at Carnegie-Mellon University * Fixed scm.c to free space for remote host name when connection * is closed. * * 07-Nov-85 Glenn Marcy (gm0w) at Carnegie-Mellon University * Fixed 4.2 retry code to reload sin values before retry. * * 22-Oct-85 Glenn Marcy (gm0w) at Carnegie-Mellon University * Added code to retry initial connection open request. * * 22-Sep-85 Glenn Marcy (gm0w) at Carnegie-Mellon University * Merged 4.1 and 4.2 versions together. * * 21-Sep-85 Glenn Marcy (gm0w) at Carnegie-Mellon University * Add close() calls after pipe() call. * * 12-Jun-85 Steven Shafer (sas) at Carnegie-Mellon University * Converted for 4.2 sockets; added serviceprep() routine. * * 04-Jun-85 Steven Shafer (sas) at Carnegie-Mellon University * Created for 4.2 BSD. * ********************************************************************** */ #include #include #include #include #include #include #include #ifndef SIOCGIFCONF #include #endif #include #include #include #include #ifdef __STDC__ #include #else #include #endif #include #include "supcdefs.h" #include "supextern.h" #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff /* -1 return */ #endif #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK (u_long)0x7f000001 /* 127.0.0.1 */ #endif char scmversion[] = "4.3 BSD"; extern int silent; /************************* *** M A C R O S *** *************************/ /* networking parameters */ #define NCONNECTS 5 /********************************************* *** G L O B A L V A R I A B L E S *** *********************************************/ extern char program[]; /* name of program we are running */ extern int progpid; /* process id to display */ int netfile = -1; /* network file descriptor */ static int sock = -1; /* socket used to make connection */ static struct sockaddr_storage remoteaddr; /* remote host address */ static char *remotename = NULL; /* remote host name */ static int swapmode; /* byte-swapping needed on server? */ static char *myhost __P((void)); /*************************************************** *** C O N N E C T I O N R O U T I N E S *** *** F O R S E R V E R *** ***************************************************/ int servicesetup (server, af) /* listen for clients */ char *server; int af; { struct addrinfo hints, *res0, *res; char port[NI_MAXSERV]; int error; const char *cause = "unknown"; int one = 1; memset(&hints, 0, sizeof(hints)); hints.ai_family = af; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; error = getaddrinfo(NULL, server, &hints, &res0); if (error) { /* retry with precompiled knowledge */ if (strcmp(server, FILEPORT) == 0) snprintf(port, sizeof(port), "%u", FILEPORTNUM); else if (strcmp(server, DEBUGFPORT) == 0) snprintf(port, sizeof(port), "%u", DEBUGFPORTNUM); else port[0] = '\0'; if (port[0]) error = getaddrinfo(NULL, port, &hints, &res0); if (error) return (scmerr (-1, "%s: %s", server, gai_strerror(error))); } for (res = res0; res; res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) { cause = "socket"; continue; } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(int)) < 0) { cause = "setsockopt(SO_REUSEADDR)"; close(sock); continue; } if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { cause = "bind"; close(sock); continue; } if (listen(sock, NCONNECTS) < 0) { cause = "listen"; close(sock); continue; } freeaddrinfo(res0); return SCMOK; } freeaddrinfo(res0); return (scmerr (errno, "%s", cause)); } int service () { struct sockaddr_storage from; int x,len; remotename = NULL; len = sizeof (from); do { netfile = accept (sock,(struct sockaddr *)&from,&len); } while (netfile < 0 && errno == EINTR); if (netfile < 0) return (scmerr (errno,"Can't accept connections")); if (len > sizeof(remoteaddr)) { close(netfile); return (scmerr (errno,"Can't accept connections")); } memcpy(&remoteaddr, &from, len); if (read(netfile,(char *)&x,sizeof(int)) != sizeof(int)) return (scmerr (errno,"Can't transmit data on connection")); if (x == 0x01020304) swapmode = 0; else if (x == 0x04030201) swapmode = 1; else return (scmerr (-1,"Unexpected byteswap mode %x",x)); return (SCMOK); } int serviceprep () /* kill temp socket in daemon */ { if (sock >= 0) { (void) close (sock); sock = -1; } return (SCMOK); } int servicekill () /* kill net file in daemon's parent */ { if (netfile >= 0) { (void) close (netfile); netfile = -1; } if (remotename) { free (remotename); remotename = NULL; } return (SCMOK); } int serviceend () /* kill net file after use in daemon */ { if (netfile >= 0) { (void) close (netfile); netfile = -1; } if (remotename) { free (remotename); remotename = NULL; } return (SCMOK); } /*************************************************** *** C O N N E C T I O N R O U T I N E S *** *** F O R C L I E N T *** ***************************************************/ int dobackoff (t,b) int *t,*b; { struct timeval tt; unsigned s; if (*t == 0) return (0); s = *b * 30; if (gettimeofday (&tt,(struct timezone *)NULL) >= 0) s += (tt.tv_usec >> 8) % s; if (*b < 32) *b <<= 1; if (*t != -1) { if (s > *t) s = *t; *t -= s; } if (!silent) (void) scmerr (-1,"Will retry in %d seconds",s); sleep (s); return (1); } int request (server,hostname,retry) /* connect to server */ char *server; char *hostname; int *retry; { struct addrinfo hints, *res, *res0; int error; char port[NI_MAXSERV]; int backoff; int x; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(hostname, server, &hints, &res0); if (error) { /* retry with precompiled knowledge */ if (strcmp(server, FILEPORT) == 0) snprintf(port, sizeof(port), "%u", FILEPORTNUM); else if (strcmp(server, DEBUGFPORT) == 0) snprintf(port, sizeof(port), "%u", DEBUGFPORTNUM); else port[0] = '\0'; if (port[0]) error = getaddrinfo(hostname, port, &hints, &res0); if (error) return (scmerr (-1, "%s: %s", server, gai_strerror(error))); } backoff = 1; while (1) { netfile = -1; for (res = res0; res; res = res->ai_next) { if (res->ai_addrlen > sizeof(remoteaddr)) continue; netfile = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (netfile < 0) continue; if (connect(netfile, res->ai_addr, res->ai_addrlen) < 0) { close(netfile); netfile = -1; continue; } break; } if (netfile < 0) { if (!dobackoff (retry, &backoff)) { freeaddrinfo(res0); return (SCMERR); } continue; } else break; } memcpy(&remoteaddr, res->ai_addr, res->ai_addrlen); remotename = salloc(hostname); x = 0x01020304; (void) write (netfile,(char *)&x,sizeof(int)); swapmode = 0; /* swap only on server, not client */ freeaddrinfo(res0); return (SCMOK); } int requestend () /* end connection to server */ { (void) readflush (); if (netfile >= 0) { (void) close (netfile); netfile = -1; } if (remotename) { free (remotename); remotename = NULL; } return (SCMOK); } /************************************************* *** H O S T N A M E C H E C K I N G *** *************************************************/ static char *myhost () /* find my host name */ { struct hostent *h; static char name[MAXHOSTNAMELEN + 1]; if (name[0] == '\0') { if (gethostname(name, sizeof name) < 0) return (NULL); name[sizeof(name) - 1] = '\0'; if ((h = gethostbyname (name)) == NULL) return (NULL); (void) strcpy (name,h->h_name); } return (name); } char *remotehost () /* remote host name (if known) */ { char h1[NI_MAXHOST]; if (remotename == NULL) { if (getnameinfo((struct sockaddr *)&remoteaddr, remoteaddr.ss_len, h1, sizeof(h1), NULL, 0, 0)) return("UNKNOWN"); remotename = salloc (h1); if (remotename == NULL) return("UNKNOWN"); } return (remotename); } int thishost (host) register char *host; { register struct hostent *h; char *name; if ((name = myhost ()) == NULL) logquit (1,"Can't find my host entry '%s'", myhost()); h = gethostbyname (host); if (h == NULL) return (0); return (strcasecmp (name,h->h_name) == 0); } int samehost () /* is remote host same as local host? */ { struct ifaddrs *ifap, *ifa; char h1[NI_MAXHOST], h2[NI_MAXHOST]; #ifdef NI_WITHSCOPEID const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; #else const int niflags = NI_NUMERICHOST; #endif if (getnameinfo((struct sockaddr *)&remoteaddr, remoteaddr.ss_len, h1, sizeof(h1), NULL, 0, niflags)) return (0); if (getifaddrs(&ifap) < 0) return (0); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (remoteaddr.ss_family != ifa->ifa_addr->sa_family) continue; if (getnameinfo(ifa->ifa_addr, ifa->ifa_addr->sa_len, h2, sizeof(h2), NULL, 0, niflags)) continue; if (strcmp(h1, h2) == 0) { freeifaddrs(ifap); return (1); } } freeifaddrs(ifap); return (0); } int matchhost (name) /* is this name of remote host? */ char *name; { char h1[NI_MAXHOST], h2[NI_MAXHOST]; #ifdef NI_WITHSCOPEID const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; #else const int niflags = NI_NUMERICHOST; #endif struct addrinfo hints, *res0, *res; if (getnameinfo((struct sockaddr *)&remoteaddr, remoteaddr.ss_len, h1, sizeof(h1), NULL, 0, niflags)) return (0); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(name, "0", &hints, &res0) != 0) return (0); for (res = res0; res; res = res->ai_next) { if (remoteaddr.ss_family != res->ai_family) continue; if (getnameinfo(res->ai_addr, res->ai_addrlen, h2, sizeof(h2), NULL, 0, niflags)) continue; if (strcmp(h1, h2) == 0) { freeaddrinfo(res0); return (1); } } freeaddrinfo(res0); return (0); } #ifdef __STDC__ int scmerr (int error,char *fmt,...) #else /*VARARGS*//*ARGSUSED*/ int scmerr (va_alist) va_dcl #endif { va_list ap; #ifdef __STDC__ va_start(ap,fmt); #else int error; char *fmt; va_start(ap); error = va_arg(ap,int); fmt = va_arg(ap,char *); #endif (void) fflush (stdout); if (progpid > 0) fprintf (stderr,"%s %d: ",program,progpid); else fprintf (stderr,"%s: ",program); vfprintf(stderr, fmt, ap); va_end(ap); if (error >= 0) fprintf (stderr,": %s\n",errmsg(error)); else fprintf (stderr,"\n"); (void) fflush (stderr); return (SCMERR); } /******************************************************* *** I N T E G E R B Y T E - S W A P P I N G *** *******************************************************/ union intchar { int ui; char uc[sizeof(int)]; }; int byteswap (in) int in; { union intchar x,y; register int ix,iy; if (swapmode == 0) return (in); x.ui = in; iy = sizeof(int); for (ix=0; ix