Defines | Functions | Variables

proxy_server.c File Reference

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/errno.h>
#include <memory.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include "symtab.h"
#include "queue.h"
#include "proxy_utils.h"
#include "proxy_server.h"
Include dependency graph for proxy_server.c:

Go to the source code of this file.

Defines

#define _REENTRANT
#define PROXY_SERVER_USAGE_STR   "Usage: proxy_server [-l logfile] [-c] [-k]"

Functions

void proxy_print_component_tag (FILE *f, char *prefix, char *ctag, char *suffix)
int proxy_daemon_init (char *root, char *logfile)
int proxy_block_sigpipe ()
static int getenv_int (char *name, int defval)
int proxy_server_parse_cmd_line (int argc, char **argv, char **logfile, int *daemon, int *kerberos)
int main (int argc, char *argv[])
int proxy_auth (int s)
void dump_component (char *key, void *var)
void * proxy_purge_old_components (void *arg)
void * proxy_generic_conn_handler (void *arg)
COMPONENT * proxy_new_component ()
COMPONENT * proxy_get_request_header (int connfd, CID *client_id)
int proxy_send_cid_to_control_conn (int s, COMPONENT *c, CID client_id, in_port_t port)
int proxy_wait_for_server_conn (int connfd, int sock2)
int proxy_handle_data_transfer (int client_fd, int serv_fd)
int proxy_check_client_match (int data_conn_fd, int client_fd, in_port_t port, CID client_id)
int proxy_handle_connection_request (int connfd)
COMPONENT * proxy_init_server_component (int connfd, char *component_tag)
COMPONENT * proxy_wait_for_queue_message (int connfd, COMPONENT *component)
int proxy_send_conn_request_to_server (int connfd, char *component_tag, COMPONENT *component, COMPONENT *item)
int proxy_delete_server_tag (char *component_tag, COMPONENT *component)
int proxy_handle_control_connection (int connfd)
int proxy_init_sock (int *sfd, struct sockaddr_in *serv, char *proto, int nb, int port)

Variables

SYMTABLE * accept_table
pthread_mutex_t accept_tab_mutex
QUEUE * removed_components_queue
int proxy_krb5_enabled = FALSE

Detailed Description

This file contains the implementation of a proxy server that allows components behind a NAT to receive incoming connections. This server runs outside the NATted subnet and the component registers with it via an outgoing connection. The control connection between the component and the proxy server is persistent. When another component wants to initiate a connection to the component behind the NAT it connects to the proxy server and requests that a connection be established. All the protocol issues are handled by using the proxy library as a replacement for the normal socket calls.

Definition in file proxy_server.c.


Define Documentation

#define _REENTRANT

Definition at line 24 of file proxy_server.c.

#define PROXY_SERVER_USAGE_STR   "Usage: proxy_server [-l logfile] [-c] [-k]"

Function Documentation

void dump_component ( char *  key,
void *  var 
)

This function dumps some information about a component to the console.

Parameters:
key -- the component key
var -- pointer to the component structure

Definition at line 488 of file proxy_server.c.

