Logo Search packages:      
Sourcecode: ziproxy version File versions  Download package

netd.c

//
// netd.c : wrapper for ziproxy - user mode inetd replacement
// This code used to generate the 'netd' executable
// now it is the core for the 'ziproxy' executable (netd is obsolete)
// 
// Copyright (c)2003-2004 Juraj Variny<variny@naex.sk>
// Copyright (c)2005-2008 Daniel Mealha Cabrita
// 
// Based on
// proxy.c from microproxy package:
// (c) May 2001, by David McNab - david@rebirthing.co.nz, http://freeweb.sf.org
// 
// Released subject to GNU General Public License v2 or later version.
//
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#define _GNU_SOURCE
#include <getopt.h>

#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <time.h>
#include <assert.h>

typedef int SOCKET;
typedef unsigned int UINT;

#include "cfgfile.h"
#include "log.h"
#include "ziproxy.h"

/*#define SockSend(sd, buf, len)                write(sd, buf, len)
#define SockReceive(sd, buf, len)         read(sd, buf, len)
#define SockClose(sd)                                 close(sd)*/
#define SockAddrType                            struct sockaddr_in
#define SockError(msg)                            fprintf(stderr,"%s - %s\n",msg,strerror(errno))

//#define Strnicmp(s1, s2, n)                         strncasecmp(s1, s2, n)
#define LOGGING ((LogFileName != NULL) || (LogPipe != NULL))


//
// PRIVATE DECLARATIONS
//

int   proxy_server ();
int   proxy_handlereq (SOCKET sock_client, const char *client_addr, struct sockaddr_in *socket_host);
void  process_request (const char *client_addr, struct sockaddr_in *socket_host);
void  sigcatch (int);
void  process_command_line_arguments (int argc, char **argv);
void  option_error (int exitcode, char *message, ...);
int   daemonize (void);

char *cfg_file = DefaultCfgLocation;

struct struct_command_options{
      int daemon_mode;  /* != 0, daemon mode; == 0, [x]inetd mode)      */
//    int cfg_specified;      /* != 0, used overrided default config file; == 0, default config file path   */
      struct in_addr addr_low, addr_high;
} command_options;

// Parse the input string into an address range defined by addr_low and
// addr_high. The input can be a single address or a range separated by '-'.
// If the input is a single address, addr_low would be the same as addr_high.
// Return 0 if fails, 1 if succeeds.
static int parse_address_range(const char *str, struct in_addr *addr_low,
                         struct in_addr *addr_high)
{
  char *str_copy, *tmp_ptr;
  struct hostent * addr2 = NULL;

  // try a single address
  if (inet_aton(str, addr_low)) {
    addr_high->s_addr = addr_low->s_addr;
    return 1;
  }
  if ((addr2 = gethostbyname(str)) != NULL) {
    addr_low->s_addr = ((struct in_addr *)addr2->h_addr)->s_addr;
    addr_high->s_addr = addr_low->s_addr;
    return 1;
  }

  // try address range
  str_copy = strdup(str);
  if ((tmp_ptr = index(str_copy, '-')) == NULL) {
    free(str_copy);
    return 0;
  }

  *tmp_ptr = '\0'; // break the string into 2
  if (!inet_aton(str_copy,addr_low) && ((addr2 = gethostbyname(str_copy)) == NULL)) {
    free(str_copy);
    return 0;
  }
  if (addr2 != NULL) {
    addr_low->s_addr = ((struct in_addr *)addr2->h_addr)->s_addr;
    addr2 = NULL;
  }
  if (!inet_aton(tmp_ptr+1,addr_high) && ((addr2 = gethostbyname(tmp_ptr+1)) == NULL)) {
    free(str_copy);
    return 0;
  }
  free(str_copy);

  if (addr2 != NULL)
    addr_high->s_addr = ((struct in_addr *)addr2->h_addr)->s_addr;
  
  if (ntohl(addr_low->s_addr) > ntohl(addr_high->s_addr))
    return 0;

  return 1;
}

