]> code.citadel.org Git - citadel.git/blob - citadel/ipc_c_tcp.c
* socket stuff
[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                     "citadel unix domain socket type of thing"
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 char 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 sun;
132         int s;
133
134         memset(&sun, 0, sizeof(sun));
135         sun.sun_family = AF_UNIX;
136         strncpy(sun.sun_path, sockpath, sizeof sun.sun_path);
137
138         s = socket(AF_UNIX, SOCK_STREAM, 0);
139         if (s < 0) {
140                 fprintf(stderr, "Can't create socket: %s\n",
141                         strerror(errno));
142                 logoff(3);
143         }
144
145         if (connect(s, (struct sockaddr *) &sun, sizeof(sun)) < 0) {
146                 fprintf(stderr, "can't connect: %s\n",
147                         strerror(errno));
148                 logoff(3);
149         }
150
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)
273 {
274         int a;
275         char cithost[256];
276         int host_copied = 0;
277         char citport[256];
278         int port_copied = 0;
279         char socks4[256];
280         char buf[256];
281         struct passwd *p;
282         char sockpath[256];
283
284         strcpy(cithost, DEFAULT_HOST);  /* default host */
285         strcpy(citport, DEFAULT_PORT);  /* default port */
286         strcpy(socks4, "");     /* SOCKS v4 server */
287
288         for (a = 0; a < argc; ++a) {
289                 if (a == 0) {
290                         /* do nothing */
291                 } else if (!strcmp(argv[a], "-s")) {
292                         strcpy(socks4, argv[++a]);
293                 } else if (host_copied == 0) {
294                         host_copied = 1;
295                         strcpy(cithost, argv[a]);
296                 } else if (port_copied == 0) {
297                         port_copied = 1;
298                         strcpy(citport, argv[a]);
299                 }
300 /*
301    else {
302    fprintf(stderr,"%s: usage: ",argv[0]);
303    fprintf(stderr,"%s [host] [port] ",argv[0]);
304    fprintf(stderr,"[-s socks_server]\n");
305    logoff(2);
306    }
307  */
308         }
309
310         server_is_local = 0;
311         if ((!strcmp(cithost, "localhost"))
312             || (!strcmp(cithost, "127.0.0.1"))
313             || (!strcmp(cithost, "uds")))
314                 server_is_local = 1;
315
316         /* If we're using a unix domain socket we can do a bunch of stuff */
317         if (!strcmp(cithost, UDS)) {
318                 sprintf(sockpath, "%s/citadel.socket", BBSDIR);
319                 serv_sock = uds_connectsock(sockpath);
320                 return;
321         }
322
323         /* if not using a SOCKS proxy server, make the connection directly */
324         if (strlen(socks4) == 0) {
325                 serv_sock = connectsock(cithost, citport, "tcp");
326                 return;
327         }
328         /* if using SOCKS, connect first to the proxy... */
329         serv_sock = connectsock(socks4, "1080", "tcp");
330         printf("Connected to SOCKS proxy at %s.\n", socks4);
331         printf("Attaching to server...\r");
332         fflush(stdout);
333
334         snprintf(buf, sizeof buf, "%c%c",
335                  4,             /* version 4 */
336                  1);            /* method = connect */
337         serv_write(buf, 2);
338
339         numericize(buf, cithost, citport, "tcp");
340         serv_write(buf, 6);     /* port and address */
341
342         p = (struct passwd *) getpwuid(getuid());
343         serv_write(p->pw_name, strlen(p->pw_name) + 1);
344         /* user name */
345
346         serv_read(buf, 8);      /* get response */
347
348         if (buf[1] != 90) {
349                 printf("SOCKS server denied this proxy request.\n");
350                 logoff(3);
351         }
352 }
353
354 /*
355  * return the file descriptor of the server socket so we can select() on it.
356  */
357 int getsockfd(void)
358 {
359         return serv_sock;
360 }
361
362
363 /*
364  * return one character
365  */
366 char serv_getc(void)
367 {
368         char buf[2];
369         char ch;
370
371         serv_read(buf, 1);
372         ch = (int) buf[0];
373
374         return (ch);
375 }
376