Logo Search packages:      
Sourcecode: afbackup version File versions

netutils.c

/****************** Start of $RCSfile: netutils.c,v $  ****************
*
* $Source: /home/alb/afbackup/afbackup-3.3.8beta7/RCS/netutils.c,v $
* $Id: netutils.c,v 1.4 2004/07/08 20:34:44 alb Exp alb $
* $Date: 2004/07/08 20:34:44 $
* $Author: alb $
*
*
******* description ***********************************************
*
*
*
*******************************************************************/

#include <conf.h>
#include <version.h>

  static char * fileversion = "$RCSfile: netutils.c,v $ $Source: /home/alb/afbackup/afbackup-3.3.8beta7/RCS/netutils.c,v $ $Id: netutils.c,v 1.4 2004/07/08 20:34:44 alb Exp alb $ " PACKAGE " " VERSION_STRING;

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#ifdef      HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <netinet/in.h>
#include <netdb.h>
#ifdef      HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif
#ifdef      HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#ifdef      HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef      HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <x_types.h>
#include <sysutils.h>
#include <netutils.h>
#include <fileutil.h>
#include <genutils.h>
#ifdef      HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#define     CLEANUPR(ret)     { r = (ret); goto cleanup; }
#define     CLEANUP           { goto cleanup; }
#define     GETOUT            { goto getout; }


#define     EMPTY_USER_DATA   ((void *) 1)

typedef struct _tcpmux_status {
  int       listensock;
  int       unixsock;
  Int32           maxconn;
  Int32           num_conns;
  void            **conn_user_data;
  int       *conns;
  FILE            **conn_fps;
  fd_set    allfds;
  int       maxfd;
  Flag            *closed_conns;
  Flag            have_closed_conns;
  void            (*failed_conn_func)(int, void *, void *);
  void            *(*conn_init_func)(int, Int32, void *, struct sockaddr *,
                        void *, TcpMuxCallbDoneActions *);
  void            *data;
  Uns32           flags;
  void            *conn_user_data_to_be_freed;
} TcpMuxStatus;


struct hostent *
get_host_by_name(UChar * name)
{
#if   defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_IP6)
  int       error_num;
  struct hostent  *he;

      /* None of the flags (arg 3) does, what i consider appropriate. */
      /* i guess, most of the networks will move from IP4 to IP6, thus */
      /* introduce new IP6 nodes, that should be able to communicate */
      /* with old and existing IP4 nodes. If there are new IP6 nodes, */
      /* having also a IP4 address, the latter one can happily be used. */
      /* If others should use IP6 to communicate with the host, that */
      /* still has also an IP4 entry, the administrator should gefaelligst */
      /* delete the IP4 entry, because then it makes no sense any more. */
      /* Thus i implement it explicitely requiring 2 lookups :-( */
  he = getipnodebyname(name, AF_INET, 0, &error_num);
  if(!he){
    he = getipnodebyname(name, AF_INET6, 0, &error_num);
    errno = error_num;
  }

  return(he);
#else
  return(gethostbyname(name));
#endif
}

struct hostent *
get_host_by_addr(void * addr, int len, int type)
{
#if   defined(HAVE_GETIPNODEBYADDR) && defined(HAVE_IP6)
  int       error_num;
  struct hostent  *he;

  he = getipnodebyaddr(addr, len, type, &error_num);
  if(!he)
    errno = error_num;

  return(he);
#else
  return(gethostbyaddr(addr, len, type));
#endif
}

struct hostent *
get_host_by_sockaddr(struct sockaddr * addr)
{
  if(((struct sockaddr *)addr)->sa_family == AF_INET)
    return(get_host_by_addr(&(((struct sockaddr_in *)addr)->sin_addr),
                  sizeof(struct in_addr), AF_INET));

#ifdef      HAVE_IP6
  if(((struct sockaddr *)addr)->sa_family == AF_INET6)
    return(get_host_by_addr(&(((struct sockaddr_in6 *)addr)->sin6_addr),
                  sizeof(struct in6_addr), AF_INET6));
#endif

  return(NULL);
}