{
  COMPONENT *item = (COMPONENT *)var;

  proxy_print_component_tag(stdout, "COMPONENT key =", key, NULL);

  printf("\tID = ");
  proxy_print_componentID(stdout, item->id.id);
  printf("\n");
  printf("\tport = %d\n", ntohs(item->port));
  printf("\tsockfd = %d\n", item->sockfd);

  return;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int getenv_int ( char *  name,
int  defval 
) [static]

Local function to get an integer from the environment, using a default value if it fails.

Parameters:
name -- the name of the environment variable
defval -- default value
Returns:
An integer value

Definition at line 214 of file proxy_server.c.

{
  char *envstr = NULL;
  long int longval = -1;
  char *endptr;
  extern int errno;

  if (name == NULL) return defval;

  /* Env variable does not exist */
  if ((envstr = getenv(name)) == NULL) return defval;

  /* Convert to long, checking for errors */
  longval = strtol(envstr, &endptr, 10);
  if ((errno == ERANGE) || (longval==0 && endptr==envstr))
    return defval;

  return (int)longval;
}

Here is the caller graph for this function:

int main ( int  argc,
char *  argv[] 
)

Main routine for the proxy server. Called by the OS of course. The proxy listens at PROXY_LISTEN_PORT_DEFAULT, which can overwritten by setting the environment variable PROXY_LISTEN_PORT.

Parameters:
argc -- number of args on the command line
argv -- array of strings representing the command line args
Returns:
nothing, this server runs forever.

Definition at line 302 of file proxy_server.c.

{
  int listen_sock, daemon;
  int proxy_listen_port = -1;
  socklen_t clilen;
  struct sockaddr_in tcp_serv, cliaddr;
  void *proxy_generic_conn_handler(void *);
  void *proxy_purge_old_components(void *);
  char *logfile;
  pthread_t tid;

  /* Parse command line args */
  if(proxy_server_parse_cmd_line(argc, argv, &logfile, &daemon, 
       &proxy_krb5_enabled) < 0) 
  {
    fprintf(stderr, "%s\n", PROXY_SERVER_USAGE_STR);
    exit(EXIT_FAILURE);
  }

#ifdef KERBEROS5
  if (proxy_check_krb5_envvars() != 0)
    exit(EXIT_FAILURE);
#else
  if(proxy_krb5_enabled) {
    fprintf(stderr, "Warning!  Kerberos was not enabled during compilation ");
    fprintf(stderr, "(-k ignored).\n\n");
  }
#endif

  /* if logfile wasn't specified on the command line, use default */
  if(!logfile)
    logfile = PROXY_LOGFILE;

  /* Make current process into a daemon */
  if(daemon && proxy_daemon_init(".", logfile) < 0) {
    fprintf(stderr, "Failed to start proxy server as a daemon.\n");
    exit(EXIT_FAILURE);
  }

  if (proxy_block_sigpipe() < 0) {
    fprintf(stderr, "Couldn't block SIGPIPE.  Aborting.\n");
    exit(EXIT_FAILURE);
  }

  removed_components_queue = new_queue();
  if (!removed_components_queue) {
    fprintf(stderr, "Bad news!  out of memory allocating queue\n");
    exit(EXIT_FAILURE);
  }

  /* create a hash table containing the components that have connected */
  accept_table = new_symtable(PROXY_ACCEPT_TABLE_SIZE);

  if (!accept_table) {
    fprintf(stderr, "Bad news!  out of memory allocating table\n");
    exit(EXIT_FAILURE);
  }

  if (pthread_mutex_init(&accept_tab_mutex, NULL)) {
    fprintf(stderr, "Bad news!  cannot create mutex\n");
    exit(EXIT_FAILURE);
  }

  proxy_listen_port = getenv_int("PROXY_LISTEN_PORT", PROXY_LISTEN_PORT_DEFAULT);
  if (proxy_init_sock(&listen_sock, &tcp_serv, "tcp", FALSE, proxy_listen_port) < 0) {
    fprintf(stderr, "Failed to create socket\n");
    perror("proxy_init_sock");
    exit(EXIT_FAILURE);
  }

  if(pthread_create(&tid, NULL, &proxy_purge_old_components, NULL)) {
    fprintf(stderr, "Warning: error creating new thread.\n");
    perror("pthread_create");
  }

  if(pthread_detach(tid)) 
    fprintf(stderr,"Warning: could not detach thread after creation.\n");

  printf("proxy IP %u\n", proxy_get_my_ipaddr());
  printf("proxy listening on port %d\n", ntohs(tcp_serv.sin_port));

  if (listen(listen_sock, 50) < 0) {
    perror("listen");
    exit(EXIT_FAILURE);
  }

  for (;;) {
    int *connfd;

    clilen = sizeof(cliaddr);

    /* malloc to avoid race between accept and new thread */
    connfd = (int *) malloc(sizeof(int));

    if (!connfd) {
      fprintf(stderr, "Cannot malloc.  going to sleep for a while.\n");
      sleep(3);
      continue;
    }

    *connfd = accept(listen_sock, (struct sockaddr *) &cliaddr, &clilen);

    if (*connfd < 0) {
      free(connfd);
      perror("accept");
      continue;
    }

    printf("new client connected!\n");

    if (proxy_auth(*connfd) < 0) {
      fprintf(stderr, "Warning: failed to authenticate client.\n");
      close(*connfd);
      continue;
    }

    if (pthread_create(&tid, NULL, &proxy_generic_conn_handler, connfd)) {
      fprintf(stderr, "Warning: error creating new thread.\n");
      close(*connfd);
      free(connfd);
      continue;
    }

    if(pthread_detach(tid)) 
      fprintf(stderr,"Warning: could not detach thread after creation.\n");
  }
}

Here is the call graph for this function:

int proxy_auth ( int  s  ) 

Performs Kerberos authentication for the component that has connected on the given socket.

Parameters:
s -- socket descriptor (already connected)
Returns:
0 on success, -1 on failure

Definition at line 440 of file proxy_server.c.

{
  proxy_tag_t response;

#ifdef KERBEROS5
  if (proxy_krb5_enabled) {
    response = PROXY_KRB5_AUTH_REQUIRED;
    if (write(s, &response, sizeof(response)) < 0)
      return -1;

    /* get credentials from client */
    if (proxy_recv_krb5_credentials(s) < 0) {
      /* 
       * if we didn't like the credentials; tell client that the
       * authentication failed, and this will be the error code
       * associated with the problem request.  otherwise, fall
       * through to other error checking below
       */
      response = PROXY_AUTH_FAILED;

      if (write(s, &response, sizeof(response)) == -1)
    return -1;

      return -1;
    }

    response = PROXY_AUTH_ACCEPTED;
  } 
  else
    response = PROXY_AUTH_ACCEPTED;
#else
  response = PROXY_AUTH_ACCEPTED;
#endif

  if (write(s, &response, sizeof(response)) < 0)
    return -1;

  return 0;
}

Here is the caller graph for this function:

int proxy_block_sigpipe (  ) 

Blocks SIGPIPE.

Returns:
0 on success, -1 on failure.

Definition at line 188 of file proxy_server.c.

{
  sigset_t sigvec;

  if (sigemptyset(&sigvec) < 0 ||
      sigaddset(&sigvec, SIGPIPE) < 0 || 
      pthread_sigmask(SIG_BLOCK, &sigvec, NULL) != 0) 
  {
    fprintf(stderr, "Unable to set thread signal mask.  Aborting\n");
    return -1;
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_check_client_match ( int  data_conn_fd,
int  client_fd,
in_port_t  port,
CID  client_id 
)

Checks whether this connection from the server component is referencing the correct client ID. This is checked mainly as a precaution against some rogue component connecting to the port we reserved for the client that had made a request previously.

Parameters:
data_conn_fd -- connection established with server component (this is the 2nd connection, not the control connection)
client_fd -- connection previously established with the client
port -- the destination port
client_id -- the client's component ID
Returns:
0 on success, -1 on failure

Definition at line 907 of file proxy_server.c.

{
  proxy_tag_t response = PROXY_CONNECTION_ACCEPTED;
  in_port_t new_port;
  CID new_cid;
  int n;

  printf("sending connection accepted to client\n");
  n = write(client_fd, &response, sizeof(response));

  if (n <= 0) {
    fprintf(stderr, "Error writing response to client\n");
    return -1;
  }

  printf("getting response\n");

  n = proxy_tread(data_conn_fd, (char *)&response, sizeof(response), 
    PROXY_TIMEOUT_DEFAULT);
  if (n <= 0) {
    fprintf(stderr, "Error getting response from client\n");
    return -1;
  }

  if (response != PROXY_CONNECT_REPLY) {
    fprintf(stderr, "Didn't get the expected tag!!\n");
    return -1;
  }

  printf("getting cid\n");
  n = proxy_tread(data_conn_fd, (char *)&(new_cid.id), sizeof(new_cid.id), 
    PROXY_TIMEOUT_DEFAULT);
  if (n <= 0) {
    fprintf(stderr, "Error getting ID from client\n");
    return -1;
  }

  printf("getting port\n");
  n = proxy_tread(data_conn_fd, (char *)&new_port, sizeof(new_port), 
    PROXY_TIMEOUT_DEFAULT);
  if (n <= 0) {
    fprintf(stderr, "Error getting port from client\n");
    return -1;
  }

  /* check for a bad match */
  if (memcmp(client_id.id, new_cid.id, sizeof(new_cid.id))
      || (port != new_port)) {
    fprintf(stderr, "BAD MATCH!!\n");
    response = PROXY_CONNECTION_REFUSED;
    write(data_conn_fd, &response, sizeof(response));

    return -1;
  }

  printf("IDs match, now sending connection accepted msg to server.\n");

  /* found a match.. send connection accepted to server */
  response = PROXY_CONNECTION_ACCEPTED;
  n = write(data_conn_fd, &response, sizeof(response));
  if (n <= 0) {
    fprintf(stderr, "Error writing response to client\n");
    return -1;
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_daemon_init ( char *  root,
char *  logfile 
)

This makes the current process into a daemon. This was taken from the original NetSolve code which had adapted Stevens' version from "UNIX Network Programming" for use in NetSolve.

Parameters:
root -- The name of the directory to chdir to after starting as a daemon.
logfile -- The name of the file to open for logging. This file descriptor will be duplicated for stdout and stderr.
Returns:
0 on success, -1 on failure.

Definition at line 104 of file proxy_server.c.

                                             {
  pid_t pid;
  int fd;

  if(!root || !logfile)
    return -1;

  pid = fork();

  if(pid < 0)
    return -1;

  if(pid != 0) /* parent exits */
    exit(0);

  setsid();   /* become session leader */

  pid = fork();

  if(pid < 0)
    return -1;

  if(pid != 0) /* 1st child exits */
    exit(0);

  chdir(root);
  umask(0);

  /* Remap stdin to /dev/null */
  fd = open("/dev/null", O_RDWR|O_CREAT, 0644);
  if(fd < 0) {
    perror("open()");
    return -1;
  }
  close(0);
  if(dup2(fd, 0) < 0) {
    fprintf(stderr,"dup2 failed");
    perror("dup2()");
    return -1;
  }
  close(fd);

  fprintf(stderr, "\n"
    "==============================================================\n"
    "|       -- Giving up terminal control! --\n|\n"
    "| All further terminal output will be sent to the log file:\n"
    "|\t\"%s\"\n"
    "=============================================================="
    "\n\n", logfile);

  /* Open logfile */
  remove(logfile);
  fd = open(logfile, O_RDWR|O_CREAT, 0644);
  if(fd < 0){
    fprintf(stderr,"dup2 failed");
    perror("open()");
    return -1;
  }

  /* Remap stdout and stderr to logfile */
  close(1);
  if(dup2(fd, 1) < 0){
    fprintf(stderr,"dup2 failed");
    perror("dup2()");
    return -1;
  }
  close(2);
  if(dup2(fd, 2) < 0){
    fprintf(stderr,"dup2 failed");
    perror("dup2()");
    return -1;
  }
  close(fd);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_delete_server_tag ( char *  component_tag,
COMPONENT *  component 
)

Removes the given server from the table.

Parameters:
component_tag -- the component tag (ID/port pair) for this server
component -- the component struct for this server
Returns:
0 on success, -1 on failure

Definition at line 1252 of file proxy_server.c.

{

  proxy_print_component_tag(stdout, "going to attempt to delete component: ",
    component_tag, NULL);

  if (pthread_mutex_lock(&accept_tab_mutex) == 0) {
    HASHNODE *ht;

    printf("going to delete component_tag now...\n");
    ht = hash_delete(accept_table, (void *) component_tag);

    if (!ht)
      printf("warning: expected to find component in hash table\n");
    else if (component != (COMPONENT *) (ht->item))
      printf("warning: wrong component! \n");

    if(ht) free(ht);
    pthread_mutex_unlock(&accept_tab_mutex);
  } else
    return -1;

  component->to_be_freed = TRUE;

  /* put component in queue to be freed later */

  if (pthread_mutex_lock(removed_components_queue->mutex) == 0) {

    printf("putting component in removal queue\n");

    enqueue(removed_components_queue, (void *) component);
    pthread_mutex_unlock(removed_components_queue->mutex);
  } else {
    fprintf(stderr, "Warning: couldn't lock removed components queue.\n");
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void* proxy_generic_conn_handler ( void *  arg  ) 

This is the function that gets run as a new thread when a connection has been established. Here we determine the type of request and call some function to handle it.

Parameters:
arg -- pointer to the connected socket descriptor (cast as void pointer)

Definition at line 561 of file proxy_server.c.

{
  int s, n;
  proxy_tag_t tag;

  s = *((int *) arg);
  free(arg);

  n = proxy_tread(s, (void *) &tag, 1, PROXY_TIMEOUT_DEFAULT);

  if (n <= 0) {
    fprintf(stderr, "Warning: error reading tag.  Closing connection.\n");
    close(s);
    return NULL;
  }

  printf("tag = %d\n", tag);

  switch (tag) {

    case PROXY_CONTROL_CONNECTION:
      proxy_handle_control_connection(s);
      break;

    case PROXY_CONNECT:
      proxy_handle_connection_request(s);
      break;

    /* this case is for testing purposes.. remove later. */
    case 'Z':
      fprintf(stderr, "TERMINATING...\n");
      exit(EXIT_SUCCESS);
      break;

    default:
      fprintf(stderr, "Warning: unknown tag %d\n", tag);
      close(s);
      break;
  }

  return NULL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

COMPONENT* proxy_get_request_header ( int  connfd,
CID *  client_id 
)

Receives the request header from the component that has connected. This will provide the following information: -source component ID -destination component ID -destination port

Parameters:
connfd -- socket descriptor for connection with client
client_id -- component ID of the client that has connected (this is set upon return)
Returns:
pointer to the destination's component info, NULL on error

Lock accept table

Definition at line 646 of file proxy_server.c.

{
  int id_size, port_size, to;
  COMPONENT *component;
  in_port_t dest_port;
  HASHNODE *ht;
  CID dest_id;
  char component_tag[sizeof(CID) + sizeof(dest_port) + 1];

  to = PROXY_TIMEOUT_DEFAULT;
  id_size = sizeof(client_id->id);
  port_size = sizeof(dest_port);

  if (proxy_tread(connfd, (void *) client_id->id, id_size, to) <= 0 ||
      proxy_tread(connfd, (void *) dest_id.id, id_size, to) <= 0 ||
      proxy_tread(connfd, (void *) &dest_port, port_size, to) <= 0) {
    fprintf(stderr, "Error reading IDs.  Aborting connection.\n");
    return NULL;
  }

  /* component_tag is composed of CID/port */
  memcpy(component_tag, dest_id.id, id_size);
  memcpy(component_tag + id_size, &dest_port, port_size);
  *(component_tag + id_size + port_size) = '\0';

  proxy_print_component_tag(stdout, "CONNECT REQ dest = ", 
    component_tag, NULL);

  if (pthread_mutex_lock(&accept_tab_mutex) == 0) {
    ht = hash_lookup(accept_table, (void *) component_tag);

    if (!ht) {
      proxy_tag_t response = PROXY_CONNECTION_REFUSED;

      fprintf(stderr, "Component entry not found!\n");
      pthread_mutex_unlock(&accept_tab_mutex);
      write(connfd, &response, sizeof(response));
      return NULL;
    }

    component = (COMPONENT *) (ht->item);

    printf("found entry.. socket desc = %d\n", component->sockfd);

    pthread_mutex_unlock(&accept_tab_mutex);
  } else {
    fprintf(stderr, "proxy_get_request_header(): Error locking.\n");
    return NULL;
  }

  return component;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_handle_connection_request ( int  connfd  ) 

Handles a client's request for a connection to a server component behind a NAT.

Parameters:
connfd -- socket on which the client is connected
Returns:
0 on success, -1 on failure

Definition at line 986 of file proxy_server.c.

{
  int new_incoming_sock, data_conn_fd;
  struct sockaddr_in tcp_serv;
  CID client_id;
  COMPONENT *component;

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

  component = proxy_get_request_header(connfd, &client_id);

  if (!component) {
    fprintf(stderr, "Error getting component header.  Aborting connection.\n");
    close(connfd);
    return -1;
  }

  if (proxy_init_sock(&new_incoming_sock, &tcp_serv, "tcp", FALSE, 0) < 0) {
    fprintf(stderr, "Error creating new incoming socket.\n");
    close(connfd);
    return -1;
  }

  printf("new port  -->  %d\n", ntohs(tcp_serv.sin_port));

  if (listen(new_incoming_sock, 5) < 0) {
    proxy_tag_t response = PROXY_CONNECTION_REFUSED;

    write(connfd, &response, sizeof(response));
    close(connfd);
    return -1;
  }

  if (proxy_send_cid_to_control_conn(connfd, component, client_id, tcp_serv.sin_port) < 0) {
    fprintf(stderr, "Error queueing control id.\n");
    close(connfd);
    return -1;
  }

  data_conn_fd = proxy_wait_for_server_conn(connfd, new_incoming_sock);

  if (data_conn_fd > 0) {
    if (proxy_check_client_match(data_conn_fd, connfd, tcp_serv.sin_port, client_id) < 0) {
      printf("bad match\n");
    } else
      proxy_handle_data_transfer(connfd, data_conn_fd);

    close(data_conn_fd);
  }

  close(connfd);
  close(new_incoming_sock);

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_handle_control_connection ( int  connfd  ) 

Handles a server's request for the proxy to handle connections on its behalf.

Parameters:
connfd -- socket on which the server is connected
Returns:
0 on success, -1 on failure

Definition at line 1301 of file proxy_server.c.

{
  COMPONENT *component;
  char component_tag[sizeof(CID) + sizeof(in_port_t) + 1];

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

  printf("i am a thread handling a control connection %d\n", connfd);

  component = proxy_init_server_component(connfd, component_tag);
  if (!component) {
    fprintf(stderr, "Error intializing server component.\n");
    close(connfd);
    return -1;
  }

  for (;;) {
    COMPONENT *item;

    item = proxy_wait_for_queue_message(connfd, component);

    if (!item) {
      close(connfd);
      return -1;
    }

    if (proxy_send_conn_request_to_server(connfd, component_tag, component, item) < 0) {
      free(item);
      close(connfd);
      return -1;
    }

    /* free the queue item.  the msg_queue field isn't initialized for queue items, so
     * there's no need to dispose of it here. */
    free(item);
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_handle_data_transfer ( int  client_fd,
int  serv_fd 
)

Once the client and server are both connected to the proxy, we write anything received on one connection to the other connection. This simulates a direct connection from client to server.

Parameters:
client_fd -- descriptor for the client connection
serv_fd -- descriptor for the server connection
Returns:
0 on success, -1 on failure

Definition at line 835 of file proxy_server.c.

{
  int maxfd, nready;
  fd_set allset, rset;

  maxfd = serv_fd > client_fd ? serv_fd : client_fd;

  FD_ZERO(&allset);
  FD_SET(serv_fd, &allset);
  FD_SET(client_fd, &allset);

  for (;;) {
    int n;
    char buf[PROXY_XFER_CHUNKSIZE];

    rset = allset;

    nready = select(maxfd + 1, &rset, NULL, NULL, NULL);

    if (nready < 0) {

      if (errno == EINTR)
    continue;

      return -1;
    }

    if (nready == 0)
      continue;

    if (FD_ISSET(client_fd, &rset)) {
      n = proxy_read_timeout(client_fd, buf, PROXY_XFER_CHUNKSIZE, 
        PROXY_TIMEOUT_DEFAULT);

      if (n <= 0) {
    printf("seems the requesting host (client) broke connection\n");
    break;
      }

      n = write(serv_fd, buf, n);
    } else if (FD_ISSET(serv_fd, &rset)) {
      n = proxy_read_timeout(serv_fd, buf, PROXY_XFER_CHUNKSIZE, 
        PROXY_TIMEOUT_DEFAULT);

      if (n <= 0) {
    printf("seems the proxied host (server) broke connection\n");
    break;
      }

      n = write(client_fd, buf, n);
    }
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

COMPONENT* proxy_init_server_component ( int  connfd,
char *  component_tag 
)

Initializes a component struct to hold information about a server that has just established a control connection.

Parameters:
connfd -- socket on which the server is connected
component_tag -- the component ID and port pair that is unique to this server (set upon return)
Returns:
pointer to newly created component struct

Definition at line 1055 of file proxy_server.c.

{
  COMPONENT *component;
  CID server_id;
  in_port_t server_port;
  int id_size, to;
  int port_size;

  to = PROXY_TIMEOUT_DEFAULT;
  id_size = sizeof(server_id.id);
  port_size = sizeof(server_port);

  component = proxy_new_component();
  if (component)
    component->msg_queue = new_queue();

  if (!component || !component->msg_queue) {
    fprintf(stderr, "Cannot malloc, aborting connection\n");
    if (component) {
      if (component->msg_queue)
    destroy_queue(component->msg_queue);
      free(component);
    }
    return NULL;
  }

  printf("id size = %d, port size = %d\n", id_size, port_size);

  if (proxy_tread(connfd, (void *) server_id.id, id_size, to) < 0 ||
      proxy_tread(connfd, (void *) &server_port, port_size, to) < 0) {
    if (component->msg_queue)
      destroy_queue(component->msg_queue);
    free(component);
    return NULL;
  }

  printf("port = %d\n", ntohs(server_port));

  /* component_tag is composed of CID/port */
  memcpy(component_tag, server_id.id, id_size);
  memcpy(component_tag + id_size, &server_port, port_size);
  *(component_tag + id_size + port_size) = '\0';

  proxy_print_component_tag(stdout, "component_tag =", component_tag, NULL);

  component->id = server_id;
  component->port = server_port;
  component->sockfd = connfd;

  if (pthread_mutex_lock(&accept_tab_mutex) == 0) {
    HASHNODE *ht;

    ht = hash_lookup(accept_table, (void *) component_tag);

    if(ht) {
      proxy_print_component_tag(stderr, "Component ", component_tag,
        " already exists in the table!\n");
      pthread_mutex_unlock(&accept_tab_mutex);
      return NULL;
    }

    hash_insert(accept_table, (void *) component, (void *) component_tag);
    pthread_mutex_unlock(&accept_tab_mutex);
  } else {
    if (component->msg_queue)
      destroy_queue(component->msg_queue);
    free(component);
    return NULL;
  }

  return component;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_init_sock ( int *  sfd,
struct sockaddr_in *  serv,
char *  proto,
int  nb,
int  port 
)

Initializes the specified socket, getting the port number assigned to that socket. set the socket for non-blocking i/o and set socket option to allow reuse of the port number and optionally set to non-blocking i/o.

Parameters:
sfd -- the initialized socket descriptor (set upon return)
serv -- sockaddr structure containing the usual information (set upon return)
proto -- string representing the protocol to use ("tcp" or "udp")
nb -- if TRUE, set the socket non-blocking. if FALSE set blocking.
port -- the port to which we should bind
Returns:
0 on success, -1 on failure

Definition at line 1359 of file proxy_server.c.

{
  int r, flag = 1;
  socklen_t namelen;
  char istcp = strcmp(proto, "udp");

  if ((*sfd = socket(AF_INET, istcp ? SOCK_STREAM : SOCK_DGRAM, 0)) < 0)
    return *sfd;

  if ((r = setsockopt(*sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(flag))) < 0)
    return r;

  if(istcp)
    setsockopt(*sfd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));

  serv->sin_family = AF_INET;
  serv->sin_addr.s_addr = htonl(INADDR_ANY);
  serv->sin_port = htons(port);

  if ((r = bind(*sfd, (struct sockaddr *) serv, sizeof(*serv))) < 0)
    return r;

  namelen = sizeof(*serv);
  if ((r = getsockname(*sfd, (struct sockaddr *) serv, &namelen)) < 0)
    return r;

  if (nb)
    if ((r = fcntl(*sfd, F_SETFL, O_NDELAY)) < 0)   /* make socket non-blocking */
      return r;

  return 0;
}

Here is the caller graph for this function:

COMPONENT* proxy_new_component (  ) 

Creates a new component struct, which is used to store information about a component that has connected.

Returns:
pointer to the new component struct

Definition at line 612 of file proxy_server.c.

{
  COMPONENT *new;

  new = (COMPONENT *) malloc(sizeof(COMPONENT));

  if (!new)
    return NULL;

  new->id.id[0] = '\0';
  new->port = 0;
  new->sockfd = 0;
  new->to_be_freed = FALSE;
  new->msg_queue = NULL;

  return new;
}

Here is the caller graph for this function:

void proxy_print_component_tag ( FILE *  f,
char *  prefix,
char *  ctag,
char *  suffix 
)

Prints component info to the specified file.

Parameters:
f -- file pointer to use as output
prefix -- stuff to be printed before the ID info
ctag -- the component to print
suffix -- stuff to be printed after the ID info

Definition at line 68 of file proxy_server.c.

{
  int id_size, port_size;
  in_port_t server_port;
  CID server_id;

  id_size = sizeof(server_id.id);
  port_size = sizeof(server_port);

  memcpy(&server_port, ctag + id_size, port_size);

  if(prefix)
    fprintf(f, "%s ", prefix);
  proxy_print_componentID(f, ctag);
  fprintf(f, "/%d", ntohs(server_port));
  if(suffix)
    fprintf(f, " %s", suffix);
  fprintf(f, "\n");
}

Here is the call graph for this function:

Here is the caller graph for this function:

void* proxy_purge_old_components ( void *  arg  ) 

This function periodically checks whether there are unused component structures that need to be freed. we don't free them immediately because another thread could have gotten a pointer just before we free it and cause problems.

Parameters:
arg -- this argument is here to match the prototype for the function argument to pthread_create, but it is unused
Returns:
this function should never return

Definition at line 516 of file proxy_server.c.

{
  COMPONENT *component;

  for(;;) {
    sleep(PROXY_PURGE_FREQ);

    if (pthread_mutex_lock(&accept_tab_mutex) == 0) {
      printf("\n");
      printf("--------- ACCEPT TABLE [%d items] ---------\n", accept_table->num_items);
      hash_dump(accept_table, dump_component);
      printf("------------------------------------------\n");
      printf("\n");

      pthread_mutex_unlock(&accept_tab_mutex);
    }

    if(pthread_mutex_lock(removed_components_queue->mutex) == 0) {

      /* free every component in the queue */

      while((component = (COMPONENT *)dequeue(removed_components_queue))) {
        destroy_queue(component->msg_queue);
        free(component);
      }

      pthread_mutex_unlock(removed_components_queue->mutex);
    } else {
      fprintf(stderr, "Warning: couldn't lock removed components queue.\n");
    }
  }

  return NULL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_send_cid_to_control_conn ( int  s,
COMPONENT *  c,
CID  client_id,
in_port_t  port 
)

Notifies a component that a client would like to establish a connection to it. This is done using a message queue into which the component information is placed. The thread handling the control connection for the server component is notified of the connection request and it retrieves the information from the queue.

Parameters:
s -- socket on which the client is connected
c -- information about the server component to be contacted
client_id -- component ID for the source (the client)
port -- the proxy port (already) opened to handle the second connection from the server component
Returns:
0 on success, -1 on failure

Definition at line 719 of file proxy_server.c.

{
  /* must enqueue request to control connection and signal notempty */

  printf("waiting for msg queue lock\n");

  if (pthread_mutex_lock(c->msg_queue->mutex) == 0) {
    COMPONENT *client_component;

    client_component = proxy_new_component();

    /* make sure this component isn't destined to be freed */
    if (c->to_be_freed || !client_component) {
      proxy_tag_t response = PROXY_CONNECTION_REFUSED;

      if (client_component)
    free(client_component);
      pthread_mutex_unlock(c->msg_queue->mutex);
      write(s, &response, sizeof(response));
      return -1;
    }

    memcpy(client_component->id.id, client_id.id, sizeof(client_id.id));
    client_component->port = port;

    printf("putting component in queue\n");

    enqueue(c->msg_queue, (void *) client_component);
    pthread_cond_signal(c->msg_queue->notEmpty);
    pthread_mutex_unlock(c->msg_queue->mutex);
  } else {
    fprintf(stderr, "Error locking.\n");
    return -1;
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_send_conn_request_to_server ( int  connfd,
char *  component_tag,
COMPONENT *  component,
COMPONENT *  item 
)

Sends a message over the control connection to the server component requesting that it accept a new connection from the client.

Parameters:
connfd -- socket on which the server is connected
component_tag -- the component tag (ID/port pair) for this server
component -- the component struct for this server
item -- the component struct for the client that wishes to connect
Returns:
0 on success, -1 on failure

Definition at line 1222 of file proxy_server.c.

{
  proxy_tag_t response = PROXY_CONNECT_REQUEST;

  printf("going to write to the server now (port = %d)...\n", ntohs(item->port));
  if (write(connfd, &response, sizeof(response)) < 0 ||
      write(connfd, item->id.id, sizeof(item->id.id)) < 0 ||
      write(connfd, &item->port, sizeof(item->port)) < 0) {
    fprintf(stderr, "Can't write to server, aborting.\n");

    proxy_delete_server_tag(component_tag, component);

    return -1;
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_server_parse_cmd_line ( int  argc,
char **  argv,
char **  logfile,
int *  daemon,
int *  kerberos 
)

Parse the command line for flags passed to the proxy server. Currently the syntax is: proxy_server [-l logfile] [-c] [-k]

Parameters:
argc -- arg count
argv -- array of arguments
logfile -- if a -l arg is seen this is set to the name of the log file upon return, otherwise it is set to NULL. The user need not allocate any memory.
daemon -- set to 1 upon return if the server should be a daemon
kerberos -- set to 1 upon return if the server should use kerberos
Returns:
0 if the args parsed successfully, -1 otherwise.

Definition at line 251 of file proxy_server.c.

{
  int c;

  *logfile = NULL;
  *daemon = 1;
  *kerberos = 0;

  /* when making changes to the command line args, update
   * PROXY_SERVER_USAGE_STR so the usage information is printed
   * correctly upon error.
   */

#define PROXY_SERVER_USAGE_STR "Usage: proxy_server [-l logfile] [-c] [-k]"

  while((c = getopt(argc,argv,"kcl:")) != EOF) {
    switch(c) {
      case 'l':
        *logfile = strdup(optarg);
        break;
      case 'c':
        *daemon = 0;
        break;
      case 'k':
        *kerberos = 1;
        break;
      case '?':
        return -1;
        break;
      default:
        fprintf(stderr,"Bad arg: '%c'.\n",c);
        return -1;
    }
  }

  return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

COMPONENT* proxy_wait_for_queue_message ( int  connfd,
COMPONENT *  component 
)

Waits for notification that a client wants to connect to the server component. This is done via a message queue. The thread handling the client request puts the request information into the queue and notifies us that a new entry has been added.

Parameters:
connfd -- socket on which the server is connected
component -- the component struct for this server
Returns:
pointer to the component struct received from the client via the message queue

Definition at line 1142 of file proxy_server.c.

{
  COMPONENT *item;
  int keepalive_timer;

  keepalive_timer = PROXY_KEEPALIVE_FREQ;

  if (pthread_mutex_lock(component->msg_queue->mutex) == 0) {
    while (is_empty(component->msg_queue)) {
      struct timespec timeout;
      struct timeval tv;
      int rv;

      gettimeofday(&tv, NULL);

      timeout.tv_sec = tv.tv_sec + PROXY_MSG_QUEUE_FREQ;
      timeout.tv_nsec = 0;

      /* wait until a message is placed in the queue or until the timeout elapses. */
      rv = pthread_cond_timedwait(component->msg_queue->notEmpty,
                  component->msg_queue->mutex, &timeout);

      keepalive_timer -= PROXY_MSG_QUEUE_FREQ;

      if (rv != 0) {
        int remove_srv = 0;

    /* if we timed out, check if the server is still there. */

        if(keepalive_timer <= 0) {
          keepalive_timer = PROXY_KEEPALIVE_FREQ;
      remove_srv = (proxy_send_keepalive(connfd) < 0);
        }
        else {
      remove_srv = (proxy_is_something_on_socket(connfd) == 0);
        }

    if(remove_srv) {
      char component_tag[sizeof(CID) + sizeof(in_port_t) + 1];
      int id_size = sizeof(component->id);
      int port_size = sizeof(component->port);

      printf("server must have gone away!\n");

      /* component_tag is composed of CID/port */
      memcpy(component_tag, &(component->id), id_size);
      memcpy(component_tag + id_size, &(component->port), port_size);
      *(component_tag + id_size + port_size) = '\0';

      proxy_delete_server_tag(component_tag, component);
          close(component->sockfd);

      return NULL;
    }
      }
    }
    item = (COMPONENT *) dequeue(component->msg_queue);
    pthread_mutex_unlock(component->msg_queue->mutex);
  } else
    return NULL;

  return item;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int proxy_wait_for_server_conn ( int  connfd,
int  sock2 
)

Waits for the server component to establish another connection to the proxy. This is done in response to a connection request from a client component. Since the component behind the NAT can't receive incoming connections, we must notify it via the persistent control connection that a connection request has been received. We then open another port for the server component to connect to and tie that socket to the client connection already established.

Parameters:
connfd -- socket on which the client is connected
sock2 -- the socket opened to handle the second connection from the server component
Returns:
the descriptor for the second connection or -1 on failure.

Definition at line 775 of file proxy_server.c.

{
  int maxfd, nready;
  fd_set allset, rset;
  struct timeval tv;
  int newfd = -1;
  struct sockaddr_in cliaddr;
  socklen_t clilen;

  /* now listen for incoming connection from server behind nat */

  maxfd = sock2;
  FD_ZERO(&allset);
  FD_SET(sock2, &allset);

  rset = allset;
  tv.tv_sec = 15;
  tv.tv_usec = 0;

  printf("waiting for 2nd connection\n");
  nready = select(maxfd + 1, &rset, NULL, NULL, &tv);

  if (nready <= 0) {
    proxy_tag_t response = PROXY_CONNECTION_REFUSED;

    printf("select error %d (0 means timed out)\n", nready);
    write(connfd, &response, sizeof(response));
    return -1;
  }

  if (FD_ISSET(sock2, &rset)) {
    clilen = sizeof(cliaddr);

    printf("something happened on the second listening socket!\n");

    newfd = accept(sock2, (struct sockaddr *) &cliaddr, &clilen);

    printf("after accept, new fd = %d\n", newfd);

    if (proxy_auth(newfd) < 0) {
      fprintf(stderr, "Warning: failed to authenticate client.\n");
      close(newfd);
    }
  }

  return newfd;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

pthread_mutex_t accept_tab_mutex

Definition at line 55 of file proxy_server.c.

SYMTABLE* accept_table

Definition at line 54 of file proxy_server.c.

int proxy_krb5_enabled = FALSE

Definition at line 203 of file proxy_server.c.

Definition at line 56 of file proxy_server.c.