/* ToolKit.xs
*
* Copyright 2003 - 2011, Michael Robinton <michael@bizsystems.com>
*
* Michael Robinton <michael@bizsystems.com>
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of either:
*
* a) the GNU General Public License as published by the Free
* Software Foundation; either version 2, or (at your option) any
* later version, or
*
* b) the "Artistic License" which comes with this distribution.
*
* 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 either
* the GNU General Public License or the Artistic License for more details.
*
* You should have received a copy of the Artistic License with this
* distribution, in the file named "Artistic". If not, I'll be glad to provide
* one.
*
* You should also have received a copy of the GNU General Public License
* along with this program in the file named "Copying". If not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307, USA
*
* or visit their web page on the internet at:
*
* http://www.gnu.org/copyleft/gpl.html.
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "u_intxx.h"
/* for size of string buffer below */
#include <netdb.h>
#include <resolv.h>
#include <arpa/nameser.h>
#ifdef BIND_4_COMPAT
#include <arpa/nameser_compat.h>
#endif
#include "ToolKit.h"
/* for clock */
#include <time.h>
/* for resolver */
struct __res_state res;
#ifdef RES_XINIT
#define RES_Init RES_INIT | RES_XINIT
#else
#define RES_Init RES_INIT
#endif
/* from /usr/include/arpa/nameser_compat.h, modified a little */
typedef struct {
unsigned id :16; /* query identification number */
#ifdef host_is_BIG_ENDIAN
/* fields in third byte */
unsigned qr :1; /* response flag */
unsigned opcode :4; /* purpose of message */
unsigned aa :1; /* authoritive answer */
unsigned tc :1; /* truncated message */
unsigned rd :1; /* recursion desired */
/* fields in fourth byte */
unsigned ra :1; /* recursion available */
unsigned z :1; /* unused bits (MBZ as of 4.9.3a3) */
unsigned ad :1; /* authentic data from named */
unsigned cd :1; /* checking disabled by resolver */
unsigned rcode :4; /* response code */
#else
# ifdef host_is_LITTLE_ENDIAN
/* fields in third byte */
unsigned rd :1; /* recursion desired */
unsigned tc :1; /* truncated message */
unsigned aa :1; /* authoritive answer */
unsigned opcode :4; /* purpose of message */
unsigned qr :1; /* response flag */
/* fields in fourth byte */
unsigned rcode :4; /* response code */
unsigned cd :1; /* checking disabled by resolver */
unsigned ad :1; /* authentic data from named */
unsigned z :1; /* unused bits (MBZ as of 4.9.3a3) */
unsigned ra :1; /* recursion available */
# else
# error ENDIANness is not defined
# endif
#endif
/* remaining bytes */
unsigned qdcount :16; /* number of question entries */
unsigned ancount :16; /* number of answer entries */
unsigned nscount :16; /* number of authority entries */
unsigned arcount :16; /* number of resource entries */
} MY_HEADER;
u_char * dnptrs[20];
struct in_addr i2p;
struct timeval tv;
#ifdef lastchanceTEST
#ifndef NO_RESOLV_CONF
#define NO_RESOLV_CONF
#endif
#endif
struct __res_state my_res_state;
struct sockaddr_in mysa;
#ifdef _PATH_RESCONF
char *path = _PATH_RESCONF;
size_t pathz = sizeof(_PATH_RESCONF);
#else
char *path = NULL;
size_t pathz = 0;
#endif
/* **************************** *
* return int's, long's *
* from char string *
* **************************** *
*/
u_short
gint16(u_char * cp)
{
u_short i;
NS_GET16(i,cp);
return(i);
}
u_int32_t
gint32(u_char * cp)
{
u_int32_t i;
NS_GET32(i,cp);
return(i);
}
void
gput16(u_char * cp, unsigned int i)
{
NS_PUT16(i,cp);
}
void
gput32(u_char * cp, u_int32_t v)
{
NS_PUT32(v,cp);
}
u_char *
ns_ptr(int i)
{
i2p.s_addr = _res.nsaddr_list[i].sin_addr.s_addr;
return((u_char *)&i2p.s_addr);
}
void
mysin()
{
#ifdef USELOOPBACK
mysa.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1);
#else
mysa.sin_addr.s_addr = INADDR_ANY;
#endif
}
int
lchance()
{
#ifndef NO_RESOLV_CONF
return 0;
#else
#ifdef lastchanceTEST
my_res_state.options = RES_INIT;
if (res_ninit(&my_res_state) != 0) /* punt if we can not initialize resolver interface */
return 0;
return my_res_state.nscount;
#else
memset(&my_res_state,0,sizeof(my_res_state));
return get_nameservers(&my_res_state);
#endif
#endif
}
MODULE = Net::DNS::ToolKit PACKAGE = Net::DNS::ToolKit
PROTOTYPES: DISABLE
# include functions for inet_aton, inet_ntoa, dn_expand
INCLUDE: xs_include/dn_expand.inc
# int dn_comp(unsigned char *exp_dn, unsigned char *comp_dn,
# int length, unsigned char **dnptrs, unsigned char **lastdnptr);
#
# dn_comp
# dn_comp() compresses the domain name exp_dn and stores it in
# the buffer comp_dn of length 'length'. The compression uses
# an array of pointers dnptrs to previously compressed names
# in the current message. The first pointer points to the
# beginning of the message and the list ends with NULL. The
# limit of the array is specified by lastdnptr. if dnptr
# is NULL, domain names are not compressed. If lastdnptr is
# NULL, the list of labels is not updated.
#
void
dn_comp(buffer, offset, name,...)
SV * buffer
int offset
SV * name
PROTOTYPE: $$$;@
PREINIT:
AV * dptr;
SV **aptrs;
STRLEN len, size, bsize;
u_char **lastdnptr, **aoff;
u_char * exp_dn, * msg, * comp_dn;
int i, v, dnsize;
PPCODE:
if (! SvROK(buffer) || ! SvROK(name))
XSRETURN_EMPTY;
# see: perlapi on 'svtype' and /usr/lib/perl5/i386-linux/CORE/sv.h
name = SvRV(name);
if (SvTYPE(name) == SVt_PVGV) /* debugging, skip dn_comp */
exp_dn = (u_char *)SvPV(GvSV(name), len);
if (SvPOK(name)) /* normal */
exp_dn = (u_char *)SvPV(name, len);
else /* punt, not scalar or glob */
XSRETURN_EMPTY;
buffer = SvRV(buffer);
if (! SvPOK(buffer))
XSRETURN_EMPTY;
# get the size of the buffer
(void)SvPV(buffer,size);
if (size != offset) /* punt if it does not match offset */
XSRETURN_EMPTY;
# add some space at the end of the string, get pointer
msg = (u_char *)SvGROW(buffer, (STRLEN)(size + MAXDNAME));
comp_dn = msg + offset;
# setup dnptrs from outside or init them to zero
dnsize = sizeof dnptrs/sizeof dnptrs[0];
lastdnptr = dnptrs + dnsize;
if (items > 3 && SvTRUE(ST(3)) && SvROK(ST(3))) { /* defined, should be \@dnptrs */
dptr = (AV *)SvRV(ST(3)); /* array pointer */
# external array must be exactly the same size as internal one
i = av_len(dptr);
if ( i != dnsize -1)
XSRETURN_EMPTY;
for(i=0;i<dnsize;i++) {
aptrs = av_fetch(dptr,i,0);
if (aptrs == NULL) /* should never happen */
XSRETURN_EMPTY;
v = SvIV(*aptrs);
if (i == 0 || v)
dnptrs[i] = msg + v;
else
dnptrs[i] = NULL;
}
} else {
memset(dnptrs,0,sizeof(dnptrs));
dnptrs[0] = msg;
}
if (SvTYPE(name) == SVt_PVGV)
dnptrs[0] = NULL; /* do not compress */
len = dn_comp((char *)exp_dn,comp_dn,MAXDNAME,dnptrs,lastdnptr);
# set the string length to the new real length
SvCUR_set(buffer, (I32)(size + len));
EXTEND(SP, dnsize + 1);
PUSHs(sv_2mortal(newSViv(size + len )));
for(i=0;i<dnsize;i++) {
if (dnptrs[i] == NULL)
PUSHs(sv_2mortal(newSViv(0)));
else
PUSHs(sv_2mortal(newSViv(dnptrs[i] - msg)));
}
XSRETURN(dnsize + 1);
# return the header values
# ID, QR, Opcode, AA, TC, RD, RA, MBZ, AD, CD, RCODE, QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT
void
gethead(header)
SV * header
PROTOTYPE: $
PREINIT:
STRLEN len;
MY_HEADER * hp;
PPCODE:
if (! SvROK(header))
XSRETURN_EMPTY;
hp = (MY_HEADER *)SvPV(SvRV(header),len);
EXTEND(SP, 16);
PUSHs(sv_2mortal(newSViv(NS_HFIXEDSZ)));
PUSHs(sv_2mortal(newSViv(ntohs(hp->id))));
PUSHs(sv_2mortal(newSViv(hp->qr)));
PUSHs(sv_2mortal(newSViv(hp->opcode)));
PUSHs(sv_2mortal(newSViv(hp->aa)));
PUSHs(sv_2mortal(newSViv(hp->tc)));
PUSHs(sv_2mortal(newSViv(hp->rd)));
PUSHs(sv_2mortal(newSViv(hp->ra)));
PUSHs(sv_2mortal(newSViv(hp->z)));
PUSHs(sv_2mortal(newSViv(hp->ad)));
PUSHs(sv_2mortal(newSViv(hp->cd)));
PUSHs(sv_2mortal(newSViv(hp->rcode)));
PUSHs(sv_2mortal(newSViv(ntohs(hp->qdcount))));
PUSHs(sv_2mortal(newSViv(ntohs(hp->ancount))));
PUSHs(sv_2mortal(newSViv(ntohs(hp->nscount))));
PUSHs(sv_2mortal(newSViv(ntohs(hp->arcount))));
XSRETURN(16);
void
parse_char(ch)
unsigned char ch
PROTOTYPE: $
PREINIT:
u_char bmask[] = {128,64,32,16,8,4,2,1};
unsigned int i, hi, lo, tens[] = {1000,100,10,1, 1000,100,10,1};
char out[15];
PPCODE:
hi = lo = 0;
for(i=0;i<4;i++) {
if (ch & bmask[i])
hi += tens[i];
}
for(i=4;i<8;i++) {
if (ch & bmask[i])
lo += tens[i];
}
EXTEND(SP,4);
sprintf(out,"%04d_%04d",hi,lo);
PUSHs(sv_2mortal(newSVpv(out,0)));
i = (int)ch;
sprintf(out,"0x%02X",i);
PUSHs(sv_2mortal( newSVpv(out,0)));
sprintf(out,"%3d",i);
PUSHs(sv_2mortal(newSVpv(out,0)));
if (i < 0x20 || i > 0x7E) {
sprintf(out,"%s","");
PUSHs(sv_2mortal(newSVpv(out,0)));
} else {
sprintf(out,"%c",ch);
PUSHs(sv_2mortal(newSVpv(out,0)));
}
XSRETURN(4);
unsigned char
get1char(buffer,off)
SV * buffer
unsigned int off
PROTOTYPE: $$
PREINIT:
STRLEN size;
unsigned char * cp;
CODE:
if (!SvROK(buffer)) /* not a pointer */
XSRETURN_UNDEF;
cp = (u_char *)(SvPV(SvRV(buffer),size) + off);
if (size <= off) /* offset beyond end */
XSRETURN_UNDEF;
RETVAL = *cp;
OUTPUT:
RETVAL
void
getstring(buffer,off,len)
SV * buffer
unsigned int off
unsigned int len
PROTOTYPE: $$$
PREINIT:
STRLEN size;
unsigned char * cp;
SV * out;
PPCODE:
if (!SvROK(buffer)) /* not a pointer */
XSRETURN_EMPTY;
cp = (u_char *)(SvPV(SvRV(buffer),size) + off);
if (off + len > size) /* offset beyond end */
XSRETURN_EMPTY;
out = sv_newmortal();
sv_setpvn(out, (char *)cp, len );
XPUSHs(out);
if (GIMME_V == G_ARRAY) {
XPUSHs(sv_2mortal(newSViv(off + len)));
XSRETURN(2);
}
XSRETURN(1);
unsigned int
putstring(buffer,off,string)
SV * buffer
unsigned int off
SV * string
PROTOTYPE: $$$
PREINIT:
SV * buf;
STRLEN size, len;
unsigned char * cp, * bp;
CODE:
if (!SvROK(buffer)) /* not a pointer */
XSRETURN_UNDEF;
if (!SvROK(string)) /* not a pointer */
XSRETURN_UNDEF;
buf = SvRV(buffer);
(void)SvPV(buf,size);
if (off > size) /* not a valid offset */
XSRETURN_UNDEF;
cp = (u_char *)SvPV(SvRV(string),len);
if (off + len > MAXDNAME) /* too big to add */
XSRETURN_UNDEF;
if (off < size)
SvCUR_set(buf,off);
sv_catpvn(buf, (char *)cp, len);
SvCUR_set(buf, (I32)(off + len));
RETVAL = off + len;
OUTPUT:
RETVAL
# void
# get32
void
get16(buffer,off)
SV * buffer
unsigned int off
ALIAS:
Net::DNS::ToolKit::get32 = 1
PREINIT:
SV * out;
STRLEN size;
u_char * cp;
PPCODE:
if (GIMME_V == G_VOID)
XSRETURN_UNDEF; /* punt, nothing to return */
if (!SvROK(buffer)) {
bail:
if (GIMME_V != G_ARRAY)
XSRETURN_UNDEF;
else
XSRETURN_EMPTY;
}
cp = (u_char *)(SvPV(SvRV(buffer),size) + off);
if (ix) {
off += NS_INT32SZ;
if (off > size) /* punt if pointing beyond end of buff */
goto bail;
XPUSHs(sv_2mortal(newSViv(gint32(cp))));
} else {
off += NS_INT16SZ;
if (off > size) /* punt if pointing beyond end of buff */
goto bail;
XPUSHs(sv_2mortal(newSViv(gint16(cp))));
}
if (GIMME_V == G_ARRAY) {
XPUSHs(sv_2mortal(newSViv(off)));
XSRETURN(2);
}
XSRETURN(1);
# void
# put1char, put32
unsigned int
put16(buffer,off,val_long)
SV * buffer
unsigned int off
SV * val_long
ALIAS:
Net::DNS::ToolKit::put32 = 1
Net::DNS::ToolKit::put1char = 2
PREINIT:
STRLEN size;
U32 val;
unsigned int i, ns_size = NS_INT16SZ;
u_char c, * cp, blank[NS_INT32SZ];
CODE:
if (!SvROK(buffer))
XSRETURN_UNDEF;
buffer = SvRV(buffer);
(void)SvPV(buffer,size); /* get size of buffer */
if (off > size) /* punt if pointing beyond end of buff */
XSRETURN_UNDEF;
val = SvUV(val_long);
if (ix == 1)
ns_size = NS_INT32SZ;
else if (ix == 2) {
if (val > 255)
XSRETURN_UNDEF;
ns_size = 1;
c = (u_char)val;
}
else {
if (val > 65535)
XSRETURN_UNDEF;
i = (unsigned int)val;
}
if (off + ns_size > size) /* add space at end if needed */
sv_catpvn(buffer,(char *)blank,ns_size);
cp = (u_char *)(SvPV(buffer,size) + off);
if (ix == 1)
gput32(cp,val);
else if (ix == 2)
*cp = c;
else
gput16(cp,i);
RETVAL = off + ns_size;
OUTPUT:
RETVAL
void
getIPv4(buffer,off)
SV * buffer
unsigned int off
PREINIT:
SV * netaddr;
STRLEN size;
u_char * cp, out[NS_INADDRSZ];
PPCODE:
if (GIMME_V == G_VOID)
XSRETURN_UNDEF; /* punt, nothing to return */
if (!SvROK(buffer)) {
bail:
if (GIMME_V != G_ARRAY)
XSRETURN_UNDEF;
else
XSRETURN_EMPTY;
}
cp = (u_char *)(SvPV(SvRV(buffer),size) + off);
off += NS_INADDRSZ;
if (off > size) /* punt if pointing beyond end of buff */
goto bail;
netaddr = sv_newmortal();
sv_setpvn(netaddr, (char *)cp, NS_INADDRSZ );
XPUSHs(netaddr);
if (GIMME_V == G_ARRAY) {
XPUSHs(sv_2mortal(newSViv( off)));
XSRETURN(2);
}
XSRETURN(1);
unsigned int
putIPv4(buffer,off,netaddr)
SV * buffer
unsigned int off
unsigned char * netaddr
PREINIT:
STRLEN size, discard;
u_char * cp, blank[NS_INADDRSZ];
CODE:
if (!SvROK(buffer))
XSRETURN_UNDEF;
buffer = SvRV(buffer);
(void)SvPV(buffer,size); /* get size of buffer */
if (off > size) /* punt if pointing beyond end of buff */
XSRETURN_UNDEF;
if (off + NS_INADDRSZ > size)
sv_catpvn(buffer,(char *)blank,NS_INADDRSZ); /* extend buffer if needed */
cp = (u_char *)(SvPV(buffer, discard) + size);
memcpy(cp,netaddr,NS_INADDRSZ);
RETVAL = (int)(size + NS_INADDRSZ);
OUTPUT:
RETVAL
void
getIPv6(buffer,off)
SV * buffer
unsigned int off
PREINIT:
SV * ipv6addr;
STRLEN size;
u_char * cp, out[NS_IN6ADDRSZ];
PPCODE:
if (GIMME_V == G_VOID)
XSRETURN_UNDEF; /* punt, nothing to return */
if (!SvROK(buffer)) {
bail:
if (GIMME_V != G_ARRAY)
XSRETURN_UNDEF;
else
XSRETURN_EMPTY;
}
cp = (u_char *)(SvPV(SvRV(buffer),size) + off);
off += NS_IN6ADDRSZ;
if (off > size) /* punt if pointing beyond end of buff */
goto bail;
ipv6addr = sv_newmortal();
sv_setpvn(ipv6addr, (char *)cp, NS_IN6ADDRSZ );
XPUSHs(ipv6addr);
if (GIMME_V == G_ARRAY) {
XPUSHs(sv_2mortal(newSViv( off)));
XSRETURN(2);
}
XSRETURN(1);
unsigned int
putIPv6(buffer,off,ipv6addr)
SV * buffer
unsigned int off
unsigned char * ipv6addr
PREINIT:
STRLEN size, discard;
u_char * cp, blank[NS_IN6ADDRSZ];
CODE:
if (!SvROK(buffer))
XSRETURN_UNDEF;
buffer = SvRV(buffer);
(void)SvPV(buffer,size); /* get size of buffer */
if (off > size) /* punt if pointing beyond end of buff */
XSRETURN_UNDEF;
if (off + NS_IN6ADDRSZ > size)
sv_catpvn(buffer,(char *)blank,NS_IN6ADDRSZ); /* extend buffer if needed */
cp = (u_char *)(SvPV(buffer, discard) + size);
memcpy(cp,ipv6addr,NS_IN6ADDRSZ);
RETVAL = (int)(size + NS_IN6ADDRSZ);
OUTPUT:
RETVAL
void
gettimeofday()
PREINIT:
SV * tmp; /* older perl does not know about newSVuv */
PPCODE:
if (gettimeofday(&tv,NULL)) { /* any error */
if (GIMME_V == G_ARRAY)
XSRETURN_EMPTY;
else
XSRETURN_UNDEF;
}
tmp = newSViv(tv.tv_sec);
sv_setuv(tmp,tv.tv_sec);
XPUSHs(sv_2mortal(tmp));
if (GIMME_V == G_ARRAY) {
tmp = newSViv(tv.tv_usec);
sv_setuv(tmp,tv.tv_usec);
XPUSHs(sv_2mortal(tmp));
XSRETURN(2);
}
XSRETURN(1);
void
get_default()
PPCODE:
mysin();
XPUSHs(sv_2mortal(newSVpvn((char *)&mysa.sin_addr, NS_INADDRSZ)));
XSRETURN(1);
void
get_path()
PREINIT:
SV * out;
PPCODE:
if (path == NULL)
XSRETURN_UNDEF;
out = sv_newmortal();
sv_setpvn(out, path, (STRLEN)pathz);
XPUSHs(out);
XSRETURN(1);
void
lastchance()
PREINIT:
int i, nscount;
u_char * netptr;
PPCODE:
if ((nscount = lchance()) < 1) {
if (GIMME_V != G_ARRAY)
XSRETURN_UNDEF;
else
XSRETURN_EMPTY;
}
if (GIMME_V != G_ARRAY)
nscount = 1;
for(i=0;i<nscount;i++) {
netptr = ns_ptr(i);
XPUSHs(sv_2mortal(newSVpvn((char *)netptr, NS_INADDRSZ)));
}
XSRETURN(nscount);