static Flag
accept_connection(TcpMuxStatus * status, Flag in_subr, Flag uxsock)
{
  int       fl, fd, sock;
  Flag            end = NO;
  Int32           sock_optlen[2];
#define     addr_len sock_optlen
  nodeaddr  peeraddr;
  TcpMuxCallbDoneActions      init_done_actions;

  sock = (uxsock ? status->unixsock : status->listensock);

  fl = fcntl(sock, F_GETFL);
  fcntl(sock, F_SETFL, fl | NONBLOCKING_FLAGS);

  *((int *) &(addr_len[0])) = sizeof(peeraddr);
  fd = accept(sock, (struct sockaddr *)(&peeraddr), (int *) &(addr_len[0]));
  if(fd < 0 || (status->maxconn > 0 && status->num_conns + 1 > status->maxconn)){
    if(status->failed_conn_func)
      status->failed_conn_func(fd >= 0 ? fd : sock, status->data, status);

    if(fd >= 0)
      close(fd);
  }
  else{
    fcntl(sock, F_SETFL, fl & INV_NONBLOCKING_FLAGS);

    fl = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, fl & INV_NONBLOCKING_FLAGS);

    fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | 1);

    if(in_subr && ! status->conn_user_data_to_be_freed){
            /* the memory space must be valid until exit from input_proc_func, */
            /* cause it might be in use by that function, so we store it in a */
            /* status field and free it later */
      status->conn_user_data_to_be_freed = status->conn_user_data;

      status->conn_user_data = NEWP(void *, status->num_conns + 2);

      memcpy(status->conn_user_data, status->conn_user_data_to_be_freed,
                  sizeof(void *) * (status->num_conns + 1));
    }
    else{
      status->conn_user_data = ZRENEWP(status->conn_user_data, void *, status->num_conns + 2);
    }

    status->conns = ZRENEWP(status->conns, int, status->num_conns + 1);
    status->closed_conns = ZRENEWP(status->closed_conns, Flag, status->num_conns + 1);
    status->conn_fps = ZRENEWP(status->conn_fps, FILE *, status->num_conns + 1);

    status->closed_conns[status->num_conns] = NO;
    status->conn_user_data[status->num_conns + 1] = NULL;

    if(status->conn_init_func){
      SETZERO(init_done_actions);
      init_done_actions.conns_to_close = status->closed_conns;

      status->conn_user_data[status->num_conns] = (*status->conn_init_func)(fd,
                  status->num_conns, &status->conn_user_data,
                  (struct sockaddr *)(&peeraddr), status->data,
                  &init_done_actions);
      if(!status->conn_user_data[status->num_conns]){
        close(fd);
        fd = -1;
        if(status->num_conns < 1 && (status->flags & TCPMUX_STOP_ON_LAST_CLOSE))
          end = YES;
      }

      if(init_done_actions.have_conns_to_close)
        status->have_closed_conns = YES;
    }
    else
      status->conn_user_data[status->num_conns] = EMPTY_USER_DATA;

    if(fd >= 0){
      set_socket_keepalive(fd);

      status->conns[status->num_conns] = fd;
      status->conn_fps[status->num_conns] = fdopen(fd, "r+");
      FD_SET(fd, &status->allfds);
      if(fd > status->maxfd)
        status->maxfd = fd;

      status->num_conns++;
    }
  }

  return(end);
}

