This is an omnibus commit which moves the Citadel Server from crusty old GNU Autotool...
[citadel.git] / citadel / server / clientsocket.c
1 // This module handles client-side sockets opened by the Citadel server (for
2 // the client side of Internet protocols, etc.)   It does _not_ handle client
3 // sockets for the Citadel client; for that you must look in ipc_c_tcp.c
4 // (which, uncoincidentally, bears a striking similarity to this file).
5 //
6 // Copyright (c) 1987-2017 by the citadel.org team
7 //
8 // This program is open source software.  Use, duplication, or disclosure
9 // is subject to the terms of the GNU General Public License, version 3.
10 // The program is distributed without any warranty, expressed or implied.
11
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <netdb.h>
15 #include <stdio.h>
16 #ifdef __FreeBSD__
17 #include <sys/socket.h>
18 #endif
19 #include <libcitadel.h>
20 #include "ctdl_module.h"
21 #include "clientsocket.h"
22
23 int sock_connect(char *host, char *service) {
24         struct in6_addr serveraddr;
25         struct addrinfo hints;
26         struct addrinfo *res = NULL;
27         struct addrinfo *ai = NULL;
28         int rc = (-1);
29         int sock = (-1);
30
31         if ((host == NULL) || IsEmptyStr(host))
32                 return (-1);
33         if ((service == NULL) || IsEmptyStr(service))
34                 return (-1);
35
36         memset(&hints, 0x00, sizeof(hints));
37         hints.ai_flags = AI_NUMERICSERV;
38         hints.ai_family = AF_UNSPEC;
39         hints.ai_socktype = SOCK_STREAM;
40
41         // Handle numeric IPv4 and IPv6 addresses
42         rc = inet_pton(AF_INET, host, &serveraddr);
43         if (rc == 1) {                                          // dotted quad
44                 hints.ai_family = AF_INET;
45                 hints.ai_flags |= AI_NUMERICHOST;
46         }
47         else {
48                 rc = inet_pton(AF_INET6, host, &serveraddr);
49                 if (rc == 1) {                                  // IPv6 address
50                         hints.ai_family = AF_INET6;
51                         hints.ai_flags |= AI_NUMERICHOST;
52                 }
53         }
54
55         // Begin the connection process
56         rc = getaddrinfo(host, service, &hints, &res);
57         if (rc != 0) {
58                 syslog(LOG_ERR, "%s: %s", host, gai_strerror(rc));
59                 return(-1);
60         }
61
62         // Try all available addresses until we connect to one or until we run out.
63         for (ai = res; ai != NULL; ai = ai->ai_next) {
64                 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
65                 if (sock < 0) {
66                         syslog(LOG_ERR, "%s: %m", host);
67                         freeaddrinfo(res);
68                         return(-1);
69                 }
70                 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
71                 if (rc >= 0) {
72                         freeaddrinfo(res);
73                         return(sock);
74                 }
75                 else {
76                         syslog(LOG_ERR, "%s: %m", host);
77                         close(sock);
78                 }
79         }
80         freeaddrinfo(res);
81         return(-1);
82 }
83
84
85 // Read data from the client socket.
86 //
87 // sock         socket fd to read from
88 // buf          buffer to read into 
89 // bytes        number of bytes to read
90 // timeout      Number of seconds to wait before timing out
91 //
92 // Possible return values:
93 //      1       Requested number of bytes has been read.
94 //      0       Request timed out.
95 //      -1      Connection is broken, or other error.
96 int socket_read_blob(int *Socket, StrBuf *Target, int bytes, int timeout) {
97         const char *Error;
98         int retval = 0;
99
100         retval = StrBufReadBLOBBuffered(Target, CC->SBuf.Buf, &CC->SBuf.ReadWritePointer, Socket, 1, bytes, O_TERM, &Error); 
101         if (retval < 0) {
102                 syslog(LOG_ERR, "clientsocket: socket_read_blob() failed: %s", Error);
103         }
104         return retval;
105 }
106
107
108 int CtdlSockGetLine(int *sock, StrBuf *Target, int nSec) {
109         const char *Error;
110         int rc;
111
112         FlushStrBuf(Target);
113         rc = StrBufTCP_read_buffered_line_fast(Target,
114                                                CC->SBuf.Buf,
115                                                &CC->SBuf.ReadWritePointer,
116                                                sock, nSec, 1, &Error);
117         if ((rc < 0) && (Error != NULL)) {
118                 syslog(LOG_ERR, "clientsocket: CtdlSockGetLine() failed: %s", Error);
119         }
120         return rc;
121 }
122
123
124 // client_getln()   ...   Get a LF-terminated line of text from the client.
125 int sock_getln(int *sock, char *buf, int bufsize) {
126         int i, retval;
127         const char *pCh;
128
129         FlushStrBuf(CC->sMigrateBuf);
130         retval = CtdlSockGetLine(sock, CC->sMigrateBuf, 5);
131
132         i = StrLength(CC->sMigrateBuf);
133         pCh = ChrPtr(CC->sMigrateBuf);
134
135         memcpy(buf, pCh, i + 1);
136
137         FlushStrBuf(CC->sMigrateBuf);
138         if (retval < 0) {
139                 safestrncpy(&buf[i], "000", bufsize - i);
140                 i += 3;
141         }
142         return i;
143 }
144
145
146 // sock_write() - send binary to server.
147 // Returns the number of bytes written, or -1 for error.
148 int sock_write(int *sock, const char *buf, int nbytes) {
149         return sock_write_timeout(sock, buf, nbytes, 50);
150 }
151
152
153 int sock_write_timeout(int *sock, const char *buf, int nbytes, int timeout) {
154         int nSuccessLess = 0;
155         int bytes_written = 0;
156         int retval;
157         fd_set rfds;
158         int fdflags;
159         int IsNonBlock;
160         struct timeval tv;
161         int selectresolution = 100;
162
163         fdflags = fcntl(*sock, F_GETFL);
164         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
165
166         while ((nSuccessLess < timeout) && (*sock != -1) && (bytes_written < nbytes)) {
167                 if (IsNonBlock) {
168                         tv.tv_sec = selectresolution;
169                         tv.tv_usec = 0;
170                         
171                         FD_ZERO(&rfds);
172                         FD_SET(*sock, &rfds);
173                         if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) {
174                                 close (*sock);
175                                 *sock = -1;
176                                 return -1;
177                         }
178                 }
179                 if (IsNonBlock && !  FD_ISSET(*sock, &rfds)) {
180                         nSuccessLess ++;
181                         continue;
182                 }
183                 retval = write(*sock, &buf[bytes_written],
184                                nbytes - bytes_written);
185                 if (retval < 1) {
186                         sock_close(*sock);
187                         *sock = -1;
188                         return (-1);
189                 }
190                 bytes_written = bytes_written + retval;
191                 if (IsNonBlock && (bytes_written == nbytes)){
192                         tv.tv_sec = selectresolution;
193                         tv.tv_usec = 0;
194                         
195                         FD_ZERO(&rfds);
196                         FD_SET(*sock, &rfds);
197                         if (select(*sock + 1, NULL, &rfds, NULL, &tv) == -1) {
198                                 close (*sock);
199                                 *sock = -1;
200                                 return -1;
201                         }
202                 }
203         }
204         return (bytes_written);
205 }
206
207
208 // client_getln()   ...   Get a LF-terminated line of text from the client.
209 int sock_getln_err(int *sock, char *buf, int bufsize, int *rc, int nSec) {
210         int i, retval;
211         const char *pCh;
212
213         FlushStrBuf(CC->sMigrateBuf);
214         *rc = retval = CtdlSockGetLine(sock, CC->sMigrateBuf, nSec);
215
216         i = StrLength(CC->sMigrateBuf);
217         pCh = ChrPtr(CC->sMigrateBuf);
218
219         memcpy(buf, pCh, i + 1);
220
221         FlushStrBuf(CC->sMigrateBuf);
222         if (retval < 0) {
223                 safestrncpy(&buf[i], "000", bufsize - i);
224                 i += 3;
225         }
226         return i;
227 }
228
229
230 // Multiline version of sock_gets() ... this is a convenience function for
231 // client side protocol implementations.  It only returns the first line of
232 // a multiline response, discarding the rest.
233 int ml_sock_gets(int *sock, char *buf, int nSec) {
234         int rc = 0;
235         char bigbuf[1024];
236         int g;
237
238         g = sock_getln_err(sock, buf, SIZ, &rc, nSec);
239         if (rc < 0)
240                 return rc;
241         if (g < 4)
242                 return (g);
243         if (buf[3] != '-')
244                 return (g);
245
246         do {
247                 g = sock_getln_err(sock, bigbuf, SIZ, &rc, nSec);
248                 if (rc < 0)
249                         return rc;
250                 if (g < 0)
251                         return (g);
252         } while ((g >= 4) && (bigbuf[3] == '-'));
253
254         return (strlen(buf));
255 }
256
257
258 // sock_puts() - send line to server - implemented in terms of serv_write()
259 // Returns the number of bytes written, or -1 for error.
260 int sock_puts(int *sock, char *buf) {
261         int i, j;
262
263         i = sock_write(sock, buf, strlen(buf));
264         if (i < 0)
265                 return (i);
266         j = sock_write(sock, "\n", 1);
267         if (j < 0)
268                 return (j);
269         return (i + j);
270 }