1 /*
2 * libmavis_limit.c
3 *
4 * (C)1998-2011 by Marc Huber <Marc.Huber@web.de>
5 * All rights reserved.
6 *
7 * $Id: libmavis_limit.c,v 1.25 2015/11/01 09:20:27 marc Exp $
8 *
9 */
10
11 #define __MAVIS_limit
12 #include <sys/types.h>
13 #include <netinet/in.h>
14 #include <arpa/inet.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <time.h>
18 #include <stdio.h>
19 #include <sys/types.h>
20 #include <dlfcn.h>
21
22 #include "misc/sysconf.h"
23
24 #include "log.h"
25 #include "debug.h"
26 #include "misc/memops.h"
27 #include "misc/net.h"
28 #include "misc/rb.h"
29
30 static const char rcsid[] __attribute__ ((used)) = "$Id: libmavis_limit.c,v 1.25 2015/11/01 09:20:27 marc Exp $";
31
32 #define MAVIS_CTX_PRIVATE \
33 int initialized; \
34 time_t lastpurge; \
35 time_t ip_timeout; \
36 u_int ip_blacklist_count; \
37 time_t ip_blacklist_time; \
38 time_t purge_outdated; \
39 struct cache *cache_blacklist;
40
41 #include "mavis.h"
42
43 struct item {
44 time_t expire;
45 u_int count;
46 struct in6_addr addr;
47 char user[1];
48 };
49
50 struct cache {
51 time_t maxage;
52 rb_tree_t *items;
53 };
54
free_payload(void * payload)55 static void free_payload(void *payload)
56 {
57 free(payload);
58 }
59
cache_new(int (* compar)(const void *,const void *),void (* freenode)(void *),time_t maxage)60 static struct cache *cache_new(int (*compar) (const void *, const void *), void (*freenode) (void *), time_t maxage)
61 {
62 struct cache *cache;
63
64 cache = Xcalloc(1, sizeof(struct cache));
65 cache->items = RB_tree_new(compar, freenode);
66 cache->maxage = maxage;
67 return cache;
68 }
69
cache_add_addr(struct cache * cache,char * user,char * addr)70 static void cache_add_addr(struct cache *cache, char *user, char *addr)
71 {
72 struct item item, *newitem = NULL;
73 rb_node_t *t;
74
75 if (!user)
76 return;
77
78 if (v6_ptoh(&item.addr, NULL, addr))
79 return;
80
81 t = RB_search(cache->items, &item);
82
83 if (!t || strcmp(RB_payload(t, struct item *)->user, user)) {
84 if (t)
85 RB_delete(cache->items, t);
86 newitem = Xcalloc(1, sizeof(struct item) + strlen(user));
87 strcpy(newitem->user, user);
88 newitem->count = 1;
89 newitem->addr = item.addr;
90 newitem->expire = io_now.tv_sec + cache->maxage;
91 RB_insert(cache->items, newitem);
92 } else {
93 struct item *ti = RB_payload(t, struct item *);
94 if (io_now.tv_sec > ti->expire)
95 ti->count = 0;
96 ti->expire = io_now.tv_sec + cache->maxage;
97 ti->count++;
98 }
99 }
100
cache_find_addr(struct cache * cache,char * addr)101 static struct item *cache_find_addr(struct cache *cache, char *addr)
102 {
103 struct item item;
104 rb_node_t *t = NULL;
105
106 DebugIn(DEBUG_PROC);
107
108 if (!v6_ptoh(&item.addr, NULL, addr))
109 t = RB_search(cache->items, &item);
110 DebugOut(DEBUG_PROC);
111 return t ? RB_payload(t, struct item *) : NULL;
112 }
113
garbage_collection_one(struct cache * cache)114 static void garbage_collection_one(struct cache *cache)
115 {
116 rb_node_t *t, *u;
117
118 if (cache && cache->items)
119 for (t = RB_first(cache->items); t; t = u)
120 if (u = RB_next(t), RB_payload(t, struct item *)->expire < io_now.tv_sec)
121 RB_delete(cache->items, t);
122 }
123
garbage_collection(mavis_ctx * mcx)124 static void garbage_collection(mavis_ctx * mcx)
125 {
126 DebugIn(DEBUG_PROC);
127
128 garbage_collection_one(mcx->cache_blacklist);
129
130 DebugOut(DEBUG_PROC);
131 }
132
133 #define HAVE_mavis_drop_in
mavis_drop_in(mavis_ctx * mcx)134 static void mavis_drop_in(mavis_ctx * mcx)
135 {
136 RB_tree_delete(mcx->cache_blacklist->items);
137 Xfree(&mcx->cache_blacklist);
138 }
139
140 /*
141 purge period =...blacklist time =...blacklist count =...
142 */
143 #define HAVE_mavis_parse_in
mavis_parse_in(mavis_ctx * mcx,struct sym * sym)144 static int mavis_parse_in(mavis_ctx * mcx, struct sym *sym)
145 {
146 while (1) {
147 switch (sym->code) {
148 case S_script:
149 mavis_script_parse(mcx, sym);
150 continue;
151 case S_purge:
152 sym_get(sym);
153 parse(sym, S_period);
154 parse(sym, S_equal);
155 mcx->purge_outdated = (time_t) parse_int(sym);
156 continue;
157 case S_blacklist:
158 sym_get(sym);
159 switch (sym->code) {
160 case S_time:
161 sym_get(sym);
162 parse(sym, S_equal);
163 mcx->ip_blacklist_time = (time_t) parse_int(sym);;
164 break;
165 case S_count:
166 sym_get(sym);
167 parse(sym, S_equal);
168 mcx->ip_blacklist_count = (u_int) parse_int(sym);;
169 break;
170 default:
171 parse_error_expect(sym, S_time, S_count, S_unknown);
172 }
173 continue;
174 case S_eof:
175 case S_closebra:
176 return MAVIS_CONF_OK;
177 default:
178 parse_error_expect(sym, S_script, S_purge, S_expire, S_closebra, S_unknown);
179 }
180 }
181 }
182
183
compare_addr(const void * a,const void * b)184 static int compare_addr(const void *a, const void *b)
185 {
186 return v6_cmp(&((struct item *) a)->addr, &((struct item *) b)->addr);
187 }
188
189 #define HAVE_mavis_init_in
mavis_init_in(mavis_ctx * mcx)190 static int mavis_init_in(mavis_ctx * mcx)
191 {
192 if (!mcx->initialized) {
193 mcx->initialized++;
194 mcx->cache_blacklist = cache_new(compare_addr, free_payload, mcx->ip_blacklist_time);
195 mcx->lastpurge = io_now.tv_sec;
196 }
197 return MAVIS_INIT_OK;
198 }
199
200 #define HAVE_mavis_send_in
mavis_send_in(mavis_ctx * mcx,av_ctx ** ac)201 static int mavis_send_in(mavis_ctx * mcx, av_ctx ** ac)
202 {
203 char *t, *addr;
204 t = av_get(*ac, AV_A_TYPE);
205 if (!t)
206 return MAVIS_FINAL;
207 if (io_now.tv_sec > mcx->lastpurge + mcx->purge_outdated) {
208 garbage_collection(mcx);
209 mcx->lastpurge = io_now.tv_sec;
210 }
211
212 addr = av_get(*ac, AV_A_IPADDR);
213 if (addr) {
214 struct item *item;
215 item = cache_find_addr(mcx->cache_blacklist, addr);
216 if (mcx->ip_blacklist_count && item && item->count >= mcx->ip_blacklist_count && item->expire > io_now.tv_sec) {
217 av_set(*ac, AV_A_RESULT, AV_V_RESULT_FAIL);
218 av_setf(*ac, AV_A_COMMENT, "client ip blacklisted for %ld seconds", (long) item->expire - io_now.tv_sec);
219 return MAVIS_FINAL;
220 }
221
222 }
223
224 return MAVIS_DOWN;
225 }
226
227 #define HAVE_mavis_recv_out
mavis_recv_out(mavis_ctx * mcx,av_ctx ** ac)228 static int mavis_recv_out(mavis_ctx * mcx, av_ctx ** ac)
229 {
230 char *t = av_get(*ac, AV_A_TYPE);
231 char *u = av_get(*ac, AV_A_USER);
232 char *i = av_get(*ac, AV_A_IPADDR);
233 char *r = av_get(*ac, AV_A_RESULT);
234 if (!r)
235 r = AV_V_RESULT_FAIL;
236 if (t && i && u) {
237 if (!strcmp(r, AV_V_RESULT_FAIL)
238 && (!strcmp(t, AV_V_TYPE_TACPLUS)
239 || !strcmp(t, AV_V_TYPE_FTP)
240 || !strcmp(t, AV_V_TYPE_WWW)
241 || !strcmp(t, AV_V_TYPE_POP3)))
242 cache_add_addr(mcx->cache_blacklist, "", i);
243 }
244
245 return MAVIS_FINAL;
246 }
247
248 #define HAVE_mavis_new
mavis_new(mavis_ctx * mcx)249 static void mavis_new(mavis_ctx * mcx)
250 {
251 mcx->ip_blacklist_time = 300;
252 mcx->purge_outdated = 300;
253 }
254
255 #define MAVIS_name "limit"
256 #include "mavis_glue.c"
257