Int32
tcp_mux_long_io(
  void *    tmstatptr,
  int       fd,
  UChar *   data,
  Int32           num,
  Int32           do_write,
  Int32           (*rw_func)(int, UChar *, Int32))
{
  fd_set    rfds, wfds;
  int       sock, uxsock;
  Int32           i, n;
  TcpMuxStatus    *tcpmux_status;
  Flag            lastconn;

  if(!tmstatptr)
    return((*rw_func)(fd, data, num));

  tcpmux_status = (TcpMuxStatus *) tmstatptr;
  sock = tcpmux_status->listensock;
  uxsock = tcpmux_status->unixsock;

  n = MAX(fd, sock) + 1;

  forever{
    FD_ZERO(&rfds);
    FD_ZERO(&wfds);
    if(sock >= 0)
      FD_SET(sock, &rfds);
    if(uxsock >= 0)
      FD_SET(uxsock, &rfds);
    FD_SET(fd, (do_write ? (&wfds) : (&rfds)));
    i = select(n, &rfds, &wfds, NULL, NULL);
    if(i <= 0)
      return(i);

    if(FD_ISSET(fd, (do_write ? (&wfds) : (&rfds))))
      return((*rw_func)(fd, data, num));

    lastconn = NO;
    if(sock >= 0)
      if(FD_ISSET(sock, &rfds))
      lastconn = (lastconn || accept_connection(tmstatptr, YES, NO));
    if(uxsock >= 0)
      if(FD_ISSET(uxsock, &rfds))
      lastconn = (lastconn || accept_connection(tmstatptr, YES, YES));

    if(lastconn)
      fprintf(stderr, "Internal error in netutils.c, 1.\n");
  }
}

