2dd4af9d1d726d9d15cdf093b202a1c1fb0888a1
[citadel.git] / ctdlphp / sessionproxy.c
1 /*
2  * $Id$
3  *
4  * Session proxy for Citadel PHP bindings
5  *
6  * This is an unfinished session proxy ... it is a C version of the
7  * session proxy implemented in sessionproxy.php ... that version is pure PHP
8  * so we should probably stick with it as long as it works.
9  *
10  * Copyright (c) 2003 by Art Cancro <ajc@uncensored.citadel.org>
11  * This program is released under the terms of the GNU General Public License.
12  */
13
14 #define SIZ 4096
15
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <fcntl.h>
21 #include <signal.h>
22 #include <sys/types.h>
23 #include <sys/wait.h>
24 #include <sys/socket.h>
25 #include <sys/time.h>
26 #include <limits.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <sys/un.h>
30 #include <netdb.h>
31 #include <string.h>
32 #include <pwd.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <pthread.h>
36 #include <signal.h>
37
38
39 #ifndef INADDR_NONE
40 #define INADDR_NONE 0xffffffff
41 #endif
42
43
44 void timeout(int signum)
45 {
46         fprintf(stderr, "Connection timed out.\n");
47         exit(3);
48 }
49
50
51 /*
52  * Connect a unix domain socket
53  */
54 int uds_connectsock(char *sockpath)
55 {
56         struct sockaddr_un addr;
57         int s;
58
59         memset(&addr, 0, sizeof(addr));
60         addr.sun_family = AF_UNIX;
61         strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
62
63         s = socket(AF_UNIX, SOCK_STREAM, 0);
64         if (s < 0) {
65                 fprintf(stderr, "Can't create socket: %s\n",
66                         strerror(errno));
67                 return(-1);
68         }
69
70         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
71                 fprintf(stderr, "Can't connect: %s\n",
72                         strerror(errno));
73                 close(s);
74                 return(-1);
75         }
76
77         return s;
78 }
79
80
81 /*
82  * Connect a TCP/IP socket
83  */
84 int tcp_connectsock(char *host, char *service)
85 {
86         struct hostent *phe;
87         struct servent *pse;
88         struct protoent *ppe;
89         struct sockaddr_in sin;
90         int s;
91
92         memset(&sin, 0, sizeof(sin));
93         sin.sin_family = AF_INET;
94
95         pse = getservbyname(service, "tcp");
96         if (pse) {
97                 sin.sin_port = pse->s_port;
98         } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) {
99                 fprintf(stderr, "Can't get %s service entry\n", service);
100                 return (-1);
101         }
102         phe = gethostbyname(host);
103         if (phe) {
104                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
105         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
106                 fprintf(stderr, "Can't get %s host entry: %s\n",
107                         host, strerror(errno));
108                 return (-1);
109         }
110         if ((ppe = getprotobyname("tcp")) == 0) {
111                 fprintf(stderr, "Can't get TCP protocol entry: %s\n",
112                         strerror(errno));
113                 return (-1);
114         }
115
116         s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
117         if (s < 0) {
118                 fprintf(stderr, "Can't create socket: %s\n", strerror(errno));
119                 return (-1);
120         }
121         signal(SIGALRM, timeout);
122         alarm(30);
123
124         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
125                 fprintf(stderr, "Can't connect to %s.%s: %s\n",
126                         host, service, strerror(errno));
127                 close(s);
128                 return (-1);
129         }
130         alarm(0);
131         signal(SIGALRM, SIG_IGN);
132
133         return (s);
134 }
135
136
137
138
139 /*
140  * Input binary data from socket
141  */
142 int sock_read(int sock, char *buf, int bytes)
143 {
144         int len, rlen;
145
146         len = 0;
147         while (len < bytes) {
148                 rlen = read(sock, &buf[len], bytes - len);
149                 if (rlen < 1) {
150                         fprintf(stderr, "Server connection broken: %s\n",
151                                 strerror(errno));
152                         return(-1);
153                 }
154                 len = len + rlen;
155         }
156         return(len);
157 }
158
159
160 /*
161  * input string from pipe
162  */
163 int sock_gets(int sock, char *strbuf)
164 {
165         int ch, len;
166         char buf[2];
167
168         len = 0;
169         strcpy(strbuf, "");
170         do {
171                 if (sock_read(sock, &buf[0], 1) < 0) return(-1);
172                 ch = buf[0];
173                 strbuf[len++] = ch;
174         } while ((ch != 10) && (ch != 0) && (len < (SIZ-1)));
175         if (strbuf[len-1] == 10) strbuf[--len] = 0;
176         if (strbuf[len-1] == 13) strbuf[--len] = 0;
177         return(len);
178 }
179
180
181
182 /*
183  * send binary to server
184  */
185 int sock_write(int sock, char *buf, int nbytes)
186 {
187         int bytes_written = 0;
188         int retval;
189         while (bytes_written < nbytes) {
190                 retval = write(sock, &buf[bytes_written],
191                                nbytes - bytes_written);
192                 if (retval < 1) {
193                         fprintf(stderr, "Server connection broken: %s\n",
194                                 strerror(errno));
195                         return(-1);
196                 }
197                 bytes_written = bytes_written + retval;
198         }
199         return(bytes_written);
200 }
201
202
203 /*
204  * send line to server
205  */
206 int sock_puts(int sock, char *string)
207 {
208         char buf[SIZ];
209
210         sprintf(buf, "%s\n", string);
211         return sock_write(sock, buf, strlen(buf));
212 }
213
214
215 /*
216  * Create a Unix domain socket and listen on it
217  */
218 int ig_uds_server(char *sockpath, int queue_len)
219 {
220         struct sockaddr_un addr;
221         int s;
222         int i;
223         int actual_queue_len;
224
225         actual_queue_len = queue_len;
226         if (actual_queue_len < 5) actual_queue_len = 5;
227
228         i = unlink(sockpath);
229         if (i != 0) if (errno != ENOENT) {
230                 fprintf(stderr, "can't unlink %s: %s\n",
231                         sockpath, strerror(errno));
232                 return(-1);
233         }
234
235         memset(&addr, 0, sizeof(addr));
236         addr.sun_family = AF_UNIX;
237         strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
238
239         s = socket(AF_UNIX, SOCK_STREAM, 0);
240         if (s < 0) {
241                 fprintf(stderr, "Can't create a socket: %s\n",
242                         strerror(errno));
243                 return(-1);
244         }
245
246         if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
247                 fprintf(stderr, "Can't bind: %s\n",
248                         strerror(errno));
249                 return(-1);
250         }
251
252         if (listen(s, actual_queue_len) < 0) {
253                 fprintf(stderr, "Can't listen: %s\n", strerror(errno));
254                 return(-1);
255         }
256
257         chmod(sockpath, 0700);  /* only me me me can talk to this */
258         return(s);
259 }
260
261
262
263 /*
264  * main loop
265  */
266 int main(int argc, char **argv) {
267
268         char buf[SIZ];
269         char dbuf[SIZ];
270         int i, f;
271         int ctdl_sock;
272         int listen_sock;
273         int cmd_sock;
274
275         /* Fail if we weren't supplied with the right number of arguments
276          */
277         if (argc != 2) {
278                 exit(1);
279         }
280
281         /* Fail if we can't connect to Citadel
282          */
283         ctdl_sock = uds_connectsock("/appl/citadel/citadel.socket");
284         if (ctdl_sock < 0) {
285                 exit(2);
286         }
287
288         /* Fail if we can't read the server greeting message
289          */
290         if (sock_gets(ctdl_sock, buf) < 0) {
291                 exit(3);
292         }
293
294         /* Fail if the server isn't giving us an error-free startup
295          */
296         if (buf[0] != '2') {
297                 exit(4);
298         }
299
300         /* Now we're solid with the Citadel server.  Nice.  It's time to
301          * open our proxy socket so PHP can talk to us.  Fail if we can't
302          * set this up.
303          */
304         listen_sock = ig_uds_server(argv[1], 5);
305         if (listen_sock < 0) {
306                 close(ctdl_sock);
307                 exit(5);
308         }
309
310         /* The socket is ready to listen for connections, so it's time for
311          * this program to go into the background.  Fork, then close all file
312          * descriptors so that the PHP script that called us can continue
313          * its processing.
314          */
315         f = fork();
316         if (f < 0) {
317                 close(ctdl_sock);
318                 close(listen_sock);
319                 unlink(argv[1]);
320                 exit(6);
321         }
322         if (f != 0) {
323                 exit(0);
324         }
325         if (f == 0) {
326                 setpgrp();
327                 for (i=0; i<256; ++i) {
328                         /* Close fd's so PHP doesn't get all, like, whatever */
329                         if ( (i != ctdl_sock) && (i != listen_sock) ) {
330                                 close(i);
331                         }
332                 }
333         }
334
335         /* Listen for connections. */
336
337         signal(SIGPIPE, SIG_IGN);
338         while (cmd_sock = accept(listen_sock, NULL, 0), cmd_sock >= 0) {
339
340                 while (sock_gets(cmd_sock, buf) >= 0) {
341                         if (sock_puts(ctdl_sock, buf) < 0) goto CTDL_BAIL;
342                         if (sock_gets(ctdl_sock, buf) < 0) goto CTDL_BAIL;
343                         sock_puts(cmd_sock, buf);
344
345                         if (buf[0] == '1') do {
346                                 if (sock_gets(ctdl_sock, dbuf) < 0) {
347                                         goto CTDL_BAIL;
348                                 }
349                                 sock_puts(cmd_sock, dbuf);
350                         } while (strcmp(dbuf, "000"));
351
352                         else if (buf[0] == '4') do {
353                                 sock_gets(cmd_sock, dbuf);
354                                 if (sock_puts(ctdl_sock, dbuf) < 0) {
355                                         goto CTDL_BAIL;
356                                 }
357                         } while (strcmp(dbuf, "000"));
358
359                 }
360                 close(cmd_sock);
361         }
362
363 CTDL_BAIL:
364         /* Clean up and go away.
365          */
366         close(ctdl_sock);
367         close(listen_sock);
368         unlink(argv[1]);
369         exit(0);
370 }