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