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.

@nayelyz Sorry to revive a zombie thread on you but this report from 2023 still looks to be unresolved as of API 16. Cosock still doesn’t allow passthrough calling of getpeername(), getoption() and setoption(). I understand that the context and attention has likely been lost, but is there any tracking on if this bug was ever looked at?

Added this PR to the luasec library. Hopefully they can review it. Will propose similar changes to cosock if/when it gets approved.

Hi, @blueyetisoftware
I’ve been checking the old report, and the thing identified as a bug was the execution hanging.
The first feedback provided here regarding the TCP methods not being passed through after calling ssl.wrap on them remains unchanged.
Also, the current implementation of “getoption” is still in progress since the cosock code is wrapped around the base socket, so the method exists in cosock, but when it tries to call the method on the base socket, it results in a nil index error.
The team will work on improvements on this one and other methods, but there’s no ETA on them. Stay tuned for the release notes in the firmware updates announcements.

Thanks. The PR I added about resolves the pass through issue you linked. I understand that :getoption is still hanging