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