306 lines
8.0 KiB
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);
|
||
|
}
|