RSSC: remove old malloc not needed anymore.
[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-2011 by the citadel.org team
8  *
9  * This program is open source software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License, version 3.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17
18 #include "sysdep.h"
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <signal.h>
23 #include <sys/types.h>
24 #include <sys/time.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <netdb.h>
29 #include <string.h>
30 #include <pwd.h>
31 #include <errno.h>
32 #include <stdarg.h>
33 #include <syslog.h>
34 #include <libcitadel.h>
35 #include "citadel.h"
36 #include "server.h"
37 #include "sysdep_decls.h"
38 #include "config.h"
39 #include "clientsocket.h"
40 #include "ctdl_module.h"
41
42 int sock_connect(char *host, char *service)
43 {
44         struct in6_addr serveraddr;
45         struct addrinfo hints;
46         struct addrinfo *res = NULL;
47         struct addrinfo *ai = NULL;
48         int rc = (-1);
49         int sock = (-1);
50
51         if ((host == NULL) || IsEmptyStr(host))
52                 return (-1);
53         if ((service == NULL) || IsEmptyStr(service))
54                 return (-1);
55
56         memset(&hints, 0x00, sizeof(hints));
57         hints.ai_flags = AI_NUMERICSERV;
58         hints.ai_family = AF_UNSPEC;
59         hints.ai_socktype = SOCK_STREAM;
60
61         /*
62          * Handle numeric IPv4 and IPv6 addresses
63          */
64         rc = inet_pton(AF_INET, host, &serveraddr);
65         if (rc == 1) {                                          /* dotted quad */
66                 hints.ai_family = AF_INET;
67                 hints.ai_flags |= AI_NUMERICHOST;
68         } else {
69                 rc = inet_pton(AF_INET6, host, &serveraddr);
70                 if (rc == 1) {                                  /* IPv6 address */
71                         hints.ai_family = AF_INET6;
72                         hints.ai_flags |= AI_NUMERICHOST;
73                 }
74         }
75
76         /* Begin the connection process */
77
78         rc = getaddrinfo(host, service, &hints, &res);
79         if (rc != 0) {
80                 syslog(LOG_ERR, "%s: %s", host, gai_strerror(rc));
81                 return(-1);
82         }
83
84         /*
85          * Try all available addresses until we connect to one or until we run out.
86          */
87         for (ai = res; ai != NULL; ai = ai->ai_next) {
88                 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
89                 if (sock < 0) {
90                         syslog(LOG_ERR, "socket() failed: %s", strerror(errno));
91                         freeaddrinfo(res);
92                         return(-1);
93                 }
94                 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
95                 if (rc >= 0) {
96                         freeaddrinfo(res);
97                         return(sock);
98                 }
99                 else {
100                         syslog(LOG_ERR, "connect() failed: %s", strerror(errno));
101                         close(sock);
102                 }
103         }
104         freeaddrinfo(res);
105         return(-1);
106 }
107
108
109
110 /*
111  * Read data from the client socket.
112  *
113  * sock         socket fd to read from
114  * buf          buffer to read into 
115  * bytes        number of bytes to read
116  * timeout      Number of seconds to wait before timing out
117  *
118  * Possible return values:
119  *      1       Requested number of bytes has been read.
120  *      0       Request timed out.
121  *      -1      Connection is broken, or other error.
122  */
123 int socket_read_blob(int *Socket, StrBuf * Target, int bytes, int timeout)
124 {
125         CitContext *CCC = MyContext();
126         const char *Error;
127         int retval = 0;
128
129
130         retval = StrBufReadBLOBBuffered(Target,
131                                         CCC->SBuf.Buf,
132                                         &CCC->SBuf.ReadWritePointer,
133                                         Socket, 1, bytes, O_TERM, &Error);
134         
135         if (retval < 0) {
136                 syslog(LOG_CRIT, "socket_read_blob() failed: %s", Error);
137         }
138         return retval;
139 }
140
141
142 int CtdlSockGetLine(int *sock, StrBuf * Target, int nSec)
143 {
144         CitContext *CCC = MyContext();
145         const char *Error;
146         int rc;
147
148         FlushStrBuf(Target);
149         rc = StrBufTCP_read_buffered_line_fast(Target,
150                                                CCC->SBuf.Buf,
151                                                &CCC->SBuf.ReadWritePointer,
152                                                sock, nSec, 1, &Error);
153         if ((rc < 0) && (Error != NULL))
154                 syslog(LOG_CRIT, "CtdlSockGetLine() failed: %s", Error);
155         return rc;
156 }
157
158
159 /*
160  * client_getln()   ...   Get a LF-terminated line of text from the client.
161  * (This is implemented in terms of client_read() and could be
162  * justifiably moved out of sysdep.c)
163  */
164 int sock_getln(int *sock, char *buf, int bufsize)
165 {
166         int i, retval;
167         CitContext *CCC = MyContext();
168         const char *pCh;
169
170         FlushStrBuf(CCC->sMigrateBuf);
171         retval = CtdlSockGetLine(sock, CCC->sMigrateBuf, 5);
172
173         i = StrLength(CCC->sMigrateBuf);
174         pCh = ChrPtr(CCC->sMigrateBuf);
175
176         memcpy(buf, pCh, i + 1);
177
178         FlushStrBuf(CCC->sMigrateBuf);
179         if (retval < 0) {
180                 safestrncpy(&buf[i], "000", bufsize - i);
181                 i += 3;
182         }
183         return i;
184 }
185
186
187 /*
188  * sock_write() - send binary to server.
189  * Returns the number of bytes written, or -1 for error.
190  */
191 int sock_write(int *sock, const char *buf, int nbytes) 
192 { return sock_write_timeout(sock, buf, nbytes, 50); }
193 int sock_write_timeout(int *sock, const char *buf, int nbytes, int timeout)
194 {
195         int nSuccessLess = 0;
196         int bytes_written = 0;
197         int retval;
198         fd_set rfds;
199         int fdflags;
200         int IsNonBlock;
201         struct timeval tv;
202         int selectresolution = 100;
203
204         fdflags = fcntl(*sock, F_GETFL);
205         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
206
207         while ((nSuccessLess < timeout) && 
208                (*sock != -1) && 
209                (bytes_written < nbytes)) 
210         {
211                 if (IsNonBlock){
212                         tv.tv_sec = selectresolution;
213                         tv.tv_usec = 0;
214                         
215                         FD_ZERO(&rfds);
216                         FD_SET(*sock, &rfds);
217                         if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) {
218 ///                             *Error = strerror(errno);
219                                 close (*sock);
220                                 *sock = -1;
221                                 return -1;
222                         }
223                 }
224                 if (IsNonBlock && !  FD_ISSET(*sock, &rfds)) {
225                         nSuccessLess ++;
226                         continue;
227                 }
228                 retval = write(*sock, &buf[bytes_written],
229                                nbytes - bytes_written);
230                 if (retval < 1) {
231                         sock_close(*sock);
232                         *sock = -1;
233                         return (-1);
234                 }
235                 bytes_written = bytes_written + retval;
236                 if (IsNonBlock && (bytes_written == nbytes)){
237                         tv.tv_sec = selectresolution;
238                         tv.tv_usec = 0;
239                         
240                         FD_ZERO(&rfds);
241                         FD_SET(*sock, &rfds);
242                         if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) {
243 ///                             *Error = strerror(errno);
244                                 close (*sock);
245                                 *sock = -1;
246                                 return -1;
247                         }
248                 }
249         }
250         return (bytes_written);
251 }
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_err(int *sock, char *buf, int bufsize, int *rc, int nSec)
261 {
262         int i, retval;
263         CitContext *CCC = MyContext();
264         const char *pCh;
265
266         FlushStrBuf(CCC->sMigrateBuf);
267         *rc = retval = CtdlSockGetLine(sock, CCC->sMigrateBuf, nSec);
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  * Multiline version of sock_gets() ... this is a convenience function for
284  * client side protocol implementations.  It only returns the first line of
285  * a multiline response, discarding the rest.
286  */
287 int ml_sock_gets(int *sock, char *buf, int nSec)
288 {
289         int rc = 0;
290         char bigbuf[1024];
291         int g;
292
293         g = sock_getln_err(sock, buf, SIZ, &rc, nSec);
294         if (rc < 0)
295                 return rc;
296         if (g < 4)
297                 return (g);
298         if (buf[3] != '-')
299                 return (g);
300
301         do {
302                 g = sock_getln_err(sock, bigbuf, SIZ, &rc, nSec);
303                 if (rc < 0)
304                         return rc;
305                 if (g < 0)
306                         return (g);
307         } while ((g >= 4) && (bigbuf[3] == '-'));
308
309         return (strlen(buf));
310 }
311
312
313 /*
314  * sock_puts() - send line to server - implemented in terms of serv_write()
315  * Returns the number of bytes written, or -1 for error.
316  */
317 int sock_puts(int *sock, char *buf)
318 {
319         int i, j;
320
321         i = sock_write(sock, buf, strlen(buf));
322         if (i < 0)
323                 return (i);
324         j = sock_write(sock, "\n", 1);
325         if (j < 0)
326                 return (j);
327         return (i + j);
328 }