The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* $Id: Libdnet.xs 57 2012-11-02 16:39:39Z gomor $ */

/*
 * Copyright (c) 2004 Vlad Manilici
 * Copyright (c) 2008-2012 Patrice <GomoR> Auffret
 *
 * 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 ``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 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 "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include <stdio.h>
#include <dnet.h>

#ifdef DNET_BLOB_H
typedef blob_t              Blob;
#endif

#ifdef DNET_ETH_H
typedef eth_t               EthHandle;
typedef eth_addr_t          EthAddr;
#endif

#ifdef DNET_INTF_H
typedef intf_t              IntfHandle;
#endif

#ifdef DNET_ARP_H
typedef arp_t               ArpHandle;
#endif

#ifdef DNET_FW_H
typedef fw_t                FwHandle;
#endif

#ifdef DNET_ROUTE_H
typedef route_t             RouteHandle;
#endif

#ifdef DNET_TUN_H
typedef tun_t               TunHandle;
#endif

#ifdef DNET_IP_H
typedef ip_t                IpHandle;
#endif

typedef struct intf_entry   IntfEntry;
typedef struct arp_entry    ArpEntry;
typedef struct fw_rule      FwRule;
typedef struct route_entry  RouteEntry;

#include "c/intf_entry.c"
#include "c/arp_entry.c"
#include "c/route_entry.c"
#include "c/fw_rule.c"

static SV * keepSub = (SV *)NULL;

static int
intf_callback(IntfEntry *entry, SV *data)
{
   dSP;
   int ret;
   SV *e = intf_c2sv(entry);
   ENTER; SAVETMPS; PUSHMARK(SP);
   XPUSHs(e);
   XPUSHs(data);
   PUTBACK;
   call_sv(keepSub, G_DISCARD);
   SPAGAIN;
   FREETMPS; LEAVE;
   return 0;
}

static int
route_callback(RouteEntry *entry, SV *data)
{
   dSP;
   int ret;
   SV *e = route_c2sv(entry);
   ENTER; SAVETMPS; PUSHMARK(SP);
   XPUSHs(e);
   XPUSHs(data);
   //XPUSHs(sv_setref_pv(sv_newmortal(), "RouteEntryPtr", entry));
   //XPUSHs(sv_setref_pv(sv_newmortal(), Nullch, data));
   PUTBACK;
   call_sv(keepSub, G_DISCARD);
   SPAGAIN;
   //ret = POPi;
   FREETMPS; LEAVE;
   //return ret;
   return 0;
}

static int
arp_callback(ArpEntry *entry, SV *data)
{
   dSP;
   int ret;
   SV *e = arp_c2sv(entry);
   ENTER; SAVETMPS; PUSHMARK(SP);
   XPUSHs(e);
   XPUSHs(data);
   PUTBACK;
   call_sv(keepSub, G_DISCARD);
   SPAGAIN;
   FREETMPS; LEAVE;
   return 0;
}

static int
fw_callback(FwRule *rule, SV *data)
{
   dSP;
   int ret;
   SV *e = fw_c2sv(rule);
   ENTER; SAVETMPS; PUSHMARK(SP);
   XPUSHs(e);
   XPUSHs(data);
   PUTBACK;
   call_sv(keepSub, G_DISCARD);
   SPAGAIN;
   FREETMPS; LEAVE;
   return 0;
}

HV * intf2hash(struct intf_entry *IeInt){
        HV *HvInt, *HvUndef;
        SV *SvData, *SvKey;
        char *StrAddr;

        /* prepare undefined hash */
        HvUndef = newHV();
        hv_undef(HvUndef);

        HvInt = newHV();

        /* intf_len */
        SvKey = newSVpv("len", 0);
        SvData = newSVnv((double) IeInt->intf_len);
        if( hv_store_ent(HvInt, SvKey, SvData, 0) == NULL ){
                warn("intf2hash: error: intf_len\n");
                return HvUndef;
        }

        /* intf_name */
        SvKey = newSVpv("name", 0);
        SvData = newSVpv(IeInt->intf_name, 0);
        if( hv_store_ent(HvInt, SvKey, SvData, 0) == NULL ){
                warn("intf2hash: error: int_name\n");
                return HvUndef;
        }

        /* intf_type */
        SvKey = newSVpv("type", 0);
        SvData = newSVnv((double) IeInt->intf_type);
        if( hv_store_ent(HvInt, SvKey, SvData, 0) == NULL ){
                warn("intf2hash: error: intf_type\n");
                return HvUndef;
        }

        /* intf_flags */
        SvKey = newSVpv("flags", 0);
        SvData = newSVnv((double) IeInt->intf_flags);
        if( hv_store_ent(HvInt, SvKey, SvData, 0) == NULL ){
                warn("intf2hash: error: intf_flags\n");
                return HvUndef;
        }

        /* intf_mtu */
        SvKey = newSVpv("mtu", 0);
        SvData = newSVnv((double) IeInt->intf_mtu);
        if( hv_store_ent(HvInt, SvKey, SvData, 0) == NULL ){
                warn("intf2hash: error: intf_mtu\n");
                return HvUndef;
        }

        /* intf_addr */
        SvKey = newSVpv("addr", 0);
        /* does not allways exist */
        StrAddr = addr_ntoa(&(IeInt->intf_addr));
        if( StrAddr == NULL ){
                SvData = &PL_sv_undef;
        }else{
                SvData = newSVpv(addr_ntoa(&(IeInt->intf_addr)), 0);
        }
        if( hv_store_ent(HvInt, SvKey, SvData, 0) == NULL ){
                warn("intf2hash: error: intf_addr\n");
                return HvUndef;
        }

        /* intf_dst_addr */
        SvKey = newSVpv("dst_addr", 0);
        /* does not allways exist */
        StrAddr = addr_ntoa(&(IeInt->intf_dst_addr));
        if( StrAddr == NULL ){
                SvData = &PL_sv_undef;
        }else{
                SvData = newSVpv(addr_ntoa(&(IeInt->intf_dst_addr)), 0);
        }
        if( hv_store_ent(HvInt, SvKey, SvData, 0) == NULL ){
                warn("intf2hash: error: intf_dst_addr\n");
                return HvUndef;
        }

        /* intf_link_addr */
        SvKey = newSVpv("link_addr", 0);
        /* does not allways exist */
        StrAddr = addr_ntoa(&(IeInt->intf_link_addr));
        if( StrAddr == NULL ){
                SvData = &PL_sv_undef;
        }else{
                SvData = newSVpv(addr_ntoa(&(IeInt->intf_link_addr)), 0);
        }
        if( hv_store_ent(HvInt, SvKey, SvData, 0) == NULL ){
                warn("intf2hash: error: intf_link_addr\n");
                return HvUndef;
        }

        /* XXX skipped the aliases problematic */

        return HvInt;
}

