Skip to content

Commit f00e129

Browse files
committed
MDEV-37969 sphinx_snippets() UDF fails to resolve hostname
The SphinxSE sphinx_snippets had a erronous test condition on the getaddrinfo return value. With this inverted there was always an error. The entire CSphUrl::Connect also failed to handle IPv6 addresses. As getaddrinfo can return multiple addresses it makes sense to try each of these. Performing freeaddrinfo(hp) on an error condition in getaddrinfo caused a SEGV. Removed some boilerplate #ifdef around very old MySQL versions and unused defines. This sphinx tests reuse the parsing of configuration files from the test My::Config packaging, which needed to change as sphinx supports multiple listen directives needed for the test. Without this the last specified listen only was recorded in the configuration file. charset_type removed the indexer reported 2.2.11: WARNING: key 'charset_type' was permanently removed from Sphinx configuration. Thanks to bug report by Misagh Laghaei who kindly refered me to manticoresoftware/manticoresearch#3804 by Sergey Nikolaev <[email protected]> that showed showed the getaddrinfo handling errors, and other odd handling of connection code.
1 parent cedb0ed commit f00e129

File tree

6 files changed

+95
-100
lines changed

6 files changed

+95
-100
lines changed

mysql-test/lib/My/Config.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use Carp;
2525
# many times in the config file. Note that options must be written
2626
# using '-' instead of '_' here!
2727