Int32
tcp_mux_service(
  int       port,
  UChar           *ux_socket,
  void            *(*conn_init_func)(int, Int32, void *, struct sockaddr *, void *, TcpMuxCallbDoneActions *),
  Int32           (*input_proc_func)(int, void *, Int32, void *, TcpMuxCallbDoneActions *, void *),
  Int32           (*conn_deinit_func)(int, void *, Int32, void *, void *),
  Int32           maxconn,
  void            (*failed_conn_func)(int, void *, void *),
  Uns32           flags,
  void            *data)
{
  Int32           i, j;
  Int32           r = 0;
  Int32           actconn;
  nodeaddr  addr;
  int       sock_optval, sockfd_socktype;
  int       sock_typesize, sock_family;
  Int32           sock_optlen[2];
  Flag            end = NO;
  fd_set    tmpfds;
  TcpMuxStatus    status;
  TcpMuxCallbDoneActions      input_done_actions;
  struct sigaction      pipe_sigact, org_sigact;

  SETZERO(status);
  status.maxconn = maxconn;
  status.failed_conn_func = failed_conn_func;
  status.conn_init_func = conn_init_func;
  status.data = data;
  status.flags = flags;
  status.listensock = -1;
  status.unixsock = -1;

  SETZERO(pipe_sigact);
  pipe_sigact.sa_handler = SIG_IGN;
#ifdef      SA_RESTART
  pipe_sigact.sa_flags = SA_RESTART;
#endif
  sigaction(SIGPIPE, &pipe_sigact, &org_sigact);

  if(flags & TCPMUX_INETD_STARTED){
    status.listensock = 0;
  }
  else{
#ifdef      HAVE_IP6
    status.listensock = socket(sock_family = AF_INET6, SOCK_STREAM, 0);
    if(status.listensock < 0)
#endif
     status.listensock = socket(sock_family = AF_INET, SOCK_STREAM, 0);
    if(status.listensock >= 0){
      *((int *) &(sock_optlen[0])) = sizeof(int);
      i = getsockopt(status.listensock, SOL_SOCKET, SO_TYPE,
                  (char *) &sockfd_socktype, (int *) &(sock_optlen[0]));
      if(i)
        sockfd_socktype = -1;

      sock_optval = 1;
      if(sockfd_socktype == SOCK_STREAM){
        i = setsockopt(status.listensock, SOL_SOCKET, SO_REUSEADDR,
                        (char *) &sock_optval, sizeof(int));
        /* i is not evaluated: if set REUSEADDR fails, the bind
            below fails. That's sufficient */
      }

#ifdef      HAVE_IP6
      i = -1;
      if(sock_family == AF_INET6){
        ((struct sockaddr *)(&addr))->sa_family = AF_INET6;
        ((struct sockaddr_in6 *)(&addr))->sin6_port = htons(port);
        SETZERO(((struct sockaddr_in6 *)(&addr))->sin6_addr.s6_addr);
        sock_typesize = sizeof(struct sockaddr_in6);

        i = bind(status.listensock, (struct sockaddr *)(&addr), sock_typesize);
      }
      if(i){
#endif

      ((struct sockaddr *)(&addr))->sa_family = AF_INET;
      ((struct sockaddr_in *)(&addr))->sin_port = htons(port);
      SETZERO(((struct sockaddr_in *)(&addr))->sin_addr.s_addr);
      sock_typesize = sizeof(struct sockaddr_in);

      if( (i = bind(status.listensock, (struct sockaddr *)(&addr), sock_typesize)) ){
        close(status.listensock);
        status.listensock = -1;
      }
    
#ifdef      HAVE_IP6
      }
#endif

      if(!i){
        if(listen(status.listensock, 64)){
          close(status.listensock);
          status.listensock = -1;
        }
      }
    }
  }

  if(ux_socket){
    status.unixsock = create_unix_socket(ux_socket);
    if(status.unixsock >= 0){
      fcntl(status.unixsock, F_SETFD, fcntl(status.unixsock, F_GETFD) | 1);

      i = listen(status.unixsock, 64);
      if(i){
        close(status.unixsock);
        status.unixsock = -1;
      }
    }
  }

  if(status.listensock < 0 && status.unixsock < 0)
    CLEANUPR(-1);

  if(status.listensock >= 0)
    fcntl(status.listensock, F_SETFD, fcntl(status.listensock, F_GETFD) | 1);

  FD_ZERO(&status.allfds);
  if(status.listensock >= 0)
    FD_SET(status.listensock, &status.allfds);
  if(status.unixsock >= 0)
    FD_SET(status.unixsock, &status.allfds);

  status.maxfd = MAX(status.listensock, status.unixsock);

  actconn = status.num_conns; /* force accept first */

  do{
    status.have_closed_conns = NO;

    COPYVAL(tmpfds, status.allfds);
    i = select(status.maxfd + 1, &tmpfds, NULL, NULL, 0);

    memset(status.closed_conns, 0, status.num_conns * sizeof(status.closed_conns[0]));

    for(i = -1; i < status.num_conns; i++){     /* one more because of */
      if(actconn >= status.num_conns){    /* rotating accept and */
        if(status.listensock >= 0){
          if(FD_ISSET(status.listensock, &tmpfds)){   /* continue */
            if(accept_connection(&status, NO, NO))
            end = YES;
          }
        }
        if(status.unixsock >= 0){
          if(FD_ISSET(status.unixsock, &tmpfds)){     /* continue */
            if(accept_connection(&status, NO, YES))
            end = YES;
          }
        }

        actconn = 0;

        continue;
      }

      if(FD_ISSET(status.conns[actconn], &tmpfds)){
        if(input_proc_func){
          SETZERO(input_done_actions);
          input_done_actions.conns_to_close = status.closed_conns;

          j = (*input_proc_func)(status.conns[actconn],
                        status.conn_user_data + actconn, actconn, data,
                        &input_done_actions, &status);

          if(j)
            status.closed_conns[actconn] = status.have_closed_conns = YES;

          ZFREE(status.conn_user_data_to_be_freed);

          if(input_done_actions.have_conns_to_close)
            status.have_closed_conns = YES;
        }
      }

      actconn++;
    }

    if(status.have_closed_conns){
      for(i = 0; i < status.num_conns; i++){
      if(status.closed_conns[i]){
        if(conn_deinit_func){
          j = (*conn_deinit_func)(status.conns[i], status.conn_user_data + i, i,
                              data, &status);
        }

        FD_CLR(status.conns[i], &status.allfds);

        if(status.conn_fps[i])
          fclose(status.conn_fps[i]);
        else
          close(status.conns[i]);

        if(status.conns[i] == status.maxfd)
          status.maxfd--;

        memmove(status.conns + i, status.conns + i + 1,
                  (status.num_conns - i - 1) * sizeof(status.conns[0]));
        memmove(status.conn_fps + i, status.conn_fps + i + 1,
                  (status.num_conns - i - 1) * sizeof(status.conn_fps[0]));
        memmove(status.closed_conns + i, status.closed_conns + i + 1,
                  (status.num_conns - i - 1) * sizeof(status.closed_conns[0]));
        memmove(status.conn_user_data + i, status.conn_user_data + i + 1,
                  (status.num_conns - i) * sizeof(status.conn_user_data[0]));

        i--;
        status.num_conns--;

        if(status.num_conns < 1 && (flags & TCPMUX_STOP_ON_LAST_CLOSE)){
          end = YES;
          break;
        }
      }
      }
    }
  } while(! end);

 cleanup:

  if(status.listensock >= 0)
    close(status.listensock);
  if(status.unixsock >= 0)
    close(status.unixsock);

  ZFREE(status.conns);
  ZFREE(status.conn_fps);
  ZFREE(status.closed_conns);
  ZFREE(status.conn_user_data);

  sigaction(SIGPIPE, &org_sigact, NULL);

  return(r);
}

