#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 2)."
# Contents:  ftpd.c traceroute.c
# Wrapped by warren@slug.pws.bull.com on Thu Oct 11 14:55:00 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'ftpd.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ftpd.c'\"
else
echo shar: Extracting \"'ftpd.c'\" \(26820 characters\)
sed "s/^X//" >'ftpd.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1985 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1985 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)ftpd.c	5.16 (Berkeley) 10/30/88";
X#endif /* not lint */
X
X/*
X * FTP server.
X */
X#include <sys/param.h>
X#include <sys/stat.h>
X#include <sys/ioctl.h>
X#include <sys/socket.h>
X#include <sys/file.h>
X#include <sys/wait.h>
X
X#include <netinet/in.h>
X
X#include <arpa/ftp.h>
X#include <arpa/inet.h>
X#include <arpa/telnet.h>
X
X#include <stdio.h>
X#include <signal.h>
X#include <pwd.h>
X#include <setjmp.h>
X#include <netdb.h>
X#include <errno.h>
X#include <strings.h>
X#include <syslog.h>
X#include <dirent.h>
X#include <ctype.h>
X#include "conf.h"
X
X/*
X * File containing login names
X * NOT to be used on this machine.
X * Commonly used to disallow uucp.
X */
X#define	FTPUSERS	"/etc/ftpusers"
X     
Xint restricted_user;
X
Xextern	int errno;
Xextern	char *sys_errlist[];
Xextern	char *crypt();
Xextern	char version[];
Xextern	char *home;		/* pointer to home directory for glob */
Xextern	FILE *popen(), *fopen(), *freopen();
Xextern	int  pclose(), fclose();
Xextern	char *getline();
Xextern	char cbuf[];
Xchar	*badhost();
X
Xstruct	sockaddr_in ctrl_addr;
Xstruct	sockaddr_in data_source;
Xstruct	sockaddr_in data_dest;
Xstruct	sockaddr_in his_addr;
X
Xint	data;
Xjmp_buf	errcatch, urgcatch;
Xint	logged_in;
Xstruct	passwd *pw;
Xint	debug;
Xint	timeout = 900;    /* timeout after 15 minutes of inactivity */
Xint	logging;
Xint	anon_only;
Xint	guest;
Xint	wtmp;
Xint	type;
Xint	form;
Xint	stru;			/* avoid C keyword */
Xint	mode;
Xint	usedefault = 1;		/* for data transfers */
Xint	pdata;			/* for passive mode */
Xint	unique;
Xint	transflag;
Xchar	tmpline[7];
Xchar	hostname[32];
Xchar	remotehost[32];
X
X/*
X * Timeout intervals for retrying connections
X * to hosts that don't accept PORT cmds.  This
X * is a kludge, but given the problems with TCP...
X */
X#define	SWAITMAX	90	/* wait at most 90 seconds */
X#define	SWAITINT	5	/* interval between retries */
X
Xint	swaitmax = SWAITMAX;
Xint	swaitint = SWAITINT;
X
Xint	lostconn();
Xint	myoob();
XFILE	*getdatasock(), *dataconn();
X
Xmain(argc, argv)
X     int argc;
X     char *argv[];
X{
X  int addrlen, on = 1;
X  long pgid;
X  char *cp;
X  
X  addrlen = sizeof (his_addr);
X  if (getpeername(0, &his_addr, &addrlen) < 0) {
X    syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
X    exit(1);
X  }
X  addrlen = sizeof (ctrl_addr);
X  if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) {
X    syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
X    exit(1);
X  }
X  data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
X  debug = 0;
X#ifndef ultrix
X  openlog("ftpd", LOG_PID|LOG_NDELAY, LOG_DAEMON);
X#else
X  openlog("ftpd", LOG_PID);
X#endif
X  argc--, argv++;
X  while (argc > 0 && *argv[0] == '-') {
X    for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
X      
X    case 'v':
X      debug = 1;
X      break;
X      
X    case 'd':
X      debug = 1;
X      break;
X      
X    case 'l':
X      logging = 1;
X      break;
X      
X    case 'a':
X      anon_only = 1;
X      break;
X      
X    case 't':
X      timeout = atoi(++cp);
X      goto nextopt;
X      break;
X      
X    default:
X      fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
X	      *cp);
X      break;
X    }
X  nextopt:
X    argc--, argv++;
X  }
X  (void) freopen("/dev/null", "w", stderr);
X  (void) signal(SIGPIPE, lostconn);
X  (void) signal(SIGCHLD, SIG_IGN);
X  if ((int)signal(SIGURG, myoob) < 0)
X    syslog(LOG_ERR, "signal: %m");
X  
X  /* handle urgent data inline */
X#ifndef ultrix
X#ifdef SO_OOBINLINE
X  if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) {
X    syslog(LOG_ERR, "setsockopt: %m");
X  }
X#endif SO_OOBINLINE
X#endif ultrix
X  pgid = getpid();
X  if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) {
X    syslog(LOG_ERR, "ioctl: %m");
X  }
X  dolog(&his_addr);
X  /* do telnet option negotiation here */
X  /*
X   * Set up default state
X   */
X  logged_in = 0;
X  data = -1;
X  type = TYPE_A;
X  form = FORM_N;
X  stru = STRU_F;
X  mode = MODE_S;
X  tmpline[0] = '\0';
X  (void) gethostname(hostname, sizeof (hostname));
X  reply(220, "%s FTP server (%s) ready.",
X	hostname, version);
X  for (;;) {
X    (void) setjmp(errcatch);
X    (void) yyparse();
X  }
X}
X
Xlostconn()
X{
X  
X  if (debug)
X    syslog(LOG_DEBUG, "lost connection");
X  dologout(-1);
X}
X
Xstatic char ttyline[20];
X
X/*
X * Helper function for sgetpwnam().
X */
Xchar *
X  sgetsave(s)
Xchar *s;
X{
X#ifdef notdef
X  char *new = strdup(s);
X#else
X  char *malloc();
X  char *new = malloc((unsigned) strlen(s) + 1);
X#endif
X  
X  if (new == NULL) {
X    reply(553, "Local resource failure");
X    dologout(1);
X  }
X#ifndef notdef
X  (void) strcpy(new, s);
X#endif
X  return (new);
X}
X
X/*
X * Save the result of a getpwnam.  Used for USER command, since
X * the data returned must not be clobbered by any other command
X * (e.g., globbing).
X */
Xstruct passwd *
X  sgetpwnam(name)
Xchar *name;
X{
X  static struct passwd save;
X  register struct passwd *p;
X  char *sgetsave();
X  
X  if ((p = getpwnam(name)) == NULL)
X    return (p);
X  if (save.pw_name) {
X    free(save.pw_name);
X    free(save.pw_passwd);
X    free(save.pw_comment);
X    free(save.pw_gecos);
X    free(save.pw_dir);
X    free(save.pw_shell);
X  }
X  save = *p;
X  save.pw_name = sgetsave(p->pw_name);
X  save.pw_passwd = sgetsave(p->pw_passwd);
X  save.pw_comment = sgetsave(p->pw_comment);
X  save.pw_gecos = sgetsave(p->pw_gecos);
X  save.pw_dir = sgetsave(p->pw_dir);
X  save.pw_shell = sgetsave(p->pw_shell);
X  return (&save);
X}
X
Xpass(passwd)
X     char *passwd;
X{
X  char *xpasswd;
X  
X  if (logged_in || pw == NULL) {
X    reply(503, "Login with USER first.");
X    return;
X  }
X  if (!guest) {		/* "ftp" is only account allowed no password */
X    xpasswd = crypt(passwd, pw->pw_passwd);
X    /* The strcmp does not catch null passwords! */
X    if (*pw->pw_passwd == '\0'
X	|| strcmp(xpasswd, pw->pw_passwd)
X	|| anon_only
X	) {
X      reply(530, "Login incorrect.");
X      pw = NULL;
X      if (logging)
X	syslog(LOG_WARNING,
X	       "login %s from %s failed, id=%s",
X	       pw->pw_name, remotehost, passwd);
X      return;
X    }
X  } else {
X    char *reason;
X    
X#ifdef STRICT_EMAIL
X    if (!strncmp(passwd, "guest", 5) || !strncmp(passwd, "ftp", 3)
X	|| (strlen(passwd) < 3) || !strncmp(passwd, "Email", 5) ||
X	!strncmp(passwd, "EMail", 5) || !strncmp(passwd, "ident", 5)) {
X      lreply(530, "Give me a break, %s is not an EMail address.", passwd);
X      reply(530, "Login incorrect.");
X      pw = NULL;
X      if (logging)
X	syslog(LOG_WARNING,
X	       "anonymous login from %s rejected (email), id=%s",
X	       remotehost, passwd);
X      return;
X    }	
X#endif
X#ifdef BAD_HOSTS
X    if ((reason = badhost(remotehost)) == NULL) {
X#endif
X      if (logging)
X	syslog(LOG_WARNING, "anonymous ftp from %s id=%s",
X	       remotehost, passwd);
X
X#ifdef REGULATE
X      restricted_user = NetInRouteTo(REGULATE, remotehost);
X#else 
X      restricted_user = 0;
X#endif
X      
X#ifdef BAD_HOSTS
X    } else {
X      lreply(530, "Categorically refused: %s", reason);
X      lreply(530, "If you think this is unfair, send %s mail", EMAIL_CONTACT);
X      reply(530, "Login refused.");
X      pw = NULL;
X      if (logging)
X	syslog(LOG_WARNING,
X	       "anonymous login from %s rejected (badhost), id=%s",
X	       remotehost, passwd);
X    }
X#endif
X    
X  }
X  setegid(pw->pw_gid);
X  initgroups(pw->pw_name, pw->pw_gid);
X  if (chdir(pw->pw_dir)) {
X    reply(530, "User %s: can't change directory to %s.",
X	  pw->pw_name, pw->pw_dir);
X    goto bad;
X  }
X  
X  /* grab wtmp before chroot */
X  wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
X  if (guest && chroot(pw->pw_dir) < 0) {
X    reply(550, "Can't set guest privileges.");
X    if (wtmp >= 0) {
X      (void) close(wtmp);
X      wtmp = -1;
X    }
X    goto bad;
X  }
X  if (!guest) {
X    reply(230, "User %s logged in.", pw->pw_name);
X  } else {
X    FILE *not;
X    char notbuf[80];
X    /** Check for .message file in ~ftp **/
X    
X    Logme(passwd, remotehost);
X    if ((not = fopen(".message", "r")) != NULL) {
X      while (fgets(notbuf, 80, not) != NULL) {
X	notbuf[strlen(notbuf)-1] = '\0';
X	lreply(230, notbuf);
X      }
X      fclose(not);
X    }
X    if (restricted_user) 
X       reply(230, "Welcome... further restrictions apply.");
X    else
X       reply(230, "Welcome... usual restrictions apply, priority modified.");
X
X    nice(10);
X  }
X  
X  logged_in = 1;
X  (void)sprintf(ttyline, "ftp%d", getpid());
X  logwtmp(ttyline, pw->pw_name, remotehost);
X  seteuid(pw->pw_uid);
X  home = pw->pw_dir;		/* home dir for globbing */
X  return;
X bad:
X  seteuid(0);
X  pw = NULL;
X}
X
Xretrieve(cmd, name)
X     char *cmd, *name;
X{
X  FILE *fin, *dout;
X  struct stat st;
X  int (*closefunc)(), tmp;
X  char wd[MAXPATHLEN];
X  char buf[MAXPATHLEN+50];
X  char line[BUFSIZ];
X  
X  if (cmd == 0) {
X#ifdef notdef
X    /* no remote command execution -- it's a security hole */
X    if (*name == '|')
X      fin = popen(name + 1, "r"), closefunc = pclose;
X    else
X#endif
X      {
X	if (xfer_ok(name)) 
X	  {
X	    fin = fopen(name, "r"), closefunc = fclose;
X	    (void) sprintf(buf, "retrieve %s/%s ", getwd(wd), name);
X	  }
X	else
X	  {
X	    fin = NULL;
X	    errno = EACCES;
X            reply(550, "%s: You may not xfer this file.", name);
X	    (void) sprintf(buf, "retrieve %s/%s (deny-restrict)", getwd(wd), name);
X            if (logging)
X               syslog(LOG_NOTICE, buf);
X	  }
X      }
X  } else {
X    (void) sprintf(line, cmd, name), name = line;
X    (void) sprintf(buf, "%s (cwd=%s) ", line, getwd(wd));
X    fin = popen(line, "r"), closefunc = pclose;
X  }
X  if (fin == NULL) {
X    if (errno != 0)
X      reply(550, "%s: %s.", name, sys_errlist[errno]);
X    return;
X  }
X  st.st_size = 0;
X  if (cmd == 0 &&
X      (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
X    reply(550, "%s: not a plain file.", name);
X    goto done;
X  }
X  dout = dataconn(name, st.st_size, "w");
X  if (dout == NULL)
X    goto done;
X  if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) {
X    reply(550, "%s: %s.", name, sys_errlist[errno]);
X    strcat(buf, "failed: %m");
X  }
X  else if (tmp == 0) {
X    reply(226, "Transfer complete.");
X    strcat(buf, "succeeded");
X  }
X  (void) fclose(dout);
X  if (logging)
X    syslog(LOG_NOTICE, buf);
X  data = -1;
X  pdata = -1;
X done:
X  (*closefunc)(fin);
X}
X
Xstore(name, mode)
X     char *name, *mode;
X{
X  FILE *fout, *din;
X  int (*closefunc)(), dochown = 0, tmp;
X  char *gunique(), *local;
X  char wd[MAXPATHLEN];
X  char buf[MAXPATHLEN+50];
X  
X  sprintf(buf, "store %s/%s ", getwd(wd), name);
X#ifdef notdef
X  /* no remote command execution -- it's a security hole */
X  if (name[0] == '|')
X    fout = popen(&name[1], "w"), closefunc = pclose;
X  else
X#endif
X    {
X      struct stat st;
X      
X      local = name;
X      if (stat(name, &st) < 0) {
X	dochown++;
X      }
X      else if (unique) {
X	if ((local = gunique(name)) == NULL) {
X	  return;
X	}
X	dochown++;
X      }
X      fout = fopen(local, mode), closefunc = fclose;
X    }
X  if (fout == NULL) {
X    reply(553, "%s: %s.", local, sys_errlist[errno]);
X    strcat(buf, "failed: %m");
X    syslog(LOG_WARNING, buf);
X    return;
X  }
X  din = dataconn(local, (off_t)-1, "r");
X  if (din == NULL)
X    goto done;
X  if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) {
X    reply(552, "%s: %s.", local, sys_errlist[errno]);
X    strcat(buf, "failed: %m");
X  }
X  else if (tmp == 0 && !unique) {
X    reply(226, "Transfer complete.");
X    strcat(buf, "succeeded");
X  }
X  else if (tmp == 0 && unique) {
X    reply(226, "Transfer complete (unique file name:%s).", local);
X  }
X  (void) fclose(din);
X  if (logging)
X    syslog(LOG_WARNING, buf);
X  data = -1;
X  pdata = -1;
X done:
X  if (dochown)
X    (void) chown(local, pw->pw_uid, -1);
X  (*closefunc)(fout);
X}
X
XFILE *
X  getdatasock(mode)
Xchar *mode;
X{
X  int s, on = 1;
X  
X  if (data >= 0)
X    return (fdopen(data, mode));
X  s = socket(AF_INET, SOCK_STREAM, 0);
X  if (s < 0)
X    return (NULL);
X  seteuid(0);
X  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0)
X    goto bad;
X  /* anchor socket to avoid multi-homing problems */
X  data_source.sin_family = AF_INET;
X  data_source.sin_addr = ctrl_addr.sin_addr;
X  if (bind(s, &data_source, sizeof (data_source)) < 0)
X    goto bad;
X  seteuid(pw->pw_uid);
X  return (fdopen(s, mode));
X bad:
X  seteuid(pw->pw_uid);
X  (void) close(s);
X  return (NULL);
X}
X
XFILE *
X  dataconn(name, size, mode)
Xchar *name;
Xoff_t size;
Xchar *mode;
X{
X  char sizebuf[32];
X  FILE *file;
X  int retry = 0;
X  
X  if (size >= 0)
X    (void) sprintf (sizebuf, " (%ld bytes)", size);
X  else
X    (void) strcpy(sizebuf, "");
X  if (pdata > 0) {
X    struct sockaddr_in from;
X    int s, fromlen = sizeof(from);
X    
X    s = accept(pdata, &from, &fromlen);
X    if (s < 0) {
X      reply(425, "Can't open data connection.");
X      (void) close(pdata);
X      pdata = -1;
X      return(NULL);
X    }
X    (void) close(pdata);
X    pdata = s;
X    reply(150, "Opening data connection for %s (%s mode)%s.",
X	  name, type == TYPE_A ? "ascii" : "binary", sizebuf);
X    return(fdopen(pdata, mode));
X  }
X  if (data >= 0) {
X    reply(125, "Using existing data connection for %s%s.",
X	  name, sizebuf);
X    usedefault = 1;
X    return (fdopen(data, mode));
X  }
X  if (usedefault)
X    data_dest = his_addr;
X  usedefault = 1;
X  if (badport((int) data_dest.sin_port)) {
X    reply(425, "Can't open data connection: Illegal port.");
X    return (NULL);
X  }
X  file = getdatasock(mode);
X  if (file == NULL) {
X    reply(425, "Can't create data socket (%s,%d): %s.",
X	  inet_ntoa(data_source.sin_addr),
X	  ntohs(data_source.sin_port),
X	  sys_errlist[errno]);
X    return (NULL);
X  }
X  data = fileno(file);
X  while (connect(data, &data_dest, sizeof (data_dest)) < 0) {
X    if (errno == EADDRINUSE && retry < swaitmax) {
X      sleep((unsigned) swaitint);
X      retry += swaitint;
X      continue;
X    }
X    reply(425, "Can't build data connection: %s.",
X	  sys_errlist[errno]);
X    (void) fclose(file);
X    data = -1;
X    return (NULL);
X  }
X  reply(150, "Opening data connection for %s (%s mode)%s.",
X	name, type == TYPE_A ? "ascii" : "binary", sizebuf);
X  return (file);
X}
X
X/*
X * Tranfer the contents of "instr" to
X * "outstr" peer using the appropriate
X * encapulation of the date subject
X * to Mode, Structure, and Type.
X *
X * NB: Form isn't handled.
X */
Xsend_data(instr, outstr)
X     FILE *instr, *outstr;
X{
X  register int c;
X  int netfd, filefd, cnt;
X  char buf[BUFSIZ];
X  
X  transflag++;
X  if (setjmp(urgcatch)) {
X    transflag = 0;
X    return(-1);
X  }
X  switch (type) {
X    
X  case TYPE_A:
X    while ((c = getc(instr)) != EOF) {
X      if (c == '\n') {
X	if (ferror (outstr)) {
X	  transflag = 0;
X	  return (1);
X	}
X	(void) putc('\r', outstr);
X      }
X      (void) putc(c, outstr);
X      /*	if (c == '\r')			*/
X      /*		putc ('\0', outstr);	*/
X    }
X    transflag = 0;
X    if (ferror (instr) || ferror (outstr)) {
X      return (1);
X    }
X    return (0);
X    
X  case TYPE_I:
X  case TYPE_L:
X    netfd = fileno(outstr);
X    filefd = fileno(instr);
X    
X    while ((cnt = read(filefd, buf, sizeof (buf))) > 0) {
X      if (write(netfd, buf, cnt) < 0) {
X	transflag = 0;
X	return (1);
X      }
X    }
X    transflag = 0;
X    return (cnt < 0);
X  }
X  reply(550, "Unimplemented TYPE %d in send_data", type);
X  transflag = 0;
X  return (-1);
X}
X
X/*
X * Transfer data from peer to
X * "outstr" using the appropriate
X * encapulation of the data subject
X * to Mode, Structure, and Type.
X *
X * N.B.: Form isn't handled.
X */
Xreceive_data(instr, outstr)
X     FILE *instr, *outstr;
X{
X  register int c;
X  int cnt;
X  char buf[BUFSIZ];
X  
X  
X  transflag++;
X  if (setjmp(urgcatch)) {
X    transflag = 0;
X    return(-1);
X  }
X  switch (type) {
X    
X  case TYPE_I:
X  case TYPE_L:
X    while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
X      if (write(fileno(outstr), buf, cnt) < 0) {
X	transflag = 0;
X	return (1);
X      }
X    }
X    transflag = 0;
X    return (cnt < 0);
X    
X  case TYPE_E:
X    reply(553, "TYPE E not implemented.");
X    transflag = 0;
X    return (-1);
X    
X  case TYPE_A:
X    while ((c = getc(instr)) != EOF) {
X      while (c == '\r') {
X	if (ferror (outstr)) {
X	  transflag = 0;
X	  return (1);
X	}
X	if ((c = getc(instr)) != '\n')
X	  (void) putc ('\r', outstr);
X	/*	if (c == '\0')			*/
X	/*		continue;		*/
X      }
X      (void) putc (c, outstr);
X    }
X    transflag = 0;
X    if (ferror (instr) || ferror (outstr))
X      return (1);
X    return (0);
X  }
X  transflag = 0;
X  fatal("Unknown type in receive_data.");
X  /*NOTREACHED*/
X}
X
Xfatal(s)
X     char *s;
X{
X  reply(451, "Error in server: %s\n", s);
X  reply(221, "Closing connection due to server error.");
X  dologout(0);
X}
X
Xreply(n, s, p0, p1, p2, p3, p4)
X     int n;
X     char *s;
X{
X  
X  printf("%d ", n);
X  printf(s, p0, p1, p2, p3, p4);
X  printf("\r\n");
X  (void) fflush(stdout);
X  if (debug) {
X    syslog(LOG_DEBUG, "<--- %d ", n);
X    syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4);
X  }
X}
X
Xlreply(n, s, p0, p1, p2, p3, p4)
X     int n;
X     char *s;
X{
X  printf("%d-", n);
X  printf(s, p0, p1, p2, p3, p4);
X  printf("\r\n");
X  (void) fflush(stdout);
X  if (debug) {
X    syslog(LOG_DEBUG, "<--- %d- ", n);
X    syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4);
X  }
X}
X
Xack(s)
X     char *s;
X{
X  reply(250, "%s command successful.", s);
X}
X
Xnack(s)
X     char *s;
X{
X  reply(502, "%s command not implemented.", s);
X}
X
Xyyerror(s)
X     char *s;
X{
X  char *cp;
X  
X  cp = index(cbuf,'\n');
X  *cp = '\0';
X  reply(500, "'%s': command not understood.",cbuf);
X}
X
Xdelete(name)
X     char *name;
X{
X  struct stat st;
X  
X  if (stat(name, &st) < 0) {
X    reply(550, "%s: %s.", name, sys_errlist[errno]);
X    return;
X  }
X  if ((st.st_mode&S_IFMT) == S_IFDIR) {
X    if (rmdir(name) < 0) {
X      reply(550, "%s: %s.", name, sys_errlist[errno]);
X      return;
X    }
X    goto done;
X  }
X  if (unlink(name) < 0) {
X    reply(550, "%s: %s.", name, sys_errlist[errno]);
X    return;
X  }
X done:
X  ack("DELE");
X}
X
Xcwd(path)
X     char *path;
X{
X  FILE *not;
X  char notbuf[80];
X  
X  if (chdir(path) < 0) {
X    reply(550, "%s: %s.", path, sys_errlist[errno]);
X    return;
X  }
X  if ((not = fopen(".message", "r")) != NULL) {
X    while (fgets(notbuf, 80, not) != NULL) {
X      notbuf[strlen(notbuf)-1] = '\0';
X      lreply(250, notbuf);
X    }
X    fclose(not);
X  }
X  ack("CWD");
X}
X
Xmakedir(name)
X     char *name;
X{
X  struct stat st;
X  int dochown = stat(name, &st) < 0;
X  
X  if (mkdir(name, 0777) < 0) {
X    reply(550, "%s: %s.", name, sys_errlist[errno]);
X    return;
X  }
X  if (dochown)
X    (void) chown(name, pw->pw_uid, -1);
X  reply(257, "MKD command successful.");
X}
X
Xremovedir(name)
X     char *name;
X{
X  
X  if (rmdir(name) < 0) {
X    reply(550, "%s: %s.", name, sys_errlist[errno]);
X    return;
X  }
X  ack("RMD");
X}
X
Xpwd()
X{
X  char path[MAXPATHLEN + 1];
X  
X  if (getwd(path) == NULL) {
X    reply(550, "%s.", path);
X    return;
X  }
X  reply(257, "\"%s\" is current directory.", path);
X}
X
Xchar *
X  renamefrom(name)
Xchar *name;
X{
X  struct stat st;
X  
X  if (stat(name, &st) < 0) {
X    reply(550, "%s: %s.", name, sys_errlist[errno]);
X    return ((char *)0);
X  }
X  reply(350, "File exists, ready for destination name");
X  return (name);
X}
X
Xrenamecmd(from, to)
X     char *from, *to;
X{
X  
X  if (rename(from, to) < 0) {
X    reply(550, "rename: %s.", sys_errlist[errno]);
X    return;
X  }
X  ack("RNTO");
X}
X
Xdolog(sin)
X     struct sockaddr_in *sin;
X{
X  struct hostent *hp = gethostbyaddr(&sin->sin_addr,
X				     sizeof (struct in_addr), AF_INET);
X  
X  if (hp) {
X    (void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
X    endhostent();
X  } else
X    (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
X		   sizeof (remotehost));
X  
X#ifndef ALLOW_STRANGERS
X  if (isdigit(remotehost[0])) {  /** Didn't expand number to name **/
X    
X    lreply(421,"Your nameserver failed to change %s to a fully",
X	   remotehost);
X    lreply(421,"qualified hostname.  Please ask your system manager to");
X    lreply(421,"add your machine [%s] to the nameserver.",
X	   remotehost);
X    lreply(421, "Try back when this is done, or from a machine listed");
X    reply(421, "in the nameserver.");
X    
X    syslog(LOG_INFO,"FTPD: Reject %s", remotehost);
X    Removeme(); /** Just to be sure **/
X    exit(1);
X  }
X#endif
X  
X  if (!logging)
X    return;
X  syslog(LOG_INFO,"FTPD: connection from %s", remotehost);
X}
X
X/*
X * Record logout in wtmp file
X * and exit with supplied status.
X */
Xdologout(status)
X     int status;
X{
X  if (logged_in) {
X    (void) seteuid(0);
X    logwtmp(ttyline, "", "");
X  }
X  /* beware of flushing buffers after a SIGPIPE */
X  Removeme();
X  closelog();
X  _exit(status);
X}
X
X/*
X * Check user requesting login priviledges.
X * Disallow anyone who does not have a standard
X * shell returned by getusershell() (/etc/shells).
X * Disallow anyone mentioned in the file FTPUSERS
X * to allow people such as uucp to be avoided.
X */
Xcheckuser(name)
X     register char *name;
X{
X  register char *cp;
X  FILE *fd;
X  struct passwd *p;
X  char *shell;
X  int found = 0;
X  char line[BUFSIZ], *index(), *getusershell();
X  
X  if ((p = getpwnam(name)) == NULL)
X    return (0);
X  if ((shell = p->pw_shell) == NULL || *shell == 0)
X    shell = "/bin/sh";
X#ifndef ultrix
X  while ((cp = getusershell()) != NULL)
X    if (strcmp(cp, shell) == 0)
X      break;
X  endusershell();
X  if (cp == NULL)
X    return (0);
X#else
X  if (strcmp(shell, "/bin/sh") && strcmp(shell, "/bin/csh"))
X    return 0;
X#endif
X  if ((fd = fopen(FTPUSERS, "r")) == NULL)
X    return (1);
X  while (fgets(line, sizeof (line), fd) != NULL) {
X    if ((cp = index(line, '\n')) != NULL)
X      *cp = '\0';
X    if (strcmp(line, name) == 0) {
X      found++;
X      break;
X    }
X  }
X  (void) fclose(fd);
X  return (!found);
X}
X
Xmyoob()
X{
X  char *cp;
X  
X  /* only process if transfer occurring */
X  if (!transflag) {
X    return;
X  }
X  cp = tmpline;
X  if (getline(cp, 7, stdin) == NULL) {
X    reply(221, "You could at least say goodby.");
X    dologout(0);
X  }
X  upper(cp);
X  if (strcmp(cp, "ABOR\r\n"))
X    return;
X  tmpline[0] = '\0';
X  reply(426,"Transfer aborted. Data connection closed.");
X  reply(226,"Abort successful");
X  longjmp(urgcatch, 1);
X}
X
X/*
X * Note: The 530 reply codes could be 4xx codes, except nothing is
X * given in the state tables except 421 which implies an exit.  (RFC959)
X */
Xpassive()
X{
X  int len;
X  struct sockaddr_in tmp;
X  register char *p, *a;
X  
X  pdata = socket(AF_INET, SOCK_STREAM, 0);
X  if (pdata < 0) {
X    reply(530, "Can't open passive connection");
X    return;
X  }
X  tmp = ctrl_addr;
X  tmp.sin_port = 0;
X  seteuid(0);
X  if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) {
X    seteuid(pw->pw_uid);
X    (void) close(pdata);
X    pdata = -1;
X    reply(530, "Can't open passive connection");
X    return;
X  }
X  seteuid(pw->pw_uid);
X  len = sizeof(tmp);
X  if (getsockname(pdata, (char *) &tmp, &len) < 0) {
X    (void) close(pdata);
X    pdata = -1;
X    reply(530, "Can't open passive connection");
X    return;
X  }
X  if (listen(pdata, 1) < 0) {
X    (void) close(pdata);
X    pdata = -1;
X    reply(530, "Can't open passive connection");
X    return;
X  }
X  a = (char *) &tmp.sin_addr;
X  p = (char *) &tmp.sin_port;
X  
X#define UC(b) (((int) b) & 0xff)
X  
X  reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
X	UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
X}
X
Xchar *
X  gunique(local)
Xchar *local;
X{
X  static char new[MAXPATHLEN];
X  char *cp = rindex(local, '/');
X  int d, count=0;
X  char ext = '1';
X  
X  if (cp) {
X    *cp = '\0';
X  }
X  d = access(cp ? local : ".", 2);
X  if (cp) {
X    *cp = '/';
X  }
X  if (d < 0) {
X    syslog(LOG_ERR, "%s: %m", local);
X    return((char *) 0);
X  }
X  (void) strcpy(new, local);
X  cp = new + strlen(new);
X  *cp++ = '.';
X  while (!d) {
X    if (++count == 100) {
X      reply(452, "Unique file name not cannot be created.");
X      return((char *) 0);
X    }
X    *cp++ = ext;
X    *cp = '\0';
X    if (ext == '9') {
X      ext = '0';
X    }
X    else {
X      ext++;
X    }
X    if ((d = access(new, 0)) < 0) {
X      break;
X    }
X    if (ext != '0') {
X      cp--;
X    }
X    else if (*(cp - 2) == '.') {
X      *(cp - 1) = '1';
X    }
X    else {
X      *(cp - 2) = *(cp - 2) + 1;
X      cp--;
X    }
X  }
X  return(new);
X}
X
X/*
X * Check to see if the port is legal or not.
X * A port is considered legal if it is unnamed or if it is the ftp-data port.
X */
Xbadport(port)
X     int port;
X{
X  struct servent *service;
X  
X  service = getservbyport(port, "tcp");
X  
X  return (service != NULL) && (strcmp(service->s_name, "ftp-data") != 0);
X}
X#ifdef MAXANON
XLogme(id, host)
X     char *id, *host;
X{
X  FILE *logf;
X  char file[120];
X  
X  sprintf(file, "/conn/%d", getpid());
X  
X  unlink(file);
X  if ((logf = fopen(file, "w") ) == NULL) 
X    return(-1);
X  fprintf(logf, "%s [%s]\n", id, host);
X  fclose(logf);
X}
XRemoveme()
X{
X  char file[120];
X  
X  sprintf(file, "%s/%d", ANONDIR, getpid());
X  
X  unlink(file);
X}
Xnumcon() {
X  int concnt = 0;
X  DIR *condir;
X  struct dirent *ent;
X  
X  if ((condir = opendir(ANONDIRP)) == NULL)
X    return (-1);
X  
X  while ((ent = readdir(condir)) != NULL) {
X    if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) {
X      if (kill(atoi(ent->d_name), 0) == 0) {
X         concnt++; 
X      } else {
X         unlink(ent->d_name);
X      }
X    }
X  }
X  
X  closedir(condir);
X  concnt -= 2;
X  
X  return(concnt);
X}
X#endif
X#ifdef BAD_HOSTS
Xchar foo[256];
Xchar host2[128];
X
Xchar *badhost(host)
X     char *host;
X{
X  FILE *bhf;
X  char *bar;
X  
X  bar = host;
X  while (*bar != '\0') {
X    if (islower(*bar))
X      *bar = toupper(*bar);
X    ++bar;
X  }
X  
X  if ((bhf = fopen(BAD_HOSTS, "r")) == NULL) {
X    syslog(LOG_WARNING, "fopen of %s failed", BAD_HOSTS);
X    return(0);
X  }
X  
X  while (fscanf(bhf, "%s", host2) != EOF)  {
X    
X    fgets(foo, 256, bhf);
X    bar = foo;
X    foo[strlen(foo)-1] = '\0';
X    while ((*bar == ' ') || (*bar == '\t')) ++bar;
X    
X    if (!strcmp(host2, host)) {
X      fclose(bhf);
X      return(bar);
X    }
X  }
X  fclose(bhf);
X  return(NULL);
X}
X#endif
X
Xxfer_ok(file)
X     char *file;
X{
X  char path[512];
X  char *cp;
X  
X  if (!restricted_user) return (1);
X
X  strcpy(path, file);
X  
X  if ((cp = rindex(path, '/')) == NULL) 
X    {
X      if (access(".restrict", F_OK) == 0) return (0);
X       /** Not OK **/
X    }
X  
X  cp++;
X  strcpy(cp, ".restrict");
X  if (access(path, F_OK) == 0) return (0);
X  
X  return(1);
X}
END_OF_FILE
if test 26820 -ne `wc -c <'ftpd.c'`; then
    echo shar: \"'ftpd.c'\" unpacked with wrong size!