int main(int argc, char **argv, char *env[])
{
      struct tm *btime;
      time_t cas;
      int pipedes[2],logdes, i;
      
      command_options.addr_low.s_addr=0;

      /* set defaults */
      command_options.daemon_mode = 0;

      process_command_line_arguments (argc, argv);

      i = ReadCfgFile(cfg_file);

      if (command_options.daemon_mode == 0) {   // [x]inetd mode
            /* start logging ([x]inetd mode) */
            init_logging();
            init_access_log();
            
            process_request (NULL, NULL);       // client address is unknown in this mode
      }

      if(i != 0)
      {
            if(i == -1) fprintf(stderr, "Configuration file '%s' not found!\n", cfg_file);
            return 5;
      }

      if(!command_options.addr_low.s_addr && OnlyFrom){
            if(!parse_address_range(OnlyFrom, &(command_options.addr_low), &(command_options.addr_high))) {
                  fprintf(stderr,"Invalid address or host name '%s'\n",OnlyFrom);
                  return 3;
            }
      }

      if (NetdTimeout) signal(SIGALRM, sigcatch);
      
      if (LogFileName != NULL) {
            char logname[50];
            time(&cas);
            btime = localtime(&cas);
            strftime(logname,sizeof(logname),LogFileName,btime);
            if((logdes = open(logname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR)) == -1)
            {
                  SockError("Error while creating logfile");
                  return 7;
            }
      }

      if (LogPipe != NULL) {
      
            fflush(stderr);
            pipe(pipedes);


            switch(fork()){
                              
            case 0: //child
                  //should have a chance to write out last line(s) of logging output:
                  signal(SIGPIPE,SIG_IGN);
                  signal(SIGHUP,SIG_IGN);
                  signal(SIGINT,SIG_IGN);
                  signal(SIGQUIT,SIG_IGN);

                  //stdin <- pipe
                  dup2(pipedes[0],0);
                  close(pipedes[0]);
                  close(pipedes[1]);
                               
                  if (LogFileName != NULL) {
                  //stdout -> logfile
                        close(1);
                        dup2(logdes,1);
                        close(logdes);
                  }
                  execvp(LogPipe[0], LogPipe);
                  SockError("Error starting LogPipe process");
                  return 7;

            case -1:SockError("fork()");
                  return 8;

            default://parent
                  signal(SIGHUP, sigcatch);
                  signal(SIGINT, sigcatch);
                  signal(SIGTERM, sigcatch);

                  close(pipedes[0]);
                  //stderr->pipe
                  fclose(stderr);
                  
                  dup2(pipedes[1],2);
                  close(pipedes[1]);
/*                if(!(stderr = fdopen(2,"a"))) 
                        SockError("Error while reopening stderr->pipe");*/
                  if (LogFileName != NULL) {
                        close(logdes);
                        logdes = 2;
                  }
                  break;
            }
      } else if (LogFileName != NULL) {
                  dup2(logdes,2);
                  close(logdes);
      }
            setvbuf(stderr,NULL,_IOLBF,BUFSIZ);
      
      /* start logging (daemon mode) */
      init_logging();
      init_access_log();

      /* turn it into a daemon */
      {
            int retcode;

            retcode = daemonize ();
            if (retcode > 0) {
                  printf ("%d\n", retcode);
                  return (0);
            } else if (retcode < 0) {
                  fprintf (stderr, "\nERROR: Unable to create daemon. Aborting.\n");
                  return (9);
            }     
      }

      return proxy_server(&(command_options.addr_low), &(command_options.addr_high));
}


