#include "EXTERN.h" #include "perl.h" #include "XSUB.h" /* socket definitions */ #include #include #include /* location of IFF_* constants */ #include /* location of getifaddrs() definition */ #ifdef USE_GETIFADDRS #include #ifdef HAVE_SOCKADDR_DL_STRUCT #include #endif #endif #ifndef SIOCGIFCONF #include #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 (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 = NULL; 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); while(1) { if (ifap == NULL) break; if (strncmp(name, ifap -> ifa_name, IFNAMSIZ) == 0) { family = ifap -> ifa_addr -> sa_family; if (family == AF_LINK) { sdl = (struct sockaddr_dl *) ifap->ifa_addr; haddr = sdl->sdl_data + sdl->sdl_nlen; hlen = sdl->sdl_alen; break; } } ifap = ifap -> ifa_next; } freeifaddrs(ifap); 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; } } 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