]> code.citadel.org Git - citadel.git/blob - citadel/ipc_c_tcp.c
* SSL/TLS support for the Citadel/UX wire protocol
[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 #include "tools.h"
33 #if defined(HAVE_OPENSSL) && defined(CIT_CLIENT)
34 #include "client_crypto.h"
35 #endif
36 #ifndef HAVE_SNPRINTF
37 #include "snprintf.h"
38 #endif
39
40 /*
41  * If server_is_local is set to nonzero, the client assumes that it is running
42  * on the same computer as the server.  Several things happen when this is
43  * the case, including the ability to map a specific tty to a particular login
44  * session in the "<W>ho is online" listing, the ability to run external
45  * programs, and the ability to download files directly off the disk without
46  * having to first fetch them from the server.
47  * Set the flag to 1 if this IPC is local (as is the case with pipes, or a
48  * network session to the local machine) or 0 if the server is executing on
49  * a remote computer.
50  */
51 int server_is_local = 0;
52
53 #ifndef INADDR_NONE
54 #define INADDR_NONE 0xffffffff
55 #endif
56
57 int serv_sock;
58
59 #if defined(HAVE_OPENSSL) && defined(CIT_CLIENT)
60 extern int ssl_is_connected;
61 #endif
62
63
64 void connection_died(void) {
65         fprintf(stderr, "\r"
66                         "Your connection to this Citadel server is broken.\n"
67                         "Please re-connect and log in again.\n");
68         logoff(3);
69 }
70
71
72 void timeout(int signum)
73 {
74         printf("\rConnection timed out.\n");
75         logoff(3);
76 }
77
78
79 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
80 {
81         struct hostent *phe;
82         struct servent *pse;
83         struct protoent *ppe;
84         struct sockaddr_in sin;
85         int s, type;
86
87         memset(&sin, 0, sizeof(sin));
88         sin.sin_family = AF_INET;
89
90         pse = getservbyname(service, protocol);
91         if (pse != NULL) {
92                 sin.sin_port = pse->s_port;
93         }
94         else if (atoi(service) > 0) {
95                 sin.sin_port = htons(atoi(service));
96         }
97         else {
98                 sin.sin_port = htons(defaultPort);
99         }
100         phe = gethostbyname(host);
101         if (phe) {
102                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
103         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
104                 fprintf(stderr, "Can't get %s host entry: %s\n",
105                         host, strerror(errno));
106                 logoff(3);
107         }
108         if ((ppe = getprotobyname(protocol)) == 0) {
109                 fprintf(stderr, "Can't get %s protocol entry: %s\n",
110                         protocol, strerror(errno));
111                 logoff(3);
112         }
113         if (!strcmp(protocol, "udp")) {
114                 type = SOCK_DGRAM;
115         } else {
116                 type = SOCK_STREAM;
117         }
118
119         s = socket(PF_INET, type, ppe->p_proto);
120         if (s < 0) {
121                 fprintf(stderr, "Can't create socket: %s\n", strerror(errno));
122                 logoff(3);
123         }
124         signal(SIGALRM, timeout);
125         alarm(30);
126
127         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
128                 fprintf(stderr, "can't connect to %s.%s: %s\n",
129                         host, service, strerror(errno));
130                 logoff(3);
131         }
132         alarm(0);
133         signal(SIGALRM, SIG_IGN);
134
135         return (s);
136 }
137
138 int uds_connectsock(char *sockpath)
139 {
140         struct sockaddr_un addr;
141         int s;
142
143         memset(&addr, 0, sizeof(addr));
144         addr.sun_family = AF_UNIX;
145         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
146
147         s = socket(AF_UNIX, SOCK_STREAM, 0);
148         if (s < 0) {
149                 fprintf(stderr, "Can't create socket: %s\n",
150                         strerror(errno));
151                 logoff(3);
152         }
153
154         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
155                 fprintf(stderr, "can't connect: %s\n",
156                         strerror(errno));
157                 logoff(3);
158         }
159
160         server_is_local = 1;
161         return s;
162 }
163
164
165 /*
166  * input binary data from socket
167  */
168 void serv_read(char *buf, int bytes)
169 {
170         int len, rlen;
171
172 #if defined(HAVE_OPENSSL) && defined(CIT_CLIENT)
173         if (ssl_is_connected) {
174                 serv_read_ssl(buf, bytes);
175                 return;
176         }
177 #endif
178         len = 0;
179         while (len < bytes) {
180                 rlen = read(serv_sock, &buf[len], bytes - len);
181                 if (rlen < 1) {
182                         connection_died();
183                         return;
184                 }
185                 len += rlen;
186         }
187 }
188
189
190 /*
191  * send binary to server
192  */
193 void serv_write(char *buf, int nbytes)
194 {
195         int bytes_written = 0;
196         int retval;
197
198 #if defined(HAVE_OPENSSL) && defined(CIT_CLIENT)
199         if (ssl_is_connected) {
200                 serv_write_ssl(buf, nbytes);
201                 return;
202         }
203 #endif
204         while (bytes_written < nbytes) {
205                 retval = write(serv_sock, &buf[bytes_written],
206                                nbytes - bytes_written);
207                 if (retval < 1) {
208                         connection_died();
209                         return;
210                 }
211                 bytes_written += retval;
212         }
213 }
214
215
216
217 /*
218  * input string from socket - implemented in terms of serv_read()
219  */
220 void serv_gets(char *buf)
221 {
222         int i;
223
224         /* Read one character at a time.
225          */
226         for (i = 0;; i++) {
227                 serv_read(&buf[i], 1);
228                 if (buf[i] == '\n' || i == (SIZ-1))
229                         break;
230         }
231
232         /* If we got a long line, discard characters until the newline.
233          */
234         if (i == (SIZ-1))
235                 while (buf[i] != '\n')
236                         serv_read(&buf[i], 1);
237
238         /* Strip the trailing newline.
239          */
240         buf[i] = 0;
241 }
242
243
244 /*
245  * send line to server - implemented in terms of serv_write()
246  */
247 void serv_puts(char *buf)
248 {
249         /* printf("< %s\n", buf); */
250         serv_write(buf, strlen(buf));
251         serv_write("\n", 1);
252 }
253
254
255 /*
256  * attach to server
257  */
258 void attach_to_server(int argc, char **argv, char *hostbuf, char *portbuf)
259 {
260         int a;
261         char cithost[SIZ];
262         char citport[SIZ];
263         char sockpath[SIZ];
264
265         strcpy(cithost, DEFAULT_HOST);  /* default host */
266         strcpy(citport, DEFAULT_PORT);  /* default port */
267
268         for (a = 0; a < argc; ++a) {
269                 if (a == 0) {
270                         /* do nothing */
271                 } else if (a == 1) {
272                         strcpy(cithost, argv[a]);
273                 } else if (a == 2) {
274                         strcpy(citport, argv[a]);
275                 }
276                 else {
277                         fprintf(stderr,"%s: usage: ",argv[0]);
278                         fprintf(stderr,"%s [host] [port] ",argv[0]);
279                         logoff(2);
280                 }
281         }
282
283         if ((!strcmp(cithost, "localhost"))
284            || (!strcmp(cithost, "127.0.0.1"))) {
285                 server_is_local = 1;
286         }
287
288         /* If we're using a unix domain socket we can do a bunch of stuff */
289         if (!strcmp(cithost, UDS)) {
290                 sprintf(sockpath, "citadel.socket");
291                 serv_sock = uds_connectsock(sockpath);
292                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
293                 if (portbuf != NULL) strcpy(portbuf, sockpath);
294                 return;
295         }
296
297         serv_sock = connectsock(cithost, citport, "tcp", 504);
298         if (hostbuf != NULL) strcpy(hostbuf, cithost);
299         if (portbuf != NULL) strcpy(portbuf, citport);
300         return;
301 }
302
303 /*
304  * return the file descriptor of the server socket so we can select() on it.
305  */
306 int getsockfd(void)
307 {
308         return serv_sock;
309 }
310
311
312 /*
313  * return one character
314  */
315 char serv_getc(void)
316 {
317         char buf[2];
318         char ch;
319
320         serv_read(buf, 1);
321         ch = (int) buf[0];
322
323         return (ch);
324 }