//
// main thread which listens for incoming http connections, and launches a child process
// when a connection comes in
//
int proxy_server(struct in_addr *addr_low, struct in_addr *addr_high)
{
      SOCKET                    sock_listen, sock_client;
      int                                 one = 1,sin_size,Status;
      // These are addresses in host byte order for comparison purpose.
      uint32_t addr_low_host, addr_high_host, connection_host;
      struct timeval tv;
      fd_set readfds;
      SockAddrType      sockAddr,gotConn;
      int which_BindOutgoing = 0;
      struct sockaddr_in pre_socket_host;
      struct sockaddr_in *socket_host = NULL;

      if (BindOutgoing_entries != 0)
            socket_host = &pre_socket_host;
      
      sockAddr.sin_family=AF_INET;
      sockAddr.sin_port=htons(Port);
      sockAddr.sin_addr.s_addr=INADDR_ANY;
      if (Address != NULL) {
            if (*Address != '\0')
                  sockAddr.sin_addr.s_addr = inet_addr(Address);
      }
      
      // bind the socket
      sock_listen = socket(AF_INET, SOCK_STREAM, 0);
      //Status = setsockopt (sock_listen, IPPROTO_TCP, TCP_NODELAY, (char * ) &one, sizeof (int));
        /* FIXME: netd does not close the socket while exiting, this is just a workaround */
        Status = setsockopt (sock_listen, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one));
      Status = bind(sock_listen, (struct sockaddr*) &sockAddr, sizeof(sockAddr));
      if(Status < 0)
      {
            SockError("Failed to connect socket for receiving connections");
            return 20;
      }
            // set socket to listen
      if (listen(sock_listen, SOMAXCONN) != 0)
      {
            SockError("Failed to listening mode on socket");
            return 21;
      }
      if(setsockopt(sock_listen,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(int)) < 0)
            SockError ("Failed to set REUSEADDR flag on socket(not critical)");
      
      addr_low_host = ntohl(addr_low->s_addr);
      addr_high_host = ntohl(addr_high->s_addr);

      if(NetdTimeout) alarm(NetdTimeout);

      // loop accepting connections
      while (1)
      {
            /* create data structures for BindOutgoing rotation (is appliable) */
            if (socket_host != NULL) {
                  if (which_BindOutgoing == BindOutgoing_entries)
                        which_BindOutgoing = 0;

                  socket_host->sin_family = AF_INET;
                  socket_host->sin_port = 0; // the OS chooses the port
                  socket_host->sin_addr.s_addr = BindOutgoing [which_BindOutgoing];
            
                  which_BindOutgoing++;
            }

            /* watch listen socket for readability */
            FD_ZERO(&readfds);
            FD_SET(sock_listen, &readfds);

            /* timeout after one second */
            tv.tv_sec  = 1;
            tv.tv_usec = 0;

            Status = select(sock_listen + 1, &readfds, NULL, NULL, &tv);
            if (Status < 0) {
                  SockError("select() failed");
            }
            else if (Status != 0) {
                  /* data ready */
                  sin_size=sizeof(gotConn);

                  if ((sock_client = accept(sock_listen, (struct sockaddr *) &gotConn, &sin_size)) < 0) {
                        SockError("Accept() failed");
                  }
                  // there's a new connection
                  if(addr_low->s_addr) {
                        connection_host = ntohl(gotConn.sin_addr.s_addr);
                        if ((connection_host < addr_low_host) ||
                            (connection_host > addr_high_host)) {
                              if (LOGGING)
                                    fprintf(stderr,"connection from %s refused\n",inet_ntoa(gotConn.sin_addr));
                              close(sock_client);
                              continue;
                        } else if (NetdTimeout)
                              alarm(NetdTimeout);
                  }else{
                        if(NetdTimeout)
                              alarm(NetdTimeout);
                  }

fork_retry:
                  switch(fork())
                  {
                  case 0://child 
                        close(sock_listen);

                        if (LOGGING && !addr_low->s_addr)
                              fprintf(stderr,"[%d] from %s\n",getpid(),inet_ntoa(gotConn.sin_addr));

                        return proxy_handlereq (sock_client, inet_ntoa(gotConn.sin_addr), socket_host);
                  case -1:
#ifndef NOWAIT
                        if (LOGGING) printf("Fork() failed, waiting...\n");
                        if(waitpid(-1,NULL,0) < 0)
                        {
                              SockError("Error while waiting for child");
                              return 22;
                        }else{
                              putchar('\n');
                              goto fork_retry;
                        }
#endif
                        break;
                        
                  default://parent
                        close(sock_client);
                  }
            }
#ifndef NOWAIT
            /* collect terminated child procs */
            while(waitpid(-1,NULL,WNOHANG) > 0)
                  ;
#endif
      }
                        // 'while (1)'
      return 0;   // how on earth did we get here???
}                 // 'proxy_server()'


//
// function which handles a single http request
//
int proxy_handlereq (SOCKET sock_client, const char *client_addr, struct sockaddr_in *socket_host)
{
      close(0);
      if(dup(sock_client) < 0)//client -> child in
      {
            SockError("dup() failed");
            exit(11);
      }

      close(1);
      if(dup(sock_client) < 0)//child out -> client   
      {
            SockError("dup() failed");
            exit(12);
      }
      
      if(MSIETest){
            if(system(WhereZiproxy) < 0) SockError("system() failed");
            close(sock_client);
            exit(14);
      }else{
            close(sock_client);
            process_request (client_addr, socket_host);
      }

      return(0);
}           // 'proxy_handlereq()'

void sigcatch (int signo)
{
      if (LOGGING){
            if(signo == SIGALRM) fprintf(stderr,"Timed out,");
            else fprintf(stderr,"Signal %d caught,",signo);

            fprintf(stderr," exiting.\n");
      }
      exit(0);
}

