
/*
 * util.c
 *
 * Written by Archie Cobbs <archie@freebsd.org>
 * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
 * See ``COPYRIGHT.whistle''
 */

#include "ppp.h"
#include <termios.h>

#include <netdb.h>
#include <tcpd.h>

/*
 * DEFINITIONS
 */

  #define MAX_FILENAME		50
  #define MAX_LINE_ARGS		50
  #define BIG_LINE_SIZE		1000
  #define MAX_OPEN_DELAY	2
  #define MAX_LOCK_ATTEMPTS	30

/*
 * INTERNAL VARIABLES
 */

  static const u_int16_t	Crc16Table[];
  static FILE			*lockFp = NULL;

/*
 * INTERNAL FUNCTIONS
 */

  static int		UuLock(const char *devname);
  static int		UuUnlock(const char *devname);

  static void		Escape(char *line);
  static char		*ReadLine(FILE *fp, int *lineNum);

/*
 * LengthenArray()
 */

void
LengthenArray(void *array, int esize, int *alenp, int type)
{
  void **const arrayp = (void **)array;
  void *newa;

  newa = Malloc(type, (*alenp + 1) * esize);
  if (*arrayp != NULL) {
    memcpy(newa, *arrayp, *alenp * esize);
    Freee(*arrayp);
  }
  *arrayp = newa;
  (*alenp)++;
}

/*
 * ExecCmd()
 */

int
ExecCmd(int log, const char *fmt, ...)
{
  int		rtn;
  char		cmd[BIG_LINE_SIZE];
  va_list	ap;

  va_start(ap, fmt);
  vsnprintf(cmd, sizeof(cmd), fmt, ap);

/* Log command on the console */

  Log(log, ("[%s] exec: %s", bund->name, cmd));

/* Hide any stdout output of command */

  snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), " >/dev/null 2>&1");

/* Do command */

  if ((rtn = system(cmd)))
    Log(log, ("[%s] exec: command returned %d", bund->name, rtn));

/* Return command's return value */

  va_end(ap);
  return(rtn);
}

/*
 * ParseAddr()
 *
 * Parse an IP address & mask of the form X.Y.Z.W/M.
 * If no slash, then M is assumed to be 32.
 * Returns TRUE if successful
 */

int
ParseAddr(char *s, struct in_range *r)
{
  int			n_bits;
  char			*widp, buf[100];
  struct in_addr	address;

  snprintf(buf, sizeof(buf), "%s", s);
  if ((widp = strchr(buf, '/')) != NULL)
    *widp++ = '\0';
  else
    widp = buf + strlen(buf);

/* Get IP address part; if that fails, try looking up hostname */

  if (!inet_aton(buf, &address))
  {
    struct hostent	*hptr;
    int			k;

    if ((hptr = gethostbyname(buf)) == NULL)
      return (FALSE);
    for (k = 0; hptr->h_addr_list[k]; k++);
    memcpy(&address, hptr->h_addr_list[random() % k], sizeof(address));
  }

/* Get mask width */

  if (*widp)
  {
    if ((n_bits = atoi(widp)) < 0 || n_bits > 32)
    {
      Log(LG_ERR, ("mpd: bad IP mask width: \"%s\"", widp));
      return(FALSE);
    }
  }
  else
    n_bits = 32;

/* Done */

  r->ipaddr = address;
  r->width = n_bits;
  return(TRUE);
}

/*
 * ParseLine()
 *
 * Parse arguments, respecting double quotes and backslash escapes.
 * Returns number of arguments, at most "max_args". This destroys
 * the original line. The arguments returned are Malloc()'d strings
 * which must be freed by the caller using FreeArgs().
 */

int
ParseLine(char *line, char *av[], int max_args)
{
  int	ac;
  char	*s, *arg;

/* Get args one at a time */

  for (ac = 0; ac < max_args; ac++)
  {

  /* Skip white space */

    while (*line && isspace(*line))
      line++;

  /* Done? */

    if (*line == 0)
      break;

  /* Get normal or quoted arg */

    if (*line == '"')
    {

    /* Stop only upon matching quote or NUL */

      for (arg = ++line; *line; line++)
	if (*line == '"')
	{
	  *line++ = 0;
	  break;
	}
	else if (*line == '\\' && line[1] != 0)
	{
	  strcpy(line, line + 1);
	  Escape(line);
	}
    }
    else
    {

    /* NUL terminate this argument at first white space */

      for (arg = line; *line && !isspace(*line); line++);
      if (*line)
	*line++ = 0;

    /* Convert characters */

      for (s = arg; *s; s++)
	if (*s == '\\')
	{
	  strcpy(s, s + 1);
	  Escape(s);
	}
    }

  /* Make a copy of this arg */

    strcpy(av[ac] = Malloc(MB_UTIL, strlen(arg) + 1), arg);
  }

#if 0
  {
    int	k;

    printf("ParseLine: %d args:\n", ac);
    for (k = 0; k < ac; k++)
      printf("  [%2d] \"%s\"\n", k, av[k]);
  }
#endif

  return(ac);
}

