[erlang-questions] inet_res:getbyname/2 and udp:connect/3

Tony Rogvall tony@REDACTED
Thu Jun 17 13:15:15 CEST 2010


On 17 jun 2010, at 00.33, Per Hedeland wrote:

> Tony Rogvall <tony@REDACTED> wrote:
>> Cool!
>> send is failing on subsequent send, but recv is not ???? This is strange
>> times.
> 
> Erlang gen_udp:recv() is not failing.

No, I meant it returned ok instead of {error, econnrefused}

> 
>> Do you think this behavior is documented anywhere?
> 
> 'man inet_drv' maybe?
> 

> man inet_drv
No manual entry for inet_drv

I was thinking in terms of how to get connected UDP working on more than one platform.

I have been testing a similar C program (as the one you write) on Linux and Darwin and think it is a bit confusing
to say the least.

If I test poll under Darwin (10.3.0)  it will timeout, I have set the timeout to 5s and the timeout is early. So it is clearly a bug.

./cudp 
poll: timeout  (too early)
poll: revents=0

(poll under Darwin exist but is not very useful since it can only be used for sockets and pipes, also buggy ;-)
if I run select under Darwin it will work as intended. BTW select is normally chosen as poll method for Darwin.

./cudp 
select: readable
read:select:: Connection refused


under Linux (2.6.32-22) select is also working.

./cudp 
select: readable
read:select:: Connection refused

Poll is also working but return only POLLERR in revents.
./cudp
poll: revents=8  (POLLERR)
read:poll:: Connection refused

This may be the problem, I have not read erl_check_io.c in depth but someone may have ;-)



>> A small C program may clear this issue for us ?
> 
> A small C program (below) works perfectly:
> 
> $ ./conn_udp
> recv: Connection refused
> 
> I might point out that the "connect UDP socket in resolver code" trick
> was invented in BSD code at a point in time when not much else even
> *had* TCP/IP.:-)
> 
> But inet_drv is "slightly" more complex than that program...
> 
>> To get the {error, econnrefused} speed things up when trying servers
>> where DNS servers may have crashed.
> 
> Yes, but it's not DNS-specific of course, it can be useful for any
> UDP-based protocol.
> 
>> But in the general case, server may be down/burned, routers is
>> down/corrupt, this will not help since no one will send
>> the ICMP reply message on the network.
> 
> True - and add to that misconfigured firewalls that drop all ICMP
> packets and there is probably not much usefulness left. Maybe just as a
> way to quickly find out if you are running a local server.
> 
> --Per
> 
> conn_udp.c---------------------------------------------
> 
> #include <sys/types.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
> #include <arpa/inet.h>
> #include <stdlib.h>
> #include <stdio.h>
> 
> int error(char *str)
> {
>    perror(str);
>    exit(1);
> }
> 
> int main()
> {
>    struct sockaddr_in addr;
>    int fd;
>    char buf[1];
> 
>    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
>    addr.sin_family = AF_INET;
>    addr.sin_port = htons(12345);
>    buf[0] = 'x';
>    if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
> 	error("socket");
>    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
> 	error("connect");
>    if (send(fd, buf, sizeof(buf), 0) < 0)
> 	error("send");
>    if (recv(fd, buf, sizeof(buf), 0) < 0)
> 	error("recv");
> 
>    return 0;
> }


cudp.c ----------------------------------------

//
//  Connected UDP test
//
#include <sys/types.h>
#include <sys/socket.h>

#include <stdio.h>
#include <stdlib.h>

// #define USE_SELECT
// #define USE_POLL

#ifdef USE_SELECT
#include <sys/select.h>
#endif

#ifdef USE_POLL
#include <poll.h>
#endif

#include <netinet/in.h>

#define TIMEOUT 5000
#define LOCAL_PORT  0
#define LOCAL_ADDR  INADDR_ANY

#define REMOTE_PORT 53
#define REMOTE_ADDR INADDR_LOOPBACK

main()
{
    struct sockaddr_in laddr;
    struct sockaddr_in raddr;
    int s;
    socklen_t len;
    char obuf[3];
    char ibuf[3];

    if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	perror("socket");
	exit(1);
    }
    laddr.sin_family        = AF_INET;
    laddr.sin_port          = htons(LOCAL_PORT);
    laddr.sin_addr.s_addr   = htonl(LOCAL_ADDR);
    len = sizeof(laddr);

    if (bind(s, (struct sockaddr*) &laddr, len) < 0) {
	perror("bind");
	exit(1);
    }
    
    raddr.sin_family        = AF_INET;
    raddr.sin_port          = htons(REMOTE_PORT);
    raddr.sin_addr.s_addr   = htonl(REMOTE_ADDR);
    len = sizeof(raddr);
    if (connect(s, (struct sockaddr*) &raddr, len) < 0) {
	perror("bind");
	exit(1);
    }

    obuf[0] = 1;
    obuf[1] = 2;
    obuf[2] = 3;
    if (write(s, obuf, 3) < 0) {
	perror("write");
	exit(1);
    }

#ifdef USE_POLL
    { 
	int n;
	struct pollfd fds[1];

	fds[0].fd = s;
	fds[0].events = POLLIN; // |POLLOUT;
	fds[0].revents = 0;
    
	if ((n=poll(fds, 1, TIMEOUT)) < 0) {
	    perror("poll");
	    exit(1);
	}
	if (n == 0) {
	    fprintf(stderr, "poll: timeout\n");
	    fprintf(stderr, "poll: revents=%x\n", fds[0].revents);
	    exit(1);
	}
	if (fds[0].revents & (POLLIN|POLLOUT|POLLERR)) {
	    fprintf(stderr, "poll: revents=%x\n", fds[0].revents);
	    if (read(s, ibuf, 3) < 0) {
		perror("read:poll:");
		exit(1);
	    }
	}
	else {
	    fprintf(stderr, "poll: no event\n");
	    exit(1);
	}
    }
#elif defined(USE_SELECT)
    {
	int n;
	fd_set readfds;
	fd_set writefds;
	fd_set errorfds;
	struct timeval timeout;

	FD_ZERO(&readfds);
	FD_SET(s, &readfds);

	FD_ZERO(&writefds);
	// FD_SET(s, &writefds);

	FD_ZERO(&errorfds);
	FD_SET(s, &errorfds);

	timeout.tv_sec = TIMEOUT / 1000;
	timeout.tv_usec = (TIMEOUT % 1000)*1000;

	if (select(s+1, &readfds, &writefds, &errorfds, &timeout) < 0) {
	    perror("select");
	    exit(1);
	}
	if (n == 0) {
	    fprintf(stderr, "select: timeout\n");
	    exit(1);
	}
	if (FD_ISSET(s, &readfds))
	    fprintf(stderr, "select: readable\n");
	if (FD_ISSET(s, &writefds))
	    fprintf(stderr, "select: writable\n");
	if (FD_ISSET(s, &errorfds))
	    fprintf(stderr, "select: error\n");


	if (FD_ISSET(s, &readfds)) {
	    if (read(s, ibuf, 3) < 0) {
		perror("read:select:");
		exit(1);
	    }
	}
	else {
	    fprintf(stderr, "select: no event\n");
	    exit(1);
	}
    }
#else
    if (read(s, ibuf, 3) < 0) {
	perror("read");
	exit(1);
    }
#endif
    close(s);
    exit(0);
}



More information about the erlang-questions mailing list