MODULE = Net::Libdnet  PACKAGE = Net::Libdnet
PROTOTYPES: DISABLE

#
# The following are obsolete functions, but stills there for compatibility reasons
#

SV *
_obsolete_addr_cmp(SvA, SvB)
		SV *SvA;
		SV *SvB;
	PROTOTYPE: $$
	CODE:
		char *StrA, *StrB;
		struct addr SadA, SadB;
		int len;

		/*
		we cannot avoid ugly nesting, because
		return and goto are out of scope
		*/

		/* check input */
		if( !SvOK(SvA) ){
			warn("addr_cmp: undef input (1)\n");
			RETVAL = &PL_sv_undef;
		}else if( !SvOK(SvB) ){
			warn("addr_cmp: undef input (2)\n");
			RETVAL = &PL_sv_undef;
		}else{
			/* A: SV -> string */
			StrA = (char *) SvPV(SvA, len);
			/* A: string -> struct addr */
			if( addr_aton(StrA, &SadA) < 0 ){
				warn("addr_cmp: addr_aton: error (1)\n");
				RETVAL = &PL_sv_undef; 
			}else{
				/* B: SV -> string */
				StrB = (char *) SvPV(SvB, len);
				/* B: string -> struct addr */
				if( addr_aton(StrB, &SadB) < 0 ){
					warn("addr_cmp: addr_aton: error (2)\n");
					RETVAL = &PL_sv_undef; 
				}else{
					/* compute output */
					RETVAL = newSVnv((double) addr_cmp(&SadA, &SadB));
				}
			}
		}
	OUTPUT:
	RETVAL

SV *
_obsolete_addr_bcast(SvAd)
		SV *SvAd;
	PROTOTYPE: $
	CODE:
		char *StrAd;
		struct addr SadAd, SadBc;
		int len;

		/* check input */
		if( !SvOK(SvAd) ){
			warn("addr_bcast: undef input\n");
			RETVAL = &PL_sv_undef;
		}else{
			/* address: SV -> string */
			StrAd = (char *) SvPV(SvAd, len);
			/* address: string -> struct addr */
			if( addr_aton(StrAd, &SadAd) < 0 ){
				warn("addr_bcast: addr_aton: error\n");
				RETVAL = &PL_sv_undef; 
			/* compute output */
			}else if( addr_bcast(&SadAd, &SadBc) < 0 ){
				warn("addr_bcast: error\n");
				RETVAL = &PL_sv_undef;	
			}else{
				/* broadcast: struct addr -> SV */
				if( (StrAd = addr_ntoa((struct addr *) &SadBc)) == NULL){
					warn("addr_bcast: addr_ntoa: error\n");
					RETVAL = &PL_sv_undef;
				}else{
					/* 0 means Perl does strlen() itself */
					RETVAL = newSVpv(StrAd, 0);
				}
			}
		}
	OUTPUT:
	RETVAL

