[erlang-questions] prim_inet:accept() bug with {fd, N} socket option?

Per Hedeland <>
Mon Oct 8 08:12:34 CEST 2007


Serge Aleynikov <> wrote:
>
>Per Hedeland wrote:
>> Serge Aleynikov <> wrote:
>>> gen_tcp:connect/2, gen_tcp:listen/2 functions expose an option {fd, 
>>> FileDescriptor} that allows to take an elsewhere opened file descriptor 
>>> and make it usable in the gen_tcp module.  This is a very convenient 
>>> option, however, what I discovered was that the default options that are 
>>> being set on a socket by prim_inet:accept_opts/2 call involved in 
>>> gen_tcp:accept/1 try to get/set a 'priority' option that may not be 
>>> supported
>> 
>> Well, it's rather dubious to pass in a non-inet socket to the inet
>> modules/driver, 
>
>What else would be the use of the {fd, N} option in gen_tcp:connect/2, 
>gen_tcp:listen/2?

Primarily the case where you need to bind a socket to a "privileged"
port, but don't want to run the whole Erlang system as root. You can
then (create and) bind the socket in an external process, and pass it to
Erlang via sendmsg(2) and a driver. You could also have "inherited" the
socket descriptor somehow, e.g. if you start Erlang via [x]inetd,
stdin/out/err is a socket - this can also be a way to handle the
privileged port case: Bind the socket in a setuid-root or root-started
program that then setuid()s to an unprivileged uid and execs the Erlang
runtime system.

>  I would think that Unix Domain Sockets (UDS) is a 
>rather good example or else one would need to mirror all 
>gen_tcp/inet_tcp/prim_inet functionality to support UDS.
>
>Ideally the inet_drv along with gen_tcp/inet_tcp/prim_inet would be 
>extended to be able to support UDS on Unix-like platforms.

Yeah, it's nice to get all that stuff for "UDS", but I think it's clear
from the code that this was not the intention. The inet driver clearly
assumes AF_INET[6] sockets, not just by way of its name. And UDS doesn't
do TCP (or UDP) - of course the difference from generic SOCK_STREAM or
SOCK_DGRAM is pretty minimal (mainly the set of available options), but
gen_tcp and gen_udp would at least be "inappropriate" names...

>> Below is a patch that addresses this - it's against R10B-10 though, and
>> won't apply against current versions since even though the code is
>> essentially the same there, it has moved around a bit. But maybe you can
>> convert it - or just comment out the whole setopt_prio_tos_trick() thing
>> there.:-)
>
>Thank you very much, this is very helpful!

I converted the patch to match R11B-5, it was pretty trivial, but let me
know if you want it (untested though).

>> I have subsequently hacked prim_inet and inet_drv to handle arbitrary
>> non-inet sockets "properly", i.e. allow for passing in 'unspec' (as in
>> AF_UNSPEC) as the address family, and in that case refrain from doing
>> *any* AF_INET/AF_INET6-specific get/setsockopts, as well as
>> getsockname() and getpeername() (since the address format is unknown).
>> It's perhaps still a bit dubious to "abuse" the inet code this way
>> though, but you sure get a lot of stuff for free that way...
>
>Perhaps you could send these patches as well and the OTP team could 
>consider including them in the distribution?

If they want them, sure (actually the EPL says that I must:-), but I'm
not sure that they do... In any case you don't really need them for UDS,
the inet driver is very "tolerant" of failing get/setsockopts (except in
the case of SO_PRIORITY/IP_TOS:-), so it "just works".

I had a broader requirement though, to support any SOCK_STREAM type,
allowing the customer to plug in the needed interface code, and didn't
want to have to answer questions about why we were attempting to do
INET-specific things with it.:-) More seriously, there is a very real
possibility that getsockname()/getpeername() calls assuming AF_INET[6]
will fail in the case of an address family that is neither INET nor
UNIX/LOCAL - the inet driver does these w/o being asked in some cases
(in particular when passing in an existing file descriptor:-), and
failure there is fatal.

Note also that I don't use gen_tcp for passing in the descriptor and so
haven't modified it for the 'unspec' family - a mostly-wrapper module
with the same interface as gen_tcp talks to the driver that creates the
socket, and passes the descriptor in via inet:fdopen/5 - maybe
marginally "cleaner", but of course it just turns around and calls
gen_tcp for accept/recv/send etc, and you still get 'tcp' in the tuples
received in active mode.

--Per



More information about the erlang-questions mailing list