2
* Copyright (C) 2014 Cisco and/or its affiliates. All rights reserved.
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License version 2 as
8
* published by the Free Software Foundation.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22
#include "clamav-config.h"
35
#include <sys/types.h>
41
#include <sys/socket.h>
42
#include <sys/select.h>
43
#include <netinet/in.h>
49
#include <openssl/ssl.h>
50
#include <openssl/err.h>
51
#include "libclamav/crypto.h"
53
#include "libclamav/others.h"
54
#include "libclamav/clamav.h"
55
#include "libclamav/www.h"
57
int connect_host(const char *host, const char *port, uint32_t timeout, int useAsync)
60
struct addrinfo hints, *servinfo, *p;
63
fd_set read_fds, write_fds;
69
/* Force initialization of Windows sockets, even if it already happened elsewhere */
70
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
75
memset(&hints, 0x00, sizeof(struct addrinfo));
76
hints.ai_family = AF_UNSPEC;
77
hints.ai_socktype = SOCK_STREAM;
79
if (getaddrinfo(host, port, &hints, &servinfo))
82
for (p = servinfo; p != NULL; p = p->ai_next) {
83
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
88
flags = fcntl(sockfd, F_GETFL, 0);
89
if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) {
95
if ((error = connect(sockfd, p->ai_addr, p->ai_addrlen))) {
97
if (errno != EINPROGRESS) {
105
FD_SET(sockfd, &read_fds);
106
FD_SET(sockfd, &write_fds);
108
/* TODO: Make this timeout configurable */
111
if (select(sockfd + 1, &read_fds, &write_fds, NULL, &tv) <= 0) {
116
if (FD_ISSET(sockfd, &read_fds) || FD_ISSET(sockfd, &write_fds)) {
118
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
133
/* Connected to host */
138
freeaddrinfo(servinfo);
144
freeaddrinfo(servinfo);
146
/* Return to using a synchronous socket to make Linux happy */
148
if (fcntl(sockfd, F_SETFL, flags) < 0) {
157
size_t encoded_size(const char *postdata)
162
for (p = postdata; *p != '\0'; p++)
163
len += isalnum(*p) ? 1 : 3;
168
char *encode_data(const char *postdata)
173
bufsz = encoded_size(postdata);
177
buf = cli_calloc(1, bufsz+1);
181
for (i=0, j=0; postdata[i] != '\0'; i++) {
182
if (isalnum(postdata[i])) {
183
buf[j++] = postdata[i];
185
sprintf(buf+j, "%%%02x", postdata[i]);
193
void submit_post(const char *host, const char *port, const char *method, const char *url, const char *postdata, uint32_t timeout)
197
char *buf, *encoded=NULL;
203
char *acceptable_methods[] = {
210
for (i=0; acceptable_methods[i] != NULL; i++)
211
if (!strcmp(method, acceptable_methods[i]))
214
if (acceptable_methods[i] == NULL)
217
bufsz = strlen(method);
218
bufsz += sizeof(" HTTP/1.1") + 2; /* Yes. Three blank spaces. +1 for the \n */
219
bufsz += strlen(url);
220
bufsz += sizeof("Host: \r\n");
221
bufsz += strlen(host);
222
bufsz += sizeof("Connection: Close\r\n");
223
bufsz += 4; /* +4 for \r\n\r\n */
225
if (!strcmp(method, "POST") || !strcmp(method, "PUT")) {
226
encoded = encode_data(postdata);
230
snprintf(chunkedlen, sizeof(chunkedlen), "%u", strlen(encoded));
232
snprintf(chunkedlen, sizeof(chunkedlen), "%zu", strlen(encoded));
234
bufsz += sizeof("Content-Type: application/x-www-form-urlencoded\r\n");
235
bufsz += sizeof("Content-Length: \r\n");
236
bufsz += strlen(chunkedlen);
237
bufsz += strlen(encoded);
240
buf = cli_calloc(1, bufsz);
248
snprintf(buf, bufsz, "%s %s HTTP/1.1\r\n", method, url);
249
snprintf(buf+strlen(buf), bufsz-strlen(buf), "Host: %s\r\n", host);
250
snprintf(buf+strlen(buf), bufsz-strlen(buf), "Connection: Close\r\n");
252
if (!strcmp(method, "POST") || !strcmp(method, "PUT")) {
253
snprintf(buf+strlen(buf), bufsz-strlen(buf), "Content-Type: application/x-www-form-urlencoded\r\n");
254
snprintf(buf+strlen(buf), bufsz-strlen(buf), "Content-Length: %s\r\n", chunkedlen);
255
snprintf(buf+strlen(buf), bufsz-strlen(buf), "\r\n");
256
snprintf(buf+strlen(buf), bufsz-strlen(buf), "%s", encoded);
260
sockfd = connect_host(host, port, timeout, 0);
262
sockfd = connect_host(host, port, timeout, 1);
269
cli_dbgmsg("stats - Connected to %s:%s\n", host, port);
271
if (send(sockfd, buf, strlen(buf), 0) != strlen(buf)) {
277
cli_dbgmsg("stats - Sending %s\n", buf);
281
FD_SET(sockfd, &readfds);
284
* Check to make sure the stats submitted okay (so that we don't kill the HTTP request
285
* while it's being processed). Give a ten-second timeout so we don't have a major
286
* impact on scanning.
290
if ((n = select(sockfd+1, &readfds, NULL, NULL, &tv)) <= 0)
293
if (FD_ISSET(sockfd, &readfds)) {
294
memset(buf, 0x00, bufsz);
295
if ((recvsz = recv(sockfd, buf, bufsz-1, 0) <= 0))
300
cli_dbgmsg("stats - received: %s\n", buf);
302
if (strstr(buf, "STATOK")) {
303
cli_dbgmsg("stats - Data received okay\n");