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

ziproxy.c

/* ziproxy.c
 * Part of ziproxy package.
 *
 * Copyright (c)2003-2004 Juraj Variny<variny@naex.sk>
 * Copyright (c)2005-2008 Daniel Mealha Cabrita
 *
 * Released subject to GNU General Public License v2 or later version.
 *
 * Main program, basic socket communications functions.
 * Based on mwp_proxy.c from Mark Williams<mwp@aussiemail.com.au>
 *
 * Code for processing an individual request
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
//#include <sys/resource.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <syslog.h>


#define SRC_ZIPROXY_C
#include "image.h"
#include "htmlmodify.h"
#include "cfgfile.h"
#include "http.h"
#include "log.h"

//static char* allocstr(size_t size);
static void sigcatch (int sig);

static int open_client_socket (char* hostname, short Port, struct sockaddr_in *socket_host);
void proxy_ssl (http_headers *hdr, FILE* sockrfp, FILE* sockwfp);

// define socket_host=NULL if binding to a specific IP is not required
int ziproxy (const char *client_addr, struct sockaddr_in *socket_host) {
      int sockfd;
      FILE* sockrfp;
      FILE* sockwfp;
      http_headers* hdrs;
      struct timeval currtime;

      gettimeofday (&currtime, NULL);

      /* use current PID (process for a specific request) for (debug) log */
      set_logpid_current ();
      
      if(ZTimeout){
            /* catch timeouts */
            (void) signal (SIGALRM, sigcatch);
            (void) alarm( ZTimeout );
      }

      /* catch broken pipes */
      (void) signal (SIGPIPE, sigcatch);
      
      hdrs = parse_initial_request();

      if (((hdrs->flags & H_TRANSP_PROXY_REQUEST) == 0) && (! ConventionalProxy))
            send_error (400, "Bad Request", NULL, "HTTP proxy requests not honoured by server.");
      
      if (hdrs->flags & H_USE_SSL) {
            NextProxy = NULL;
      } else {    /* not SSL, fill in the rest of client request */
            get_client_headers (hdrs);
            logdifftime("getting, parsing headers");
            logprintf("Method = %s Protocol = %s\n", hdrs->method, hdrs->proto);

            /* if a request received as transparent proxy... */
            if (hdrs->flags & H_TRANSP_PROXY_REQUEST)
                  fix_request_url (hdrs);
      }

      /* initialize global vars related to access stats (access log)
       * those are used only for access logging purposes, but the rest of the code
       * does not know whether such logging is enabled or not, thus these vars are
       * always updated */
      accesslog_data->hdr = hdrs;
      accesslog_data->giventime = &currtime;
      accesslog_data->client_addr = client_addr;
      accesslog_data->inlen = accesslog_data->inlen_decompressed = accesslog_data->outlen = 0; // we need to update those vars at a later stage
      accesslog_data->rep_strm_inlen_decompressed = 0;
      accesslog_data->flag_as_broken_pipe = 0;
            
      /* Open the client socket to the real web server. */
      sockfd = open_client_socket (hdrs->host, hdrs->port, socket_host);

      /* Open separate streams for read and write, r+ doesn't always work. 
       * What about "a+" ? */
      sockrfp = fdopen( sockfd, "r" );
      sockwfp = fdopen( sockfd, "w" );

      if ( hdrs->flags & H_USE_SSL )
            proxy_ssl (hdrs, sockrfp, sockwfp);
      else
            proxy_http (hdrs, sockrfp, sockwfp);

      /* Done. */
      (void) close( sockfd );
      exit( 0 );
}


#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED)
#define USE_IPV6
#endif

/* FIXME: This is a hardcoded limit, it should be dynamic instead.
          It poses a theoretically possible problem. */
/* unlikely a hostname will have more than that many IPs */
#define MAX_SA_ENTRIES 16

