denyhosts/clamav/libclamav/www.c

306 lines
8.0 KiB
C

/*
* Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved.
*
* Author: Shawn Webb
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#if !defined(_WIN32)
#include <sys/socket.h>
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#else
#include <sys/time.h>
#endif
#include <netinet/in.h>
#include <netdb.h>
#endif
#include "platform.h"
#include "others.h"
#include "clamav.h"
#include "www.h"
int connect_host(const char *host, const char *port, uint32_t timeout, int useAsync)
{
int sockfd = -1;
struct addrinfo hints, *servinfo = NULL, *p = NULL;
int flags = 0, error;
socklen_t len;
fd_set read_fds, write_fds;
struct timeval tv;
#ifdef _WIN32
int iResult;
WSADATA wsaData;
/* Force initialization of Windows sockets, even if it already happened elsewhere */
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
return -1;
#endif
memset(&hints, 0x00, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(host, port, &hints, &servinfo))
return -1;
for (p = servinfo; p != NULL; p = p->ai_next) {
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0)
continue;
if (useAsync) {
flags = fcntl(sockfd, F_GETFL, 0);
if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) {
closesocket(sockfd);
continue;
}
}
if ((error = connect(sockfd, p->ai_addr, p->ai_addrlen))) {
if (useAsync) {
if (errno != EINPROGRESS) {
closesocket(sockfd);
continue;
}
errno = 0;
FD_ZERO(&write_fds);
FD_ZERO(&read_fds);
FD_SET(sockfd, &read_fds);
FD_SET(sockfd, &write_fds);
/* TODO: Make this timeout configurable */
tv.tv_sec = timeout;
tv.tv_usec = 0;
if (select(sockfd + 1, &read_fds, &write_fds, NULL, &tv) <= 0) {
closesocket(sockfd);
continue;
}
if (FD_ISSET(sockfd, &read_fds) || FD_ISSET(sockfd, &write_fds)) {
len = sizeof(error);
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
closesocket(sockfd);
continue;
}
} else {
closesocket(sockfd);
continue;
}
} else {
closesocket(sockfd);
continue;
}
}
/* Connected to host */
break;
}
if (!(p)) {
freeaddrinfo(servinfo);
if (sockfd >= 0)
closesocket(sockfd);
return -1;
}
freeaddrinfo(servinfo);
/* Return to using a synchronous socket to make Linux happy */
if (useAsync && (sockfd >= 0)) {
if (fcntl(sockfd, F_SETFL, flags) < 0) {
closesocket(sockfd);
return -1;
}
}
return sockfd;
}
size_t encoded_size(const char *postdata)
{
const char *p;
size_t len = 0;
for (p = postdata; *p != '\0'; p++)
len += isalnum(*p) ? 1 : 3;
return len;
}
char *encode_data(const char *postdata)
{
char *buf;
size_t bufsz, i, j;
bufsz = encoded_size(postdata);
if (bufsz == 0)
return NULL;
buf = cli_calloc(1, bufsz + 1);
if (!(buf))
return NULL;
for (i = 0, j = 0; postdata[i] != '\0'; i++) {
if (isalnum(postdata[i])) {
buf[j++] = postdata[i];
} else {
sprintf(buf + j, "%%%02x", postdata[i]);
j += 3;
}
}
return buf;
}
void submit_post(const char *host, const char *port, const char *method, const char *url, const char *postdata, uint32_t timeout)
{
int sockfd, n;
unsigned int i;
char *buf, *encoded = NULL;
size_t bufsz;
ssize_t recvsz;
char chunkedlen[21];
fd_set readfds;
struct timeval tv;
char *acceptable_methods[] = {
"GET",
"PUT",
"POST",
NULL};
for (i = 0; acceptable_methods[i] != NULL; i++)
if (!strcmp(method, acceptable_methods[i]))
break;
if (acceptable_methods[i] == NULL)
return;
bufsz = strlen(method);
bufsz += sizeof(" HTTP/1.1") + 2; /* Yes. Three blank spaces. +1 for the \n */
bufsz += strlen(url);
bufsz += sizeof("Host: \r\n");
bufsz += strlen(host);
bufsz += sizeof("Connection: Close\r\n");
bufsz += 4; /* +4 for \r\n\r\n */
if (!strcmp(method, "POST") || !strcmp(method, "PUT")) {
encoded = encode_data(postdata);
if (!(encoded))
return;
snprintf(chunkedlen, sizeof(chunkedlen), "%zu", strlen(encoded));
bufsz += sizeof("Content-Type: application/x-www-form-urlencoded\r\n");
bufsz += sizeof("Content-Length: \r\n");
bufsz += strlen(chunkedlen);
bufsz += strlen(encoded);
}
buf = cli_calloc(1, bufsz);
if (!(buf)) {
if ((encoded))
free(encoded);
return;
}
snprintf(buf, bufsz, "%s %s HTTP/1.1\r\n", method, url);
snprintf(buf + strlen(buf), bufsz - strlen(buf), "Host: %s\r\n", host);
snprintf(buf + strlen(buf), bufsz - strlen(buf), "Connection: Close\r\n");
if (!strcmp(method, "POST") || !strcmp(method, "PUT")) {
snprintf(buf + strlen(buf), bufsz - strlen(buf), "Content-Type: application/x-www-form-urlencoded\r\n");
snprintf(buf + strlen(buf), bufsz - strlen(buf), "Content-Length: %s\r\n", chunkedlen);
snprintf(buf + strlen(buf), bufsz - strlen(buf), "\r\n");
snprintf(buf + strlen(buf), bufsz - strlen(buf), "%s", encoded);
free(encoded);
}
#if defined(_WIN32)
sockfd = connect_host(host, port, timeout, 0);
#else
sockfd = connect_host(host, port, timeout, 1);
#endif
if (sockfd < 0) {
free(buf);
return;
}
cli_dbgmsg("stats - Connected to %s:%s\n", host, port);
if ((size_t)send(sockfd, buf, strlen(buf), 0) != (size_t)strlen(buf)) {
closesocket(sockfd);
free(buf);
return;
}
cli_dbgmsg("stats - Sending %s\n", buf);
while (1) {
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
/*
* Check to make sure the stats submitted okay (so that we don't kill the HTTP request
* while it's being processed). Give a ten-second timeout so we don't have a major
* impact on scanning.
*/
tv.tv_sec = timeout;
tv.tv_usec = 0;
if ((n = select(sockfd + 1, &readfds, NULL, NULL, &tv)) <= 0)
break;
if (FD_ISSET(sockfd, &readfds)) {
memset(buf, 0x00, bufsz);
if ((recvsz = recv(sockfd, buf, bufsz - 1, 0) <= 0))
break;
buf[bufsz - 1] = '\0';
cli_dbgmsg("stats - received: %s\n", buf);
if (strstr(buf, "STATOK")) {
cli_dbgmsg("stats - Data received okay\n");
break;
}
}
}
closesocket(sockfd);
free(buf);
}