* The size constant "256" which shows up everywhere as a buffer size has now
[citadel.git] / citadel / ipc_c_tcp.c
1 /*
2  * $Id$
3  * 
4  * Client-side IPC functions
5  *
6  */
7
8 #define UDS                     "_UDS_"
9
10 #define DEFAULT_HOST            UDS
11 #define DEFAULT_PORT            "citadel"
12
13
14 #include "sysdep.h"
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <signal.h>
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <sys/un.h>
24 #include <netdb.h>
25 #include <string.h>
26 #include <pwd.h>
27 #include <errno.h>
28 #include <stdarg.h>
29 #include "citadel.h"
30 #include "citadel_decls.h"
31 #include "ipc.h"
32 #ifndef HAVE_SNPRINTF
33 #include "snprintf.h"
34 #endif
35
36 /*
37  * If server_is_local is set to nonzero, the client assumes that it is running
38  * on the same computer as the server.  Several things happen when this is
39  * the case, including the ability to map a specific tty to a particular login
40  * session in the "<W>ho is online" listing, the ability to run external
41  * programs, and the ability to download files directly off the disk without
42  * having to first fetch them from the server.
43  * Set the flag to 1 if this IPC is local (as is the case with pipes, or a
44  * network session to the local machine) or 0 if the server is executing on
45  * a remote computer.
46  */
47 int server_is_local = 0;
48
49 #ifndef INADDR_NONE
50 #define INADDR_NONE 0xffffffff
51 #endif
52
53 int serv_sock;
54
55 void connection_died(void) {
56         fprintf(stderr, "\r"
57                         "Your connection to this Citadel server is broken.\n"
58                         "Please re-connect and log in again.\n");
59         logoff(3);
60 }
61
62
63 void timeout(int signum)
64 {
65         printf("\rConnection timed out.\n");
66         logoff(3);
67 }
68
69
70 int connectsock(char *host, char *service, char *protocol)
71 {
72         struct hostent *phe;
73         struct servent *pse;
74         struct protoent *ppe;
75         struct sockaddr_in sin;
76         int s, type;
77
78         memset(&sin, 0, sizeof(sin));
79         sin.sin_family = AF_INET;
80
81         pse = getservbyname(service, protocol);
82         if (pse) {
83                 sin.sin_port = pse->s_port;
84         } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) {
85                 fprintf(stderr, "Can't get %s service entry: %s\n",
86                         service, strerror(errno));
87                 logoff(3);
88         }
89         phe = gethostbyname(host);
90         if (phe) {
91                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
92         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
93                 fprintf(stderr, "Can't get %s host entry: %s\n",
94                         host, strerror(errno));
95                 logoff(3);
96         }
97         if ((ppe = getprotobyname(protocol)) == 0) {
98                 fprintf(stderr, "Can't get %s protocol entry: %s\n",
99                         protocol, strerror(errno));
100                 logoff(3);
101         }
102         if (!strcmp(protocol, "udp")) {
103                 type = SOCK_DGRAM;
104         } else {
105                 type = SOCK_STREAM;
106         }
107
108         s = socket(PF_INET, type, ppe->p_proto);
109         if (s < 0) {
110                 fprintf(stderr, "Can't create socket: %s\n", strerror(errno));
111                 logoff(3);
112         }
113         signal(SIGALRM, timeout);
114         alarm(30);
115
116         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
117                 fprintf(stderr, "can't connect to %s.%s: %s\n",
118                         host, service, strerror(errno));
119                 logoff(3);
120         }
121         alarm(0);
122         signal(SIGALRM, SIG_IGN);
123
124         return (s);
125 }
126
127 int uds_connectsock(char *sockpath)
128 {
129         struct sockaddr_un addr;
130         int s;
131
132
133         memset(&addr, 0, sizeof(addr));
134         addr.sun_family = AF_UNIX;
135         strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
136
137         s = socket(AF_UNIX, SOCK_STREAM, 0);
138         if (s < 0) {
139                 fprintf(stderr, "Can't create socket: %s\n",
140                         strerror(errno));
141                 logoff(3);
142         }
143
144         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
145                 fprintf(stderr, "can't connect: %s\n",
146                         strerror(errno));
147                 logoff(3);
148         }
149
150         server_is_local = 1;
151         return s;
152 }
153
154 /*
155  * convert service and host entries into a six-byte numeric in the format
156  * expected by a SOCKS v4 server
157  */
158 void numericize(char *buf, char *host, char *service, char *protocol)
159 {
160         struct hostent *phe;
161         struct servent *pse;
162         struct sockaddr_in sin;
163
164         memset(&sin, 0, sizeof(sin));
165         sin.sin_family = AF_INET;
166
167         pse = getservbyname(service, protocol);
168         if (pse) {
169                 sin.sin_port = pse->s_port;
170         } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) {
171                 fprintf(stderr, "Can't get %s service entry: %s\n",
172                         service, strerror(errno));
173                 logoff(3);
174         }
175         buf[1] = (((sin.sin_port) & 0xFF00) >> 8);
176         buf[0] = ((sin.sin_port) & 0x00FF);
177
178         phe = gethostbyname(host);
179         if (phe) {
180                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
181         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
182                 fprintf(stderr, "Can't get %s host entry: %s\n",
183                         host, strerror(errno));
184                 logoff(3);
185         }
186         buf[5] = ((sin.sin_addr.s_addr) & 0xFF000000) >> 24;
187         buf[4] = ((sin.sin_addr.s_addr) & 0x00FF0000) >> 16;
188         buf[3] = ((sin.sin_addr.s_addr) & 0x0000FF00) >> 8;
189         buf[2] = ((sin.sin_addr.s_addr) & 0x000000FF);
190 }
191
192 /*
193  * input binary data from socket
194  */
195 void serv_read(char *buf, int bytes)
196 {
197         int len, rlen;
198
199         len = 0;
200         while (len < bytes) {
201                 rlen = read(serv_sock, &buf[len], bytes - len);
202                 if (rlen < 1) {
203                         connection_died();
204                         return;
205                 }
206                 len = len + rlen;
207         }
208 }
209
210
211 /*
212  * send binary to server
213  */
214 void serv_write(char *buf, int nbytes)
215 {
216         int bytes_written = 0;
217         int retval;
218         while (bytes_written < nbytes) {
219                 retval = write(serv_sock, &buf[bytes_written],
220                                nbytes - bytes_written);
221                 if (retval < 1) {
222                         connection_died();
223                         return;
224                 }
225                 bytes_written = bytes_written + retval;
226         }
227 }
228
229
230
231 /*
232  * input string from socket - implemented in terms of serv_read()
233  */
234 void serv_gets(char *buf)
235 {
236         int i;
237
238         /* Read one character at a time.
239          */
240         for (i = 0;; i++) {
241                 serv_read(&buf[i], 1);
242                 if (buf[i] == '\n' || i == 255)
243                         break;
244         }
245
246         /* If we got a long line, discard characters until the newline.
247          */
248         if (i == 255)
249                 while (buf[i] != '\n')
250                         serv_read(&buf[i], 1);
251
252         /* Strip the trailing newline.
253          */
254         buf[i] = 0;
255 }
256
257
258 /*
259  * send line to server - implemented in terms of serv_write()
260  */
261 void serv_puts(char *buf)
262 {
263         /* printf("< %s\n", buf); */
264         serv_write(buf, strlen(buf));
265         serv_write("\n", 1);
266 }
267
268
269 /*
270  * attach to server
271  */
272 void attach_to_server(int argc, char **argv, char *hostbuf, char *portbuf)
273 {
274         int a;
275         char cithost[SIZ];
276         int host_copied = 0;
277         char citport[SIZ];
278         int port_copied = 0;
279         char socks4[SIZ];
280         char buf[SIZ];
281         struct passwd *p;
282         char sockpath[SIZ];
283
284         strcpy(cithost, DEFAULT_HOST);  /* default host */
285         strcpy(citport, DEFAULT_PORT);  /* default port */
286         strcpy(socks4, "");     /* SOCKS v4 server */
287
288
289         for (a = 0; a < argc; ++a) {
290                 if (a == 0) {
291                         /* do nothing */
292                 } else if (!strcmp(argv[a], "-s")) {
293                         strcpy(socks4, argv[++a]);
294                 } else if (host_copied == 0) {
295                         host_copied = 1;
296                         strcpy(cithost, argv[a]);
297                 } else if (port_copied == 0) {
298                         port_copied = 1;
299                         strcpy(citport, argv[a]);
300                 }
301 /*
302    else {
303    fprintf(stderr,"%s: usage: ",argv[0]);
304    fprintf(stderr,"%s [host] [port] ",argv[0]);
305    fprintf(stderr,"[-s socks_server]\n");
306    logoff(2);
307    }
308  */
309         }
310
311         if ((!strcmp(cithost, "localhost"))
312             || (!strcmp(cithost, "127.0.0.1")))
313                 server_is_local = 1;
314
315         /* If we're using a unix domain socket we can do a bunch of stuff */
316         if (!strcmp(cithost, UDS)) {
317                 sprintf(sockpath, "citadel.socket");
318                 serv_sock = uds_connectsock(sockpath);
319                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
320                 if (portbuf != NULL) strcpy(portbuf, sockpath);
321                 return;
322         }
323
324         /* if not using a SOCKS proxy server, make the connection directly */
325         if (strlen(socks4) == 0) {
326                 serv_sock = connectsock(cithost, citport, "tcp");
327                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
328                 if (portbuf != NULL) strcpy(portbuf, citport);
329                 return;
330         }
331         /* if using SOCKS, connect first to the proxy... */
332         serv_sock = connectsock(socks4, "1080", "tcp");
333         printf("Connected to SOCKS proxy at %s.\n", socks4);
334         printf("Attaching to server...\r");
335         fflush(stdout);
336
337         snprintf(buf, sizeof buf, "%c%c",
338                  4,             /* version 4 */
339                  1);            /* method = connect */
340         serv_write(buf, 2);
341
342         numericize(buf, cithost, citport, "tcp");
343         serv_write(buf, 6);     /* port and address */
344
345         p = (struct passwd *) getpwuid(getuid());
346         serv_write(p->pw_name, strlen(p->pw_name) + 1);
347         /* user name */
348
349         serv_read(buf, 8);      /* get response */
350
351         if (buf[1] != 90) {
352                 printf("SOCKS server denied this proxy request.\n");
353                 logoff(3);
354         }
355 }
356
357 /*
358  * return the file descriptor of the server socket so we can select() on it.
359  */
360 int getsockfd(void)
361 {
362         return serv_sock;
363 }
364
365
366 /*
367  * return one character
368  */
369 char serv_getc(void)
370 {
371         char buf[2];
372         char ch;
373
374         serv_read(buf, 1);
375         ch = (int) buf[0];
376
377         return (ch);
378 }
379