SV *
_obsolete_addr_net(SvAd)
		SV *SvAd;
	PROTOTYPE: $
	CODE:
		char *StrAd;
		struct addr SadAd, SadBc;
		int len;

		/* check input */
		if( !SvOK(SvAd) ){
			warn("addr_net: undef input\n");
			RETVAL = &PL_sv_undef;
		}else{
			/* address: SV -> string */
			StrAd = (char *) SvPV(SvAd, len);
			/* address: string -> struct addr */
			if( addr_aton(StrAd, &SadAd) < 0 ){
				warn("addr_net: addr_aton: error\n");
				RETVAL = &PL_sv_undef; 
			/* compute output */
			}else if( addr_net(&SadAd, &SadBc) < 0 ){
				warn("addr_net: error\n");
				RETVAL = &PL_sv_undef;	
			}else{
				/* broadcast: struct addr -> SV */
				if( (StrAd = addr_ntoa((struct addr *) &SadBc)) == NULL){
					warn("addr_net: addr_ntoa: error\n");
					RETVAL = &PL_sv_undef;
				}else{
					/* 0 means Perl does strlen() itself */
					RETVAL = newSVpv(StrAd, 0);
				}
			}
		}
	OUTPUT:
	RETVAL

SV*
_obsolete_arp_add(SvProtoAddr, SvHwAddr)
		SV *SvProtoAddr;
		SV *SvHwAddr;
	PROTOTYPE: $$
	CODE:
		arp_t *AtArp;
		struct arp_entry SarEntry;
		struct addr SadAddr;
		char *StrAddr;
		int len;

		/* check input */
		if( !SvOK(SvProtoAddr) ){
			warn("arp_add: undef input(1)\n");
			RETVAL = &PL_sv_undef;
		}else if( !SvOK(SvHwAddr) ){
			warn("arp_add: undef input(2)\n");
			RETVAL = &PL_sv_undef;
		}else{
			/* open arp handler */
			if( (AtArp = arp_open()) == NULL ){
				warn("arp_add: arp_open: error\n");
				RETVAL = &PL_sv_undef;
			}else{

				/* protocol address: SV -> string  */
				StrAddr = (char *) SvPV(SvProtoAddr, len);

				/* protocol address: string -> struct addr */
				if( addr_aton(StrAddr, &SadAddr) < 0 ){
					warn("arp_add: addr_aton: error (1)\n");
					RETVAL = &PL_sv_undef;
				}else{
					/* protocol address -> arp_entry */
					memcpy(&SarEntry.arp_pa, &SadAddr, sizeof(struct addr));

					/* hardware address: SV -> string  */
					StrAddr = (char *) SvPV(SvHwAddr, len);

					/* hardware address: string -> struct addr */
					if( addr_aton(StrAddr, &SadAddr) < 0 ){
						warn("arp_add: addr_aton: error (2)\n");
						RETVAL = &PL_sv_undef;
					}else{
						memcpy(&SarEntry.arp_ha, &SadAddr, sizeof(struct addr));

						/* add to ARP table */
						if( arp_add(AtArp, &SarEntry) < 0 ){
							warn("arp_add: error\n");
							RETVAL = &PL_sv_undef;
						}else{
							RETVAL = newSVnv(1);
						}
					}
				}

				/* close arp handler */
				arp_close(AtArp);
			}
		}
	OUTPUT:
	RETVAL

SV*
_obsolete_arp_delete(SvProtoAddr)
		SV *SvProtoAddr;
	PROTOTYPE: $
	CODE:
		arp_t *AtArp;
		struct arp_entry SarEntry;
		struct addr SadAddr;
		char *StrAddr;
		int len;

		/* check input */
		if( !SvOK(SvProtoAddr) ){
			warn("arp_delete: undef input\n");
			RETVAL = &PL_sv_undef;
		}else{
			/* open arp handler */
			if( (AtArp = arp_open()) == NULL ){
				warn("arp_get: arp_open: error\n");
				RETVAL = &PL_sv_undef;
			}else{

				/* convert input to string */
				StrAddr = (char *) SvPV(SvProtoAddr, len);

				/* convert input to struct addr */
				if( addr_aton(StrAddr, &SadAddr) < 0 ){
					warn("arp_delete: addr_aton: error\n");
					RETVAL = &PL_sv_undef;
				}else{
					memcpy(&SarEntry.arp_pa, &SadAddr, sizeof(struct addr));

					/* resolve protocol address with arp */
					if( arp_delete(AtArp, &SarEntry) < 0 ){
						/* do not warn: a request for a nonexistant address is valid */
						RETVAL = &PL_sv_undef;
					}else{
						RETVAL = newSVnv(1);
					}

				}

				/* close arp handler */
				arp_close(AtArp);
			}
		}
	OUTPUT:
	RETVAL

