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