1 /*
2  * Copyright (c) 2013, 2014
3  *      Inferno Nettverk A/S, Norway.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. The above copyright notice, this list of conditions and the following
9  *    disclaimer must appear in all copies of the software, derivative works
10  *    or modified versions, and any portions thereof, aswell as in all
11  *    supporting documentation.
12  * 2. All advertising materials mentioning features or use of this software
13  *    must display the following acknowledgement:
14  *      This product includes software developed by
15  *      Inferno Nettverk A/S, Norway.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * Inferno Nettverk A/S requests users of this software to return to
31  *
32  *  Software Distribution Coordinator  or  sdc@inet.no
33  *  Inferno Nettverk A/S
34  *  Oslo Research Park
35  *  Gaustadall�en 21
36  *  NO-0349 Oslo
37  *  Norway
38  *
39  * any improvements or extensions that they make and grant Inferno Nettverk A/S
40  * the rights to redistribute these changes.
41  *
42  */
43 
44 #include "common.h"
45 #include "monitor.h"
46 
47 static const char rcsid[] =
48 "$Id: sockd_io_misc.c,v 1.30.4.5 2014/08/24 11:41:35 karls Exp $";
49 
50 void
io_updatemonitor(io)51 io_updatemonitor(io)
52    sockd_io_t *io;
53 {
54    const char *function = "io_updatemonitor()";
55    const monitor_t *newmatch;
56    monitor_t oldmonitor, newmonitor;
57    rule_t *rule = IORULE(io);
58    clientinfo_t cinfo;
59    size_t disconnect_needed;
60    int is_same;
61 
62    SASSERTX(sockscf.state.type == PROC_IO);
63 
64    slog(LOG_DEBUG,
65         "%s: control-fd %d, src-fd %d, dst-fd %d, protocol %s, command %s, "
66         "mstats_shmid %lu, at address %p",
67         function,
68         io->control.s,
69         io->src.s,
70         io->dst.s,
71         protocol2string(io->state.protocol),
72         command2string(io->state.command),
73         rule->mstats_shmid,
74         rule->mstats);
75 
76    if (rule->mstats_shmid != 0)
77       SASSERTX(rule->mstats != NULL); /* i/o process; always attached. */
78 
79    /*
80     * XXX could probably optimize this function away be checking if a sighup
81     * has occurred since the monitor settings were applied to this i/o object.
82     */
83 
84 #define DISCONNECT_NEEDED(_io, _dodisconnect)                                  \
85 do {                                                                           \
86    *(_dodisconnect) = 0;                                                       \
87                                                                                \
88    if (!CONTROLIO((_io))->state.alarmdisconnectdone)                           \
89       *(_dodisconnect) |= ALARM_INTERNAL;                                      \
90                                                                                \
91    if ((_io)->state.protocol == SOCKS_TCP) {                                   \
92       if (!EXTERNALIO((_io))->state.alarmdisconnectdone)                       \
93          *(_dodisconnect) |= ALARM_EXTERNAL;                                   \
94    }                                                                           \
95    /* else: other side is udp, only control-connection is tcp. */              \
96 } while (/* CONSTCOND */ 0)
97 
98    DISCONNECT_NEEDED(io, &disconnect_needed);
99 
100    if (io->state.protocol == SOCKS_UDP) {
101 #if HAVE_CONTROL_CONNECTION
102       const connectionstate_t oldstate = io->state;
103 
104       /*
105        * For the fixed (not per-packet) monitormatch we always base ourselves
106        * on the control-connection, which is a TCP connection.
107        */
108       io->state.protocol = SOCKS_TCP;
109       io->state.command  = SOCKS_ACCEPT; /* XXX what about SOCKS_HOSTID? */
110 
111       newmatch = monitormatch(&io->control.host,
112                               sockaddr2sockshost(&io->control.laddr, NULL),
113                               &io->src.auth,
114                               &io->state);
115       io->state = oldstate;
116 
117 #else /* !HAVE_CONTROL_CONNECTION, and thus no TCP. */
118 
119       /*
120        * we do a monitormatch() on the udp-endpoints also, but that
121        * is done on a per-packet basis and that match is not saved any
122        * longer than what it takes to attach, update the data-counters,
123        * and detach.
124        */
125       newmatch = NULL;
126 
127 #endif /* !HAVE_CONTROL_CONNECTION */
128    }
129    else
130       newmatch = monitormatch(&io->src.host,
131                               &io->dst.host,
132                               &io->src.auth,
133                               &io->state);
134 
135    if (newmatch == NULL) {
136       /*
137        * No matching monitor now ...
138        */
139       if (rule->mstats_shmid == 0)
140          is_same = 1; /* ... and no matching monitor before.     */
141       else
142          is_same = 0; /* ... but were matching a monitor before. */
143    }
144    else {
145       /*
146        * Matching a monitor now ...
147        */
148       SASSERTX(newmatch->mstats_shmid != 0);
149 
150       if (rule->mstats_shmid == 0)
151          is_same = 0; /* ... but were not matching a monitor before. */
152       else
153          is_same = (rule->mstats_shmid == newmatch->mstats_shmid);
154    }
155 
156    slog(LOG_DEBUG,
157         "%s: previously matched mstats_shmid %lu, now matching monitor "
158         "#%lu with mstats_shmid %lu (%s) and alarmsconfigured = %lu",
159         function,
160         (unsigned long)rule->mstats_shmid,
161         newmatch == NULL ? 0 : (unsigned long)newmatch->number,
162         newmatch == NULL ? 0 : (unsigned long)newmatch->mstats_shmid,
163         is_same ? "same as now" : "different from before",
164         newmatch == NULL ? 0 : (unsigned long)newmatch->alarmsconfigured);
165 
166    if (rule->mstats_shmid == 0
167    &&  (newmatch == NULL || newmatch->mstats_shmid == 0))
168       return;
169 
170    cinfo.from = CONTROLIO(io)->raddr;
171    HOSTIDCOPY(&io->state, &cinfo);
172 
173    if (newmatch == NULL) {
174       /*
175        * remove any monitorstate belonging to old rule and return.
176        */
177       SASSERTX(rule->mstats_shmid != 0);
178 
179       if (rule->alarmsconfigured & ALARM_DISCONNECT) {
180          io_add_alarmdisconnects(io, function);
181 
182          /*
183           * disconnecting because alarm no longer configured, so
184           * don't confuse ourselves later by having this set to true.
185           */
186          CONTROLIO(io)->state.alarmdisconnectdone  = 0;
187          EXTERNALIO(io)->state.alarmdisconnectdone = 0;
188       }
189 
190       monitor_unuse(rule->mstats, &cinfo, sockscf.shmemfd);
191       sockd_shmdt(rule, SHMEM_MONITOR);
192 
193       SHMEM_CLEAR(rule, SHMEM_MONITOR, 1);
194       return;
195    }
196 
197    SASSERTX(newmatch->mstats       == NULL);
198    SASSERTX(newmatch->mstats_shmid != 0);
199 
200    if (is_same) {
201       /*
202        * Nothing much to do, but there are som state-variables global
203        * to this i/o process which may need updating based on this
204        * monitormatch (assuming the session was previously matched by a
205        * request process, or we are called due to a sighup).
206        *
207        * Do this at the same time as we do it in the case the new match
208        * differs from the old match, after the else-case.
209        */
210       SASSERTX(rule->mstats_shmid == newmatch->mstats_shmid);
211 
212       /*
213        * I/O process stays attached to its shared memory.
214        */
215       SASSERTX(rule->mstats != NULL);
216    }
217    else {
218       /*
219        * matched monitor changed.  Can happen if a SIGHUP occurred.
220        * Update rule for monitorstate belonging to new match.
221        */
222       rule_t tmprule;
223 
224       COPY_MONITORFIELDS(newmatch, &tmprule);
225       (void)sockd_shmat(&tmprule, SHMEM_MONITOR);
226 
227       COPY_MONITORFIELDS(rule,  &oldmonitor);
228       COPY_MONITORFIELDS(&tmprule, &newmonitor);
229 
230       monitor_move(&oldmonitor,
231                    &newmonitor,
232                    1,            /* i/o process stays attached.               */
233                    disconnect_needed,
234                    &cinfo,
235                    sockscf.shmemfd);
236 
237       COPY_MONITORFIELDS(&newmonitor, rule);
238 
239 #if DIAGNOSTIC
240       if (rule->mstats_shmid != 0)
241          SASSERTX(shmid2monitor(rule->mstats_shmid) != NULL);
242 #endif /* DIAGNOSTIC */
243 
244       /*
245        * i/o process stays attached to monitor objects, so no need
246        * for sockd_shmtd().
247        */
248    }
249 }
250 
251 
252 void
io_add_alarmdisconnects(io,reason)253 io_add_alarmdisconnects(io, reason)
254    sockd_io_t *io;
255    const char *reason;
256 {
257    const char *function = "io_add_alarmdisconnect()";
258    clientinfo_t cinfo;
259    rule_t *rule = IORULE(io);
260    size_t sidestodisconnect;
261 
262    if (rule->mstats_shmid == 0
263    || !(rule->alarmsconfigured & ALARM_DISCONNECT))
264       return;
265 
266    DISCONNECT_NEEDED(io, &sidestodisconnect);
267 
268    if (sidestodisconnect == 0)
269       return;
270 
271    /*
272     * at least one disconnect to do.
273     */
274 
275    cinfo.from = CONTROLIO(io)->raddr;
276    HOSTIDCOPY(&io->state, &cinfo);
277 
278    alarm_add_disconnect(1,
279                         rule,
280                         sidestodisconnect,
281                         &cinfo,
282                         reason,
283                         sockscf.shmemfd);
284 
285    if (sidestodisconnect & ALARM_INTERNAL)
286       CONTROLIO(io)->state.alarmdisconnectdone  = 1;
287 
288    if (sidestodisconnect & ALARM_EXTERNAL)
289       EXTERNALIO(io)->state.alarmdisconnectdone = 1;
290 }
291