SV*
_obsolete_arp_get(SvProtoAddr)
		SV *SvProtoAddr;
	PROTOTYPE: $
	CODE:
		arp_t *AtArp;
		struct arp_entry SarEntry;
		struct addr SadAddr;
		char *StrAddr;
		int len;

		/* check input */
		if( !SvOK(SvProtoAddr) ){
			warn("arp_get: undef input\n");
			RETVAL = &PL_sv_undef;
		}else{
			/* open arp handler */
			if( (AtArp = arp_open()) == NULL ){
				warn("arp_get: arp_open: error\n");
				RETVAL = &PL_sv_undef;
			}else{

				/* convert input to string */
				StrAddr = (char *) SvPV(SvProtoAddr, len);

				/* convert input to struct addr */
				if( addr_aton(StrAddr, &SadAddr) < 0 ){
					warn("arp_get: addr_aton: error\n");
					RETVAL = &PL_sv_undef;
				}else{
					memcpy(&SarEntry.arp_pa, &SadAddr, sizeof(struct addr));

					/* resolve protocol address with arp */
					if( arp_get(AtArp, &SarEntry) < 0 ){
						/* do not warn: a request for a nonexistant address is valid */
						RETVAL = &PL_sv_undef;
					}else{

						/* convert output to string */
						if( (StrAddr = addr_ntoa( (struct addr *) &SarEntry.arp_ha)) == NULL){
							warn("arp_get: addr_ntoa: error\n");
							RETVAL = &PL_sv_undef;
						}else{
							/* 0 means Perl does strlen() itself */
							RETVAL = newSVpv(StrAddr, 0);
						}
					}
				}

				/* close arp handler */
				arp_close(AtArp);
			}
		}
	OUTPUT:
	RETVAL

HV *
_obsolete_intf_get(SvName)
		SV *SvName;
	PROTOTYPE: $
	CODE:
		HV *HvUndef;
		intf_t *ItIntf;
		struct intf_entry SieEntry;
		char *StrName;
		int len;

		/* prepare undefined hash */
		HvUndef = newHV();
		hv_undef(HvUndef);

		/* check input */
		if( !SvOK(SvName) ){
			warn("intf_get: undef input\n");
			RETVAL = HvUndef;
		}else{
			/* open intf handler */
			if( (ItIntf = intf_open()) == NULL ){
				warn("intf_get: intf_open: error\n");
				RETVAL = HvUndef;
			}else{
				/* name: SV -> string */
				StrName = (char *) SvPV(SvName, len);

				/* request interface */
				SieEntry.intf_len = sizeof(SieEntry);
				strncpy(SieEntry.intf_name, StrName, INTF_NAME_LEN);
				if( intf_get(ItIntf, &SieEntry) < 0 ){
					/* cannot warn, since the name may not exist */
					RETVAL = HvUndef;
				}else{
					RETVAL = intf2hash(&SieEntry);
				}

				/* close intf handler */
				intf_close(ItIntf);
			}
		}
	OUTPUT:
	RETVAL

HV *
_obsolete_intf_get_src(SvAddr)
		SV *SvAddr;
	PROTOTYPE: $
	CODE:
		HV *HvUndef;
		intf_t *ItIntf;
		struct intf_entry SieEntry;
		struct addr SaAddr;
		char *StrAddr;
		int len;

		/* prepare undefined hash */
		HvUndef = newHV();
		hv_undef(HvUndef);

		/* check input */
		if( !SvOK(SvAddr) ){
			warn("intf_get_src: undef input\n");
			RETVAL = HvUndef;
		}else{
			/* open intf handler */
			if( (ItIntf = intf_open()) == NULL ){
				warn("intf_get_src: intf_open: error\n");
				RETVAL = HvUndef;
			}else{
				/* addr: SV -> string */
				StrAddr = (char *) SvPV(SvAddr, len);

				/* addr: string -> struct addr */
				if( addr_aton(StrAddr, &SaAddr) < 0 ){
					warn("intf_get_src: addr_aton: error\n");
					RETVAL = HvUndef;
				}else{
					/* request interface */
					SieEntry.intf_len = sizeof(SieEntry);
					if( intf_get_src(ItIntf, &SieEntry, &SaAddr) < 0 ){
						/* cannot warn, since the name may not exist */
						RETVAL = HvUndef;
					}else{
						RETVAL = intf2hash(&SieEntry);
					}
				}

				/* close intf handler */
				intf_close(ItIntf);
			}
		}
	OUTPUT:
	RETVAL

HV *
_obsolete_intf_get_dst(SvAddr)
		SV *SvAddr;
	PROTOTYPE: $
	CODE:
		HV *HvUndef;
		intf_t *ItIntf;
		struct intf_entry SieEntry;
		struct addr SaAddr;
		char *StrAddr;
		int len;

		/* prepare undefined hash */
		HvUndef = newHV();
		hv_undef(HvUndef);

		/* check input */
		if( !SvOK(SvAddr) ){
			warn("intf_get_dst: undef input\n");
			RETVAL = HvUndef;
		}else{
			/* open intf handler */
			if( (ItIntf = intf_open()) == NULL ){
				warn("intf_get_dst: intf_open: error\n");
				RETVAL = HvUndef;
			}else{
				/* addr: SV -> string */
				StrAddr = (char *) SvPV(SvAddr, len);

				/* addr: string -> struct addr */
				if( addr_aton(StrAddr, &SaAddr) < 0 ){
					warn("intf_get_dst: addr_aton: error\n");
					RETVAL = HvUndef;
				}else{
					/* request interface */
					SieEntry.intf_len = sizeof(SieEntry);
					if( intf_get_dst(ItIntf, &SieEntry, &SaAddr) < 0 ){
						/* cannot warn, since the name may not exist */
						RETVAL = HvUndef;
					}else{
						RETVAL = intf2hash(&SieEntry);
					}
				}

				/* close intf handler */
				intf_close(ItIntf);
			}
		}
	OUTPUT:
	RETVAL