fi
# end of 'ftpd.c'
fi
if test -f 'traceroute.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'traceroute.c'\"
else
echo shar: Extracting \"'traceroute.c'\" \(22163 characters\)
sed "s/^X//" >'traceroute.c' <<'END_OF_FILE'
X#include "conf.h"
X
X#ifdef REGULATE
X#ifndef lint
Xstatic char *rcsid =
X	"@(#)$Header: traceroute.c,v 1.17 89/02/28 21:01:13 van Exp $ (LBL)";
X#endif
X
X/*
X * traceroute host  - trace the route ip packets follow going to "host".
X *
X * Attempt to trace the route an ip packet would follow to some
X * internet host.  We find out intermediate hops by launching probe
X * packets with a small ttl (time to live) then listening for an
X * icmp "time exceeded" reply from a gateway.  We start our probes
X * with a ttl of one and increase by one until we get an icmp "port
X * unreachable" (which means we got to "host") or hit a max (which
X * defaults to 30 hops & can be changed with the -m flag).  Three
X * probes (change with -q flag) are sent at each ttl setting and a
X * line is printed showing the ttl, address of the gateway and
X * round trip time of each probe.  If the probe answers come from
X * different gateways, the address of each responding system will
X * be printed.  If there is no response within a 5 sec. timeout
X * interval (changed with the -w flag), a "*" is printed for that
X * probe.
X *
X * Probe packets are UDP format.  We don't want the destination
X * host to process them so the destination port is set to an
X * unlikely value (if some clod on the destination is using that
X * value, it can be changed with the -p flag).
X *
X * A sample use might be:
X *
X *     [yak 71]% traceroute nis.nsf.net.
X *     traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
X *      1  helios.ee.lbl.gov (128.3.112.1)  19 ms  19 ms  0 ms
X *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
X *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
X *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  39 ms
X *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  39 ms  39 ms  39 ms
X *      6  128.32.197.4 (128.32.197.4)  40 ms  59 ms  59 ms
X *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  59 ms
X *      8  129.140.70.13 (129.140.70.13)  99 ms  99 ms  80 ms
X *      9  129.140.71.6 (129.140.71.6)  139 ms  239 ms  319 ms
X *     10  129.140.81.7 (129.140.81.7)  220 ms  199 ms  199 ms
X *     11  nic.merit.edu (35.1.1.48)  239 ms  239 ms  239 ms
X *
X * Note that lines 2 & 3 are the same.  This is due to a buggy
X * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
X * packets with a zero ttl.
X *
X * A more interesting example is:
X *
X *     [yak 72]% traceroute allspice.lcs.mit.edu.
X *     traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
X *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
X *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  19 ms  19 ms
X *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  19 ms
X *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  19 ms  39 ms  39 ms
X *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  20 ms  39 ms  39 ms
X *      6  128.32.197.4 (128.32.197.4)  59 ms  119 ms  39 ms
X *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  39 ms
X *      8  129.140.70.13 (129.140.70.13)  80 ms  79 ms  99 ms
X *      9  129.140.71.6 (129.140.71.6)  139 ms  139 ms  159 ms
X *     10  129.140.81.7 (129.140.81.7)  199 ms  180 ms  300 ms
X *     11  129.140.72.17 (129.140.72.17)  300 ms  239 ms  239 ms
X *     12  * * *
X *     13  128.121.54.72 (128.121.54.72)  259 ms  499 ms  279 ms
X *     14  * * *
X *     15  * * *
X *     16  * * *
X *     17  * * *
X *     18  ALLSPICE.LCS.MIT.EDU (18.26.0.115)  339 ms  279 ms  279 ms
X *
X * (I start to see why I'm having so much trouble with mail to
X * MIT.)  Note that the gateways 12, 14, 15, 16 & 17 hops away
X * either don't send ICMP "time exceeded" messages or send them
X * with a ttl too small to reach us.  14 - 17 are running the
X * MIT C Gateway code that doesn't send "time exceeded"s.  God
X * only knows what's going on with 12.
X *
X * The silent gateway 12 in the above may be the result of a bug in
X * the 4.[23]BSD network code (and its derivatives):  4.x (x <= 3)
X * sends an unreachable message using whatever ttl remains in the
X * original datagram.  Since, for gateways, the remaining ttl is
X * zero, the icmp "time exceeded" is guaranteed to not make it back
X * to us.  The behavior of this bug is slightly more interesting
X * when it appears on the destination system:
X *
X *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
X *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  39 ms
X *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  39 ms  19 ms
X *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  19 ms
X *      5  ccn-nerif35.Berkeley.EDU (128.32.168.35)  39 ms  39 ms  39 ms
X *      6  csgw.Berkeley.EDU (128.32.133.254)  39 ms  59 ms  39 ms
X *      7  * * *
X *      8  * * *
X *      9  * * *
X *     10  * * *
X *     11  * * *
X *     12  * * *
X *     13  rip.Berkeley.EDU (128.32.131.22)  59 ms !  39 ms !  39 ms !
X *
X * Notice that there are 12 "gateways" (13 is the final
X * destination) and exactly the last half of them are "missing".
X * What's really happening is that rip (a Sun-3 running Sun OS3.5)
X * is using the ttl from our arriving datagram as the ttl in its
X * icmp reply.  So, the reply will time out on the return path
X * (with no notice sent to anyone since icmp's aren't sent for
X * icmp's) until we probe with a ttl that's at least twice the path
X * length.  I.e., rip is really only 7 hops away.  A reply that
X * returns with a ttl of 1 is a clue this problem exists.
X * Traceroute prints a "!" after the time if the ttl is <= 1.
X * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
X * non-standard (HPUX) software, expect to see this problem
X * frequently and/or take care picking the target host of your
X * probes.
X *
X * Other possible annotations after the time are !H, !N, !P (got a host,
X * network or protocol unreachable, respectively), !S or !F (source
X * route failed or fragmentation needed -- neither of these should
X * ever occur and the associated gateway is busted if you see one).  If
X * almost all the probes result in some kind of unreachable, traceroute
X * will give up and return.
X *
X * Notes
X * -----
X * This program must be run by root or be setuid.  (I suggest that
X * you *don't* make it setuid -- casual use could result in a lot
X * of unnecessary traffic on our poor, congested nets.)
X *
X * This program requires a kernel mod that does not appear in any
X * system available from Berkeley:  A raw ip socket using proto
X * IPPROTO_RAW must interpret the data sent as an ip datagram (as
X * opposed to data to be wrapped in a ip datagram).  See the README
X * file that came with the source to this program for a description
X * of the mods I made to /sys/netinet/raw_ip.c.  Your mileage may
X * vary.  But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
X * MODIFIED TO RUN THIS PROGRAM.
X *
X * The udp port usage may appear bizarre (well, ok, it is bizarre).
X * The problem is that an icmp message only contains 8 bytes of
X * data from the original datagram.  8 bytes is the size of a udp
X * header so, if we want to associate replies with the original
X * datagram, the necessary information must be encoded into the
X * udp header (the ip id could be used but there's no way to
X * interlock with the kernel's assignment of ip id's and, anyway,
X * it would have taken a lot more kernel hacking to allow this
X * code to set the ip id).  So, to allow two or more users to
X * use traceroute simultaneously, we use this task's pid as the
X * source port (the high bit is set to move the port number out
X * of the "likely" range).  To keep track of which probe is being
X * replied to (so times and/or hop counts don't get confused by a
X * reply that was delayed in transit), we increment the destination
X * port number before each probe.
X *
X * Don't use this as a coding example.  I was trying to find a
X * routing problem and this code sort-of popped out after 48 hours
X * without sleep.  I was amazed it ever compiled, much less ran.
X *
X * I stole the idea for this program from Steve Deering.  Since
X * the first release, I've learned that had I attended the right
X * IETF working group meetings, I also could have stolen it from Guy
X * Almes or Matt Mathis.  I don't know (or care) who came up with
X * the idea first.  I envy the originators' perspicacity and I'm
X * glad they didn't keep the idea a secret.
X *
X * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
X * enhancements to the original distribution.
X *
X * I've hacked up a round-trip-route version of this that works by
X * sending a loose-source-routed udp datagram through the destination
X * back to yourself.  Unfortunately, SO many gateways botch source
X * routing, the thing is almost worthless.  Maybe one day...
X *
X *  -- Van Jacobson (van@helios.ee.lbl.gov)
X *     Tue Dec 20 03:50:13 PST 1988
X *
X * Copyright (c) 1988 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#include <stdio.h>
X#include <errno.h>
X#include <strings.h>
X#include <sys/time.h>
X
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <sys/file.h>
X#include <sys/ioctl.h>
X
X#include <netinet/in_systm.h>
X#include <netinet/in.h>
X#include <netinet/ip.h>
X#include <netinet/ip_var.h>
X#include <netinet/ip_icmp.h>
X#include <netinet/udp.h>
X#include <netdb.h>
X#include <ctype.h>
X
X#define	MAXPACKET	65535	/* max ip packet size */
X#ifndef MAXHOSTNAMELEN
X#define MAXHOSTNAMELEN	64
X#endif
X
X#ifndef FD_SET
X#define NFDBITS         (8*sizeof(fd_set))
X#define FD_SETSIZE      NFDBITS
X#define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
X#define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
X#define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
X#define FD_ZERO(p)      bzero((char *)(p), sizeof(*(p)))
X#endif
X
X#define Fprintf (void)fprintf
X#define Sprintf (void)sprintf
X#define Printf (void)printf
X
Xextern	int errno;
Xextern  char *malloc();
Xextern  char *inet_ntoa();
Xextern  u_long inet_addr();
X
X/*
X * format of a (udp) probe packet.
X */
Xstruct opacket {
X  struct ip ip;
X  struct udphdr udp;
X  u_char seq;		/* sequence number of this packet */
X  u_char ttl;		/* ttl packet left with */
X  struct timeval tv;	/* time packet left */
X};
X
Xu_char	packet[512];		/* last inbound (icmp) packet */
Xstruct opacket	*outpacket;	/* last output (udp) packet */
Xchar *inetname();
X
Xint s;				/* receive (icmp) socket file descriptor */
Xint sndsock;			/* send (udp) socket file descriptor */
Xstruct timezone tz;		/* leftover */
X
Xstruct sockaddr whereto;	/* Who to try to reach */
Xint datalen;			/* How much data */
X
Xchar *source = 0;
Xchar *hostname;
Xchar hnamebuf[MAXHOSTNAMELEN];
X
Xint nprobes = 3;
Xint max_ttl = 30;
Xu_short ident;
Xu_short port = 32768+666;	/* start udp dest port # for probe packets */
X
Xint options;			/* socket options */
Xint verbose;
Xint waittime = 5;		/* time to wait for response (in seconds) */
Xint nflag;			/* print addresses numerically */
Xchar Target[64];
X
X
XNetInRouteTo(network, host)
X     char *network, *host;
X{
X  struct sockaddr_in from;
X  struct sockaddr_in *to = (struct sockaddr_in *) &whereto;
X  int on = 1;
X  struct protoent *pe;
X  int ttl, probe, i;
X  int seq = 0;
X  int tos = 0;
X  struct hostent *hp;
X  int lsrr = 0;
X  u_long gw;
X  u_char optlist[MAX_IPOPTLEN], *oix;
X
X  strcpy(Target, network);
X  oix = optlist;
X  bzero(optlist, sizeof(optlist));
X  
X  (void) bzero((char *)&whereto, sizeof(struct sockaddr));
X  to->sin_family = AF_INET;
X  to->sin_addr.s_addr = inet_addr(host);
X  if (to->sin_addr.s_addr != -1) {
X    (void) strcpy(hnamebuf, host);
X    hostname = hnamebuf;
X  } else {
X    hp = gethostbyname(host);
X    if (hp) {
X      to->sin_family = hp->h_addrtype;
X      bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
X      hostname = hp->h_name;
X    } else {
X      return(-1);     /** Unknown host **/
X    }
X  }
X  
X  if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket)) {
X    /*    Fprintf(stderr, "traceroute: packet size must be 0 <= s < %ld\n",
X	  MAXPACKET - sizeof(struct opacket)); */
X    return(-2);
X    
X  }
X  datalen += sizeof(struct opacket);
X  outpacket = (struct opacket *)malloc((unsigned)datalen);
X  if (! outpacket) {
X    /*    perror("traceroute: malloc"); */
X    return(-3);
X  }
X  (void) bzero((char *)outpacket, datalen);
X  outpacket->ip.ip_dst = to->sin_addr;
X  outpacket->ip.ip_tos = tos;
X  
X  ident = (getpid() & 0xffff) | 0x8000;
X  
X  if ((pe = getprotobyname("icmp")) == NULL) {
X    /*    Fprintf(stderr, "icmp: unknown protocol\n"); */
X    return(-4);
X  }
X  if ((s = socket(AF_INET, SOCK_RAW, pe->p_proto)) < 0) {
X    /*    perror("traceroute: icmp socket"); */
X    return(-5);
X  }
X  if (options & SO_DEBUG)
X    (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
X		      (char *)&on, sizeof(on));
X  if (options & SO_DONTROUTE)
X    (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
X		      (char *)&on, sizeof(on));
X  
X  if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
X    /*    perror("traceroute: raw socket"); */
X    return(-6);
X  }
X  
X  if (lsrr > 0) {
X    lsrr++;
X    optlist[IPOPT_OLEN]=IPOPT_MINOFF-1+(lsrr*sizeof(u_long));
X    bcopy((caddr_t)&to->sin_addr, oix, sizeof(u_long));
X    oix += sizeof(u_long);
X    while ((oix - optlist)&3) oix++;		/* Pad to an even boundry */
X    
X    if ((pe = getprotobyname("ip")) == NULL) {
X      /* perror("traceroute: unknown protocol ip\n"); */
X      return(-7);
X    }
X    if ((setsockopt(sndsock, pe->p_proto, IP_OPTIONS, optlist, oix-optlist)) < 0) {
X      /*  perror("traceroute: lsrr options"); */
X      return(-8);
X    }
X  }
X  
X#ifdef SO_SNDBUF
X  if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
X		 sizeof(datalen)) < 0) {
X    /*    perror("traceroute: SO_SNDBUF"); */
X    return(-9);
X  }
X#endif SO_SNDBUF
X#ifdef IP_HDRINCL
X  if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
X		 sizeof(on)) < 0) {
X    /* perror("traceroute: IP_HDRINCL");*/
X    return(-10);
X  }
X#endif IP_HDRINCL
X  if (options & SO_DEBUG)
X    (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
X		      (char *)&on, sizeof(on));
X  if (options & SO_DONTROUTE)
X    (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
X		      (char *)&on, sizeof(on));
X  
X  if (source) {
X    (void) bzero((char *)&from, sizeof(struct sockaddr));
X    from.sin_family = AF_INET;
X    from.sin_addr.s_addr = inet_addr(source);
X    if (from.sin_addr.s_addr == -1) {
X      /* Printf("traceroute: unknown host %s\n", source); */
X      return(-1);
X    }
X    outpacket->ip.ip_src = from.sin_addr;
X#ifndef IP_HDRINCL
X    if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0) {
X      perror ("traceroute: bind:");
X      return (1);
X    }
X#endif IP_HDRINCL
X  }
X  
X  for (ttl = 1; ttl <= max_ttl; ++ttl) {
X    u_long lastaddr = 0;
X    int got_there = 0;
X    int unreachable = 0;
X    
X    for (probe = 0; probe < nprobes; ++probe) {
X      int cc;
X      struct timeval tv;
X      struct ip *ip;
X      
X      (void) gettimeofday(&tv, &tz);
X      send_probe(++seq, ttl);
X      while (cc = wait_for_reply(s, &from)) {
X	if ((i = packet_ok(packet, cc, &from, seq))) {
X	  int dt = deltaT(&tv);
X	  if (from.sin_addr.s_addr != lastaddr) {
X	    if (print(packet, cc, &from)) return(1);
X	    lastaddr = from.sin_addr.s_addr;
X	  }
X
X	  switch(i - 1) {
X	  case ICMP_UNREACH_PORT:
X#ifndef ARCHAIC
X	    ip = (struct ip *)packet;
X#endif ARCHAIC
X	    ++got_there;
X	    break;
X	  case ICMP_UNREACH_NET:
X	    ++unreachable;
X	    break;
X	  case ICMP_UNREACH_HOST:
X	    ++unreachable;
X	    break;
X	  case ICMP_UNREACH_PROTOCOL:
X	    ++got_there;
X	    break;
X	  case ICMP_UNREACH_NEEDFRAG:
X	    ++unreachable;
X	    break;
X	  case ICMP_UNREACH_SRCFAIL:
X	    ++unreachable;
X	    break;
X	  }
X	  break;
X	}
X      }
X      if (cc == 0)
X	return(0);
X      
X    /**  if (cc == 0)
X     **    Printf(" *");
X     **/
X    }
X    if (got_there || unreachable >= nprobes-1)
X      return(0);
X  }
X}
X
Xwait_for_reply(sock, from)
X     int sock;
X     struct sockaddr_in *from;
X{
X  fd_set fds;
X  struct timeval wait;
X  int cc = 0;
X  int fromlen = sizeof (*from);
X  
X  FD_ZERO(&fds);
X  FD_SET(sock, &fds);
X  wait.tv_sec = waittime; wait.tv_usec = 0;
X  
X  if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
X    cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
X		(struct sockaddr *)from, &fromlen);
X  
X  return(cc);
X}
X
X
Xsend_probe(seq, ttl)
X{
X  struct opacket *op = outpacket;
X  struct ip *ip = &op->ip;
X  struct udphdr *up = &op->udp;
X  int i;
X  
X  ip->ip_off = 0;
X  ip->ip_p = IPPROTO_UDP;
X  ip->ip_len = datalen;
X  ip->ip_ttl = ttl;
X  
X  up->uh_sport = htons(ident);
X  up->uh_dport = htons(port+seq);
X  up->uh_ulen = htons((u_short)(datalen - sizeof(struct ip)));
X  up->uh_sum = 0;
X  
X  op->seq = seq;
X  op->ttl = ttl;
X  (void) gettimeofday(&op->tv, &tz);
X  
X  i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
X	     sizeof(struct sockaddr));
X  if (i < 0 || i != datalen)  {
X    if (i<0)
X      perror("sendto");
X    Printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
X	   datalen, i);
X    (void) fflush(stdout);
X  }
X}
X
X
XdeltaT(tp)
X     struct timeval *tp;
X{
X  struct timeval tv;
X  
X  (void) gettimeofday(&tv, &tz);
X  tvsub(&tv, tp);
X  return (tv.tv_sec*1000 + (tv.tv_usec + 500)/1000);
X}
X
X
X/*
X * Convert an ICMP "type" field to a printable string.
X */
Xchar *
X  pr_type(t)
Xu_char t;
X{
X  static char *ttab[] = {
X    "Echo Reply",	"ICMP 1",	"ICMP 2",	"Dest Unreachable",
X    "Source Quench", "Redirect",	"ICMP 6",	"ICMP 7",
X    "Echo",		"ICMP 9",	"ICMP 10",	"Time Exceeded",
X    "Param Problem", "Timestamp",	"Timestamp Reply", "Info Request",
X    "Info Reply"
X    };
X  
X  if(t > 16)
X    return("OUT-OF-RANGE");
X  
X  return(ttab[t]);
X}
X
X
Xpacket_ok(buf, cc, from, seq)
X     u_char *buf;
X     int cc;
X     struct sockaddr_in *from;
X     int seq;
X{
X  register struct icmp *icp;
X  u_char type, code;
X  int hlen;
X#ifndef ARCHAIC
X  struct ip *ip;
X  
X  ip = (struct ip *) buf;
X  hlen = ip->ip_hl << 2;
X  if (cc < hlen + ICMP_MINLEN) {
X    if (verbose)
X      Printf("packet too short (%d bytes) from %s\n", cc,
X	     inet_ntoa(from->sin_addr));
X    return (0);
X  }
X  cc -= hlen;
X  icp = (struct icmp *)(buf + hlen);
X#else
X  icp = (struct icmp *)buf;
X#endif ARCHAIC
X  type = icp->icmp_type; code = icp->icmp_code;
X  if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
X      type == ICMP_UNREACH) {
X    struct ip *hip;
X    struct udphdr *up;
X    
X    hip = &icp->icmp_ip;
X    hlen = hip->ip_hl << 2;
X    up = (struct udphdr *)((u_char *)hip + hlen);
X    if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
X	up->uh_sport == htons(ident) &&
X	up->uh_dport == htons(port+seq))
X      return (type == ICMP_TIMXCEED? -1 : code+1);
X  }
X#ifndef ARCHAIC
X  if (verbose) {
X    int i;
X    u_long *lp = (u_long *)&icp->icmp_ip;
X    
X    Printf("\n%d bytes from %s to %s", cc,
X	   inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst));
X    Printf(": icmp type %d (%s) code %d\n", type, pr_type(type),
X	   icp->icmp_code);
X    for (i = 4; i < cc ; i += sizeof(long))
X      Printf("%2d: x%8.8lx\n", i, *lp++);
X  }
X#endif ARCHAIC
X  return(0);
X}
X
X
Xprint(buf, cc, from)
X     u_char *buf;
X     int cc;
X     struct sockaddr_in *from;
X{
X  struct ip *ip;
X  char IPADDR[64];
X  
X  int hlen;
X  
X  ip = (struct ip *) buf;
X  hlen = ip->ip_hl << 2;
X  cc -= hlen;
X
X  sprintf(IPADDR, "%s", inet_ntoa(from->sin_addr));
X  if ( !strncmp(Target, IPADDR, strlen(Target)) ) return (1);
X    
X  return(0);
X  
X}
X
X
X#ifdef notyet
X/*
X * Checksum routine for Internet Protocol family headers (C Version)
X */
Xin_cksum(addr, len)
X     u_short *addr;
X     int len;
X{
X  register int nleft = len;
X  register u_short *w = addr;
X  register u_short answer;
X  register int sum = 0;
X  
X  /*
X   *  Our algorithm is simple, using a 32 bit accumulator (sum),
X   *  we add sequential 16 bit words to it, and at the end, fold
X   *  back all the carry bits from the top 16 bits into the lower
X   *  16 bits.
X   */
X  while (nleft > 1)  {
X    sum += *w++;
X    nleft -= 2;
X  }
X  
X  /* mop up an odd byte, if necessary */
X  if (nleft == 1)
X    sum += *(u_char *)w;
X  
X  /*
X   * add back carry outs from top 16 bits to low 16 bits
X   */
X  sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
X  sum += (sum >> 16);			/* add carry */
X  answer = ~sum;				/* truncate to 16 bits */
X  return (answer);
X}
X#endif notyet
X
X/*
X * Subtract 2 timeval structs:  out = out - in.
X * Out is assumed to be >= in.
X */
Xtvsub(out, in)
X     register struct timeval *out, *in;
X{
X  if ((out->tv_usec -= in->tv_usec) < 0)   {
X    out->tv_sec--;
X    out->tv_usec += 1000000;
X  }
X  out->tv_sec -= in->tv_sec;
X}
X
X
X/*
X * Construct an Internet address representation.
X * If the nflag has been supplied, give 
X * numeric value, otherwise try for symbolic name.
X */
Xchar *
X  inetname(in)
Xstruct in_addr in;
X{
X  register char *cp;
X  static char line[50];
X  struct hostent *hp;
X  static char domain[MAXHOSTNAMELEN + 1];
X  static int first = 1;
X  
X  if (first && !nflag) {
X    first = 0;
X    if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
X	(cp = index(domain, '.')))
X      (void) strcpy(domain, cp + 1);
X    else
X      domain[0] = 0;
X  }
X  cp = 0;
X  if (!nflag && in.s_addr != INADDR_ANY) {
X    hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
X    if (hp) {
X      if ((cp = index(hp->h_name, '.')) &&
X	  !strcmp(cp + 1, domain))
X	*cp = 0;
X      cp = hp->h_name;
X    }
X  }
X  if (cp)
X    (void) strcpy(line, cp);
X  else {
X    in.s_addr = ntohl(in.s_addr);
X#define C(x)	((x) & 0xff)
X    Sprintf(line, "%lu.%lu.%lu.%lu", C(in.s_addr >> 24),
X	    C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
X  }
X  return (line);
X}
X#endif
END_OF_FILE
if test 22163 -ne `wc -c <'traceroute.c'`; then
    echo shar: \"'traceroute.c'\" unpacked with wrong size!
fi
# end of 'traceroute.c'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

