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