1 /*
2  * Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010, 2011,
3  *               2012, 2013, 2014, 2016
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: shmem.c,v 1.238.4.6.2.2 2017/01/31 08:17:38 karls Exp $";
49 
50 
51 #define FIRST_SHMEMID  (0)
52 
53 typedef enum { keytype_ipv4 = 1, keytype_ipv6 } keystate_data_type;
54 
55 typedef struct {
56    keystate_data_type type;
57 
58    union {
59       struct in_addr  ipv4;
60       struct in6_addr ipv6;
61    } data;
62 } keystate_data_t;
63 
64 static unsigned long
65 mem2shmem(struct config *config, const unsigned long firstid);
66 /*
67  * Deallocates from all rules starting with "firstrule" ordinary memory for
68  * objects that should be in shared memory and allocates shared memory
69  * for it instead.
70  * "firstid" is the id to use for the first allocation.  The id of
71  * subsequent allocations is incremented by one.
72  *
73  * Returns the id of the last id used.
74  */
75 
76 static void
77 keystate_removeindex(keystate_t *keystate, const size_t index);
78 /*
79  * Removes the keystate entry with index "index".
80  */
81 
82 static keystate_data_t *
83 keystate_data(const keystate_t *keystate, const size_t index,
84               keystate_data_t *key);
85 /*
86  * Returns the keystate data at index "index" corresponding to the key
87  * set in keystate.
88  */
89 
90 static const char *
91 keydata2string(const keystate_data_t *keydata, char *buf, size_t buflen);
92 /*
93  * Stores a string representation of keydata "keydata" in "buf" and
94  * returns a pointer to buf.
95  *
96  * If "buf" is NULL, the representation is written to a local static buffer
97  * and a pointer to it is returned.
98  */
99 
100 static size_t
101 keystate_clientcount(const keystate_t *keystate, const size_t index);
102 /*
103  * Returns the current clientcount for the keystate entry with index "index".
104  */
105 
106 static ssize_t
107 keystate_timer(const shmem_object_t *shmem);
108 /*
109  * is there a relevant timer we can use for calculating how often to
110  * scan for old keystate entries to expire and remove in "shmem"?
111  *
112  * Returns the timer as number of seconds if so, or -1 if not.
113  */
114 
115 static int
116 should_do_expirescan(const shmem_object_t *shmem);
117 /*
118  * Should we do a scan for old keyentries in shmem at this time?
119  * Returns true if so, false otherwise.
120  */
121 
122 void
shmem_setup(void)123 shmem_setup(void)
124 {
125    const char *function = "shmem_setup()";
126    char *p;
127 
128    /*
129     * Main shmem-stuff.  sockd.conf and shmemfd used for locking access
130     * to shmem-stuff.
131     */
132 
133    SASSERTX(sockscf.shmemfd == -1);
134    SASSERTX(pidismother(sockscf.state.pid) == 1);
135 
136    STRCPY_ASSERTSIZE(sockscf.shmem_fnamebase, SOCKD_SHMEMFILE);
137 
138    /*
139     * First check that this works ok.  If e.g., user has changed some
140     * config-files and made strings too long, it might fail.
141     */
142    if ((sockscf.shmemfd = socks_mklock(sockscf.shmem_fnamebase,
143                                        sockscf.shmem_fnamebase,
144                                        sizeof(sockscf.shmem_fnamebase))) == -1)
145       serr("%s: socks_mklock() failed to create shmemfile using base %s",
146            function, sockscf.shmem_fnamebase);
147 
148    if ((p = sockd_getshmemname((unsigned long)FIRST_SHMEMID, key_from)) == NULL)
149       serrx("%s: failed to generate shmem filename based on fnamebase "
150             "\"%s\" and id %d",
151             function, sockscf.shmem_fnamebase, FIRST_SHMEMID);
152 
153    if (strlen(p) >= sizeof(sockscf.shmem_fnamebase))
154       serrx("%s: shmem filename is %ld bytes too long for this system.  Please "
155             "reduce the length of the SOCKD_SHMEMFILE define and recompile",
156             function,
157             (unsigned long)(strlen(p) + 1) - sizeof(sockscf.shmem_fnamebase));
158 
159    if ((sockscf.shmeminfo = sockd_mmap(NULL,
160                                        sizeof(*sockscf.shmeminfo),
161                                        PROT_READ | PROT_WRITE,
162                                        MAP_SHARED,
163                                        sockscf.shmemfd,
164                                        1)) == MAP_FAILED)
165       serr("%s: failed to mmap shmeminfo", function);
166 
167    /* can unlink this file; all children will inherit the fd. */
168    if (unlink(sockscf.shmem_fnamebase) != 0)
169       serr("%s: failed to unlink shmemfile %s",
170            function, sockscf.shmem_fnamebase);
171 
172    SASSERTX(sockscf.shmemconfigfd == -1);
173    if ((sockscf.shmemconfigfd
174    = socks_mklock(SOCKD_SHMEMFILE, NULL, 0)) == -1)
175       serr("%s: socks_mklock() failed to create shmemfile using base %s",
176            function, SOCKD_SHMEMFILE);
177 
178    /*
179     * Hostcache also uses shmem, but has it's own area.
180     */
181    hostcachesetup();
182 
183 #if HAVE_LDAP
184    /*
185     * And so does the LDAP module.
186     */
187    ldapcachesetup();
188 #endif /* HAVE_LDAP */
189 
190 }
191 
192 void
shmem_idupdate(config)193 shmem_idupdate(config)
194    struct config *config;
195 {
196    static size_t lastshmid = FIRST_SHMEMID + 1;
197 
198    lastshmid = mem2shmem(config, lastshmid);
199    ++lastshmid;
200 }
201 
202 void
sockd_shmdt(rule,objects)203 sockd_shmdt(rule, objects)
204    rule_t *rule;
205    const int objects;
206 {
207    const char *function = "sockd_shmdt()";
208 
209    if ((objects & SHMEM_BW) && rule->bw_shmid)
210       HANDLE_SHMDT(rule, bw, bw_shmid);
211 
212    if ((objects & SHMEM_MONITOR) && rule->mstats_shmid)
213       HANDLE_SHMDT(rule, mstats, mstats_shmid);
214 
215    if ((objects & SHMEM_SS) && rule->ss_shmid)
216       HANDLE_SHMDT(rule, ss, ss_shmid);
217 }
218 
219 int
sockd_shmat(rule,objects)220 sockd_shmat(rule, objects)
221    rule_t *rule;
222    int objects;
223 {
224    const char *function = "sockd_shmat()";
225    int haveerror = 0;
226 
227    if ((objects & SHMEM_BW) && rule->bw_shmid != 0) {
228       HANDLE_SHMAT(rule, bw, bw_shmid);
229 
230       if (rule->bw_shmid) {
231 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
232          shmem_object_t _shmem = *rule->bw;
233 #endif /* DEBUG */
234 
235          SASSERTX(rule->bw->type == SHMEM_BW);
236       }
237       else {
238          SASSERTX(rule->bw == NULL);
239 
240          haveerror = 1;
241          objects  &= ~SHMEM_BW;
242       }
243    }
244 #if DEBUG
245    else
246       slog(LOG_DEBUG, "%s: no bw_shmid we need to (re)attach to for rule #%lu",
247            function, (unsigned long)rule->number);
248 #endif /* DEBUG */
249 
250    if ((objects & SHMEM_MONITOR) && rule->mstats_shmid != 0) {
251       /*
252        * monitor-files are deleted and reset on every sighup, so can
253        * be deleted even if mother still exists.
254        */
255       HANDLE_SHMAT(rule, mstats, mstats_shmid);
256 
257       if (rule->mstats_shmid) {
258 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
259             shmem_object_t _shmem = *rule->mstats;
260 #endif /* DEBUG */
261 
262          SASSERTX(rule->mstats->type == SHMEM_MONITOR);
263       }
264       else {
265          SASSERTX(rule->mstats == NULL);
266 
267          haveerror = 1;
268          objects  &= ~SHMEM_MONITOR;
269       }
270    }
271 #if DEBUG
272    else
273       slog(LOG_DEBUG,
274            "%s: no mstats_shmid we need to (re)attach to for rule #%lu",
275            function, (unsigned long)rule->number);
276 #endif /* DEBUG */
277 
278    if ((objects & SHMEM_SS) && rule->ss_shmid != 0) {
279       HANDLE_SHMAT(rule, ss, ss_shmid);
280 
281       if (rule->ss_shmid) {
282 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
283          shmem_object_t _shmem = *rule->ss;
284 #endif /* DEBUG */
285 
286          SASSERTX(rule->ss->type == SHMEM_SS);
287       }
288       else {
289          SASSERTX(rule->ss == NULL);
290 
291          haveerror = 1;
292          objects  &= ~SHMEM_SS;
293       }
294    }
295 #if DEBUG
296    else
297       slog(LOG_DEBUG, "%s: no ss_shmid we need to (re)attach to for rule #%lu",
298             function, (unsigned long)rule->number);
299 #endif /* DEBUG */
300 
301    if (haveerror)
302       return -1;
303    else
304       return 0;
305 }
306 
307 int
shmem_userule(rule,cinfo,emsg,emsglen)308 shmem_userule(rule, cinfo, emsg, emsglen)
309    rule_t *rule;
310    const clientinfo_t *cinfo;
311    char *emsg;
312    const size_t emsglen;
313 {
314    const char *function = "shmem_userule()";
315    size_t attached_to;
316 
317    slog(LOG_DEBUG, "%s: cinfo: %s",
318         function, clientinfo2string(cinfo, NULL, 0));
319 
320    log_ruleinfo_shmid(rule, function, NULL);
321 
322    /*
323     * Do sessions first, since that's the one that can fail.
324     */
325 
326    attached_to = 0;
327 
328    if (rule->ss_shmid != 0 && rule->ss == NULL)
329       if (sockd_shmat(rule, SHMEM_SS) == 0)
330          attached_to |= SHMEM_SS;
331 
332    if (rule->ss_shmid != 0) {
333       if (!session_use(rule->ss,
334                        cinfo,
335                        sockscf.shmemfd,
336                        emsg,
337                        emsglen)) {
338          SASSERTX(rule->ss_shmid == (rule)->ss->mstate.shmid);
339 
340          sockd_shmdt(rule, attached_to);
341          return -1;
342       }
343    }
344 
345    if (rule->bw_shmid != 0 && rule->bw == NULL)
346       if (sockd_shmat(rule, SHMEM_BW) == 0)
347          attached_to |= SHMEM_BW;
348 
349    if (rule->bw_shmid != 0)
350       bw_use(rule->bw, cinfo, sockscf.shmemfd);
351 
352    if (rule->mstats_shmid != 0 && rule->mstats == NULL)
353       if (sockd_shmat(rule, SHMEM_MONITOR) == 0)
354          attached_to |= SHMEM_MONITOR;
355 
356    if (rule->mstats_shmid != 0)
357       monitor_use(rule->mstats, cinfo, sockscf.shmemfd);
358 
359    sockd_shmdt(rule, attached_to);
360 
361    return 0;
362 }
363 
364 
365 char *
sockd_getshmemname(id,key)366 sockd_getshmemname(id, key)
367    const unsigned long id;
368    const statekey_t key;
369 {
370 /*   const char *function = "sockd_getshmemname()"; */
371    static char name[PATH_MAX];
372 
373    SASSERTX(*sockscf.shmem_fnamebase != NUL);
374 
375    if (key == key_unset)
376       snprintf(name, sizeof(name), "%s.%lu", sockscf.shmem_fnamebase, id);
377    else
378       snprintf(name, sizeof(name), "%s.%lu.%d",
379                sockscf.shmem_fnamebase, id, (int)key);
380 
381    return name;
382 }
383 
384 void *
sockd_mmap(mapping,size,prot,flags,fd,docreate)385 sockd_mmap(mapping, size, prot, flags, fd, docreate)
386    void *mapping;
387    size_t size;
388    const int prot;
389    const int flags;
390    const int fd;
391    const int docreate;
392 {
393    const char *function = "sockd_mmap()";
394    const void *oldmapping = mapping;
395 
396    if (size == 0)
397       return MAP_FAILED;
398 
399    if (docreate) {
400       if (ftruncate(fd, (off_t)size) == -1) {
401          swarn("%s: ftruncate() to offset %lu failed",
402                function, (unsigned long)size);
403 
404          return MAP_FAILED;
405       }
406    }
407 
408    slog(LOG_DEBUG, "%s: mmap(2)'ing %lu bytes from fd %d, mapping = %p",
409         function, (unsigned long)size, fd, mapping);
410 
411    if ((mapping = mmap(mapping,
412                        size,
413                        prot,
414                        flags,
415                        fd,
416                        (off_t)0)) == MAP_FAILED)
417       swarn("%s: %smmap(2) of %lu bytes from fd %d failed",
418             function, oldmapping == NULL ? "" : "re-", (unsigned long)size, fd);
419 
420    return mapping;
421 }
422 
423 int
shmem_use(shmem,cinfo,lock,mapisopen)424 shmem_use(shmem, cinfo, lock, mapisopen)
425    shmem_object_t *shmem;
426    const clientinfo_t *cinfo;
427    const int lock;
428    const int mapisopen;
429 {
430    const char *function = "shmem_use()";
431    const int doclosemap = (mapisopen ? 0 : 1);
432 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
433    shmem_object_t _shmem = *shmem;
434 #endif /* DEBUG */
435    ssize_t statecount = -1;
436 
437    SASSERTX(shmem != NULL);
438    SASSERTX(shmem->mstate.number > 0);
439 
440    if (sockscf.option.debug >= DEBUG_VERBOSE)
441       slog(LOG_DEBUG,
442            "%s: pre-use: lock = %d, # of clients = %lu, rule = %lu, "
443            "shmem = %p, keystate = %p, mapisopen = %d, cinfo = %s, shmid %ld, "
444            "key %d",
445            function,
446            lock,
447            (unsigned long)shmem->mstate.clients,
448            (unsigned long)shmem->mstate.number,
449            shmem,
450            shmem->keystate.keyv,
451            mapisopen,
452            clientinfo2string(cinfo, NULL, 0),
453            shmem->mstate.shmid,
454            (int)shmem->keystate.key);
455 
456    switch (shmem->type) {
457       case SHMEM_BW:
458       case SHMEM_MONITOR:
459       case SHMEM_SS:
460          break;
461 
462       default:
463          SERRX(shmem->type);
464    }
465 
466    socks_lock(lock, (off_t)shmem->mstate.shmid, 1, 1, 1);
467 
468    MUNPROTECT_SHMEMHEADER(shmem);
469 
470    ++shmem->mstate.clients;
471 
472    MPROTECT_SHMEMHEADER(shmem);
473    if (shmem->keystate.key != key_unset) {
474       size_t sizemapped;
475       ssize_t i;
476       int rc;
477 
478       if (!mapisopen) {
479          MUNPROTECT_SHMEMHEADER(shmem);
480 
481          rc = keystate_openmap(shmem->mstate.shmid,
482                                &shmem->keystate,
483                                &sizemapped);
484 
485          MPROTECT_SHMEMHEADER(shmem);
486 
487          if (rc != 0) {
488             socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
489 
490             slog(LOG_DEBUG, "%s: keystate_openmap() of shmid %lu failed: %s",
491                  function, (unsigned long)shmem->mstate.shmid, strerror(errno));
492             return -1;
493          }
494       }
495 
496       /*
497        * increment the current count for address, or add an entry for the addr.
498        */
499       if ((i = keystate_index(shmem, cinfo, should_do_expirescan(shmem))) >= 0)
500          slog(LOG_DEBUG, "%s: entry exists at index #%lu",
501               function, (unsigned long)i);
502       else {
503          /*
504           * a bit more complex.  Need to expand the array and add the new
505           * entry; remap with a larger size first.
506           */
507          const char *fname = sockd_getshmemname(shmem->mstate.shmid,
508                                                 shmem->keystate.key);
509          void *newmap;
510          int fd;
511 
512          slog(LOG_DEBUG,
513               "%s: need to expand keyv array to %lu entries for cinfo %s",
514               function,
515               (unsigned long)shmem->keystate.keyc + 1,
516               clientinfo2string(cinfo, NULL, 0));
517 
518          if ((fd = open(fname, O_RDWR)) == -1) {
519             swarn("%s: could not open shmemfile %s", function, fname);
520 
521             if (doclosemap) {
522                MUNPROTECT_SHMEMHEADER(shmem);
523 
524                keystate_closemap(shmem->mstate.shmid,
525                                  &shmem->keystate,
526                                  sizemapped,
527                                  -1);
528 
529                MPROTECT_SHMEMHEADER(shmem);
530             }
531 
532             socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
533 
534             return -1;
535          }
536 
537          sizemapped
538          = (shmem->keystate.keyc + 1) * sizeof(*shmem->keystate.keyv);
539 
540          MUNPROTECT_SHMEMHEADER(shmem);
541 
542          newmap = sockd_mmap(shmem->keystate.keyv,
543                              sizemapped,
544                              PROT_READ | PROT_WRITE,
545                              MAP_SHARED,
546                              fd,
547                              1);
548 
549          MPROTECT_SHMEMHEADER(shmem);
550 
551          close(fd);
552 
553          if (newmap == MAP_FAILED) {
554             swarn("%s: failed to mmap(2) shmeminfo of size %lu from "
555                   "file %s on fd %d",
556                   function, (unsigned long)sizemapped, fname, fd);
557 
558             socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
559             return -1;
560          }
561 
562          shmem->keystate.keyv = newmap;
563 
564          i = shmem->keystate.keyc;
565 
566          MUNPROTECT_SHMEMHEADER(shmem);
567 
568          ++shmem->keystate.keyc;
569 
570          MPROTECT_SHMEMHEADER(shmem);
571       }
572 
573       SASSERTX((size_t)i < shmem->keystate.keyc);
574 
575       MUNPROTECT_SHMEMHEADER(shmem);
576       switch (shmem->keystate.key) {
577          case key_from: {
578             void *dst;
579 
580             switch (cinfo->from.ss_family) {
581                case AF_INET:
582                   dst = &shmem->keystate.keyv[i].data.from.addr.ipv4;
583                   break;
584 
585                case AF_INET6:
586                   dst = &shmem->keystate.keyv[i].data.from.addr.ipv6;
587                   break;
588 
589                default:
590                   SERRX(cinfo->from.ss_family);
591             }
592 
593             shmem->keystate.keyv[i].data.from.safamily  = cinfo->from.ss_family;
594             memcpy(dst,
595                    GET_SOCKADDRADDR(&cinfo->from),
596                    inaddrlen(cinfo->from.ss_family));
597             ++shmem->keystate.keyv[i].data.from.addrc;
598 
599             statecount = (ssize_t)shmem->keystate.keyv[i].data.from.addrc;
600 
601             slog(LOG_DEBUG, "%s: updated entry for address %s at index #%lu",
602                  function,
603                  sockaddr2string2(&cinfo->from, 0, NULL, 0),
604                  (unsigned long)i);
605 
606             break;
607          }
608 
609 #if HAVE_SOCKS_HOSTID
610          case key_hostid:
611             SASSERTX(shmem->keystate.keyinfo.hostindex <= cinfo->hostidc);
612 
613             shmem->keystate.keyv[i].data.hostid.ipv4
614             = cinfo->hostidv[shmem->keystate.keyinfo.hostindex - 1];
615 
616             ++shmem->keystate.keyv[i].data.hostid.addrc;
617 
618             statecount = (ssize_t)shmem->keystate.keyv[i].data.hostid.addrc;
619 
620             break;
621 #endif /* HAVE_SOCKS_HOSTID */
622 
623          default:
624             SERRX(shmem->keystate.key);
625       }
626 
627       if (doclosemap) {
628          MUNPROTECT_SHMEMHEADER(shmem);
629 
630          keystate_closemap(shmem->mstate.shmid,
631                            &shmem->keystate,
632                            sizemapped,
633                            i);
634 
635          MPROTECT_SHMEMHEADER(shmem);
636       }
637    }
638    MUNPROTECT_SHMEMHEADER(shmem);
639 
640    if (sockscf.option.debug >= DEBUG_VERBOSE)
641       slog(LOG_DEBUG,
642            "%s: post-use: lock = %d, clients = %lu (%ld), shmem = %p, "
643            "shmid %ld, cinfo = %s",
644            function,
645            lock,
646            (unsigned long)shmem->mstate.clients,
647            (long)statecount,
648            shmem,
649            shmem->mstate.shmid,
650            clientinfo2string(cinfo, NULL, 0));
651 
652    socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
653    return 0;
654 }
655 
656 int
shmem_unuse(shmem,cinfo,lock)657 shmem_unuse(shmem, cinfo, lock)
658    shmem_object_t *shmem;
659    const clientinfo_t *cinfo;
660    int lock;
661 {
662    const char *function = "shmem_unuse()";
663 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
664    shmem_object_t _shmem = *shmem;
665 #endif /* DEBUG */
666    ssize_t statecount = -1;
667 
668    SASSERTX(shmem != NULL);
669    SASSERTX(shmem->mstate.number > 0);
670 
671    if (sockscf.option.debug >= DEBUG_VERBOSE)
672       slog(LOG_DEBUG, "%s: pre-use: lock = %d, # of clients = %lu, rule = %lu, "
673                       "shmem = %p, cinfo = %s, shmid %ld, key %d",
674                       function,
675                       lock,
676                       (unsigned long)shmem->mstate.clients,
677                       (unsigned long)shmem->mstate.number,
678                       shmem,
679                       clientinfo2string(cinfo, NULL, 0),
680                       shmem->mstate.shmid,
681                       (int)shmem->keystate.key);
682 
683    switch (shmem->type) {
684       case SHMEM_BW:
685       case SHMEM_MONITOR:
686       case SHMEM_SS:
687          break;
688 
689       default:
690          SERRX(shmem->type);
691    }
692 
693    socks_lock(lock, (off_t)shmem->mstate.shmid, 1, 1, 1);
694 
695    if (shmem->keystate.key != key_unset) {
696 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
697       keystate_t _keystate;
698 #endif /* DEBUG */
699       ssize_t i;
700       size_t sizemapped;
701       int rc;
702 
703       SASSERTX(shmem->keystate.keyv == NULL);
704       SASSERTX(shmem->keystate.keyc > 0);
705 
706       MUNPROTECT_SHMEMHEADER(shmem);
707 
708       rc = keystate_openmap(shmem->mstate.shmid, &shmem->keystate, &sizemapped);
709 
710       MPROTECT_SHMEMHEADER(shmem);
711 
712       if (rc != 0) {
713          socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
714 
715          slog(LOG_DEBUG, "%s: keystate_openmap() of shmid %lu failed: %s",
716               function, (unsigned long)shmem->mstate.shmid, strerror(errno));
717 
718          return -1;
719       }
720 
721 #if DEBUG /* memory-mapped file contents may not be saved in coredumps. */
722       _keystate = shmem->keystate;
723 #endif /* DEBUG */
724 
725       SASSERTX(sizemapped > 0);
726       SASSERTX(shmem->keystate.keyv != NULL);
727       SASSERTX(shmem->keystate.keyc > 0);
728 
729       i = keystate_index(shmem, cinfo, should_do_expirescan(shmem));
730       SASSERTX(i >= 0);
731       SASSERTX((size_t)i < shmem->keystate.keyc);
732 
733       MUNPROTECT_SHMEMHEADER(shmem);
734 
735       switch (shmem->keystate.key) {
736          case key_from:
737             SASSERTX(shmem->keystate.keyv[i].data.from.addrc > 0);
738             --shmem->keystate.keyv[i].data.from.addrc;
739             statecount = (ssize_t)shmem->keystate.keyv[i].data.from.addrc;
740             break;
741 
742 #if HAVE_SOCKS_HOSTID
743          case key_hostid:
744             SASSERTX(shmem->keystate.keyv[i].data.hostid.addrc > 0);
745             --shmem->keystate.keyv[i].data.hostid.addrc;
746             statecount = (ssize_t)shmem->keystate.keyv[i].data.hostid.addrc;
747             break;
748 #endif /* HAVE_SOCKS_HOSTID */
749 
750          default:
751             SERRX(shmem->keystate.key);
752       }
753 
754       keystate_closemap(shmem->mstate.shmid, &shmem->keystate, sizemapped, i);
755 
756       MPROTECT_SHMEMHEADER(shmem);
757    }
758 
759    MUNPROTECT_SHMEMHEADER(shmem);
760 
761    SASSERTX(shmem->mstate.clients > 0);
762    --shmem->mstate.clients;
763 
764    MPROTECT_SHMEMHEADER(shmem);
765 
766    if (sockscf.option.debug >= DEBUG_VERBOSE)
767       slog(LOG_DEBUG,
768            "%s: post-use: lock = %d, clients = %lu (%ld), shmem = %p, "
769            "shmid = %ld, cinfo = %s",
770            function,
771            lock,
772            (unsigned long)shmem->mstate.clients,
773            (long)statecount,
774            shmem,
775            shmem->mstate.shmid,
776            clientinfo2string(cinfo, NULL, 0));
777 
778    socks_unlock(lock, (off_t)shmem->mstate.shmid, 1);
779    return 0;
780 }
781 
782 
783 int
keystate_hasexpired(shmem,keyindex,timenow)784 keystate_hasexpired(shmem, keyindex, timenow)
785    const shmem_object_t *shmem;
786    const size_t keyindex;
787    const struct timeval *timenow;
788 {
789    const char *function = "keystate_hasexpired()";
790    struct timeval tdiff;
791    char buf[64];
792    int expired = 0;
793 
794    SASSERTX(keyindex < shmem->keystate.keyc);
795 
796    switch (shmem->type) {
797       case SHMEM_SS: {
798 
799          if (!shmem->object.ss.throttle_perstate_isset)
800             break; /* only throttle-limit can expire. */
801 
802          timersub(timenow,
803                   &shmem->keystate.keyv[keyindex].info.ss.starttime,
804                   &tdiff);
805 
806          if (tdiff.tv_sec >= shmem->object.ss.throttle_perstate.limit.seconds)
807             expired = 1;
808 
809          break;
810       }
811 
812       default:
813          break;
814    }
815 
816    if (expired) {
817       if (!timerisset(&shmem->keystate.keyv[keyindex].info.ss.starttime))
818          snprintf(buf, sizeof(buf), "yes (init)");
819       else
820          snprintf(buf, sizeof(buf), "yes, %ld.%06lds ago",
821                   (long)tdiff.tv_sec, (long)tdiff.tv_usec);
822    }
823 
824    if (sockscf.option.debug) {
825       const char *keystring;
826       keystate_data_t key;
827 
828       keystate_data(&shmem->keystate, keyindex, &key);
829       keystring = keydata2string(&key, NULL, 0);
830 
831       slog(LOG_DEBUG,
832            "%s: keystate address %s, index %lu, starttime = %ld.%06ld, "
833            "clients = %lu, expired: %s",
834            function,
835            keystring,
836            (unsigned long)keyindex,
837            (long)shmem->keystate.keyv[keyindex].info.ss.starttime.tv_sec,
838            (long)shmem->keystate.keyv[keyindex].info.ss.starttime.tv_usec,
839            (unsigned long)keystate_clientcount(&shmem->keystate, keyindex),
840            expired ? buf : "no");
841    }
842 
843    return expired;
844 }
845 
846 static unsigned long
mem2shmem(config,firstid)847 mem2shmem(config, firstid)
848    struct config *config;
849    const unsigned long firstid;
850 {
851    const char *function = "mem2shmem()";
852    monitor_t *monitor;
853    rule_t *rule;
854    rule_t *rulev[]    = { config->crule,
855 
856 #if HAVE_SOCKS_HOSTID
857                           config->hrule,
858 #endif /* HAVE_SOCKS_HOSTID */
859 
860 #if HAVE_SOCKS_RULES
861                           config->srule
862 #endif /* HAVE_SOCKS_RULES */
863                                };
864    unsigned long nextid;
865    size_t i;
866 
867    /*
868     * Only main mother allocates the memory.  Children just set the
869     * shmid value and use it to attach to the memory as needed later on.
870     * Mother makes sure all the ids are in consecutive order starting
871     * from the passed "firstid" argument, so children just need to
872     * increment it to get the shmid of the next object.
873     */
874    SASSERTX(pidismother(sockscf.state.pid) == 1);
875 
876 /*
877  * Mother needs to create and fill in the correct contents initially,
878  * but after that, the child-processes attach and update the segments
879  * as needed.
880  */
881 #define HANDLE_SHMCR(object, memfield, idfield, id)                            \
882 do {                                                                           \
883    const statekey_t key = (object)->memfield->keystate.key;                    \
884    const char *fname    = sockd_getshmemname(id, key_unset);                   \
885    const int flags      = O_RDWR  | O_CREAT | O_EXCL;                          \
886    const mode_t mode    = S_IRUSR | S_IWUSR;                                   \
887    shmem_object_t *mem;                                                        \
888    int fd;                                                                     \
889                                                                                \
890    SASSERTX(id != 0);                                                          \
891    SASSERTX(fname != NULL && *fname != NUL);                                   \
892                                                                                \
893    SASSERTX((object)->memfield->mstate.number     > 0);                        \
894    SASSERTX((object)->memfield->mstate.parenttype != object_none);             \
895    SASSERTX((object)->type                        != object_none);             \
896    SASSERTX((object)->memfield->type              != SHMEM_NONE);              \
897                                                                                \
898    if ((fd = open(fname, flags, mode)) == -1)                                  \
899       serr("%s: failed to create shmemfile \"%s\"", function, fname);          \
900                                                                                \
901    slog(LOG_DEBUG,                                                             \
902         "%s: will use filename %s for shmid %ld when creating shmem segment "  \
903         "for " #memfield " in %s #%lu",                                        \
904         function,                                                              \
905         fname,                                                                 \
906         (id),                                                                  \
907         objecttype2string(object->type),                                       \
908         (unsigned long)(object)->number);                                      \
909                                                                                \
910    if ((mem = sockd_mmap(NULL,                                                 \
911                          sizeof(*(object)->memfield),                          \
912                          PROT_READ | PROT_WRITE,                               \
913                          MAP_SHARED,                                           \
914                          fd,                                                   \
915                          1)) == MAP_FAILED)                                    \
916       serr("%s: sockd_mmap of size %lu failed",                                \
917            function, (unsigned long)sizeof(*(object)->memfield));              \
918                                                                                \
919                                                                                \
920    /*                                                                          \
921     * replace the ordinary memory with shared memory.                          \
922     */                                                                         \
923    *mem               = *(object)->memfield;                                   \
924    free((object)->memfield);                                                   \
925    (object)->memfield = mem;                                                   \
926                                                                                \
927    close(fd);                                                                  \
928                                                                                \
929    if (key != key_unset) {                                                     \
930       const char *fname = sockd_getshmemname(id, key);                         \
931                                                                                \
932       SASSERTX(fname != NULL && *fname != NUL);                                \
933                                                                                \
934       if ((fd = open(fname, flags, mode)) == -1)                               \
935          serr("%s: failed to create file %s", function, fname);                \
936                                                                                \
937       /*                                                                       \
938        * Just create the file for now.  Nothing to init.                       \
939        */                                                                      \
940                                                                                \
941       close(fd);                                                               \
942                                                                                \
943       slog(LOG_DEBUG,                                                          \
944            "%s: will use filename %s for shmid %lu/key %lu when creating "     \
945            "shmem segment for " #memfield " in %s #%lu",                       \
946            function,                                                           \
947            fname,                                                              \
948            (unsigned long)(id),                                                \
949            (unsigned long)(key),                                               \
950            objecttype2string((object)->type),                                  \
951            (unsigned long)(object)->number);                                   \
952    }                                                                           \
953                                                                                \
954    SASSERTX((object)->memfield->mstate.shmid == 0);                            \
955    (object)->memfield->mstate.shmid = (id);                                    \
956                                                                                \
957    SASSERTX((object)->idfield == 0);                                           \
958    (object)->idfield = (id);                                                   \
959                                                                                \
960    slog(LOG_DEBUG, "%s: allocated " #idfield " %ld for object #%lu",           \
961         function, (object)->idfield, (unsigned long)(object)->number);         \
962                                                                                \
963    SASSERTX(munmap((object)->memfield, sizeof(*(object)->memfield)) == 0);     \
964    (object)->memfield = NULL;                                                  \
965 } while (/* CONSTCOND */ 0)
966 
967 
968    nextid = firstid;
969 
970    /*
971     * Shmem for rules.
972     */
973    for (i = 0; i < ELEMENTS(rulev); ++i) {
974       rule = rulev[i];
975 
976       while (rule != NULL) {
977          if (rule->bw != NULL) {
978             SASSERTX(rule->bw->type == SHMEM_BW);
979             HANDLE_SHMCR(rule, bw, bw_shmid, nextid);
980             ++nextid;
981          }
982 
983          if (rule->ss != NULL) {
984             SASSERTX(rule->ss->type == SHMEM_SS);
985             HANDLE_SHMCR(rule, ss, ss_shmid, nextid);
986             ++nextid;
987          }
988 
989          rule = rule->next;
990       }
991    }
992 
993    /*
994     * Shmem for monitors.
995     */
996    for (monitor = config->monitor; monitor != NULL; monitor = monitor->next) {
997       if (monitor->mstats != NULL) {
998          SASSERTX(monitor->mstats->type == SHMEM_MONITOR);
999          HANDLE_SHMCR(monitor, mstats, mstats_shmid, nextid);
1000          ++nextid;
1001       }
1002    }
1003 
1004    slog(LOG_DEBUG,
1005         "%s: ok, allocated %ld shared memory id%s, first id is %lu, "
1006         "last id is %lu",
1007         function,
1008         (long)(nextid - firstid),
1009         nextid - firstid == 1 ? "" : "s",
1010         firstid,
1011         nextid);
1012 
1013    return nextid;
1014 }
1015 
1016 static void
keystate_removeindex(keystate,index)1017 keystate_removeindex(keystate, index)
1018    keystate_t *keystate;
1019    const size_t index;
1020 {
1021    const char *function = "keystate_removeindex()";
1022    sa_family_t safamily;
1023    void *addr;
1024 
1025    switch (keystate->key) {
1026       case key_from:
1027          switch (keystate->keyv[index].data.from.safamily) {
1028             case AF_INET:
1029                safamily = AF_INET;
1030                addr     = &keystate->keyv[index].data.from.addr.ipv4;
1031                break;
1032 
1033             case AF_INET6:
1034                safamily = AF_INET6;
1035                addr     = &keystate->keyv[index].data.from.addr.ipv6;
1036                break;
1037 
1038             default:
1039                SERRX(keystate->keyv[index].data.from.safamily);
1040          }
1041 
1042          break;
1043 
1044 
1045 #if HAVE_SOCKS_HOSTID
1046       case key_hostid:
1047          safamily = AF_INET6;
1048          addr     = &keystate->keyv[index].data.hostid.ipv4;
1049          break;
1050 #endif /* HAVE_SOCKS_HOSTID */
1051 
1052       default:
1053          SERRX(keystate->key);
1054    }
1055 
1056    if (sockscf.option.debug) {
1057       char ntop[MAXSOCKADDRSTRING];
1058 
1059       if (inet_ntop(safamily, addr, ntop, sizeof(ntop)) == NULL) {
1060          swarn("%s: inet_ntop(3) failed on safamily %s, addr %s",
1061               function,
1062               safamily2string(safamily),
1063               addr2hexstring(addr, safamily, NULL, 0));
1064       }
1065 
1066       slog(LOG_DEBUG,
1067            "%s: removing entry for address %s (key: %s) at index #%lu "
1068            "from keystate. Will have %lu entries in keyv afterwards",
1069            function,
1070            ntop,
1071            statekey2string(keystate->key),
1072            (unsigned long)index,
1073            (unsigned long)keystate->keyc - 1);
1074    }
1075 
1076 
1077    /*
1078     * bzero the removed entry, so that if somebody overwrites it before
1079     * we close and reopen the mmap(2)-ed file, it will be zero.
1080     */
1081    bzero(&keystate->keyv[index], sizeof(*keystate->keyv));
1082 
1083    if (keystate->keyc > 1)
1084       memmove(&keystate->keyv[index],
1085               &keystate->keyv[index + 1],
1086               sizeof(*keystate->keyv) * (keystate->keyc - (index + 1)));
1087 
1088    --keystate->keyc;
1089 
1090    if (sockscf.option.debug) {
1091       size_t i;
1092 
1093       for (i = 0; i < keystate->keyc; ++i) {
1094          const char *keystring;
1095          keystate_data_t key;
1096 
1097          keystate_data(keystate, i, &key);
1098          keystring = keydata2string(&key, NULL, 0);
1099 
1100          slog(LOG_DEBUG, "%s: entry #%lu: %s",
1101               function, (unsigned long)i, keystring);
1102       }
1103    }
1104 }
1105 
1106 ssize_t
keystate_index(shmem,cinfo,doexpirescan)1107 keystate_index(shmem, cinfo, doexpirescan)
1108    shmem_object_t *shmem;
1109    const clientinfo_t *cinfo;
1110    const int doexpirescan;
1111 {
1112    const char *function = "keystate_index()";
1113    static ssize_t lastmatched = -1;
1114    const void *datatomatch;
1115    keystate_data_t keydata;
1116    keystate_data_type datatype;
1117    struct timeval timenow;
1118    ssize_t matchedi;
1119    size_t i, datalen;
1120    char extrainfo[128] = { NUL };
1121 
1122    if (doexpirescan)
1123       gettimeofday_monotonic(&timenow);
1124 
1125    SASSERTX(shmem->keystate.key != key_unset);
1126 
1127    switch (shmem->keystate.key) {
1128       case key_from:
1129          switch (cinfo->from.ss_family) {
1130             case AF_INET:
1131                datatype = keytype_ipv4;
1132                break;
1133 
1134             case AF_INET6:
1135                datatype = keytype_ipv6;
1136                break;
1137 
1138             default:
1139                SERRX(cinfo->from.ss_family);
1140          }
1141 
1142          datatomatch = GET_SOCKADDRADDR(&cinfo->from);
1143          datalen     = inaddrlen(cinfo->from.ss_family);
1144          break;
1145 
1146 #if HAVE_SOCKS_HOSTID
1147       case key_hostid:
1148          SASSERTX(shmem->keystate.keyinfo.hostindex <= cinfo->hostidc);
1149 
1150          datatomatch = &cinfo->hostidv[shmem->keystate.keyinfo.hostindex - 1];
1151          datatype    = keytype_ipv4;
1152          datalen     = sizeof(keydata.data.ipv4);
1153 
1154          snprintf(extrainfo, sizeof(extrainfo), "(hostindex = %u)",
1155                   (unsigned)shmem->keystate.keyinfo.hostindex);
1156 
1157          break;
1158 #endif /* HAVE_SOCKS_HOSTID */
1159 
1160       default:
1161          SERRX(shmem->keystate.key);
1162    }
1163 
1164    if (lastmatched != -1 && lastmatched < (ssize_t)shmem->keystate.keyc) {
1165       keystate_data(&shmem->keystate, lastmatched, &keydata);
1166 
1167       switch (keydata.type) {
1168          case keytype_ipv4:
1169             if (memcmp(&keydata.data.ipv4, datatomatch, datalen) == 0)
1170                matchedi = lastmatched;
1171             else
1172                matchedi = -1;
1173             break;
1174 
1175          case keytype_ipv6:
1176             if (memcmp(&keydata.data.ipv6, datatomatch, datalen) == 0)
1177                matchedi = lastmatched;
1178             else
1179                matchedi = -1;
1180             break;
1181 
1182          default:
1183             SERRX(keydata.type);
1184       }
1185    }
1186    else
1187       matchedi = -1;
1188 
1189    if (sockscf.option.debug) {
1190       char ntop[MAXSOCKADDRSTRING];
1191 
1192       switch (datatype) {
1193          case keytype_ipv4:
1194             if (inet_ntop(AF_INET, datatomatch, ntop, sizeof(ntop)) == NULL) {
1195                swarn("%s: inet_ntop(3) failed on %s %x",
1196                     function,
1197                     atype2string(AF_INET),
1198                     ((const struct in_addr *)datatomatch)->s_addr);
1199 
1200                snprintf(ntop, sizeof(ntop), "<unknown>");
1201             }
1202             break;
1203 
1204          case keytype_ipv6:
1205             if (inet_ntop(AF_INET6, datatomatch, ntop, sizeof(ntop)) == NULL) {
1206                swarn("%s: inet_ntop(3) failed on %s " IP6_FMTSTR,
1207                     function,
1208                     atype2string(AF_INET6),
1209                     IP6_ELEMENTS((const struct in6_addr *)datatomatch));
1210 
1211                snprintf(ntop, sizeof(ntop), "<unknown>");
1212             }
1213             break;
1214 
1215          default:
1216             SERRX(datatype);
1217       }
1218 
1219       slog(LOG_DEBUG,
1220            "%s: trying to find a match for key %s, address %s%s%s in keyv "
1221            "array with %lu entries.  Doexpirescan = %d, matchedi = %ld%s",
1222            function,
1223            statekey2string(shmem->keystate.key),
1224            ntop,
1225            *extrainfo == NUL ? "" : " ",
1226            *extrainfo == NUL ? "" : extrainfo,
1227            (unsigned long)shmem->keystate.keyc,
1228            doexpirescan,
1229            (long)matchedi,
1230            matchedi == -1 ?
1231                "" : doexpirescan ?
1232                  ".  Not scanning since index from last time matches" :  "");
1233    }
1234 
1235    if (matchedi != -1)
1236       return matchedi;
1237 
1238    i  = 0;
1239    while (i < shmem->keystate.keyc) {
1240       keystate_data_t keydata;
1241       int matches;
1242 
1243       if (doexpirescan && keystate_hasexpired(shmem, i, &timenow)) {
1244          MUNPROTECT_SHMEMHEADER(shmem);
1245 
1246          if (keystate_clientcount(&shmem->keystate, i) == 0) {
1247             keystate_removeindex(&shmem->keystate, i);
1248             continue;
1249          }
1250          else /* can't remove as long as long as entry is in use; just reset. */
1251             RESET_THROTTLE(&shmem->object.ss.throttle, timenow);
1252 
1253          MPROTECT_SHMEMHEADER(shmem);
1254       }
1255 
1256       keystate_data(&shmem->keystate, i, &keydata);
1257 
1258       if (keydata.type == datatype) {
1259          switch (keydata.type) {
1260             case keytype_ipv4:
1261                matches
1262                = (memcmp(&keydata.data.ipv4, datatomatch, datalen) == 0);
1263                break;
1264 
1265             case keytype_ipv6:
1266                matches
1267                = (memcmp(&keydata.data.ipv6, datatomatch, datalen) == 0);
1268                break;
1269 
1270             default:
1271                SERRX(keydata.type);
1272          }
1273 
1274          if (sockscf.option.debug)
1275             slog(LOG_DEBUG,
1276                  "%s: compared against %s at index #%lu using %lu bytes: %s",
1277                  function,
1278                  keydata2string(&keydata, NULL, 0),
1279                  (unsigned long)i,
1280                  (unsigned long)datalen,
1281                  matches ? "matches" : "no match");
1282       }
1283       else /* can't possibly match. */
1284          matches = 0;
1285 
1286       if (matches) {
1287          SASSERTX(matchedi == -1);
1288          matchedi = lastmatched = (ssize_t)i;
1289 
1290          if (!doexpirescan)
1291             break;
1292       }
1293 
1294       ++i;
1295    }
1296 
1297    if (doexpirescan) {
1298       MUNPROTECT_SHMEMHEADER(shmem);
1299 
1300       shmem->keystate.lastexpirescan = timenow.tv_sec;
1301 
1302       MPROTECT_SHMEMHEADER(shmem);
1303    }
1304    return matchedi;
1305 }
1306 
1307 int
keystate_openmap(id,keystate,sizemapped)1308 keystate_openmap(id, keystate, sizemapped)
1309    const unsigned long id;
1310    keystate_t *keystate;
1311    size_t *sizemapped;
1312 {
1313    const char *function = "keystate_openmap()";
1314    const char *fname    = sockd_getshmemname(id, keystate->key);
1315    size_t _sizemapped;
1316 
1317    SASSERTX(fname != NULL && *fname != NUL);
1318    SASSERTX(keystate->key != key_unset);
1319 
1320    slog(LOG_DEBUG, "%s: shmid %ld, key %d",
1321         function, id, (int)keystate->key);
1322 
1323    if (sizemapped == NULL)
1324       sizemapped = &_sizemapped;
1325 
1326    *sizemapped = keystate->keyc * sizeof(*keystate->keyv);
1327 
1328    if (*sizemapped == 0) {
1329       SASSERTX(keystate->keyc == 0);
1330       SASSERTX(keystate->keyv == NULL);
1331    }
1332    else {
1333       const int fd = open(fname, O_RDWR);
1334 
1335       if (fd == -1) {
1336          slog(sockd_motherexists() ? LOG_WARNING: LOG_DEBUG,
1337                "%s: could not open shmemfile %s generated from "
1338                "id %ld, key %d: %s",
1339                function,
1340                fname,
1341                id,
1342                (int)keystate->key,
1343                getppid() == 1 ?
1344                     "probably because mother has exited and deleted it already"
1345                   : strerror(errno));
1346 
1347          return -1;
1348       }
1349 
1350       if ((keystate->keyv = sockd_mmap(NULL,
1351                                        *sizemapped,
1352                                        PROT_READ | PROT_WRITE,
1353                                        MAP_SHARED,
1354                                        fd,
1355                                        0)) == MAP_FAILED) {
1356          swarn("%s: could not mmap shmemfile %s of size %lu generated from "
1357                "id %ld, key %d",
1358                function,
1359                fname,
1360                (unsigned long)*sizemapped,
1361                id,
1362                (int)keystate->key);
1363 
1364          keystate->keyv = NULL;
1365          close(fd);
1366 
1367          return -1;
1368       }
1369 
1370       close(fd);
1371    }
1372 
1373    slog(LOG_DEBUG, "%s: mapped %lu bytes for shmid %ld, key %d, at address %p",
1374                    function,
1375                    (unsigned long)*sizemapped,
1376                    id,
1377                    (int)keystate->key,
1378                    keystate->keyv);
1379 
1380    return 0;
1381 }
1382 
1383 void
keystate_closemap(id,keystate,mappedsize,changedindex)1384 keystate_closemap(id, keystate, mappedsize, changedindex)
1385    const unsigned long id;
1386    keystate_t *keystate;
1387    const size_t mappedsize;
1388    const ssize_t changedindex;
1389 {
1390    const char *function = "keystate_closemap()";
1391    const char *fname    = sockd_getshmemname(id, keystate->key);
1392    size_t newsize;
1393 
1394    newsize = keystate->keyc * sizeof(*keystate->keyv);
1395 
1396    slog(LOG_DEBUG,
1397         "%s: mapped size for keystate of id %lu is %lu, newsize is %lu, "
1398         "changedindex: %ld",
1399         function,
1400         (unsigned long)id,
1401         (unsigned long)mappedsize,
1402         (unsigned long)newsize,
1403         (long)changedindex);
1404 
1405    if (sockscf.option.debug) {
1406       keystate_data_t keydata;
1407       size_t i;
1408 
1409       for (i = 0; i < keystate->keyc; ++i)
1410          slog(LOG_DEBUG, "%s: entry #%lu: %s",
1411               function,
1412               (unsigned long)i,
1413               keydata2string(keystate_data(keystate, i, &keydata), NULL, 0));
1414    }
1415 
1416    if (mappedsize == 0)
1417       SASSERTX(keystate->keyv == NULL);
1418    else {
1419 #if !HAVE_UNIFIED_BUFFERCACHE
1420       /*
1421        * With a unified buffer cache, modifications to the mmap(2)-ed file will
1422        * be visible by other processes that open(2) and mmap(2) the same file
1423        * without having to msync(2) the memory to file.  I.e., this will work
1424        * Process1:
1425        *    t1: open(2) file F.
1426        *    t2: mmap(2) file F.
1427        *    t3: <modify mmap(2)-ed memory>
1428        *
1429        * Process2:
1430        *    t4: open(2) file F.
1431        *    t5: mmap(2) file F.
1432        *    t6: <see same thing Process1 sees>
1433        *
1434        * This code is for platforms where it does not work.  Currently
1435        * only OpenBSD AFAIK (5.1 at least).
1436        */
1437       void *addr;
1438       size_t len;
1439       int needmsync;
1440 
1441       SASSERTX(keystate->keyv != NULL);
1442 
1443       if (mappedsize < newsize) {
1444          needmsync = 1;
1445 
1446          /*
1447           * don't know what has changed, have to msync everything.
1448           */
1449 
1450          addr = keystate->keyv;
1451          len  = newsize;
1452       }
1453       else if (changedindex != -1) {
1454          needmsync = 1;
1455 
1456          addr = (void *)ROUNDDOWN((uintptr_t)&keystate->keyv[changedindex],
1457                                   sockscf.state.pagesize);
1458 
1459          SASSERTX((uintptr_t)keystate->keyv <= (uintptr_t)addr);
1460 
1461          len = ((uintptr_t)&keystate->keyv[changedindex] - (uintptr_t)addr)
1462                + sizeof(keystate->keyv[changedindex]);
1463       }
1464       else {
1465          SASSERTX(mappedsize == newsize);
1466          needmsync = 0;
1467       }
1468 
1469       if (needmsync) {
1470          const int rc = msync(addr, len, MS_SYNC);
1471 
1472          slog(rc == 0 ? LOG_DEBUG : LOG_WARNING,
1473               "%s: msync(%p, %lu, MS_SYNC), based on mappedsize %lu, "
1474               "newsize %lu, changedindex %ld, keyv %p %s (%s)",
1475               function,
1476               addr,
1477               len,
1478               (unsigned long)mappedsize,
1479               (unsigned long)newsize,
1480               (long)changedindex,
1481               keystate->keyv,
1482               rc == 0 ? "is ok" : "failed",
1483               strerror(errno));
1484       }
1485 #endif /* !HAVE_UNIFIED_BUFFERCACHE */
1486 
1487       SASSERTX(keystate->keyv != NULL);
1488 
1489       if (munmap(keystate->keyv, mappedsize) != 0)
1490          swarn("%s: munmap(2) of keystate.keyv (%p) of size %lu failed",
1491                function, keystate->keyv, (unsigned long)mappedsize);
1492 
1493       keystate->keyv = NULL;
1494    }
1495 
1496    if (newsize != mappedsize) {
1497       if (truncate(fname, (off_t)newsize) == 0)
1498          slog(LOG_DEBUG, "%s: truncated shmemfile %s to size %lu",
1499                          function, fname, (unsigned long)newsize);
1500       else
1501          swarn("%s: could not truncate shmemfile %s to size %lu",
1502                function, fname, (unsigned long)newsize);
1503    }
1504 }
1505 
1506 #if DIAGNOSTIC && !SOCKS_CLIENT /* for internal debugging/testing. */
1507 void
shmemcheck(void)1508 shmemcheck(void)
1509 {
1510    const int errno_s = errno;
1511    rule_t *rulev[]    = { sockscf.crule,
1512 
1513 #if HAVE_SOCKS_HOSTID
1514                           sockscf.hrule,
1515 #endif /* HAVE_SOCKS_HOSTID */
1516 
1517 #if HAVE_SOCKS_RULES
1518                           sockscf.srule
1519 #endif /* HAVE_SOCKS_RULES */
1520                                };
1521    size_t i;
1522 
1523    /*
1524     * Shmem for existing rules.
1525     */
1526    for (i = 0; i < ELEMENTS(rulev); ++i) {
1527       rule_t *_rule = rulev[i];
1528 
1529       while (_rule != NULL) {
1530          rule_t rule = *_rule;
1531 
1532          /*
1533           * sockd_shm{at,dt} will do the checking.
1534           */
1535          sockd_shmat(&rule, SHMEM_ALL);
1536          sockd_shmdt(&rule, SHMEM_ALL);
1537 
1538          _rule = _rule->next;
1539       }
1540    }
1541 
1542    if (pidismother(sockscf.state.pid) == 1) {
1543       /*
1544        * Shmem for old rules.
1545        */
1546       for (i = 0; i < sockscf.oldshmemc; ++i) {
1547          rule_t rule;
1548 
1549          bzero(&rule, sizeof(rule));
1550 
1551          switch (sockscf.oldshmemv[i].type) {
1552             case SHMEM_BW:
1553                rule.bw_shmid = sockscf.oldshmemv[i].id;
1554                break;
1555 
1556             case SHMEM_MONITOR:
1557                rule.mstats_shmid = sockscf.oldshmemv[i].id;
1558                break;
1559 
1560             case SHMEM_SS:
1561                rule.ss_shmid = sockscf.oldshmemv[i].id;
1562                break;
1563 
1564             default:
1565                SERRX(sockscf.oldshmemv[i].type);
1566          }
1567 
1568          (void)sockd_shmat(&rule, sockscf.oldshmemv[i].type);
1569          (void)sockd_shmdt(&rule, sockscf.oldshmemv[i].type);
1570       }
1571    }
1572 
1573    errno = errno_s;
1574 }
1575 #endif /* DIAGNOSTIC && !SOCKS_CLIENT */
1576 
1577 static int
should_do_expirescan(shmem)1578 should_do_expirescan(shmem)
1579    const shmem_object_t *shmem;
1580 {
1581    ssize_t timer;
1582 
1583    if ((timer = keystate_timer(shmem)) != -1
1584    && socks_difftime(time_monotonic(NULL),
1585                      shmem->keystate.lastexpirescan) >= timer)
1586       return 1;
1587 
1588    return 0;
1589 }
1590 
1591 static ssize_t
keystate_timer(shmem)1592 keystate_timer(shmem)
1593    const shmem_object_t *shmem;
1594 {
1595 
1596    switch (shmem->type) {
1597       case SHMEM_SS:
1598          if (shmem->object.ss.throttle_perstate_isset)
1599             return (size_t)shmem->object.ss.throttle_perstate.limit.seconds;
1600          else
1601             return -1;
1602 
1603       default:
1604          return -1;
1605    }
1606 }
1607 
1608 static keystate_data_t *
keystate_data(keystate,index,keydata)1609 keystate_data(keystate, index, keydata)
1610    const keystate_t *keystate;
1611    const size_t index;
1612    keystate_data_t *keydata;
1613 {
1614 
1615    SASSERTX(index < keystate->keyc);
1616    switch (keystate->key) {
1617       case key_from:
1618          switch (keystate->keyv[index].data.from.safamily) {
1619             case AF_INET:
1620                keydata->type      = keytype_ipv4;
1621                keydata->data.ipv4 = keystate->keyv[index].data.from.addr.ipv4;
1622                break;
1623 
1624             case AF_INET6:
1625                keydata->type      = keytype_ipv6;
1626                keydata->data.ipv6 = keystate->keyv[index].data.from.addr.ipv6;
1627                break;
1628 
1629             default:
1630                SERRX(keystate->keyv[index].data.from.safamily);
1631          }
1632 
1633          break;
1634 
1635 
1636 #if HAVE_SOCKS_HOSTID
1637       case key_hostid:
1638          keydata->type      = keytype_ipv4;
1639          keydata->data.ipv4 = keystate->keyv[index].data.hostid.ipv4;
1640          break;
1641 #endif /* HAVE_SOCKS_HOSTID */
1642 
1643       default:
1644          SERRX(keystate->key);
1645    }
1646 
1647    return keydata;
1648 }
1649 
1650 static const char *
keydata2string(keydata,buf,buflen)1651 keydata2string(keydata, buf, buflen)
1652    const keystate_data_t *keydata;
1653    char *buf;
1654    size_t buflen;
1655 
1656 {
1657    const char *function = "keydata2string()";
1658    const void *addr;
1659    sa_family_t safamily;
1660 
1661    if (buf == NULL) {
1662       static char _buf[MAXSOCKADDRSTRING];
1663 
1664       buf    = _buf;
1665       buflen = sizeof(_buf);
1666    }
1667 
1668    switch (keydata->type) {
1669       case keytype_ipv4:
1670          safamily = AF_INET;
1671          addr     = &keydata->data.ipv4;
1672          break;
1673 
1674       case keytype_ipv6:
1675          safamily = AF_INET6;
1676          addr     = &keydata->data.ipv6;
1677          break;
1678 
1679       default:
1680          SERRX(keydata->type);
1681    }
1682 
1683    if (inet_ntop(safamily, addr, buf, buflen) == NULL) {
1684       addr2hexstring(addr, safamily, buf, buflen);
1685       swarn("%s: inet_ntop(3) failed on safamily %s, addr %s",
1686            function, safamily2string(safamily), buf);
1687    }
1688 
1689    return buf;
1690 }
1691 
1692 static size_t
keystate_clientcount(keystate,index)1693 keystate_clientcount(keystate, index)
1694    const keystate_t *keystate;
1695    const size_t index;
1696 {
1697 
1698    SASSERTX(index < keystate->keyc);
1699 
1700    switch (keystate->key) {
1701       case key_from:
1702          return keystate->keyv[index].data.from.addrc;
1703 
1704 #if HAVE_SOCKS_HOSTID
1705       case key_hostid:
1706          return keystate->keyv[index].data.hostid.addrc;
1707 #endif /* HAVE_SOCKS_HOSTID */
1708 
1709       default:
1710          SERRX(keystate->key);
1711    }
1712 
1713    /* NOTREACHED */
1714 }
1715