/*
 * FreeArgs()
 */

void
FreeArgs(int ac, char *av[])
{
  while (ac > 0)
    Freee(av[--ac]);
}

/*
 * Escape()
 *
 * Give a string, interpret the beginning characters as an escape
 * code and return with that code converted.
 */

static void
Escape(char *line)
{
  int	x, k;
  char	*s = line;

  switch (*line)
  {
    case 't': *s = '\t'; return;
    case 'n': *s = '\n'; return;
    case 'r': *s = '\r'; return;
    case 's': *s =  ' '; return;
    case '"': *s =  '"'; return;
    case '0': case '1': case '2': case '3':
    case '4': case '5': case '6': case '7':
      for (x = k = 0; k < 3 && *s >= '0' && *s <= '7'; s++)
	x = (x << 3) + (*s - '0');
      *--s = x;
      break;
    case 'x':
      for (s++, x = k = 0; k < 2 && isxdigit(*s); s++)
	x = (x << 4) + (isdigit(*s) ? (*s - '0') : (tolower(*s) - 'a' + 10));
      *--s = x;
      break;
    default:
      return;
  }
  strcpy(line, s);
}

/*
 * ReadFile()
 *
 * Read the commands specified for the target in the specified
 * file, which can be found in the PATH_CONF_DIR directory.
 * Returns negative if the file or target was not found.
 */

int
ReadFile(const char *filename, const char *target,
	int (*func)(int ac, char *av[]))
{
  FILE	*fp;
  int	ac;
  char	*av[MAX_LINE_ARGS], *av_copy[MAX_LINE_ARGS];
  char	*line;

/* Open file */

  if ((fp = OpenConfFile(filename)) == NULL)
    return(-1);

/* Find label */

  if (SeekToLabel(fp, target, NULL) < 0) {
    fclose(fp);
    return(-1);
  }

/* Execute command list */

  while ((line = ReadFullLine(fp, NULL)) != NULL)
  {
    if (!isspace(*line))
    {
      Freee(line);
      break;
    }
    ac = ParseLine(line, av, sizeof(av) / sizeof(*av));
    Freee(line);
    memcpy(av_copy, av, sizeof(av));
    (*func)(ac, av);
    FreeArgs(ac, av_copy);
  }

/* Done */

  fclose(fp);
  return(0);
}

/*
 * SeekToLabel()
 *
 * Find a label in file and position file pointer just after it
 */

int
SeekToLabel(FILE *fp, const char *label, int *lineNum)
{
  char	*s, *line;

/* Start at beginning */

  rewind(fp);
  if (lineNum)
    *lineNum = 0;

/* Find label */

  while ((line = ReadFullLine(fp, lineNum)) != NULL)
  {
    int	found;

    if (isspace(*line))
    {
      Freee(line);
      continue;
    }
    found = (s = strtok(line, " \t\f:")) && !strcmp(s, label);
    Freee(line);
    if (found)
      return(0);
  }

/* Not found */

  return(-1);
}

/*
 * OpenConfFile()
 *
 * Open a configuration file
 */

FILE *
OpenConfFile(const char *name)
{
  char	pathname[MAX_FILENAME];
  FILE	*fp;

/* Build full pathname */

  snprintf(pathname, sizeof(pathname), "%s/%s", gConfDirectory, name);

/* Open file */

  if ((fp = fopen(pathname, "r")) == NULL)
  {
    Perror("fopen(%s)", pathname);
    Log(LG_ERR, ("mpd: can't open file \"%s\"", pathname));
    return(NULL);
  }
  (void) fcntl(fileno(fp), F_SETFD, 1);
  return(fp);
}

/*
 * ReadFullLine()
 *
 * Read a full line, respecting backslash continuations.
 * Returns pointer to Malloc'd storage, which must be Freee'd
 */

