1 /*
2 * Copyright (c) 2005, 2008, 2009, 2010, 2011, 2012, 2013
3 * Inferno Nettverk A/S, Norway. All rights reserved.
4 * Inferno Nettverk A/S, Norway. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. The above copyright notice, this list of conditions and the following
10 * disclaimer must appear in all copies of the software, derivative works
11 * or modified versions, and any portions thereof, aswell as in all
12 * supporting documentation.
13 * 2. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by
16 * Inferno Nettverk A/S, Norway.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Inferno Nettverk A/S requests users of this software to return to
32 *
33 * Software Distribution Coordinator or sdc@inet.no
34 * Inferno Nettverk A/S
35 * Oslo Research Park
36 * Gaustadall�en 21
37 * NO-0349 Oslo
38 * Norway
39 *
40 * any improvements or extensions that they make and grant Inferno Nettverk A/S
41 * the rights to redistribute these changes.
42 *
43 */
44
45 #include "common.h"
46
47 static const char rcsid[] =
48 "$Id: session.c,v 1.102.4.2 2014/09/01 07:44:01 karls Exp $";
49
50 static int
51 session_isavailable(shmem_object_t *ss, const clientinfo_t *cinfo,
52 const int lock, const int mapisopen,
53 char *emsg, size_t emsglen);
54 /*
55 * Returns true if there is at least one session free for use in the
56 * "ss" object, assuming the client is "cinfo".
57 * Returns false otherwise.
58 *
59 * Might also remove expired keystate entries from ss->keystate.
60
61 * If "mapisopen" is true, any keymap in shmem to be used for keystate
62 * is already open. Upon function return, the open/close state of the
63 * keystate map should remain as it was.
64 */
65
66 static int
67 throttlepermits(const sessionthrottle_t *throttle,
68 const struct timeval *starttime,
69 const size_t client_since_starttime,
70 const struct timeval *timenow);
71 /*
72 * Checks whether the throttle "throttle" permits one more client
73 * at time "timenow". "clientsnow" is the current number of clients
74 * related to the "throttle".
75 *
76 * Returns true if the throttle permits, false if not.
77 */
78
79 static char *
80 sessionlimitstring(const size_t inuse, const size_t max,
81 char *buf, const size_t buflen);
82 /*
83 * Return error string corresponding to throttle limit having been reached.
84 */
85
86 static char *
87 throttlelimitstring(const struct timeval *starttime,
88 const struct timeval *timenow,
89 const size_t newclients, const size_t currentclients,
90 char *buf, const size_t buflen);
91
92 /*
93 * Return error string corresponding to ratelimit having been reached.
94 */
95
96
97
98 int
session_use(shmem,cinfo,lock,emsg,emsglen)99 session_use(shmem, cinfo, lock, emsg, emsglen)
100 shmem_object_t *shmem;
101 const clientinfo_t *cinfo;
102 const int lock;
103 char *emsg;
104 const size_t emsglen;
105 {
106 const char *function = "session_use()";
107 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
108 shmem_object_t _shmem = *shmem;
109 #endif /* DEBUG */
110 session_t *ss = &shmem->object.ss;
111 struct timeval timenow = { 0, 0 };
112 ssize_t index = -1;
113 size_t mappedsize;
114
115 SASSERTX(shmem != NULL);
116
117 slog(LOG_DEBUG, "%s: shmid %lu, cinfo = %s, lock = %d",
118 function, shmem->mstate.shmid, clientinfo2string(cinfo, NULL, 0), lock);
119
120 #if HAVE_SOCKS_HOSTID
121 if (shmem->keystate.key == key_hostid)
122 if (shmem->keystate.keyinfo.hostindex > cinfo->hostidc) {
123 snprintf(emsg, emsglen,
124 "session.state.hostindex specifies using hostindex #%u, but "
125 "client connection provides %u hostindex%s. "
126 "Network/%s misconfiguration?",
127 (unsigned)shmem->keystate.keyinfo.hostindex,
128 (unsigned)cinfo->hostidc,
129 (unsigned)cinfo->hostidc == 1 ? "" : "es",
130 PRODUCT);
131
132 slog(LOG_WARNING, "%s: problem related to %s #%lu detected: %s",
133 function,
134 objecttype2string(shmem->mstate.parenttype),
135 (unsigned long)shmem->mstate.number,
136 emsg);
137
138 return 0;
139 }
140 #endif /* HAVE_SOCKS_HOSTID */
141
142 socks_lock(lock, (off_t)shmem->mstate.shmid, 1, 1, 1);
143
144 if (shmem->keystate.key == key_unset)
145 mappedsize = 0;
146 else {
147 int rc;
148
149 MUNPROTECT_SHMEMHEADER(shmem);
150 rc = keystate_openmap(shmem->mstate.shmid, &shmem->keystate, &mappedsize);
151 MPROTECT_SHMEMHEADER(shmem);
152
153 if (rc == -1) {
154 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
155
156 slog(LOG_DEBUG,
157 "%s: keystate_openmap() of shmid %ld/key %d, failed: %s",
158 function,
159 shmem->mstate.shmid,
160 (int)shmem->keystate.key,
161 strerror(errno));
162
163 return 0;
164 }
165 else
166 slog(LOG_DEBUG,
167 "%s: opened mmap(2) of size %lu at address %p for shmid %lu",
168 function,
169 (unsigned long)mappedsize,
170 shmem->keystate.keyv,
171 (unsigned long)shmem->mstate.shmid);
172 }
173
174 if (mappedsize != 0)
175 SASSERTX(shmem->keystate.keyv != NULL);
176
177 if (!session_isavailable(shmem, cinfo, -1, 1, emsg, emsglen)) {
178 SASSERTX(mappedsize == shmem->keystate.keyc
179 * sizeof(*shmem->keystate.keyv));
180
181 MUNPROTECT_SHMEMHEADER(shmem);
182
183 keystate_closemap(shmem->mstate.shmid, &shmem->keystate, mappedsize, -1);
184
185 MPROTECT_SHMEMHEADER(shmem);
186
187 /*
188 * *must* be after keystate_closemap() since keystate_closemap also
189 * NULL's the map and this will affect other processes who open it.
190 */
191 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
192
193 slog(LOG_DEBUG, "%s: no free session slots available: %s",
194 function, emsglen == 0 ? "" : emsg);
195
196 return 0;
197 }
198
199 if (shmem_use(shmem, cinfo, -1, 1) == -1) {
200 MUNPROTECT_SHMEMHEADER(shmem);
201
202 keystate_closemap(shmem->mstate.shmid,
203 &shmem->keystate,
204 mappedsize,
205 -1);
206
207 MUNPROTECT_SHMEMHEADER(shmem);
208
209 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
210 return 0;
211 }
212
213 /* may have changed if shmem_use() had to expand the array. */
214 mappedsize = shmem->keystate.keyc * sizeof(*shmem->keystate.keyv);
215
216 if (ss->throttle_isset) {
217 struct timeval tdiff;
218
219 if (!timerisset(&timenow))
220 gettimeofday_monotonic(&timenow);
221
222 timersub(&timenow, &ss->throttle.starttime, &tdiff);
223 if (tdiff.tv_sec >= ss->throttle.limit.seconds)
224 RESET_THROTTLE(&ss->throttle, timenow);
225
226 ++ss->throttle.newclients;
227 }
228
229 if (ss->throttle_perstate_isset || ss->max_perstate_isset) {
230 /*
231 * shmem_use() should make sure an entry is allocated for this
232 * address by now.
233 */
234
235 SASSERTX(mappedsize != 0);
236
237 index = keystate_index(shmem, cinfo, 0);
238 SASSERTX(index >= 0);
239 SASSERTX((size_t)index < shmem->keystate.keyc);
240 }
241
242 if (ss->throttle_perstate_isset) {
243 if (!timerisset(&timenow))
244 gettimeofday_monotonic(&timenow);
245
246 if (keystate_hasexpired(shmem, (size_t)index, &timenow))
247 RESET_THROTTLE(&shmem->keystate.keyv[index].info.ss, timenow);
248
249 ++shmem->keystate.keyv[index].info.ss.newclients;
250 }
251
252 MUNPROTECT_SHMEMHEADER(shmem);
253
254 keystate_closemap(shmem->mstate.shmid, &shmem->keystate, mappedsize, index);
255
256 MUNPROTECT_SHMEMHEADER(shmem);
257
258 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
259 return 1;
260 }
261
262 void
session_unuse(ss,cinfo,lock)263 session_unuse(ss, cinfo, lock)
264 shmem_object_t *ss;
265 const clientinfo_t *cinfo;
266 const int lock;
267 {
268 const char *function = "session_unuse()";
269 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
270 shmem_object_t _shmem = *ss;
271 #endif /* DEBUG */
272
273 SASSERTX(ss != NULL);
274
275 slog(LOG_DEBUG, "%s: shmid = %ld, key = %d, cinfo = %s, lock = %d",
276 function,
277 ss->mstate.shmid,
278 (int)ss->keystate.key,
279 clientinfo2string(cinfo, NULL, 0),
280 lock);
281
282 socks_lock(lock, (off_t)ss->mstate.shmid, 1, 1, 1);
283
284 shmem_unuse(ss, cinfo, -1);
285
286 if (sockscf.option.debug)
287 slog(LOG_DEBUG, "%s: sessions left after unuse on shmid %lu, key %d: %s",
288 function,
289 (unsigned long)ss->mstate.shmid,
290 (int)ss->keystate.key,
291 session_isavailable(ss, cinfo, -1, 0, NULL, 0) ?
292 "yes" : "no");
293
294 socks_unlock(lock, (off_t)ss->mstate.shmid, 1);
295 }
296
297 static int
session_isavailable(shmem,cinfo,lock,mapisopen,emsg,emsglen)298 session_isavailable(shmem, cinfo, lock, mapisopen, emsg, emsglen)
299 shmem_object_t *shmem;
300 const clientinfo_t *cinfo;
301 const int lock;
302 const int mapisopen;
303 char *emsg;
304 size_t emsglen;
305 {
306 const char *function = "session_isavailable()";
307 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
308 shmem_object_t _shmem = *shmem;
309 #endif /* DEBUG */
310 const session_t *ss = &shmem->object.ss;
311 const int doclosemap = (mapisopen ? 0 : 1);
312 struct timeval timenow = { 0, 0 };
313 size_t mappedsize, inuse;
314 ssize_t i;
315 char buf[256], backupemsg[256];
316
317 if (emsg == NULL || emsglen == 0) {
318 emsg = backupemsg;
319 emsglen = sizeof(backupemsg);
320 }
321
322 SASSERTX(ss->max_isset
323 || ss->throttle_isset
324 || shmem->keystate.key != key_unset);
325
326 socks_lock(lock, (off_t)shmem->mstate.shmid, 1, 1, 1);
327
328 /*
329 * Check not-keystate limits first.
330 */
331
332 if (ss->throttle_isset) {
333 if (!timerisset(&timenow))
334 gettimeofday_monotonic(&timenow);
335
336 if (!throttlepermits(&ss->throttle.limit,
337 &ss->throttle.starttime,
338 ss->throttle.newclients,
339 &timenow)) {
340 throttlelimitstring(&ss->throttle.starttime,
341 &timenow,
342 ss->throttle.newclients,
343 shmem->mstate.clients,
344 emsg,
345 emsglen);
346
347 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
348
349 slog(LOG_DEBUG, "%s: throttlelimit reached for shmid %lu: %s",
350 function, (unsigned long)shmem->mstate.shmid, emsg);
351
352 return 0;
353 }
354 }
355
356 if (ss->max_isset) {
357 const size_t left = (unsigned long)(ss->max - shmem->mstate.clients);
358
359 SASSERTX(shmem->mstate.clients <= ss->max);
360
361 slog(LOG_DEBUG, "%s: sessions in use for shmid %lu: %lu, max: %lu",
362 function,
363 (unsigned long)shmem->mstate.shmid,
364 (unsigned long)shmem->mstate.clients,
365 (unsigned long)ss->max);
366
367 if (left <= 0) {
368 sessionlimitstring(shmem->mstate.clients, ss->max, emsg, emsglen);
369
370 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
371 slog(LOG_DEBUG, "%s: sessionlimit reached for shmid %lu: %s",
372 function, (unsigned long)shmem->mstate.shmid, emsg);
373
374 return 0;
375 }
376 }
377
378 /*
379 * not-keystate limits are ok. Now check keystate-based limits, if any.
380 */
381
382 if (shmem->keystate.key == key_unset
383 || shmem->keystate.keyc == 0 /* limit can't have been reached. */) {
384 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
385 return 1;
386 }
387
388 if (mapisopen)
389 SASSERTX(shmem->keystate.keyv != NULL);
390 else {
391 int rc;
392
393 MUNPROTECT_SHMEMHEADER(shmem);
394
395 rc = keystate_openmap(shmem->mstate.shmid, &shmem->keystate, &mappedsize);
396
397 MPROTECT_SHMEMHEADER(shmem);
398
399 if (rc == -1) {
400 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
401
402 slog(LOG_DEBUG,
403 "%s: keystate_openmap() of shmid %lu/key %d failed: %s",
404 function,
405 shmem->mstate.shmid,
406 (int)shmem->keystate.key,
407 strerror(errno));
408
409 return 0;
410 }
411 }
412
413 SASSERTX(shmem->keystate.keyv != NULL);
414
415 if ((i = keystate_index(shmem, cinfo, 0)) != -1) {
416 SASSERTX(i >= 0);
417 SASSERTX((size_t)i < shmem->keystate.keyc);
418
419 switch (shmem->keystate.key) {
420 case key_from:
421 inuse = shmem->keystate.keyv[i].data.from.addrc;
422 break;
423
424 #if HAVE_SOCKS_HOSTID
425 case key_hostid:
426 inuse = shmem->keystate.keyv[i].data.hostid.addrc;
427 break;
428 #endif /* HAVE_SOCKS_HOSTID */
429
430 default:
431 SERRX(shmem->keystate.key);
432 }
433
434 if (ss->max_perstate_isset) {
435 slog(LOG_DEBUG,
436 "%s: per-key %s sessions in use for shmid %lu: %ld, max: %lu",
437 function,
438 statekey2string(shmem->keystate.key),
439 (unsigned long)shmem->mstate.shmid,
440 (long)inuse,
441 (unsigned long)ss->max_perstate);
442
443 SASSERTX(inuse <= ss->max_perstate);
444
445 if (ss->max_perstate - inuse <= 0) {
446 snprintf(emsg, emsglen, "per-key %s",
447 sessionlimitstring((size_t)inuse,
448 ss->max_perstate,
449 buf,
450 sizeof(buf)));
451 slog(LOG_DEBUG,
452 "%s: per-key sessionlimit reached for shmid %lu: %s",
453 function, (unsigned long)shmem->mstate.shmid, emsg);
454
455 if (doclosemap) {
456 MUNPROTECT_SHMEMHEADER(shmem);
457
458 keystate_closemap(shmem->mstate.shmid,
459 &shmem->keystate,
460 mappedsize,
461 -1);
462
463 MUNPROTECT_SHMEMHEADER(shmem);
464 }
465
466 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
467 return 0;
468 }
469 }
470
471 if (ss->throttle_perstate_isset) {
472 SASSERTX(ss->throttle_perstate.limit.clients != 0);
473 SASSERTX(ss->throttle_perstate.limit.seconds != 0);
474
475 if (!timerisset(&timenow))
476 gettimeofday_monotonic(&timenow);
477
478 if (!throttlepermits(&ss->throttle_perstate.limit,
479 &shmem->keystate.keyv[i].info.ss.starttime,
480 shmem->keystate.keyv[i].info.ss.newclients,
481 &timenow)) {
482 snprintf(emsg, emsglen, "per-key %s",
483 throttlelimitstring(
484 &shmem->keystate.keyv[i].info.ss.starttime,
485 &timenow,
486 shmem->keystate.keyv[i].info.ss.newclients,
487 inuse,
488 buf,
489 sizeof(buf)));
490
491 slog(LOG_DEBUG,
492 "%s: per-key throttlelimit reached for shmid %lu: %s",
493 function, (unsigned long)shmem->mstate.shmid, emsg);
494
495 if (doclosemap) {
496 MUNPROTECT_SHMEMHEADER(shmem);
497
498 keystate_closemap(shmem->mstate.shmid,
499 &shmem->keystate,
500 mappedsize,
501 -1);
502
503 MPROTECT_SHMEMHEADER(shmem);
504 }
505
506 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
507 return 0;
508 }
509 }
510 }
511
512 if (doclosemap) {
513 MUNPROTECT_SHMEMHEADER(shmem);
514
515 keystate_closemap(shmem->mstate.shmid, &shmem->keystate, mappedsize, -1);
516
517 MPROTECT_SHMEMHEADER(shmem);
518 }
519
520 socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
521 return 1;
522 }
523
524 static int
throttlepermits(throttle,starttime,newclients,timenow)525 throttlepermits(throttle, starttime, newclients, timenow)
526 const sessionthrottle_t *throttle;
527 const struct timeval *starttime;
528 const size_t newclients;
529 const struct timeval *timenow;
530 {
531 const char *function = "throttlepermits()";
532 struct timeval tdiff;
533
534 slog(LOG_DEBUG,
535 "%s: throttle: %lu/%ld, starttime: %ld.%06ld, newclients: %lu, "
536 "timenow: %ld.%06ld",
537 function,
538 (unsigned long)throttle->clients,
539 (long)throttle->seconds,
540 (long)starttime->tv_sec,
541 (long)starttime->tv_usec,
542 (unsigned long)newclients,
543 (long)timenow->tv_sec,
544 (long)timenow->tv_usec);
545
546 if (newclients < throttle->clients)
547 return 1;
548
549 timersub(timenow, starttime, &tdiff);
550
551 if (tdiff.tv_sec >= throttle->seconds)
552 return 1;
553
554 return 0;
555 }
556
557 static char *
sessionlimitstring(inuse,max,buf,buflen)558 sessionlimitstring(inuse, max, buf, buflen)
559 const size_t inuse;
560 const size_t max;
561 char *buf;
562 size_t buflen;
563 {
564
565 SASSERTX(inuse <= max);
566
567 snprintf(buf, buflen, "session limit reached (%ld/%ld slot%s in use)",
568 (long)inuse, (long)max, max == 1 ? "" : "s");
569
570 return buf;
571 }
572
573 static char *
throttlelimitstring(starttime,timenow,newclients,currentclients,buf,buflen)574 throttlelimitstring(starttime, timenow, newclients, currentclients, buf, buflen)
575 const struct timeval *starttime;
576 const struct timeval *timenow;
577 const size_t newclients;
578 const size_t currentclients;
579 char *buf;
580 const size_t buflen;
581 {
582 struct timeval tdiff;
583
584 timersub(timenow, starttime, &tdiff);
585 snprintf(buf, buflen,
586 "session rate limit reached (already accepted %lu new client%s "
587 "during the last %lds. Current client count: %lu)",
588 (unsigned long)newclients,
589 (unsigned long)newclients == 1 ? "" : "s",
590 (long)timeval2seconds(&tdiff),
591 (unsigned long)currentclients);
592
593 return buf;
594 }
595