Tarantool development patches archive
 help / color / mirror / Atom feed
From: Alexander Turenko <alexander.turenko@tarantool.org>
To: Vladimir Davydov <vdavydov.dev@gmail.com>
Cc: Alexander Turenko <alexander.turenko@tarantool.org>,
	tarantool-patches@freelists.org,
	Yaroslav Dynnikov <yaroslav.dynnikov@tarantool.org>
Subject: [PATCH 1/3] socket: evaluate buffer size in recv / recvfrom
Date: Fri, 24 Aug 2018 05:47:37 +0300	[thread overview]
Message-ID: <36b23f07009f3cd467c1eb7e54e982b1f907908d.1535076888.git.alexander.turenko@tarantool.org> (raw)
In-Reply-To: <cover.1535076888.git.alexander.turenko@tarantool.org>
In-Reply-To: <cover.1535076888.git.alexander.turenko@tarantool.org>

When size parameter is not passed to socket:recv() or socket:recvfrom()
it will call a) or b) on the socket to evaluate size of the buffer to
store the receiving datagram. Before this commit a datagram will be
truncated to 512 bytes in the case.

a) Linux: recv(fd, NULL, 0 , MSG_TRUNC | MSG_PEEK)
b) Mac OS: getsockopt(fd, SOL_SOCKET, SO_NREAD, &val, &len)

It is recommended to set 'size' parameter (size of the input buffer)
explicitly based on known message format and known network conditions
(say, set it less then MTU to prevent IP fragmentation, which can be
inefficient) or pass it from a configuration option of a library / an
application. The reason is that explicit buffer size provided allows to
avoid extra recv syscall on Linux.

Added socket:is_udp() and socket:dgram_length() methods to the public
API.

Part of #3619.
---
 src/lua/socket.c         |   3 +
 src/lua/socket.lua       |  57 +++++-
 test/app/socket.result   | 401 +++++++++++++++++++++++++++++++++++++++
 test/app/socket.test.lua | 154 +++++++++++++++
 4 files changed, 613 insertions(+), 2 deletions(-)

