From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtpng1.m.smailru.net (smtpng1.m.smailru.net [94.100.181.251]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 7B94B469719 for ; Tue, 18 Feb 2020 16:55:59 +0300 (MSK) From: Roman Khabibov Content-Type: multipart/alternative; boundary="Apple-Mail=_803335FA-DC9C-4F4E-BCC8-D062C2D2C139" Mime-Version: 1.0 (Mac OS X Mail 13.0 \(3594.4.19\)) Date: Tue, 18 Feb 2020 16:55:57 +0300 References: Message-Id: Subject: [Tarantool-patches] Fwd: [tarantool-patches] [server-dev] Re: Re: [PATCH v2 1/2] lua: return getaddrinfo() errors List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Sergey Ostanevich Cc: tarantool-patches@dev.tarantool.org --Apple-Mail=_803335FA-DC9C-4F4E-BCC8-D062C2D2C139 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii > Begin forwarded message: >=20 > From: Roman Khabibov > Subject: Re: [tarantool-patches] [server-dev] Re: Re: [PATCH v2 1/2] = lua: return getaddrinfo() errors > Date: December 26, 2019 at 20:29:16 GMT+3 > To: Sergey Ostanevich > Cc: tarantool-patches@dev.tarantool.org, Alexander Turenko = >=20 > Hi! Alexander, thanks for the review. Sergos, can you, please, do a = second review? >=20 > = https://github.com/tarantool/tarantool/tree/romanhabibov/gh-4138-getaddrin= fo-full-ci = >=20 >> On Dec 23, 2019, at 16:36, Alexander Turenko = wrote: >>=20 >> Comments below are trivial. Let's fix and proceed with the next = reviewer >> (Sergey O.). Please, resend the patchset for him. >>=20 >> I still think you should run CI on all targets (push to ...-full-ci >> branch). >>=20 >> LGTM except minor comments below. >>=20 >> WBR, Alexander Turenko. >>=20 >>> @@ -1360,8 +1363,12 @@ local function lsocket_tcp_connect(self, = host, port) >>> -- This function is broken by design >>> local ga_opts =3D { family =3D 'AF_INET', type =3D 'SOCK_STREAM' = } >>> local timeout =3D deadline - fiber.clock() >>> - local dns =3D getaddrinfo(host, port, timeout, ga_opts) >>> - if dns =3D=3D nil or #dns =3D=3D 0 then >>> + local dns, err =3D getaddrinfo(host, port, timeout, ga_opts) >>> + if dns =3D=3D nil then >>> + self._errno =3D boxerrno() >>=20 >> Master's socket _errno was set to EINVAL before this patch. Let's = keep >> this behaviour, but change the 2nd return value. >>=20 >> I mean: >>=20 >> | if dns =3D=3D nil then >> | self._errno =3D boxerrno.EINVAL >> | return nil, err >> | end >> | if #dns =3D=3D 0 then >> | self._errno =3D boxerrno.EINVAL >> | return nil, socket_error(self) >> | end > @@ -1360,8 +1363,12 @@ local function lsocket_tcp_connect(self, host, = port) > -- This function is broken by design > local ga_opts =3D { family =3D 'AF_INET', type =3D 'SOCK_STREAM' } > local timeout =3D deadline - fiber.clock() > - local dns =3D getaddrinfo(host, port, timeout, ga_opts) > - if dns =3D=3D nil or #dns =3D=3D 0 then > + local dns, err =3D getaddrinfo(host, port, timeout, ga_opts) > + if dns =3D=3D nil then > + self._errno =3D boxerrno.EINVAL > + return nil, err > + end > + if #dns =3D=3D 0 then > self._errno =3D boxerrno.EINVAL > return nil, socket_error(self) > end >=20 >>> + return nil, err >>> + end >>> + if #dns =3D=3D 0 then >>> self._errno =3D boxerrno.EINVAL >>> return nil, socket_error(self) >>> end >>=20 >>> diff --git a/test/app/socket.test.lua b/test/app/socket.test.lua >>> index c72d41763..7086e496b 100644 >>> --- a/test/app/socket.test.lua >>> +++ b/test/app/socket.test.lua >>> @@ -960,6 +968,27 @@ server:close() >>>=20 >>> test_run:cmd("clear filter") >>>=20 >>> +-- gh-4138 Check getaddrinfo() error from socket:connect() only. >>> +-- Error code and error message returned by getaddrinfo() depends >>> +-- on system's gai_strerror(). >>> +test_run:cmd("setopt delimiter ';'") >>> +function check_err(err) >>> + if err =3D=3D 'getaddrinfo: nodename nor servname provided, or = not known' or >>> + err =3D=3D 'getaddrinfo: Servname not supported for = ai_socktype' or >>> + err =3D=3D 'getaddrinfo: Name or service not known' then >>> + return true >>> + end >>> + return false >>> +end; >>=20 >> You added the following error message to coio_getaddrinfo() unit test = in >> the first commit: >>=20 >> | const char *exp_errmsg_4 =3D "getaddrinfo: hostname nor servname = provided" >> | ", or not known"; >>=20 >> So it worth to add it here too. We'll enable the test of FreeBSD = sooner >> or later. >>=20 >> Also I got EAI_AGAIN on my system now (maybe something was changed on >> DNS server side). Let's add it too (for both commits). > +function check_err(err) > + if err =3D=3D 'getaddrinfo: nodename nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: Servname not supported for = ai_socktype' or > + err =3D=3D 'getaddrinfo: Name or service not known' or > + err =3D=3D 'getaddrinfo: hostname nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: temporary failure in name resolution' = then > + return true > + end > + return false > +end; >=20 >>> diff --git a/test/box/net.box.test.lua b/test/box/net.box.test.lua >>> index 8e65ff470..9c9aee1ad 100644 >>> --- a/test/box/net.box.test.lua >>> +++ b/test/box/net.box.test.lua >>> @@ -1582,4 +1582,21 @@ test_run:wait_log('default', '00000030:.*', = nil, 10) >>> -- we expect nothing below, so don't wait >>> test_run:grep_log('default', '00000040:.*') >>>=20 >>> +-- gh-4138 Check getaddrinfo() error from connect() only. Error >>> +-- code and error message returned by getaddrinfo() depends on >>> +-- system's gai_strerror(). >>> +test_run:cmd("setopt delimiter ';'") >>> +function check_err(err) >>> + if err =3D=3D 'getaddrinfo: nodename nor servname provided, or = not known' or >>> + err =3D=3D 'getaddrinfo: Servname not supported for = ai_socktype' or >>> + err =3D=3D 'getaddrinfo: Name or service not known' then >>> + return true >>> + end >>> + return false >>> +end; >>> +test_run:cmd("setopt delimiter ''"); >>=20 >> Same here. > Done. >=20 >=20 > commit 22937d2fce2d127a762a32e392d2c4d525c22c0b > Author: Roman Khabibov > > Date: Thu Nov 21 14:37:54 2019 +0300 >=20 > lua: return getaddrinfo() errors >=20 > Add getaddrinfo() errors into the several fuctions of socket. Now > getaddrinfo() can return a pair of values (nil and error message) > in case of error. >=20 > Closes #4138 >=20 > @TarantoolBot document > Title: socket API changes >=20 > * socket.getaddrinfo() >=20 > Can return error message as second return value in case of > internal getaddrinfo() error. >=20 > Example: > tarantool> socket.getaddrinfo('non_exists_hostname', 3301) > --- > - null > - 'getaddrinfo: nodename nor servname provided, or not known' > ... >=20 > * socket.tcp_connect() >=20 > Can return socket.getaddrinfo() error message as second return > value. >=20 > Example: > tarantool> socket.tcp_connect('non_exists_hostname', 3301) > --- > - null > - 'getaddrinfo: nodename nor servname provided, or not known' > ... >=20 > diff --git a/src/box/lua/net_box.lua b/src/box/lua/net_box.lua > index c2e1bb9c4..2fb3e6df6 100644 > --- a/src/box/lua/net_box.lua > +++ b/src/box/lua/net_box.lua > @@ -150,9 +150,12 @@ local function next_id(id) return band(id + 1, = 0x7FFFFFFF) end > local function establish_connection(host, port, timeout) > local timeout =3D timeout or DEFAULT_CONNECT_TIMEOUT > local begin =3D fiber.clock() > - local s =3D socket.tcp_connect(host, port, timeout) > + local s, err =3D socket.tcp_connect(host, port, timeout) > if not s then > - return nil, errno.strerror(errno()) > + if not err then > + return nil, errno.strerror(errno()) > + end > + return nil, err > end > local msg =3D s:read({chunk =3D IPROTO_GREETING_SIZE}, > timeout - (fiber.clock() - begin)) > diff --git a/src/lua/socket.c b/src/lua/socket.c > index 130378caf..dbade2d27 100644 > --- a/src/lua/socket.c > +++ b/src/lua/socket.c > @@ -774,6 +774,18 @@ lbox_getaddrinfo_result_wrapper(struct lua_State = *L) > return 1; > } >=20 > +/** > + * Wrap coio_getaddrinfo() and call it. Push returned values onto > + * @a L Lua stack. > + * > + * @param L Lua stack. > + * > + * @retval 1 Number of returned values by Lua function if > + * coio_getaddrinfo() success. > + * @retval 2 Number of returned values by Lua function if > + * coio_getaddrinfo() is failed (first is nil, second is error > + * message). > + */ > static int > lbox_socket_getaddrinfo(struct lua_State *L) > { > @@ -816,7 +828,9 @@ lbox_socket_getaddrinfo(struct lua_State *L) >=20 > if (dns_res !=3D 0) { > lua_pushnil(L); > - return 1; > + struct error *err =3D diag_get()->last; > + lua_pushstring(L, err->errmsg); > + return 2; > } >=20 > /* no results */ > diff --git a/src/lua/socket.lua b/src/lua/socket.lua > index a334ad45b..ce6dab301 100644 > --- a/src/lua/socket.lua > +++ b/src/lua/socket.lua > @@ -1041,9 +1041,12 @@ local function tcp_connect(host, port, timeout) > end > local timeout =3D timeout or TIMEOUT_INFINITY > local stop =3D fiber.clock() + timeout > - local dns =3D getaddrinfo(host, port, timeout, { type =3D = 'SOCK_STREAM', > + local dns, err =3D getaddrinfo(host, port, timeout, { type =3D = 'SOCK_STREAM', > protocol =3D 'tcp' }) > - if dns =3D=3D nil or #dns =3D=3D 0 then > + if dns =3D=3D nil then > + return nil, err > + end > + if #dns =3D=3D 0 then > boxerrno(boxerrno.EINVAL) > return nil > end > @@ -1360,8 +1363,12 @@ local function lsocket_tcp_connect(self, host, = port) > -- This function is broken by design > local ga_opts =3D { family =3D 'AF_INET', type =3D 'SOCK_STREAM' } > local timeout =3D deadline - fiber.clock() > - local dns =3D getaddrinfo(host, port, timeout, ga_opts) > - if dns =3D=3D nil or #dns =3D=3D 0 then > + local dns, err =3D getaddrinfo(host, port, timeout, ga_opts) > + if dns =3D=3D nil then > + self._errno =3D boxerrno.EINVAL > + return nil, err > + end > + if #dns =3D=3D 0 then > self._errno =3D boxerrno.EINVAL > return nil, socket_error(self) > end > @@ -1547,9 +1554,12 @@ local function lsocket_connect(host, port) > if host =3D=3D nil or port =3D=3D nil then > error("Usage: luasocket.connect(host, port)") > end > - local s =3D tcp_connect(host, port) > + local s, err =3D tcp_connect(host, port) > if not s then > - return nil, boxerrno.strerror() > + if not err then > + return nil, boxerrno.strerror() > + end > + return nil, err > end > setmetatable(s, lsocket_tcp_client_mt) > return s > diff --git a/test/app/socket.result b/test/app/socket.result > index fd299424c..aca189f4c 100644 > --- a/test/app/socket.result > +++ b/test/app/socket.result > @@ -941,10 +941,20 @@ sc:close() > - true > ... > -- tcp_connect > --- test timeout > -socket.tcp_connect('127.0.0.1', 80, 0.00000000001) > +-- Test timeout. In this test, tcp_connect can return the second > +-- output value from internal.getaddrinfo (usually on Mac OS, but > +-- theoretically it can happen on Linux too). Sometimes > +-- getaddrinfo() is timed out, sometimes connect. On Linux however > +-- getaddrinfo is fast enough to never give timeout error in > +-- the case. So, there are two sources of timeout errors that are > +-- reported differently. This difference has appeared after > +-- gh-4138 patch. > +s, err =3D socket.tcp_connect('127.0.0.1', 80, 0.00000000001) > --- > -- null > +... > +s =3D=3D nil > +--- > +- true > ... > -- AF_INET > s =3D socket('AF_INET', 'SOCK_STREAM', 'tcp') > @@ -1606,7 +1616,7 @@ fio.stat(path) =3D=3D nil > { socket.tcp_connect('abrakadabra#123') =3D=3D nil, errno.strerror() } > --- > - - true > - - Invalid argument > + - Input/output error > ... > -- wrong options for getaddrinfo > socket.getaddrinfo('host', 'port', { type =3D 'WRONG' }) =3D=3D nil = and errno() =3D=3D errno.EINVAL > @@ -2816,6 +2826,50 @@ test_run:cmd("clear filter") > --- > - true > ... > +-- gh-4138 Check getaddrinfo() error from socket:connect() only. > +-- Error code and error message returned by getaddrinfo() depends > +-- on system's gai_strerror(). > +test_run:cmd("setopt delimiter ';'") > +--- > +- true > +... > +function check_err(err) > + if err =3D=3D 'getaddrinfo: nodename nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: Servname not supported for = ai_socktype' or > + err =3D=3D 'getaddrinfo: Name or service not known' or > + err =3D=3D 'getaddrinfo: hostname nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: temporary failure in name resolution' = then > + return true > + end > + return false > +end; > +--- > +... > +test_run:cmd("setopt delimiter ''"); > +--- > +- true > +... > +s, err =3D socket.getaddrinfo('non_exists_hostname', 3301) > +--- > +... > +check_err(err) > +--- > +- true > +... > +s, err =3D socket.connect('non_exists_hostname', 3301) > +--- > +... > +check_err(err) > +--- > +- true > +... > +s, err =3D socket.tcp_connect('non_exists_hostname', 3301) > +--- > +... > +check_err(err) > +--- > +- true > +... > -- case: sicket receive inconsistent behavior > chan =3D fiber.channel() > --- > diff --git a/test/app/socket.test.lua b/test/app/socket.test.lua > index c72d41763..223935e66 100644 > --- a/test/app/socket.test.lua > +++ b/test/app/socket.test.lua > @@ -300,8 +300,16 @@ sc:close() >=20 > -- tcp_connect >=20 > --- test timeout > -socket.tcp_connect('127.0.0.1', 80, 0.00000000001) > +-- Test timeout. In this test, tcp_connect can return the second > +-- output value from internal.getaddrinfo (usually on Mac OS, but > +-- theoretically it can happen on Linux too). Sometimes > +-- getaddrinfo() is timed out, sometimes connect. On Linux however > +-- getaddrinfo is fast enough to never give timeout error in > +-- the case. So, there are two sources of timeout errors that are > +-- reported differently. This difference has appeared after > +-- gh-4138 patch. > +s, err =3D socket.tcp_connect('127.0.0.1', 80, 0.00000000001) > +s =3D=3D nil >=20 > -- AF_INET > s =3D socket('AF_INET', 'SOCK_STREAM', 'tcp') > @@ -960,6 +968,29 @@ server:close() >=20 > test_run:cmd("clear filter") >=20 > +-- gh-4138 Check getaddrinfo() error from socket:connect() only. > +-- Error code and error message returned by getaddrinfo() depends > +-- on system's gai_strerror(). > +test_run:cmd("setopt delimiter ';'") > +function check_err(err) > + if err =3D=3D 'getaddrinfo: nodename nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: Servname not supported for = ai_socktype' or > + err =3D=3D 'getaddrinfo: Name or service not known' or > + err =3D=3D 'getaddrinfo: hostname nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: temporary failure in name resolution' = then > + return true > + end > + return false > +end; > +test_run:cmd("setopt delimiter ''"); > + > +s, err =3D socket.getaddrinfo('non_exists_hostname', 3301) > +check_err(err) > +s, err =3D socket.connect('non_exists_hostname', 3301) > +check_err(err) > +s, err =3D socket.tcp_connect('non_exists_hostname', 3301) > +check_err(err) > + > -- case: sicket receive inconsistent behavior > chan =3D fiber.channel() > counter =3D 0 > diff --git a/test/box/net.box.result b/test/box/net.box.result > index e3dabf7d9..11914dd68 100644 > --- a/test/box/net.box.result > +++ b/test/box/net.box.result > @@ -3927,6 +3927,36 @@ test_run:grep_log('default', '00000040:.*') > --- > - null > ... > +-- gh-4138 Check getaddrinfo() error from connect() only. Error > +-- code and error message returned by getaddrinfo() depends on > +-- system's gai_strerror(). > +test_run:cmd("setopt delimiter ';'") > +--- > +- true > +... > +function check_err(err) > + if err =3D=3D 'getaddrinfo: nodename nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: Servname not supported for = ai_socktype' or > + err =3D=3D 'getaddrinfo: Name or service not known' or > + err =3D=3D 'getaddrinfo: hostname nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: temporary failure in name resolution' = then > + return true > + end > + return false > +end; > +--- > +... > +test_run:cmd("setopt delimiter ''"); > +--- > +- true > +... > +s =3D remote.connect('non_exists_hostname:3301') > +--- > +... > +check_err(s['error']) > +--- > +- true > +... > box.cfg{log_level=3Dlog_level} > --- > ... > diff --git a/test/box/net.box.test.lua b/test/box/net.box.test.lua > index 8e65ff470..79707eaa4 100644 > --- a/test/box/net.box.test.lua > +++ b/test/box/net.box.test.lua > @@ -1582,4 +1582,23 @@ test_run:wait_log('default', '00000030:.*', = nil, 10) > -- we expect nothing below, so don't wait > test_run:grep_log('default', '00000040:.*') >=20 > +-- gh-4138 Check getaddrinfo() error from connect() only. Error > +-- code and error message returned by getaddrinfo() depends on > +-- system's gai_strerror(). > +test_run:cmd("setopt delimiter ';'") > +function check_err(err) > + if err =3D=3D 'getaddrinfo: nodename nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: Servname not supported for = ai_socktype' or > + err =3D=3D 'getaddrinfo: Name or service not known' or > + err =3D=3D 'getaddrinfo: hostname nor servname provided, or = not known' or > + err =3D=3D 'getaddrinfo: temporary failure in name resolution' = then > + return true > + end > + return false > +end; > +test_run:cmd("setopt delimiter ''"); > + > +s =3D remote.connect('non_exists_hostname:3301') > +check_err(s['error']) > + > box.cfg{log_level=3Dlog_level} --Apple-Mail=_803335FA-DC9C-4F4E-BCC8-D062C2D2C139 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=us-ascii

Begin forwarded message:

From: = Roman Khabibov <roman.habibov@tarantool.org>
Subject: = Re: = [tarantool-patches] [server-dev] Re: Re: [PATCH v2 1/2] lua: return = getaddrinfo() errors
Date: = December 26, 2019 at 20:29:16 = GMT+3
To: = Sergey Ostanevich <sergos@tarantool.org>

Hi! = Alexander, thanks for the review. Sergos, can you, please, do a second = review?

https://github.com/tarantool/tarantool/tree/romanhabibov/gh-413= 8-getaddrinfo-full-ci

On Dec 23, 2019, at 16:36, Alexander = Turenko <alexander.turenko@tarantool.org> wrote:

Comments below are trivial. Let's fix and = proceed with the next reviewer
(Sergey O.). Please, resend = the patchset for him.

I still think you = should run CI on all targets (push to ...-full-ci
branch).

LGTM except minor = comments below.

WBR, Alexander Turenko.

@@ = -1360,8 +1363,12 @@ local function lsocket_tcp_connect(self, host, = port)
   -- This function is broken by = design
   local ga_opts =3D { family =3D = 'AF_INET', type =3D 'SOCK_STREAM' }
   local = timeout =3D deadline - fiber.clock()
- =    local dns =3D getaddrinfo(host, port, timeout, = ga_opts)
-    if dns =3D=3D nil or #dns =3D=3D= 0 then
+    local dns, err =3D = getaddrinfo(host, port, timeout, ga_opts)
+ =    if dns =3D=3D nil then
+ =        self._errno =3D boxerrno()

Master's socket _errno was set to = EINVAL before this patch. Let's keep
this behaviour, but = change the 2nd return value.

I mean:

| if dns =3D=3D nil then
| =     self._errno =3D boxerrno.EINVAL
| =     return nil, err
| end
| if #dns =3D=3D 0 then
| =     self._errno =3D boxerrno.EINVAL
| =     return nil, socket_error(self)
| = end
@@ -1360,8 +1363,12 @@ local function = lsocket_tcp_connect(self, host, port)
    -- This function is broken by = design
    local ga_opts =3D { family =3D = 'AF_INET', type =3D 'SOCK_STREAM' }
    local timeout =3D deadline - = fiber.clock()
- =    local dns =3D getaddrinfo(host, port, timeout, = ga_opts)
- =    if dns =3D=3D nil or #dns =3D=3D 0 then
+ =    local dns, err =3D getaddrinfo(host, port, timeout, = ga_opts)
+ =    if dns =3D=3D nil then
+        self._errno =3D = boxerrno.EINVAL
+        return nil, = err
+ =    end
+    if #dns =3D=3D 0 then
        self._errno =3D= boxerrno.EINVAL
        return nil, = socket_error(self)
    end

+ =        return nil, err
+ =    end
+    if #dns =3D=3D 0 = then
       self._errno = =3D boxerrno.EINVAL
       return nil, = socket_error(self)
   end

diff --git a/test/app/socket.test.lua = b/test/app/socket.test.lua
index c72d41763..7086e496b = 100644
--- a/test/app/socket.test.lua
+++ = b/test/app/socket.test.lua
@@ -960,6 +968,27 @@ = server:close()

test_run:cmd("clear = filter")

+-- gh-4138 Check getaddrinfo() = error from socket:connect() only.
+-- Error code and error = message returned by getaddrinfo() depends
+-- on system's = gai_strerror().
+test_run:cmd("setopt delimiter ';'")
+function check_err(err)
+    if = err =3D=3D 'getaddrinfo: nodename nor servname provided, or not known' = or
+       err =3D=3D = 'getaddrinfo: Servname not supported for ai_socktype' or
+ =       err =3D=3D 'getaddrinfo: Name or = service not known' then
+ =        return true
+ =    end
+    return false
+end;

You added the = following error message to coio_getaddrinfo() unit test in
the first commit:

| const char = *exp_errmsg_4 =3D "getaddrinfo: hostname nor servname provided"
|         ", or not = known";

So it worth to add it here too. = We'll enable the test of FreeBSD sooner
or later.

Also I got EAI_AGAIN on my system now (maybe = something was changed on
DNS server side). Let's add it = too (for both commits).
+function = check_err(err)
+    if err =3D=3D 'getaddrinfo: nodename nor = servname provided, or not known' or
+       err =3D=3D = 'getaddrinfo: Servname not supported for ai_socktype' or
+ =       err =3D=3D 'getaddrinfo: Name or = service not known' or
+       err =3D=3D = 'getaddrinfo: hostname nor servname provided, or not known' or
+ =       err =3D=3D 'getaddrinfo: temporary = failure in name resolution' then
+        return = true
+ =    end
+    return false
+end;
diff --git = a/test/box/net.box.test.lua b/test/box/net.box.test.lua
index 8e65ff470..9c9aee1ad 100644
--- = a/test/box/net.box.test.lua
+++ = b/test/box/net.box.test.lua
@@ -1582,4 +1582,21 @@ = test_run:wait_log('default', '00000030:.*', nil, 10)
-- we = expect nothing below, so don't wait
test_run:grep_log('default', '00000040:.*')

+-- gh-4138 Check getaddrinfo() error from connect() only. = Error
+-- code and error message returned by getaddrinfo() = depends on
+-- system's gai_strerror().
+test_run:cmd("setopt delimiter ';'")
+function = check_err(err)
+    if err =3D=3D = 'getaddrinfo: nodename nor servname provided, or not known' or
+       err =3D=3D = 'getaddrinfo: Servname not supported for ai_socktype' or
+ =       err =3D=3D 'getaddrinfo: Name or = service not known' then
+ =            return = true
+    end
+ =    return false
+end;
+test_run:cmd("setopt delimiter ''");

Same here.
Done.


commit 22937d2fce2d127a762a32e392d2c4d525c22c0b
Author: Roman = Khabibov <roman.habibov@tarantool.org>
Date:   Thu Nov 21 14:37:54 2019 +0300

   lua: return getaddrinfo() errors

   Add getaddrinfo() errors into the several = fuctions of socket. Now
   getaddrinfo() can return a pair of values = (nil and error message)
   in case of error.

   Closes #4138

   @TarantoolBot document
   Title: socket API changes

   * socket.getaddrinfo()

   Can return error message as second return = value in case of
   internal getaddrinfo() error.

   Example:
   tarantool> = socket.getaddrinfo('non_exists_hostname', 3301)
   ---
   - null
   - 'getaddrinfo: nodename nor servname = provided, or not known'
   ...

   * socket.tcp_connect()

   Can return socket.getaddrinfo() error = message as second return
   value.

   Example:
   tarantool> = socket.tcp_connect('non_exists_hostname', 3301)
   ---
   - null
   - 'getaddrinfo: nodename nor servname = provided, or not known'
   ...

diff --git a/src/box/lua/net_box.lua = b/src/box/lua/net_box.lua
index c2e1bb9c4..2fb3e6df6 100644
--- = a/src/box/lua/net_box.lua
+++ b/src/box/lua/net_box.lua
@@ -150,9 +150,12 @@ local function next_id(id) return = band(id + 1, 0x7FFFFFFF) end
local function establish_connection(host, port, = timeout)
    local timeout =3D timeout or = DEFAULT_CONNECT_TIMEOUT
    local begin =3D = fiber.clock()
- =    local s =3D socket.tcp_connect(host, port, = timeout)
+ =    local s, err =3D socket.tcp_connect(host, port, = timeout)
    if not s then
- =        return nil, = errno.strerror(errno())
+        if not err = then
+ =            return = nil, errno.strerror(errno())
+        end
+ =        return nil, err
    end
    local msg =3D s:read({chunk =3D = IPROTO_GREETING_SIZE},
          &nb= sp;            = ; timeout - (fiber.clock() - begin))
diff --git a/src/lua/socket.c b/src/lua/socket.c
index = 130378caf..dbade2d27 100644
--- a/src/lua/socket.c
+++ b/src/lua/socket.c
@@ -774,6 +774,18 @@ lbox_getaddrinfo_result_wrapper(struct = lua_State *L)
return 1;
}

