1 /*
2  * Copyright (C) Tildeslash Ltd. All rights reserved.
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Affero General Public License version 3.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU Affero General Public License
13  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
14  *
15  * In addition, as a special exception, the copyright holders give
16  * permission to link the code of portions of this program with the
17  * OpenSSL library under certain conditions as described in each
18  * individual source file, and distribute linked combinations
19  * including the two.
20  *
21  * You must obey the GNU Affero General Public License in all respects
22  * for all of the code used other than OpenSSL.
23  */
24 
25 #include "config.h"
26 
27 #ifdef HAVE_POLL_H
28 #include <poll.h>
29 #endif
30 
31 #ifdef HAVE_FCNTL_H
32 #include <fcntl.h>
33 #endif
34 
35 #ifdef HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif
38 
39 #ifdef HAVE_STRING_H
40 #include <string.h>
41 #endif
42 
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
45 #endif
46 
47 #ifdef HAVE_STRINGS_H
48 #include <strings.h>
49 #endif
50 
51 #ifdef HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54 
55 #ifdef HAVE_NETINET_IN_H
56 #include <netinet/in.h>
57 #endif
58 
59 #ifdef HAVE_SYS_UN_H
60 #include <sys/un.h>
61 #endif
62 
63 #ifdef HAVE_ARPA_INET_H
64 #include <arpa/inet.h>
65 #endif
66 
67 #ifdef HAVE_NETINET_TCP_H
68 #include <netinet/tcp.h>
69 #endif
70 
71 #ifdef HAVE_NETDB_H
72 #include <netdb.h>
73 #endif
74 
75 #include "net.h"
76 #include "monit.h"
77 #include "socket.h"
78 #include "SslServer.h"
79 
80 // libmonit
81 #include "exceptions/assert.h"
82 #include "exceptions/IOException.h"
83 #include "util/Str.h"
84 #include "system/Net.h"
85 #include "system/Time.h"
86 
87 
88 
89 /**
90  * Implementation of the socket interface.
91  *
92  * @file
93  */
94 
95 
96 /* ------------------------------------------------------------- Definitions */
97 
98 
99 typedef enum {
100         Connection_Client = 0,
101         Connection_Server
102 } __attribute__((__packed__)) Connection_Type;
103 
104 
105 // One TCP frame data size
106 #define RBUFFER_SIZE 1460
107 
108 
109 #define T Socket_T
110 struct T {
111         Socket_Type type;
112         Socket_Family family;
113         Connection_Type connection_type;
114         int socket;
115         int port;
116         int timeout; // milliseconds
117         int length;
118         int offset;
119         char *host;
120         Port_T Port;
121 #ifdef HAVE_OPENSSL
122         Ssl_T ssl;
123         SslServer_T sslserver;
124 #endif
125         unsigned char buffer[RBUFFER_SIZE + 1];
126 };
127 
128 
129 /* --------------------------------------------------------------- Private */
130 
131 
132 /*
133  * Fill the internal buffer. If an error occurs or if the read
134  * operation timed out -1 is returned.
135  * @param S A Socket object
136  * @param timeout The number of milliseconds to wait for data to be read
137  * @return the length of data read or -1 if an error occurred
138  */
_fill(T S,int timeout)139 static int _fill(T S, int timeout) {
140         S->offset = 0;
141         S->length = 0;
142         if (S->type == Socket_Udp)
143                 timeout = 500;
144         int n;
145 #ifdef HAVE_OPENSSL
146         if (S->ssl)
147                 n = Ssl_read(S->ssl, S->buffer + S->length, RBUFFER_SIZE - S->length, timeout);
148         else
149 #endif
150                 n = (int)Net_read(S->socket, S->buffer + S->length,  RBUFFER_SIZE - S->length, timeout);
151         if (n > 0)
152                 S->length += n;
153         else if (n < 0)
154                 return -1;
155         else if (! (errno == EAGAIN || errno == EWOULDBLOCK)) // Peer closed connection
156                 return -1;
157         return n;
158 }
159 
160 
_getPort(const struct sockaddr * addr)161 static int _getPort(const struct sockaddr *addr) {
162         if (addr->sa_family == AF_INET)
163                 return ntohs(((const struct sockaddr_in *)addr)->sin_port);
164 #ifdef HAVE_IPV6
165         else if (addr->sa_family == AF_INET6)
166                 return ntohs(((const struct sockaddr_in6 *)addr)->sin6_port);
167 #endif
168         else
169                 return -1;
170 }
171 
172 
_addressToString(const struct sockaddr * addr,socklen_t addrlen,char * buf,int buflen)173 static char *_addressToString(const struct sockaddr *addr, socklen_t addrlen, char *buf, int buflen) {
174         int oerrno = errno;
175         if (addr->sa_family == AF_UNIX) {
176                 snprintf(buf, buflen, "%s", ((const struct sockaddr_un *)addr)->sun_path);
177         } else {
178                 char ip[NI_MAXHOST];
179                 char port[NI_MAXSERV];
180                 int status = getnameinfo(addr, addrlen, ip, sizeof(ip), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
181                 if (status) {
182                         Log_error("Cannot get address string -- %s\n", status == EAI_SYSTEM ? STRERROR : gai_strerror(status));
183                         *buf = 0;
184                 } else {
185                         snprintf(buf, buflen, "[%s]:%s", ip, port);
186                 }
187         }
188         errno = oerrno;
189         return buf;
190 }
191 
192 
_doConnect(int s,const struct sockaddr * addr,socklen_t addrlen,int timeout,char * error,int errorlen)193 static bool _doConnect(int s, const struct sockaddr *addr, socklen_t addrlen, int timeout, char *error, int errorlen) {
194         int rv = connect(s, addr, addrlen);
195         if (! rv) {
196                 return true;
197         } else if (errno != EINPROGRESS) {
198                 snprintf(error, errorlen, "%s", STRERROR);
199                 return false;
200         }
201         struct pollfd fds[1];
202         fds[0].fd = s;
203         fds[0].events = POLLIN | POLLOUT;
204         rv = poll(fds, 1, timeout);
205         if (rv == 0) {
206                 snprintf(error, errorlen, "Connection timed out");
207                 return false;
208         } else if (rv == -1) {
209                 snprintf(error, errorlen, "Poll failed: %s", STRERROR);
210                 return false;
211         }
212         if (fds[0].events & POLLIN || fds[0].events & POLLOUT) {
213                 socklen_t rvlen = sizeof(rv);
214                 if (getsockopt(s, SOL_SOCKET, SO_ERROR, &rv, &rvlen) < 0) {
215                         snprintf(error, errorlen, "Read of error details failed: %s", STRERROR);
216                         return false;
217                 } else if (rv) {
218                         snprintf(error, errorlen, "%s", strerror(rv));
219                         return false;
220                 }
221         } else {
222                 snprintf(error, errorlen, "Not ready for I/O");
223                 return false;
224         }
225         return true;
226 }
227 
228 
_createIpSocket(const char * host,const struct sockaddr * addr,socklen_t addrlen,const struct sockaddr * localaddr,socklen_t localaddrlen,int family,int type,int protocol,int timeout)229 static T _createIpSocket(const char *host, const struct sockaddr *addr, socklen_t addrlen, const struct sockaddr *localaddr, socklen_t localaddrlen, int family, int type, int protocol, int timeout) {
230         ASSERT(host);
231         char error[STRLEN];
232         int s = socket(family, type, protocol);
233         if (s >= 0) {
234                 if (localaddr) {
235                         if (bind(s, localaddr, localaddrlen) < 0) {
236                                 snprintf(error, sizeof(error), "Cannot bind to outgoing address -- %s", STRERROR);
237                                 goto error;
238                         }
239                 }
240                 if (Net_setNonBlocking(s)) {
241                         if (fcntl(s, F_SETFD, FD_CLOEXEC) != -1) {
242                                 if (_doConnect(s, addr, addrlen, timeout, error, sizeof(error))) {
243                                         T S;
244                                         NEW(S);
245                                         S->socket = s;
246                                         S->type = type;
247                                         S->family = family == AF_INET ? Socket_Ip4 : Socket_Ip6;
248                                         S->timeout = timeout;
249                                         S->host = Str_dup(host);
250                                         S->port = _getPort(addr);
251                                         S->connection_type = Connection_Client;
252                                         return S;
253                                 }
254                         } else {
255                                 snprintf(error, sizeof(error), "Cannot set socket close on exec -- %s", STRERROR);
256                         }
257                 } else {
258                         snprintf(error, sizeof(error), "Cannot set nonblocking socket -- %s", STRERROR);
259                 }
260 error:
261                 Net_close(s);
262         } else {
263                 snprintf(error, sizeof(error), "Cannot create socket to %s -- %s", _addressToString(addr, addrlen, (char[2048]){}, 2048), STRERROR);
264         }
265         THROW(IOException, "%s", error);
266         return NULL;
267 }
268 
269 
_resolve(const char * hostname,int port,Socket_Type type,Socket_Family family)270 static struct addrinfo *_resolve(const char *hostname, int port, Socket_Type type, Socket_Family family) {
271         ASSERT(hostname);
272         struct addrinfo *result, hints = {
273                 .ai_socktype = type,
274                 .ai_protocol = type == Socket_Udp ? IPPROTO_UDP : IPPROTO_TCP
275         };
276         switch (family) {
277                 case Socket_Ip:
278                         hints.ai_family = AF_UNSPEC;
279                         break;
280                 case Socket_Ip4:
281                         hints.ai_family = AF_INET;
282                         break;
283 #ifdef HAVE_IPV6
284                 case Socket_Ip6:
285                         hints.ai_family = AF_INET6;
286 #ifdef AI_ADDRCONFIG
287                         hints.ai_flags = AI_ADDRCONFIG;
288 #endif
289                         break;
290 #endif
291                 default:
292                         Log_error("Invalid socket family %d\n", family);
293                         return NULL;
294         }
295         char _port[6];
296         snprintf(_port, sizeof(_port), "%d", port);
297         int status = getaddrinfo(hostname, _port, &hints, &result);
298         if (status != 0) {
299                 Log_error("Cannot translate '%s' to IP address -- %s\n", hostname, status == EAI_SYSTEM ? STRERROR : gai_strerror(status));
300                 return NULL;
301         }
302         return result;
303 }
304 
305 
306 /* ------------------------------------------------------------------ Public */
307 
308 
Socket_new(const char * host,int port,Socket_Type type,Socket_Family family,Ssl_Flags flags,int timeout)309 T Socket_new(const char *host, int port, Socket_Type type, Socket_Family family, Ssl_Flags flags, int timeout) {
310         struct SslOptions_T options = {.flags = flags};
311         return Socket_create(host, port, type, family, &options, timeout);
312 }
313 
314 
Socket_create(const char * host,int port,Socket_Type type,Socket_Family family,SslOptions_T options,int timeout)315 T Socket_create(const char *host, int port, Socket_Type type, Socket_Family family, SslOptions_T options, int timeout) {
316         ASSERT(host);
317         ASSERT(timeout > 0);
318         volatile T S = NULL;
319         struct addrinfo *result = _resolve(host, port, type, family);
320         if (result) {
321                 char error[512] = {};
322                 // The host may resolve to multiple IPs and if at least one succeeded, we have no problem and don't have to flood the log with partial errors => log only the last error
323                 for (struct addrinfo *r = result; r && S == NULL; r = r->ai_next) {
324                         TRY
325                         {
326                                 S = _createIpSocket(host, r->ai_addr, r->ai_addrlen, NULL, 0, r->ai_family, r->ai_socktype, r->ai_protocol, timeout);
327                                 if (options->flags == SSL_Enabled)
328                                         Socket_enableSsl(S, options, host);
329                         }
330                         ELSE
331                         {
332                                 if (S)
333                                         Socket_free((T *)&S);
334                                 DEBUG("Info: Cannot connect to [%s]:%d -- %s\nTrying next address record\n", host, port, Exception_frame.message);
335                                 snprintf(error, sizeof(error), "%s", Exception_frame.message);
336                         }
337                         END_TRY;
338                 }
339                 freeaddrinfo(result);
340                 if (! S)
341                         Log_error("Cannot connect to [%s]:%d -- %s\n", host, port, error);
342         }
343         return S;
344 }
345 
346 
Socket_createUnix(const char * path,Socket_Type type,int timeout)347 T Socket_createUnix(const char *path, Socket_Type type, int timeout) {
348         ASSERT(path);
349         ASSERT(timeout > 0);
350         int s = socket(PF_UNIX, type, 0);
351         if (s >= 0) {
352                 struct sockaddr_un unixsocket_client = {};
353                 if (type == Socket_Udp) {
354                         unixsocket_client.sun_family = AF_UNIX;
355                         snprintf(unixsocket_client.sun_path, sizeof(unixsocket_client.sun_path), "/tmp/monit_%p.sock", &unixsocket_client);
356                         if (bind(s, (struct sockaddr *) &unixsocket_client, sizeof(unixsocket_client)) != 0) {
357                                 Log_error("Unix socket %s bind error -- %s\n", unixsocket_client.sun_path, STRERROR);
358                                 goto error;
359                         }
360                 }
361                 struct sockaddr_un unixsocket_server = {};
362                 unixsocket_server.sun_family = AF_UNIX;
363                 strncpy(unixsocket_server.sun_path, path, sizeof(unixsocket_server.sun_path) - 1);
364                 if (Net_setNonBlocking(s)) {
365                         char error[STRLEN];
366                         if (_doConnect(s, (struct sockaddr *)&unixsocket_server, sizeof(unixsocket_server), timeout, error, sizeof(error))) {
367                                 T S;
368                                 NEW(S);
369                                 S->connection_type = Connection_Client;
370                                 S->family = Socket_Unix;
371                                 S->type = type;
372                                 S->socket = s;
373                                 S->timeout = timeout;
374                                 S->host = Str_dup(LOCALHOST);
375                                 return S;
376                         }
377                         Log_error("Unix socket %s connection error -- %s\n", path, error);
378                 } else {
379                         Log_error("Cannot set nonblocking unix socket %s -- %s\n", path, STRERROR);
380                 }
381 error:
382                 Net_close(s);
383                 if (type == Socket_Udp)
384                         unlink(unixsocket_client.sun_path);
385         } else {
386                 Log_error("Cannot create unix socket %s -- %s\n", path, STRERROR);
387         }
388         return NULL;
389 }
390 
391 
Socket_createAccepted(int socket,struct sockaddr * addr,void * sslserver)392 T Socket_createAccepted(int socket, struct sockaddr *addr, void *sslserver) {
393         ASSERT(socket >= 0);
394         ASSERT(addr);
395         T S;
396         NEW(S);
397         S->socket = socket;
398         S->timeout = Run.limits.networkTimeout;
399         S->connection_type = Connection_Server;
400         S->type = Socket_Tcp;
401         if (addr->sa_family != AF_UNIX) {
402                 if (addr->sa_family == AF_INET) {
403                         struct sockaddr_in *a = (struct sockaddr_in *)addr;
404                         S->family = Socket_Ip4;
405                         S->host = Str_dup(inet_ntop(addr->sa_family, &a->sin_addr, (char[INET_ADDRSTRLEN]){}, INET_ADDRSTRLEN));
406                 }
407 #ifdef HAVE_IPV6
408                 else {
409                         struct sockaddr_in6 *a = (struct sockaddr_in6 *)addr;
410                         S->family = Socket_Ip6;
411                         S->host = Str_dup(inet_ntop(addr->sa_family, &a->sin6_addr, (char[INET6_ADDRSTRLEN]){}, INET6_ADDRSTRLEN));
412                 }
413 #endif
414                 S->port = _getPort(addr);
415 #ifdef HAVE_OPENSSL
416                 if (sslserver) {
417                         S->sslserver = sslserver;
418                         if (! (S->ssl = SslServer_newConnection(S->sslserver)) || ! SslServer_accept(S->ssl, S->socket, S->timeout)) {
419                                 Socket_free(&S);
420                                 return NULL;
421                         }
422                 }
423 #endif
424         } else {
425                 S->family = Socket_Unix;
426         }
427         return S;
428 }
429 
430 
Socket_free(T * S)431 void Socket_free(T *S) {
432         ASSERT(S && *S);
433 #ifdef HAVE_OPENSSL
434         if ((*S)->ssl)
435         {
436                 if ((*S)->connection_type == Connection_Client) {
437                         Ssl_close((*S)->ssl);
438                         Ssl_free(&((*S)->ssl));
439                 } else if ((*S)->connection_type == Connection_Server && (*S)->sslserver) {
440                         SslServer_freeConnection((*S)->sslserver, &((*S)->ssl));
441                 }
442         }
443         else
444 #endif
445         {
446                 int type;
447                 socklen_t length = sizeof(type);
448                 int rv = getsockopt((*S)->socket, SOL_SOCKET, SO_TYPE, &type, &length);
449                 if (rv) {
450                         Log_error("Freeing socket -- getsockopt failed: %s\n", STRERROR);
451                 } else if (type == SOCK_DGRAM) {
452                         struct sockaddr_storage addr;
453                         socklen_t addrlen = sizeof(addr);
454                         if (getsockname((*S)->socket, (struct sockaddr *)&addr, &addrlen) == 0) {
455                                 if (addr.ss_family == AF_UNIX) {
456                                         struct sockaddr_un *_addr = (struct sockaddr_un *)&addr;
457                                         unlink(_addr->sun_path);
458                                 }
459                         }
460                 }
461                 Net_shutdown((*S)->socket, SHUT_RDWR);
462                 Net_close((*S)->socket);
463         }
464         FREE((*S)->host);
465         FREE(*S);
466 }
467 
468 
469 /* ------------------------------------------------------------ Properties */
470 
471 
Socket_setTimeout(T S,int timeout)472 void Socket_setTimeout(T S, int timeout) {
473         ASSERT(S);
474         S->timeout = timeout;
475 }
476 
477 
Socket_getTimeout(T S)478 int Socket_getTimeout(T S) {
479         ASSERT(S);
480         return S->timeout;
481 }
482 
483 
Socket_isSecure(T S)484 bool Socket_isSecure(T S) {
485         ASSERT(S);
486 #ifdef HAVE_OPENSSL
487         return (S->ssl != NULL);
488 #else
489         return false;
490 #endif
491 }
492 
493 
Socket_getSocket(T S)494 int Socket_getSocket(T S) {
495         ASSERT(S);
496         return S->socket;
497 }
498 
499 
Socket_getType(T S)500 Socket_Type Socket_getType(T S) {
501         ASSERT(S);
502         return S->type;
503 }
504 
505 
Socket_getPort(T S)506 void *Socket_getPort(T S) {
507         ASSERT(S);
508         return S->Port;
509 }
510 
511 
Socket_getRemotePort(T S)512 int Socket_getRemotePort(T S) {
513         ASSERT(S);
514         return S->port;
515 }
516 
517 
Socket_getRemoteHost(T S)518 const char *Socket_getRemoteHost(T S) {
519         ASSERT(S);
520         return S->host;
521 }
522 
523 
Socket_getLocalPort(T S)524 int Socket_getLocalPort(T S) {
525         ASSERT(S);
526         struct sockaddr_storage addr;
527         socklen_t addrlen = sizeof(addr);
528         if (getsockname(S->socket, (struct sockaddr *)&addr, &addrlen) == 0)
529                 return _getPort((struct sockaddr *)&addr);
530         return -1;
531 }
532 
533 
Socket_getLocalHost(T S,char * host,int hostlen)534 const char *Socket_getLocalHost(T S, char *host, int hostlen) {
535         ASSERT(S);
536         ASSERT(host);
537         ASSERT(hostlen);
538         struct sockaddr_storage addr;
539         socklen_t addrlen = sizeof(addr);
540         if (! getsockname(S->socket, (struct sockaddr *)&addr, &addrlen)) {
541                 int status = getnameinfo((struct sockaddr *)&addr, addrlen, host, hostlen, NULL, 0, NI_NUMERICHOST);
542                 if (! status)
543                         return host;
544                 Log_error("Cannot translate address to hostname -- %s\n", status == EAI_SYSTEM ? STRERROR : gai_strerror(status));
545         } else {
546                 Log_error("Cannot translate address to hostname -- getsockname failed: %s\n", STRERROR);
547         }
548         return NULL;
549 }
550 
551 
_testUnix(Port_T p)552 static void _testUnix(Port_T p) {
553         T S = Socket_createUnix(p->target.unix.pathname, p->type, p->timeout);
554         if (S) {
555                 S->Port = p;
556                 TRY
557                 {
558                         p->protocol->check(S);
559                 }
560                 FINALLY
561                 {
562                         Socket_free(&S);
563                 }
564                 END_TRY;
565         } else {
566                 THROW(IOException, "Cannot create unix socket for %s", p->target.unix.pathname);
567         }
568 }
569 
570 
_testIp(Port_T p)571 static void _testIp(Port_T p) {
572         char error[512];
573         volatile Connection_State is_available = Connection_Failed;
574         struct addrinfo *result = _resolve(p->hostname, p->target.net.port, p->type, p->family);
575         if (result) {
576                 // The host may resolve to multiple IPs and if at least one succeeded, we have no problem and don't have to flood the log with partial errors => log only the last error
577                 for (struct addrinfo *r = result; r && is_available != Connection_Ok; r = r->ai_next) {
578                         if (p->outgoing.addrlen == 0 || p->outgoing.addrlen == r->ai_addrlen) {
579                                 volatile T S = NULL;
580                                 TRY
581                                 {
582                                         S = _createIpSocket(p->hostname, r->ai_addr, r->ai_addrlen, p->outgoing.addrlen ? (struct sockaddr *)&(p->outgoing.addr) : NULL, p->outgoing.addrlen, r->ai_family, r->ai_socktype, r->ai_protocol, p->timeout);
583                                         S->Port = p;
584                                         TRY
585                                         {
586                                                 if (p->target.net.ssl.options.flags == SSL_Enabled) {
587                                                         Socket_enableSsl(S, &(p->target.net.ssl.options), p->hostname);
588                                                 }
589                                                 p->protocol->check(S);
590                                         }
591                                         FINALLY
592                                         {
593                                                 // Set the minimum valid days past the protocol check as if the connection uses STARTTLS to switch plain->SSL, we have no SSL certificate information until the STARTTTLS is performed.
594                                                 // Try to collect the certificate validDays even on protocol exception - the protocol test may fail on higher level (e.g. when HTTP returns 400), but we can still get certificate info
595 #ifdef HAVE_OPENSSL
596                                                 if (S->ssl)
597                                                         p->target.net.ssl.certificate.validDays = Ssl_getCertificateValidDays(S->ssl);
598 #endif
599                                         }
600                                         END_TRY;
601                                         is_available = Connection_Ok;
602 
603                                 }
604                                 ELSE
605                                 {
606                                         snprintf(error, sizeof(error), "%s", Exception_frame.message);
607                                         DEBUG("Socket test failed for %s -- %s\n", _addressToString(r->ai_addr, r->ai_addrlen, (char[STRLEN]){}, STRLEN), error);
608                                 }
609                                 FINALLY
610                                 {
611                                         if (S) {
612                                                 Socket_free((Socket_T *)&S);
613                                         }
614                                 }
615                                 END_TRY;
616                         } else {
617                                 snprintf(error, sizeof(error), "No IP address matching '%s' was found", p->outgoing.ip);
618                         }
619                 }
620                 freeaddrinfo(result);
621                 if (is_available != Connection_Ok)
622                         THROW(IOException, "%s", error);
623         } else {
624                 THROW(IOException, "Cannot resolve [%s]:%d", p->hostname, p->target.net.port);
625         }
626 }
627 
628 
629 /* ---------------------------------------------------------------- Public */
630 
631 
Socket_test(void * P)632 void Socket_test(void *P) {
633         ASSERT(P);
634         Port_T p = P;
635         TRY
636         {
637                 long long start = Time_micro();
638                 switch (p->family) {
639                         case Socket_Unix:
640                                 _testUnix(p);
641                                 break;
642                         case Socket_Ip:
643                         case Socket_Ip4:
644                         case Socket_Ip6:
645                                 _testIp(p);
646                                 break;
647                         default:
648                                 THROW(IOException, "Invalid socket family %d\n", p->family);
649                                 break;
650                 }
651                 p->responsetime.current = (double)(Time_micro() - start) / 1000.; // Convert microseconds to milliseconds
652                 p->is_available = Connection_Ok;
653         }
654         ELSE
655         {
656                 p->is_available = Connection_Failed;
657                 p->responsetime.current = -1.;
658                 RETHROW;
659         }
660         END_TRY;
661 }
662 
663 
Socket_enableSsl(T S,SslOptions_T options,const char * name)664 void Socket_enableSsl(T S, SslOptions_T options, const char *name)  {
665         assert(S);
666 #ifdef HAVE_OPENSSL
667         if ((S->ssl = Ssl_new(options)))
668                 Ssl_connect(S->ssl, S->socket, S->timeout, name);
669 #endif
670 }
671 
672 
Socket_print(T S,const char * m,...)673 int Socket_print(T S, const char *m, ...) {
674         int n;
675         va_list ap;
676         char *buf = NULL;
677         ASSERT(S);
678         ASSERT(m);
679         va_start(ap, m);
680         buf = Str_vcat(m, ap);
681         va_end(ap);
682         n = Socket_write(S, buf, strlen(buf));
683         FREE(buf);
684         return n;
685 }
686 
687 
Socket_write(T S,const void * b,size_t size)688 int Socket_write(T S, const void *b, size_t size) {
689         ssize_t n = 0;
690         const void *p = b;
691         ASSERT(S);
692         while (size > 0) {
693 #ifdef HAVE_OPENSSL
694                 if (S->ssl) {
695                         n = Ssl_write(S->ssl, p, (int)size, S->timeout);
696                 } else {
697 #endif
698                         n = Net_write(S->socket, p, size, S->timeout);
699 #ifdef HAVE_OPENSSL
700                 }
701 #endif
702                 if (n <= 0)
703                         break;
704                 p = (const unsigned char *)p + n;
705                 size -= n;
706 
707         }
708         if (n < 0) {
709                 /* No write or a partial write is an error */
710                 return -1;
711         }
712         return  (int)((const unsigned char *)p - (const unsigned char *)b);
713 }
714 
715 
Socket_readByte(T S)716 int Socket_readByte(T S) {
717         ASSERT(S);
718         if (S->offset >= S->length)
719                 if (_fill(S, S->timeout) <= 0)
720                         return -1;
721         return S->buffer[S->offset++];
722 }
723 
724 
Socket_read(T S,void * b,int size)725 int Socket_read(T S, void *b, int size) {
726         int c;
727         unsigned char *p = b;
728         ASSERT(S);
729         while ((size-- > 0) && ((c = Socket_readByte(S)) >= 0))
730                 *p++ = c;
731         return (int)((long)p - (long)b);
732 }
733 
734 
Socket_readLine(T S,char * s,int size)735 char *Socket_readLine(T S, char *s, int size) {
736         int c;
737         unsigned char *p = (unsigned char *)s;
738         ASSERT(S);
739         while (--size && ((c = Socket_readByte(S)) > 0)) { // Stop when \0 is read
740                 *p++ = c;
741                 if (c == '\n')
742                         break;
743         }
744         *p = 0;
745         if (*s)
746                 return s;
747         return NULL;
748 }
749 
750