char *
ReadFullLine(FILE *fp, int *lineNum)
{
  int		len, continuation;
  char		*real_line;
  static char	line[BIG_LINE_SIZE];

  for (*line = 0, continuation = TRUE; continuation; )
  {

  /* Get next real line */

    if ((real_line = ReadLine(fp, lineNum)) == NULL) {
      if (*line)
	break;
      else
	return(NULL);
    }

  /* Strip trailing white space, detect backslash */

    for (len = strlen(real_line);
	len > 0 && isspace(real_line[len - 1]);
	len--)
      real_line[len - 1] = 0;
    if ((continuation = (*real_line && real_line[len - 1] == '\\')))
      real_line[len - 1] = ' ';

  /* Append real line to what we've got so far */

    snprintf(line + strlen(line),
      sizeof(line) - strlen(line), "%s", real_line);
  }

/* Report any overflow */

  if (strlen(line) >= sizeof(line) - 1)
    Log(LG_ERR, ("mpd: warning: line too long, truncated"));

/* Copy line and return */

  return(strcpy(Malloc(MB_UTIL, strlen(line) + 1), line));
}

/*
 * ReadLine()
 *
 * Read a line, skipping blank lines & comments. A comment
 * is a line whose first non-white-space character is a hash.
 */

static char *
ReadLine(FILE *fp, int *lineNum)
{
  int		empty;
  char		ch, *s;
  static char	line[BIG_LINE_SIZE];

/* Get first non-empty, non-commented line */

  for (empty = TRUE; empty; )
  {

  /* Read next line from file */

    if ((fgets(line, sizeof(line), fp)) == NULL)
      return(NULL);
    if (lineNum)
      (*lineNum)++;

  /* Truncate long lines */

    if (line[strlen(line) - 1] == '\n')
      line[strlen(line) - 1] = 0;
    else
    {
      Log(LG_ERR, ("mpd: warning: line too long, truncated"));
      while ((ch = getc(fp)) != EOF && ch != '\n');
    }

  /* Ignore comments */

    s = line + strspn(line, " \t");
    if (*s == '#')
      *s = 0;

  /* Is this line empty? */

    for (empty = TRUE, s = line; *s; s++)
      if (!isspace(*s))
      {
	empty = FALSE;
	break;
      }
  }

/* Done */

  return(line);
}

/*
 * OpenSerialDevice()
 *
 * Open and configure a serial device. Call ExclusiveCloseDevice()
 * to close a file descriptor returned by this function.
 */

int
OpenSerialDevice(const char *path, int baudrate)
{
  struct termios	attr;
  int			fd;

/* Open & lock serial port */

  if ((fd = ExclusiveOpenDevice(path)) < 0)
    return(-1);

/* Set non-blocking I/O */

  if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
  {
    Log(LG_PHYS, ("[%s] can't set \"%s\" to non-blocking: %s",
      lnk->name, path, strerror(errno)));
    goto failed;
  }

/* Set serial port raw mode, baud rate, hardware flow control, etc. */

  if (tcgetattr(fd, &attr) < 0)
  {
    Log(LG_ERR, ("[%s] can't tcgetattr \"%s\": %s",
      lnk->name, path, strerror(errno)));
    goto failed;
  }

  cfmakeraw(&attr);

  attr.c_cflag &= ~(CSIZE|PARENB|PARODD);
  attr.c_cflag |= (CS8|CREAD|CLOCAL|HUPCL|CCTS_OFLOW|CRTS_IFLOW);
  attr.c_iflag &= ~(IXANY|IMAXBEL|ISTRIP|IXON|IXOFF|BRKINT|ICRNL|INLCR);
  attr.c_iflag |= (IGNBRK|IGNPAR);
  attr.c_oflag &= ~OPOST;
  attr.c_lflag = 0;

  cfsetspeed(&attr, (speed_t) baudrate);

  if (tcsetattr(fd, TCSANOW, &attr) < 0)
  {
    Log(LG_ERR, ("[%s] can't tcsetattr \"%s\": %s",
      lnk->name, path, strerror(errno)));
failed:
    ExclusiveCloseDevice(fd, path);
    return(-1);
  }

/* OK */

  return(fd);
}

/*
 * ExclusiveOpenDevice()
 */

