lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Mon, Aug 25, 2014 at 6:52 PM, William Ahern
<william@25thandclement.com> wrote:
> On Mon, Aug 25, 2014 at 04:54:53PM -0700, Coroutines wrote:
>> On Mon, Aug 25, 2014 at 4:38 PM, Sean Conner <sean@conman.org> wrote:
> <snip>
>> >   read() is fine for TCP and for "connected" UDP sockets [1], but for
>> > unconnected TCP (or even packets for other IP protocols like OSPF) you can't
>> > use read().  That's one reason I rejected using read().
>>
>> Secret:  You can associate the remote peer (sockaddr) with a UDP
>> socket by calling connect() -- after that I believe you can call
>> read() like normal.  The man page says you can only connect() once for
>> TCP sockets, but you can call connect() multiple times for UDP
>> sockets.  This results in the kernel associating the remote peer with
>> that socket in the networking stack -- outwardly it doesn't seem to
>> have any effect.  People think because you only have an fd (an int)
>> that nothing happened -- buwhahaha bsd socket juju.  I know for sure
>> is how you can use recv() instead of recvfrom() on a UDP socket.
>>
>
> This is broken on Linux, although I'm not sure when this problem was
> introduced. I brought it up on the netdev mailing list last year and nobody
> seemed to care.
>
> If you try to reassociate a socket from a loopback address to a non-loopback
> address then connect will fail. The problem resides in the kernel handling
> of loopback device binding. I couldn't figure it out but somebody else on
> comp.unix.programmer tracked it down. It actually only happens if the
> _first_ bind was to a loopback address.
>
> I originally stumbled upon this issue with my DNS resolver. In the resolver
> I would simply reassociate the UDP socket using connect. However, if
> somebody had an /etc/resolv.conf like
>
>         nameserver 127.0.0.1
>         nameserver 8.8.8.8
>
> then it would return an error on the rare occassion it failed over to
> 8.8.8.8. You can see the behavior using the example code I've pasted at the
> end of the message.
>
> $ ./udp 127.0.0.1 8.8.8.8
> connect(127.0.0.1): OK
> connect(8.8.8.8): Invalid argument
>
> $ ./udp 8.8.8.8 127.0.0.1
> connect(8.8.8.8): OK
> connect(127.0.0.1): OK
>
> I've only seen this behavior on Linux. On every other systems I've tried--OS
> X, *BSD, Solaris, AIX--you get the expected behavior.
>
> The "fix" is to first unbind the socket by connecting it to AF_UNSPEC.
> However, keep in mind that some systems (e.g. OS X) will return an error
> when calling connect with an AF_UNSPEC address faily. So you either have to
> do this using platform-specific logic, or ignore any failure when
> "unbinding" the socket (or both, because I'm not sure which kernel version
> or patch introduced this behavior).
>
> Here's sample test code:
>
> #include <stdio.h>
> #include <string.h>
> #include <errno.h>
> #include <sys/types.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
> #include <arpa/inet.h>
> #include <err.h>
>
> int main(int argc, char *argv[]) {
>   int i, fd;
>   struct sockaddr_in sin;
>
>   if (-1 == (fd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC)))
>     err(1, "socket");
>
>   for (i = 1; i < argc; i++) {
>     sin.sin_family = AF_INET;
>     sin.sin_port = htons(53);
>     sin.sin_addr.s_addr = inet_addr(argv[i]);
>
>     if (0 != connect(fd, (void *)&sin, sizeof sin))
>       printf("connect(%s): %s\n", inet_ntoa(sin.sin_addr), strerror(errno));
>     else
>       printf("connect(%s): OK\n", inet_ntoa(sin.sin_addr));
>   }
>
>   return 0;
> }
>

Where have you been all my life?