]> code.citadel.org Git - citadel.git/blob - citadel/clientsocket.c
sock_connect() - if first char of hostname is '/' assume it is a unix domain socket...
[citadel.git] / citadel / clientsocket.c
1 /*
2  * This module handles client-side sockets opened by the Citadel server (for
3  * the client side of Internet protocols, etc.)   It does _not_ handle client
4  * sockets for the Citadel client; for that you must look in ipc_c_tcp.c
5  * (which, uncoincidentally, bears a striking similarity to this file).
6  *
7  * Copyright (c) 1987-2009 by the citadel.org team
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 3 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "sysdep.h"
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <signal.h>
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <sys/un.h>
34 #include <arpa/inet.h>
35 #include <netdb.h>
36 #include <string.h>
37 #include <pwd.h>
38 #include <errno.h>
39 #include <stdarg.h>
40 #include <libcitadel.h>
41 #include "citadel.h"
42 #include "server.h"
43 #ifndef HAVE_SNPRINTF
44 #include "snprintf.h"
45 #endif
46 #include "sysdep_decls.h"
47 #include "config.h"
48 #include "clientsocket.h"
49
50 #include "ctdl_module.h"
51
52 #ifndef INADDR_NONE
53 #define INADDR_NONE 0xffffffff
54 #endif
55
56
57 int uds_sock_connect(char *sockpath)
58 {
59         struct sockaddr_un addr;
60         int s;
61
62         memset(&addr, 0, sizeof(addr));
63         addr.sun_family = AF_UNIX;
64         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
65
66         s = socket(AF_UNIX, SOCK_STREAM, 0);
67         if (s < 0) {
68                 return(-1);
69         }
70
71         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
72                 close(s);
73                 return(-1);
74         }
75
76         return s;
77 }
78
79
80 int sock_connect(char *host, char *service, char *protocol)
81 {
82         struct hostent *phe;
83         struct servent *pse;
84         struct protoent *ppe;
85         struct sockaddr_in sin;
86         struct sockaddr_in egress_sin;
87         int s, type;
88
89         if ((host == NULL) || IsEmptyStr(host)) 
90                 return(-1);
91
92         if (host[0] == '/') {
93                 return uds_sock_connect(host);
94         }
95
96         if ((service == NULL) || IsEmptyStr(service)) 
97                 return(-1);
98         if ((protocol == NULL) || IsEmptyStr(protocol)) 
99                 return(-1);
100
101         memset(&sin, 0, sizeof(sin));
102         sin.sin_family = AF_INET;
103
104         pse = getservbyname(service, protocol);
105         if (pse) {
106                 sin.sin_port = pse->s_port;
107         } else if ((sin.sin_port = htons((u_short) atoi(service))) == 0) {
108                 CtdlLogPrintf(CTDL_CRIT, "Can't get %s service entry: %s\n",
109                         service, strerror(errno));
110                 return(-1);
111         }
112         phe = gethostbyname(host);
113         if (phe) {
114                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
115         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
116                 CtdlLogPrintf(CTDL_ERR, "Can't get %s host entry: %s\n",
117                         host, strerror(errno));
118                 return(-1);
119         }
120         if ((ppe = getprotobyname(protocol)) == 0) {
121                 CtdlLogPrintf(CTDL_CRIT, "Can't get %s protocol entry: %s\n",
122                         protocol, strerror(errno));
123                 return(-1);
124         }
125         if (!strcmp(protocol, "udp")) {
126                 type = SOCK_DGRAM;
127         } else {
128                 type = SOCK_STREAM;
129         }
130
131         s = socket(PF_INET, type, ppe->p_proto);
132         if (s < 0) {
133                 CtdlLogPrintf(CTDL_CRIT, "Can't create socket: %s\n", strerror(errno));
134                 return(-1);
135         }
136
137         /* If citserver is bound to a specific IP address on the host, make
138          * sure we use that address for outbound connections.
139          */
140         memset(&egress_sin, 0, sizeof(egress_sin));
141         egress_sin.sin_family = AF_INET;
142         if (!IsEmptyStr(config.c_ip_addr)) {
143                 egress_sin.sin_addr.s_addr = inet_addr(config.c_ip_addr);
144                 if (egress_sin.sin_addr.s_addr == !INADDR_ANY) {
145                         egress_sin.sin_addr.s_addr = INADDR_ANY;
146                 }
147
148                 /* If this bind fails, no problem; we can still use INADDR_ANY */
149                 bind(s, (struct sockaddr *)&egress_sin, sizeof(egress_sin));
150         }
151
152         /* Now try to connect to the remote host. */
153         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
154                 CtdlLogPrintf(CTDL_ERR, "Can't connect to %s:%s: %s\n",
155                         host, service, strerror(errno));
156                 close(s);
157                 return(-1);
158         }
159
160         return (s);
161 }
162
163
164
165 /*
166  * Read data from the client socket.
167  *
168  * sock         socket fd to read from
169  * buf          buffer to read into 
170  * bytes        number of bytes to read
171  * timeout      Number of seconds to wait before timing out
172  *
173  * Possible return values:
174  *      1       Requested number of bytes has been read.
175  *      0       Request timed out.
176  *      -1      Connection is broken, or other error.
177  */
178 int socket_read_blob(int *Socket, 
179                      StrBuf *Target, 
180                      int bytes, 
181                      int timeout)
182 {
183         CitContext *CCC=CC;
184         const char *Error;
185         int retval = 0;
186
187
188         retval = StrBufReadBLOBBuffered(Target, 
189                                         CCC->sReadBuf,
190                                         &CCC->sPos,
191                                         Socket,
192                                         1, 
193                                         bytes,
194                                         O_TERM,
195                                         &Error);
196         if (retval < 0) {
197                 CtdlLogPrintf(CTDL_CRIT, 
198                               "%s failed: %s\n",
199                               __FUNCTION__,
200                               Error);
201         }
202         return retval;
203 }
204
205
206 int sock_read_to(int *sock, char *buf, int bytes, int timeout, int keep_reading_until_full)
207 {
208         CitContext *CCC=CC;
209         int rc;
210
211         FlushStrBuf(CCC->MigrateBuf);
212         rc = socket_read_blob(sock, 
213                               CCC->sMigrateBuf, 
214                               bytes, 
215                               timeout);
216         if (rc < 0)
217         {
218                 *buf = '\0';
219                 return rc;
220         }
221         else
222         {
223                 if (StrLength(CCC->MigrateBuf) < bytes)
224                         bytes = StrLength(CCC->MigrateBuf);
225                 memcpy(buf, 
226                        ChrPtr(CCC->MigrateBuf),
227                        bytes);
228
229                 FlushStrBuf(CCC->MigrateBuf);
230                 return rc;
231         }
232 }
233
234
235 int CtdlSockGetLine(int *sock, StrBuf * Target, int nSec)
236 {
237         CitContext *CCC=CC;
238         const char *Error;
239         int rc;
240
241         FlushStrBuf(Target);
242         rc = StrBufTCP_read_buffered_line_fast(Target, 
243                                                CCC->sReadBuf,
244                                                &CCC->sPos,
245                                                sock, nSec, 1, &Error);
246         if ((rc < 0) && (Error != NULL))
247                 CtdlLogPrintf(CTDL_CRIT, 
248                               "%s failed: %s\n",
249                               __FUNCTION__,
250                               Error);
251         return rc;
252 }
253
254
255 /*
256  * client_getln()   ...   Get a LF-terminated line of text from the client.
257  * (This is implemented in terms of client_read() and could be
258  * justifiably moved out of sysdep.c)
259  */
260 int sock_getln(int *sock, char *buf, int bufsize)
261 {
262         int i, retval;
263         CitContext *CCC=CC;
264         const char *pCh;
265
266         FlushStrBuf(CCC->sMigrateBuf);
267         retval = CtdlSockGetLine(sock, CCC->sMigrateBuf, 5);
268
269         i = StrLength(CCC->sMigrateBuf);
270         pCh = ChrPtr(CCC->sMigrateBuf);
271
272         memcpy(buf, pCh, i + 1);
273
274         FlushStrBuf(CCC->sMigrateBuf);
275         if (retval < 0) {
276                 safestrncpy(&buf[i], "000", bufsize - i);
277                 i += 3;
278         }
279         return i;
280 }
281
282
283 /*
284  * sock_read() - input binary data from socket.
285  * Returns the number of bytes read, or -1 for error.
286  */
287 INLINE int sock_read(int *sock, char *buf, int bytes, int keep_reading_until_full)
288 {
289         return sock_read_to(sock, buf, bytes, CLIENT_TIMEOUT, keep_reading_until_full);
290 }
291
292
293 /*
294  * sock_write() - send binary to server.
295  * Returns the number of bytes written, or -1 for error.
296  */
297 int sock_write(int *sock, const char *buf, int nbytes) 
298 { return sock_write_timeout(sock, buf, nbytes, 50); }
299 int sock_write_timeout(int *sock, const char *buf, int nbytes, int timeout)
300 {
301         int nSuccessLess = 0;
302         int bytes_written = 0;
303         int retval;
304         fd_set rfds;
305         int fdflags;
306         int IsNonBlock;
307         struct timeval tv;
308         int selectresolution = 100;
309
310         fdflags = fcntl(*sock, F_GETFL);
311         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
312
313         while ((nSuccessLess < timeout) && 
314                (*sock != -1) && 
315                (bytes_written < nbytes)) 
316         {
317                 if (IsNonBlock){
318                         tv.tv_sec = selectresolution;
319                         tv.tv_usec = 0;
320                         
321                         FD_ZERO(&rfds);
322                         FD_SET(*sock, &rfds);
323                         if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) {
324 ///                             *Error = strerror(errno);
325                                 close (*sock);
326                                 *sock = -1;
327                                 return -1;
328                         }
329                 }
330                 if (IsNonBlock && !  FD_ISSET(*sock, &rfds)) {
331                         nSuccessLess ++;
332                         continue;
333                 }
334                 retval = write(*sock, &buf[bytes_written],
335                                nbytes - bytes_written);
336                 if (retval < 1) {
337                         sock_close(*sock);
338                         *sock = -1;
339                         return (-1);
340                 }
341                 bytes_written = bytes_written + retval;
342                 if (IsNonBlock && (bytes_written == nbytes)){
343                         tv.tv_sec = selectresolution;
344                         tv.tv_usec = 0;
345                         
346                         FD_ZERO(&rfds);
347                         FD_SET(*sock, &rfds);
348                         if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) {
349 ///                             *Error = strerror(errno);
350                                 close (*sock);
351                                 *sock = -1;
352                                 return -1;
353                         }
354                 }
355         }
356         return (bytes_written);
357 }
358
359
360
361 /*
362  * client_getln()   ...   Get a LF-terminated line of text from the client.
363  * (This is implemented in terms of client_read() and could be
364  * justifiably moved out of sysdep.c)
365  */
366 int sock_getln_err(int *sock, char *buf, int bufsize, int *rc, int nSec)
367 {
368         int i, retval;
369         CitContext *CCC = MyContext();
370         const char *pCh;
371
372         FlushStrBuf(CCC->sMigrateBuf);
373         *rc = retval = CtdlSockGetLine(sock, CCC->sMigrateBuf, nSec);
374
375         i = StrLength(CCC->sMigrateBuf);
376         pCh = ChrPtr(CCC->sMigrateBuf);
377
378         memcpy(buf, pCh, i + 1);
379
380         FlushStrBuf(CCC->sMigrateBuf);
381         if (retval < 0) {
382                 safestrncpy(&buf[i], "000", bufsize - i);
383                 i += 3;
384         }
385         return i;
386 }
387
388 /*
389  * Multiline version of sock_gets() ... this is a convenience function for
390  * client side protocol implementations.  It only returns the first line of
391  * a multiline response, discarding the rest.
392  */
393
394 int ml_sock_gets(int *sock, char *buf, int nSec)
395 {
396         int rc = 0;
397         char bigbuf[1024];
398         int g;
399
400         g = sock_getln_err(sock, buf, SIZ, &rc, nSec);
401         if (rc < 0)
402                 return rc;
403         if (g < 4)
404                 return (g);
405         if (buf[3] != '-')
406                 return (g);
407
408         do {
409                 g = sock_getln_err(sock, bigbuf, SIZ, &rc, nSec);
410                 if (rc < 0)
411                         return rc;
412                 if (g < 0)
413                         return (g);
414         } while ((g >= 4) && (bigbuf[3] == '-'));
415
416         return(strlen(buf));
417 }
418
419
420 /*
421  * sock_puts() - send line to server - implemented in terms of serv_write()
422  * Returns the number of bytes written, or -1 for error.
423  */
424 int sock_puts(int *sock, char *buf)
425 {
426         int i, j;
427
428         i = sock_write(sock, buf, strlen(buf));
429         if (i<0) return(i);
430         j = sock_write(sock, "\n", 1);
431         if (j<0) return(j);
432         return(i+j);
433 }