Logo Search packages:      
Sourcecode: ipsec-tools version File versions

handler.c

/* $Id: handler.c,v 1.13 2004/11/21 19:36:26 manubsd Exp $ */

/*
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>

#include "var.h"
#include "misc.h"
#include "vmbuf.h"
#include "plog.h"
#include "sockmisc.h"
#include "debug.h"

#include "schedule.h"
#include "grabmyaddr.h"
#include "algorithm.h"
#include "crypto_openssl.h"
#include "policy.h"
#include "proposal.h"
#include "isakmp_var.h"
#include "evt.h"
#include "isakmp.h"
#ifdef ENABLE_HYBRID
#include "isakmp_xauth.h"  
#include "isakmp_cfg.h"
#endif
#include "isakmp_inf.h"
#include "oakley.h"
#include "remoteconf.h"
#include "localconf.h"
#include "handler.h"
#include "gcmalloc.h"
#include "nattraversal.h"

#ifdef HAVE_GSSAPI
#include "gssapi.h"
#endif

static LIST_HEAD(_ph1tree_, ph1handle) ph1tree;
static LIST_HEAD(_ph2tree_, ph2handle) ph2tree;
static LIST_HEAD(_ctdtree_, contacted) ctdtree;
static LIST_HEAD(_rcptree_, recvdpkt) rcptree;

static void del_recvdpkt __P((struct recvdpkt *));
static void rem_recvdpkt __P((struct recvdpkt *));
static void sweep_recvdpkt __P((void *));

/*
 * functions about management of the isakmp status table
 */
/* %%% management phase 1 handler */
/*
 * search for isakmpsa handler with isakmp index.
 */

extern caddr_t val2str(const char *, size_t);

struct ph1handle *
getph1byindex(index)
      isakmp_index *index;
{
      struct ph1handle *p;

      LIST_FOREACH(p, &ph1tree, chain) {
            if (p->status == PHASE1ST_EXPIRED)
                  continue;
            if (memcmp(&p->index, index, sizeof(*index)) == 0)
                  return p;
      }

      return NULL;
}

/*
 * search for isakmp handler by i_ck in index.
 */
struct ph1handle *
getph1byindex0(index)
      isakmp_index *index;
{
      struct ph1handle *p;

      LIST_FOREACH(p, &ph1tree, chain) {
            if (p->status == PHASE1ST_EXPIRED)
                  continue;
            if (memcmp(&p->index, index, sizeof(cookie_t)) == 0)
                  return p;
      }

      return NULL;
}

/*
 * search for isakmpsa handler by source and remote address.
 * don't use port number to search because this function search
 * with phase 2's destinaion.
 */
struct ph1handle *
getph1byaddr(local, remote)
      struct sockaddr *local, *remote;
{
      struct ph1handle *p;

      LIST_FOREACH(p, &ph1tree, chain) {
            if (p->status == PHASE1ST_EXPIRED)
                  continue;
            if (cmpsaddrwop(local, p->local) == 0
             && cmpsaddrwop(remote, p->remote) == 0)
                  return p;
      }

      return NULL;
}

/*
 * search for isakmpsa handler by remote address.
 * don't use port number to search because this function search
 * with phase 2's destinaion.
 */
struct ph1handle *
getph1bydstaddr(remote)
      struct sockaddr *remote;
{
      struct ph1handle *p;

      LIST_FOREACH(p, &ph1tree, chain) {
            if (p->status == PHASE1ST_EXPIRED)
                  continue;
            if (cmpsaddrwop(remote, p->remote) == 0)
                  return p;
      }

      return NULL;
}

/*
 * dump isakmp-sa
 */
vchar_t *
dumpph1()
{
      struct ph1handle *iph1;
      struct ph1dump *pd;
      int cnt = 0;
      vchar_t *buf;

      /* get length of buffer */
      LIST_FOREACH(iph1, &ph1tree, chain)
            cnt++;

      buf = vmalloc(cnt * sizeof(struct ph1dump));
      if (buf == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to get buffer\n");
            return NULL;
      }
      pd = (struct ph1dump *)buf->v;

      LIST_FOREACH(iph1, &ph1tree, chain) {
            memcpy(&pd->index, &iph1->index, sizeof(iph1->index));
            pd->status = iph1->status;
            pd->side = iph1->side;
            memcpy(&pd->remote, iph1->remote, sysdep_sa_len(iph1->remote));
            memcpy(&pd->local, iph1->local, sysdep_sa_len(iph1->local));
            pd->version = iph1->version;
            pd->etype = iph1->etype;
            pd->created = iph1->created;
            pd->ph2cnt = iph1->ph2cnt;
            pd++;
      }

      return buf;
}