+/**
+ * Wrap = coio_getaddrinfo() and call it. Push returned values onto
+ * @a L Lua = stack.
+ *
+ * @param L = Lua stack.
+ *
+ * @retval 1 = Number of returned values by Lua function if
+ * = coio_getaddrinfo() success.
+ * @retval 2 Number of returned values by Lua function = if
+ * = coio_getaddrinfo() is failed (first is nil, second is error
+ * = message).
+ = */
static = int
lbox_socket_getaddrinfo(struct lua_State *L)
{
@@ -816,7 = +828,9 @@ lbox_socket_getaddrinfo(struct lua_State *L)

if (dns_res !=3D 0) {
= lua_pushnil(L);
- = return = 1;
+ struct error *err =3D diag_get()->last;
+ lua_pushstring(L, err->errmsg);
+ return 2;
= }

/* no results */
diff --git a/src/lua/socket.lua = b/src/lua/socket.lua
index a334ad45b..ce6dab301 100644
--- = a/src/lua/socket.lua
+++ b/src/lua/socket.lua
@@ -1041,9 +1041,12 @@ local function tcp_connect(host, port, = timeout)
    end
    local timeout =3D timeout or = TIMEOUT_INFINITY
    local stop =3D fiber.clock() + = timeout
- =    local dns =3D getaddrinfo(host, port, timeout, { type = =3D 'SOCK_STREAM',
+    local dns, err =3D getaddrinfo(host, = port, timeout, { type =3D 'SOCK_STREAM',
        protocol =3D = 'tcp' })
- =    if dns =3D=3D nil or #dns =3D=3D 0 then
+ =    if dns =3D=3D nil then
+        return nil, = err
+ =    end
+    if #dns =3D=3D 0 then
        boxerrno(boxerr= no.EINVAL)
        return = nil
    end
@@ -1360,8 +1363,12 @@ local function = lsocket_tcp_connect(self, host, port)
    -- This function is broken by = design
    local ga_opts =3D { family =3D = 'AF_INET', type =3D 'SOCK_STREAM' }
    local timeout =3D deadline - = fiber.clock()
- =    local dns =3D getaddrinfo(host, port, timeout, = ga_opts)
- =    if dns =3D=3D nil or #dns =3D=3D 0 then
+ =    local dns, err =3D getaddrinfo(host, port, timeout, = ga_opts)
+ =    if dns =3D=3D nil then
+        self._errno =3D = boxerrno.EINVAL
+        return nil, = err
+ =    end
+    if #dns =3D=3D 0 then
        self._errno =3D= boxerrno.EINVAL
        return nil, = socket_error(self)
    end
@@ -1547,9 +1554,12 @@ local function lsocket_connect(host, = port)
    if host =3D=3D nil or port =3D=3D nil = then
        error("Usage: = luasocket.connect(host, port)")
    end
-    local s =3D tcp_connect(host, = port)
+ =    local s, err =3D tcp_connect(host, port)
    if not s then
- =        return nil, = boxerrno.strerror()
+        if not err = then
+ =            return = nil, boxerrno.strerror()
+        end
+ =        return nil, err
    end
    setmetatable(s, = lsocket_tcp_client_mt)
    return s
diff --git = a/test/app/socket.result b/test/app/socket.result
index = fd299424c..aca189f4c 100644
--- a/test/app/socket.result
+++ b/test/app/socket.result
@@ -941,10 +941,20 @@ sc:close()
- = true
...
-- = tcp_connect
--- test = timeout
-socket.tcp_connect('127.0.0.1', 80, 0.00000000001)
+-- Test = timeout. In this test, tcp_connect can return the second
+-- output = value from internal.getaddrinfo (usually on Mac OS, but
+-- = theoretically it can happen on Linux too). Sometimes
+-- = getaddrinfo() is timed out, sometimes connect. On Linux = however
+-- = getaddrinfo is fast enough to never give timeout error in
+-- the case. = So, there are two sources of timeout errors that are
+-- reported = differently. This difference has appeared after
+-- gh-4138 = patch.
+s, err =3D = socket.tcp_connect('127.0.0.1', 80, 0.00000000001)
---
-- = null
+...
+s =3D=3D = nil
+---
+- = true
...
-- = AF_INET
s =3D = socket('AF_INET', 'SOCK_STREAM', 'tcp')
@@ -1606,7 +1616,7 @@ fio.stat(path) =3D=3D nil
{ = socket.tcp_connect('abrakadabra#123') =3D=3D nil, errno.strerror() = }
---
- - = true
-  - = Invalid argument
+  - Input/output error
...
-- wrong options for getaddrinfo
socket.getaddrinfo('host', 'port', { type =3D 'WRONG' }) =3D=3D= nil and errno() =3D=3D errno.EINVAL
@@ -2816,6 +2826,50 @@ test_run:cmd("clear filter")
---
- = true
...
+-- gh-4138 = Check getaddrinfo() error from socket:connect() only.
+-- Error = code and error message returned by getaddrinfo() depends
+-- on = system's gai_strerror().
+test_run:cmd("setopt delimiter ';'")
+---
+- = true
+...
+function = check_err(err)
+    if err =3D=3D 'getaddrinfo: nodename nor = servname provided, or not known' or
+       err =3D=3D = 'getaddrinfo: Servname not supported for ai_socktype' or
+ =       err =3D=3D 'getaddrinfo: Name or = service not known' or
+       err =3D=3D = 'getaddrinfo: hostname nor servname provided, or not known' or
+ =       err =3D=3D 'getaddrinfo: temporary = failure in name resolution' then
+        return = true
+ =    end
+    return false
+end;+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- = true
+...
+s, err =3D = socket.getaddrinfo('non_exists_hostname', 3301)
+---
+...
+check_err(err)
+---
+- true
+...
+s, err =3D socket.connect('non_exists_hostname', = 3301)
+---
+...
+check_err(err)
+---
+- true
+...
+s, err =3D socket.tcp_connect('non_exists_hostname', = 3301)
+---
+...
+check_err(err)
+---
+- true
+...
-- case: sicket receive inconsistent behavior
chan =3D = fiber.channel()
---
diff --git a/test/app/socket.test.lua = b/test/app/socket.test.lua
index c72d41763..223935e66 100644
--- = a/test/app/socket.test.lua
+++ b/test/app/socket.test.lua
@@ -300,8 +300,16 @@ sc:close()

-- tcp_connect

--- test timeout
-socket.tcp_connect('127.0.0.1', 80, 0.00000000001)
+-- Test = timeout. In this test, tcp_connect can return the second
+-- output = value from internal.getaddrinfo (usually on Mac OS, but
+-- = theoretically it can happen on Linux too). Sometimes
+-- = getaddrinfo() is timed out, sometimes connect. On Linux = however
+-- = getaddrinfo is fast enough to never give timeout error in
+-- the case. = So, there are two sources of timeout errors that are
+-- reported = differently. This difference has appeared after
+-- gh-4138 = patch.
+s, err =3D = socket.tcp_connect('127.0.0.1', 80, 0.00000000001)
+s =3D=3D = nil

-- = AF_INET
s =3D = socket('AF_INET', 'SOCK_STREAM', 'tcp')
@@ -960,6 +968,29 @@ server:close()

test_run:cmd("clear filter")

+-- gh-4138 Check getaddrinfo() error from socket:connect() = only.
+-- Error = code and error message returned by getaddrinfo() depends
+-- on = system's gai_strerror().
+test_run:cmd("setopt delimiter ';'")
+function = check_err(err)
+    if err =3D=3D 'getaddrinfo: nodename nor = servname provided, or not known' or
+       err =3D=3D = 'getaddrinfo: Servname not supported for ai_socktype' or
+ =       err =3D=3D 'getaddrinfo: Name or = service not known' or
+       err =3D=3D = 'getaddrinfo: hostname nor servname provided, or not known' or
+ =       err =3D=3D 'getaddrinfo: temporary = failure in name resolution' then
+        return = true
+ =    end
+    return false
+end;+test_run:cmd("setopt delimiter ''");
+
+s, err =3D = socket.getaddrinfo('non_exists_hostname', 3301)
+check_err(err)
+s, err =3D socket.connect('non_exists_hostname', = 3301)
+check_err(err)
+s, err =3D socket.tcp_connect('non_exists_hostname', = 3301)
+check_err(err)
+
-- case: = sicket receive inconsistent behavior
chan =3D fiber.channel()
counter =3D 0
diff --git a/test/box/net.box.result = b/test/box/net.box.result
index e3dabf7d9..11914dd68 100644
--- = a/test/box/net.box.result
+++ b/test/box/net.box.result
@@ -3927,6 +3927,36 @@ test_run:grep_log('default', = '00000040:.*')
---
- null
...
+-- gh-4138 Check getaddrinfo() error from connect() only. = Error
+-- code and = error message returned by getaddrinfo() depends on
+-- system's = gai_strerror().
+test_run:cmd("setopt delimiter ';'")
+---
+- = true
+...
+function = check_err(err)
+    if err =3D=3D 'getaddrinfo: nodename nor = servname provided, or not known' or
+       err =3D=3D = 'getaddrinfo: Servname not supported for ai_socktype' or
+ =       err =3D=3D 'getaddrinfo: Name or = service not known' or
+       err =3D=3D = 'getaddrinfo: hostname nor servname provided, or not known' or
+ =       err =3D=3D 'getaddrinfo: temporary = failure in name resolution' then
+ =            return = true
+ =    end
+    return false
+end;+---
+...
+test_run:cmd("setopt delimiter ''");
+---
+- = true
+...
+s =3D = remote.connect('non_exists_hostname:3301')
+---
+...
+check_err(s['error'])
+---
+- true
+...
box.cfg{log_level=3Dlog_level}
---
...
diff --git a/test/box/net.box.test.lua = b/test/box/net.box.test.lua
index 8e65ff470..79707eaa4 100644
--- = a/test/box/net.box.test.lua
+++ b/test/box/net.box.test.lua
@@ -1582,4 +1582,23 @@ test_run:wait_log('default', = '00000030:.*', nil, 10)
-- we expect nothing below, so don't wait
test_run:grep_log('default', '00000040:.*')

+-- gh-4138 = Check getaddrinfo() error from connect() only. Error
+-- code and = error message returned by getaddrinfo() depends on
+-- system's = gai_strerror().
+test_run:cmd("setopt delimiter ';'")
+function = check_err(err)
+    if err =3D=3D 'getaddrinfo: nodename nor = servname provided, or not known' or
+       err =3D=3D = 'getaddrinfo: Servname not supported for ai_socktype' or
+ =       err =3D=3D 'getaddrinfo: Name or = service not known' or
+       err =3D=3D = 'getaddrinfo: hostname nor servname provided, or not known' or
+ =       err =3D=3D 'getaddrinfo: temporary = failure in name resolution' then
+ =            return = true
+ =    end
+    return false
+end;+test_run:cmd("setopt delimiter ''");
+
+s =3D = remote.connect('non_exists_hostname:3301')
+check_err(s['error'])
+
box.cfg{log_level=3Dlog_level}
<= br class=3D"">= --Apple-Mail=_803335FA-DC9C-4F4E-BCC8-D062C2D2C139--