[BACK]Return to scm.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sup / src

File: [local] / src / usr.bin / sup / src / Attic / scm.c (download)

Revision 1.9, Tue Feb 1 03:23:40 2000 UTC (24 years, 4 months ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_2_9_BASE, OPENBSD_2_9, OPENBSD_2_8_BASE, OPENBSD_2_8, OPENBSD_2_7_BASE, OPENBSD_2_7
Changes since 1.8: +1 -2 lines

no need to extern int errno if errno.h is included

/*	$OpenBSD: scm.c,v 1.9 2000/02/01 03:23:40 deraadt 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 <netinet/in.h> does not define them.
 * 
 * 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 <errno.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#ifndef SIOCGIFCONF
#include <sys/sockio.h>
#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <syslog.h>
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.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 in_addr 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 lock_host_file(char *lockdir) {

  /* mark that we are servicing a connection from host at ia.  we do
   * this by getting an exclusive lock on a file that is "ia" in
   * dotted decimal, in directory "lockdir". For convienence's sake we
   * write our process ID in there. Should be called from servicing
   * child, so lock goes away on exit. must be called after service()
   * sets global remoteaddr (above).
   *
   * returns fd for file on success,-1 on failure.
   */

  char *dd, *lpath;
  int i, fd; 
  FILE *f;

  dd=strdup(inet_ntoa(remoteaddr));
  if (dd == NULL) {
    syslog(LOG_ERR, "Malloc failed in lock_host_file()");
    return(-1);
  }
  i=strlen(lockdir) + strlen(dd) + 2; /* null and maybe a / */
  lpath=(char *)malloc(i * sizeof(char));
  lpath[i-1]='\0';
  if (lpath == NULL) {
    syslog(LOG_ERR, "Malloc failed in lock_host_file()");
    free(dd);
    return(-1);
  }
  (void) strncpy(lpath, lockdir, i-1);
  if (lpath[strlen(lpath) - 1] != '/') {
    lpath[strlen(lpath) + 1] = '\0';
    lpath[strlen(lpath)] = '/'; 
  }
  (void) strncat(lpath, dd, i - strlen(lpath)); 
  if (lpath[i-1] != '\0') {
    syslog(LOG_CRIT, "Buffer overrun in lock_host_file(). SHOULD NOT HAPPEN!");
    abort();
  }
  free(dd);
  if ((fd = open(lpath, O_CREAT | O_WRONLY, 0600)) < 0) {
    syslog(LOG_ERR, "Couldn't open/create lock file %s (%m)", lpath);
    free(lpath);
    return(-1);
  }
#ifdef USE_LOCKF
  if (lockf(fd, F_TLOCK, 0) != 0) 
#else
  if (flock(fd, LOCK_EX | LOCK_NB ) != 0)
#endif
    {
      syslog(LOG_DEBUG, "Can't get lock on %s.", lpath);
      free(lpath);
      close(fd);
      return(-1);
    }
  if (ftruncate(fd, 0) < 0) {
    syslog(LOG_ERR, "Couldn't ftruncate fd %d for lock file %s (%m)",
	   fd, lpath);
    free(lpath);
    close(fd);
    return(-1);
  }
  f=fdopen(fd,"w");
  if (f == NULL) {
    syslog(LOG_ERR, "Couldn't fopen fd %d for lock file %s (%m)", fd, lpath);
    free(lpath);
    close(fd);
    return(-1);
  }
  (void)fprintf(f,"%d\n", getpid());
  fflush(f);
  free(lpath);
  return(fd);
}