SV*
_obsolete_route_add(SvDstAddr, SvGwAddr)
		SV *SvDstAddr;
		SV *SvGwAddr;
	PROTOTYPE: $$
	CODE:
		route_t *RtRoute;
		struct route_entry SrtEntry;
		struct addr SadAddr;
		char *StrAddr;
		int len;

		/* check input */
		if( !SvOK(SvDstAddr) ){
			warn("route_add: undef input(1)\n");
			RETVAL = &PL_sv_undef;
		}else if( !SvOK(SvGwAddr) ){
			warn("route_add: undef input(2)\n");
			RETVAL = &PL_sv_undef;
		}else{
			/* open route handler */
			if( (RtRoute = route_open()) == NULL ){
				warn("route_add: route_open: error\n");
				RETVAL = &PL_sv_undef;
			}else{

				/* destination address: SV -> string  */
				StrAddr = (char *) SvPV(SvDstAddr, len);

				/* destination address: string -> struct addr */
				if( addr_aton(StrAddr, &SadAddr) < 0 ){
					warn("route_add: addr_aton: error (1)\n");
					RETVAL = &PL_sv_undef;
				}else{
					/* destination address -> route_entry */
					memcpy(&SrtEntry.route_dst, &SadAddr, sizeof(struct addr));

					/* gateway address: SV -> string  */
					StrAddr = (char *) SvPV(SvGwAddr, len);

					/* gateway address: string -> struct addr */
					if( addr_aton(StrAddr, &SadAddr) < 0 ){
						warn("route_add: addr_aton: error (2)\n");
						RETVAL = &PL_sv_undef;
					}else{
						memcpy(&SrtEntry.route_gw, &SadAddr, sizeof(struct addr));

						/* add to route table */
						if( route_add(RtRoute, &SrtEntry) < 0 ){
							warn("route_add: error\n");
							RETVAL = &PL_sv_undef;
						}else{
							RETVAL = newSVnv(1);
						}
					}
				}

				/* close route handler */
				route_close(RtRoute);
			}
		}
	OUTPUT:
	RETVAL

SV*
_obsolete_route_delete(SvDstAddr)
		SV *SvDstAddr;
	PROTOTYPE: $
	CODE:
		route_t *RtRoute;
		struct route_entry SrtEntry;
		struct addr SadAddr;
		char *StrAddr;
		int len;

		/* check input */
		if( !SvOK(SvDstAddr) ){
			warn("route_delete: undef input\n");
			RETVAL = &PL_sv_undef;
		}else{
			/* open route handler */
			if( (RtRoute = route_open()) == NULL ){
				warn("route_get: route_open: error\n");
				RETVAL = &PL_sv_undef;
			}else{

				/* convert input to string */
				StrAddr = (char *) SvPV(SvDstAddr, len);

				/* convert input to struct addr */
				if( addr_aton(StrAddr, &SadAddr) < 0 ){
					warn("route_delete: addr_aton: error\n");
					RETVAL = &PL_sv_undef;
				}else{
					memcpy(&SrtEntry.route_dst, &SadAddr, sizeof(struct addr));

					/* remove route */
					if( route_delete(RtRoute, &SrtEntry) < 0 ){
						/* do not warn: a request for a nonexistant address is valid */
						RETVAL = &PL_sv_undef;
					}else{
						RETVAL = newSVnv(1);
					}

				}

				/* close route handler */
				route_close(RtRoute);
			}
		}
	OUTPUT:
	RETVAL

