291 lines
6.0 KiB
C
291 lines
6.0 KiB
C
/*****************************************************************************
|
|
*
|
|
* NETUTILS.C - Network connection utility functions for Nagios
|
|
*
|
|
* Portions Copyright (c) 1999-2008 Nagios Plugin development team
|
|
*
|
|
* License:
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "../include/config.h"
|
|
#include "../include/common.h"
|
|
#include "../include/netutils.h"
|
|
|
|
|
|
/* connect to a TCP socket in nonblocking fashion */
|
|
int my_tcp_connect(const char *host_name, int port, int *sd, int timeout) {
|
|
struct addrinfo hints;
|
|
struct addrinfo *res;
|
|
int result;
|
|
char port_str[6];
|
|
int flags = 0;
|
|
fd_set rfds;
|
|
fd_set wfds;
|
|
struct timeval tv;
|
|
int optval;
|
|
socklen_t optlen;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = PF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
/* make sure our static port_str is long enough */
|
|
if(port > 65535)
|
|
return ERROR;
|
|
|
|
snprintf(port_str, sizeof(port_str), "%d", port);
|
|
result = getaddrinfo(host_name, port_str, &hints, &res);
|
|
if(result != 0) {
|
|
/*printf("GETADDRINFO: %s (%s) = %s\n",host_name,port_str,gai_strerror(result));*/
|
|
return ERROR;
|
|
}
|
|
|
|
/* create a socket */
|
|
*sd = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
|
|
if(*sd < 0) {
|
|
freeaddrinfo(res);
|
|
return ERROR;
|
|
}
|
|
|
|
/* make socket non-blocking */
|
|
flags = fcntl(*sd, F_GETFL, 0);
|
|
fcntl(*sd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
/* attempt to connect */
|
|
result = connect(*sd, res->ai_addr, res->ai_addrlen);
|
|
|
|
/* immediately successful connect */
|
|
if(result == 0) {
|
|
result = OK;
|
|
/*printf("IMMEDIATE SUCCESS\n");*/
|
|
}
|
|
|
|
/* connection error */
|
|
else if(result < 0 && errno != EINPROGRESS) {
|
|
result = ERROR;
|
|
}
|
|
|
|
/* connection in progress - wait for it... */
|
|
else {
|
|
|
|
do {
|
|
/* set connection timeout */
|
|
tv.tv_sec = timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&wfds);
|
|
FD_SET(*sd, &wfds);
|
|
rfds = wfds;
|
|
|
|
/* wait for readiness */
|
|
result = select((*sd) + 1, &rfds, &wfds, NULL, &tv);
|
|
|
|
/*printf("SELECT RESULT: %d\n",result);*/
|
|
|
|
/* timeout */
|
|
if(result == 0) {
|
|
/*printf("TIMEOUT\n");*/
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
|
|
/* an error occurred */
|
|
if(result < 0 && errno != EINTR) {
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
|
|
/* got something - check it */
|
|
else if(result > 0) {
|
|
|
|
/* get socket options to check for errors */
|
|
optlen = sizeof(int);
|
|
if(getsockopt(*sd, SOL_SOCKET, SO_ERROR, (void *)(&optval), &optlen) < 0) {
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
|
|
/* an error occurred in the connection */
|
|
if(optval != 0) {
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
|
|
/* the connection was good! */
|
|
/*
|
|
printf("CONNECT SELECT: ERRNO=%s\n",strerror(errno));
|
|
printf("CONNECT SELECT: OPTVAL=%s\n",strerror(optval));
|
|
*/
|
|
result = OK;
|
|
break;
|
|
}
|
|
|
|
/* some other error occurred */
|
|
else {
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
|
|
}
|
|
while(1);
|
|
}
|
|
|
|
|
|
freeaddrinfo(res);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* based on Beej's sendall - thanks Beej! */
|
|
int my_sendall(int s, const char *buf, int *len, int timeout) {
|
|
int total_sent = 0;
|
|
int bytes_left = 0;
|
|
int n;
|
|
fd_set wfds;
|
|
struct timeval tv;
|
|
int result = OK;
|
|
time_t start_time;
|
|
time_t current_time;
|
|
|
|
time(&start_time);
|
|
|
|
bytes_left = *len;
|
|
while(total_sent < *len) {
|
|
|
|
/* set send timeout */
|
|
tv.tv_sec = timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&wfds);
|
|
FD_SET(s, &wfds);
|
|
|
|
/* wait for readiness */
|
|
result = select(s + 1, NULL, &wfds, NULL, &tv);
|
|
|
|
/* timeout */
|
|
if(result == 0) {
|
|
/*printf("RECV SELECT TIMEOUT\n");*/
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
/* error */
|
|
else if(result < 0) {
|
|
/*printf("RECV SELECT ERROR: %s\n",strerror(errno));*/
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
|
|
/* we're ready to write some data */
|
|
result = OK;
|
|
|
|
/* send the data */
|
|
n = send(s, buf + total_sent, bytes_left, 0);
|
|
if(n == -1) {
|
|
/*printf("SEND ERROR: (%d) %s\n",s,strerror(errno));*/
|
|
break;
|
|
}
|
|
|
|
total_sent += n;
|
|
bytes_left -= n;
|
|
|
|
/* make sure we haven't overrun the timeout */
|
|
time(¤t_time);
|
|
if(current_time - start_time > timeout) {
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*len = total_sent;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* receives all data in non-blocking mode with a timeout - modelled after sendall() */
|
|
int my_recvall(int s, char *buf, int *len, int timeout) {
|
|
int total_received = 0;
|
|
int bytes_left = *len;
|
|
int n = 0;
|
|
time_t start_time;
|
|
time_t current_time;
|
|
fd_set rfds;
|
|
struct timeval tv;
|
|
int result = OK;
|
|
|
|
/* clear the receive buffer */
|
|
bzero(buf, *len);
|
|
|
|
time(&start_time);
|
|
|
|
/* receive all data */
|
|
while(total_received < *len) {
|
|
|
|
/* set receive timeout */
|
|
tv.tv_sec = timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&rfds);
|
|
FD_SET(s, &rfds);
|
|
|
|
/* wait for readiness */
|
|
result = select(s + 1, &rfds, NULL, NULL, &tv);
|
|
|
|
/* timeout */
|
|
if(result == 0) {
|
|
/*printf("RECV SELECT TIMEOUT\n");*/
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
/* error */
|
|
else if(result < 0) {
|
|
/*printf("RECV SELECT ERROR: %s\n",strerror(errno));*/
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
|
|
/* we're ready to read some data */
|
|
result = OK;
|
|
|
|
/* receive some data */
|
|
n = recv(s, buf + total_received, bytes_left, 0);
|
|
|
|
/* server disconnected */
|
|
if(n == 0) {
|
|
/*printf("SERVER DISCONNECT\n");*/
|
|
break;
|
|
}
|
|
|
|
/* apply bytes we received */
|
|
total_received += n;
|
|
bytes_left -= n;
|
|
|
|
/* make sure we haven't overrun the timeout */
|
|
time(¤t_time);
|
|
if(current_time - start_time > timeout) {
|
|
result = ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* return number of bytes actually received here */
|
|
*len = total_received;
|
|
|
|
return result;
|
|
}
|