/*
 * Use configured name services to get our official host name.  Returns
 * a malloc'd string on success, NULL on failure.
 */

UChar *
get_my_off_hn()
{
  struct utsname  unamb;
  struct hostent  *hp;

  if(uname(&unamb) < 0)
    return(NULL);

  hp = get_host_by_name(unamb.nodename);
  if(!hp)
    return(NULL);

  return(strdup(hp->h_name));
}

Int8
same_host(UChar * hn1, UChar * hn2)
{
  struct hostent  *hep1, *hep2;
  Int32           i, j, n1, n2, l1, l2;
  Int8            r = 0, ce;
  char            *addresses1 = NULL, *addresses2 = NULL;

  if(! hn1 || ! hn2){
    errno = EINVAL;
    return(-1);
  }

  ce = (strcmp(hn1, hn2) ? 0 : 2);

  if(ce)
    return(ce);

  hep1 = get_host_by_name(hn1);
  if(!hep1)
    return(ce);

  l1 = hep1->h_length;
  for(n1 = 0; hep1->h_addr_list[n1]; n1++);
  addresses1 = NEWP(UChar, n1 * l1);
  if(!addresses1){
    r = - errno;
    GETOUT;
  }

  for(i = 0; i < n1; i++)
    memcpy(addresses1 + i * l1, hep1->h_addr_list[i], l1);

  hep2 = get_host_by_name(hn2);
  if(!hep2){
    r = ce;
    CLEANUP;
  }

  l2 = hep2->h_length;
  for(n2 = 0; hep2->h_addr_list[n2]; n2++);
  addresses2 = NEWP(UChar, n2 * l2);
  if(!addresses2){
    r = - errno;
    GETOUT;
  }

  for(i = 0; i < n2; i++)
    memcpy(addresses2 + i * l2, hep2->h_addr_list[i], l2);

  if(l1 != l2){
    r = ce;
    CLEANUP;
  }

  for(i = 0; i < n1; i++){
    for(j = 0; j < n2; j++){
      if(! memcmp(addresses1 + i * l1, addresses2 + j * l2, l1)){
        r = 1;
        i = n1;
        break;
      }
    }
  }

 cleanup:
  ZFREE(addresses1);
  ZFREE(addresses2);

  return(r);

 getout:
  CLEANUP;
}