28-
my %multipart_options=
28+
our %multipart_options=
2929
(
3030
"plugin-load-add" => 1,
3131
"optimizer-switch" => 1,

storage/sphinx/mysql-test/sphinx/my.cnf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ xmlpipe_command = cat @ENV.MTR_SUITE_DIR/testdata.xml
77
[index test1]
88
source = src1
99
docinfo = extern
10-
charset_type = utf-8
1110
path = @ENV.MYSQLTEST_VARDIR/searchd/test1
1211

1312
[indexer]
@@ -24,6 +23,8 @@ query_log = @ENV.MYSQLTEST_VARDIR/searchd/sphinx-query.log
2423
#log-error = @ENV.MYSQLTEST_VARDIR/searchd/sphinx.log
2524
pid_file = @ENV.MYSQLTEST_VARDIR/run/searchd.pid
2625
listen = @ENV.SPHINXSEARCH_PORT
26+
listen = @ENV.SPHINXSEARCH_SOCKET
2727

2828
[ENV]
2929
SPHINXSEARCH_PORT = @OPT.port
30+
SPHINXSEARCH_SOCKET = @ENV.MYSQLTEST_VARDIR/run/sphinx.socket

storage/sphinx/mysql-test/sphinx/sphinx.result

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,20 @@ id w q
9595
1 2 test;range=meta.foo_count,100,500
9696
5 1 test;range=meta.foo_count,100,500
9797
drop table ts;
98+
# MDEV-37969 sphinx_snippets() UDF fails to resolve hostname
99+
CREATE FUNCTION sphinx_snippets RETURNS STRING SONAME 'ha_sphinx.so';
100+
SELECT sphinx_snippets('d', 'test1', 'search_term', 'sphinx://localhost:SPHINXSEARCH_PORT' AS sphinx);
101+
sphinx_snippets('d', 'test1', 'search_term', 'sphinx://localhost:SPHINXSEARCH_PORT' AS sphinx)
102+
d
103+
SELECT sphinx_snippets('this is to test groups', 'test1', 'groups', 'sphinx://127.0.0.1:19001' AS sphinx);
104+
sphinx_snippets('this is to test groups', 'test1', 'groups', 'sphinx://127.0.0.1:19001' AS sphinx)
105+
this is to test <b>groups</b>
106+
SELECT sphinx_snippets('this is to test groups', 'test1', 'groups', 'unix://SPHINXSEARCH_SOCKET' AS sphinx);
107+
sphinx_snippets('this is to test groups', 'test1', 'groups', 'unix://SPHINXSEARCH_SOCKET' AS sphinx)
108+
this is to test <b>groups</b>
109+
SELECT sphinx_snippets('d', 'test1', 'search_term', 'sphinx://somedomainthatdoesntresolve.example.com:19001' AS sphinx);
110+
ERROR HY000: Unable to connect to foreign data source: failed to resolve searchd host (name=somedomainthatdoesntresolve
111+
SELECT sphinx_snippets('this is to test groups', 'test1', 'groups', 'unix:///home/dan/repos/build-mariadb-server-10.11/mysql-test/var/tmp/missing_socket' AS sphinx);
112+
ERROR HY000: Unable to connect to foreign data source: unix:////home/dan/repos/build-mariadb-server-10.11/mysql-test/va
113+
DROP FUNCTION sphinx_snippets;
114+
# End of 10.11 tests

storage/sphinx/mysql-test/sphinx/sphinx.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,20 @@ select * from ts where q=';filter=meta.sub.list[0],4';
5454
select * from ts where q=';filter=meta.sub.list[1],4';
5555
select * from ts where q='test;range=meta.foo_count,100,500';
5656
drop table ts;
57+
58+
--echo # MDEV-37969 sphinx_snippets() UDF fails to resolve hostname
59+
60+
eval CREATE FUNCTION sphinx_snippets RETURNS STRING SONAME '$HA_SPHINX_SO';
61+
--replace_result $SPHINXSEARCH_PORT SPHINXSEARCH_PORT
62+
eval SELECT sphinx_snippets('d', 'test1', 'search_term', 'sphinx://localhost:$SPHINXSEARCH_PORT' AS sphinx);
63+
eval SELECT sphinx_snippets('this is to test groups', 'test1', 'groups', 'sphinx://127.0.0.1:$SPHINXSEARCH_PORT' AS sphinx);
64+
--replace_result $SPHINXSEARCH_SOCKET SPHINXSEARCH_SOCKET
65+
eval SELECT sphinx_snippets('this is to test groups', 'test1', 'groups', 'unix://$SPHINXSEARCH_SOCKET' AS sphinx);
66+
67+
--error ER_CONNECT_TO_FOREIGN_DATA_SOURCE
68+
eval SELECT sphinx_snippets('d', 'test1', 'search_term', 'sphinx://somedomainthatdoesntresolve.example.com:$SPHINXSEARCH_PORT' AS sphinx);
69+
--error ER_CONNECT_TO_FOREIGN_DATA_SOURCE
70+
eval SELECT sphinx_snippets('this is to test groups', 'test1', 'groups', 'unix://$MYSQL_TMP_DIR/missing_socket' AS sphinx);
71+
DROP FUNCTION sphinx_snippets;
72+
73+
--echo # End of 10.11 tests

storage/sphinx/mysql-test/sphinx/suite.pm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use My::SafeProcess;
44
use My::File::Path;
55
use mtr_report;
66

7+
# Sphinx configuration supports multiple listens
8+
$My::Config::Option::multipart_options{"listen"} = 1;
9+
710
@ISA = qw(My::Suite);
811

912
############# initialization ######################

storage/sphinx/snippets_udf.cc

Lines changed: 55 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,13 @@
2626

2727
#include <mysql_version.h>
2828

29-
#if MYSQL_VERSION_ID>=50515
3029
#include "sql_class.h"
3130
#include "sql_array.h"
32-
#elif MYSQL_VERSION_ID>50100
33-
#include "mysql_priv.h"
34-
#include <mysql/plugin.h>
35-
#else
36-
#include "../mysql_priv.h"
37-
#endif
3831

3932
#include <mysys_err.h>
4033
#include <my_sys.h>
4134

42-
#if MYSQL_VERSION_ID>=50120
4335
typedef uchar byte;
44-
#endif
4536

4637
/// partially copy-pasted stuff that should be moved elsewhere
4738

@@ -90,7 +81,6 @@ void sphUnalignedWrite ( void * pPtr, const T & tVal )
9081
#define SafeDelete(_arg) { if ( _arg ) delete ( _arg ); (_arg) = NULL; }
9182
#define SafeDeleteArray(_arg) { if ( _arg ) delete [] ( _arg ); (_arg) = NULL; }
9283

93-
#define Min(a,b) ((a)<(b)?(a):(b))
9484
#ifndef _WIN32
9585
typedef unsigned int DWORD;
9686
#endif
@@ -360,82 +350,68 @@ bool CSphUrl::Parse ( const char * sUrl, int iLen )
360350

361351
int CSphUrl::Connect()
362352
{
363-
struct sockaddr_in sin;
364-
#ifndef _WIN32
365-
struct sockaddr_un saun;
366-
#endif
367-
368-
int iDomain = 0;
369-
int iSockaddrSize = 0;
370-
struct sockaddr * pSockaddr = NULL;
371-
372-
in_addr_t ip_addr;
353+
int iSocket = -1;
373354

374355
if ( m_iPort )
375356
{
376-
iDomain = AF_INET;
377-
iSockaddrSize = sizeof(sin);
378-
pSockaddr = (struct sockaddr *) &sin;
357+
char portStr[16];
358+
struct addrinfo hints, *hp, *p;
359+
int tmp_errno;
379360

380-
memset ( &sin, 0, sizeof(sin) );
381-
sin.sin_family = AF_INET;
382-
sin.sin_port = htons ( m_iPort );
361+
my_snprintf(portStr, sizeof(portStr), "%d", m_iPort);
383362

384-
// resolve address
385-
if ( (int)( ip_addr = inet_addr ( m_sHost ) )!=(int)INADDR_NONE )
386-
memcpy ( &sin.sin_addr, &ip_addr, sizeof(ip_addr) );
387-
else
363+
memset(&hints, 0, sizeof(hints));
364+
hints.ai_family = AF_UNSPEC;
365+
hints.ai_socktype = SOCK_STREAM;
366+
367+
tmp_errno = getaddrinfo ( m_sHost, portStr, &hints, &hp );
368+
if ( tmp_errno!=0 || !hp || !hp->ai_addr )
388369
{
389-
int tmp_errno;
390-
bool bError = false;
370+
char sError[256];
371+
my_snprintf ( sError, sizeof(sError),
372+
"failed to resolve searchd host (name=%s) because %s",
373+
m_sHost, gai_strerror(tmp_errno) );
391374

392-
#if MYSQL_VERSION_ID>=50515
393-
struct addrinfo *hp = NULL;
394-
tmp_errno = getaddrinfo ( m_sHost, NULL, NULL, &hp );
395-
if ( !tmp_errno || !hp || !hp->ai_addr )
396-
{
397-
bError = true;
398-
if ( hp )
399-
freeaddrinfo ( hp );
400-
}
401-
#else
402-
struct hostent tmp_hostent, *hp;
403-
char buff2 [ GETHOSTBYNAME_BUFF_SIZE ];
404-
hp = my_gethostbyname_r ( m_sHost, &tmp_hostent, buff2, sizeof(buff2), &tmp_errno );
405-
if ( !hp )
406-
{
407-
my_gethostbyname_r_free();
408-
bError = true;
409-
}
410-
#endif
375+
my_error ( ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), sError );
376+
return -1;
377+
}
411378

412-
if ( bError )
413-
{
414-
char sError[256];
415-
my_snprintf ( sError, sizeof(sError), "failed to resolve searchd host (name=%s)", m_sHost );
379+
for (p = hp; p != NULL; p = p->ai_next)
380+
{
381+
iSocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
382+
if (iSocket == -1)
383+
continue;
416384

417-
my_error ( ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), sError );
418-
return -1;
419-
}
385+
if (connect(iSocket, p->ai_addr, p->ai_addrlen) == 0)
386+
break; // success
420387

421-
#if MYSQL_VERSION_ID>=50515
422-
memcpy ( &sin.sin_addr, hp->ai_addr, Min ( sizeof(sin.sin_addr), (size_t)hp->ai_addrlen ) );
423-
freeaddrinfo ( hp );
424-
#else
425-
memcpy ( &sin.sin_addr, hp->h_addr, Min ( sizeof(sin.sin_addr), (size_t)hp->h_length ) );
426-
my_gethostbyname_r_free();
427-
#endif
388+
close(iSocket);
389+
iSocket = -2;
428390
}
391+
freeaddrinfo ( hp );
429392
} else
430393
{
431394
#ifndef _WIN32
432-
iDomain = AF_UNIX;
433-
iSockaddrSize = sizeof(saun);
434-
pSockaddr = (struct sockaddr *) &saun;
435-
395+
struct sockaddr_un saun;
436396
memset ( &saun, 0, sizeof(saun) );
437397
saun.sun_family = AF_UNIX;
398+
399+
if (strlen(m_sHost) >= sizeof(saun.sun_path))
400+
{
401+
my_error ( ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), "Unix socket path too long" );
402+
return -1;
403+
}
438404
strncpy ( saun.sun_path, m_sHost, sizeof(saun.sun_path)-1 );
405+
406+
iSocket = socket(AF_UNIX, SOCK_STREAM, 0);
407+
if (iSocket >= 0)
408+
{
409+
if (connect(iSocket, (struct sockaddr *)&saun, sizeof(saun)) == -1)
410+
{
411+
close(iSocket);
412+
iSocket = -2;
413+
}
414+
}
439415
#else
440416
my_error ( ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), "Unix-domain sockets are not supported on Windows" );
441417
return -1;
@@ -445,36 +421,17 @@ int CSphUrl::Connect()
445421
// connect to searchd and exchange versions
446422
uint uServerVersion;
447423
uint uClientVersion = htonl ( SPHINX_SEARCHD_PROTO );
448-
int iSocket = -1;
449424
const char * pError = NULL;
450-
do
451-
{
452-
iSocket = (int)socket ( iDomain, SOCK_STREAM, 0 );
453-
if ( iSocket==-1 )
454-
{
455-
pError = "Failed to create client socket";
456-
break;
457-
}
425+
if ( iSocket == -1 )
426+
pError = "Failed to create client socket";
427+
else if ( iSocket == -2 )
428+
pError = "Failed to connect to searchd";
458429

459-
if ( connect ( iSocket, pSockaddr, iSockaddrSize )==-1 )
460-
{
461-
pError = "Failed to connect to searchd";
462-
break;
463-
}
464-
465-
if ( !sphRecv ( iSocket, (char *)&uServerVersion, sizeof(uServerVersion) ) )
466-
{
467-
pError = "Failed to receive searchd version";
468-
break;
469-
}
430+
else if ( !sphRecv ( iSocket, (char *)&uServerVersion, sizeof(uServerVersion) ) )
431+
pError = "Failed to receive searchd version";
470432

471-
if ( !sphSend ( iSocket, (char *)&uClientVersion, sizeof(uClientVersion) ) )
472-
{
473-
pError = "Failed to send client version";
474-
break;
475-
}
476-
}
477-
while(0);
433+
else if ( !sphSend ( iSocket, (char *)&uClientVersion, sizeof(uClientVersion) ) )
434+
pError = "Failed to send client version";
478435

479436
// fixme: compare versions?
480437

@@ -484,8 +441,8 @@ int CSphUrl::Connect()
484441
snprintf ( sError, sizeof(sError), "%s [%d] %s", Format(), errno, strerror(errno) );
485442
my_error ( ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), sError );
486443

487-
if ( iSocket!=-1 )
488-
close ( iSocket );
444+
if ( iSocket >= 0 )
445+
close(iSocket);
489446

490447
return -1;
491448
}

0 commit comments

Comments
 (0)