int
ExclusiveOpenDevice(const char *pathname)
{
  int		fd, locked = FALSE;
  const char	*ttyname = NULL;
  time_t	startTime;

/* Lock device UUCP style, if it resides in /dev */

  if (!strncmp(pathname, "/dev/", 5))
  {
    ttyname = pathname + 5;
    if (UuLock(ttyname) < 0)
    {
      Log(LG_ERR, ("[%s] can't lock device %s", lnk->name, ttyname));
      return(-1);
    }
    locked = TRUE;
  }

/* Open it, but give up after so many interruptions */

  for (startTime = time(NULL);
      (fd = open(pathname, O_RDWR, 0)) < 0
      && time(NULL) < startTime + MAX_OPEN_DELAY; )
    if (errno != EINTR)
    {
      Log(LG_ERR, ("[%s] can't open %s: %s",
	lnk->name, pathname, strerror(errno)));
      if (locked)
	UuUnlock(ttyname);
      return(-1);
    }

/* Did we succeed? */

  if (fd < 0)
  {
    Log(LG_ERR, ("[%s] can't open %s after %d secs",
      lnk->name, pathname, MAX_OPEN_DELAY));
    if (locked)
      UuUnlock(ttyname);
    return(-1);
  }
  (void) fcntl(fd, F_SETFD, 1);

/* Done */

  return(fd);
}

/*
 * ExclusiveCloseDevice()
 */

void
ExclusiveCloseDevice(int fd, const char *pathname)
{
  int		rtn = -1;
  const char	*ttyname;
  time_t	startTime;

/* Close file(s) */

  for (startTime = time(NULL);
      time(NULL) < startTime + MAX_OPEN_DELAY && (rtn = close(fd)) < 0; )
    if (errno != EINTR)
    {
      Log(LG_ERR, ("[%s] can't close %s: %s",
	lnk->name, pathname, strerror(errno)));
      DoExit(EX_ERRDEAD);
    }

/* Did we succeed? */

  if (rtn < 0)
  {
    Log(LG_ERR, ("[%s] can't close %s after %d secs",
      lnk->name, pathname, MAX_OPEN_DELAY));
    DoExit(EX_ERRDEAD);
  }

/* Remove lock */

  if (!strncmp(pathname, "/dev/", 5))
  {
    ttyname = pathname + 5;
    if (UuUnlock(ttyname) < 0)
      Log(LG_ERR, ("[%s] can't unlock %s: %s",
	lnk->name, ttyname, strerror(errno)));
  }
}

/*
 * UuLock()
 *
 * Try to atomically create lockfile. Returns negative if failed.
 */

static int
UuLock(const char *ttyname)
{
  int	fd, pid;
  char	tbuf[sizeof(PATH_LOCKFILENAME) + MAX_FILENAME];
  char	pid_buf[64];

  snprintf(tbuf, sizeof(tbuf), PATH_LOCKFILENAME, ttyname);
  if ((fd = open(tbuf, O_RDWR|O_CREAT|O_EXCL, 0664)) < 0)
  {

  /* File is already locked; Check to see if the process
   * holding the lock still exists */

    if ((fd = open(tbuf, O_RDWR, 0)) < 0)
    {
      Perror("UuLock: open(%s)", tbuf);
      return(-1);
    }

    if (read(fd, pid_buf, sizeof(pid_buf)) <= 0)
    {
      (void)close(fd);
      Perror("UuLock: read");
      return(-1);
    }

    pid = atoi(pid_buf);

    if (kill(pid, 0) == 0 || errno != ESRCH)
    {
      (void)close(fd);  /* process is still running */
      return(-1);
    }

  /* The process that locked the file isn't running, so we'll lock it */

    if (lseek(fd, (off_t) 0, L_SET) < 0)
    {
      (void)close(fd);
      Perror("UuLock: lseek");
      return(-1);
    }
  }

/* Finish the locking process */

  sprintf(pid_buf, "%10u\n", (int) getpid());
  if (write(fd, pid_buf, strlen(pid_buf)) != strlen(pid_buf))
  {
    (void)close(fd);
    (void)unlink(tbuf);
    Perror("UuLock: write");
    return(-1);
  }
  (void)close(fd);
  return(0);
}

/*
 * UuUnlock()
 */

static int
UuUnlock(const char *ttyname)
{
  char	tbuf[sizeof(PATH_LOCKFILENAME) + MAX_FILENAME];

  (void) sprintf(tbuf, PATH_LOCKFILENAME, ttyname);
  return(unlink(tbuf));
}

/*
 * WriteMbuf()
 *
 * Write an mbuf to a file descriptor which is character based.
 * Leave whatever portion of the mbuf is remaining.
 */