/*
 * Takes <servicename>, which may either be a number or a name as specified
 * in /etc/services or in a name service (according to /etc/nsswitch.conf),
 * and returns the associated port number as an int, always for protocol TCP
 * Returns a value < 0 on error.
 */

int
get_tcp_portnum(UChar * servicename)
{
  struct servent  *se;
  int             n, p, len, l;
  UChar                 *sn_cp;

  if(!servicename){
    errno = EINVAL;
    return(-1);
  }

  sn_cp = strdup(servicename);
  if(!sn_cp)
    return(-1);

  massage_string(sn_cp);
  len = strlen(sn_cp);

  l = -1;
  n = sscanf(sn_cp, "%d%n", &p, &l);
  free(sn_cp);

  if(n > 0 && len == l)
    return(p);

  se = getservbyname(servicename, "tcp");
  if(!se)
    return(-1);

  return(ntohs(se->s_port));
}

Int32
anon_tcp_sockaddr(
  struct sockaddr *addr,
  Int32           *sockaddrsize,
  int       addrfamily)
{
  if(addrfamily != AF_INET
#ifdef      HAVE_IP6
                  && addrfamily != AF_INET6
#endif
                                          )
    return(-5);

  if(addrfamily == AF_INET){
    if(addr){
      SETZERO(((struct sockaddr_in *)addr)[0]);
      ((struct sockaddr_in *)addr)->sin_port = htons(0);
    }
    if(sockaddrsize)
      *sockaddrsize = sizeof(struct sockaddr_in);
  }
#ifdef      HAVE_IP6
  if(addrfamily == AF_INET6){
    if(addr){
      SETZERO(((struct sockaddr_in6 *)addr)[0]);
      ((struct sockaddr_in6 *)addr)->sin6_port = htons(0);
    }
    if(sockaddrsize)
      *sockaddrsize = sizeof(struct sockaddr_in6);
  }
#endif

  if(addr)
    addr->sa_family = addrfamily;

  return(0);
}

void *
inaddr_from_sockaddr(struct sockaddr * sockaddr, Int32 * addrsize)
{
  void            *retaddr = NULL;

  if(sockaddr->sa_family != AF_INET
#ifdef      HAVE_IP6
                  && sockaddr->sa_family != AF_INET6
#endif
                                          )
    return(NULL);

  if(sockaddr->sa_family == AF_INET){
    retaddr = &(((struct sockaddr_in *)sockaddr)->sin_addr);
    if(addrsize)
      *addrsize = sizeof(struct in_addr);
  }
#ifdef      HAVE_IP6
  if(sockaddr->sa_family == AF_INET6){
    retaddr = &(((struct sockaddr_in6 *)sockaddr)->sin6_addr);
    if(addrsize)
      *addrsize = sizeof(struct in6_addr);
  }
#endif

  return(retaddr);
}

Int32
set_tcp_sockaddr_hp(
  struct sockaddr *addr,
  struct hostent  *hp,
  int       portnum)
{
  if(hp->h_addrtype != AF_INET
#ifdef      HAVE_IP6
                  && hp->h_addrtype != AF_INET6
#endif
                                          )
    return(-5);

  addr->sa_family = hp->h_addrtype;

  if(hp->h_addrtype == AF_INET){
    if(hp)
      memcpy(&(((struct sockaddr_in *)addr)->sin_addr),
                              hp->h_addr, hp->h_length);
    if(portnum >= 0)
      ((struct sockaddr_in *)addr)->sin_port = htons(portnum);
  }
#ifdef      HAVE_IP6
  if(hp->h_addrtype == AF_INET6){
    if(hp)
      memcpy(&(((struct sockaddr_in6 *)addr)->sin6_addr),
                              hp->h_addr, hp->h_length);
    if(portnum >= 0)
      ((struct sockaddr_in6 *)addr)->sin6_port = htons(portnum);
  }
#endif

  return(0);
}

