1 /*
2  *
3  * Conky, a system monitor, based on torsmo
4  *
5  * Any original torsmo code is licensed under the BSD license
6  *
7  * All code written since the fork of torsmo is licensed under the GPL
8  *
9  * Please see COPYING for details
10  *
11  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
12  * Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al.
13  *	(see AUTHORS)
14  * All rights reserved.
15  *
16  * This program is free software: you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation, either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  * You should have received a copy of the GNU General Public License
26  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27  *
28  */
29 
30 #include <fcntl.h>
31 #include <netdb.h>
32 #include <netinet/in.h>
33 #include <sys/time.h>
34 #include <unistd.h>
35 #include <cerrno>
36 #include <cinttypes>
37 #include <cstdlib>
38 #include <string>
39 #include "conky.h"
40 #include "logging.h"
41 #include "text_object.h"
42 
43 #ifndef SOCK_CLOEXEC
44 #define SOCK_CLOEXEC O_CLOEXEC
45 #endif /* SOCK_CLOEXEC */
46 
47 struct read_tcpip_data {
48   char *host;
49   unsigned int port;
50 };
51 
parse_read_tcpip_arg(struct text_object * obj,const char * arg,void * free_at_crash)52 void parse_read_tcpip_arg(struct text_object *obj, const char *arg,
53                           void *free_at_crash) {
54   struct read_tcpip_data *rtd;
55 
56   rtd = static_cast<struct read_tcpip_data *>(
57       malloc(sizeof(struct read_tcpip_data)));
58   memset(rtd, 0, sizeof(struct read_tcpip_data));
59 
60   rtd->host = static_cast<char *>(malloc(text_buffer_size.get(*state)));
61   sscanf(arg, "%s", rtd->host);
62   sscanf(arg + strlen(rtd->host), "%u", &(rtd->port));
63   if (rtd->port == 0) {
64     rtd->port = atoi(rtd->host);
65     strncpy(rtd->host, "localhost", 10);
66   }
67   if (rtd->port < 1 || rtd->port > 65535) {
68     CRIT_ERR(obj, free_at_crash,
69              "read_tcp and read_udp need a port from 1 to 65535 as argument");
70   }
71 
72   obj->data.opaque = rtd;
73 }
74 
parse_tcp_ping_arg(struct text_object * obj,const char * arg,void * free_at_crash)75 void parse_tcp_ping_arg(struct text_object *obj, const char *arg,
76                         void *free_at_crash) {
77 #define DEFAULT_TCP_PING_PORT 80
78   struct sockaddr_in *addr;
79   char *hostname;
80   struct hostent *he;
81 
82   addr = static_cast<struct sockaddr_in *>(malloc(sizeof(struct sockaddr_in)));
83   obj->data.opaque = addr;
84   memset(addr, 0, sizeof(struct sockaddr_in));
85   hostname = static_cast<char *>(malloc(strlen(arg) + 1));
86   switch (sscanf(arg, "%s %" SCNu16, hostname, &(addr->sin_port))) {
87     case 1:
88       addr->sin_port = DEFAULT_TCP_PING_PORT;
89       break;
90     case 2:
91       break;
92     default:  // this point should never be reached
93       free(hostname);
94       CRIT_ERR(obj, free_at_crash, "tcp_ping: Reading arguments failed");
95   }
96   if ((he = gethostbyname(hostname)) == nullptr) {
97     NORM_ERR("tcp_ping: Problem with resolving '%s', using 'localhost' instead",
98              hostname);
99     if ((he = gethostbyname("localhost")) == nullptr) {
100       free(hostname);
101       CRIT_ERR(obj, free_at_crash,
102                "tcp_ping: Resolving 'localhost' also failed");
103     }
104   }
105   if (he != nullptr) {
106     free(hostname);
107     addr->sin_port = htons(addr->sin_port);
108     addr->sin_family = he->h_addrtype;
109     memcpy(&(addr->sin_addr), he->h_addr, he->h_length);
110   }
111 }
112 
print_tcp_ping(struct text_object * obj,char * p,unsigned int p_max_size)113 void print_tcp_ping(struct text_object *obj, char *p, unsigned int p_max_size) {
114   struct timeval tv1 {
115   }, tv2{}, timeout{};
116   auto *addr = static_cast<struct sockaddr_in *>(obj->data.opaque);
117   int addrlen = sizeof(struct sockaddr);
118   int sock = socket(addr->sin_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
119   unsigned long long usecdiff;
120   fd_set writefds;
121 
122   if (sock != -1) {
123     fcntl(sock, F_SETFL, O_NONBLOCK | fcntl(sock, F_GETFL));
124 
125     FD_ZERO(&writefds);
126     FD_SET(sock, &writefds);
127 #define TCP_PING_TIMEOUT 10
128     timeout.tv_sec = TCP_PING_TIMEOUT;
129     timeout.tv_usec = (TCP_PING_TIMEOUT - timeout.tv_sec) * 1000000;
130     connect(sock, reinterpret_cast<struct sockaddr *>(addr),
131             addrlen);  // this will "fail" because sock is non-blocking
132     if (errno == EINPROGRESS) {  // but EINPROGRESS is only a "false fail"
133       gettimeofday(&tv1, nullptr);
134       if (select(sock + 1, nullptr, &writefds, nullptr, &timeout) != -1) {
135         gettimeofday(&tv2, nullptr);
136         usecdiff =
137             ((tv2.tv_sec - tv1.tv_sec) * 1000000) + tv2.tv_usec - tv1.tv_usec;
138         if (usecdiff <= TCP_PING_TIMEOUT * 1000000) {
139           snprintf(p, p_max_size, "%llu", (usecdiff / 1000U));
140         } else {
141 #define TCP_PING_FAILED "down"
142           snprintf(p, p_max_size, "%s", TCP_PING_FAILED);
143         }
144       } else {
145         NORM_ERR("tcp_ping: Couldn't wait on the 'pong'");
146       }
147     } else {
148       NORM_ERR("tcp_ping: Couldn't start connection");
149     }
150     close(sock);
151   } else {
152     NORM_ERR("tcp_ping: Couldn't create socket");
153   }
154 }
155 
print_read_tcpip(struct text_object * obj,char * p,int p_max_size,int protocol)156 void print_read_tcpip(struct text_object *obj, char *p, int p_max_size,
157                       int protocol) {
158   int sock, received;
159   fd_set readfds;
160   struct timeval tv {};
161   auto *rtd = static_cast<struct read_tcpip_data *>(obj->data.opaque);
162   struct addrinfo hints {};
163   struct addrinfo *airesult, *rp;
164   char portbuf[8];
165 
166   if (rtd == nullptr) { return; }
167 
168   memset(&hints, 0, sizeof(struct addrinfo));
169   hints.ai_family = AF_UNSPEC;
170   hints.ai_socktype = protocol == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM;
171   hints.ai_flags = 0;
172   hints.ai_protocol = protocol;
173   snprintf(portbuf, 8, "%u", rtd->port);
174   if (getaddrinfo(rtd->host, portbuf, &hints, &airesult) != 0) {
175     NORM_ERR("%s: Problem with resolving the hostname",
176              protocol == IPPROTO_TCP ? "read_tcp" : "read_udp");
177     return;
178   }
179   for (rp = airesult; rp != nullptr; rp = rp->ai_next) {
180     sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
181     if (sock == -1) { continue; }
182     if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1) { break; }
183     close(sock);
184     return;
185   }
186   freeaddrinfo(airesult);
187   if (rp == nullptr) {
188     if (protocol == IPPROTO_TCP) {
189       NORM_ERR("read_tcp: Couldn't create a connection");
190     } else {
191       NORM_ERR("read_udp: Couldn't listen");  // other error because udp is
192                                               // connectionless
193     }
194     return;
195   }
196   if (protocol == IPPROTO_UDP) {
197     // when using udp send a zero-length packet to let the other end know of our
198     // existence
199     if (write(sock, nullptr, 0) < 0) {
200       NORM_ERR("read_udp: Couldn't create a empty package");
201     }
202   }
203   FD_ZERO(&readfds);
204   FD_SET(sock, &readfds);
205   tv.tv_sec = 1;
206   tv.tv_usec = 0;
207   if (select(sock + 1, &readfds, nullptr, nullptr, &tv) > 0) {
208     received = recv(sock, p, p_max_size, 0);
209     if (received != -1) {
210       p[received] = 0;
211     } else {
212       p[0] = 0;
213     }
214   }
215   close(sock);
216 }
217 
print_read_tcp(struct text_object * obj,char * p,unsigned int p_max_size)218 void print_read_tcp(struct text_object *obj, char *p, unsigned int p_max_size) {
219   print_read_tcpip(obj, p, p_max_size, IPPROTO_TCP);
220 }
221 
print_read_udp(struct text_object * obj,char * p,unsigned int p_max_size)222 void print_read_udp(struct text_object *obj, char *p, unsigned int p_max_size) {
223   print_read_tcpip(obj, p, p_max_size, IPPROTO_UDP);
224 }
225 
free_read_tcpip(struct text_object * obj)226 void free_read_tcpip(struct text_object *obj) {
227   auto *rtd = static_cast<struct read_tcpip_data *>(obj->data.opaque);
228 
229   if (rtd == nullptr) { return; }
230 
231   free_and_zero(rtd->host);
232   free_and_zero(obj->data.opaque);
233 }
234 
free_tcp_ping(struct text_object * obj)235 void free_tcp_ping(struct text_object *obj) {
236   auto *addr = static_cast<struct sockaddr_in *>(obj->data.opaque);
237 
238   if (addr == nullptr) { return; }
239 
240   free_and_zero(obj->data.opaque);
241 }
242