Merge pull request #2045 from RincewindsHat/fix/calloc_argument_order
[monitoring-plugins.git] / plugins / netutils.c
blobee81912a77cb0f6789e69b46ab0343b8a0d9b37b
1 /*****************************************************************************
3 * Monitoring Plugins network utilities
5 * License: GPL
6 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
7 * Copyright (c) 2003-2024 Monitoring Plugins Development Team
9 * Description:
11 * This file contains commons functions used in many of the plugins.
14 * This program is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 *****************************************************************************/
30 #include "common.h"
31 #include "netutils.h"
33 unsigned int socket_timeout = DEFAULT_SOCKET_TIMEOUT;
34 unsigned int socket_timeout_state = STATE_CRITICAL;
36 int econn_refuse_state = STATE_CRITICAL;
37 bool was_refused = false;
38 #if USE_IPV6
39 int address_family = AF_UNSPEC;
40 #else
41 int address_family = AF_INET;
42 #endif
44 /* handles socket timeouts */
45 void socket_timeout_alarm_handler(int sig) {
46 if (sig == SIGALRM)
47 printf(_("%s - Socket timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout);
48 else
49 printf(_("%s - Abnormal timeout after %d seconds\n"), state_text(socket_timeout_state), socket_timeout);
51 exit(socket_timeout_state);
54 /* connects to a host on a specified tcp port, sends a string, and gets a
55 response. loops on select-recv until timeout or eof to get all of a
56 multi-packet answer */
57 int process_tcp_request2(const char *server_address, int server_port, const char *send_buffer, char *recv_buffer, int recv_size) {
59 int result;
60 int send_result;
61 int recv_result;
62 int sd;
63 struct timeval tv;
64 fd_set readfds;
65 int recv_length = 0;
67 result = np_net_connect(server_address, server_port, &sd, IPPROTO_TCP);
68 if (result != STATE_OK)
69 return STATE_CRITICAL;
71 send_result = send(sd, send_buffer, strlen(send_buffer), 0);
72 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
73 printf("%s\n", _("Send failed"));
74 result = STATE_WARNING;
77 while (1) {
78 /* wait up to the number of seconds for socket timeout
79 minus one for data from the host */
80 tv.tv_sec = socket_timeout - 1;
81 tv.tv_usec = 0;
82 FD_ZERO(&readfds);
83 FD_SET(sd, &readfds);
84 select(sd + 1, &readfds, NULL, NULL, &tv);
86 /* make sure some data has arrived */
87 if (!FD_ISSET(sd, &readfds)) { /* it hasn't */
88 if (!recv_length) {
89 strcpy(recv_buffer, "");
90 printf("%s\n", _("No data was received from host!"));
91 result = STATE_WARNING;
92 } else { /* this one failed, but previous ones worked */
93 recv_buffer[recv_length] = 0;
95 break;
96 } else { /* it has */
97 recv_result = recv(sd, recv_buffer + recv_length, (size_t)recv_size - recv_length - 1, 0);
98 if (recv_result == -1) {
99 /* recv failed, bail out */
100 strcpy(recv_buffer + recv_length, "");
101 result = STATE_WARNING;
102 break;
103 } else if (recv_result == 0) {
104 /* end of file ? */
105 recv_buffer[recv_length] = 0;
106 break;
107 } else { /* we got data! */
108 recv_length += recv_result;
109 if (recv_length >= recv_size - 1) {
110 /* buffer full, we're done */
111 recv_buffer[recv_size - 1] = 0;
112 break;
116 /* end if(!FD_ISSET(sd,&readfds)) */
118 /* end while(1) */
120 close(sd);
121 return result;
124 /* connects to a host on a specified port, sends a string, and gets a
125 response */
126 int process_request(const char *server_address, int server_port, int proto, const char *send_buffer, char *recv_buffer, int recv_size) {
127 int result;
128 int sd;
130 result = STATE_OK;
132 result = np_net_connect(server_address, server_port, &sd, proto);
133 if (result != STATE_OK)
134 return STATE_CRITICAL;
136 result = send_request(sd, proto, send_buffer, recv_buffer, recv_size);
138 close(sd);
140 return result;
143 /* opens a tcp or udp connection to a remote host or local socket */
144 int np_net_connect(const char *host_name, int port, int *sd, int proto) {
145 /* send back STATE_UNKOWN if there's an error
146 send back STATE_OK if we connect
147 send back STATE_CRITICAL if we can't connect.
148 Let upstream figure out what to send to the user. */
149 struct addrinfo hints;
150 struct addrinfo *r, *res;
151 struct sockaddr_un su;
152 char port_str[6], host[MAX_HOST_ADDRESS_LENGTH];
153 size_t len;
154 int socktype, result;
155 short is_socket = (host_name[0] == '/');
157 socktype = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
159 /* as long as it doesn't start with a '/', it's assumed a host or ip */
160 if (!is_socket) {
161 memset(&hints, 0, sizeof(hints));
162 hints.ai_family = address_family;
163 hints.ai_protocol = proto;
164 hints.ai_socktype = socktype;
166 len = strlen(host_name);
167 /* check for an [IPv6] address (and strip the brackets) */
168 if (len >= 2 && host_name[0] == '[' && host_name[len - 1] == ']') {
169 host_name++;
170 len -= 2;
172 if (len >= sizeof(host))
173 return STATE_UNKNOWN;
174 memcpy(host, host_name, len);
175 host[len] = '\0';
176 snprintf(port_str, sizeof(port_str), "%d", port);
177 result = getaddrinfo(host, port_str, &hints, &res);
179 if (result != 0) {
180 printf("%s\n", gai_strerror(result));
181 return STATE_UNKNOWN;
184 r = res;
185 while (r) {
186 /* attempt to create a socket */
187 *sd = socket(r->ai_family, socktype, r->ai_protocol);
189 if (*sd < 0) {
190 printf("%s\n", _("Socket creation failed"));
191 freeaddrinfo(r);
192 return STATE_UNKNOWN;
195 /* attempt to open a connection */
196 result = connect(*sd, r->ai_addr, r->ai_addrlen);
198 if (result == 0) {
199 was_refused = false;
200 break;
203 if (result < 0) {
204 switch (errno) {
205 case ECONNREFUSED:
206 was_refused = true;
207 break;
211 close(*sd);
212 r = r->ai_next;
214 freeaddrinfo(res);
216 /* else the hostname is interpreted as a path to a unix socket */
217 else {
218 if (strlen(host_name) >= UNIX_PATH_MAX) {
219 die(STATE_UNKNOWN, _("Supplied path too long unix domain socket"));
221 memset(&su, 0, sizeof(su));
222 su.sun_family = AF_UNIX;
223 strncpy(su.sun_path, host_name, UNIX_PATH_MAX);
224 *sd = socket(PF_UNIX, SOCK_STREAM, 0);
225 if (*sd < 0) {
226 die(STATE_UNKNOWN, _("Socket creation failed"));
228 result = connect(*sd, (struct sockaddr *)&su, sizeof(su));
229 if (result < 0 && errno == ECONNREFUSED)
230 was_refused = true;
233 if (result == 0)
234 return STATE_OK;
235 else if (was_refused) {
236 switch (econn_refuse_state) { /* a user-defined expected outcome */
237 case STATE_OK:
238 case STATE_WARNING: /* user wants WARN or OK on refusal, or... */
239 case STATE_CRITICAL: /* user did not set econn_refuse_state, or wanted critical */
240 if (is_socket)
241 printf("connect to file socket %s: %s\n", host_name, strerror(errno));
242 else
243 printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno));
244 return STATE_CRITICAL;
245 break;
246 default: /* it's a logic error if we do not end up in STATE_(OK|WARNING|CRITICAL) */
247 return STATE_UNKNOWN;
248 break;
250 } else {
251 if (is_socket)
252 printf("connect to file socket %s: %s\n", host_name, strerror(errno));
253 else
254 printf("connect to address %s and port %d: %s\n", host_name, port, strerror(errno));
255 return STATE_CRITICAL;
259 int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size) {
260 int result = STATE_OK;
261 int send_result;
262 int recv_result;
263 struct timeval tv;
264 fd_set readfds;
266 send_result = send(sd, send_buffer, strlen(send_buffer), 0);
267 if (send_result < 0 || (size_t)send_result != strlen(send_buffer)) {
268 printf("%s\n", _("Send failed"));
269 result = STATE_WARNING;
272 /* wait up to the number of seconds for socket timeout minus one
273 for data from the host */
274 tv.tv_sec = socket_timeout - 1;
275 tv.tv_usec = 0;
276 FD_ZERO(&readfds);
277 FD_SET(sd, &readfds);
278 select(sd + 1, &readfds, NULL, NULL, &tv);
280 /* make sure some data has arrived */
281 if (!FD_ISSET(sd, &readfds)) {
282 strcpy(recv_buffer, "");
283 printf("%s\n", _("No data was received from host!"));
284 result = STATE_WARNING;
287 else {
288 recv_result = recv(sd, recv_buffer, (size_t)recv_size - 1, 0);
289 if (recv_result == -1) {
290 strcpy(recv_buffer, "");
291 if (proto != IPPROTO_TCP)
292 printf("%s\n", _("Receive failed"));
293 result = STATE_WARNING;
294 } else
295 recv_buffer[recv_result] = 0;
297 /* die returned string */
298 recv_buffer[recv_size - 1] = 0;
300 return result;
303 bool is_host(const char *address) {
304 if (is_addr(address) || is_hostname(address))
305 return (true);
307 return (false);
310 void host_or_die(const char *str) {
311 if (!str || (!is_addr(str) && !is_hostname(str)))
312 usage_va(_("Invalid hostname/address - %s"), str);
315 bool is_addr(const char *address) {
316 #ifdef USE_IPV6
317 if (address_family == AF_INET && is_inet_addr(address))
318 return true;
319 else if (address_family == AF_INET6 && is_inet6_addr(address))
320 return true;
321 #else
322 if (is_inet_addr(address))
323 return (true);
324 #endif
326 return (false);
329 int dns_lookup(const char *in, struct sockaddr_storage *ss, int family) {
330 struct addrinfo hints;
331 struct addrinfo *res;
332 int retval;
334 memset(&hints, 0, sizeof(struct addrinfo));
335 hints.ai_family = family;
337 retval = getaddrinfo(in, NULL, &hints, &res);
338 if (retval != 0)
339 return false;
341 if (ss != NULL)
342 memcpy(ss, res->ai_addr, res->ai_addrlen);
343 freeaddrinfo(res);
344 return true;