-
Notifications
You must be signed in to change notification settings - Fork 8
/
ip2asn.c
295 lines (252 loc) · 9.93 KB
/
ip2asn.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
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 <[email protected]>
- 2013/04/14: Returns the entire list of found ASNs. -- Jason Fesler <[email protected]>
- 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 ;
}