// define socket_host=NULL if binding to a specific IP is not required
static int open_client_socket (char* hostname, short Port, struct sockaddr_in *socket_host) {
#ifdef USE_IPV6
    struct addrinfo hints;
    char Portstr[10];
    int gaierr;
    struct addrinfo* ai;
    struct addrinfo* ai2;
    struct addrinfo* aiv4;
    struct addrinfo* aiv6;
    struct sockaddr_in6 sa[MAX_SA_ENTRIES];
#else /* USE_IPV6 */
    struct hostent *he;
    struct sockaddr_in sa[MAX_SA_ENTRIES];
#endif /* USE_IPV6 */
    int sa_len, sock_family, sock_type, sock_protocol;
    int sockfd;
    int sa_entries = 0;
    
    memset( (void*) &sa, 0, sizeof(sa) );

#ifdef USE_IPV6
#define SIZEOF_SA sizeof(struct sockaddr_in6)
#else
#define SIZEOF_SA sizeof(struct sockaddr_in)
#endif
    
if (NextProxy != NULL) {
      hostname = NextProxy;
      Port = NextPort;
}


#ifdef USE_IPV6
    (void) memset( &hints, 0, sizeof(hints) );
    hints.ai_family = PF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    (void) snprintf( Portstr, sizeof(Portstr), "%d", Port );
    if ( (gaierr = getaddrinfo( hostname, Portstr, &hints, &ai )) != 0 )
      send_error( 404, "Not Found", NULL, "Unknown host." );

    /* Find the first IPv4 and IPv6 entries. */
    aiv4 = NULL;
    aiv6 = NULL;
    for ( ai2 = ai; ai2 != NULL; ai2 = ai2->ai_next )
      {
      switch ( ai2->ai_family )
          {
          case AF_INET: 
          if ( aiv4 == NULL )
            aiv4 = ai2;
          break;
          case AF_INET6:
          if ( aiv6 == NULL )
            aiv6 = ai2;
          break;
          }
      }

    /* If there's an IPv4 address, use that, otherwise try IPv6. */
    if ( aiv4 != NULL )
      {
      if ( SIZEOF_SA < aiv4->ai_addrlen )
          {
          (void) fprintf(stderr, "%s - sockaddr too small (%lu < %lu)\n",
            hostname, (unsigned long) SIZEOF_SA,
            (unsigned long) aiv4->ai_addrlen );
          exit( 1 );
          }
      sock_family = aiv4->ai_family;
      sock_type = aiv4->ai_socktype;
      sock_protocol = aiv4->ai_protocol;
      sa_len = aiv4->ai_addrlen;

      /* loops each returned IP address and fills the array */
      {     
            struct addrinfo* current_aiv4 = aiv4;
            
            (void) memmove (&sa[sa_entries++], current_aiv4->ai_addr, sa_len);
            while ((sa_entries < MAX_SA_ENTRIES) && (current_aiv4->ai_next != NULL)) {
                  current_aiv4 = current_aiv4->ai_next;
                  (void) memmove (&sa[sa_entries++], current_aiv4->ai_addr, sa_len);
            }
      }
      
      goto ok;
      }
    if ( aiv6 != NULL )
      {
      if ( SIZEOF_SA < aiv6->ai_addrlen )
          {
          (void) fprintf(
            stderr, "%s - sockaddr too small (%lu < %lu)\n",
            hostname, (unsigned long) SIZEOF_SA,
            (unsigned long) aiv6->ai_addrlen );
          exit( 1 );
          }
      sock_family = aiv6->ai_family;
      sock_type = aiv6->ai_socktype;
      sock_protocol = aiv6->ai_protocol;
      sa_len = aiv6->ai_addrlen;

      /* loops each returned IP address and fills the array */
      {
            struct addrinfo* current_aiv6 = aiv6;

            (void) memmove (&sa[sa_entries++], current_aiv6->ai_addr, sa_len);
            while ((sa_entries < MAX_SA_ENTRIES) && (current_aiv6->ai_next != NULL)) {
                  current_aiv6 = current_aiv6->ai_next;
                  (void) memmove (&sa[sa_entries++], current_aiv6->ai_addr, sa_len);
            }
      }

      goto ok;
      }

    send_error( 404, "Not Found", NULL, "Unknown host." );

    ok:
    freeaddrinfo( ai );

#else /* USE_IPV6 */

    he = gethostbyname( hostname );
    if ( he == NULL )
      send_error( 404, "Not Found", NULL, "Unknown host." );
    sock_family = he->h_addrtype;
    sock_type = SOCK_STREAM;
    sock_protocol = 0;
    sa_len = SIZEOF_SA;

    /* loops each returned IP address and fills the array */
    while ((sa_entries < MAX_SA_ENTRIES) && (he->h_addr_list[sa_entries] != NULL)) {
      (void) memmove (&sa[sa_entries].sin_addr, he->h_addr_list[sa_entries], sizeof (sa[sa_entries].sin_addr));
      sa[sa_entries].sin_family = he->h_addrtype;
      sa[sa_entries].sin_port = htons (Port);
      sa_entries++;
    }
    
#endif /* USE_IPV6 */

    sockfd = socket( sock_family, sock_type, sock_protocol );
    if ( sockfd < 0 )
      send_error( 500, "Internal Error", NULL, "Couldn't create socket." );

    /* bind (outgoing connection) to a specific IP */
    if (socket_host != NULL)
      bind (sockfd, socket_host, sizeof (*socket_host));

    /* try each returned IP of that hostname */
    while (sa_entries--){
        if ( connect( sockfd, (struct sockaddr*) &sa[sa_entries], sa_len ) >= 0 )
            return sockfd;
    }
    send_error( 503, "Service Unavailable", NULL, "Connection refused." );

    /* it won't reach this point (it will either return sockfd or it will call send_error()
     * which will finish the process soon later) */
    return (-1);
}