/*
 * create new isakmp Phase 1 status record to handle isakmp in Phase1
 */
struct ph1handle *
newph1()
{
      struct ph1handle *iph1;

      /* create new iph1 */
      iph1 = racoon_calloc(1, sizeof(*iph1));
      if (iph1 == NULL)
            return NULL;

      iph1->status = PHASE1ST_SPAWN;

#ifdef ENABLE_DPD
      iph1->dpd_support = 0;
      iph1->dpd_lastack = 0;
      iph1->dpd_seq = 0;
      iph1->dpd_fails = 0;
      iph1->dpd_r_u = NULL;
#endif

      return iph1;
}

/*
 * delete new isakmp Phase 1 status record to handle isakmp in Phase1
 */
void
delph1(iph1)
      struct ph1handle *iph1;
{
      /* SA down shell script hook */
      if (iph1 != NULL)
            script_hook(iph1, SCRIPT_PHASE1_DOWN);

      EVT_PUSH(iph1->local, iph1->remote, EVTT_PHASE1_DOWN, NULL);

#ifdef ENABLE_NATT
      if (iph1->natt_flags & NAT_KA_QUEUED)
            natt_keepalive_remove (iph1->local, iph1->remote);

      if (iph1->natt_options) {
            racoon_free(iph1->natt_options);
            iph1->natt_options = NULL;
      }
#endif

      if (iph1->remote) {
            racoon_free(iph1->remote);
            iph1->remote = NULL;
      }
      if (iph1->local) {
            racoon_free(iph1->local);
            iph1->local = NULL;
      }

#ifdef ENABLE_HYBRID
      if (iph1->mode_cfg)
            isakmp_cfg_rmstate(iph1);
#endif

      VPTRINIT(iph1->authstr);

      sched_scrub_param(iph1);
      iph1->sce = NULL;
      iph1->scr = NULL;

      VPTRINIT(iph1->sendbuf);

      VPTRINIT(iph1->dhpriv);
      VPTRINIT(iph1->dhpub);
      VPTRINIT(iph1->dhpub_p);
      VPTRINIT(iph1->dhgxy);
      VPTRINIT(iph1->nonce);
      VPTRINIT(iph1->nonce_p);
      VPTRINIT(iph1->skeyid);
      VPTRINIT(iph1->skeyid_d);
      VPTRINIT(iph1->skeyid_a);
      VPTRINIT(iph1->skeyid_e);
      VPTRINIT(iph1->key);
      VPTRINIT(iph1->hash);
      VPTRINIT(iph1->sig);
      VPTRINIT(iph1->sig_p);
      oakley_delcert(iph1->cert);
      iph1->cert = NULL;
      oakley_delcert(iph1->cert_p);
      iph1->cert_p = NULL;
      oakley_delcert(iph1->crl_p);
      iph1->crl_p = NULL;
      oakley_delcert(iph1->cr_p);
      iph1->cr_p = NULL;
      VPTRINIT(iph1->id);
      VPTRINIT(iph1->id_p);

      if (iph1->ivm) {
            oakley_delivm(iph1->ivm);
            iph1->ivm = NULL;
      }

      VPTRINIT(iph1->sa);
      VPTRINIT(iph1->sa_ret);

#ifdef HAVE_GSSAPI
      VPTRINIT(iph1->gi_i);
      VPTRINIT(iph1->gi_r);

      gssapi_free_state(iph1);
#endif

      racoon_free(iph1);
}

/*
 * create new isakmp Phase 1 status record to handle isakmp in Phase1
 */
int
insph1(iph1)
      struct ph1handle *iph1;
{
      /* validity check */
      if (iph1->remote == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "invalid isakmp SA handler. no remote address.\n");
            return -1;
      }
      LIST_INSERT_HEAD(&ph1tree, iph1, chain);

      return 0;
}

void
remph1(iph1)
      struct ph1handle *iph1;
{
      LIST_REMOVE(iph1, chain);
}

/*
 * flush isakmp-sa
 */
void
flushph1()
{
      struct ph1handle *p, *next;

      for (p = LIST_FIRST(&ph1tree); p; p = next) {
            next = LIST_NEXT(p, chain);

            /* send delete information */
            if (p->status == PHASE1ST_ESTABLISHED) 
                  isakmp_info_send_d1(p);

            remph1(p);
            delph1(p);
      }
}

void
initph1tree()
{
      LIST_INIT(&ph1tree);
}

/* %%% management phase 2 handler */
/*
 * search ph2handle with policy id.
 */
