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