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