struct ph2handle *
getph2byspid(spid)
      u_int32_t spid;
{
      struct ph2handle *p;

      LIST_FOREACH(p, &ph2tree, chain) {
            /*
             * there are ph2handle independent on policy
             * such like informational exchange.
             */
            if (p->spid == spid)
                  return p;
      }

      return NULL;
}

/*
 * search ph2handle with sequence number.
 */
struct ph2handle *
getph2byseq(seq)
      u_int32_t seq;
{
      struct ph2handle *p;

      LIST_FOREACH(p, &ph2tree, chain) {
            if (p->seq == seq)
                  return p;
      }

      return NULL;
}

/*
 * search ph2handle with message id.
 */
struct ph2handle *
getph2bymsgid(iph1, msgid)
      struct ph1handle *iph1;
      u_int32_t msgid;
{
      struct ph2handle *p;

      LIST_FOREACH(p, &ph2tree, chain) {
            if (p->msgid == msgid)
                  return p;
      }

      return NULL;
}

/*
 * call by pk_recvexpire().
 */
struct ph2handle *
getph2bysaidx(src, dst, proto_id, spi)
      struct sockaddr *src, *dst;
      u_int proto_id;
      u_int32_t spi;
{
      struct ph2handle *iph2;
      struct saproto *pr;

      LIST_FOREACH(iph2, &ph2tree, chain) {
            if (iph2->proposal == NULL && iph2->approval == NULL)
                  continue;
            if (iph2->approval != NULL) {
                  for (pr = iph2->approval->head; pr != NULL;
                       pr = pr->next) {
                        if (proto_id != pr->proto_id)
                              break;
                        if (spi == pr->spi || spi == pr->spi_p)
                              return iph2;
                  }
            } else if (iph2->proposal != NULL) {
                  for (pr = iph2->proposal->head; pr != NULL;
                       pr = pr->next) {
                        if (proto_id != pr->proto_id)
                              break;
                        if (spi == pr->spi)
                              return iph2;
                  }
            }
      }

      return NULL;
}

/*
 * create new isakmp Phase 2 status record to handle isakmp in Phase2
 */
struct ph2handle *
newph2()
{
      struct ph2handle *iph2 = NULL;

      /* create new iph2 */
      iph2 = racoon_calloc(1, sizeof(*iph2));
      if (iph2 == NULL)
            return NULL;

      iph2->status = PHASE1ST_SPAWN;

      return iph2;
}

/*
 * initialize ph2handle
 * NOTE: don't initialize src/dst.
 *       SPI in the proposal is cleared.
 */
void
initph2(iph2)
      struct ph2handle *iph2;
{
      sched_scrub_param(iph2);
      iph2->sce = NULL;
      iph2->scr = NULL;

      VPTRINIT(iph2->sendbuf);
      VPTRINIT(iph2->msg1);

      /* clear spi, keep variables in the proposal */
      if (iph2->proposal) {
            struct saproto *pr;
            for (pr = iph2->proposal->head; pr != NULL; pr = pr->next)
                  pr->spi = 0;
      }

      /* clear approval */
      if (iph2->approval) {
            flushsaprop(iph2->approval);
            iph2->approval = NULL;
      }

      /* clear the generated policy */
      if (iph2->spidx_gen) {
            delsp_bothdir((struct policyindex *)iph2->spidx_gen);
            racoon_free(iph2->spidx_gen);
            iph2->spidx_gen = NULL;
      }

      if (iph2->pfsgrp) {
            oakley_dhgrp_free(iph2->pfsgrp);
            iph2->pfsgrp = NULL;
      }

      VPTRINIT(iph2->dhpriv);
      VPTRINIT(iph2->dhpub);
      VPTRINIT(iph2->dhpub_p);
      VPTRINIT(iph2->dhgxy);
      VPTRINIT(iph2->id);
      VPTRINIT(iph2->id_p);
      VPTRINIT(iph2->nonce);
      VPTRINIT(iph2->nonce_p);
      VPTRINIT(iph2->sa);
      VPTRINIT(iph2->sa_ret);

      if (iph2->ivm) {
            oakley_delivm(iph2->ivm);
            iph2->ivm = NULL;
      }
}

/*
 * delete new isakmp Phase 2 status record to handle isakmp in Phase2
 */
void
delph2(iph2)
      struct ph2handle *iph2;
{
      initph2(iph2);

      if (iph2->src) {
            racoon_free(iph2->src);
            iph2->src = NULL;
      }
      if (iph2->dst) {
            racoon_free(iph2->dst);
            iph2->dst = NULL;
      }
      if (iph2->src_id) {
            racoon_free(iph2->src_id);
            iph2->src_id = NULL;
      }
      if (iph2->dst_id) {
            racoon_free(iph2->dst_id);
            iph2->dst_id = NULL;
      }

