Bug: Cosock SSL wrap invalidates socket functions

I have been running into issues trying to use getpeername, setoption and getoption for a tcp socket utilizing ssl. None of these functions are available after wrapping the socket for SSL.

From cosock/ssl.lua in the platform

m.wrap = function(tcp_socket, config)
  assert(tcp_socket.inner_sock, "tcp inner_sock is null")
  local inner_sock, err = luasec.wrap(tcp_socket.inner_sock, config)
  if not inner_sock then
    return inner_sock, err
  end 
  inner_sock:settimeout(0)
  return setmetatable({inner_sock = inner_sock, class = "tls{}"}, {__index = m})
end

it is used like this and the functions are gone after wrapping:

print('pre setoption', type(sock.setoption)) -- function
print('pre getoption', type(sock.getoption)) -- function
print('pre getpeername', type(sock.getpeername)) -- function
sock, err = ssl.wrap(sock, { mode = 'client', protocol = 'any', verify = 'none', options = 'all' })
print('post setoption', type(sock.setoption)) -- nil
print('post getoption', type(sock.getoption)) -- nil
print('post getpeername', type(sock.getpeername)) -- nil

Cosock/ssl.lua has a bunch of passthrough declarations and these fucntions are missing. I assume there are many more.

Adding the passthroughs to cosock gets rid of the nils, but there are other crashes inside cosock/internals as a result.

Hi, @blueyetisoftware

I asked the team about this and they mentioned the following:

  1. This is the behavior of the upstream Lua libraries, not the result of a modification made.
  2. There are some workarounds you can try:
    a. Set all socket options before wrapping (they have confirmed this option works)
    b. Maintain a reference to both the wrapped and unwrapped sockets, set the option on the unwrapped socket

If you choose option b on #2, it would be helpful to know if your results were successful.

Please, let us know if you have any questions.

@nayelyz I can confirm that the functions are callable before wrapping. Although getoption in particular has a major issue. I reported this issue here. The getoption function locks up and hangs:

Other than getoption, getpeername can be called after the fact by accessing the original pre-wrapped socket. I handled it like this to test your suggestion:

sock, err = ssl.wrap(pre, { mode = 'client', protocol = 'any', verify = 'none', options = 'all' })
if sock and not err then
  _, err = sock:dohandshake()
  sock.pre = pre -- added the pre-wrapped socket as an additional field on the socket
else
  log.error('error setting up TLS', err)
end

I was then able to call getpeername downstream later in the driver

print('sock.pre', type(sock.pre)) -- table
print('sock.pre.getpeername', type(sock.pre.getpeername)) -- function
print('getpeername', sock.pre:getpeername()) -- getpeername  192.168.0.XXX     443

It would be nice if cosock wrap exposed these pre-wrapped portions of the socket api. The bug with getoption is a big one if you need to check options on a socket to handle things later in the driver execution. Since it hangs, the resulting nil socket issues in other parts of the driver were tricky to originally diagnose.

Hi, @blueyetisoftware
Thank you for bringing this up.

The team confirmed it is a bug and they made the corresponding report. Once we get more info, we’ll let you know.

1 Like