int
WriteMbuf(Mbuf *mp, int fd, const char *label)
{
  while (*mp)
  {
    Mbuf	const bp = *mp;
    int		nw;

    if ((nw = write(fd, MBDATA(bp), MBLEN(bp))) < 0)
    {
      if (errno == EAGAIN)
	return(0);
      Log(LG_ERR, ("[%s] %s write: %s", lnk->name, label, strerror(errno)));
      return(-1);
    }
    bp->offset += nw;
    bp->cnt -= nw;
    if (bp->cnt != 0)
      break;
    *mp = mbfree(bp);
  }
  return(0);
}

/*
 * GenerateMagic()
 *
 * Generate random number which will be used as magic number.
 * This could be made a little more "random"...
 */

u_long
GenerateMagic(void)
{
  time_t		now;
  struct timeval	tval;

  time(&now);
  gettimeofday(&tval, NULL);
  now += (tval.tv_sec ^ tval.tv_usec) + getppid();
  now *= getpid();
  return(now);
}

/*
 * PIDCheck()
 *
 * See if process is already running and deal with PID file.
 */

int
PIDCheck(const char *filename, int killem)
{
  int	fd = -1, n_tries;

/* Sanity */

  assert(!lockFp);

/* Atomically open and lock file */

  for (n_tries = 0;
    n_tries < MAX_LOCK_ATTEMPTS
      && (fd = open(filename, O_RDWR|O_CREAT|O_EXLOCK|O_NONBLOCK, 0644)) < 0;
    n_tries++)
  {
    int		nscan, old_pid;
    FILE	*fp;

  /* Abort on any unexpected errors */

    if (errno != EAGAIN)
    {
      Perror("open(%s)", filename);
      return(-1);
    }

  /* We're already running ... see who it is */

    if ((fp = fopen(filename, "r")) == NULL)
    {
      Perror("fopen(%s)", filename);
      return(-1);
    }

  /* If there's a PID in there, sniff it out */

    nscan = fscanf(fp, "%d", &old_pid);
    fclose(fp);
    if (nscan != 1)
    {
      Log(LG_ERR, ("%s: contents mangled", filename));
      return(-1);
    }

  /* Maybe kill the other guy */

    if (!killem)
    {
      Log(LG_ERR, ("mpd: already running as process %d", old_pid));
      return(-1);
    }
    if (kill(old_pid, SIGTERM) < 0)
      switch (errno)
      {
	case ESRCH:
	  Log(LG_ERR, ("mpd: process %d no longer exists", old_pid));
	  break;
	default:
	  Perror("kill(%d)", old_pid);
	  return(-1);
      }

  /* Wait and try again */

    Log(LG_ERR, ("mpd: waiting for process %d to die...", old_pid));
    sleep(1);
  }
  if (n_tries == MAX_LOCK_ATTEMPTS)
  {
    Log(LG_ERR, ("mpd: can't lock %s after %d attempts", filename, n_tries));
    return(-1);
  }

/* Close on exec */

  (void) fcntl(fd, F_SETFD, 1);

/* Create a stream on top of file descriptor */

  if ((lockFp = fdopen(fd, "r+")) == NULL)
  {
    Perror("fdopen");
    return(-1);
  }
  setbuf(lockFp, NULL);

/* Write my PID in there */

  rewind(lockFp);
  fprintf(lockFp, "%u\n", (u_int) getpid());
  fflush(lockFp);
  (void) ftruncate(fileno(lockFp), ftell(lockFp));
  return(0);
}

/*
 * GetInetSocket()
 *
 * Get a TCP socket and bind it to an address. Set SO_REUSEADDR on the socket.
 */

int
GetInetSocket(int type, struct in_addr locip, int locport, char *ebuf, int len)
{
  struct sockaddr_in	self;
  int			sock, self_size = sizeof(self);
  static int		one = 1;

/* Get and bind non-blocking socket */

  if ((sock = socket(AF_INET, type, type == SOCK_STREAM ? IPPROTO_TCP : 0)) < 0)
  {
    snprintf(ebuf, len, "socket: %s", strerror(errno));
    return(-1);
  }
  (void) fcntl(sock, F_SETFD, 1);
  if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
  {
    snprintf(ebuf, len, "can't set socket non-blocking: %s", strerror(errno));
    close(sock);
    return(-1);
  }
  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)))
  {
    snprintf(ebuf, len, "setsockopt: %s", strerror(errno));
    close(sock);
    return(-1);
  }
  memset(&self, 0, sizeof(self));
  self.sin_family = AF_INET;
  self.sin_addr = locip;
  self.sin_port = htons((u_short) locport);
  if (bind(sock, (struct sockaddr *) &self, self_size) < 0)
  {
    snprintf(ebuf, len, "bind: %s", strerror(errno));
    close(sock);
    return(-1);
  }
  return(sock);
}