/* return value <= - 128: may be a temporary resource shortage */
int
open_tcpip_conn(UChar * hostname, UChar * servname, Int32 fallback_port)
{
  nodeaddr  addr;
  struct hostent  *hp;
  struct linger   linger;
  int       sockfd, fl;
  Int32           sock_typesize, i, portnum;

  if(!servname && fallback_port < 0){
    errno = EINVAL;
    return(-1);
  }

  hp = get_host_by_name(hostname);
  if(!hp)
    return(-2);

  if(!servname){
    portnum = fallback_port;
  }
  else{
    portnum = get_tcp_portnum(servname);
    if(portnum < 0)
      return(-3);
  }

  if( (i = set_tcp_sockaddr_hp((struct sockaddr *)(&addr), hp, portnum)) )
    return(i);

  if( (i = anon_tcp_sockaddr((struct sockaddr *)(&addr), &sock_typesize, hp->h_addrtype)) )
    return(i);

  sockfd = socket(hp->h_addrtype, SOCK_STREAM, 0);
  if(sockfd < 0)
    return(-4 - 128);

  i = bind(sockfd, (struct sockaddr *)(&addr), sock_typesize);
  if(i){
    close(sockfd);
    return(-5 - 128);
  }

  set_tcp_sockaddr_hp((struct sockaddr *)(&addr), hp, portnum);

  if(connect(sockfd, (struct sockaddr *)(&addr), sock_typesize) < 0){
    close(sockfd);
    i = 128;

    if(errno == EACCES || errno == EPERM
#ifdef      ENETUNREACH
      || errno == ENETUNREACH
#endif
#ifdef      EADDRNOTAVAIL
      || errno == EADDRNOTAVAIL
#endif
            )
      i = 0;

    return(-6 - i);
  }

  linger.l_onoff = 1;
  linger.l_linger = 60;
  i = setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char *) &linger,
                                    sizeof (linger));

  fl = fcntl(sockfd, F_GETFL);
  fcntl(sockfd, F_SETFL, fl & INV_NONBLOCKING_FLAGS);

  return(sockfd);
}

Int32
set_ip_throughput(int sockfd)
{
    int                 sock_optval;
    Int32         r, i;

    r = 0;

#if defined(IP_TOS) && defined(IPTOS_THROUGHPUT)
    sock_optval = IPTOS_THROUGHPUT;
    if( (i = setsockopt(sockfd, IPPROTO_IP, IP_TOS,
                        (char *) &sock_optval, sizeof(int))) < 0)
      r -= 1;
#endif

    return(r);
}

Int32
set_tcp_nodelay(int sockfd, Flag turn_on)
{
    int                 sock_optval, sockfd_socktype;
    Int32         sock_optlen[2], r, i;

    r = 0;

      /* I use 32 Bit int for sock_optlen. See server.c for a *comment* */
    *((int *) &(sock_optlen[0])) = sizeof(int);
    i = getsockopt(sockfd, SOL_SOCKET, SO_TYPE,
                  (char *) &sockfd_socktype, (int *) &(sock_optlen[0]));
    if(i){
      r -= 1;
      sockfd_socktype = -1;
    }

#ifdef      TCP_NODELAY
    sock_optval = (turn_on ? 1 : 0);
    if(sockfd_socktype == SOCK_STREAM){
      if( (i = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
                        (char *) &sock_optval, sizeof(int))) < 0)
      r -= 2;
    }
#endif

    return(r);
}

Int32
set_socket_keepalive(int sockfd)
{
    int                 sock_optval;
    Int32         r, i;

    r = 0;

#ifdef      SO_KEEPALIVE
    sock_optval = 1;
    if( (i = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
                        (char *) &sock_optval, sizeof(int))) < 0)
      r -= 1;
#endif

    return(r);
}

