1/*	$NetBSD: scm.c,v 1.28 2009/10/17 22:26:13 christos Exp $	*/
2
3/*
4 * Copyright (c) 1992 Carnegie Mellon University
5 * All Rights Reserved.
6 *
7 * Permission to use, copy, modify and distribute this software and its
8 * documentation is hereby granted, provided that both the copyright
9 * notice and this permission notice appear in all copies of the
10 * software, derivative works or modified versions, and any portions
11 * thereof, and that both notices appear in supporting documentation.
12 *
13 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
14 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
15 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
16 *
17 * Carnegie Mellon requests users of this software to return to
18 *
19 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
20 *  School of Computer Science
21 *  Carnegie Mellon University
22 *  Pittsburgh PA 15213-3890
23 *
24 * any improvements or extensions that they make and grant Carnegie Mellon
25 * the rights to redistribute these changes.
26 */
27/*
28 * SUP Communication Module for 4.3 BSD
29 *
30 * SUP COMMUNICATION MODULE SPECIFICATIONS:
31 *
32 * IN THIS MODULE:
33 *
34 * CONNECTION ROUTINES
35 *
36 *   FOR SERVER
37 *	servicesetup (port)	establish TCP port connection
38 *	  char *port;			name of service
39 *	service ()		accept TCP port connection
40 *	servicekill ()		close TCP port in use by another process
41 *	serviceprep ()		close temp ports used to make connection
42 *	serviceend ()		close TCP port
43 *
44 *   FOR CLIENT
45 *	request (port,hostname,retry) establish TCP port connection
46 *	  char *port,*hostname;		  name of service and host
47 *	  int retry;			  true if retries should be used
48 *	requestend ()		close TCP port
49 *
50 * HOST NAME CHECKING
51 *	p = remotehost ()	remote host name (if known)
52 *	  char *p;
53 *	i = samehost ()		whether remote host is also this host
54 *	  int i;
55 *	i = matchhost (name)	whether remote host is same as name
56 *	  int i;
57 *	  char *name;
58 *
59 * RETURN CODES
60 *	All procedures return values as indicated above.  Other routines
61 *	normally return SCMOK on success, SCMERR on error.
62 *
63 * COMMUNICATION PROTOCOL
64 *
65 *	Described in scmio.c.
66 *
67 **********************************************************************
68 * HISTORY
69 *  2-Oct-92  Mary Thompson (mrt) at Carnegie-Mellon University
70 *	Added conditional declarations of INADDR_NONE and INADDR_LOOPBACK
71 *	since Tahoe version of <netinet/in.h> does not define them.
72 *
73 * Revision 1.13  92/08/11  12:05:35  mrt
74 * 	Added changes from stump:
75 * 	  Allow for multiple interfaces, and for numeric addresses.
76 * 	  Changed to use builtin port for the "supfiledbg"
77 * 	    service when getservbyname() cannot find it.
78 * 	  Added forward static declatations, delinted.
79 * 	  Updated variable argument usage.
80 * 	[92/08/08            mrt]
81 *
82 * Revision 1.12  92/02/08  19:01:11  mja
83 * 	Add (struct sockaddr *) casts for HC 2.1.
84 * 	[92/02/08  18:59:09  mja]
85 *
86 * Revision 1.11  89/08/03  19:49:03  mja
87 * 	Updated to use v*printf() in place of _doprnt().
88 * 	[89/04/19            mja]
89 *
90 * 11-Feb-88  Glenn Marcy (gm0w) at Carnegie-Mellon University
91 *	Moved sleep into computeBackoff, renamed to dobackoff.
92 *
93 * 10-Feb-88  Glenn Marcy (gm0w) at Carnegie-Mellon University
94 *	Added timeout to backoff.
95 *
96 * 27-Dec-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
97 *	Removed nameserver support.
98 *
99 * 09-Sep-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
100 *	Fixed to depend less upon having name of remote host.
101 *
102 * 25-May-87  Doug Philips (dwp) at Carnegie-Mellon Universtiy
103 *	Extracted backoff/sleeptime computation from "request" and
104 *	created "computeBackoff" so that I could use it in sup.c when
105 *	trying to get to nameservers as a group.
106 *
107 * 21-May-87  Chriss Stephens (chriss) at Carnegie Mellon University
108 *	Merged divergent CS and EE versions.
109 *
110 * 02-May-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
111 *	Added some bullet-proofing code around hostname calls.
112 *
113 * 31-Mar-87  Dan Nydick (dan) at Carnegie-Mellon University
114 *	Fixed for 4.3.
115 *
116 * 30-May-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
117 *	Added code to use known values for well-known ports if they are
118 *	not found in the host table.
119 *
120 * 19-Feb-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
121 *	Changed setsockopt SO_REUSEADDR to be non-fatal.  Added fourth
122 *	parameter as described in 4.3 manual entry.
123 *
124 * 15-Feb-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
125 *	Added call of readflush() to requestend() routine.
126 *
127 * 29-Dec-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
128 *	Major rewrite for protocol version 4.  All read/write and crypt
129 *	routines are now in scmio.c.
130 *
131 * 14-Dec-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
132 *	Added setsockopt SO_REUSEADDR call.
133 *
134 * 01-Dec-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
135 *	Removed code to "gracefully" handle unexpected messages.  This
136 *	seems reasonable since it didn't work anyway, and should be
137 *	handled at a higher level anyway by adhering to protocol version
138 *	number conventions.
139 *
140 * 26-Nov-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
141 *	Fixed scm.c to free space for remote host name when connection
142 *	is closed.
143 *
144 * 07-Nov-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
145 *	Fixed 4.2 retry code to reload sin values before retry.
146 *
147 * 22-Oct-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
148 *	Added code to retry initial connection open request.
149 *
150 * 22-Sep-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
151 *	Merged 4.1 and 4.2 versions together.
152 *
153 * 21-Sep-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
154 *	Add close() calls after pipe() call.
155 *
156 * 12-Jun-85  Steven Shafer (sas) at Carnegie-Mellon University
157 *	Converted for 4.2 sockets; added serviceprep() routine.
158 *
159 * 04-Jun-85  Steven Shafer (sas) at Carnegie-Mellon University
160 *	Created for 4.2 BSD.
161 *
162 **********************************************************************
163 */
164
165#include "libc.h"
166#include <errno.h>
167#include <sys/param.h>
168#include <sys/types.h>
169#include <sys/time.h>
170#include <sys/socket.h>
171#include <netinet/in.h>
172#include <arpa/inet.h>
173#include <net/if.h>
174#include <netdb.h>
175#include <stdarg.h>
176#if !defined(__linux__)
177#if !defined(__CYGWIN__)
178#include <ifaddrs.h>
179#else
180#include "ifaddrs.h"
181#endif
182#else
183#include <sys/ioctl.h>
184#endif
185#ifdef __CYGWIN__
186#include "getaddrinfo.h"
187#endif
188#include "supcdefs.h"
189#include "supextern.h"
190
191#ifndef INADDR_NONE
192#define	INADDR_NONE		0xffffffff	/* -1 return */
193#endif
194#ifndef INADDR_LOOPBACK
195#define	INADDR_LOOPBACK		(u_long)0x7f000001	/* 127.0.0.1 */
196#endif
197
198char scmversion[] = "4.3 BSD";
199extern int silent;
200
201/*************************
202 ***    M A C R O S    ***
203 *************************/
204
205/* networking parameters */
206#define NCONNECTS 5
207
208/*********************************************
209 ***    G L O B A L   V A R I A B L E S    ***
210 *********************************************/
211
212extern char program[];		/* name of program we are running */
213extern int progpid;		/* process id to display */
214
215int netfile = -1;		/* network file descriptor */
216
217static int sock = -1;		/* socket used to make connection */
218static struct sockaddr_storage remoteaddr;	/* remote host address */
219static char *remotename = NULL;	/* remote host name */
220static int swapmode;		/* byte-swapping needed on server? */
221
222
223static char *myhost(void);
224
225/***************************************************
226 ***    C O N N E C T I O N   R O U T I N E S    ***
227 ***    F O R   S E R V E R                      ***
228 ***************************************************/
229
230int
231servicesetup(char *server, int af)
232{				/* listen for clients */
233	struct addrinfo hints, *res0, *res;
234	char port[NI_MAXSERV];
235	int error;
236	const char *cause = "unknown";
237	int one = 1;
238
239	memset(&hints, 0, sizeof(hints));
240	hints.ai_family = af;
241	hints.ai_socktype = SOCK_STREAM;
242	hints.ai_flags = AI_PASSIVE;
243	error = getaddrinfo(NULL, server, &hints, &res0);
244	if (error) {
245		/* retry with precompiled knowledge */
246		if (strcmp(server, FILEPORT) == 0)
247			snprintf(port, sizeof(port), "%u", FILEPORTNUM);
248		else if (strcmp(server, DEBUGFPORT) == 0)
249			snprintf(port, sizeof(port), "%u", DEBUGFPORTNUM);
250		else
251			port[0] = '\0';
252		if (port[0])
253			error = getaddrinfo(NULL, port, &hints, &res0);
254		if (error)
255			return (scmerr(-1, "%s: %s", server,
256				gai_strerror(error)));
257	}
258	for (res = res0; res; res = res->ai_next) {
259		sock = socket(res->ai_family, res->ai_socktype,
260		    res->ai_protocol);
261		if (sock < 0) {
262			cause = "socket";
263			continue;
264		}
265		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
266		    &one, sizeof(int)) < 0) {
267			cause = "setsockopt(SO_REUSEADDR)";
268			close(sock);
269			continue;
270		}
271		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
272			cause = "bind";
273			close(sock);
274			continue;
275		}
276		if (listen(sock, NCONNECTS) < 0) {
277			cause = "listen";
278			close(sock);
279			continue;
280		}
281		freeaddrinfo(res0);
282		return SCMOK;
283	}
284
285	freeaddrinfo(res0);
286	return (scmerr(errno, "%s", cause));
287}
288
289int
290service(void)
291{
292	struct sockaddr_storage from;
293	int x;
294	socklen_t len;
295
296	remotename = NULL;
297	len = sizeof(from);
298	do {
299		netfile = accept(sock, (struct sockaddr *)(void *)&from, &len);
300	} while (netfile < 0 && errno == EINTR);
301	if (netfile < 0)
302		return (scmerr(errno, "Can't accept connections"));
303	if (len > sizeof(remoteaddr)) {
304		close(netfile);
305		return (scmerr(errno, "Can't accept connections"));
306	}
307	memcpy(&remoteaddr, &from, len);
308	if (read(netfile, &x, sizeof(int)) != sizeof(int))
309		return (scmerr(errno, "Can't transmit data on connection"));
310	if (x == 0x01020304)
311		swapmode = 0;
312	else if (x == 0x04030201)
313		swapmode = 1;
314	else
315		return (scmerr(-1, "Unexpected byteswap mode %x", x));
316	setproctitle("Serving %s", remotehost());
317	return (SCMOK);
318}
319
320int
321serviceprep(void)
322{				/* kill temp socket in daemon */
323	if (sock >= 0) {
324		(void) close(sock);
325		sock = -1;
326	}
327	return (SCMOK);
328}
329
330int
331servicekill(void)
332{				/* kill net file in daemon's parent */
333	if (netfile >= 0) {
334		(void) close(netfile);
335		netfile = -1;
336	}
337	if (remotename) {
338		free(remotename);
339		remotename = NULL;
340	}
341	return (SCMOK);
342}
343
344int
345serviceend(void)
346{				/* kill net file after use in daemon */
347	if (netfile >= 0) {
348		(void) close(netfile);
349		netfile = -1;
350	}
351	if (remotename) {
352		free(remotename);
353		remotename = NULL;
354	}
355	return (SCMOK);
356}
357/***************************************************
358 ***    C O N N E C T I O N   R O U T I N E S    ***
359 ***    F O R   C L I E N T                      ***
360 ***************************************************/
361
362int
363dobackoff(int *t, int *b)
364{
365	struct timeval tt;
366	unsigned s;
367
368	if (*t == 0)
369		return (0);
370	s = *b * 30;
371	if (gettimeofday(&tt, NULL) >= 0)
372		s += ((uint32_t)tt.tv_usec >> 8) % s;
373	if (*b < 32)
374		*b <<= 1;
375	if (*t != -1) {
376		if (s > (unsigned) *t)
377			s = *t;
378		*t -= s;
379	}
380	if (!silent)
381		(void) scmerr(-1, "Will retry in %d seconds", s);
382	sleep(s);
383	return (1);
384}
385
386int
387request(char *server, char *hostname, int *retry)
388{				/* connect to server */
389	struct addrinfo hints, *res, *res0;
390	int error;
391	char port[NI_MAXSERV];
392	int backoff;
393	int x;
394
395	memset(&hints, 0, sizeof(hints));
396	hints.ai_family = PF_UNSPEC;
397	hints.ai_socktype = SOCK_STREAM;
398	error = getaddrinfo(hostname, server, &hints, &res0);
399	if (error) {
400		/* retry with precompiled knowledge */
401		if (strcmp(server, FILEPORT) == 0)
402			snprintf(port, sizeof(port), "%u", FILEPORTNUM);
403		else if (strcmp(server, DEBUGFPORT) == 0)
404			snprintf(port, sizeof(port), "%u", DEBUGFPORTNUM);
405		else
406			port[0] = '\0';
407		if (port[0])
408			error = getaddrinfo(hostname, port, &hints, &res0);
409		if (error)
410			return (scmerr(-1, "%s: %s", server,
411				gai_strerror(error)));
412	}
413	backoff = 1;
414	for (;;) {
415		netfile = -1;
416		for (res = res0; res; res = res->ai_next) {
417			if (res->ai_addrlen > sizeof(remoteaddr))
418				continue;
419			netfile = socket(res->ai_family, res->ai_socktype,
420			    res->ai_protocol);
421			if (netfile < 0)
422				continue;
423			if (connect(netfile, res->ai_addr, res->ai_addrlen) < 0) {
424				close(netfile);
425				netfile = -1;
426				continue;
427			}
428			break;
429		}
430
431		if (netfile < 0) {
432			if (!dobackoff(retry, &backoff)) {
433				freeaddrinfo(res0);
434				return (SCMERR);
435			}
436			continue;
437		} else
438			break;
439	}
440
441	if (res == NULL) {
442		freeaddrinfo(res0);
443		return (SCMERR);
444	}
445	memcpy(&remoteaddr, res->ai_addr, res->ai_addrlen);
446	remotename = estrdup(hostname);
447	x = 0x01020304;
448	(void) write(netfile, &x, sizeof(int));
449	swapmode = 0;		/* swap only on server, not client */
450	freeaddrinfo(res0);
451	return (SCMOK);
452}
453
454int
455requestend(void)
456{				/* end connection to server */
457	(void) readflush();
458	if (netfile >= 0) {
459		(void) close(netfile);
460		netfile = -1;
461	}
462	if (remotename) {
463		free(remotename);
464		remotename = NULL;
465	}
466	return (SCMOK);
467}
468/*************************************************
469 ***    H O S T   N A M E   C H E C K I N G    ***
470 *************************************************/
471
472static char *
473myhost(void)
474{				/* find my host name */
475	struct hostent *h;
476	static char name[MAXHOSTNAMELEN + 1];
477
478	if (name[0] == '\0') {
479		if (gethostname(name, sizeof name) < 0)
480			return (NULL);
481		name[sizeof(name) - 1] = '\0';
482		if ((h = gethostbyname(name)) == NULL)
483			return (NULL);
484		(void) strcpy(name, h->h_name);
485	}
486	return (name);
487}
488
489const char *
490remotehost(void)
491{				/* remote host name (if known) */
492	char h1[NI_MAXHOST];
493
494	if (remotename == NULL) {
495		if (getnameinfo((struct sockaddr *)(void *)&remoteaddr,
496#ifdef BSD4_4
497			(socklen_t)remoteaddr.ss_len,
498#else
499			sizeof(struct sockaddr),
500#endif
501			h1, sizeof(h1), NULL, 0, 0))
502			return ("UNKNOWN");
503		remotename = estrdup(h1);
504		if (remotename == NULL)
505			return ("UNKNOWN");
506	}
507	return (remotename);
508}
509
510int
511thishost(char *host)
512{
513	struct hostent *h;
514	char *name;
515
516	if ((name = myhost()) == NULL)
517		logquit(1, "Can't find my host entry '%s'", myhost());
518	h = gethostbyname(host);
519	if (h == NULL)
520		return (0);
521	return (strcasecmp(name, h->h_name) == 0);
522}
523
524#ifdef __linux__
525/* Nice and sleazy does it... */
526struct ifaddrs {
527	struct ifaddrs *ifa_next;
528	struct sockaddr *ifa_addr;
529	struct sockaddr ifa_addrspace;
530};
531
532static int
533getifaddrs(struct ifaddrs **ifap)
534{
535	struct ifaddrs *ifa;
536	int nint;
537	int n;
538	char buf[10 * 1024];
539	struct ifconf ifc;
540	struct ifreq *ifr;
541	int s;
542
543	if ((s = socket (AF_INET, SOCK_DGRAM, 0)) == -1)
544		return -1;
545
546	ifc.ifc_len = sizeof(buf);
547	ifc.ifc_buf = buf;
548
549	if (ioctl(s, SIOCGIFCONF, &ifc) == -1) {
550		(void)close(s);
551		return -1;
552	}
553
554	(void)close(s);
555
556	if ((nint = ifc.ifc_len / sizeof(struct ifreq)) <= 0)
557		return 0;
558
559	if ((ifa = malloc((unsigned)nint * sizeof(struct ifaddrs))) == NULL)
560		return -1;
561
562	for (ifr = ifc.ifc_req, n = 0; n < nint; n++, ifr++) {
563		ifa[n].ifa_next = &ifa[n + 1];
564		ifa[n].ifa_addr = &ifa[n].ifa_addrspace;
565		(void)memcpy(ifa[n].ifa_addr, &ifr->ifr_addr,
566		    sizeof(*ifa[n].ifa_addr));
567	}
568
569	ifa[nint - 1].ifa_next = NULL;
570	*ifap = ifa;
571	return nint;
572}
573
574static void
575freeifaddrs(struct ifaddrs *ifa)
576{
577	free(ifa);
578}
579
580#endif
581
582int
583samehost(void)
584{				/* is remote host same as local host? */
585	struct ifaddrs *ifap, *ifa;
586	char h1[NI_MAXHOST], h2[NI_MAXHOST];
587	const int niflags = NI_NUMERICHOST;
588
589	if (getnameinfo((struct sockaddr *)(void *)&remoteaddr,
590#ifdef BSD4_4
591	    (socklen_t)remoteaddr.ss_len,
592#else
593	    sizeof(struct sockaddr),
594#endif
595	    h1, sizeof(h1), NULL, 0, niflags))
596		return (0);
597	if (getifaddrs(&ifap) < 0)
598		return (0);
599	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
600		if (remoteaddr.ss_family != ifa->ifa_addr->sa_family)
601			continue;
602		if (getnameinfo(ifa->ifa_addr,
603#ifdef BSD4_4
604		    (socklen_t)ifa->ifa_addr->sa_len,
605#else
606		    sizeof(struct sockaddr),
607#endif
608		    h2, sizeof(h2), NULL, 0, niflags))
609			continue;
610		if (strcmp(h1, h2) == 0) {
611			freeifaddrs(ifap);
612			return (1);
613		}
614	}
615	freeifaddrs(ifap);
616	return (0);
617}
618
619int
620matchhost(char *name)
621{				/* is this name of remote host? */
622	char h1[NI_MAXHOST], h2[NI_MAXHOST];
623	const int niflags = NI_NUMERICHOST;
624	struct addrinfo hints, *res0, *res;
625
626	if (getnameinfo((struct sockaddr *)(void *)&remoteaddr,
627#ifdef BSD4_4
628	    (socklen_t)remoteaddr.ss_len,
629#else
630	    sizeof(struct sockaddr),
631#endif
632	    h1, sizeof(h1), NULL, 0, niflags))
633		return (0);
634	memset(&hints, 0, sizeof(hints));
635	hints.ai_family = PF_UNSPEC;
636	hints.ai_socktype = SOCK_DGRAM;	/* dummy */
637	if (getaddrinfo(name, "0", &hints, &res0) != 0)
638		return (0);
639	for (res = res0; res; res = res->ai_next) {
640		if (remoteaddr.ss_family != res->ai_family)
641			continue;
642		if (getnameinfo(res->ai_addr, res->ai_addrlen,
643		    h2, sizeof(h2), NULL, 0, niflags))
644			continue;
645		if (strcmp(h1, h2) == 0) {
646			freeaddrinfo(res0);
647			return (1);
648		}
649	}
650	freeaddrinfo(res0);
651	return (0);
652}
653
654int
655scmerr(int error, const char *fmt, ...)
656{
657	va_list ap;
658
659	va_start(ap, fmt);
660
661	(void) fflush(stdout);
662	if (progpid > 0)
663		fprintf(stderr, "%s %d: ", program, progpid);
664	else
665		fprintf(stderr, "%s: ", program);
666
667	vfprintf(stderr, fmt, ap);
668	va_end(ap);
669	if (error >= 0)
670		fprintf(stderr, " (%s)\n", strerror(error));
671	else
672		fprintf(stderr, "\n");
673	(void) fflush(stderr);
674	return (SCMERR);
675}
676/*******************************************************
677 ***    I N T E G E R   B Y T E - S W A P P I N G    ***
678 *******************************************************/
679
680union intchar {
681	int ui;
682	char uc[sizeof(int)];
683};
684
685int
686byteswap(int in)
687{
688	union intchar x, y;
689	int ix, iy;
690
691	if (swapmode == 0)
692		return (in);
693	x.ui = in;
694	iy = sizeof(int);
695	for (ix = 0; ix < (int) sizeof(int); ix++) {
696		--iy;
697		y.uc[iy] = x.uc[ix];
698	}
699	return (y.ui);
700}
701