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

relay.c

/******************************************************************************

      relay.c -- relayed connections
      Copyright (C) 2004  Wessel Dankers <wsl@uvt.nl>

      This program is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with this program; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

      $Id: relay.c 23144 2007-12-21 15:36:48Z wsl $
      $URL: https://infix.uvt.nl/its-id/trunk/sources/fair-0.5.1/src/relay.c $

******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <pwd.h>
#include <grp.h>

#include "fair.h"
#include "error.h"
#include "sock.h"
#include "conn.h"
#include "chrono.h"
#include "fdcopy.h"
#include "worker.h"
#include "address.h"
#include "init.h"
#include "conf.h"

typedef struct relay {
      unsigned long long int id;
      fd_t *src, *dst;
      fdcopy_t *from, *to;
      address_t *addr;
      chrono_t *cr;
      connector_t *cn;
      size_t attempts;
      address_string_t str_from;
      address_string_t str_to;
} relay_t;

static const relay_t relay_0 = {0};

unsigned int relay_count = 0;
static unsigned long long int relay_seq = 0;

static void relay_trail(chrono_t *cr) {
      address_t *addr;

      unless(cr) return;
      addr = cr->data;
      chrono_delete(cr);

      unless(addr) return;
      address_unload(addr);
}

static void relay_delete_immediately(relay_t *rl) {
      unless(rl) return;

      if(rl->from)
            fdcopy_delete(rl->from);
      if(rl->to)
            fdcopy_delete(rl->to);
      if(rl->src)
            fd_close(rl->src);
      if(rl->dst)
            fd_close(rl->dst);
      if(rl->cr)
            chrono_delete(rl->cr);
      if(rl->cn)
            connector_delete(rl->cn);
      if(rl->addr)
            address_unload(rl->addr);
      if(conf_Debug)
            memset(rl, 'A', sizeof *rl);
      free(rl);
      relay_count--;
}

static void relay_delete(relay_t *rl) {
      unless(rl) return;

      if(rl->cr && rl->addr) {
            rl->cr->func = relay_trail;
            rl->cr->data = rl->addr;
            chrono_after(rl->cr, conf_TrailPeriod);
            rl->cr = NULL;
            rl->addr = NULL;
      }

      return relay_delete_immediately(rl);
}

static void relay_throughput_to(fdcopy_t *fdc, fdcopy_event_t evt, int len) {
      relay_t *rl;
      assert(fdc);
      rl = fdc->data;
      assert(rl);
      assert(rl->cr);

      if(evt == FDCOPY_EVENT_DONE) {
            if(!rl->from)
                  return relay_delete(rl);
            assert(rl->src);
            shutdown(rl->src->fd, SHUT_RD);
            assert(rl->dst);
            shutdown(rl->dst->fd, SHUT_WR);
            assert(rl->to);
            fdcopy_delete(rl->to);
            rl->to = NULL;
      }
      chrono_after(rl->cr, conf_IdleTimeout);
}

static void relay_throughput_from(fdcopy_t *fdc, fdcopy_event_t evt, int len) {
      relay_t *rl;
      assert(fdc);
      rl = fdc->data;
      assert(rl);
      assert(rl->cr);

      if(evt == FDCOPY_EVENT_DONE) {
            if(!rl->to)
                  return relay_delete(rl);
            assert(rl->dst);
            shutdown(rl->dst->fd, SHUT_RD);
            assert(rl->src);
            shutdown(rl->src->fd, SHUT_WR);
            assert(rl->from);
            fdcopy_delete(rl->from);
            rl->from = NULL;
      }
      chrono_after(rl->cr, conf_IdleTimeout);
}

static void relay_outgoing_tcp(connector_t *cn, fd_t *fd);

static bool relay_connect(relay_t *rl) {
      address_t *addr;

      unless(rl) return FALSE;

      if(rl->attempts--) {
            addr = worker_bestaddress();
            if(addr) {
                  address_string(&rl->str_to, addr);
                  if(conf_Debug)
                        syslog(LOG_INFO, "%lld Connecting to %s %s", rl->id,
                              rl->str_to.host, rl->str_to.serv);
                  address_load(rl->addr = addr);
                  chrono_after(rl->cr, conf_ConnectTimeout);
                  if(rl->cn)
                        connector_delete(rl->cn);
                  rl->cn = connector_new(addr, relay_outgoing_tcp, rl);
                  if(rl->cn)
                        return TRUE;
                  else
                        syslog(LOG_ERR, "%lld Setting up connection failed: %m", rl->id);
            } else {
                  syslog(LOG_ERR, "%lld No available workers; dropping connection", rl->id);
            }
      } else {
            syslog(LOG_ERR, "%lld No attempts left; dropping connection", rl->id);
      }
      relay_delete(rl);
      return FALSE;
}

static void relay_timeout(chrono_t *cr) {
      relay_t *rl;

      unless(cr) return;
      rl = cr->data;
      unless(rl) return;

      if(rl->cn) {
            errno = ETIMEDOUT;
            relay_outgoing_tcp(rl->cn, NULL);
      } else {
            syslog(LOG_WARNING, "%lld Closing idle connection from %s %s to %s %s", rl->id,
                  rl->str_from.host, rl->str_from.serv, rl->str_to.host, rl->str_to.serv);
            relay_delete(rl);
      }
}

static void relay_outgoing_tcp(connector_t *cn, fd_t *fd) {
      relay_t *rl;
      address_t *addr;

      unless(cn) return;
      rl = cn->data;
      unless(rl) return;

      if(rl->cn)
            connector_delete(rl->cn);
      rl->cn = NULL;

      if(fd) {
            rl->dst = fd;
            rl->from = fdcopy_new(rl->src, fd, relay_throughput_from, rl);
            assert(rl->from);
            rl->to = fdcopy_new(fd, rl->src, relay_throughput_to, rl);
            assert(rl->to);
            address_string(&rl->str_to, rl->addr);
            syslog(LOG_INFO, "%lld Connection from %s %s put through to %s %s", rl->id,
                  rl->str_from.host, rl->str_from.serv, rl->str_to.host, rl->str_to.serv);
            address_working(rl->addr);
            chrono_after(rl->cr, conf_IdleTimeout);
      } else {
            addr = rl->addr;
            if(addr) {
                  rl->addr = NULL;
                  syslog(LOG_ERR, "%lld connect(): %m while connecting to %s %s", rl->id,
                        rl->str_to.host, rl->str_to.serv);
                  address_broken(addr);
                  address_unload(addr);
            }
            relay_connect(rl);
      }
}

relay_t *relay_new(int fd, const struct sockaddr *sa, socklen_t sa_len) {
      relay_t *rl;

      relay_count++;
      rl = xalloc(sizeof *rl);
      *rl = relay_0;
      rl->id = relay_seq++;

      address_string_sa(&rl->str_from, sa, sa_len);
      syslog(LOG_INFO, "%llu Connection from %s %s", rl->id,
            rl->str_from.host, rl->str_from.serv);

      rl->attempts = conf_ConnectAttempts;
      rl->cr = chrono_new(relay_timeout, rl);
      assert(rl->cr);
      rl->src = fd_new(fd);
      assert(rl->src);

      relay_connect(rl);

      return rl;
}

Generated by  Doxygen 1.6.0   Back to index