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