      if (iph2->proposal) {
            flushsaprop(iph2->proposal);
            iph2->proposal = NULL;
      }

      racoon_free(iph2);
}

/*
 * create new isakmp Phase 2 status record to handle isakmp in Phase2
 */
int
insph2(iph2)
      struct ph2handle *iph2;
{
      LIST_INSERT_HEAD(&ph2tree, iph2, chain);

      return 0;
}

void
remph2(iph2)
      struct ph2handle *iph2;
{
      LIST_REMOVE(iph2, chain);
}

void
initph2tree()
{
      LIST_INIT(&ph2tree);
}

void
flushph2()
{
      struct ph2handle *p, *next;

      for (p = LIST_FIRST(&ph2tree); p; p = next) {
            next = LIST_NEXT(p, chain);

            /* send delete information */
            if (p->status == PHASE2ST_ESTABLISHED) 
                  isakmp_info_send_d2(p);

            unbindph12(p);
            remph2(p);
            delph2(p);
      }
}

/*
 * Delete all Phase 2 handlers for this src/dst/proto.  This
 * is used during INITIAL-CONTACT processing (so no need to
 * send a message to the peer).
 */
void
deleteallph2(src, dst, proto_id)
      struct sockaddr *src, *dst;
      u_int proto_id;
{
      struct ph2handle *iph2, *next;
      struct saproto *pr;

      for (iph2 = LIST_FIRST(&ph2tree); iph2 != NULL; iph2 = next) {
            next = LIST_NEXT(iph2, chain);
            if (iph2->proposal == NULL && iph2->approval == NULL)
                  continue;
            if (iph2->approval != NULL) {
                  for (pr = iph2->approval->head; pr != NULL;
                       pr = pr->next) {
                        if (proto_id == pr->proto_id)
                              goto zap_it;
                  }
            } else if (iph2->proposal != NULL) {
                  for (pr = iph2->proposal->head; pr != NULL;
                       pr = pr->next) {
                        if (proto_id == pr->proto_id)
                              goto zap_it;
                  }
            }
            continue;
 zap_it:
            unbindph12(iph2);
            remph2(iph2);
            delph2(iph2);
      }
}

/* %%% */
void
bindph12(iph1, iph2)
      struct ph1handle *iph1;
      struct ph2handle *iph2;
{
      iph2->ph1 = iph1;
      LIST_INSERT_HEAD(&iph1->ph2tree, iph2, ph1bind);
}

void
unbindph12(iph2)
      struct ph2handle *iph2;
{
      if (iph2->ph1 != NULL) {
            iph2->ph1 = NULL;
            LIST_REMOVE(iph2, ph1bind);
      }
}

/* %%% management contacted list */
/*
 * search contacted list.
 */
struct contacted *
getcontacted(remote)
      struct sockaddr *remote;
{
      struct contacted *p;

      LIST_FOREACH(p, &ctdtree, chain) {
            if (cmpsaddrstrict(remote, p->remote) == 0)
                  return p;
      }

      return NULL;
}

/*
 * create new isakmp Phase 2 status record to handle isakmp in Phase2
 */
int
inscontacted(remote)
      struct sockaddr *remote;
{
      struct contacted *new;

      /* create new iph2 */
      new = racoon_calloc(1, sizeof(*new));
      if (new == NULL)
            return -1;

      new->remote = dupsaddr(remote);

      LIST_INSERT_HEAD(&ctdtree, new, chain);

      return 0;
}

void
initctdtree()
{
      LIST_INIT(&ctdtree);
}

/*
 * check the response has been sent to the peer.  when not, simply reply
 * the buffered packet to the peer.
 * OUT:
 *     0:   the packet is received at the first time.
 *     1:   the packet was processed before.
 *     2:   the packet was processed before, but the address mismatches.
 *    -1:   error happened.
 */
