| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | /* World.xs - XS module of the IP::World module | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | this module maps from IP addresses to country codes, using | 
| 4 |  |  |  |  |  |  | the free WorldIP database from wipmania.com and | 
| 5 |  |  |  |  |  |  | the free GeoIPCountry database from maxmind.com */ | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | #ifdef __cplusplus | 
| 8 |  |  |  |  |  |  | extern "C" { | 
| 9 |  |  |  |  |  |  | #endif | 
| 10 |  |  |  |  |  |  | #include "EXTERN.h" | 
| 11 |  |  |  |  |  |  | #include "perl.h" | 
| 12 |  |  |  |  |  |  | #include "XSUB.h" | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | /* Required by Mac OS with XCode 12 | 
| 15 |  |  |  |  |  |  | Works on Linux and BSD | 
| 16 |  |  |  |  |  |  | Not found on Windows but works anyway without */ | 
| 17 |  |  |  |  |  |  | #ifndef WIN32 | 
| 18 |  |  |  |  |  |  | #include | 
| 19 |  |  |  |  |  |  | #endif | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | #ifdef __cplusplus | 
| 22 |  |  |  |  |  |  | } | 
| 23 |  |  |  |  |  |  | #endif | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | #if U32SIZE != 4 | 
| 26 |  |  |  |  |  |  | #error IP::World can only be run on a system in which the U32 type is 4 bytes long | 
| 27 |  |  |  |  |  |  | #endif | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | typedef unsigned char uc; | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | typedef struct { | 
| 32 |  |  |  |  |  |  | char   *addr; | 
| 33 |  |  |  |  |  |  | union { | 
| 34 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 35 |  |  |  |  |  |  | PerlIO *p; | 
| 36 |  |  |  |  |  |  | #endif | 
| 37 |  |  |  |  |  |  | FILE   *f; | 
| 38 |  |  |  |  |  |  | } io; | 
| 39 |  |  |  |  |  |  | UV   entries; | 
| 40 |  |  |  |  |  |  | U32  mode; | 
| 41 |  |  |  |  |  |  | } wip_self; | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | /* there doesn't seem to be a way to check if function inet_pton is defined */ | 
| 44 | 644 |  |  |  |  |  | int ck_ip4(const char *src, uc *dest) { | 
| 45 |  |  |  |  |  |  | unsigned parts = 0; | 
| 46 |  |  |  |  |  |  | int part = -1; | 
| 47 |  |  |  |  |  |  | char c; | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 4333 | 100 |  |  |  |  | while ((c = *src++)) { | 
| 50 | 4011 | 100 |  |  |  |  | if (c == '.') { | 
| 51 | 917 | 50 |  |  |  |  | if (++parts > 3 || part < 0) return 0; | 
|  |  | 100 |  |  |  |  |  | 
| 52 | 903 |  |  |  |  |  | *dest++ = (uc)part; | 
| 53 |  |  |  |  |  |  | part = -1; | 
| 54 | 3094 | 100 |  |  |  |  | } else if ((c -= '0') >= 0 | 
| 55 | 2905 | 100 |  |  |  |  | && c <= 9) { | 
| 56 | 2814 | 100 |  |  |  |  | if (part < 0) part = c; | 
| 57 | 3717 | 100 |  |  |  |  | else if ((part = part*10 + c) > 255) return 0; | 
| 58 |  |  |  |  |  |  | } else return 0; | 
| 59 |  |  |  |  |  |  | } | 
| 60 | 322 | 100 |  |  |  |  | if (part < 0 || parts < 3) return 0; | 
| 61 | 301 |  |  |  |  |  | *dest = (uc)part; | 
| 62 | 301 |  |  |  |  |  | return 1; | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | /* subsequent code is in the specialized 'XS' dialect of C */ | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | MODULE = IP::World       PACKAGE = IP::World | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | PROTOTYPES: DISABLE | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | SV * | 
| 72 |  |  |  |  |  |  | allocNew(filepath, fileLen, mode=0) | 
| 73 |  |  |  |  |  |  | const char *filepath | 
| 74 |  |  |  |  |  |  | STRLEN fileLen | 
| 75 |  |  |  |  |  |  | unsigned mode | 
| 76 |  |  |  |  |  |  | PREINIT: | 
| 77 |  |  |  |  |  |  | wip_self self; | 
| 78 |  |  |  |  |  |  | int readLen; | 
| 79 |  |  |  |  |  |  | CODE: | 
| 80 |  |  |  |  |  |  | /* XS part of IP::World->new | 
| 81 |  |  |  |  |  |  | allocate a block of memory and fill it from the ipworld.dat file */ | 
| 82 | 9 | 50 |  |  |  |  | if (mode > 3) croak("operand of IP::World::new = %d, should be 0-3", mode); | 
| 83 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 84 | 9 | 100 |  |  |  |  | if (mode != 2) self.io.p = PerlIO_open(filepath, "rb"); | 
| 85 |  |  |  |  |  |  | else | 
| 86 |  |  |  |  |  |  | #endif | 
| 87 | 2 |  |  |  |  |  | self.io.f = fopen(filepath, "rb"); | 
| 88 | 9 | 50 |  |  |  |  | if (!self.io.f) croak("Can't open %s: %s", filepath, strerror(errno)); | 
| 89 | 9 |  |  |  |  |  | self.mode = mode; | 
| 90 |  |  |  |  |  |  | #ifdef HAS_MMAP | 
| 91 |  |  |  |  |  |  | #include | 
| 92 | 9 | 100 |  |  |  |  | if (mode == 1) { | 
| 93 |  |  |  |  |  |  | /* experimental feature: use mmap rather than read */ | 
| 94 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 95 | 2 |  |  |  |  |  | int fd = PerlIO_fileno(self.io.p); | 
| 96 |  |  |  |  |  |  | #else | 
| 97 |  |  |  |  |  |  | int fd = fileno(self.io.f); | 
| 98 |  |  |  |  |  |  | #endif | 
| 99 | 2 |  |  |  |  |  | self.addr = (char *)mmap(0, fileLen, PROT_READ, MAP_SHARED, fd, 0); | 
| 100 | 2 | 50 |  |  |  |  | if (self.addr == MAP_FAILED) | 
| 101 | 0 |  |  |  |  |  | croak ("mmap failed on %s: %s\n", filepath, strerror(errno)); | 
| 102 |  |  |  |  |  |  | } else | 
| 103 |  |  |  |  |  |  | #endif | 
| 104 | 7 | 100 |  |  |  |  | if (mode < 2) { | 
| 105 |  |  |  |  |  |  | /* malloc a block of size fileLen */ | 
| 106 |  |  |  |  |  |  | #if (PERL_VERSION==8 && PERL_SUBVERSION > 7) || (PERL_VERSION==9 && PERL_SUBVERSION > 2) || PERL_VERSION > 9 | 
| 107 | 3 |  |  |  |  |  | Newx(self.addr, fileLen, char); | 
| 108 |  |  |  |  |  |  | #else | 
| 109 |  |  |  |  |  |  | New(0, self.addr, fileLen, char); | 
| 110 |  |  |  |  |  |  | #endif | 
| 111 | 3 | 50 |  |  |  |  | if (!self.addr) croak ("memory allocation for %s failed", filepath); | 
| 112 |  |  |  |  |  |  | /* read the data from the .dat file into the new block */ | 
| 113 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 114 | 3 |  |  |  |  |  | readLen = PerlIO_read(self.io.p, self.addr, fileLen); | 
| 115 |  |  |  |  |  |  | #else | 
| 116 |  |  |  |  |  |  | readLen = fread(self.addr, 1, fileLen, self.io.f); | 
| 117 |  |  |  |  |  |  | #endif | 
| 118 | 3 | 50 |  |  |  |  | if (readLen < 0) croak("read from %s failed: %s", filepath, strerror(errno)); | 
| 119 | 3 | 50 |  |  |  |  | if ((STRLEN)readLen != fileLen) | 
| 120 | 0 |  |  |  |  |  | croak("should have read %d bytes from %s, actually read %d", | 
| 121 |  |  |  |  |  |  | (int)fileLen, filepath, readLen); | 
| 122 | 3 |  |  |  |  |  | self.mode = 0; | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  | /* all is well */ | 
| 125 | 9 | 100 |  |  |  |  | if (mode < 2) | 
| 126 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 127 | 5 |  |  |  |  |  | PerlIO_close(self.io.p); | 
| 128 |  |  |  |  |  |  | #else | 
| 129 |  |  |  |  |  |  | fclose(self.io.f); | 
| 130 |  |  |  |  |  |  | #endif | 
| 131 |  |  |  |  |  |  | /* For each entry there is a 4 byte address plus a 10 bit country code. | 
| 132 |  |  |  |  |  |  | At 3 codes/word, the number of entries = 3/16 * the number of bytes */ | 
| 133 | 9 |  |  |  |  |  | self.entries = fileLen*3 >> 4; | 
| 134 |  |  |  |  |  |  | /* {new} in World.pm will bless the object we return */ | 
| 135 | 9 |  |  |  |  |  | RETVAL = newSVpv((const char *)(&self), sizeof(wip_self)); | 
| 136 |  |  |  |  |  |  | OUTPUT: | 
| 137 |  |  |  |  |  |  | RETVAL | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | SV* | 
| 140 |  |  |  |  |  |  | getcc(self_ref, ip_sv) | 
| 141 |  |  |  |  |  |  | SV* self_ref | 
| 142 |  |  |  |  |  |  | SV* ip_sv | 
| 143 |  |  |  |  |  |  | PREINIT: | 
| 144 |  |  |  |  |  |  | SV* self_deref; | 
| 145 |  |  |  |  |  |  | char *s; | 
| 146 | 651 |  |  |  |  |  | STRLEN len = 0; | 
| 147 |  |  |  |  |  |  | wip_self self; | 
| 148 |  |  |  |  |  |  | I32 flgs; | 
| 149 |  |  |  |  |  |  | uc netip[4]; | 
| 150 |  |  |  |  |  |  | register U32 ip, *ips; | 
| 151 |  |  |  |  |  |  | register UV i, bottom = 0, top; | 
| 152 |  |  |  |  |  |  | U32 word; | 
| 153 | 651 |  |  |  |  |  | char c[3] = "**"; | 
| 154 |  |  |  |  |  |  | CODE: | 
| 155 |  |  |  |  |  |  | /* $new_obj->getcc is only in XS/C | 
| 156 |  |  |  |  |  |  | check that self_ref is defined ref; dref it; check len; copy to self */ | 
| 157 | 651 | 50 |  |  |  |  | if (sv_isobject(self_ref)) { | 
| 158 | 651 |  |  |  |  |  | self_deref = SvRV(self_ref); | 
| 159 | 651 | 50 |  |  |  |  | if (SvPOK(self_deref)) s = SvPV(self_deref, len); | 
|  |  | 50 |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | } | 
| 161 | 651 | 50 |  |  |  |  | if (len != sizeof(wip_self)) | 
| 162 | 0 |  |  |  |  |  | croak("automatic 'self' operand to getcc is not of correct type"); | 
| 163 |  |  |  |  |  |  | memcpy (&self, s, sizeof(wip_self)); | 
| 164 |  |  |  |  |  |  | /* the ip_sv argument can be of 2 types (if error return '**') */ | 
| 165 | 651 | 100 |  |  |  |  | if (!SvOK(ip_sv)) goto set_retval; | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 166 | 644 |  |  |  |  |  | flgs = SvFLAGS(ip_sv); | 
| 167 | 644 | 50 |  |  |  |  | if (!(flgs & (SVp_POK|SVf_NOK|SVp_NOK|SVf_IOK|SVp_IOK))) goto set_retval; | 
| 168 | 644 | 50 |  |  |  |  | s = SvPV(ip_sv, len); | 
| 169 |  |  |  |  |  |  | /* if the the ip operand is a dotted string, convert it to network-order U32 | 
| 170 |  |  |  |  |  |  | else if the operand does't look like a network-order U32, lose */ | 
| 171 | 644 | 100 |  |  |  |  | if (ck_ip4(s, netip) > 0) s = (char *)netip; | 
| 172 | 343 | 100 |  |  |  |  | else if (len != 4) goto set_retval; | 
| 173 |  |  |  |  |  |  | /* if necessary, convert network order (big-endian) to native endianism */ | 
| 174 | 616 |  |  |  |  |  | ip = (uc)s[0] << 24 | (uc)s[1] << 16 | (uc)s[2] << 8 | (uc)s[3]; | 
| 175 |  |  |  |  |  |  | /* binary-search the IP table */ | 
| 176 | 616 |  |  |  |  |  | top = self.entries; | 
| 177 | 616 | 100 |  |  |  |  | if (self.mode < 2) { | 
| 178 |  |  |  |  |  |  | /* memory mode */ | 
| 179 | 264 |  |  |  |  |  | ips = (U32 *)self.addr; | 
| 180 | 5040 | 100 |  |  |  |  | while (bottom < top-1) { | 
| 181 |  |  |  |  |  |  | /* compare ip to the table entry halfway between top and bottom */ | 
| 182 | 4512 |  |  |  |  |  | i = (bottom + top) >> 1; | 
| 183 | 4776 | 100 |  |  |  |  | if (ip < ips[i]) top = i; | 
| 184 |  |  |  |  |  |  | else bottom = i; | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  | /* the table of country codes (3 per word) follows the table of IPs | 
| 187 |  |  |  |  |  |  | copy the corresponding 3 entries to word */ | 
| 188 | 264 |  |  |  |  |  | word = *(ips + self.entries + bottom/3); | 
| 189 |  |  |  |  |  |  | } else { | 
| 190 |  |  |  |  |  |  | /* DASD mode */ | 
| 191 | 6368 | 100 |  |  |  |  | while (bottom < top-1) { | 
| 192 |  |  |  |  |  |  | /* compare ip to the table entry halfway between top and bottom */ | 
| 193 | 6016 |  |  |  |  |  | i = (bottom + top) >> 1; | 
| 194 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 195 | 6016 | 100 |  |  |  |  | if (self.mode == 3) { | 
| 196 | 3008 |  |  |  |  |  | PerlIO_seek(self.io.p, i<<2, 0); | 
| 197 | 3008 |  |  |  |  |  | PerlIO_read(self.io.p, &word, 4); | 
| 198 |  |  |  |  |  |  | } else { | 
| 199 |  |  |  |  |  |  | #endif | 
| 200 | 3008 |  |  |  |  |  | fseek(self.io.f, i<<2, 0); | 
| 201 |  |  |  |  |  |  | fread(&word, 4, 1, self.io.f); | 
| 202 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 203 |  |  |  |  |  |  | } | 
| 204 |  |  |  |  |  |  | #endif | 
| 205 | 6016 | 100 |  |  |  |  | if (ip < word) top = i; | 
| 206 |  |  |  |  |  |  | else bottom = i; | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 209 |  |  |  |  |  |  | /* the table of country codes (3 per word) follows the table of IPs | 
| 210 |  |  |  |  |  |  | read the corresponding 3 entries into word */ | 
| 211 | 352 | 100 |  |  |  |  | if (self.mode == 3) { | 
| 212 | 176 |  |  |  |  |  | PerlIO_seek(self.io.p, (self.entries + bottom/3)<<2, 0); | 
| 213 | 176 |  |  |  |  |  | PerlIO_read(self.io.p, &word, 4); | 
| 214 |  |  |  |  |  |  | } else { | 
| 215 |  |  |  |  |  |  | #endif | 
| 216 | 176 |  |  |  |  |  | fseek(self.io.f, (self.entries + bottom/3)<<2, 0); | 
| 217 |  |  |  |  |  |  | fread(&word, 4, 1, self.io.f); | 
| 218 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 219 |  |  |  |  |  |  | } | 
| 220 |  |  |  |  |  |  | #endif | 
| 221 |  |  |  |  |  |  | } | 
| 222 | 616 |  |  |  |  |  | switch (bottom % 3) { | 
| 223 | 245 |  |  |  |  |  | case 0:  word >>= 20; break; | 
| 224 | 189 |  |  |  |  |  | case 1:  word = word>>10 & 0x3FF; break; | 
| 225 | 182 |  |  |  |  |  | default: word &= 0x3FF; | 
| 226 |  |  |  |  |  |  | } | 
| 227 | 616 | 100 |  |  |  |  | if (word == 26*26) c[0] = c[1] = '?'; | 
| 228 |  |  |  |  |  |  | else { | 
| 229 | 455 |  |  |  |  |  | c[0] = (char)(word / 26) + 'A'; | 
| 230 | 455 |  |  |  |  |  | c[1] = (char)(word % 26) + 'A'; | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  | set_retval: | 
| 233 | 651 |  |  |  |  |  | RETVAL = newSVpv(c, 2); | 
| 234 |  |  |  |  |  |  | OUTPUT: | 
| 235 |  |  |  |  |  |  | RETVAL | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | void | 
| 238 |  |  |  |  |  |  | DESTROY(self_ref) | 
| 239 |  |  |  |  |  |  | SV* self_ref | 
| 240 |  |  |  |  |  |  | PREINIT: | 
| 241 |  |  |  |  |  |  | SV *self_deref; | 
| 242 |  |  |  |  |  |  | char *s; | 
| 243 | 9 |  |  |  |  |  | STRLEN len = 0; | 
| 244 |  |  |  |  |  |  | wip_self self; | 
| 245 |  |  |  |  |  |  | CODE: | 
| 246 |  |  |  |  |  |  | /* DESTROY gives back allocated memory | 
| 247 |  |  |  |  |  |  | check that self_ref is defined ref; dref it; check len; copy to self */ | 
| 248 | 9 | 50 |  |  |  |  | if (sv_isobject(self_ref)) { | 
| 249 | 9 |  |  |  |  |  | self_deref = SvRV(self_ref); | 
| 250 | 9 | 50 |  |  |  |  | if (SvPOK(self_deref)) | 
| 251 | 9 | 50 |  |  |  |  | s = SvPV(self_deref, len); | 
| 252 |  |  |  |  |  |  | } | 
| 253 | 9 | 50 |  |  |  |  | if (len != sizeof(wip_self)) | 
| 254 | 0 |  |  |  |  |  | croak("automatic 'self' operand to DESTROY is not of correct type"); | 
| 255 |  |  |  |  |  |  | memcpy (&self, s, sizeof(wip_self)); | 
| 256 |  |  |  |  |  |  | #ifdef HAS_MMAP | 
| 257 | 9 | 100 |  |  |  |  | if (self.mode == 1) munmap((caddr_t)self.addr, (size_t)((self.entries<<4)/3)); | 
| 258 |  |  |  |  |  |  | else | 
| 259 |  |  |  |  |  |  | #endif | 
| 260 | 7 | 100 |  |  |  |  | if (self.mode < 2) Safefree(self.addr); | 
| 261 |  |  |  |  |  |  | else | 
| 262 |  |  |  |  |  |  | #ifdef USE_PERLIO | 
| 263 | 4 | 100 |  |  |  |  | if (self.mode == 3) PerlIO_close(self.io.p); | 
| 264 |  |  |  |  |  |  | else | 
| 265 |  |  |  |  |  |  | #endif | 
| 266 | 2 |  |  |  |  |  | fclose(self.io.f); |