summaryrefslogtreecommitdiff
path: root/ip2asn.c
diff options
context:
space:
mode:
authorJason Fesler <jfesler@free.gigo.com>2014-04-02 19:48:27 -0700
committerJason Fesler <jfesler@free.gigo.com>2014-04-02 19:48:27 -0700
commitcb465c6f511d63b5445a640735295de967126e2c (patch)
tree4b2e5639c92e9e659bc234b07eefcaae031b501c /ip2asn.c
parente8e6401f8c7bb6ab64dbbcfc10618e792fcbd85b (diff)
downloadmod_ip-cb465c6f511d63b5445a640735295de967126e2c.tar.gz
mod_ip-cb465c6f511d63b5445a640735295de967126e2c.zip
Imported without history from https://falling-sky.googlecode.com/svn/trunk/mod_ip revision 15111.0
Diffstat (limited to 'ip2asn.c')
-rw-r--r--ip2asn.c295
1 files changed, 295 insertions, 0 deletions
diff --git a/ip2asn.c b/ip2asn.c
new file mode 100644
index 0000000..0d60022
--- /dev/null
+++ b/ip2asn.c
@@ -0,0 +1,295 @@
+/*
+ Copyright 2013 Eric Vyncke
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+/* TODO use nquery to be thread safe???? */
+
+/* History
+- 2013/04/28: Made ./configure friendly. -- Jason Fesler <jfesler@gigo.com>
+- 2013/04/14: Returns the entire list of found ASNs. -- Jason Fesler <jfesler@gigo.com>
+- 2013/03/26: handles the case where multiple RR TXT are returned
+- 2013/01/25: also fetch ASN name
+- 2012/12/27: first release
+*/
+
+
+
+/* We will use Apache headers */
+
+#include <http_protocol.h>
+#include <http_config.h>
+#include <http_log.h>
+#include <apr_hash.h>
+#include <apr_strings.h>
+#include <apr_network_io.h>
+
+
+/* Those unfortunately look like they claim the autoconf variables, so let's undefine them */
+
+#undef PACKAGE_VERSION
+#undef PACKAGE_URL
+#undef PACKAGE_STRING
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_TARNAME
+
+/* We need some libraries besides what Apache provides. */
+
+#include "config.h"
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+
+/* Including DNS resolution. */
+
+ #ifdef HAVE_SYS_TYPES_H
+ # include <sys/types.h>
+ #endif
+ #ifdef HAVE_NETINET_IN_H
+ # include <netinet/in.h> /* inet_ functions / structs */
+ #endif
+ #ifdef HAVE_ARPA_NAMESER_H
+ # include <arpa/nameser.h> /* DNS HEADER struct */
+ #endif
+ #ifdef HAVE_NETDB_H
+ # include <netdb.h>
+ #endif
+ #include <resolv.h>
+
+
+
+#include "ip2asn.h"
+
+
+#undef IP2ASN_DEBUG
+
+#define MAX_REVERSE_NAME 256
+#define DNS_ANSWER_LENGTH 512
+
+#ifdef IP2ASN_DEBUG
+static void Dump(const unsigned char * s, const int l) {
+ int i, j ;
+
+ for (i = 0 ; i < l ; i += 16) {
+ for (j = 0 ; (j < 16) && (i+j < l) ; j++)
+ printf("%2.2X ", s[i+j]) ;
+ printf(" ") ;
+ for (j = 0 ; (j < 16) && (i+j < l) ; j++)
+ if ((s[i+j] >= ' ') && (s[i+j] <= 127))
+ printf("%c ", s[i+j]) ;
+ else
+ printf(". ") ;
+ printf("\n") ;
+ }
+ printf("\n") ;
+}
+#endif
+
+static unsigned char * skipName(unsigned char * pRR) { /* Let's be faithful and trust that we will not overun */
+ while (*pRR != 0) { /* a label length of zero means ROOT label, end of the chain */
+ if (*pRR & 0xC0) { /* Using a compressed format, easy as the encoded offset is two bytes and then we are done */
+ pRR++ ; /* Not +2 because at the loop exit we add 1 again, I know dirty...*/
+ break ;
+ }
+ pRR += *pRR + 1 ; /* We were pointing to the label length, so we need to skip it */
+ }
+ return pRR+1 ; /* We need to skip the root label anyway */
+}
+
+static unsigned char * skipQuestions(unsigned char * pRR, const int count) { /* Let's be faithful and trust that we will not overun */
+ if (count == 0) return pRR ;
+ /* First skip the QName field */
+ pRR = skipName(pRR) ;
+ return skipQuestions(pRR + 4, count - 1) ; /* Skip again 4 bytes for class and type */
+}
+
+static unsigned long int GetASNFromDNS(request_rec * r,const char * name, const char * domain,char **asnlist_ptr) {
+ int length, rrLength, labelLength, iAnswer, prefixLength, bestPrefixLength ;
+ unsigned long int ASN, bestASN ;
+ unsigned char answer[DNS_ANSWER_LENGTH] ;
+ char value[DNS_ANSWER_LENGTH+1] ;
+ HEADER * h ;
+ unsigned char * pRR ;
+
+#ifdef IP2ASN_DEBUG
+ printf("Querying DNS for the TXT record of %s.%s...\n", name, domain) ;
+#endif
+
+ length = res_querydomain(name, domain, C_IN, T_TXT, answer, sizeof(answer));
+ h = (HEADER *) answer ;
+#ifdef IP2ASN_DEBUG
+ Dump(answer, length) ;
+ printf("First %d bytes:\n", sizeof(HEADER)) ;
+ printf("%d questions\n", ntohs(h->qdcount)) ;
+ printf("%d answers\n", ntohs(h->ancount)) ;
+ printf("%d authority\n", ntohs(h->nscount)) ;
+ printf("%d ressources\n", ntohs(h->arcount)) ;
+#endif
+ if (ntohs(h->ancount) == 0) {
+ fprintf(stderr, "mod_ip: Received no valid answer from DNS server...\n") ;
+ return -1 ;
+ }
+ pRR = answer + sizeof(HEADER) ;
+ pRR = skipQuestions(pRR, ntohs(h->qdcount)) ;
+ /* pRR now points to the encoded answer RRs, sometimes several of them
+ "33651 | 24.130.0.0/16 | US | arin | 1996-07-19"
+ "7922 | 24.130.0.0/15 | US | arin | 1996-07-19" */
+ bestASN = -1 ;
+ bestPrefixLength = -1 ;
+ for (iAnswer = 0; iAnswer < ntohs(h->ancount); iAnswer++) {
+ pRR = skipName(pRR) ; /* Skip the name of the answer to go to the RR type */
+ if (pRR[0] * 256 + pRR[1] != T_TXT) fprintf(stderr, "mod_ip: Oups... cannot decode the DNS answer part, wrong RR type\n") ;
+ pRR += 2 ; /* Move to RR class */
+ if (pRR[0] * 256 + pRR[1] != C_IN) fprintf(stderr, "mod_ip: Oups... cannot decode the DNS answer part, wrong RR class\n") ;
+ pRR += 6 ; /* Simply skip RR class + TTL */
+ rrLength = pRR[0] * 256 + pRR[1] ; /* RRLENGTH */
+ if (rrLength > DNS_ANSWER_LENGTH) rrLength = DNS_ANSWER_LENGTH ; /* Ugly truncation */
+ pRR +=2 ; /* Skip the RR length */
+ labelLength = (unsigned char) *pRR ;
+ memcpy(value, pRR+1, labelLength) ; /* One byte for the label length followed by the label ... assuming a single label here */
+ value[labelLength] = 0 ;
+#ifdef IP2ASN_DEBUG
+ printf("TXT(%d) = '%s'\n", labelLength, value) ;
+#endif
+ if (sscanf(value, "%ld | %*[0123456789abcdef.:]/%d |", &ASN, &prefixLength) != 2) {
+ fprintf(stderr,"mod_ip: Cannot extract the ASN from %s\n", value) ;
+ return -1 ;
+ }
+ if (prefixLength > bestPrefixLength) { /* better than the current longuest match? */
+ bestPrefixLength = prefixLength ;
+ bestASN = ASN ;
+ }
+ pRR += labelLength + 1;
+
+ /* We also want to report the entire list of ASNs now. */
+ if (*asnlist_ptr) {
+ *asnlist_ptr = apr_psprintf(r->pool,"%lu;%s",ASN,*asnlist_ptr);
+ } else {
+ *asnlist_ptr = apr_psprintf(r->pool,"%lu",ASN);
+ }
+ }
+ return bestASN ;
+}
+
+static unsigned long int GetASN4(request_rec * r,const char * ip, char **asnlist_ptr) {
+ struct in_addr saddr ;
+ unsigned char addr[4] ;
+ char reverse_name[MAX_REVERSE_NAME+1] ;
+ int i ;
+
+ memset(reverse_name, 0, MAX_REVERSE_NAME+1) ;
+ if (inet_pton(AF_INET, ip, &saddr) <= 0) {
+ fprintf(stderr, "mod_ip: Invalid IPv4 address %s\n", ip) ;
+ return -1 ;
+ }
+ for (i = 0; i < 4 ; i++)
+ addr[i] = ((unsigned char *) &saddr)[3-i] ;
+ snprintf(reverse_name, MAX_REVERSE_NAME, "%d.%d.%d.%d", addr[0],addr[1],addr[2],addr[3]) ;
+ return GetASNFromDNS(r,reverse_name, "origin.asn.cymru.com",asnlist_ptr) ;
+}
+
+static unsigned long int GetASN6(request_rec * r,const char * ip, char **asnlist_ptr) {
+ struct in6_addr saddr ;
+ char nibble_pair[5] ;
+ char reverse_name[MAX_REVERSE_NAME+1] ;
+ int i ;
+
+ memset(reverse_name, 0, MAX_REVERSE_NAME+1) ;
+ if (inet_pton(AF_INET6, ip, &saddr) <= 0) {
+ fprintf(stderr, "mod_ip: Invalid IPv6 address %s\n", ip) ;
+ return -1 ;
+ }
+ reverse_name[0] = 0 ;
+ for (i = 7; i >= 0 ; i--) {
+ if (i != 7) strncat(reverse_name, ".", MAX_REVERSE_NAME) ;
+ sprintf(nibble_pair, "%x.%x", saddr.s6_addr[i] & 0x0F, saddr.s6_addr[i] >> 4) ;
+ strncat(reverse_name, nibble_pair, MAX_REVERSE_NAME) ;
+ }
+ return GetASNFromDNS(r,reverse_name, "origin6.asn.cymru.com",asnlist_ptr) ;
+}
+
+unsigned long int GetASN(request_rec * r, const char * ip, char **asnlist_ptr) {
+ if (strchr(ip, ':') == NULL)
+ return GetASN4(r,ip,asnlist_ptr) ;
+ else
+ return GetASN6(r,ip,asnlist_ptr) ;
+}
+
+int GetASNName(const unsigned long asn, char * buffer, const int buffer_length) {
+ int length, rrLength, labelLength ;
+ unsigned char answer[DNS_ANSWER_LENGTH] ;
+ char value[DNS_ANSWER_LENGTH+1] ;
+ HEADER * h ;
+ unsigned char * pRR ;
+ char * lastSlash ;
+ char asn_as_string[13] ; /* Max length of 2*32 as unsigned +3 for null & AS */
+
+ snprintf(asn_as_string, 12, "AS%ld", asn) ;
+#ifdef IP2ASN_DEBUG
+ printf("Querying DNS for the TXT record of %s.asn.cymru.com...\n", asn_as_string) ;
+#endif
+
+ length = res_querydomain(asn_as_string, "asn.cymru.com", C_IN, T_TXT, answer, sizeof(answer));
+ h = (HEADER *) answer ;
+#ifdef IP2ASN_DEBUG
+ Dump(answer, length) ;
+ printf("First %d bytes:\n", sizeof(HEADER)) ;
+ printf("%d questions\n", ntohs(h->qdcount)) ;
+ printf("%d answers\n", ntohs(h->ancount)) ;
+ printf("%d authority\n", ntohs(h->nscount)) ;
+ printf("%d ressources\n", ntohs(h->arcount)) ;
+#endif
+ if (ntohs(h->ancount) == 0) {
+ fprintf(stderr, "mod_ip: Received no valid answer from DNS server...\n") ;
+ return -1 ;
+ } else if (ntohs(h->ancount) != 1) {
+ fprintf(stderr, "mod_ip: Received more than 1 answer from DNS server!!!\n") ;
+ }
+ pRR = answer + sizeof(HEADER) ;
+ pRR = skipQuestions(pRR, ntohs(h->qdcount)) ;
+ /* pRR now points to the encoded answer RR */
+ pRR = skipName(pRR) ; /* Skip the name of the answer to go to the RR type */
+ if (pRR[0] * 256 + pRR[1] != T_TXT) fprintf(stderr, "mod_ip: Oups... cannot decode the DNS answer part, wrong RR type\n") ;
+ pRR += 2 ; /* Move to RR class */
+ if (pRR[0] * 256 + pRR[1] != C_IN) fprintf(stderr, "mod_ip: Oups... cannot decode the DNS answer part, wrong RR class\n") ;
+ pRR += 6 ; /* Simply skip RR class + TTL */
+ rrLength = pRR[0] * 256 + pRR[1] ; /* RRLENGTH */
+ if (rrLength > DNS_ANSWER_LENGTH) rrLength = DNS_ANSWER_LENGTH ; /* Ugly truncation */
+ pRR +=2 ; /* Skip the RR length */
+ labelLength = (unsigned char) *pRR ;
+ memcpy(value, pRR+1, labelLength) ; /* One byte for the label length followed by the label ... assuming a single label here */
+ value[labelLength] = 0 ;
+#ifdef IP2ASN_DEBUG
+ printf("TXT(%d) = '%s'\n", labelLength, value) ;
+#endif
+ /* Response is '16276 | FR | ripencc | 2001-02-15 | OVH OVH Systems' */
+
+ lastSlash = rindex(value, '|') ;
+ if (lastSlash == NULL) {
+ return -1 ;
+ }
+ lastSlash ++ ; /* Skip the | */
+ while ((lastSlash != 0) && (*lastSlash == ' ')) lastSlash++ ;
+ strncpy(buffer, lastSlash, buffer_length) ;
+ buffer[buffer_length-1] = 0 ; /* Just to be sure it is null terminated */
+ return 0 ;
+}