void proxy_ssl (http_headers *hdr, FILE* sockrfp, FILE* sockwfp)
{ 
      /* Return SSL-proxy greeting header. */
      (void) fputs ("HTTP/1.0 200 Connection established\r\n\r\n", stdout);
      (void) fflush (stdout);

      blind_tunnel (hdr, sockrfp, sockwfp);
      access_log (&(accesslog_data->inlen), &(accesslog_data->outlen));
}

static void sigcatch (int sig)
{
      switch (sig) {
      case SIGALRM:
            access_log_flags (accesslog_data->rep_strm_inlen_decompressed ? &(accesslog_data->inlen_decompressed) : NULL, NULL, "Z");
            send_error (408, "Request Timeout", NULL, "Request timed out.");
            break;
      case SIGPIPE:
            /* usually we can interrupt immediately when the connection is broken at the remote server's side,
             * the remaining few bytes in the buffer can be discarded since the file is incomplete anyway.
             * one problem (may be others aswell), though, is when we're downloading a redirection page,
             * from a bad-behaved http server which breaks the connection instead of properly closing it.
             * the chances are the client will receive 0 bytes and the redirection won't happen.
             * so this signal is used only for flagging broken pipe in the access log and the program will
             * continue its processing and behave accordingly. */
            accesslog_data->flag_as_broken_pipe = 1;
            logputs ("ERROR: Pipe broken. Transfer interrupted.");
            return;
            // access_log_flags (accesslog_data->rep_strm_inlen_decompressed ? &(accesslog_data->inlen_decompressed) : NULL, NULL, "B");
            // exit (100);
            break;
      default:
            // this shouldn't happen since we're not expecting other signals
            access_log_flags (accesslog_data->rep_strm_inlen_decompressed ? &(accesslog_data->inlen_decompressed) : NULL, NULL, "*"); // help!
            exit (100);
            break;
      }
}


Generated by  Doxygen 1.6.0   Back to index