/*
 * TcpGetListenPort()
 *
 * Get port for incoming telnet connections
 */

int
TcpGetListenPort(struct in_addr ip, int *port)
{
  char	ebuf[100];
  int	sock;

/* Get socket */

  if ((sock = GetInetSocket(SOCK_STREAM, ip, *port, ebuf, sizeof(ebuf))) < 0)
  {
    Log(LG_ERR, ("mpd: %s", ebuf));
    return(-1);
  }

/* Get the port number assigned by system if none requested */

  if (*port == 0)
  {
    struct sockaddr_in	address;
    int			size = sizeof(address);

    if (getsockname(sock, (struct sockaddr *) &address, &size) < 0)
    {
      Perror("getsockname");
      (void) close(sock);
      return(-1);
    }
    *port = (int) ntohs(address.sin_port);
  }

/* Make socket available for connections  */

  if (listen(sock, 2) < 0)
  {
    Perror("listen");
    (void) close(sock);
    return(-1);
  }

/* Done */

  return(sock);
}

/*
 * TcpAcceptConnection()
 *
 * Accept next connection on port
 */

int
TcpAcceptConnection(int sock, struct sockaddr_in *addr)
{
  int	new_sock;
  int	size = sizeof(*addr);
  struct request_info req;

/* Accept incoming connection */

  memset(addr, 0, sizeof(*addr));
  if ((new_sock = accept(sock, (struct sockaddr *) addr, &size)) < 0) {
    Perror("accept");
    return(-1);
  }
  
  if (bund) {
    if (Enabled(&bund->conf.options, BUND_CONF_TCPWRAPPER)) {
      request_init(&req, RQ_DAEMON, "mpd", RQ_FILE, new_sock, NULL);
      fromhost(&req);
      if (!hosts_access(&req)) {
	Log(LG_ERR, ("[%s] refused connection (tcp-wrapper) from %s", 
	  bund->name, eval_client(&req)));
	close(new_sock);
	return(-1);
      }
    }
  }
  
  (void) fcntl(new_sock, F_SETFD, 1);
  if (fcntl(new_sock, F_SETFL, O_NONBLOCK) < 0) {
    Perror("fcntl");
    return(-1);
  }

/* Done */

  return(new_sock);
}

/*
 * ShowMesg()
 */

void
ShowMesg(int log, const char *buf, int len)
{
  char	*s, mesg[256];

  if (len > 0)
  {
    if (len > sizeof(mesg) - 1)
      len = sizeof(mesg) - 1;
    memcpy(mesg, buf, len);
    mesg[len] = 0;
    for (s = strtok(mesg, "\r\n"); s; s = strtok(NULL, "\r\n"))
      Log(log, (" MESG: %s", s));
  }
}

/*
 * Crc16()
 *
 * Compute the 16 bit frame check value, per RFC 1171 Appendix B,
 * on an array of bytes.
 */

u_short
Crc16(u_short crc, u_char *cp, int len)
{
  while (len--)
    crc = (crc >> 8) ^ Crc16Table[(crc ^ *cp++) & 0xff];
  return(crc);
}

static const u_int16_t Crc16Table[256] = {
/* 00 */    0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
/* 08 */    0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
/* 10 */    0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
/* 18 */    0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
/* 20 */    0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
/* 28 */    0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
/* 30 */    0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
/* 38 */    0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
/* 40 */    0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
/* 48 */    0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
/* 50 */    0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
/* 58 */    0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
/* 60 */    0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
/* 68 */    0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
/* 70 */    0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
/* 78 */    0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
/* 80 */    0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
/* 88 */    0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
/* 90 */    0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
/* 98 */    0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
/* a0 */    0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
/* a8 */    0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
/* b0 */    0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
/* b8 */    0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
/* c0 */    0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
/* c8 */    0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
/* d0 */    0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
/* d8 */    0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
/* e0 */    0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
/* e8 */    0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
/* f0 */    0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
/* f8 */    0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};