int
check_recvdpkt(remote, local, rbuf)
      struct sockaddr *remote, *local;
      vchar_t *rbuf;
{
      vchar_t *hash;
      struct recvdpkt *r;
      time_t t;
      int len, s;

      /* set current time */
      t = time(NULL);

      hash = eay_md5_one(rbuf);
      if (!hash) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to allocate buffer.\n");
            return -1;
      }

      LIST_FOREACH(r, &rcptree, chain) {
            if (memcmp(hash->v, r->hash->v, r->hash->l) == 0)
                  break;
      }
      vfree(hash);

      /* this is the first time to receive the packet */
      if (r == NULL)
            return 0;

      /*
       * the packet was processed before, but the remote address mismatches.
       */
      if (cmpsaddrstrict(remote, r->remote) != 0)
            return 2;

      /*
       * it should not check the local address because the packet
       * may arrive at other interface.
       */

      /* check the previous time to send */
      if (t - r->time_send < 1) {
            plog(LLV_WARNING, LOCATION, NULL,
                  "the packet retransmitted in a short time from %s\n",
                  saddr2str(remote));
            /*XXX should it be error ? */
      }

      /* select the socket to be sent */
      s = getsockmyaddr(r->local);
      if (s == -1)
            return -1;

      /* resend the packet if needed */
      len = sendfromto(s, r->sendbuf->v, r->sendbuf->l,
                  r->local, r->remote, lcconf->count_persend);
      if (len == -1) {
            plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n");
            return -1;
      }

      /* check the retry counter */
      r->retry_counter--;
      if (r->retry_counter <= 0) {
            rem_recvdpkt(r);
            del_recvdpkt(r);
            plog(LLV_DEBUG, LOCATION, NULL,
                  "deleted the retransmission packet to %s.\n",
                  saddr2str(remote));
      } else
            r->time_send = t;

      return 1;
}

/*
 * adding a hash of received packet into the received list.
 */
int
add_recvdpkt(remote, local, sbuf, rbuf)
      struct sockaddr *remote, *local;
      vchar_t *sbuf, *rbuf;
{
      struct recvdpkt *new = NULL;

      if (lcconf->retry_counter == 0) {
            /* no need to add it */
            return 0;
      }

      new = racoon_calloc(1, sizeof(*new));
      if (!new) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to allocate buffer.\n");
            return -1;
      }

      new->hash = eay_md5_one(rbuf);
      if (!new->hash) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to allocate buffer.\n");
            del_recvdpkt(new);
            return -1;
      }
      new->remote = dupsaddr(remote);
      if (new->remote == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to allocate buffer.\n");
            del_recvdpkt(new);
            return -1;
      }
      new->local = dupsaddr(local);
      if (new->local == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to allocate buffer.\n");
            del_recvdpkt(new);
            return -1;
      }
      new->sendbuf = vdup(sbuf);
      if (new->sendbuf == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to allocate buffer.\n");
            del_recvdpkt(new);
            return -1;
      }

      new->retry_counter = lcconf->retry_counter;
      new->time_send = 0;
      new->created = time(NULL);

      LIST_INSERT_HEAD(&rcptree, new, chain);

      return 0;
}

void
del_recvdpkt(r)
      struct recvdpkt *r;
{
      if (r->remote)
            racoon_free(r->remote);
      if (r->local)
            racoon_free(r->local);
      if (r->hash)
            vfree(r->hash);
      if (r->sendbuf)
            vfree(r->sendbuf);
      racoon_free(r);
}

void
rem_recvdpkt(r)
      struct recvdpkt *r;
{
      LIST_REMOVE(r, chain);
}

void
sweep_recvdpkt(dummy)
      void *dummy;
{
      struct recvdpkt *r, *next;
      time_t t, lt;

      /* set current time */
      t = time(NULL);

      /* set the lifetime of the retransmission */
      lt = lcconf->retry_counter * lcconf->retry_interval;

      for (r = LIST_FIRST(&rcptree); r; r = next) {
            next = LIST_NEXT(r, chain);

            if (t - r->created > lt) {
                  rem_recvdpkt(r);
                  del_recvdpkt(r);
            }
      }

      sched_new(lt, sweep_recvdpkt, NULL);
}

void
init_recvdpkt()
{
      time_t lt = lcconf->retry_counter * lcconf->retry_interval;

      LIST_INIT(&rcptree);

      sched_new(lt, sweep_recvdpkt, NULL);
}

#ifdef ENABLE_HYBRID
/* 
 * Retruns 0 if the address was obtained by ISAKMP mode config, 1 otherwise
 * This should be in isakmp_cfg.c but ph1tree being private, it must be there
 */
int
exclude_cfg_addr(addr)
      const struct sockaddr *addr;
{
      struct ph1handle *p;
      struct sockaddr_in *sin;

      LIST_FOREACH(p, &ph1tree, chain) {
            if ((p->mode_cfg != NULL) &&
                (p->mode_cfg->flags & ISAKMP_CFG_GOT_ADDR4) &&
                (addr->sa_family == AF_INET)) {
                  sin = (struct sockaddr_in *)addr;
                  if (sin->sin_addr.s_addr == p->mode_cfg->addr4.s_addr)
                        return 0;
            }
      }

      return 1;
}
#endif

Generated by  Doxygen 1.6.0   Back to index