1 /* proxy.c - proxy support functions
2 *
3 * Copyright (c) 1994-2008 Carnegie Mellon University. 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 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. The name "Carnegie Mellon University" must not be used to
18 * endorse or promote products derived from this software without
19 * prior written permission. For permission or any legal
20 * details, please contact
21 * Carnegie Mellon University
22 * Center for Technology Transfer and Enterprise Creation
23 * 4615 Forbes Avenue
24 * Suite 302
25 * Pittsburgh, PA 15213
26 * (412) 268-7393, fax: (412) 268-7395
27 * innovation@andrew.cmu.edu
28 *
29 * 4. Redistributions of any form whatsoever must retain the following
30 * acknowledgment:
31 * "This product includes software developed by Computing Services
32 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33 *
34 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41 */
42
43 #include <config.h>
44
45 #include <errno.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <sysexits.h>
50 #include <syslog.h>
51 #include <sys/un.h>
52
53 #include "backend.h"
54 #include "global.h"
55 #include "mupdate-client.h"
56 #include "proxy.h"
57 #include "xmalloc.h"
58 #include "xstrlcpy.h"
59
60 /* generated headers are not necessarily in current directory */
61 #include "imap/imap_err.h"
62
proxy_adddest(struct dest ** dlist,const char * rcpt,int rcpt_num,const char * server,const char * authas)63 EXPORTED void proxy_adddest(struct dest **dlist, const char *rcpt, int rcpt_num,
64 const char *server, const char *authas)
65 {
66 struct dest *d;
67
68 /* see if we currently have a 'mailboxdata->server'/'authas'
69 combination. */
70 for (d = *dlist; d != NULL; d = d->next) {
71 if (!strcmp(d->server, server) &&
72 !strcmp(d->authas, authas ? authas : "")) break;
73 }
74
75 if (d == NULL) {
76 /* create a new one */
77 d = xmalloc(sizeof(struct dest));
78 strlcpy(d->server, server, sizeof(d->server));
79 strlcpy(d->authas, authas ? authas : "", sizeof(d->authas));
80 d->rnum = 0;
81 d->to = NULL;
82 d->next = *dlist;
83 *dlist = d;
84 }
85
86 if (rcpt) {
87 struct rcpt *new_rcpt = xmalloc(sizeof(struct rcpt));
88
89 strlcpy(new_rcpt->rcpt, rcpt, sizeof(new_rcpt->rcpt));
90 new_rcpt->rcpt_num = rcpt_num;
91
92 /* add rcpt to d */
93 d->rnum++;
94 new_rcpt->next = d->to;
95 d->to = new_rcpt;
96 }
97 }
98
proxy_downserver(struct backend * s)99 EXPORTED void proxy_downserver(struct backend *s)
100 {
101 if (!s || (s->sock == -1)) {
102 /* already disconnected */
103 return;
104 }
105
106 /* need to logout of server */
107 backend_disconnect(s);
108
109 /* clear any references to this backend */
110 if (s->inbox && (s == *(s->inbox))) *(s->inbox) = NULL;
111 if (s->current && (s == *(s->current))) *(s->current) = NULL;
112 s->inbox = s->current = NULL;
113
114 /* remove the timeout */
115 if (s->timeout) prot_removewaitevent(s->clientin, s->timeout);
116 s->timeout = NULL;
117 s->clientin = NULL;
118 }
119
120 static struct prot_waitevent *
backend_timeout(struct protstream * s,struct prot_waitevent * ev,void * rock)121 backend_timeout(struct protstream *s __attribute__((unused)),
122 struct prot_waitevent *ev, void *rock)
123 {
124 struct backend *be = (struct backend *) rock;
125 int is_active = (be->context ? *((int *) be->context) : 0);
126
127 if ((!be->current || (be != *(be->current))) && !is_active) {
128 /* server is not our current server, and idle too long.
129 * down the backend server (removes the event as a side-effect)
130 */
131 proxy_downserver(be);
132 return NULL;
133 }
134 else {
135 /* it will timeout in IDLE_TIMEOUT seconds from now */
136 ev->mark = time(NULL) + IDLE_TIMEOUT;
137 return ev;
138 }
139 }
140
141 /* return the connection to the server */
proxy_findserver(const char * server,struct protocol_t * prot,const char * userid,struct backend *** cache,struct backend ** current,struct backend ** inbox,struct protstream * clientin)142 EXPORTED struct backend * proxy_findserver(const char *server, /* hostname of backend */
143 struct protocol_t *prot, /* protocol we're speaking */
144 const char *userid, /* proxy as userid (ext form)*/
145 struct backend ***cache, /* ptr to backend cache */
146 struct backend **current, /* ptr to current backend */
147 struct backend **inbox, /* ptr to inbox backend */
148 struct protstream *clientin) /* protstream from client to
149 proxy (if non-NULL a timeout
150 will be setup) */
151 {
152 int i = 0;
153 struct backend *ret = NULL;
154
155 if (current && *current && !strcmp(server, (*current)->hostname)
156 && !strcmp(prot->service, (*current)->prot->service)) {
157 /* this is our current backend */
158 return *current;
159 }
160
161 /* check if we already a connection to this backend */
162 while (cache && *cache && (*cache)[i]) {
163 if ((!strcmp(server, ((*cache)[i])->hostname) &&
164 !strcmp(prot->service, ((*cache)[i])->prot->service))) {
165 ret = (*cache)[i];
166 /* ping/noop the server */
167 if ((ret->sock != -1) && backend_ping(ret, userid)) {
168 backend_disconnect(ret);
169 }
170 break;
171 }
172 i++;
173 }
174
175 if (!ret || (ret->sock == -1)) {
176 /* need to (re)establish connection to server or create one */
177 ret = backend_connect(ret, server, prot, userid, NULL, NULL, -1);
178 if (!ret) return NULL;
179
180 if (clientin) {
181 /* add the timeout */
182 ret->clientin = clientin;
183 ret->timeout = prot_addwaitevent(clientin,
184 time(NULL) + IDLE_TIMEOUT,
185 backend_timeout, ret);
186
187 ret->timeout->mark = time(NULL) + IDLE_TIMEOUT;
188 }
189 }
190
191 ret->current = current;
192 ret->inbox = inbox;
193
194 /* insert server in list of cache connections */
195 if (cache && (!*cache || !(*cache)[i])) {
196 *cache = (struct backend **)
197 xrealloc(*cache, (i + 2) * sizeof(struct backend *));
198 (*cache)[i] = ret;
199 (*cache)[i + 1] = NULL;
200 }
201
202 return ret;
203 }
204
205 /*
206 * Check a protgroup for input.
207 *
208 * Input from serverin is sent to clientout.
209 * If serverout is non-NULL:
210 * - input from clientin is sent to serverout.
211 * - returns -1 if clientin or serverin closed, otherwise returns 0.
212 * If serverout is NULL:
213 * - returns 1 if input from clientin is pending, otherwise returns 0.
214 */
proxy_check_input(struct protgroup * protin,struct protstream * clientin,struct protstream * clientout,struct protstream * serverin,struct protstream * serverout,unsigned long timeout_sec)215 EXPORTED int proxy_check_input(struct protgroup *protin,
216 struct protstream *clientin,
217 struct protstream *clientout,
218 struct protstream *serverin,
219 struct protstream *serverout,
220 unsigned long timeout_sec)
221 {
222 struct protgroup *protout = NULL;
223 struct timeval timeout = { timeout_sec, 0 };
224 int n, ret = 0;
225
226 n = prot_select(protin, PROT_NO_FD, &protout, NULL,
227 timeout_sec ? &timeout : NULL);
228 if (n == -1 && errno != EINTR) {
229 syslog(LOG_ERR, "prot_select() failed in proxy_check_input(): %m");
230 fatal("prot_select() failed in proxy_check_input()", EX_TEMPFAIL);
231 }
232
233 if (n && protout) {
234 /* see who has input */
235 for (; n; n--) {
236 struct protstream *pin = protgroup_getelement(protout, n-1);
237 struct protstream *pout = NULL;
238
239 if (pin == clientin) {
240 /* input from client */
241 if (serverout) {
242 /* stream it to server */
243 pout = serverout;
244 } else {
245 /* notify the caller */
246 ret = 1;
247 }
248 }
249 else if (pin == serverin) {
250 /* input from server, stream it to client */
251 pout = clientout;
252 }
253 else {
254 /* XXX shouldn't get here !!! */
255 fatal("unknown protstream returned by prot_select in proxy_check_input()",
256 EX_SOFTWARE);
257 }
258
259 if (pout) {
260 const char *err;
261
262 do {
263 char buf[4096];
264 int c = prot_read(pin, buf, sizeof(buf));
265
266 if (c == 0 || c < 0) break;
267 prot_write(pout, buf, c);
268 } while (pin->cnt > 0);
269
270 if ((err = prot_error(pin)) != NULL) {
271 if (serverout && !strcmp(err, PROT_EOF_STRING)) {
272 /* we're pipelining, and the connection closed */
273 ret = -1;
274 }
275 else {
276 /* uh oh, we're not happy */
277 fatal("Lost connection to input stream",
278 EX_UNAVAILABLE);
279 }
280 }
281 else {
282 return 0;
283 }
284 }
285 }
286
287 protgroup_free(protout);
288 }
289
290 return ret;
291 }
292