diff options
Diffstat (limited to 'lib/IO/Interface.xs')
-rw-r--r-- | lib/IO/Interface.xs | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/lib/IO/Interface.xs b/lib/IO/Interface.xs new file mode 100644 index 0000000..9d500d9 --- /dev/null +++ b/lib/IO/Interface.xs @@ -0,0 +1,825 @@ +/* Interface.xs: part of LibIO-Interface-Perl */ +/* Copyright 2014 Lincoln D. Stein */ +/* Licensed under Perl Artistic License 2.0 */ +/* Please see LICENSE and README.md for more information. */ + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include <stdio.h> +#include <string.h> + +/* socket definitions */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +/* location of IFF_* constants */ +#include <net/if.h> + +/* location of getifaddrs() definition */ +#ifdef USE_GETIFADDRS +#include <ifaddrs.h> + +#ifdef HAVE_SOCKADDR_DL_STRUCT +#include <net/if_dl.h> +#endif + +#endif + +#ifndef SIOCGIFCONF +#include <sys/sockio.h> +#endif + +#ifdef OSIOCGIFCONF +#define MY_SIOCGIFCONF OSIOCGIFCONF +#else +#define MY_SIOCGIFCONF SIOCGIFCONF +#endif + +#ifdef PerlIO +typedef PerlIO * InputStream; +#else +#define PERLIO_IS_STDIO 1 +typedef FILE * InputStream; +#define PerlIO_fileno(f) fileno(f) +#endif + +#if !defined(__USE_BSD) + #if defined(__linux__) + typedef int IOCTL_CMD_T; + #define __USE_BSD + #elif defined(__APPLE__) + typedef unsigned long IOCTL_CMD_T; + #define __USE_BSD + #else + typedef int IOCTL_CMD_T; + #endif +#else + typedef unsigned long IOCTL_CMD_T; +#endif + +/* HP-UX, Solaris */ +#if !defined(ifr_mtu) && defined(ifr_metric) +#define ifr_mtu ifr_metric +#endif + +static double +constant_IFF_N(char *name, int len, int arg) +{ + errno = 0; + if (5 + 1 >= len ) { + errno = EINVAL; + return 0; + } + switch (name[5 + 1]) { + case 'A': + if (strEQ(name + 5, "OARP")) { /* IFF_N removed */ +#ifdef IFF_NOARP + return IFF_NOARP; +#else + goto not_there; +#endif + } + case 'T': + if (strEQ(name + 5, "OTRAILERS")) { /* IFF_N removed */ +#ifdef IFF_NOTRAILERS + return IFF_NOTRAILERS; +#else + goto not_there; +#endif + } + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + +static double +constant_IFF_PO(char *name, int len, int arg) +{ + errno = 0; + switch (name[6 + 0]) { + case 'I': + if (strEQ(name + 6, "INTOPOINT")) { /* IFF_PO removed */ +#ifdef IFF_POINTOPOINT + return IFF_POINTOPOINT; +#else + goto not_there; +#endif + } + case 'R': + if (strEQ(name + 6, "RTSEL")) { /* IFF_PO removed */ +#ifdef IFF_PORTSEL + return IFF_PORTSEL; +#else + goto not_there; +#endif + } + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + +static double +constant_IFF_P(char *name, int len, int arg) +{ + errno = 0; + switch (name[5 + 0]) { + case 'O': + return constant_IFF_PO(name, len, arg); + case 'R': + if (strEQ(name + 5, "ROMISC")) { /* IFF_P removed */ +#ifdef IFF_PROMISC + return IFF_PROMISC; +#else + goto not_there; +#endif + } + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + +static double +constant_IFF_A(char *name, int len, int arg) +{ + errno = 0; + switch (name[5 + 0]) { + case 'L': + if (strEQ(name + 5, "LLMULTI")) { /* IFF_A removed */ +#ifdef IFF_ALLMULTI + return IFF_ALLMULTI; +#else + goto not_there; +#endif + } + case 'U': + if (strEQ(name + 5, "UTOMEDIA")) { /* IFF_A removed */ +#ifdef IFF_AUTOMEDIA + return IFF_AUTOMEDIA; +#else + goto not_there; +#endif + } + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + +static double +constant_IFF_M(char *name, int len, int arg) +{ + errno = 0; + switch (name[5 + 0]) { + case 'A': + if (strEQ(name + 5, "ASTER")) { /* IFF_M removed */ +#ifdef IFF_MASTER + return IFF_MASTER; +#else + goto not_there; +#endif + } + case 'U': + if (strEQ(name + 5, "ULTICAST")) { /* IFF_M removed */ +#ifdef IFF_MULTICAST + return IFF_MULTICAST; +#else + goto not_there; +#endif + } + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + +static double +constant_IFF(char *name, int len, int arg) +{ + errno = 0; + if (3 + 1 >= len ) { + errno = EINVAL; + return 0; + } + switch (name[3 + 1]) { + case 'A': + if (!strnEQ(name + 3,"_", 1)) + break; + return constant_IFF_A(name, len, arg); + case 'B': + if (strEQ(name + 3, "_BROADCAST")) { /* IFF removed */ +#ifdef IFF_BROADCAST + return IFF_BROADCAST; +#else + goto not_there; +#endif + } + case 'D': + if (strEQ(name + 3, "_DEBUG")) { /* IFF removed */ +#ifdef IFF_DEBUG + return IFF_DEBUG; +#else + goto not_there; +#endif + } + case 'L': + if (strEQ(name + 3, "_LOOPBACK")) { /* IFF removed */ +#ifdef IFF_LOOPBACK + return IFF_LOOPBACK; +#else + goto not_there; +#endif + } + case 'M': + if (!strnEQ(name + 3,"_", 1)) + break; + return constant_IFF_M(name, len, arg); + case 'N': + if (!strnEQ(name + 3,"_", 1)) + break; + return constant_IFF_N(name, len, arg); + case 'P': + if (!strnEQ(name + 3,"_", 1)) + break; + return constant_IFF_P(name, len, arg); + case 'R': + if (strEQ(name + 3, "_RUNNING")) { /* IFF removed */ +#ifdef IFF_RUNNING + return IFF_RUNNING; +#else + goto not_there; +#endif + } + case 'S': + if (strEQ(name + 3, "_SLAVE")) { /* IFF removed */ +#ifdef IFF_SLAVE + return IFF_SLAVE; +#else + goto not_there; +#endif + } + case 'U': + if (strEQ(name + 3, "_UP")) { /* IFF removed */ +#ifdef IFF_UP + return IFF_UP; +#else + goto not_there; +#endif + } + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + +static double +constant_I(char *name, int len, int arg) +{ + errno = 0; + if (1 + 1 >= len ) { + errno = EINVAL; + return 0; + } + switch (name[1 + 1]) { + case 'F': + if (!strnEQ(name + 1,"F", 1)) + break; + return constant_IFF(name, len, arg); + case 'H': + if (strEQ(name + 1, "FHWADDRLEN")) { /* I removed */ +#ifdef IFHWADDRLEN + return IFHWADDRLEN; +#else + goto not_there; +#endif + } + case 'N': + if (strEQ(name + 1, "FNAMSIZ")) { /* I removed */ +#ifdef IFNAMSIZ + return IFNAMSIZ; +#else + goto not_there; +#endif + } + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + +static double +constant(char *name, int len, int arg) +{ + errno = 0; + switch (name[0 + 0]) { + case 'I': + return constant_I(name, len, arg); + } + errno = EINVAL; + return 0; + +not_there: + errno = ENOENT; + return 0; +} + +int Ioctl (InputStream sock, IOCTL_CMD_T operation,void* result) { + int fd = PerlIO_fileno(sock); + return ioctl(fd,operation,result) == 0; +} + +#ifdef IFHWADDRLEN +char* parse_hwaddr (char *string, struct sockaddr* hwaddr) { + int len,i,consumed; + unsigned int converted; + char* s; + s = string; + len = strlen(s); + for (i = 0; i < IFHWADDRLEN && len > 0; i++) { + if (sscanf(s,"%x%n",&converted,&consumed) <= 0) + break; + hwaddr->sa_data[i] = converted; + s += consumed + 1; + len -= consumed + 1; + } + if (i != IFHWADDRLEN) + return NULL; + else + return string; +} + +/* No checking for string buffer length. Caller must ensure at least + 3*4 + 3 + 1 = 16 bytes long */ +char* format_hwaddr (char *string, struct sockaddr* hwaddr) { + int i,len; + char *s; + s = string; + s[0] = '\0'; + for (i = 0; i < IFHWADDRLEN; i++) { + if (i < IFHWADDRLEN-1) + len = sprintf(s,"%02x:",(unsigned char)hwaddr->sa_data[i]); + else + len = sprintf(s,"%02x",(unsigned char)hwaddr->sa_data[i]); + s += len; + } + return string; +} +#endif + +MODULE = IO::Interface PACKAGE = IO::Interface + +double +constant(sv,arg) + PREINIT: + STRLEN len; + PROTOTYPE: $;$ + INPUT: + SV * sv + char * s = SvPV(sv, len); + int arg + CODE: + RETVAL = constant(s,len,arg); + OUTPUT: + RETVAL + +char* +if_addr(sock, name, ...) + InputStream sock + char* name + PROTOTYPE: $$;$ + PREINIT: + STRLEN len; + IOCTL_CMD_T operation; + struct ifreq ifr; + char* newaddr; + CODE: + { +#if !(defined(HAS_IOCTL) && defined(SIOCGIFADDR)) + XSRETURN_UNDEF; +#else + if (strncmp(name,"any",3) == 0) { + RETVAL = "0.0.0.0"; + } else { + bzero((void*)&ifr,sizeof(struct ifreq)); + strncpy(ifr.ifr_name,name,IFNAMSIZ-1); + ifr.ifr_addr.sa_family = AF_INET; + if (items > 2) { + newaddr = SvPV(ST(2),len); + if ( inet_aton(newaddr,&((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr) == 0 ) + croak("Invalid inet address"); +#if defined(SIOCSIFADDR) + operation = SIOCSIFADDR; +#else + croak("Cannot set interface address on this platform"); +#endif + } else { + operation = SIOCGIFADDR; + } + if (!Ioctl(sock,operation,&ifr)) XSRETURN_UNDEF; + if (ifr.ifr_addr.sa_family != AF_INET) croak ("Address is not in the AF_INET family.\n"); + RETVAL = inet_ntoa(((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr); + } +#endif + } + OUTPUT: + RETVAL + +char* +if_broadcast(sock, name, ...) + InputStream sock + char* name + PROTOTYPE: $$;$ + PREINIT: + STRLEN len; + IOCTL_CMD_T operation; + struct ifreq ifr; + char* newaddr; + CODE: + { +#if !(defined(HAS_IOCTL) && defined(SIOCGIFBRDADDR)) + XSRETURN_UNDEF; +#else + bzero((void*)&ifr,sizeof(struct ifreq)); + strncpy(ifr.ifr_name,name,IFNAMSIZ-1); + ifr.ifr_addr.sa_family = AF_INET; + if (items > 2) { + newaddr = SvPV(ST(2),len); + if ( inet_aton(newaddr,&((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr) == 0 ) + croak("Invalid inet address"); +#if defined(SIOCSIFBRDADDR) + operation = SIOCSIFBRDADDR; +#else + croak("Cannot set broadcast address on this platform"); +#endif + } else { + operation = SIOCGIFBRDADDR; + } + if (!Ioctl(sock,operation,&ifr)) XSRETURN_UNDEF; + if (ifr.ifr_addr.sa_family != AF_INET) croak ("Address is not in the AF_INET family.\n"); + RETVAL = inet_ntoa(((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr); +#endif + } + OUTPUT: + RETVAL + +char* +if_netmask(sock, name, ...) + InputStream sock + char* name + PROTOTYPE: $$;$ + PREINIT: + STRLEN len; + IOCTL_CMD_T operation; + struct ifreq ifr; + char* newaddr; + CODE: + { +#if !(defined(HAS_IOCTL) && defined(SIOCGIFNETMASK)) + XSRETURN_UNDEF; +#else + bzero((void*)&ifr,sizeof(struct ifreq)); + strncpy(ifr.ifr_name,name,IFNAMSIZ-1); + ifr.ifr_addr.sa_family = AF_INET; + if (items > 2) { + newaddr = SvPV(ST(2),len); + if ( inet_aton(newaddr,&((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr) == 0 ) + croak("Invalid inet address"); +#if defined(SIOCSIFNETMASK) + operation = SIOCSIFNETMASK; +#else + croak("Cannot set netmask on this platform"); +#endif + } else { + operation = SIOCGIFNETMASK; + } + if (!Ioctl(sock,operation,&ifr)) XSRETURN_UNDEF; +#if defined(__NetBSD__) || defined(__OpenBSD__) + ifr.ifr_addr.sa_family = AF_INET; +#endif + if (ifr.ifr_addr.sa_family != AF_INET) croak ("Address is not in the AF_INET family.\n"); + RETVAL = inet_ntoa(((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr); +#endif + } + OUTPUT: + RETVAL + +char* +if_dstaddr(sock, name, ...) + InputStream sock + char* name + PROTOTYPE: $$;$ + PREINIT: + STRLEN len; + IOCTL_CMD_T operation; + struct ifreq ifr; + char* newaddr; + CODE: + { +#if !(defined(HAS_IOCTL) && defined(SIOCGIFDSTADDR)) + XSRETURN_UNDEF; +#else + bzero((void*)&ifr,sizeof(struct ifreq)); + strncpy(ifr.ifr_name,name,IFNAMSIZ-1); + ifr.ifr_addr.sa_family = AF_INET; + if (items > 2) { + newaddr = SvPV(ST(2),len); + if ( inet_aton(newaddr,&((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr) == 0 ) + croak("Invalid inet address"); +#if defined(SIOCSIFDSTADDR) + operation = SIOCSIFDSTADDR; +#else + croak("Cannot set destination address on this platform"); +#endif + } else { + operation = SIOCGIFDSTADDR; + } + if (!Ioctl(sock,operation,&ifr)) XSRETURN_UNDEF; + if (ifr.ifr_addr.sa_family != AF_INET) croak ("Address is not in the AF_INET family.\n"); + RETVAL = inet_ntoa(((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr); +#endif + } + OUTPUT: + RETVAL + +char* +if_hwaddr(sock, name, ...) + InputStream sock + char* name + PROTOTYPE: $$;$ + PREINIT: + STRLEN len; + IOCTL_CMD_T operation; + struct ifreq ifr; +#if (defined(USE_GETIFADDRS) && defined(HAVE_SOCKADDR_DL_STRUCT)) + struct ifaddrs *ifap, *ifa; + struct sockaddr_dl* sdl; + sa_family_t family; + char *sdlname, *haddr, *s; + int hlen = 0; + int i; +#endif + char *newaddr,hwaddr[128]; + CODE: + { +#if !((defined(HAS_IOCTL) && defined(SIOCGIFHWADDR)) || defined(USE_GETIFADDRS)) + XSRETURN_UNDEF; +#endif +#if (defined(USE_GETIFADDRS) && defined(HAVE_SOCKADDR_DL_STRUCT)) + getifaddrs(&ifap); + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (strncmp(name, ifa->ifa_name, IFNAMSIZ) == 0) { + family = ifa->ifa_addr->sa_family; + if (family == AF_LINK) { + sdl = (struct sockaddr_dl *) ifa->ifa_addr; + haddr = sdl->sdl_data + sdl->sdl_nlen; + hlen = sdl->sdl_alen; + break; + } + } + } + + s = hwaddr; + s[0] = '\0'; + if (ifap != NULL) { + for (i = 0; i < hlen; i++) { + if (i < hlen - 1) + len = sprintf(s,"%02x:",(unsigned char)haddr[i]); + else + len = sprintf(s,"%02x",(unsigned char)haddr[i]); + s += len; + } + } + + freeifaddrs(ifap); + + RETVAL = hwaddr; +#elif (defined(HAS_IOCTL) && defined(SIOCGIFHWADDR)) + bzero((void*)&ifr,sizeof(struct ifreq)); + strncpy(ifr.ifr_name,name,IFNAMSIZ-1); + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + if (items > 2) { + newaddr = SvPV(ST(2),len); + if (parse_hwaddr(newaddr,&ifr.ifr_hwaddr) == NULL) + croak("Invalid hardware address"); +#if defined(SIOCSIFHWADDR) + operation = SIOCSIFHWADDR; +#else + croak("Cannot set hw address on this platform"); +#endif + } else { + operation = SIOCGIFHWADDR; + } + if (!Ioctl(sock,operation,&ifr)) XSRETURN_UNDEF; + RETVAL = format_hwaddr(hwaddr,&ifr.ifr_hwaddr); +#endif + } + OUTPUT: + RETVAL + + +int +if_flags(sock, name, ...) + InputStream sock + char* name + PROTOTYPE: $$;$ + PREINIT: + IOCTL_CMD_T operation; + int flags; + struct ifreq ifr; + CODE: + { +#if !(defined(HAS_IOCTL) && defined(SIOCGIFFLAGS)) + XSRETURN_UNDEF; +#endif + bzero((void*)&ifr,sizeof(struct ifreq)); + strncpy(ifr.ifr_name,name,IFNAMSIZ-1); + if (items > 2) { + ifr.ifr_flags = SvIV(ST(2)); +#if defined(SIOCSIFFLAGS) + operation = SIOCSIFFLAGS; +#else + croak("Cannot set flags on this platform."); +#endif + } else { + operation = SIOCGIFFLAGS; + } + if (!Ioctl(sock,operation,&ifr)) XSRETURN_UNDEF; + RETVAL = ifr.ifr_flags; + } + OUTPUT: + RETVAL + +int +if_mtu(sock, name, ...) + InputStream sock + char* name + PROTOTYPE: $$;$ + PREINIT: + IOCTL_CMD_T operation; + int flags; + struct ifreq ifr; + CODE: + { +#if !(defined(HAS_IOCTL) && defined(SIOCGIFFLAGS)) + XSRETURN_UNDEF; +#endif + bzero((void*)&ifr,sizeof(struct ifreq)); + strncpy(ifr.ifr_name,name,IFNAMSIZ-1); + if (items > 2) { + ifr.ifr_flags = SvIV(ST(2)); +#if defined(SIOCSIFMTU) + operation = SIOCSIFMTU; +#else + croak("Cannot set MTU on this platform."); +#endif + } else { + operation = SIOCGIFMTU; + } + if (!Ioctl(sock,operation,&ifr)) XSRETURN_UNDEF; + RETVAL = ifr.ifr_mtu; + } + OUTPUT: + RETVAL + +int +if_metric(sock, name, ...) + InputStream sock + char* name + PROTOTYPE: $$;$ + PREINIT: + IOCTL_CMD_T operation; + int flags; + struct ifreq ifr; + CODE: + { +#if !(defined(HAS_IOCTL) && defined(SIOCGIFFLAGS)) + XSRETURN_UNDEF; +#endif + bzero((void*)&ifr,sizeof(struct ifreq)); + strncpy(ifr.ifr_name,name,IFNAMSIZ-1); + if (items > 2) { + ifr.ifr_flags = SvIV(ST(2)); +#if defined(SIOCSIFMETRIC) + operation = SIOCSIFMETRIC; +#else + croak("Cannot set metric on this platform."); +#endif + } else { + operation = SIOCGIFMETRIC; + } + if (!Ioctl(sock,operation,&ifr)) XSRETURN_UNDEF; + RETVAL = ifr.ifr_metric; + } + OUTPUT: + RETVAL + +int +if_index(sock, name, ...) + InputStream sock + char* name + PROTOTYPE: $$;$ + CODE: + { +#ifdef __USE_BSD + RETVAL = if_nametoindex(name); +#else + XSRETURN_UNDEF; +#endif + } + OUTPUT: + RETVAL + +char* +if_indextoname(sock, index, ...) + InputStream sock + int index + PROTOTYPE: $$;$ + PREINIT: + char name[IFNAMSIZ]; + CODE: + { +#ifdef __USE_BSD + RETVAL = if_indextoname(index,name); +#else + XSRETURN_UNDEF; +#endif + } + OUTPUT: + RETVAL + +void +_if_list(sock) + InputStream sock + PROTOTYPE: $ + PREINIT: +#ifdef USE_GETIFADDRS + struct ifaddrs *ifa_start; + struct ifaddrs *ifa; +#else + struct ifconf ifc; + struct ifreq *ifr; + int lastlen,len; + char *buf,*ptr; +#endif + PPCODE: +#ifdef USE_GETIFADDRS + if (getifaddrs(&ifa_start) < 0) + XSRETURN_EMPTY; + + for (ifa = ifa_start ; ifa ; ifa = ifa->ifa_next) + XPUSHs(sv_2mortal(newSVpv(ifa->ifa_name,0))); + + freeifaddrs(ifa_start); +#else + lastlen = 0; + len = 10 * sizeof(struct ifreq); /* initial buffer size guess */ + for ( ; ; ) { + if ( (buf = safemalloc(len)) == NULL) + croak("Couldn't malloc buffer for ioctl: %s",strerror(errno)); + ifc.ifc_len = len; + ifc.ifc_buf = buf; + if (ioctl(PerlIO_fileno(sock),MY_SIOCGIFCONF,&ifc) < 0) { + if (errno != EINVAL || lastlen != 0) + XSRETURN_EMPTY; + } else { + if (ifc.ifc_len == lastlen) break; /* success, len has not changed */ + lastlen = ifc.ifc_len; + } + len += 10 * sizeof(struct ifreq); /* increment */ + safefree(buf); + } + + for (ptr = buf ; ptr < buf + ifc.ifc_len ; ptr += sizeof(struct ifreq)) { + ifr = (struct ifreq*) ptr; + XPUSHs(sv_2mortal(newSVpv(ifr->ifr_name,0))); + } + safefree(buf); +#endif + |