int
servicesetup (server)		/* listen for clients */
char *server;
{
	struct sockaddr_in sin;
	struct servent *sp;
	short port;
	int one = 1;

	if (myhost () == NULL)
		return (scmerr (-1,"Local hostname not known"));
	if ((sp = getservbyname(server,"tcp")) == 0) {
		if (strcmp(server, FILEPORT) == 0)
			port = htons((u_short)FILEPORTNUM);
		else if (strcmp(server, DEBUGFPORT) == 0)
			port = htons((u_short)DEBUGFPORTNUM);
		else
			return (scmerr (-1,"Can't find %s server description",server));
		(void) scmerr (-1,"%s/tcp: unknown service: using port %d",
					server,port);
	} else
		port = sp->s_port;
	endservent ();
	sock = socket (AF_INET,SOCK_STREAM,0);
	if (sock < 0)
		return (scmerr (errno,"Can't create socket for connections"));
	if (setsockopt (sock,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(int)) < 0)
		(void) scmerr (errno,"Can't set SO_REUSEADDR socket option");
	(void) bzero ((char *)&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = port;
	if (bind (sock,(struct sockaddr *)&sin,sizeof(sin)) < 0)
		return (scmerr (errno,"Can't bind socket for connections"));
	if (listen (sock,NCONNECTS) < 0)
		return (scmerr (errno,"Can't listen on socket"));
	return (SCMOK);
}

int
service ()
{
	struct sockaddr_in from;
	int x,len;

again:
	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"));

	/* protection against ftp bounce attack */
	if (from.sin_port == htons(20)) {
		close(netfile);
		goto again;
	}
	remoteaddr = from.sin_addr;
	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;
{
	int x, backoff;
	struct hostent *h;
	struct servent *sp;
	struct sockaddr_in sin, tin;
	short port;

	if ((sp = getservbyname(server,"tcp")) == 0) {
		if (strcmp(server, FILEPORT) == 0)
			port = htons((u_short)FILEPORTNUM);
		else if (strcmp(server, DEBUGFPORT) == 0)
			port = htons((u_short)DEBUGFPORTNUM);
		else
			return (scmerr (-1,"Can't find %s server description",
					server));
		if (!silent)
		    (void) scmerr (-1,"%s/tcp: unknown service: using port %d",
				    server,port);
	} else
		port = sp->s_port;
	(void) bzero ((char *)&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr (hostname);
	if (sin.sin_addr.s_addr == (u_long) INADDR_NONE) {
		if ((h = gethostbyname (hostname)) == NULL)
			return (scmerr (-1,"Can't find host entry for %s",
					hostname));
		hostname = h->h_name;
		(void) bcopy (h->h_addr,(char *)&sin.sin_addr,h->h_length);
	}
	sin.sin_port = port;
	backoff = 1;
	for (;;) {
		netfile = socket (AF_INET,SOCK_STREAM,0);
		if (netfile < 0)
			return (scmerr (errno,"Can't create socket"));
		tin = sin;
		if (connect(netfile,(struct sockaddr *)&tin,sizeof(tin)) >= 0)
			break;
		(void) scmerr (errno,"Can't connect to server for %s",server);
		(void) close(netfile);
		if (!dobackoff (retry,&backoff))
			return (SCMERR);
	}
	remoteaddr = sin.sin_addr;
	remotename = salloc(hostname);
	x = 0x01020304;
	(void) write (netfile,(char *)&x,sizeof(int));
	swapmode = 0;		/* swap only on server, not client */
	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];


	if (name[0] == '\0') {
		if (gethostname (name,MAXHOSTNAMELEN) < 0)
			return (NULL);
		if ((h = gethostbyname (name)) == NULL)
			return (NULL);
		(void) strncpy (name,h->h_name,sizeof name-1);
		name[sizeof name-1] = '\0';
	}
	return (name);
}

char *remotehost ()	/* remote host name (if known) */
{
	register struct hostent *h;

	if (remotename == NULL) {
		h = gethostbyaddr ((char *)&remoteaddr,sizeof(remoteaddr),
				    AF_INET);
		remotename = salloc (h ? h->h_name : inet_ntoa(remoteaddr));
		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");
	h = gethostbyname (host);
	if (h == NULL) return (0);
	return (strcasecmp (name,h->h_name) == 0);
}

int samehost ()		/* is remote host same as local host? */
{
	static struct in_addr *intp;
	static int nint = 0;
	struct in_addr *ifp;
	int n;

	if (nint <= 0) {
		int s;
		char buf[BUFSIZ];
		struct ifconf ifc;
		struct ifreq *ifr;
		struct sockaddr_in sin;

		if ((s = socket (AF_INET,SOCK_DGRAM,0)) < 0)
			logquit (1,"Can't create socket for SIOCGIFCONF");
		ifc.ifc_len = sizeof(buf);
		ifc.ifc_buf = buf;
		if (ioctl (s,SIOCGIFCONF,(char *)&ifc) < 0)
			logquit (1,"SIOCGIFCONF failed");
		(void) close(s);
		if ((nint = ifc.ifc_len/sizeof(struct ifreq)) <= 0)
			return (0);
		intp = (struct in_addr *)
			malloc ((unsigned) nint*sizeof(struct in_addr));
		if ((ifp = intp) == 0)
			logquit (1,"no space for interfaces");
		for (ifr = ifc.ifc_req, n = nint; n > 0; --n, ifr++) {
			(void) bcopy ((char *)&ifr->ifr_addr,(char *)&sin,sizeof(sin));
			*ifp++ = sin.sin_addr;
		}
	}
	if (remoteaddr.s_addr == htonl(INADDR_LOOPBACK))
		return (1);
	for (ifp = intp, n = nint; n > 0; --n, ifp++)
		if (remoteaddr.s_addr == ifp->s_addr)
			return (1);
	return (0);
}

int matchhost (name)	/* is this name of remote host? */
char *name;
{
	struct hostent *h;
	struct in_addr addr;
	char **ap;
	if ((addr.s_addr = inet_addr(name)) != (u_long) INADDR_NONE)
		return (addr.s_addr == remoteaddr.s_addr);
	if ((h = gethostbyname (name)) == 0)
		return (0);
	if (h->h_addrtype != AF_INET || h->h_length != sizeof(struct in_addr))
		return (0);
	for (ap = h->h_addr_list; *ap; ap++)
		if (bcmp ((char *)&remoteaddr,*ap,h->h_length) == 0)
			return (1);
	return (0);
}

#ifdef __STDC__
int scmerr (int errno,char *fmt,...)
#else
/*VARARGS*//*ARGSUSED*/
int scmerr (va_alist)
va_dcl
#endif
{
	va_list ap;
#ifdef __STDC__
	va_start(ap,fmt);
#else
	int errno;
	char *fmt;

	va_start(ap);
	errno = 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 (errno >= 0)
		fprintf (stderr,": %s\n",errmsg(errno));
	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<sizeof(int); ix++) {
		--iy;
		y.uc[iy] = x.uc[ix];
	}
	return (y.ui);
}