/*
 *  $Id: libnet_raw.c,v 1.9 2004/02/18 18:19:00 mike Exp $
 *
 *  libnet
 *  libnet_raw.c - raw sockets routines
 *
 *  Copyright (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com>
 *  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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "common.h"

#ifndef HAVE_SOCKLEN_T
typedef int socklen_t;
#endif

/* TODO this doesn't make any sense, the code in the #else branch is littered
   with conditionals on __WIN32__ that are never reachable, what happened?
   */
#if defined (__WIN32__)
int
libnet_open_raw4(libnet_t *l)
{
    return (libnet_open_link(l));
}

int
libnet_open_raw6(libnet_t *l)
{
    return (libnet_open_link(l));
}

int
libnet_close_raw4(libnet_t *l)
{
    return (libnet_close_link_interface(l));
}

int
libnet_close_raw6(libnet_t *l)
{
    return (libnet_close_link_interface(l));
}
#else

static int libnet_finish_setup_socket(libnet_t *l)
{
#if !(__WIN32__)
     int n = 1;
#if (__svr4__)
     void *nptr = &n;
#else
    int *nptr = &n;
#endif  /* __svr4__ */
#else
	BOOL n;
#endif
    unsigned len;

#ifdef SO_BROADCAST
/*
 * man 7 socket
 *
 * Set or get the broadcast flag. When  enabled,  datagram  sockets
 * receive packets sent to a broadcast address and they are allowed
 * to send packets to a broadcast  address.   This  option  has  no
 * effect on stream-oriented sockets.
 */
    if (setsockopt(l->fd, SOL_SOCKET, SO_BROADCAST, nptr, sizeof(n)) == -1)
    {
        snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
                "%s(): set SO_BROADCAST failed: %s",
                __func__, strerror(errno));
        goto bad;
    }
#endif  /*  SO_BROADCAST  */

#if (__linux__)
    if(l->device != NULL)
        if(setsockopt(l->fd, SOL_SOCKET, SO_BINDTODEVICE, l->device, strlen(l->device)) == -1) {
            snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
                "%s(): set SO_BINDTODEVICE failed: %s", __func__, strerror(errno));
            goto bad;
        }
#endif  /* __linux__ */

    return 0;

bad:
    return (-1);
}

int
libnet_open_raw4(libnet_t *l)
{
#if !(__WIN32__)
     int n = 1;
#if (__svr4__)
     void *nptr = &n;
#else
    int *nptr = &n;
#endif  /* __svr4__ */
#else
	BOOL n;
#endif

    if (l == NULL)
    {
        return (-1);
    }

    l->fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (l->fd == -1)
    {
        snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
                "%s(): SOCK_RAW allocation failed: %s",
		 __func__, strerror(errno));
        goto bad;
    }

#ifdef IP_HDRINCL
/*
 * man raw
 *
 * The IPv4 layer generates an IP header when sending a packet unless
 * the IP_HDRINCL socket option is enabled on the socket.  When it
 * is enabled, the packet must contain an IP header.  For
 * receiving the IP header is always included in the packet.
 */
#if !(__WIN32__)
    if (setsockopt(l->fd, IPPROTO_IP, IP_HDRINCL, nptr, sizeof(n)) == -1)
#else
    n = TRUE;
    if (setsockopt(l->fd, IPPROTO_IP, IP_HDRINCL, &n, sizeof(n)) == -1)
#endif

    {
        snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
                "%s(): set IP_HDRINCL failed: %s",
                __func__, strerror(errno));
        goto bad;
    }
#endif /*  IP_HDRINCL  */


    if (libnet_finish_setup_socket(l) == -1)
        goto bad;

    return (l->fd);

bad:
    return (-1);
}


int
libnet_close_raw4(libnet_t *l)
{
    if (l == NULL)
    { 
        return (-1);
    }

    return (close(l->fd));
}

#if ((defined HAVE_SOLARIS && !defined HAVE_SOLARIS_IPV6) || defined (__WIN32__))
int libnet_open_raw6(libnet_t *l)
{
	return (-1);
}

#else
int
libnet_open_raw6(libnet_t *l)
{
    if (l == NULL)
    { 
        return (-1);
    } 

    l->fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
    if (l->fd == -1)
    {
        snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, 
                "%s(): SOCK_RAW allocation failed: %s", __func__,
                strerror(errno));
        goto bad;
    }


    if (libnet_finish_setup_socket(l) == -1)
        goto bad;

    return (l->fd);

bad:
    return (-1);    
}
#endif

int
libnet_close_raw6(libnet_t *l)
{
    if (l == NULL)
    { 
         return (-1);
    }
    return (close(l->fd));
}
#endif

/**
 * Local Variables:
 *  indent-tabs-mode: nil
 *  c-file-style: "stroustrup"
 * End:
 */
