text: /*
  common.c
  
  Copyright (C) 1999, 2000 Lars Brinkhoff.  See COPYING for terms and conditions.
  
  Code common to both htc and hts.
  */
  
  #include <time.h>
  #include <stdio.h>
  #include <netdb.h>
  #include <fcntl.h>
  #include <stdlib.h>
  #include <signal.h>
  #include <syslog.h>
  #include <termios.h>
  #include <sys/poll.h>
  
  #include "tunnel.h"
  #include "common.h"
  
  #ifndef TRUE
  #define TRUE 1
  #define FALSE 0
  #endif
  
  #ifdef DEBUG_MODE
  static void
  log_level (int level, char *fmt0, va_list ap)
  {
    if (debug_level >= level)
      {
        struct tm *t2;
        char s[40];
        time_t t;
        int i;
  
        time (&t);
        t2 = localtime (&t);
        strftime (s, sizeof s, "%Y%m%d %H%M%S ", t2);
        fprintf (debug_file, "%s", s);
        for (i = 1; i < level; i++)
  	fprintf (debug_file, "    ");
        vfprintf (debug_file, fmt0, ap);
        fprintf (debug_file, "\n");
        fflush (debug_file);
      }
  }
  #endif
  
  void
  log_exit (int status)
  {
    log_notice ("exit with status = %d", status);
    exit (status);
  }
  
  void
  log_notice (char *fmt0, ...)
  {
    va_list ap;
    va_start(ap, fmt0);
  #ifdef DEBUG_MODE
    log_level (1, fmt0, ap);
  #else
    vsyslog (LOG_NOTICE, fmt0, ap);
  #endif
    va_end(ap);
  }
  
  void
  log_error (char *fmt0, ...)
  {
    va_list ap;
    va_start(ap, fmt0);
  #ifdef DEBUG_MODE
    log_level (2, fmt0, ap);
  #else
    vsyslog (LOG_ERROR, fmt0, ap);
  #endif
    va_end(ap);
  }
  
  #ifdef DEBUG_MODE
  void
  log_debug (char *fmt0, ...)
  {
    va_list ap;
    va_start(ap, fmt0);
    log_level (3, fmt0, ap);
    va_end(ap);
  }
  #endif
  
  #ifdef DEBUG_MODE
  void
  log_verbose (char *fmt0, ...)
  {
    va_list ap;
    va_start(ap, fmt0);
    log_level (4, fmt0, ap);
    va_end(ap);
  }
  #endif
  
  #ifdef DEBUG_MODE
  void
  log_annoying (char *fmt0, ...)
  {
    va_list ap;
    va_start (ap, fmt0);
    log_level (5, fmt0, ap);
    va_end(ap);
  }
  #endif
  
  int
  server_socket (int port, int backlog)
  {
    struct sockaddr_in address;
    int i, s;
  
    s = socket (PF_INET, SOCK_STREAM, 0);
    if (s == -1)
      return -1;
    
    i = 1;
    if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof i) == -1)
      {
        log_error ("server_socket: setsockopt SO_REUSEADDR: %s",
  		 strerror (errno));
      }
  
    address.sin_family = PF_INET;
    address.sin_port = htons ((short)port);
    address.sin_addr.s_addr = INADDR_ANY;
    
    if (bind (s, (struct sockaddr *)&address, sizeof (address)) == -1)
      {
        close (s);
        return -1;
      }
  
    if (listen (s, (unsigned)backlog) == -1)
      {
        close (s);
        return -1;
      } 
  
    return s;
  }
  
  int
  set_address (struct sockaddr_in *address, const char *host, int port)
  {
    address->sin_family = PF_INET;
    address->sin_port = htons ((short)port);
    address->sin_addr.s_addr = inet_addr (host);
  
    if (address->sin_addr.s_addr == INADDR_NONE)
      {
        struct hostent *ent;
        unsigned int ip;
  
        log_annoying ("set_address: gethostbyname (\"%s\")", host);
        ent = gethostbyname (host);
        log_annoying ("set_address: ent = %p", ent);
        if (ent == 0)
  	return -1;
  
        memcpy(&address->sin_addr.s_addr, ent->h_addr, (unsigned)ent->h_length);
        ip = ntohl (address->sin_addr.s_addr);
        log_annoying ("set_address: host = %d.%d.%d.%d",
  		     ip >> 24,
  		    (ip >> 16) & 0xff,
  		    (ip >>  8) & 0xff,
  		     ip        & 0xff);
      }
  
    return 0;
  }
  
  int
  open_device (char *device)
  {
    struct termios t;
    int fd;
  
    fd = open (device, O_RDWR | O_NONBLOCK);
    if (fd == -1)
      return -1;
    
    if (tcgetattr (fd, &t) == -1)
      {
        if (errno == ENOTTY || errno == EINVAL)
  	return fd;
        else
  	return -1;
      }
    t.c_iflag = 0;
    t.c_oflag = 0;
    t.c_lflag = 0;
    if (tcsetattr (fd, TCSANOW, &t) == -1)
      return -1;
  
    return fd;
  }
  
  #ifdef DEBUG_MODE
  void
  dump_buf (FILE *f, unsigned char *buf, size_t len)
  {
    const int N = 20;
    int i, j;
  
    for (i = 0; i < len;)
      {
        fputc ('[', f);
        for (j = 0; j < N && i + j < len; j++)
  	fprintf (f, "%02x", buf[i + j]);
        for (; j < N; j++)
  	fprintf (f, "  ");
        fputc (']', f);
        fputc ('[', f);
        for (j = 0; j < N && i + j < len; j++)
  	{
  	  int c = buf[i + j];
  	  if (c < ' ' || c > 126)
  	    fputc ('.', f);
  	  else
  	    fputc (c, f);
  	}
        fputc (']', f);
        fputc ('\n', f);
        i += j;
      }
  }
  #endif
  
  int
  handle_device_input (Tunnel *tunnel, int fd, int events)
  {
    unsigned char buf[10240];
    ssize_t n, m;
  
    if (events & POLLIN)
      {
        n = read (fd, buf, sizeof buf);
        if (n == 0 || n == -1)
  	{
  	  if (n == -1 && errno != EAGAIN)
  	    log_error ("handle_device_input: read() error: %s",
  		       strerror (errno));
  	  return n;
  	}
  
  #ifdef DEBUG_MODE
        log_annoying ("read %d bytes from device:", n);
        if (debug_level >= 5)
  	dump_buf (debug_file, buf, (size_t)n);
  #endif
  
        m = tunnel_write (tunnel, buf, (size_t)n);
        log_annoying ("tunnel_write (%p, %p, %d) = %d", tunnel, buf, n, m);
        return m;
      }
    else if (events & POLLHUP)
      {
        log_error ("handle_device_input: POLLHUP");
        sleep (5);
      }
    else if (events & POLLERR)
      log_error ("handle_device_input: POLLERR");
    else if (events & POLLNVAL)
      log_error ("handle_device_input: POLLINVAL");
    else
      log_error ("handle_device_input: none of the above");
  
    errno = EIO;
    return -1;
  }
  
  int
  handle_tunnel_input (Tunnel *tunnel, int fd, int events)
  {
    unsigned char buf[10240];
    ssize_t n, m;
  
    if (events & POLLIN)
      {
        n = tunnel_read (tunnel, buf, sizeof buf);
        if (n <= 0)
  	{
  log_annoying ("handle_tunnel_input: tunnel_read() = %d\n", n);
  	  if (n == -1 && errno != EAGAIN)
  	    log_error ("handle_tunnel_input: tunnel_read() error: %s",
  		       strerror (errno));
  	  return n;
  	}
  
  #ifdef DEBUG_MODE
        log_annoying ("read %d bytes from tunnel:", n);
        if (debug_level >= 5)
  	dump_buf (debug_file, buf, (size_t)n);
  #endif
  
        m = write_all (fd, buf, (size_t)n);
        log_annoying ("write_all (%d, %p, %d) = %d", fd, buf, n, m);
        return m;
      }
    else if (events & POLLHUP)
      log_error ("handle_device_input: POLLHUP");
    else if (events & POLLERR)
      log_error ("handle_device_input: PULLERR");
    else if (events & POLLNVAL)
      log_error ("handle_device_input: PULLINVAL");
    else
      log_error ("handle_device_input: none of the above");
  
    errno = EIO;
    return -1;
  }
  
  void
  name_and_port (const char *nameport, char **name, int *port)
  {
    char *p;
  
    *name = strdup (nameport);
    if (*name == NULL)
      {
        fprintf (stderr, "Out of memory\n");
        exit (1);
      }
  
    p = strchr (*name, ':');
    if (p != NULL)
      {
        *port = atoi (p + 1);
        *p = '\0';
      }
  }
  
  int
  atoi_with_postfix (const char *s_)
  {
    char *s = strdup (s_);
    int n = strlen (s);
    int factor = 1;
    int x;
  
    if (s == NULL)
      {
        fprintf (stderr, "Out of memory\n");
        exit (1);
      }
  
    switch (s[n - 1])
      {
      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
        break;
      case 'k':
      case 'K':
        factor = 1024;
        break;
      case 'M':
        factor = 1024 * 1024;
        break;
      case 'G':
        factor = 1024 * 1024 * 1024;
        break;
      default:
        fprintf (stderr, "Unknown postfix: %c\n", s[n - 1]);
        exit (1);
      }
  
    if (factor != 1)
      s[n - 1] = '\0';
  
    x = factor * atoi (s);
    free (s);
    return x;
  }
  
  #ifdef DEBUG_MODE
  RETSIGTYPE
  log_sigpipe (int sig)
  {
    log_debug ("caught SIGPIPE");
    signal (SIGPIPE, log_sigpipe);
  }
  #endif
  