1 /*
2  * qstat
3  * by Steve Jankowski
4  * steve@activesw.com
5  * http://www.activesw.com/people/steve/qstat.html
6  *
7  * Thanks to Per Hammer for the OS/2 patches (per@mindbend.demon.co.uk)
8  * Thanks to John Ross Hunt for the OpenVMS Alpha patches (bigboote@ais.net)
9  * Thanks to Scott MacFiggen for the quicksort code (smf@activesw.com)
10  *
11  * Inspired by QuakePing by Len Norton
12  *
13  * Copyright 1996,1997,1998,1999 by Steve Jankowski
14  *
15  * Licensed under the Artistic License, see LICENSE.txt for license terms
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <errno.h>
23 
24 #include "qstat.h"
25 #include "debug.h"
26 
27 #ifndef _WIN32
28  #include <sys/types.h>
29  #include <sys/socket.h>
30  #include <netinet/in.h>
31  #include <arpa/inet.h>
32  #include <netdb.h>
33 #endif
34 
35 #ifdef _WIN32
36  #include <winsock.h>
37 #endif
38 
39 #ifdef __hpux
40 	#define STATIC    static
41 #else
42 	#define STATIC
43 #endif
44 
45 #ifndef INADDR_NONE
46 	#define INADDR_NONE    ~0
47 #endif
48 
49 typedef struct _cache_entry {
50 	unsigned long ipaddr;
51 	char *hostname[5];
52 } cache_entry;
53 
54 static cache_entry *hcache;
55 static int n_entry;
56 static int max_entry;
57 static char *last_filename;
58 static int n_changes;
59 
60 static void write_file(FILE *file);
61 static cache_entry *init_entry(unsigned long ipaddr, char *hostname,
62     cache_entry *known);
63 static cache_entry *find_entry(unsigned long ipaddr);
64 static void free_entry(cache_entry *entry);
65 static cache_entry *validate_entry(cache_entry *entry);
66 static cache_entry *find_host_entry(char *hostname);
67 static void add_hostname(cache_entry *entry, const char *hostname);
68 
69 int
hcache_open(char * filename,int update)70 hcache_open(char *filename, int update)
71 {
72 	FILE *file;
73 	char line[500], ipstr[500], hostname[500];
74 	char *l;
75 	int line_no, end;
76 	unsigned long ip1, ip2, ip3, ip4, ipaddr;
77 	cache_entry *entry;
78 
79 	file = fopen(filename, update ? "r+" : "r");
80 	if (file == NULL) {
81 		if (errno == ENOENT) {
82 			debug(2, "Creating new host cache \"%s\"\n", filename);
83 			last_filename = filename;
84 			return (0);
85 		}
86 		perror(filename);
87 		return (-1);
88 	}
89 	last_filename = filename;
90 
91 	for (line_no = 1; fgets(line, sizeof(line), file) != NULL; line_no++) {
92 		if (strlen(line) < 2) {
93 			continue;
94 		}
95 		if (line[strlen(line) - 1] != '\n') {
96 			printf("%d: line too long\n", line_no);
97 			continue;
98 		}
99 		l = line;
100 		while (isspace((unsigned char)*l)) {
101 			l++;
102 		}
103 		if ((*l == '#') || (*l == '\0')) {
104 			continue;
105 		}
106 		if (sscanf(l, "%s%n", ipstr, &end) != 1) {
107 			printf("%d: parse error\n", line_no);
108 			continue;
109 		}
110 		if (sscanf(ipstr, "%lu.%lu.%lu.%lu", &ip1, &ip2, &ip3, &ip4) != 4) {
111 			init_entry(0, ipstr, NULL);
112 			continue;
113 		}
114 
115 		if ((ip1 & 0xffffff00) || (ip2 & 0xffffff00) ||
116 		    (ip3 & 0xffffff00) || (ip4 & 0xffffff00)) {
117 			printf("%d: invalid IP address \"%s\"\n", line_no, ipstr);
118 			continue;
119 		}
120 		ipaddr = (ip1 << 24) | (ip2 << 16) | (ip3 << 8) | ip4;
121 
122 		entry = init_entry(ipaddr, NULL, NULL);
123 		while (1) {
124 			l += end;
125 			while (isspace((unsigned char)*l)) {
126 				l++;
127 			}
128 			if ((*l == '#') || (*l == '\0')) {
129 				break;
130 			}
131 			hostname[0] = '\0';
132 			if (sscanf(l, "%s%n", hostname, &end) != 1) {
133 				printf("%d: parse error\n", line_no);
134 				continue;
135 			}
136 			init_entry(ipaddr, hostname, entry);
137 		}
138 	}
139 	fclose(file);
140 	return (0);
141 }
142 
143 
144 STATIC cache_entry *
init_entry(unsigned long ipaddr,char * hostname,cache_entry * known)145 init_entry(unsigned long ipaddr, char *hostname, cache_entry *known)
146 {
147 	cache_entry *entry;
148 	int e = 0, h;
149 
150 	if (n_entry == max_entry) {
151 		if (max_entry == 0) {
152 			max_entry = 50;
153 			hcache = (cache_entry *)malloc(sizeof(cache_entry) * max_entry * 2);
154 		} else {
155 			hcache = (cache_entry *)realloc(hcache,
156 				sizeof(cache_entry) * max_entry * 2);
157 		}
158 		memset(hcache + n_entry, 0, sizeof(cache_entry) * max_entry *
159 		    (n_entry == 0 ? 2 : 1));
160 		max_entry *= 2;
161 	}
162 
163 	if (ipaddr == 0) {
164 		entry = find_host_entry(hostname);
165 		if (entry == NULL) {
166 			hcache[n_entry].hostname[0] = strdup(hostname);
167 			return (&hcache[n_entry++]);
168 		}
169 		return (entry);
170 	}
171 
172 	if (known != NULL) {
173 		entry = known;
174 	} else {
175 		for (e = 0; e < n_entry; e++) {
176 			if (hcache[e].ipaddr == ipaddr) {
177 				break;
178 			}
179 		}
180 		entry = &hcache[e];
181 		entry->ipaddr = ipaddr;
182 	}
183 
184 	if (hostname && (hostname[0] != '\0')) {
185 		for (h = 0; h < 5; h++) {
186 			if (entry->hostname[h] == NULL) {
187 				entry->hostname[h] = strdup(hostname);
188 				break;
189 			}
190 		}
191 	}
192 	if (e == n_entry) {
193 		n_entry++;
194 	}
195 	return (entry);
196 }
197 
198 
199 STATIC cache_entry *
find_host_entry(char * hostname)200 find_host_entry(char *hostname)
201 {
202 	cache_entry *entry = &hcache[0];
203 	char **ehost;
204 	int e, h;
205 	char first = *hostname;
206 
207 	for (e = 0; e < n_entry; e++, entry++) {
208 		ehost = &entry->hostname[0];
209 		for (h = 0; h < 5; h++, ehost++) {
210 			if (*ehost && (first == **ehost) && (strcmp(hostname, *ehost) == 0)) {
211 				return (entry);
212 			}
213 		}
214 	}
215 	return (NULL);
216 }
217 
218 
219 void
hcache_write_file(char * filename)220 hcache_write_file(char *filename)
221 {
222 	FILE *file;
223 
224 	if (filename != NULL) {
225 		file = fopen(filename, "w");
226 	} else {
227 		file = stdout;
228 	}
229 	if (file == NULL) {
230 		perror(filename);
231 		return;
232 	}
233 	write_file(file);
234 }
235 
236 
237 void
hcache_update_file()238 hcache_update_file()
239 {
240 	FILE *file;
241 
242 	if ((last_filename == NULL) || (n_changes == 0)) {
243 		return;
244 	}
245 
246 	file = fopen(last_filename, "w");
247 	if (file == NULL) {
248 		perror(last_filename);
249 		return;
250 	}
251 	write_file(file);
252 }
253 
254 
255 STATIC void
write_file(FILE * file)256 write_file(FILE *file)
257 {
258 	int e, h;
259 
260 	for (e = 0; e < n_entry; e++) {
261 		unsigned long ipaddr = hcache[e].ipaddr;
262 		if (ipaddr == 0) {
263 			continue;
264 		}
265 		fprintf(file, "%lu.%lu.%lu.%lu", (ipaddr & 0xff000000) >> 24,
266 		    (ipaddr & 0xff0000) >> 16, (ipaddr & 0xff00) >> 8, ipaddr & 0xff);
267 		if (hcache[e].hostname[0]) {
268 			for (h = 0; h < 5; h++) {
269 				if (hcache[e].hostname[h] != NULL) {
270 					fprintf(file, "%c%s", h ? ' ' : '\t', hcache[e].hostname[h]);
271 				}
272 			}
273 		}
274 		fprintf(file, "\n");
275 	}
276 	fclose(file);
277 }
278 
279 
280 void
hcache_invalidate()281 hcache_invalidate()
282 {
283 	int e;
284 
285 	for (e = 0; e < n_entry; e++) {
286 		if (hcache[e].ipaddr != 0) {
287 			memset(&hcache[e].hostname[0], 0, sizeof(hcache[e].hostname));
288 		}
289 	}
290 }
291 
292 
293 void
hcache_validate()294 hcache_validate()
295 {
296 	int e;
297 	char **alias;
298 	struct hostent *ent;
299 	unsigned long ipaddr;
300 	cache_entry *entry;
301 
302 	for (e = 0; e < n_entry; e++) {
303 		fprintf(stderr, "\r%d / %d  validating ", e, n_entry);
304 		if (hcache[e].ipaddr != 0) {
305 			ipaddr = hcache[e].ipaddr;
306 			fprintf(stderr, "%lu.%lu.%lu.%lu", (ipaddr & 0xff000000) >> 24,
307 			    (ipaddr & 0xff0000) >> 16, (ipaddr & 0xff00) >> 8, ipaddr & 0xff);
308 			ipaddr = htonl(ipaddr);
309 			ent = gethostbyaddr((char *)&ipaddr, sizeof(unsigned long),
310 				AF_INET);
311 		} else if (hcache[e].hostname[0] != NULL) {
312 			fprintf(stderr, "%s", hcache[e].hostname[0]);
313 			ent = gethostbyname(hcache[e].hostname[0]);
314 			if (ent != NULL) {
315 				memcpy(&ipaddr, ent->h_addr_list[0], sizeof(ipaddr));
316 				ipaddr = ntohl(ipaddr);
317 				if ((entry = find_entry(ipaddr)) != NULL) {
318 					add_hostname(entry, hcache[e].hostname[0]);
319 					free_entry(&hcache[e]);
320 				} else {
321 					hcache[e].ipaddr = ipaddr;
322 				}
323 			}
324 		} else {
325 			continue;
326 		}
327 		if (ent == NULL) {
328 			continue;
329 		}
330 
331 		if (ent->h_name && (ent->h_name[0] != '\0')) {
332 			add_hostname(&hcache[e], ent->h_name);
333 		}
334 		printf("h_name %s\n", ent->h_name ? ent->h_name : "NULL");
335 		alias = ent->h_aliases;
336 		while (*alias) {
337 			add_hostname(&hcache[e], *alias);
338 			printf("h_aliases %s\n", *alias);
339 			alias++;
340 		}
341 	}
342 }
343 
344 
345 STATIC cache_entry *
validate_entry(cache_entry * entry)346 validate_entry(cache_entry *entry)
347 {
348 	struct hostent *ent;
349 	char **alias;
350 	cache_entry *tmp;
351 	unsigned long ipaddr;
352 
353 	if (entry->ipaddr != 0) {
354 		ipaddr = htonl(entry->ipaddr);
355 
356 /*	    fprintf( stderr, "%u.%u.%u.%u", (ipaddr&0xff000000)>>24,
357  *      (ipaddr&0xff0000)>>16, (ipaddr&0xff00)>>8, ipaddr&0xff);
358  */
359 		ent = gethostbyaddr((char *)&ipaddr, sizeof(unsigned long), AF_INET);
360 	} else if (entry->hostname[0] != NULL) {
361 /*      fprintf( stderr, "%s", entry->hostname[0]);
362  */
363 		ent = gethostbyname(entry->hostname[0]);
364 		if (ent != NULL) {
365 			memcpy(&ipaddr, ent->h_addr_list[0], sizeof(ipaddr));
366 			ipaddr = ntohl(ipaddr);
367 			if ((tmp = find_entry(ipaddr)) != NULL) {
368 				add_hostname(tmp, entry->hostname[0]);
369 				free_entry(entry);
370 				entry = tmp;
371 			} else {
372 				entry->ipaddr = ipaddr;
373 			}
374 		}
375 	} else {
376 		return (NULL);
377 	}
378 
379 	if (ent == NULL) {
380 		return (NULL);
381 	}
382 
383 	if (ent->h_name && (ent->h_name[0] != '\0')) {
384 		add_hostname(entry, ent->h_name);
385 	}
386 	alias = ent->h_aliases;
387 	while (*alias) {
388 		add_hostname(entry, *alias);
389 		alias++;
390 	}
391 	return (entry);
392 }
393 
394 
395 unsigned long
hcache_lookup_hostname(char * hostname)396 hcache_lookup_hostname(char *hostname)
397 {
398 	cache_entry *entry;
399 	int e, h;
400 
401 	debug(1, "looking up %s\n", hostname);
402 	for (e = 0; e < n_entry; e++) {
403 		for (h = 0; h < 5; h++) {
404 			if (hcache[e].hostname[h] &&
405 			    (strcmp(hostname, hcache[e].hostname[h]) == 0)) {
406 				return (hcache[e].ipaddr);
407 			}
408 		}
409 	}
410 	entry = init_entry(0, hostname, NULL);
411 	if (entry->ipaddr == 0) {
412 		debug(2, "validating %s\n", hostname);
413 		entry = validate_entry(entry);
414 		n_changes++;
415 	}
416 	if ((entry != NULL) && entry->ipaddr) {
417 		debug(2, "returning %lx\n", entry->ipaddr);
418 		return (entry->ipaddr);
419 	}
420 
421 	return (INADDR_NONE);
422 }
423 
424 
425 char *
hcache_lookup_ipaddr(unsigned long ipaddr)426 hcache_lookup_ipaddr(unsigned long ipaddr)
427 {
428 	cache_entry *entry;
429 	int e;
430 
431 	for (e = 0; e < n_entry; e++) {
432 		if (hcache[e].ipaddr == ipaddr) {
433 			return (hcache[e].hostname[0]);
434 		}
435 	}
436 	entry = init_entry(ipaddr, 0, NULL);
437 	debug(1, "validating %lx\n", ipaddr);
438 	validate_entry(entry);
439 	n_changes++;
440 	return (entry ? entry->hostname[0] : NULL);
441 }
442 
443 
444 STATIC cache_entry *
find_entry(unsigned long ipaddr)445 find_entry(unsigned long ipaddr)
446 {
447 	int e;
448 
449 	for (e = 0; e < n_entry; e++) {
450 		if (hcache[e].ipaddr == ipaddr) {
451 			return (&hcache[e]);
452 		}
453 	}
454 	return (NULL);
455 }
456 
457 
458 STATIC void
free_entry(cache_entry * entry)459 free_entry(cache_entry *entry)
460 {
461 	int h;
462 
463 	for (h = 0; h < 5; h++) {
464 		if (entry->hostname[h] != NULL) {
465 			free(entry->hostname[h]);
466 		}
467 	}
468 	memset(entry, 0, sizeof(*entry));
469 }
470 
471 
472 STATIC void
add_hostname(cache_entry * entry,const char * hostname)473 add_hostname(cache_entry *entry, const char *hostname)
474 {
475 	int h;
476 
477 	for (h = 0; h < 5; h++) {
478 		if (entry->hostname[h] == NULL) {
479 			break;
480 		}
481 		if (strcmp(entry->hostname[h], hostname) == 0) {
482 			return;
483 		}
484 	}
485 	if (h < 5) {
486 		entry->hostname[h] = strdup(hostname);
487 	}
488 }
489 
490 
491 /*
492  * main(int argc, char *argv[])
493  * {
494  *  hcache_open( argv[1], 0);
495  *  hcache_write(NULL);
496  *
497  *  hcache_invalidate();
498  * printf( "invalidate\n");
499  *  hcache_write(NULL);
500  *  hcache_validate();
501  * printf( "validate\n");
502  *  hcache_write( "/tmp/qhcache.out");
503  * }
504  *
505  */
506