/***************************************************************************
 *
 * Copyright (c) 2000, 2001 BalaBit IT Ltd, Budapest, Hungary
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: sysdep.c,v 1.1.2.2 2001/09/19 19:22:17 bazsi Exp $
 *
 ***************************************************************************/

#include <zorp/sysdep.h>
#include <zorp/log.h>

#if ENABLE_IPFILTER

#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <netinet/ip_compat.h>
#include <netinet/ip_fil.h>
#include <netinet/ip_nat.h>

int ipnat_fd = -1;

void
z_sysdep_init(void)
{
  ipnat_fd = open(IPL_NAT, O_RDONLY);

  if (ipnat_fd < 0)
    {
      z_log(NULL, CORE_ERROR, 3, "Error opening ipnat device, transparency will not be supported; dev='%s', error='%m'", IPL_NAT);
    }
}

void
z_sysdep_destroy(void)
{
  close(ipnat_fd);
}

gboolean
z_get_transparent_dest(int fd, struct sockaddr *sa, int *salen)
{
  struct sockaddr_in local, peer;
  int len = sizeof(struct sockaddr_in);
  natlookup_t nl, *nlp = &nl;
  
  if (*salen < sizeof(struct sockaddr_in))
    {
      return FALSE;
    }
  
  if (ipnat_fd == -1)
    {
      if (getsockname(fd, sa, salen) < 0)
        return FALSE;
      return TRUE;
    }
  
  if (getsockname(fd, (struct sockaddr *) &local, &len) < 0)
    {
      return FALSE;
    }
  if (getpeername(fd, (struct sockaddr *) &peer, &len) < 0)
    {
      return FALSE;
    }
  if (peer.sin_family != AF_INET)
    return FALSE;
  memset(&nl, 0, sizeof(nl));
  nl.nl_inip = local.sin_addr;
  nl.nl_inport = local.sin_port;
  nl.nl_outip = peer.sin_addr;
  nl.nl_outport = peer.sin_port;
  nl.nl_flags = IPN_TCP;
  if (ioctl(ipnat_fd, SIOCGNATL, &nlp) < 0)
    {
      memcpy(sa, &local, sizeof(local));
      *salen = sizeof(local);
    }
  else
    {
      struct sockaddr_in *sin = (struct sockaddr_in *) sa;
      
      memset(sa, 0, *salen);
      sin->sin_family = AF_INET;
      sin->sin_addr = nl.nl_realip;
      sin->sin_port = nl.nl_realport;
      *salen = sizeof(struct sockaddr_in);
    }
  return TRUE;
}

#else

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

gboolean
z_get_transparent_dest(int fd, struct sockaddr *sa, int *salen)
{
#if ENABLE_NETFILTER

#define SO_ORIGINAL_DST 80
  /* NOTE: this hack limits Zorp to IPv4 transparency, the
     kernel should be fixed to allow sockaddrs larger than
     sockaddr_in */
  *salen = sizeof(struct sockaddr_in);
  if (getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, sa, salen) < 0) 
    {
      if (getsockname(fd, sa, salen) < 0)
        {
          return FALSE;
        }
    }
#elif ENABLE_IPFILTER

#else    
  if (getsockname(fd, sa, salen) < 0)
    {
      return FALSE;
    }
#endif

  return TRUE;
}

void
z_sysdep_init(void)
{
}

void
z_sysdep_destroy(void)
{
}

#endif