diff --git a/src/lua/socket.c b/src/lua/socket.c
index 14d49d106..bf23eae1f 100644
--- a/src/lua/socket.c
+++ b/src/lua/socket.c
@@ -268,6 +268,9 @@ static const struct lbox_sockopt_reg so_opts[] = {
 #ifdef SO_PROTOCOL
 	{"SO_PROTOCOL",		SO_PROTOCOL,		1,	0, },
 #endif
+#ifdef SO_NREAD /* available on Darwin */
+	{"SO_NREAD",		SO_NREAD,		1,	0, },
+#endif
 
 	{"SO_TYPE",		SO_TYPE,		1,	0, },
 
diff --git a/src/lua/socket.lua b/src/lua/socket.lua
index 06306eae2..946e11e9e 100644
--- a/src/lua/socket.lua
+++ b/src/lua/socket.lua
@@ -770,6 +770,41 @@ local function socket_send(self, octets, flags)
     return tonumber(res)
 end
 
+local function socket_is_udp(self)
+    local fd = check_socket(self)
+    if self.itype == nil then
+        self.itype = self:getsockopt('SOL_SOCKET', 'SO_TYPE')
+    end
+    return self.itype == internal.SO_TYPE['SOCK_DGRAM']
+end
+
+-- returns nil and sets EAGAIN when there are no datagrams in the receive queue
+-- as well as when zero-length datagram is the next one
+local function socket_dgram_length(self)
+    local fd = check_socket(self)
+    if not self:is_udp() then
+        error("socket:dgram_length() supports only a UDP socket")
+    end
+
+    local len
+    if jit.os == 'OSX' then
+        self._errno = nil
+        len = self:getsockopt('SOL_SOCKET', 'SO_NREAD')
+    else
+        local iflags = get_iflags(internal.SEND_FLAGS, {'MSG_TRUNC', 'MSG_PEEK'})
+        assert(iflags ~= nil)
+        self._errno = nil
+        len = tonumber(ffi.C.recv(fd, nil, 0, iflags))
+    end
+
+    if len == -1 or len == 0 then
+        self._errno = boxerrno()
+        return nil
+    end
+
+    return len
+end
+
 local function socket_recv(self, size, flags)
     local fd = check_socket(self)
     local iflags = get_iflags(internal.SEND_FLAGS, flags)
@@ -778,7 +813,15 @@ local function socket_recv(self, size, flags)
         return nil
     end
 
-    size = size or 512
+    if self:is_udp() then
+        size = size or self:dgram_length()
+        if size == nil then
+            return nil
+        end
+    else
+        size = size or 512
+    end
+
     self._errno = nil
     local buf = ffi.new("char[?]", size)
 
@@ -799,7 +842,15 @@ local function socket_recvfrom(self, size, flags)
         return nil
     end
 
-    size = size or 512
+    if self:is_udp() then
+        size = size or self:dgram_length()
+        if size == nil then
+            return nil
+        end
+    else
+        size = size or 512
+    end
+
     self._errno = nil
     local res, from = internal.recvfrom(fd, size, iflags)
     if res == nil then
@@ -1165,6 +1216,8 @@ socket_mt   = {
         read = socket_read;
         write = socket_write;
         send = socket_send;
+        is_udp = socket_is_udp;
+        dgram_length = socket_dgram_length;
         recv = socket_recv;
         recvfrom = socket_recvfrom;
         sendto = socket_sendto;
diff --git a/test/app/socket.result b/test/app/socket.result
index b4d4a9a78..07cd24c15 100644
--- a/test/app/socket.result
+++ b/test/app/socket.result
@@ -1721,6 +1721,7 @@ test_run:cmd("setopt delimiter ''");
 --------------------------------------------------------------------------------
 -- Lua Socket Emulation
 --------------------------------------------------------------------------------
+-- {{{
 test_run:cmd("push filter 'fd=([0-9]+)' to 'fd=<FD>'")
 ---
 - true
@@ -2230,6 +2231,406 @@ s:close()
 ---
 - 1
 ...
+-- }}}
+-- s:is_udp() {{{
+s = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+---
+...
+s:is_udp() -- expected true
+---
+- true
+...
+s:close()
+---
+- true
+...
+s = socket('AF_INET', 'SOCK_STREAM', 0)
+---
+...
+s:is_udp() -- expected false
+---
+- false
+...
+s:close()
+---
+- true
+...
+-- }}}
+-- s:dgram_length() {{{
+-- case: tcp socket is not supported
+s = socket('AF_INET', 'SOCK_STREAM', 0)
+---
+...
+s:dgram_length() -- expected an error
+---
+- error: 'builtin/socket.lua: socket:dgram_length() supports only a UDP socket'
+...
+s:close()
+---
+- true
+...
+receiving_socket = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+---
+...
+receiving_socket:bind('127.0.0.1', 0)
+---
+- true
+...
+receiving_socket_port = receiving_socket:name().port
+---
+...
+sending_socket = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+---
+...
+-- case: no datagram
+received_len = receiving_socket:dgram_length()
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_len == nil -- expected true
+---
+- true
+...
+received_len
+---
+- null
+...
+e == errno.EAGAIN -- expected true
+---
+- true
+...
+message_len = 0
+---
+...
+message = ''
+---
+...
+-- case: zero-length datagram (the same as no datagram)
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+---
+- true
+...
+received_len = receiving_socket:dgram_length()
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_len == nil -- expected true
+---
+- true
+...
+received_len
+---
+- null
+...
+e == errno.EAGAIN -- expected true
+---
+- true
+...
+message_len = 1025
+---
+...
+message = string.rep('x', message_len)
+---
+...
+-- case: non-zero length datagram
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+---
+- 1025
+...
+received_len = receiving_socket:dgram_length()
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_len == message_len -- expected true
+---
+- true
+...
+received_len
+---
+- 1025
+...
+e == 0 -- expected true
+---
+- true
+...
+e
+---
+- 0
+...
+receiving_socket:close()
+---
+- true
+...
+sending_socket:close()
+---
+- true
+...
+-- }}}
+-- gh-3619: socket.recvfrom crops UDP packets {{{
+receiving_socket = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+---
+...
+receiving_socket:bind('127.0.0.1', 0)
+---
+- true
+...
+receiving_socket_port = receiving_socket:name().port
+---
+...
+sending_socket = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+---
+...
+-- case: recv, no datagram
+received_message = receiving_socket:recv()
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_message == nil -- expected true
+---
+- true
+...
+received_message
+---
+- null
+...
+e == errno.EAGAIN -- expected true
+---
+- true
+...
+-- case: recvfrom, no datagram
+received_message, from = receiving_socket:recvfrom()
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_message == nil -- expected true
+---
+- true
+...
+received_message
+---
+- null
+...
+from == nil -- expected true
+---
+- true
+...
+from
+---
+- null
+...
+e == errno.EAGAIN -- expected true
+---
+- true
+...
+message_len = 0
+---
+...
+message = ''
+---
+...
+-- case: recv, zero-length datagram (the same as no datagram)
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+---
+- true
+...
+received_message = receiving_socket:recv()
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_message == nil -- expected true
+---
+- true
+...
+received_message
+---
+- null
+...
+e == errno.EAGAIN -- expected true
+---
+- true
+...
+-- case: recvfrom, zero-length datagram (the same as no datagram)
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+---
+- true
+...
+received_message, from = receiving_socket:recvfrom()
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_message == nil -- expected true
+---
+- true
+...
+received_message
+---
+- null
+...
+from == nil -- expected true
+---
+- true
+...
+from
+---
+- null
+...
+e == errno.EAGAIN -- expected true
+---
+- true
+...
+-- case: recv, zero-length datagram (the same as no datagram), explicit size
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+---
+- true
+...
+received_message = receiving_socket:recv(512)
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_message == nil -- expected true
+---
+- true
+...
+received_message
+---
+- null
+...
+e == errno.EAGAIN -- expected true
+---
+- true
+...
+-- case: recvfrom, zero-length datagram (the same as no datagram), explicit size
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+---
+- true
+...
+received_message, from = receiving_socket:recvfrom(512)
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_message == nil -- expected true
+---
+- true
+...
+received_message
+---
+- null
+...
+from == nil -- expected true
+---
+- true
+...
+from
+---
+- null
+...
+e == errno.EAGAIN -- expected true
+---
+- true
+...
+message_len = 1025
+---
+...
+message = string.rep('x', message_len)
+---
+...
+-- case: recv, non-zero length datagram
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+---
+- 1025
+...
+received_message = receiving_socket:recv()
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_message == message -- expected true
+---
+- true
+...
+received_message:len()
+---
+- 1025
+...
+received_message
+---
+- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+...
+e == 0 -- expected true
+---
+- true
+...
+e
+---
+- 0
+...
+-- case: recvfrom, non-zero length datagram
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+---
+- 1025
+...
+received_message, from = receiving_socket:recvfrom()
+---
+...
+e = receiving_socket:errno()
+---
+...
+received_message == message -- expected true
+---
+- true
+...
+received_message:len()
+---
+- 1025
+...
+received_message
+---
+- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+...
+from ~= nil -- expected true
+---
+- true
+...
+from.host == '127.0.0.1' -- expected true
+---
+- true
+...
+e == 0 -- expected true
+---
+- true
+...
+e
+---
+- 0
+...
+receiving_socket:close()
+---
+- true
+...
+sending_socket:close()
+---
+- true
+...
+-- }}}
 test_run:cmd("clear filter")
 ---
 - true
diff --git a/test/app/socket.test.lua b/test/app/socket.test.lua
index f6539438b..c2dc7ea90 100644
--- a/test/app/socket.test.lua
+++ b/test/app/socket.test.lua
@@ -591,6 +591,7 @@ test_run:cmd("setopt delimiter ''");
 --------------------------------------------------------------------------------
 -- Lua Socket Emulation
 --------------------------------------------------------------------------------
+-- {{{
 
 test_run:cmd("push filter 'fd=([0-9]+)' to 'fd=<FD>'")
 
@@ -736,4 +737,157 @@ sc:connect(host, port)
 sc:close()
 s:close()
 
+-- }}}
+
+-- s:is_udp() {{{
+
+s = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+s:is_udp() -- expected true
+s:close()
+
+s = socket('AF_INET', 'SOCK_STREAM', 0)
+s:is_udp() -- expected false
+s:close()
+
+-- }}}
+
+-- s:dgram_length() {{{
+
+-- case: tcp socket is not supported
+s = socket('AF_INET', 'SOCK_STREAM', 0)
+s:dgram_length() -- expected an error
+s:close()
+
+receiving_socket = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+receiving_socket:bind('127.0.0.1', 0)
+receiving_socket_port = receiving_socket:name().port
+sending_socket = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+
+-- case: no datagram
+received_len = receiving_socket:dgram_length()
+e = receiving_socket:errno()
+received_len == nil -- expected true
+received_len
+e == errno.EAGAIN -- expected true
+
+message_len = 0
+message = ''
+
+-- case: zero-length datagram (the same as no datagram)
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+received_len = receiving_socket:dgram_length()
+e = receiving_socket:errno()
+received_len == nil -- expected true
+received_len
+e == errno.EAGAIN -- expected true
+
+message_len = 1025
+message = string.rep('x', message_len)
+
+-- case: non-zero length datagram
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+received_len = receiving_socket:dgram_length()
+e = receiving_socket:errno()
+received_len == message_len -- expected true
+received_len
+e == 0 -- expected true
+e
+
+receiving_socket:close()
+sending_socket:close()
+
+-- }}}
+
+-- gh-3619: socket.recvfrom crops UDP packets {{{
+
+receiving_socket = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+receiving_socket:bind('127.0.0.1', 0)
+receiving_socket_port = receiving_socket:name().port
+sending_socket = socket('AF_INET', 'SOCK_DGRAM', 'udp')
+
+-- case: recv, no datagram
+received_message = receiving_socket:recv()
+e = receiving_socket:errno()
+received_message == nil -- expected true
+received_message
+e == errno.EAGAIN -- expected true
+
+-- case: recvfrom, no datagram
+received_message, from = receiving_socket:recvfrom()
+e = receiving_socket:errno()
+received_message == nil -- expected true
+received_message
+from == nil -- expected true
+from
+e == errno.EAGAIN -- expected true
+
+message_len = 0
+message = ''
+
+-- case: recv, zero-length datagram (the same as no datagram)
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+received_message = receiving_socket:recv()
+e = receiving_socket:errno()
+received_message == nil -- expected true
+received_message
+e == errno.EAGAIN -- expected true
+
+-- case: recvfrom, zero-length datagram (the same as no datagram)
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+received_message, from = receiving_socket:recvfrom()
+e = receiving_socket:errno()
+received_message == nil -- expected true
+received_message
+from == nil -- expected true
+from
+e == errno.EAGAIN -- expected true
+
+-- case: recv, zero-length datagram (the same as no datagram), explicit size
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+received_message = receiving_socket:recv(512)
+e = receiving_socket:errno()
+received_message == nil -- expected true
+received_message
+e == errno.EAGAIN -- expected true
+
+-- case: recvfrom, zero-length datagram (the same as no datagram), explicit size
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+received_message, from = receiving_socket:recvfrom(512)
+e = receiving_socket:errno()
+received_message == nil -- expected true
+received_message
+from == nil -- expected true
+from
+e == errno.EAGAIN -- expected true
+
+message_len = 1025
+message = string.rep('x', message_len)
+
+-- case: recv, non-zero length datagram
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+received_message = receiving_socket:recv()
+e = receiving_socket:errno()
+received_message == message -- expected true
+received_message:len()
+received_message
+e == 0 -- expected true
+e
+
+-- case: recvfrom, non-zero length datagram
+sending_socket:sendto('127.0.0.1', receiving_socket_port, message)
+received_message, from = receiving_socket:recvfrom()
+e = receiving_socket:errno()
+received_message == message -- expected true
+received_message:len()
+received_message
+from ~= nil -- expected true
+from.host == '127.0.0.1' -- expected true
+e == 0 -- expected true
+e
+
+receiving_socket:close()
+sending_socket:close()
+
+-- }}}
+
 test_run:cmd("clear filter")
-- 
2.17.1

  reply	other threads:[~2018-08-24  2:47 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-24  2:47 [PATCH 0/3] *** [#3619] socket.recvfrom crops UDP packets *** Alexander Turenko
2018-08-24  2:47 ` Alexander Turenko [this message]
2018-08-24 14:07   ` [PATCH 1/3] socket: evaluate buffer size in recv / recvfrom Vladimir Davydov
2018-08-24 15:25   ` Vladimir Davydov
2018-08-27  0:08     ` Alexander Turenko
2018-08-27  9:20       ` Vladimir Davydov
2018-08-27  9:26         ` Alexander Turenko
2018-08-24  2:47 ` [PATCH 2/3] socket: don't truncate a datagram in recv/recvfrom Alexander Turenko
2018-08-24 15:24   ` Vladimir Davydov
2018-08-24  2:47 ` [PATCH 3/3] socket: prevent recvfrom from returning garbage Alexander Turenko
2018-08-24 13:07   ` Vladimir Davydov
2018-08-24 13:44     ` Alexander Turenko
2018-08-24 13:55       ` Vladimir Davydov
2018-08-24 17:11   ` Vladimir Davydov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=36b23f07009f3cd467c1eb7e54e982b1f907908d.1535076888.git.alexander.turenko@tarantool.org \
    --to=alexander.turenko@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=vdavydov.dev@gmail.com \
    --cc=yaroslav.dynnikov@tarantool.org \
    --subject='Re: [PATCH 1/3] socket: evaluate buffer size in recv / recvfrom' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox