mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
db0c28eff8
Find and fix missing virtual override markings. Updates cmake maintainer flags to include -Wsuggest-override and -Winconsistent-missing-override.
353 lines
8.8 KiB
C++
353 lines
8.8 KiB
C++
/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
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-1335 USA */
|
|
|
|
#include "feedback.h"
|
|
|
|
#ifdef HAVE_NETDB_H
|
|
#include <netdb.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include <ws2tcpip.h>
|
|
#define addrinfo ADDRINFOA
|
|
#endif
|
|
|
|
namespace feedback {
|
|
|
|
static const uint FOR_READING= 0;
|
|
static const uint FOR_WRITING= 1;
|
|
|
|
/**
|
|
implementation of the Url class that sends the data via HTTP POST request.
|
|
|
|
Both http:// and https:// protocols are supported.
|
|
*/
|
|
class Url_http: public Url {
|
|
protected:
|
|
const LEX_STRING host, port, path;
|
|
LEX_STRING proxy_host, proxy_port;
|
|
my_socket fd;
|
|
bool ssl;
|
|
|
|
bool use_proxy()
|
|
{
|
|
return proxy_host.length != 0;
|
|
}
|
|
|
|
Url_http(LEX_STRING &url_arg, LEX_STRING &host_arg,
|
|
LEX_STRING &port_arg, LEX_STRING &path_arg, bool ssl_arg) :
|
|
Url(url_arg), host(host_arg), port(port_arg), path(path_arg),
|
|
fd(INVALID_SOCKET), ssl(ssl_arg)
|
|
{
|
|
proxy_host.length= 0;
|
|
}
|
|
~Url_http() override
|
|
{
|
|
my_free(host.str);
|
|
my_free(port.str);
|
|
my_free(path.str);
|
|
set_proxy(0,0);
|
|
}
|
|
|
|
public:
|
|
void abort() override;
|
|
int send(const char* data, size_t data_length) override;
|
|
int set_proxy(const char *proxy, size_t proxy_len) override
|
|
{
|
|
if (use_proxy())
|
|
{
|
|
my_free(proxy_host.str);
|
|
my_free(proxy_port.str);
|
|
}
|
|
|
|
return parse_proxy_server(proxy, proxy_len, &proxy_host, &proxy_port);
|
|
}
|
|
|
|
friend Url* http_create(const char *url, size_t url_length);
|
|
};
|
|
|
|
/**
|
|
create a Url_http object out of the url, if possible.
|
|
|
|
@note
|
|
Arbitrary limitations here.
|
|
|
|
The url must be http[s]://hostname[:port]/path
|
|
No username:password@ or ?script=parameters are supported.
|
|
|
|
But it's ok. This is not a generic purpose www browser - it only needs to be
|
|
good enough to POST the data to mariadb.org.
|
|
*/
|
|
Url* http_create(const char *url, size_t url_length)
|
|
{
|
|
const char *s;
|
|
LEX_STRING full_url= {const_cast<char*>(url), url_length};
|
|
LEX_STRING host, port, path;
|
|
bool ssl= false;
|
|
|
|
if (is_prefix(url, "http://"))
|
|
s= url + 7;
|
|
#ifdef HAVE_OPENSSL
|
|
else if (is_prefix(url, "https://"))
|
|
{
|
|
ssl= true;
|
|
s= url + 8;
|
|
}
|
|
#endif
|
|
else
|
|
return NULL;
|
|
|
|
for (url= s; *s && *s != ':' && *s != '/'; s++) /* no-op */;
|
|
host.str= const_cast<char*>(url);
|
|
host.length= s-url;
|
|
|
|
if (*s == ':')
|
|
{
|
|
for (url= ++s; *s && *s >= '0' && *s <= '9'; s++) /* no-op */;
|
|
port.str= const_cast<char*>(url);
|
|
port.length= s-url;
|
|
}
|
|
else
|
|
{
|
|
if (ssl)
|
|
{
|
|
port.str= const_cast<char*>("443");
|
|
port.length=3;
|
|
}
|
|
else
|
|
{
|
|
port.str= const_cast<char*>("80");
|
|
port.length=2;
|
|
}
|
|
}
|
|
|
|
if (*s == 0)
|
|
{
|
|
path.str= const_cast<char*>("/");
|
|
path.length= 1;
|
|
}
|
|
else
|
|
{
|
|
path.str= const_cast<char*>(s);
|
|
path.length= strlen(s);
|
|
}
|
|
if (!host.length || !port.length || path.str[0] != '/')
|
|
return NULL;
|
|
|
|
host.str= my_strndup(PSI_INSTRUMENT_ME, host.str, host.length, MYF(MY_WME));
|
|
port.str= my_strndup(PSI_INSTRUMENT_ME, port.str, port.length, MYF(MY_WME));
|
|
path.str= my_strndup(PSI_INSTRUMENT_ME, path.str, path.length, MYF(MY_WME));
|
|
|
|
if (!host.str || !port.str || !path.str)
|
|
{
|
|
my_free(host.str);
|
|
my_free(port.str);
|
|
my_free(path.str);
|
|
return NULL;
|
|
}
|
|
|
|
return new Url_http(full_url, host, port, path, ssl);
|
|
}
|
|
|
|
void Url_http::abort()
|
|
{
|
|
if (fd != INVALID_SOCKET)
|
|
closesocket(fd); // interrupt I/O waits
|
|
}
|
|
|
|
/* do the vio_write and check that all data were sent ok */
|
|
#define write_check(VIO, DATA, LEN) \
|
|
(vio_write((VIO), (uchar*)(DATA), (LEN)) != (LEN))
|
|
|
|
int Url_http::send(const char* data, size_t data_length)
|
|
{
|
|
char buf[1024];
|
|
size_t len= 0;
|
|
|
|
addrinfo *addrs, *addr, filter= {0, AF_UNSPEC, SOCK_STREAM, 6, 0, 0, 0, 0};
|
|
int res= use_proxy() ?
|
|
getaddrinfo(proxy_host.str, proxy_port.str, &filter, &addrs) :
|
|
getaddrinfo(host.str, port.str, &filter, &addrs);
|
|
|
|
if (res)
|
|
{
|
|
sql_print_error("feedback plugin: getaddrinfo() failed for url '%s': %s",
|
|
full_url.str, gai_strerror(res));
|
|
return 1;
|
|
}
|
|
|
|
DBUG_ASSERT(fd == INVALID_SOCKET);
|
|
for (addr= addrs; addr != NULL; addr= addr->ai_next)
|
|
{
|
|
fd= socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
|
if (fd == INVALID_SOCKET)
|
|
continue;
|
|
|
|
if (connect(fd, addr->ai_addr, (int) addr->ai_addrlen) == 0)
|
|
break;
|
|
|
|
closesocket(fd);
|
|
fd= INVALID_SOCKET;
|
|
}
|
|
|
|
freeaddrinfo(addrs);
|
|
|
|
if (fd == INVALID_SOCKET)
|
|
{
|
|
sql_print_error("feedback plugin: could not connect for url '%s'",
|
|
full_url.str);
|
|
return 1;
|
|
}
|
|
|
|
Vio *vio= vio_new(fd, VIO_TYPE_TCPIP, 0);
|
|
if (!vio)
|
|
{
|
|
sql_print_error("feedback plugin: vio_new failed for url '%s'",
|
|
full_url.str);
|
|
closesocket(fd);
|
|
fd= INVALID_SOCKET;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
struct st_VioSSLFd *UNINIT_VAR(ssl_fd);
|
|
if (ssl)
|
|
{
|
|
enum enum_ssl_init_error ssl_init_error= SSL_INITERR_NOERROR;
|
|
ulong ssl_error= 0;
|
|
if (!(ssl_fd= new_VioSSLConnectorFd(0, 0, 0, 0, 0, &ssl_init_error, 0, 0)) ||
|
|
sslconnect(ssl_fd, vio, send_timeout, &ssl_error))
|
|
{
|
|
const char *err;
|
|
if (ssl_init_error != SSL_INITERR_NOERROR)
|
|
err= sslGetErrString(ssl_init_error);
|
|
else
|
|
{
|
|
ERR_error_string_n(ssl_error, buf, sizeof(buf));
|
|
buf[sizeof(buf)-1]= 0;
|
|
err= buf;
|
|
}
|
|
|
|
sql_print_error("feedback plugin: ssl failed for url '%s' %s",
|
|
full_url.str, err);
|
|
if (ssl_fd)
|
|
free_vio_ssl_acceptor_fd(ssl_fd);
|
|
closesocket(fd);
|
|
vio_delete(vio);
|
|
fd= INVALID_SOCKET;
|
|
return 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static const LEX_STRING boundary=
|
|
{ C_STRING_WITH_LEN("----------------------------ba4f3696b39f") };
|
|
static const LEX_STRING header=
|
|
{ C_STRING_WITH_LEN("\r\n"
|
|
"Content-Disposition: form-data; name=\"data\"; filename=\"-\"\r\n"
|
|
"Content-Type: application/octet-stream\r\n\r\n")
|
|
};
|
|
|
|
len= my_snprintf(buf, sizeof(buf),
|
|
use_proxy() ? "POST http://%s:%s/" : "POST ",
|
|
host.str, port.str);
|
|
|
|
len+= my_snprintf(buf+len, sizeof(buf)-len,
|
|
"%s HTTP/1.0\r\n"
|
|
"User-Agent: MariaDB User Feedback Plugin\r\n"
|
|
"Host: %s:%s\r\n"
|
|
"Accept: */*\r\n"
|
|
"Content-Length: %u\r\n"
|
|
"Content-Type: multipart/form-data; boundary=%s\r\n"
|
|
"\r\n",
|
|
path.str, host.str, port.str,
|
|
(uint)(2*boundary.length + header.length + data_length + 4),
|
|
boundary.str + 2);
|
|
|
|
vio_timeout(vio, FOR_READING, send_timeout);
|
|
vio_timeout(vio, FOR_WRITING, send_timeout);
|
|
res = write_check(vio, buf, len)
|
|
|| write_check(vio, boundary.str, boundary.length)
|
|
|| write_check(vio, header.str, header.length)
|
|
|| write_check(vio, data, data_length)
|
|
|| write_check(vio, boundary.str, boundary.length)
|
|
|| write_check(vio, "--\r\n", 4);
|
|
|
|
if (res)
|
|
sql_print_error("feedback plugin: failed to send report to '%s'",
|
|
full_url.str);
|
|
else
|
|
{
|
|
sql_print_information("feedback plugin: report to '%s' was sent",
|
|
full_url.str);
|
|
|
|
/*
|
|
if the data were send successfully, read the reply.
|
|
Extract the first string between <h1>...</h1> tags
|
|
and put it as a server reply into the error log.
|
|
*/
|
|
len= 0;
|
|
for (;;)
|
|
{
|
|
size_t i= sizeof(buf) - len - 1;
|
|
if (i)
|
|
i= vio_read(vio, (uchar*)buf + len, i);
|
|
if ((int)i <= 0)
|
|
break;
|
|
len+= i;
|
|
}
|
|
if (len)
|
|
{
|
|
char *from;
|
|
|
|
buf[len]= 0; // safety
|
|
|
|
if ((from= strstr(buf, "<h1>")))
|
|
{
|
|
from+= 4;
|
|
char *to= strstr(from, "</h1>");
|
|
if (to)
|
|
*to= 0;
|
|
else
|
|
from= NULL;
|
|
}
|
|
if (from)
|
|
sql_print_information("feedback plugin: server replied '%s'", from);
|
|
else
|
|
sql_print_warning("feedback plugin: failed to parse server reply");
|
|
}
|
|
else
|
|
{
|
|
res= 1;
|
|
sql_print_error("feedback plugin: failed to read server reply");
|
|
}
|
|
}
|
|
|
|
vio_delete(vio);
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
if (ssl)
|
|
{
|
|
SSL_CTX_free(ssl_fd->ssl_context);
|
|
my_free(ssl_fd);
|
|
}
|
|
#endif
|
|
|
|
fd= INVALID_SOCKET;
|
|
return res;
|
|
}
|
|
|
|
} // namespace feedback
|
|
|