#ifdef __cplusplus extern "C" { #endif #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #ifdef __cplusplus } #endif #include "patchlevel.h" #if PATCHLEVEL < 5 # ifndef PL_sv_undef # define PL_sv_undef sv_undef # endif # ifndef PL_na # define PL_na na # endif #endif #ifdef _BSDRAW_ #define BSDFIX(a) (a) #else #define BSDFIX(a) htons(a) #endif #ifdef _SOLARIS_ #include "solaris.h" #else #include #endif #include "ifaddrlist.h" #include #include #include #include #include #ifdef _ETH_ #define ETH_ALEN 6 struct ether_header { u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */ u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */ u_int16_t ether_type; /* packet type ID field */ }; #endif struct iphdr { #if __BYTE_ORDER == __LITTLE_ENDIAN u_int8_t ihl:4; u_int8_t version:4; #elif __BYTE_ORDER == __BIG_ENDIAN u_int8_t version:4; u_int8_t ihl:4; #else #error "Please fix " #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ }; struct tcphdr { u_int16_t source; u_int16_t dest; u_int32_t seq; u_int32_t ack_seq; #if __BYTE_ORDER == __LITTLE_ENDIAN u_int16_t res1:4; u_int16_t doff:4; u_int16_t fin:1; u_int16_t syn:1; u_int16_t rst:1; u_int16_t psh:1; u_int16_t ack:1; u_int16_t urg:1; u_int16_t res2:2; #elif __BYTE_ORDER == __BIG_ENDIAN u_int16_t doff:4; u_int16_t res1:4; u_int16_t res2:2; u_int16_t urg:1; u_int16_t ack:1; u_int16_t psh:1; u_int16_t rst:1; u_int16_t syn:1; u_int16_t fin:1; #else #error "Adjust your defines" #endif u_int16_t window; u_int16_t check; u_int16_t urg_ptr; }; struct icmphdr { u_int8_t type; /* message type */ u_int8_t code; /* type sub-code */ u_int16_t checksum; union { struct { u_int16_t id; u_int16_t sequence; } echo; /* echo datagram */ u_int32_t gateway; /* gateway address */ struct { u_int16_t unused; u_int16_t mtu; } frag; /* path mtu discovery */ } un; }; struct udphdr { u_int16_t source; u_int16_t dest; u_int16_t len; u_int16_t check; }; #define TCPHDR 20 #pragma pack(1) typedef struct itpkt { struct iphdr ih; struct tcphdr th; } ITPKT; typedef struct iipkt { struct iphdr ih; struct icmphdr ich; } IIPKT; typedef struct iupkt { struct iphdr ih; struct udphdr uh; } IUPKT; unsigned short ip_in_cksum(struct iphdr *iph, unsigned short *ptr, int nbytes); unsigned short in_cksum(unsigned short *ptr, int nbytes); int rawsock(void); u_long host_to_ip (char *host_name); void pkt_send(int fd, unsigned char *sock, u_char *pkt, int size); int linkoffset(int); static int not_here(s) char *s; { croak("%s not implemented on this architecture", s); return -1; } static double constant(name, arg) char *name; int arg; { errno = 0; switch (*name) { case 'A': break; case 'B': break; case 'C': break; case 'D': break; case 'E': break; case 'F': break; case 'G': break; case 'H': break; case 'I': break; case 'J': break; case 'K': break; case 'L': break; case 'M': break; case 'N': break; case 'O': break; case 'P': if (strEQ(name, "PCAP_ERRBUF_SIZE")) #ifdef PCAP_ERRBUF_SIZE return PCAP_ERRBUF_SIZE; #else goto not_there; #endif if (strEQ(name, "PCAP_VERSION_MAJOR")) #ifdef PCAP_VERSION_MAJOR return PCAP_VERSION_MAJOR; #else goto not_there; #endif if (strEQ(name, "PCAP_VERSION_MINOR")) #ifdef PCAP_VERSION_MINOR return PCAP_VERSION_MINOR; #else goto not_there; #endif break; case 'Q': break; case 'R': break; case 'S': break; case 'T': break; case 'U': break; case 'V': break; case 'W': break; case 'X': break; case 'Y': break; case 'Z': break; case 'a': break; case 'b': break; case 'c': break; case 'd': break; case 'e': break; case 'f': break; case 'g': break; case 'h': break; case 'i': break; case 'j': break; case 'k': break; case 'l': if (strEQ(name, "lib_pcap_h")) #ifdef lib_pcap_h return lib_pcap_h; #else goto not_there; #endif break; case 'm': break; case 'n': break; case 'o': break; case 'p': break; case 'q': break; case 'r': break; case 's': break; case 't': break; case 'u': break; case 'v': break; case 'w': break; case 'x': break; case 'y': break; case 'z': break; } errno = EINVAL; return 0; not_there: errno = ENOENT; return 0; } SV * (*ptr)(u_char*); pcap_handler printer; static SV * retref (ref) u_char * ref; { return (SV*)ref; } static SV * handler (file) u_char * file; { SV * handle; GV * gv; handle = sv_newmortal(); gv = newGVgen("Net::RawIP"); do_open(gv, "+<&", 3, FALSE, 0, 0, PerlIO_importFILE((FILE*)file, NULL)); sv_setsv(handle, sv_bless(newRV_noinc((SV*)gv), gv_stashpv("Net::RawIP",1))); return handle; } SV * first; SV * second; SV * third; static void call_printer (file,pkt,user) u_char * file; struct pcap_pkthdr * pkt; u_char * user; { dSP ; PUSHMARK(sp) ; sv_setsv(first,(*ptr)(file)); sv_setpvn(second, (char *)pkt, sizeof(struct pcap_pkthdr)); sv_setpvn(third, (char *)user, pkt->caplen); XPUSHs(first); XPUSHs(second); XPUSHs(third); PUTBACK ; perl_call_sv((SV*)printer,G_VOID); } static SV * ip_opts_parse(pkt) SV * pkt; { int byte,i; STRLEN size; u_char * ptr; AV * RETVAL; byte = 0; size = SvCUR(pkt); ptr = (u_char *)SvPV(pkt, size); RETVAL = newAV(); for (i=0; byte 40) SvCUR_set(ip_opts,40); return ip_opts; } static SV * tcp_opts_parse(pkt) SV * pkt; { int byte,i; STRLEN size; u_char * ptr; AV * RETVAL; byte = 0; size = SvCUR(pkt); ptr = (u_char *)SvPV(pkt,size); RETVAL = newAV(); for (i=0; byte 40) SvCUR_set(ip_opts,40); return ip_opts; } MODULE = Net::RawIP PACKAGE = Net::RawIP PREFIX = pcap_ PROTOTYPES: ENABLE double constant(name,arg) char * name int arg void closefd(fd) int fd CODE: close(fd); SV * ip_rt_dev(addr) u_int32_t addr CODE: #ifdef _LINUX_ char dev[] = "proc"; RETVAL = newSVpv(dev,4); #endif #ifdef _BPF_ char dev[16]; int len; memset(dev,0,16); len = ip_rt_dev(addr,dev); RETVAL = newSVpv(dev,len); #endif #if !defined(_LINUX_) && !defined(_BPF_) croak("rdev() is not implemented on this system"); #endif OUTPUT: RETVAL SV * timem () CODE: struct timeval tv; struct timezone tz; tz.tz_minuteswest = 0; tz.tz_dsttime = 0; if ((gettimeofday(&tv,&tz) < 0)) { RETVAL = newSViv(0); croak("gettimeofday()"); } else { RETVAL = newSVpvf("%u.%06u",tv.tv_sec,tv.tv_usec); } OUTPUT: RETVAL unsigned int rawsock() #ifdef _IFLIST_ HV * ifaddrlist() CODE: int c,i; char buf[132]; struct ifaddrlist *al; RETVAL = newHV(); sv_2mortal((SV*)RETVAL); c = ifaddrlist(&al,buf); for (i=0; idevice, al->len, newSVpvf("%u.%u.%u.%u", (al->addr & 0xff000000) >> 24, (al->addr & 0x00ff0000) >> 16, (al->addr & 0x0000ff00) >> 8, (al->addr & 0x000000ff) ),0); al++; } OUTPUT: RETVAL #endif #ifdef _ETH_ int tap(device,ip,mac) char *device SV *ip SV *mac CODE: unsigned int i; unsigned char m[6]; RETVAL = tap(device,&i,m); if (RETVAL) { sv_setiv(ip,i); sv_setpvn(mac,(char *)m,6); } OUTPUT: ip mac RETVAL int mac_disc(addr,mac) unsigned int addr SV *mac CODE: unsigned char m[6]; RETVAL = mac_disc(addr,m); if (RETVAL) { sv_setpvn(mac,(char *)m,6); } OUTPUT: mac RETVAL void send_eth_packet(fd,eth_device,pkt,flag) int fd char* eth_device SV* pkt int flag CODE: send_eth_packet(fd, eth_device, (char*)SvPV(pkt,PL_na), SvCUR(pkt),flag); AV * eth_parse(pkt) SV * pkt CODE: u_char * c; struct ether_header *epkt; epkt = (struct ether_header *)SvPV(pkt,PL_na); RETVAL = newAV(); sv_2mortal((SV*)RETVAL); av_unshift(RETVAL,3); c = (u_char*)epkt->ether_dhost; av_store(RETVAL,0, newSVpvf("%.2X:%.2X:%.2X:%.2X:%.2X:%.2X", c[0],c[1],c[2],c[3],c[4],c[5])); c = (u_char*)epkt->ether_shost; av_store(RETVAL, 1, newSVpvf("%.2X:%.2X:%.2X:%.2X:%.2X:%.2X", c[0],c[1],c[2],c[3],c[4],c[5])); av_store(RETVAL, 2, newSViv(ntohs(epkt->ether_type))); OUTPUT: RETVAL #endif SV * set_sockaddr (daddr,port) unsigned int daddr unsigned short port CODE: int size; struct sockaddr_in dest_sockaddr; size = sizeof(struct sockaddr_in); memset(&dest_sockaddr,0,size); dest_sockaddr.sin_family = AF_INET; dest_sockaddr.sin_port = htons(port); dest_sockaddr.sin_addr.s_addr = htonl(daddr); RETVAL = newSVpv((char*)&dest_sockaddr,size); OUTPUT: RETVAL unsigned long host_to_ip (host_name) char *host_name void pkt_send (fd, sock, pkt) int fd SV *sock SV *pkt CODE: pkt_send (fd, (u_char *)SvPV(sock,PL_na), (u_char *)SvPV(pkt,PL_na), SvCUR(pkt)); AV * tcp_pkt_parse(pkt) SV * pkt CODE: u_int ipo,doff,ihl,tot_len; ITPKT *pktr; ipo = 0; pktr = (ITPKT *)SvPV(pkt,PL_na); ihl = pktr->ih.ihl; tot_len = ntohs(pktr->ih.tot_len); RETVAL = newAV(); sv_2mortal((SV*)RETVAL); av_unshift(RETVAL,29); av_store(RETVAL, 0, newSViv(pktr->ih.version)); av_store(RETVAL, 1, newSViv(pktr->ih.ihl)); av_store(RETVAL, 2, newSViv(pktr->ih.tos)); av_store(RETVAL, 3, newSViv(ntohs(pktr->ih.tot_len))); av_store(RETVAL, 4, newSViv(ntohs(pktr->ih.id))); av_store(RETVAL, 5, newSViv(ntohs(pktr->ih.frag_off))); av_store(RETVAL, 6, newSViv(pktr->ih.ttl)); av_store(RETVAL, 7, newSViv(pktr->ih.protocol)); av_store(RETVAL, 8, newSViv(ntohs(pktr->ih.check))); av_store(RETVAL, 9, newSViv(ntohl(pktr->ih.saddr))); av_store(RETVAL, 10, newSViv(ntohl(pktr->ih.daddr))); if (ihl > 5) { av_store(RETVAL,28, ip_opts_parse(sv_2mortal(newSVpv((char*)pktr + 20,ihl*4 - 20)))); pktr = (ITPKT *)pktr + (ihl*4 - 20); ipo = 1; } doff = pktr->th.doff; av_store(RETVAL, 11, newSViv(ntohs(pktr->th.source))); av_store(RETVAL, 12, newSViv(ntohs(pktr->th.dest))); av_store(RETVAL, 13, newSViv(ntohl(pktr->th.seq))); av_store(RETVAL, 14, newSViv(ntohl(pktr->th.ack_seq))); av_store(RETVAL, 15, newSViv(pktr->th.doff)); av_store(RETVAL, 16, newSViv(pktr->th.res1)); av_store(RETVAL, 17, newSViv(pktr->th.res2)); av_store(RETVAL, 18, newSViv(pktr->th.urg)); av_store(RETVAL, 19, newSViv(pktr->th.ack)); av_store(RETVAL, 20, newSViv(pktr->th.psh)); av_store(RETVAL, 21, newSViv(pktr->th.rst)); av_store(RETVAL, 22, newSViv(pktr->th.syn)); av_store(RETVAL, 23, newSViv(pktr->th.fin)); av_store(RETVAL, 24, newSViv(ntohs(pktr->th.window))); av_store(RETVAL, 25, newSViv(ntohs(pktr->th.check))); av_store(RETVAL, 26, newSViv(ntohs(pktr->th.urg_ptr))); if (doff > 5) { if (!ipo) { av_store(RETVAL, 28, newSViv(0)); } av_store(RETVAL, 29, tcp_opts_parse(sv_2mortal(newSVpv((char*)pktr+40,doff*4-20)))); pktr = (ITPKT *)pktr + (doff*4 - 20); } av_store(RETVAL, 27, newSVpv(((char*)&pktr->th.urg_ptr+2), tot_len - (4*ihl + doff*4))); OUTPUT: RETVAL AV * icmp_pkt_parse(pkt) SV * pkt CODE: u_int ihl,tot_len; IIPKT *pktr; pktr = (IIPKT *)SvPV(pkt,PL_na); ihl = pktr->ih.ihl; tot_len = ntohs(pktr->ih.tot_len); RETVAL = newAV(); sv_2mortal((SV*)RETVAL); av_unshift(RETVAL,20); av_store(RETVAL, 0, newSViv(pktr->ih.version)); av_store(RETVAL, 1, newSViv(pktr->ih.ihl)); av_store(RETVAL, 2, newSViv(pktr->ih.tos)); av_store(RETVAL, 3, newSViv(ntohs(pktr->ih.tot_len))); av_store(RETVAL, 4, newSViv(ntohs(pktr->ih.id))); av_store(RETVAL, 5, newSViv(ntohs(pktr->ih.frag_off))); av_store(RETVAL, 6, newSViv(pktr->ih.ttl)); av_store(RETVAL, 7, newSViv(pktr->ih.protocol)); av_store(RETVAL, 8, newSViv(ntohs(pktr->ih.check))); av_store(RETVAL, 9, newSViv(ntohl(pktr->ih.saddr))); av_store(RETVAL, 10, newSViv(ntohl(pktr->ih.daddr))); if (ihl > 5) { av_store(RETVAL, 20, ip_opts_parse(sv_2mortal(newSVpv((char*)pktr + 20,ihl*4 - 20)))); pktr = (IIPKT *)pktr + (ihl*4 - 20); } av_store(RETVAL, 11, newSViv(pktr->ich.type)); av_store(RETVAL, 12, newSViv(pktr->ich.code)); av_store(RETVAL, 13, newSViv(ntohs(pktr->ich.checksum))); av_store(RETVAL, 14, newSViv(pktr->ich.un.gateway)); av_store(RETVAL, 15, newSViv(pktr->ich.un.echo.id)); av_store(RETVAL, 16, newSViv(pktr->ich.un.echo.sequence)); av_store(RETVAL, 17, newSViv(pktr->ich.un.frag.unused)); av_store(RETVAL, 18, newSViv(pktr->ich.un.frag.mtu)); av_store(RETVAL, 19, newSVpv(((char*)&pktr->ich.un.frag.mtu+2), tot_len - (4*ihl + 8))); OUTPUT: RETVAL AV * generic_pkt_parse(pkt) SV * pkt CODE: u_int ihl,tot_len; struct iphdr *pktr; pktr = (struct iphdr *)SvPV(pkt,PL_na); ihl = pktr->ihl; tot_len = ntohs(pktr->tot_len); RETVAL = newAV(); sv_2mortal((SV*)RETVAL); av_store(RETVAL, 0, newSViv(pktr->version)); av_store(RETVAL, 1, newSViv(pktr->ihl)); av_store(RETVAL, 2, newSViv(pktr->tos)); av_store(RETVAL, 3, newSViv(ntohs(pktr->tot_len))); av_store(RETVAL, 4, newSViv(ntohs(pktr->id))); av_store(RETVAL, 5, newSViv(ntohs(pktr->frag_off))); av_store(RETVAL, 6, newSViv(pktr->ttl)); av_store(RETVAL, 7, newSViv(pktr->protocol)); av_store(RETVAL, 8, newSViv(ntohs(pktr->check))); av_store(RETVAL, 9, newSViv(ntohl(pktr->saddr))); av_store(RETVAL, 10, newSViv(ntohl(pktr->daddr))); if (ihl > 5) { av_store(RETVAL,12, ip_opts_parse(sv_2mortal(newSVpv((char*)pktr + 20,ihl*4 - 20)))); pktr = pktr + (ihl*4 - 20); } av_store(RETVAL, 11, newSVpv(((char*)pktr+20), tot_len - 4*ihl)); OUTPUT: RETVAL AV * udp_pkt_parse(pkt) SV * pkt CODE: u_int ihl,tot_len; IUPKT *pktr; pktr = (IUPKT *)SvPV(pkt,PL_na); ihl = pktr->ih.ihl; tot_len = ntohs(pktr->ih.tot_len); RETVAL = newAV(); sv_2mortal((SV*)RETVAL); av_unshift(RETVAL, 16); av_store(RETVAL, 0, newSViv(pktr->ih.version)); av_store(RETVAL, 1, newSViv(pktr->ih.ihl)); av_store(RETVAL, 2, newSViv(pktr->ih.tos)); av_store(RETVAL, 3, newSViv(ntohs(pktr->ih.tot_len))); av_store(RETVAL, 4, newSViv(ntohs(pktr->ih.id))); av_store(RETVAL, 5, newSViv(ntohs(pktr->ih.frag_off))); av_store(RETVAL, 6, newSViv(pktr->ih.ttl)); av_store(RETVAL, 7, newSViv(pktr->ih.protocol)); av_store(RETVAL, 8, newSViv(ntohs(pktr->ih.check))); av_store(RETVAL, 9, newSViv(ntohl(pktr->ih.saddr))); av_store(RETVAL, 10, newSViv(ntohl(pktr->ih.daddr))); if (ihl > 5) { av_store(RETVAL, 16, ip_opts_parse(sv_2mortal(newSVpv((char*)pktr + 20,ihl*4 - 20)))); pktr = pktr + (ihl*4 - 20); } av_store(RETVAL, 11, newSViv(ntohs(pktr->uh.source))); av_store(RETVAL, 12, newSViv(ntohs(pktr->uh.dest))); av_store(RETVAL, 13, newSViv(ntohs(pktr->uh.len))); av_store(RETVAL, 14, newSViv(ntohs(pktr->uh.check))); av_store(RETVAL, 15, newSVpv(((char*)&pktr->uh.check+2), tot_len - (4*ihl + 8))); OUTPUT: RETVAL SV * udp_pkt_creat(p) SV * p CODE: int opt,iplen; SV * ip_opts; u_char * ptr; AV * pkt; IUPKT piu; u_char *piur; opt = 0; iplen = 20; if (SvTYPE(SvRV(p)) == SVt_PVAV) pkt = (AV *)SvRV(p); else croak("Not array reference\n"); piu.ih.version = SvIV(*av_fetch(pkt,0,0)); piu.ih.ihl = SvIV(*av_fetch(pkt,1,0)); piu.ih.tos = SvIV(*av_fetch(pkt,2,0)); piu.ih.tot_len = BSDFIX(SvIV(*av_fetch(pkt,3,0))); if (!piu.ih.tot_len) piu.ih.tot_len = BSDFIX(iplen + 8 + SvCUR(*av_fetch(pkt,15,0))); piu.ih.id = htons(SvIV(*av_fetch(pkt,4,0))); piu.ih.frag_off = BSDFIX(SvIV(*av_fetch(pkt,5,0))); piu.ih.ttl = SvIV(*av_fetch(pkt,6,0)); piu.ih.protocol = SvIV(*av_fetch(pkt,7,0)); piu.ih.check = htons(SvIV(*av_fetch(pkt,8,0))); piu.ih.saddr = htonl(SvIV(*av_fetch(pkt,9,0))); piu.ih.daddr = htonl(SvIV(*av_fetch(pkt,10,0))); if (!piu.ih.check) piu.ih.check = in_cksum((unsigned short *)&piu,iplen); piu.uh.source = htons(SvIV(*av_fetch(pkt,11,0))); piu.uh.dest = htons(SvIV(*av_fetch(pkt,12,0))); piu.uh.len = htons(SvIV(*av_fetch(pkt,13,0))); if (!piu.uh.len) piu.uh.len = htons(8 + SvCUR(*av_fetch(pkt,15,0))); piu.uh.check = htons(SvIV(*av_fetch(pkt,14,0))); if (av_fetch(pkt,16,0)) { if (SvROK(*av_fetch(pkt,16,0))) { opt++; ip_opts = ip_opts_creat(*av_fetch(pkt,16,0)); piu.ih.ihl = 5 + SvCUR(ip_opts)/4; piu.ih.tot_len = BSDFIX(4*piu.ih.ihl + 8 + SvCUR(*av_fetch(pkt,15,0))); iplen = 4*piu.ih.ihl; piu.ih.check = 0; ptr = (u_char*)safemalloc(iplen + 8); memcpy(ptr,(u_char*)&piu,20); memcpy(ptr+20,SvPV(ip_opts,PL_na),SvCUR(ip_opts)); memcpy(ptr+20+SvCUR(ip_opts),(u_char*)&piu + 20,8); ((struct iphdr*)ptr)->check = in_cksum((unsigned short *)ptr,iplen); RETVAL = newSVpv((char*)ptr, sizeof(IUPKT)+SvCUR(ip_opts)); sv_catsv(RETVAL, *av_fetch(pkt,15,0)); Safefree(ptr); sv_2mortal(ip_opts); } } if (!opt) { RETVAL = newSVpv((char*)&piu,sizeof(IUPKT)); sv_catsv(RETVAL,*av_fetch(pkt,15,0)); } if (!piu.uh.check) { piur = (u_char*) SvPV(RETVAL, PL_na); ((struct udphdr*)(piur + iplen))->check = ip_in_cksum((struct iphdr *)piur, (unsigned short *)(piur + iplen), 8 + SvCUR(*av_fetch(pkt,15,0))); sv_setpvn(RETVAL, (char*)piur, iplen + 8 + SvCUR(*av_fetch(pkt,15,0))); } OUTPUT: RETVAL # This assembles an ICMP packet based on the data passed in as an # array reference from the Perl module. Populating this are with # comments as I try to understand the code better so I'll remember # what I thought each bit did. # Steve Bonds SV * icmp_pkt_creat(p) SV * p CODE: int opt,iplen; SV * ip_opts; u_char * ptr; AV * pkt; IIPKT pii; u_char *piir; opt = 0; iplen = 20; if (SvTYPE(SvRV(p)) == SVt_PVAV) /* pkt allows for easy access in C to the original parameter, p. Steve Bonds */ pkt = (AV *)SvRV(p); else croak("Not array reference\n"); /* Populate the C pii ICMP packet structure with information from "pkt", which is the C char pointer to the array reference passed in. The perlguts man page recommends checking that this returns non-null before calling SvIV on it, probably to follow the Second Commandment of C: Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end. (http://web.archive.org/web/19961109205914/http://www.lysator.liu.se/c/ten-commandments.html) however, it would appear that the original author didn't and I don't want to make too many changes yet, so I'll pretend I didn't see it for now... Steve Bonds */ pii.ih.version = SvIV(*av_fetch(pkt,0,0)); pii.ih.ihl = SvIV(*av_fetch(pkt,1,0)); pii.ih.tos = SvIV(*av_fetch(pkt,2,0)); pii.ih.tot_len = BSDFIX(SvIV(*av_fetch(pkt,3,0))); if (!pii.ih.tot_len) pii.ih.tot_len = BSDFIX(iplen + 8 + SvCUR(*av_fetch(pkt,19,0))); pii.ih.id = htons(SvIV(*av_fetch(pkt,4,0))); pii.ih.frag_off = BSDFIX(SvIV(*av_fetch(pkt,5,0))); pii.ih.ttl = SvIV(*av_fetch(pkt,6,0)); pii.ih.protocol = SvIV(*av_fetch(pkt,7,0)); pii.ih.check = htons(SvIV(*av_fetch(pkt,8,0))); pii.ih.saddr = htonl(SvIV(*av_fetch(pkt,9,0))); pii.ih.daddr = htonl(SvIV(*av_fetch(pkt,10,0))); if (!pii.ih.check) pii.ih.check = in_cksum((unsigned short *)&pii,iplen); /* We're done with the basic IP header assembly into the pii structure. Move on to the ICMP header specifics. Steve Bonds */ pii.ich.type = SvIV(*av_fetch(pkt,11,0)); pii.ich.code = SvIV(*av_fetch(pkt,12,0)); pii.ich.checksum = htons(SvIV(*av_fetch(pkt,13,0))); pii.ich.un.gateway = SvIV(*av_fetch(pkt,14,0)); /* Array index 20 is the "data" area of the ICMP packet. av_fetch only returns a scalar so the data area must be one, which explains why it didn't work so great when I used an array reference here based on my incorrect understanding of the POD docs. Steve Bonds */ if (av_fetch(pkt,20,0)) { if (SvROK(*av_fetch(pkt,20,0))) { opt++; /* I don't understand why this module calls an internal parsing function on a raw data field supplied by the user. This may or may not associate with an actual IP structure, depending on what the user of this module decides. It appears that this is done to get the proper packet length for the IP header, but this could also be done based on the scalar length of the raw data area itself (SvCUR). Why wouldn't the sum of these work for the total IP packet length: + IP header (20) + ICMP header (8) - type (1) - code (1) - checksum (2) - id or unused (2) - sequence or mtu (2) + ICMP data (variable length) Finding the total length shouldn't require any decoding of the ICMP data area. Doing so just gives this module additional places to either break/crash or mangle the intended data on its way out. In the interest of not breaking the module until I understand it better, leave it alone for now. Steve Bonds */ ip_opts = ip_opts_creat(*av_fetch(pkt,20,0)); pii.ih.ihl = 5 + SvCUR(ip_opts)/4; iplen = 4*pii.ih.ihl; /* Array index 19 is the MTU or Sequence. SvCUR returns the length of that scalar. Steve Bonds */ pii.ih.tot_len = BSDFIX(iplen + 8 + SvCUR(*av_fetch(pkt,19,0))); pii.ih.check = 0; /* Create a place to copy the pii structure for rebuilding the checksum. Steve Bonds */ ptr = (u_char*)safemalloc(iplen + 8); /* Copy the 20 byte IP header in */ memcpy(ptr,(u_char*)&pii,20); /* Either this is a bug or I don't understand something going on here. ip_opts is derived above from field 20 of "pkt" which is the user-supplied data area of the ICMP packet they want to send. This data area is supposed to be an IP packet, but since they can set it to any arbitrary string of bytes, it doesn't necessarily have to be. So it looks to me like we just joined the 20 byte IP header with the 8 bytes ip_opts creates from the user data area. I would instead have expected this to be the 8 bytes of ICMP packet header we created above. Steve Bonds */ memcpy(ptr+20,SvPV(ip_opts,PL_na),SvCUR(ip_opts)); /* Here's the 8 bytes we created above getting copied in. I would have expected this line to be before the above line. */ memcpy(ptr+20+SvCUR(ip_opts),(u_char*)&pii + 20,8); /* Re-build a valid IP checksum for our packet. */ ((struct iphdr*)ptr)->check = in_cksum((unsigned short *)ptr,iplen); /* Create a new scalar that will contain the string value contained in our temporary spot *ptr. This in essence becomes a packed string value in perl when it gets sent back. Steve Bonds */ RETVAL = newSVpv((char*)ptr, sizeof(IIPKT)+SvCUR(ip_opts)); /* Toss field 19 from the original array (MTU/Sequence) onto the end of the RETVAL scalar created above. Alas, these are 2-byte values so these need to be converted to proper network byte order before they will be valid. This looks like the bug I came here to find. I think it may be simpler to convert this to network byte order via Perl before it gets passed in to this code so as to minimize the changes needed inside this harder-to-follow .xs file. Steve Bonds */ sv_catsv(RETVAL, *av_fetch(pkt, 19, 0)); Safefree(ptr); sv_2mortal(ip_opts); } } if (!opt) { RETVAL = newSVpv((char*)&pii,sizeof(IIPKT)); sv_catsv(RETVAL,*av_fetch(pkt,19,0)); } if (!pii.ich.checksum) { piir = (u_char*) SvPV(RETVAL,PL_na); ((struct icmphdr*)(piir + iplen))->checksum = in_cksum((unsigned short *)(piir + iplen),8 + SvCUR(*av_fetch(pkt,19,0))); sv_setpvn(RETVAL,(char*)piir,iplen + 8 + SvCUR(*av_fetch(pkt,19,0))); } OUTPUT: RETVAL SV * generic_pkt_creat(p) SV * p CODE: int opt,iplen; SV * ip_opts; AV * pkt; struct iphdr ih; u_char *pigr; opt = 0; iplen = 20; if (SvTYPE(SvRV(p)) == SVt_PVAV) pkt = (AV *)SvRV(p); else croak("Not array reference\n"); ih.version = SvIV(*av_fetch(pkt,0,0)); ih.ihl = SvIV(*av_fetch(pkt,1,0)); ih.tos = SvIV(*av_fetch(pkt,2,0)); ih.tot_len = BSDFIX(SvIV(*av_fetch(pkt,3,0))); if (!ih.tot_len) ih.tot_len = BSDFIX(iplen + SvCUR(*av_fetch(pkt,11,0))); ih.id = htons(SvIV(*av_fetch(pkt,4,0))); ih.frag_off = BSDFIX(SvIV(*av_fetch(pkt,5,0))); ih.ttl = SvIV(*av_fetch(pkt,6,0)); ih.protocol = SvIV(*av_fetch(pkt,7,0)); ih.check = htons(SvIV(*av_fetch(pkt,8,0))); ih.saddr = htonl(SvIV(*av_fetch(pkt,9,0))); ih.daddr = htonl(SvIV(*av_fetch(pkt,10,0))); if (!ih.check) ih.check = in_cksum((unsigned short *)&ih,iplen); if (av_fetch(pkt,12,0)) { if (SvROK(*av_fetch(pkt,12,0))) { opt++; ip_opts = ip_opts_creat(*av_fetch(pkt,12,0)); if (ih.ihl <= 5) ih.ihl = 5 + SvCUR(ip_opts)/4; iplen = 20 + SvCUR(ip_opts); if (!ih.tot_len) ih.tot_len = BSDFIX(20 + SvCUR(ip_opts) + SvCUR(*av_fetch(pkt,11,0))); ih.check = 0; RETVAL = newSVpv((char*)&ih,20); sv_catsv(RETVAL,ip_opts); pigr = (u_char*) SvPV(RETVAL,PL_na); ((struct iphdr*)pigr)->check = in_cksum((unsigned short *)pigr,iplen); sv_setpvn(RETVAL,(char*)pigr,iplen); sv_catsv(RETVAL,*av_fetch(pkt,11,0)); sv_2mortal(ip_opts); } } if (!opt) { RETVAL = newSVpv((char*)&ih,iplen); sv_catsv(RETVAL,*av_fetch(pkt,11,0)); } OUTPUT: RETVAL SV * tcp_pkt_creat(p) SV * p CODE: int ipo,opt,iplen; AV * pkt; SV * ip_opts; SV * tcp_opts; u_char * ptr; u_char * tptr; ITPKT pit; u_char *pitr; ipo = 0; opt = 0; iplen = 20; if (SvTYPE(SvRV(p)) == SVt_PVAV) pkt = (AV *)SvRV(p); else croak("Not array reference\n"); pit.ih.version = SvIV(*av_fetch(pkt,0,0)); pit.ih.ihl = SvIV(*av_fetch(pkt,1,0)); pit.ih.tos = SvIV(*av_fetch(pkt,2,0)); pit.ih.tot_len = BSDFIX(SvIV(*av_fetch(pkt,3,0))); if (!pit.ih.tot_len) pit.ih.tot_len = BSDFIX(iplen + TCPHDR + SvCUR(*av_fetch(pkt,27,0))); pit.ih.id = htons(SvIV(*av_fetch(pkt,4,0))); pit.ih.frag_off = BSDFIX(SvIV(*av_fetch(pkt,5,0))); pit.ih.ttl = SvIV(*av_fetch(pkt,6,0)); pit.ih.protocol = SvIV(*av_fetch(pkt,7,0)); pit.ih.check = htons(SvIV(*av_fetch(pkt,8,0))); pit.ih.saddr = htonl(SvIV(*av_fetch(pkt,9,0))); pit.ih.daddr = htonl(SvIV(*av_fetch(pkt,10,0))); if (!pit.ih.check) pit.ih.check = in_cksum((unsigned short *)&pit,iplen); pit.th.source = htons(SvIV(*av_fetch(pkt,11,0))); pit.th.dest = htons(SvIV(*av_fetch(pkt,12,0))); pit.th.seq = htonl(SvIV(*av_fetch(pkt,13,0))); pit.th.ack_seq = htonl(SvIV(*av_fetch(pkt,14,0))); pit.th.doff = SvIV(*av_fetch(pkt,15,0)); pit.th.res1 = SvIV(*av_fetch(pkt,16,0)); pit.th.res2 = SvIV(*av_fetch(pkt,17,0)); pit.th.urg = SvIV(*av_fetch(pkt,18,0)); pit.th.ack = SvIV(*av_fetch(pkt,19,0)); pit.th.psh = SvIV(*av_fetch(pkt,20,0)); pit.th.rst = SvIV(*av_fetch(pkt,21,0)); pit.th.syn = SvIV(*av_fetch(pkt,22,0)); pit.th.fin = SvIV(*av_fetch(pkt,23,0)); pit.th.window = htons(SvIV(*av_fetch(pkt,24,0))); pit.th.check = htons(SvIV(*av_fetch(pkt,25,0))); pit.th.urg_ptr = htons(SvIV(*av_fetch(pkt,26,0))); if (av_fetch(pkt,28,0)) { if(SvROK(*av_fetch(pkt,28,0))) { opt++; ip_opts = ip_opts_creat(*av_fetch(pkt,28,0)); pit.ih.ihl = 5 + SvCUR(ip_opts)/4; pit.ih.tot_len = BSDFIX(4*pit.ih.ihl + TCPHDR + SvCUR(*av_fetch(pkt,27,0))); iplen = 4*pit.ih.ihl; pit.ih.check = 0; ptr = (u_char*)safemalloc(4*pit.ih.ihl + TCPHDR); memcpy(ptr,(u_char*)&pit,20); memcpy(ptr+20,SvPV(ip_opts,PL_na),SvCUR(ip_opts)); memcpy(ptr+20+SvCUR(ip_opts),(u_char*)&pit + 20,TCPHDR); ((struct iphdr*)ptr)->check = in_cksum((unsigned short *)ptr,4*pit.ih.ihl); RETVAL = newSVpv((char*)ptr, sizeof(ITPKT)+SvCUR(ip_opts)); sv_catsv(RETVAL,*av_fetch(pkt,27,0)); Safefree(ptr); sv_2mortal(ip_opts); ipo = 1; } if (av_fetch(pkt,29,0)) { if (SvROK(*av_fetch(pkt,29,0))) { opt++; tcp_opts = tcp_opts_creat(*av_fetch(pkt,29,0)); if (ipo) { ptr = (u_char *)SvPV(RETVAL,PL_na); tptr = (u_char*)safemalloc(SvCUR(RETVAL) + SvCUR(tcp_opts) - SvCUR(*av_fetch(pkt,27,0))); ((struct iphdr*)ptr)->tot_len = BSDFIX(SvCUR(RETVAL) + SvCUR(tcp_opts)); ((struct iphdr*)ptr)->check = 0; ((struct iphdr*)ptr)->check = in_cksum((unsigned short *)ptr,iplen); ((struct tcphdr*)(ptr + iplen))->doff = 5 + SvCUR(tcp_opts)/4; memcpy(tptr,ptr,SvCUR(RETVAL)-SvCUR(*av_fetch(pkt,27,0))); memcpy(tptr+(SvCUR(RETVAL)-SvCUR(*av_fetch(pkt,27,0))), SvPV(tcp_opts,PL_na),SvCUR(tcp_opts)); sv_setpvn(RETVAL, (char *)tptr, SvCUR(RETVAL) + SvCUR(tcp_opts) - SvCUR(*av_fetch(pkt,27,0))); sv_catsv(RETVAL, *av_fetch(pkt,27,0)); } else { pit.ih.tot_len = BSDFIX(40+SvCUR(tcp_opts)+SvCUR(*av_fetch(pkt,27,0))); pit.ih.check = 0; pit.ih.check = in_cksum((unsigned short *)&pit,iplen); pit.th.doff = 5 + SvCUR(tcp_opts)/4; tptr = (u_char*)safemalloc(40+SvCUR(tcp_opts)); memcpy(tptr,&pit,40); memcpy(tptr+40,SvPV(tcp_opts,PL_na),SvCUR(tcp_opts)); RETVAL = newSVpv((char*)tptr, 40+SvCUR(tcp_opts)); sv_catsv(RETVAL, *av_fetch(pkt,27,0)); } Safefree(tptr); sv_2mortal(tcp_opts); } } } if (!opt) { RETVAL = newSVpv((char*)&pit,sizeof(ITPKT)); sv_catsv(RETVAL,*av_fetch(pkt,27,0)); } if (!pit.th.check) { pitr = (u_char *)SvPV(RETVAL,PL_na); ((struct tcphdr*)(pitr + iplen))->check = ip_in_cksum((struct iphdr *)pitr,(unsigned short *)(pitr + iplen), 4*((struct tcphdr*)(pitr + iplen))->doff + SvCUR(*av_fetch(pkt,27,0))); sv_setpvn(RETVAL,(char*)pitr,iplen+ 4*((struct tcphdr*)(pitr + iplen))->doff + SvCUR(*av_fetch(pkt,27,0))); } OUTPUT: RETVAL pcap_t * open_live(device,snaplen,promisc,to_ms,ebuf) char *device int snaplen int promisc int to_ms char * ebuf CODE: ebuf = (char*)safemalloc(PCAP_ERRBUF_SIZE); RETVAL = pcap_open_live(device, snaplen, promisc, to_ms, ebuf); Safefree(ebuf); OUTPUT: ebuf RETVAL pcap_t * open_offline(fname,ebuf) char *fname char *ebuf CODE: ebuf = (char*)safemalloc(PCAP_ERRBUF_SIZE); RETVAL = pcap_open_offline(fname,ebuf); Safefree(ebuf); OUTPUT: ebuf RETVAL SV * pcap_dump_open(p,fname) pcap_t *p char *fname CODE: RETVAL = newSViv((unsigned long)pcap_dump_open(p,fname)); OUTPUT: RETVAL char * lookupdev(ebuf) char *ebuf CODE: ebuf = (char*)safemalloc(PCAP_ERRBUF_SIZE); RETVAL = pcap_lookupdev(ebuf); Safefree(ebuf); OUTPUT: ebuf RETVAL int lookupnet(device,netp,maskp,ebuf) char *device bpf_u_int32 netp bpf_u_int32 maskp char *ebuf CODE: ebuf = (char*)safemalloc(PCAP_ERRBUF_SIZE); RETVAL = pcap_lookupnet(device,&netp,&maskp,ebuf); Safefree(ebuf); OUTPUT: netp maskp ebuf RETVAL void dump(ptr,pkt,user) SV * ptr SV * pkt SV * user CODE: pcap_dump((u_char*)PerlIO_findFILE(IoOFP(sv_2io(ptr))), (struct pcap_pkthdr*)(SvPV(pkt,PL_na)), (u_char*)(SvPV(user,PL_na))); int dispatch(p,cnt,print,user) pcap_t *p int cnt pcap_handler print SV * user CODE: printer = print; if (!SvROK(user) && SvOK(user)) { user = (SV *) SvIV(user); ptr = &handler; } else { ptr = &retref; } first = newSViv(0); second = newSViv(0); third = newSViv(0); RETVAL = pcap_dispatch(p,cnt,(pcap_handler)&call_printer,(u_char*)user); OUTPUT: RETVAL int loop(p,cnt,print,user) pcap_t *p int cnt pcap_handler print SV *user CODE: printer = print; if (!SvROK(user) && SvOK(user)) { user = (SV *)SvIV(user); ptr = &handler; } else { ptr = &retref; } first = newSViv(0); second = newSViv(0); third = newSViv(0); RETVAL = pcap_loop(p,cnt,(pcap_handler)&call_printer,(u_char*)user); OUTPUT: RETVAL int compile(p,fp,str,optimize,netmask) pcap_t * p struct bpf_program *fp char *str int optimize unsigned int netmask CODE: fp = (struct bpf_program *)safemalloc(sizeof(struct bpf_program)); RETVAL = pcap_compile(p,fp,str,optimize,netmask); OUTPUT: fp RETVAL int linkoffset(p) pcap_t * p CODE: RETVAL = linkoffset(pcap_datalink(p)); OUTPUT: RETVAL int pcap_setfilter(p,fp) pcap_t *p struct bpf_program *fp OUTPUT: RETVAL SV * next(p,h) pcap_t *p SV *h CODE: STRLEN len; u_char * hdr; const u_char * next; len = sizeof(struct pcap_pkthdr); if (!SvOK(h)) { sv_setpv(h,"new"); SvGROW(h,len) ; } hdr = (u_char *)SvPV(h,len) ; next = pcap_next(p,(struct pcap_pkthdr*)hdr); if (next) RETVAL = newSVpv((char *)next,((struct pcap_pkthdr*)hdr)->caplen); else RETVAL = newSViv(0); sv_setpvn(h,(char *)hdr,len); OUTPUT: h RETVAL int pcap_datalink(p) pcap_t *p OUTPUT: RETVAL int pcap_snapshot(p) pcap_t *p OUTPUT: RETVAL int pcap_is_swapped(p) pcap_t *p OUTPUT: RETVAL int pcap_major_version(p) pcap_t *p OUTPUT: RETVAL int pcap_minor_version(p) pcap_t *p OUTPUT: RETVAL int stat(p,ps) pcap_t *p u_char *ps CODE: ps = safemalloc(sizeof(struct pcap_stat)); RETVAL = pcap_stats(p,(struct pcap_stat*)ps); Safefree(ps); OUTPUT: ps RETVAL int pcap_fileno(p) pcap_t *p OUTPUT: RETVAL void pcap_perror(p,prefix) pcap_t *p char *prefix SV * pcap_geterr(p) pcap_t *p CODE: RETVAL = newSVpv(pcap_geterr(p),0); OUTPUT: RETVAL SV * pcap_strerror(error) int error CODE: RETVAL = newSVpv(pcap_strerror(error),0); OUTPUT: RETVAL void pcap_close(p) pcap_t *p void pcap_dump_close(p) pcap_dumper_t *p FILE * pcap_file(p) pcap_t *p OUTPUT: RETVAL