void process_request (const char *client_addr, struct sockaddr_in *socket_host)
{
      ziproxy (client_addr, socket_host);
      SockError("ziproxy() call failed (probably a bug, please contact the maintainer)"); // being paranoid here, might be removed later
      exit(14);
}

void process_command_line_arguments (int argc, char **argv)
{
      int option_index = 0;
      int defined_mode = 0;
      int option;
      struct option long_options[] =
      {
            {"config-file", 1, 0, 'c'},
            {"daemon-mode", 0, 0, 'd'},
            {"only-from", 1, 0, 'f'},
            {"help", 0, 0, 'h'},
            {"inetd-mode", 0, 0, 'i'},
            {0, 0, 0, 0}
      };

      while ((option = getopt_long (argc, argv, "c:df:hi", long_options, &option_index)) != EOF){
            switch(option){
                  case 'c':
                        cfg_file = optarg;
                        break;
                  case 'd':
                        if (defined_mode != 0)
                              option_error (3, "Invalid parameters: Daemon mode and inetd mode are mutually exclusive.\n");
                        command_options.daemon_mode = 1;
                        defined_mode = 1;
                        break;
                  case 'f':
                        if (!parse_address_range (optarg, &(command_options.addr_low), &(command_options.addr_high)))
                              option_error (3, "Invalid address or host name '%s'\n", optarg);
                        break;
                  case 'h':
                        printf ("Ziproxy " VERSION "\n"
                                    "Copyright (c)2003-2004 Juraj Variny\n"
                                    "Copyright (c)2005-2008 Daniel Mealha Cabrita\n"
                                    "\n"

                                    "This program is free software; you can redistribute it and/or modify\n"
                                    "it under the terms of the GNU General Public License as published by\n"
                                    "the Free Software Foundation; either version 2 of the License, or\n"
                                    "(at your option) any later version.\n"
                                    "\n"
                                    "This program is distributed in the hope that it will be useful,\n"
                                    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
                                    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
                                    "GNU General Public License for more details.\n"
                                    "\n"
                                    "You should have received a copy of the GNU General Public License\n"
                                    "along with this program; if not, write to the Free Software\n"
                                    "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA\n"
                                    "\n\n"
                                    
                                    "Usage: ziproxy <-d|-i> [-c <config_file>] [-f <host_or_range>] [-h].\n\n"
                                    "-d, --daemon-mode\n\tUsed when running in standalone mode.\n\n"
                                    "-i, --inetd-mode\n\tUsed when running from inetd of xinetd.\n\n"
                                    "-c <config_file>, --config-file=<config_file>\n\tFull path to ziproxy.conf file (instead of default one).\n\n"
                                    "-f <host_or_range>, --only-from=<host_or_range>\n\tLimit incoming connections only from the specified address(es).\n\n"
                                    "-h, --help\n\tDisplay summarized help (this text).\n\n"
                                    "\n");
                        exit (0);
                        break;
                  case 'i':
                        if (defined_mode != 0)
                              option_error (3, "Invalid parameters: Daemon mode and inetd mode are mutually exclusive.\n");
                        command_options.daemon_mode = 0;
                        defined_mode = 1;
                        break;
                  case ':':
                        option_error (4, "Missing mandatory parameter.\n");
                        break;
                  case '?':
                        option_error (4, "Unknown parameter provided.\n");
                        break;
                  default:
                        option_error (4, "Unrecognized option.\n");
            }
      }
      if (defined_mode == 0)
            option_error (3, "It is required to define either 'daemon mode' or 'inetd mode'\n");
}

void option_error (int exitcode, char *message, ...) {
      va_list ap;

      va_start (ap, message);
      vfprintf (stderr, message, ap);
      va_end (ap);

      fprintf (stderr, "\nCall `ziproxy --help` for a list of available parameters with their syntax.\n");
      exit (exitcode);
}

/* returns:
 * 0: child, daemon created successfully..
 * >0: parent, (daemon created successfully from parent's side) returns child's PID. must exit afterwards.
 * -1: error (parent process)
 * -2: error (child process)
 */
int daemonize (void) {
      pid_t pid, sid;

      pid = fork ();

      if (pid < 0)
            return (-1);

      if (pid > 0)
            return (pid);

      umask(0);

      sid = setsid ();
      if (sid < 0)
            return (-2);

      if ((chdir ("/")) < 0)
            return (-2);

      // can't close stdin/stdout otherwise ziproxy won't work
//    close (STDIN_FILENO);
//    close (STDOUT_FILENO);
      close (STDERR_FILENO);
                                                                                
      return (0);
}


Generated by  Doxygen 1.6.0   Back to index