1
/***************************************************************************
3
* Copyright (c) 2001 BalaBit IT Ltd.
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
* Inspired by nsyslog, originally written by Darren Reed.
21
* $Id: nscache.c,v 1.4 2001/05/14 09:07:25 bazsi Exp $
23
***************************************************************************/
26
* Copyright (c) 2001 Ga�l Roualland <gael.roualland@iname.com>
27
* All rights reserved.
29
* Redistribution and use in source and binary forms, with or without
30
* modification, are permitted provided that the following conditions
32
* 1. Redistributions of source code must retain the above copyright
33
* notice, this list of conditions and the following disclaimer.
34
* 2. Redistributions in binary form must reproduce the above copyright
35
* notice, this list of conditions and the following disclaimer in the
36
* documentation and/or other materials provided with the distribution.
37
* 3. Neither the name of the author nor the names of its contributors
38
* may be used to endorse or promote products derived from this software
39
* without specific prior written permission.
41
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND
42
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55
#include <netinet/in.h>
56
#include <arpa/inet.h>
61
#include <sys/socket.h>
64
/* Simple NS cache, using a flat hash table (NOT thread safe).
65
* This is designed to be used with a fixed set of ip adresses.
66
* Inspired from similar caches in ippl, iplog...
68
* The size of the cache must be a primary number for good
69
* performance and somewhat larger than the maximum number
70
* of estimated entries. If more entries than available in the
71
* cache are used at the same time it will perform very poorly.
85
struct nsentry * entries;
91
struct nscache *nscache_new(int size, int expire, int expire_failed)
93
struct nscache * cache;
96
if ((cache = malloc(sizeof(struct nscache))) == NULL)
99
/* check args for reasonnable values
100
0 for expire_failed means not to cache failures */
101
cache->size = (size < 10) ? 10 : size;
102
cache->expire = (expire < 10) ? 10 : expire;
103
cache->expire_failed = (expire_failed < 0) ? 0 : expire_failed;
105
if ((cache->entries =
106
malloc(cache->size * sizeof(struct nsentry))) == NULL) {
111
for (i = 0; i < cache->size; i++) {
112
cache->entries[i].addr.s_addr = 0;
113
cache->entries[i].name = NULL;
114
cache->entries[i].expire = 0;
115
cache->entries[i].used = 0;
119
fprintf(stderr, "initialising nscache: %d entries, %d/%d expiration.\n",
120
cache->size, cache->expire, cache->expire_failed);
121
cache->hits = cache->misses = 0;
127
void nscache_free(struct nscache * cache)
134
if (cache->entries == NULL) {
140
fprintf(stderr, "destroying nscache: %d entries, %d hits, %d misses.\n",
141
cache->size, cache->hits, cache->misses);
144
for (i = 0; i < cache->size; i++) {
145
if (cache->entries[i].name != NULL)
146
free(cache->entries[i].name);
149
free(cache->entries);
153
static char *nsresolve(struct in_addr addr)
155
struct hostent *host;
157
if ((host = gethostbyaddr((char *) &addr,
158
sizeof(struct in_addr),
160
host->h_name != NULL)
165
char *nscache_lookup(struct nscache * cache, struct in_addr in)
171
time_t now = time(NULL);
172
time_t oldest = now + 1;
175
if (cache == NULL || cache->entries == NULL) /* be safe.. */
176
return nsresolve(in);
178
k = h = ntohl(in.s_addr) % (cache->size - 1) + 1;
182
if (cache->entries[k].expire > 0 &&
183
cache->entries[k].addr.s_addr == in.s_addr) { /* found it! */
184
if (now > cache->entries[k].expire) {
185
/* entry has expired, update it */
187
fprintf(stderr, "%s... updating entry %d (%d > %d).\n",
188
inet_ntoa(in), k, (int) now,
189
(int) cache->entries[k].expire);
195
fprintf(stderr, "%s... found on %d: %s (%d iters, %d/%d)\n",
196
inet_ntoa(in), k, cache->entries[k].name,
197
iter, (int) now, (int) cache->entries[k].expire);
200
cache->entries[k].used = now;
201
return cache->entries[k].name;
203
if (new < 0) { /* no empty entries found yet */
204
if (cache->entries[k].expire < now) {
205
new = k; /* use this to save new entry if not found */
206
if (cache->entries[k].expire < 1)
207
break; /* never used */
209
if (cache->entries[k].used < oldest) {
211
oldest = cache->entries[k].used;
214
k = (h + k) % cache->size;
220
if (new < 0) { /* no place to save ? use oldest. */
222
old = 0; /* shouldn't happen.. */
224
fprintf(stderr, "%s... old entry %d will be replaced.\n",
225
inet_ntoa(cache->entries[old].addr), old);
230
fprintf(stderr, "%s... adding as %d (%d iters).\n",
231
inet_ntoa(in), new, iter);
234
if (cache->entries[new].name != NULL)
235
free (cache->entries[new].name);
236
if ((host = nsresolve(in)) != NULL) {
237
cache->entries[new].name = strdup(host);
238
cache->entries[new].expire =
241
cache->entries[new].name = NULL;
242
cache->entries[new].expire =
243
now + cache->expire_failed;
245
cache->entries[new].addr.s_addr = in.s_addr;
246
return cache->entries[new].name;
250
void nscache_dump(struct nscache * cache)
254
fprintf(stderr, "nscache contents:\n");
255
for (i = 0; i < cache->size; i++) {
256
if (cache->entries[i].used > 0)
257
fprintf(stderr, " %5d %-16s %09d/%09d %s\n",
258
i, inet_ntoa(cache->entries[i].addr),
259
(int) cache->entries[i].used,
260
(int) cache->entries[i].expire,
261
cache->entries[i].name);