UChar *
addr_to_string(int socktype, void * addr)
{
  UChar           *ascaddr = NULL;

  if(socktype != AF_INET
#ifdef      HAVE_IP6
                  && socktype != AF_INET6
#endif
                                    )
    return(NULL);

#ifdef      HAVE_INET_NTOP          /* this should work, if IP6 is present */

    ascaddr = NEWP(UChar, 256);

    if(ascaddr){
      if(inet_ntop(socktype, addr, ascaddr, 256))
        ascaddr = ZRENEWP(ascaddr, UChar, strlen(ascaddr) + 1);
      else
        ZFREE(ascaddr);
    }

#else

    if(socktype == AF_INET){

#ifdef      HAVE_INET_NTOA

      ascaddr = strdup(inet_ntoa(*((struct in_addr *) addr)));

#else

      {
        char            naddr[30];  /* manually: assuming IP-V4-address */
        unsigned long   laddr;

        laddr = ntohl(addr);

        sprintf(naddr, "%u.%u.%u.%u", laddr >> 24, (laddr >> 16) & 0xff,
                        (laddr >> 8) & 0xff, laddr & 0xff);
        ascaddr = strdup(naddr);
      }       

#endif      /* defined(HAVE_INET_NTOA) */

    }

#endif      /* defined(HAVE_INET_NTOP) */

  return(ascaddr);
}

UChar *
get_hostnamestr(struct sockaddr * peeraddr)
{
  struct hostent  *hp;
  UChar           *hostname = NULL;
  void            *addr;
  Int32           alen;

  if(!peeraddr)
    return(get_my_off_hn());

  if(peeraddr->sa_family == AF_UNIX)
    return(strdup("localhost"));

  if(peeraddr->sa_family == AF_INET){
    addr = &(((struct sockaddr_in *)peeraddr)->sin_addr);
    alen = sizeof(struct in_addr);
  }

#ifdef      HAVE_IP6

  else if(peeraddr->sa_family == AF_INET6){
    addr = &(((struct sockaddr_in6 *) peeraddr)->sin6_addr);
    alen = sizeof(struct in6_addr);
  }

#endif

  else
    return(NULL);

  hp = get_host_by_addr(addr, alen, peeraddr->sa_family);
  if(hp)
    hostname = strdup(hp->h_name);
  else
    hostname = addr_to_string(peeraddr->sa_family, addr);

  return(hostname);
}

UChar *
get_connected_peername(int sock)
{
  int       i;
  nodeaddr  peeraddr;
  Int32           len[2];

  len[0] = len[1] = 0;
  *((int *) &(len[0])) = sizeof(peeraddr);
  i = getpeername(sock, (struct sockaddr *) &peeraddr, (int *) &(len[0]));
  if(i){
    return(NULL);
  }

  return(get_hostnamestr((struct sockaddr *)(&peeraddr)));
}

/* *comment* on getsockopt argument 5:
 * The IBM had the *great* idea to change the type of argument 5 of
 * getsockopt (and BTW argument 3 of getpeername) to size_t *. On the
 * Digital Unix size_t is an 8 Byte unsigned integer. Therefore the
 * safety belt 
 *  int sock_optlen[2].
 * I tried to find a workaround using autoconfig. Parsing the Headers
 * using cpp and perl or awk does not lead to satisfying results. Using
 * the TRY_COMPILE macro of autoconfig fails sometimes. E.G. the DEC
 * C-compiler does not complain on wrong re-declarations (neither via
 * error-message nor by the exit status). Try yourself, if you don't
 * believe. Furthermore there are 4 ways of declaration i found on
 * several systems. 4th argument as char * or void *, 5th arg as
 * explained of type int * or size_t *. Testing all these is annoying.
 * Maybe some other vendors have other ideas, how getsockopt should
 * be declared (sigh). Therefore i use two 32-Bit integers. Even if
 * a system call thinks, it must put there a 64-Bit int, the program
 * does not fail.
 */

Generated by  Doxygen 1.6.0   Back to index