/* $NetBSD: scm.c,v 1.30 2013/03/08 20:56:44 christos 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 "libc.h" #include #include #include #include #include #include #include #include #include #include #if !defined(__linux__) #if !defined(__CYGWIN__) #include #else #include "ifaddrs.h" #endif #else #include #endif #ifdef __CYGWIN__ #include "getaddrinfo.h" #endif #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(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(char *server, int af) { /* listen for clients */ 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, &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(void) { struct sockaddr_storage from; int x; socklen_t len; remotename = NULL; len = sizeof(from); do { netfile = accept(sock, (struct sockaddr *)(void *)&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, &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)); setproctitle("Serving %s", remotehost()); return (SCMOK); } int serviceprep(void) { /* kill temp socket in daemon */ if (sock >= 0) { (void) close(sock); sock = -1; } return (SCMOK); } int servicekill(void) { /* 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(void) { /* 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(int *t, int *b) { struct timeval tt; unsigned s; if (*t == 0) return (0); s = *b * 30; if (gettimeofday(&tt, NULL) >= 0) s += ((uint32_t)tt.tv_usec >> 8) % s; if (*b < 32) *b <<= 1; if (*t != -1) { if (s > (unsigned) *t) s = *t; *t -= s; } if (!silent) (void) scmerr(-1, "Will retry in %d seconds", s); sleep(s); return (1); } int request(char *server, char *hostname, int *retry) { /* connect to server */ 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; for (;;) { 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; } if (res == NULL) { freeaddrinfo(res0); return (SCMERR); } memcpy(&remoteaddr, res->ai_addr, res->ai_addrlen); remotename = estrdup(hostname); x = 0x01020304; if (write(netfile, &x, sizeof(int)) == -1) return (SCMERR); swapmode = 0; /* swap only on server, not client */ freeaddrinfo(res0); return (SCMOK); } int requestend(void) { /* 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(void) { /* 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); } const char * remotehost(void) { /* remote host name (if known) */ char h1[NI_MAXHOST]; if (remotename == NULL) { if (getnameinfo((struct sockaddr *)(void *)&remoteaddr, #ifdef BSD4_4 (socklen_t)remoteaddr.ss_len, #else sizeof(struct sockaddr), #endif h1, sizeof(h1), NULL, 0, 0)) return ("UNKNOWN"); remotename = estrdup(h1); if (remotename == NULL) return ("UNKNOWN"); } return (remotename); } int thishost(char *host) { 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); } #ifdef __linux__ /* Nice and sleazy does it... */ struct ifaddrs { struct ifaddrs *ifa_next; struct sockaddr *ifa_addr; struct sockaddr ifa_addrspace; }; static int getifaddrs(struct ifaddrs **ifap) { struct ifaddrs *ifa; int nint; int n; char buf[10 * 1024]; struct ifconf ifc; struct ifreq *ifr; int s; if ((s = socket (AF_INET, SOCK_DGRAM, 0)) == -1) return -1; ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl(s, SIOCGIFCONF, &ifc) == -1) { (void)close(s); return -1; } (void)close(s); if ((nint = ifc.ifc_len / sizeof(struct ifreq)) <= 0) return 0; if ((ifa = malloc((unsigned)nint * sizeof(struct ifaddrs))) == NULL) return -1; for (ifr = ifc.ifc_req, n = 0; n < nint; n++, ifr++) { ifa[n].ifa_next = &ifa[n + 1]; ifa[n].ifa_addr = &ifa[n].ifa_addrspace; (void)memcpy(ifa[n].ifa_addr, &ifr->ifr_addr, sizeof(*ifa[n].ifa_addr)); } ifa[nint - 1].ifa_next = NULL; *ifap = ifa; return nint; } static void freeifaddrs(struct ifaddrs *ifa) { free(ifa); } #endif int samehost(void) { /* is remote host same as local host? */ struct ifaddrs *ifap, *ifa; char h1[NI_MAXHOST], h2[NI_MAXHOST]; const int niflags = NI_NUMERICHOST; if (getnameinfo((struct sockaddr *)(void *)&remoteaddr, #ifdef BSD4_4 (socklen_t)remoteaddr.ss_len, #else sizeof(struct sockaddr), #endif 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, #ifdef BSD4_4 (socklen_t)ifa->ifa_addr->sa_len, #else sizeof(struct sockaddr), #endif h2, sizeof(h2), NULL, 0, niflags)) continue; if (strcmp(h1, h2) == 0) { freeifaddrs(ifap); return (1); } } freeifaddrs(ifap); return (0); } int matchhost(char *name) { /* is this name of remote host? */ char h1[NI_MAXHOST], h2[NI_MAXHOST]; const int niflags = NI_NUMERICHOST; struct addrinfo hints, *res0, *res; if (getnameinfo((struct sockaddr *)(void *)&remoteaddr, #ifdef BSD4_4 (socklen_t)remoteaddr.ss_len, #else sizeof(struct sockaddr), #endif h1, sizeof(h1), NULL, 0, niflags)) return (0); memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; /* dummy */ 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); } int scmerr(int error, const char *fmt, ...) { va_list ap; char hostname[MAXHOSTNAMELEN]; gethostname(hostname, sizeof(hostname)); va_start(ap, fmt); (void) fflush(stdout); if (progpid > 0) fprintf(stderr, "%s@%s %d: ", program, hostname, progpid); else fprintf(stderr, "%s@%s: ", program, hostname); vfprintf(stderr, fmt, ap); va_end(ap); if (error >= 0) fprintf(stderr, " (%s)\n", strerror(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(int in) { union intchar x, y; int ix, iy; if (swapmode == 0) return (in); x.ui = in; iy = sizeof(int); for (ix = 0; ix < (int) sizeof(int); ix++) { --iy; y.uc[iy] = x.uc[ix]; } return (y.ui); }