SV*
_obsolete_route_get(SvDstAddr)
		SV *SvDstAddr;
	PROTOTYPE: $
	CODE:
		route_t *RtRoute;
		struct route_entry SrtEntry;
		struct addr SadAddr;
		char *StrAddr;
		int len;

		/* check input */
		if( !SvOK(SvDstAddr) ){
			warn("route_get: undef input\n");
			RETVAL = &PL_sv_undef;
		}else{
			/* open route handler */
			if( (RtRoute = route_open()) == NULL ){
				warn("route_get: route_open: error\n");
				RETVAL = &PL_sv_undef;
			}else{

				/* convert input to string */
				StrAddr = (char *) SvPV(SvDstAddr, len);

				/* convert input to struct addr */
				if( addr_aton(StrAddr, &SadAddr) < 0 ){
					warn("route_get: addr_aton: error\n");
					RETVAL = &PL_sv_undef;
				}else{
					memcpy(&SrtEntry.route_dst, &SadAddr, sizeof(struct addr));

					/* resolve protocol address with route */
					if( route_get(RtRoute, &SrtEntry) < 0 ){
						/* do not warn: a request for a nonexistant address is valid */
						RETVAL = &PL_sv_undef;
					}else{

						/* convert output to string */
						if( (StrAddr = addr_ntoa( (struct addr *) &SrtEntry.route_gw)) == NULL){
							warn("route_get: addr_ntoa: error\n");
							RETVAL = &PL_sv_undef;
						}else{
							/* 0 means Perl does strlen() itself */
							RETVAL = newSVpv(StrAddr, 0);
						}
					}
				}

				/* close route handler */
				route_close(RtRoute);
			}
		}
	OUTPUT:
	RETVAL

#
# The following are the new XS implementation.
# I prefixed with dnet_ in order to not clash with libdnet C functions used by 
# obsolete XS implementation.
#

#if defined DNET_INTF_H

IntfHandle *
dnet_intf_open()
   CODE:
      RETVAL = intf_open();
   OUTPUT:
      RETVAL

SV *
dnet_intf_get(handle, entry)
      IntfHandle *handle
      SV         *entry
   PREINIT:
      char        buf[1024];
      IntfEntry  *intfEntry;
      IntfEntry  *intfEntryPtr;
   INIT:
      intfEntry    = (IntfEntry *)buf;
      intfEntryPtr = NULL;
      memset(buf, 0, sizeof(buf));
      intfEntryPtr = intf_sv2c(entry, intfEntry);
      intfEntry->intf_len = sizeof(buf);
   CODE:
      if (intf_get(handle, intfEntryPtr) == -1) { XSRETURN_UNDEF; }
      else { RETVAL = intf_c2sv(intfEntry); }
   OUTPUT:
      RETVAL

SV *
dnet_intf_get_src(handle, src)
      IntfHandle *handle
      SV         *src
   PREINIT:
      char        buf[1024];
      IntfEntry  *intfEntry; 
      struct addr aSrc;
      int ret;
   INIT:
      intfEntry = (IntfEntry *)buf;
      memset(buf, 0, sizeof(buf));
      intfEntry->intf_len = sizeof(buf);
      memset(&aSrc, 0, sizeof(struct addr));
      ret = addr_aton(SvPV(src, PL_na), &aSrc);
   CODE:
      if (! ret && intf_get_src(handle, intfEntry, &aSrc) == -1) {
         XSRETURN_UNDEF;
      }
      else { RETVAL = intf_c2sv(intfEntry); }
   OUTPUT:
      RETVAL

SV *
dnet_intf_get_dst(handle, dst)
      IntfHandle *handle
      SV         *dst
   PREINIT:
      char        buf[1024];
      struct addr aDst;
      int ret;
      IntfEntry  *intfEntry;
   INIT:
      intfEntry = (IntfEntry *)buf;
      memset(buf, 0, sizeof(buf));
      intfEntry->intf_len = sizeof(buf);
      memset(&aDst, 0, sizeof(struct addr));
      ret = addr_aton(SvPV(dst, PL_na), &aDst);
   CODE:
      if (! ret && intf_get_dst(handle, intfEntry, &aDst) == -1) {
         XSRETURN_UNDEF;
      }
      else { RETVAL = intf_c2sv(intfEntry); }
   OUTPUT:
      RETVAL

int
dnet_intf_set(handle, entry)
      IntfHandle *handle
      SV         *entry
   PREINIT:
         IntfEntry *intfEntryPtr;
      IntfEntry  intfEntry;
   INIT:
      intfEntryPtr = NULL;
      intfEntryPtr = intf_sv2c(entry, &intfEntry);
   CODE:
      if (intf_set(handle, &intfEntry) == -1) { XSRETURN_UNDEF; }
      else { RETVAL = 1; }
   OUTPUT:
      RETVAL

int
dnet_intf_loop(handle, callback, data)
      IntfHandle *handle
      SV         *callback
      SV         *data
   CODE:
      if (keepSub == (SV *)NULL)
         keepSub = newSVsv(callback);
      else
         SvSetSV(keepSub, callback);
      RETVAL = intf_loop(handle, (intf_handler)intf_callback, data);
   OUTPUT:
      RETVAL

IntfHandle *
dnet_intf_close(handle)
      IntfHandle *handle
   CODE:
      RETVAL = intf_close(handle);
   OUTPUT:
      RETVAL

#endif

#if defined DNET_ARP_H

ArpHandle *
dnet_arp_open()
   CODE:
      RETVAL = arp_open();
   OUTPUT:
      RETVAL

int
dnet_arp_add(handle, entry)
      ArpHandle *handle
      SV        *entry
   PREINIT:
      ArpEntry  arpEntry;
      ArpEntry *arpEntryPtr;
   INIT:
      arpEntryPtr = NULL;
      arpEntryPtr = arp_sv2c(entry, &arpEntry);
   CODE:
      RETVAL = arp_add(handle, arpEntryPtr);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
      else { RETVAL = 1; }
   OUTPUT:
      RETVAL

int
dnet_arp_delete(handle, entry)
      ArpHandle *handle
      SV        *entry
   PREINIT:
      ArpEntry  arpEntry;
      ArpEntry *arpEntryPtr;
   INIT:
      arpEntryPtr = NULL;
      arpEntryPtr = arp_sv2c(entry, &arpEntry);
   CODE:
      RETVAL = arp_delete(handle, arpEntryPtr);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
      else { RETVAL = 1; }
   OUTPUT:
      RETVAL

SV *
dnet_arp_get(handle, entry)
      ArpHandle *handle
      SV        *entry
   PREINIT:
      ArpEntry *arpEntryPtr;
      ArpEntry  arpEntry;
   INIT:
      arpEntryPtr = NULL;
      arpEntryPtr = arp_sv2c(entry, &arpEntry);
   CODE:
      if (arp_get(handle, arpEntryPtr) == -1) { XSRETURN_UNDEF; }
      else { RETVAL = arp_c2sv(arpEntryPtr); }
   OUTPUT:
      RETVAL 

int
dnet_arp_loop(handle, callback, data)
      ArpHandle *handle
      SV        *callback
      SV        *data
   CODE:
      if (keepSub == (SV *)NULL)
         keepSub = newSVsv(callback);
      else
         SvSetSV(keepSub, callback);
      RETVAL = arp_loop(handle, (arp_handler)arp_callback, data);
   OUTPUT:
      RETVAL

ArpHandle *
dnet_arp_close(handle)
      ArpHandle *handle
   CODE:
      RETVAL = arp_close(handle);
   OUTPUT:
      RETVAL

#endif

#if defined DNET_ROUTE_H

RouteHandle *
dnet_route_open()
   CODE:
      RETVAL = route_open();
   OUTPUT:
      RETVAL

int
dnet_route_add(handle, entry)
      RouteHandle *handle
      SV          *entry
   PREINIT:
      RouteEntry  routeEntry;
      RouteEntry *routeEntryPtr;
   INIT:
      routeEntryPtr = NULL;
      routeEntryPtr = route_sv2c(entry, &routeEntry);
   CODE:
      RETVAL = route_add(handle, routeEntryPtr);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
      else { RETVAL = 1; }
   OUTPUT:
      RETVAL

int
dnet_route_delete(handle, entry)
      RouteHandle *handle
      SV          *entry
   PREINIT:
      RouteEntry  routeEntry;
      RouteEntry *routeEntryPtr;
   INIT:
      routeEntryPtr = NULL;
      routeEntryPtr = route_sv2c(entry, &routeEntry);
   CODE:
      RETVAL = route_delete(handle, routeEntryPtr);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
      else { RETVAL = 1; }
   OUTPUT:
      RETVAL

SV *
dnet_route_get(handle, entry)
      RouteHandle *handle
      SV          *entry
   PREINIT:
      RouteEntry  routeEntry;
      RouteEntry *routeEntryPtr;
   INIT:
      routeEntryPtr = NULL;
      routeEntryPtr = route_sv2c(entry, &routeEntry);
   CODE:
      if (route_get(handle, routeEntryPtr) == -1) { XSRETURN_UNDEF; }
      else { RETVAL = route_c2sv(routeEntryPtr); }
   OUTPUT:
      RETVAL 

int
dnet_route_loop(handle, callback, data)
      RouteHandle *handle
      SV          *callback
      SV          *data
   CODE:
      if (keepSub == (SV *)NULL)
         keepSub = newSVsv(callback);
      else
         SvSetSV(keepSub, callback);
      RETVAL = route_loop(handle, (route_handler)route_callback, data);
      //printf("RETVAL: %d\n", RETVAL);
   OUTPUT:
      RETVAL

RouteHandle *
dnet_route_close(handle)
      RouteHandle *handle
   CODE:
      RETVAL = route_close(handle);
   OUTPUT:
      RETVAL

#endif

#if defined DNET_FW_H

FwHandle *
dnet_fw_open()
   CODE:
      RETVAL = fw_open();
   OUTPUT:
      RETVAL

int
dnet_fw_add(handle, rule)
      FwHandle *handle
      SV       *rule
   PREINIT:
      FwRule  fwRule;
      FwRule *fwRulePtr;
   INIT:
      fwRulePtr = NULL;
      fwRulePtr = fw_sv2c(rule, &fwRule);
   CODE:
      RETVAL = fw_add(handle, fwRulePtr);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
      else { RETVAL = 1; }
   OUTPUT:
      RETVAL

int
dnet_fw_delete(handle, rule)
      FwHandle *handle
      SV       *rule
   PREINIT:
      FwRule  fwRule;
      FwRule *fwRulePtr;
   INIT:
      fwRulePtr = NULL;
      fwRulePtr = fw_sv2c(rule, &fwRule);
   CODE:
      RETVAL = fw_delete(handle, fwRulePtr);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
      else { RETVAL = 1; }
   OUTPUT:
      RETVAL

int
dnet_fw_loop(handle, callback, data)
      FwHandle *handle
      SV       *callback
      SV       *data
   CODE:
      if (keepSub == (SV *)NULL)
         keepSub = newSVsv(callback);
      else
         SvSetSV(keepSub, callback);
      RETVAL = fw_loop(handle, (fw_handler)fw_callback, data);
   OUTPUT:
      RETVAL

FwHandle *
dnet_fw_close(handle)
      FwHandle *handle
   CODE:
      RETVAL = fw_close(handle);
   OUTPUT:
      RETVAL

#endif

#if defined DNET_TUN_H

TunHandle *
dnet_tun_open(src, dst, size)
      SV *src
      SV *dst
      int size
   INIT:
      struct addr aSrc;
      struct addr aDst;
      memset(&aSrc, 0, sizeof(struct addr));
      memset(&aDst, 0, sizeof(struct addr));
   CODE:
      if (addr_aton(SvPV(src, PL_na), &aSrc)) { XSRETURN_UNDEF; }
      if (addr_aton(SvPV(dst, PL_na), &aDst)) { XSRETURN_UNDEF; }
      RETVAL = tun_open(&aSrc, &aDst, size);
   OUTPUT:
      RETVAL

int
dnet_tun_fileno(handle)
      TunHandle *handle
   CODE:
      RETVAL = tun_fileno(handle);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
   OUTPUT:
      RETVAL

const char *
dnet_tun_name(handle)
      TunHandle *handle
   CODE:
      RETVAL = tun_name(handle);
      if (RETVAL == NULL) { XSRETURN_UNDEF; }
   OUTPUT:
      RETVAL

int
dnet_tun_send(handle, buf, size)
      TunHandle *handle
      SV        *buf
      int        size
   CODE:
      RETVAL = tun_send(handle, SvPV(buf, PL_na), size);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
   OUTPUT:
      RETVAL

SV *
dnet_tun_recv(handle, size)
      TunHandle *handle
      int        size
   PREINIT:
      int read;
      unsigned char buf[size+1];
   INIT:
      memset(buf, 0, size+1);
   CODE:
      if ((read = tun_recv(handle, buf, size)) > 0) {
         RETVAL = newSVpv(buf, read);
      }
      else { XSRETURN_UNDEF; }
   OUTPUT:
      RETVAL

TunHandle *
dnet_tun_close(handle)
      TunHandle *handle
   CODE:
      RETVAL = tun_close(handle);
   OUTPUT:
      RETVAL

#endif

#if defined DNET_ETH_H

EthHandle *
dnet_eth_open(device)
      SV *device
   CODE:
      RETVAL = eth_open(SvPV(device, PL_na));
   OUTPUT:
      RETVAL

SV *
dnet_eth_get(handle)
      EthHandle *handle
   PREINIT:
      char *addr;
      EthAddr a;
   INIT:
      memset(&a, 0, sizeof(EthAddr));
   CODE:
      if (eth_get(handle, &a) == -1)     { XSRETURN_UNDEF; }
      if ((addr = eth_ntoa(&a)) == NULL) { XSRETURN_UNDEF; }
      else { RETVAL = newSVpv(addr, 0); }
   OUTPUT:
      RETVAL

int
dnet_eth_set(handle, addr)
      EthHandle *handle
      SV        *addr
   CODE:
      RETVAL = eth_set(handle, (const EthAddr *)SvPV(addr, PL_na));
      if (RETVAL == -1) { XSRETURN_UNDEF; }
   OUTPUT:
      RETVAL

int
dnet_eth_send(handle, buf, size)
      EthHandle *handle
      SV        *buf
      int        size
   CODE:
      RETVAL = eth_send(handle, SvPV(buf, PL_na), size);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
   OUTPUT:
      RETVAL

EthHandle *
dnet_eth_close(handle)
      EthHandle *handle
   CODE:
      RETVAL = eth_close(handle);
   OUTPUT:
      RETVAL

#endif

#if defined DNET_IP_H

IpHandle *
dnet_ip_open()
   CODE:
      RETVAL = ip_open();
   OUTPUT:
      RETVAL

int
dnet_ip_send(handle, buf, size)
      IpHandle *handle
      SV       *buf
      int       size
   CODE:
      RETVAL = ip_send(handle, SvPV(buf, PL_na), size);
      if (RETVAL == -1) { XSRETURN_UNDEF; }
   OUTPUT:
      RETVAL

void
dnet_ip_checksum(buf, size)
      SV *buf
      int size
   CODE:
      ip_checksum(SvPV(buf, PL_na), size);

IpHandle *
dnet_ip_close(handle)
      IpHandle *handle
   CODE:
      RETVAL = ip_close(handle);
   OUTPUT:
      RETVAL

#endif