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

Re: [MiNT] tcp_select ( was: Re: inet4: errno is not set correct )



Hello,

I just got reply from the cURL team. I described the bug and the behavior seems to be non POSIX compliant.
Message: http://article.gmane.org/gmane.comp.web.curl.library/29506

So if FreeMiNT wants to be POXIS compatible, it seems that this can be described as an bug of the socket API (only if there is no other bug lurking within my test case, of course...).

It seems like the poll is returning to early, it returns when the socket is ready to be used for sending data ( from FreeMiNTs point of view) - but it is still not ready for reading, the recv() call is ending up with the ENOTCON error... but previous send() did not return that error!

If you uncomment the usleep within the code, or pass an higher timeout value to socket_ready (which is curios - why does poll runs longer when you pass an higher timeout? Maybe the return is also wrong..., not just the returned flags), the recv() returns the error EAGAIN, which is fine. That's how it should behave,...

The following contains the Test Case, which is already reduced ;) The same code is within the file attached.
I compiled it with:

gcc -O2 ( Version 4.4.3 )

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

#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#define _GNU_SOURCE
#include <poll.h>



#define BUFSIZE 48

int so_pri;
int so_sec;
struct sockaddr_in addr;
struct hostent *host;
char * p_host = "google.com";
char buf[BUFSIZE];
int nbytes;
int so_opt;

char * request = "GET / HTTP/1.1\n"
"Accept: */*\n"
"Accept-Encoding: gzip;q=0, deflate; q=0, *;q=0\n"
"Host: google.com\n"
"\r\n\r\n";

struct timeval curlx_tvnow(void)
{
 /*
** gettimeofday() is not granted to be increased monotonically, due to ** clock drifting and external source time synchronization it can jump
 ** forward or backward in time.
 */
 struct timeval now;
 (void)gettimeofday(&now, NULL);
 return now;
}


/*
* Make sure that the first argument is the more recent time, as otherwise
* we'll get a weird negative time-diff back...
*
* Returns: the time difference in number of milliseconds.
*/
long curlx_tvdiff(struct timeval newer, struct timeval older)
{
 return (newer.tv_sec-older.tv_sec)*1000+
   (newer.tv_usec-older.tv_usec)/1000;
}

/*
* Same as curlx_tvdiff but with full usec resolution.
*
* Returns: the time difference in seconds with subsecond resolution.
*/
double curlx_tvdiff_secs(struct timeval newer, struct timeval older)
{
 return (double)(newer.tv_sec-older.tv_sec)+
   (double)(newer.tv_usec-older.tv_usec)/1000000.0;
}

/* return the number of seconds in the given input timeval struct */
long Curl_tvlong(struct timeval t1)
{
 return t1.tv_sec;
}

#define  CURL_CSELECT_OUT 4
#define  CURL_CSELECT_ERR 8


#ifndef POLLRDNORM
#define POLLRDNORM POLLIN
#endif

#ifndef POLLWRNORM
#define POLLWRNORM POLLOUT
#endif

#ifndef POLLRDBAND
#define POLLRDBAND POLLPRI
#endif

#define SOCKERRNO (errno)

#define elapsed_ms  (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
#define error_not_EINTR (error != EINTR)

int socket_ready(int writefd, int timeout_ms)
{
	struct pollfd pfd[2];
	int num;
	struct timeval initial_tv = {0,0};
	int pending_ms = 0;
	int error;
	int r=-1;
	int ret;

	if(timeout_ms > 0)
	{
		pending_ms = timeout_ms;
		initial_tv = curlx_tvnow();
   }

	num = 0;
	pfd[num].fd = writefd;
	pfd[num].events = POLLWRNORM|POLLOUT;
	pfd[num].revents = 0;
	num++;

 do {
   if(timeout_ms < 0)
     pending_ms = -1;
   else if(!timeout_ms)
     pending_ms = 0;
   r = poll(pfd, num, pending_ms);
   if(r != -1)
     break;
   error = SOCKERRNO;
   if(error && error_not_EINTR)
     break;
   if(timeout_ms > 0) {
     pending_ms = timeout_ms - elapsed_ms;
     if(pending_ms <= 0)
       break;
   }
 } while(r == -1);

 if(r < 0)
   return -1;
 if(r == 0)
   return 0;

 ret = 0;
 num = 0;

   if(pfd[num].revents & (POLLWRNORM|POLLOUT))
     ret |= CURL_CSELECT_OUT;
   if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
     ret |= CURL_CSELECT_ERR;

 return ret;
}


int main()
{
	int r;

  so_pri = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (so_pri == -1)
	{
		perror("socket() failed");
		return 1;
	}

	so_opt = 1;
if( setsockopt(so_pri, SOL_SOCKET, SO_REUSEADDR, (char *)&so_opt, sizeof(so_opt)) == -1)
	{
		perror("setsockopt failed");
		return 2;
	}

	printf("Resolving host: %s ...\n", p_host );
	host = gethostbyname( p_host );
	if (!host)
	{
		perror("gethostbyname() failed");
		return 3;
	}
	addr.sin_addr = *(struct in_addr*) host->h_addr;
	addr.sin_port = htons(80);
	addr.sin_family = AF_INET;
	printf("%s\n", inet_ntoa(addr.sin_addr) );
	fcntl(so_pri, F_SETFL, O_NONBLOCK );


	printf("C0nnecting...");

	if( connect(so_pri, (struct sockaddr*) &addr, sizeof(addr)) == -1)
	{
		perror("connect()");
	}
	int c = 0;
	while( 1 ) {
		if( c > 100 )
			return( 16 );
		r = socket_ready( so_pri, 10 );
		if( r == -1 )
		{
			printf("Select error!");
			return( 5 );
		}
		else if( r == 0)
		{
			printf("Connect timeout!\n");
		}
		else if( r & CURL_CSELECT_ERR )
		{
			printf("CURL_CSELECT_ERR!\n");
		}
		else if( r & CURL_CSELECT_OUT ) {
			break;
		}
		c++;
	}
	printf("Connect OK!\n");

	send(so_pri, request, strlen(request), 0);

	//usleep(50000);
	nbytes = recv(so_pri, buf, sizeof(buf) - 1, 0);
   if (nbytes == -1)
   {
       perror("recv() failed");
       printf("Errno: %d", errno );
       return 5;
   }

   printf("Done\n");

   close(so_pri);

   return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#define _GNU_SOURCE
#include <poll.h>



#define BUFSIZE 48

int so_pri;
int so_sec;
struct sockaddr_in addr;
struct hostent *host;
char * p_host = "google.com";
char buf[BUFSIZE];
int nbytes;
int so_opt;

char * request = "GET / HTTP/1.1\n"
"Accept: */*\n"
"Accept-Encoding: gzip;q=0, deflate; q=0, *;q=0\n"
"Host: google.com\n"
"\r\n\r\n";

struct timeval curlx_tvnow(void)
{
  /*
  ** gettimeofday() is not granted to be increased monotonically, due to
  ** clock drifting and external source time synchronization it can jump
  ** forward or backward in time.
  */
  struct timeval now;
  (void)gettimeofday(&now, NULL);
  return now;
}


/*
 * Make sure that the first argument is the more recent time, as otherwise
 * we'll get a weird negative time-diff back...
 *
 * Returns: the time difference in number of milliseconds.
 */
long curlx_tvdiff(struct timeval newer, struct timeval older)
{
  return (newer.tv_sec-older.tv_sec)*1000+
    (newer.tv_usec-older.tv_usec)/1000;
}

/*
 * Same as curlx_tvdiff but with full usec resolution.
 *
 * Returns: the time difference in seconds with subsecond resolution.
 */
double curlx_tvdiff_secs(struct timeval newer, struct timeval older)
{
  return (double)(newer.tv_sec-older.tv_sec)+
    (double)(newer.tv_usec-older.tv_usec)/1000000.0;
}

/* return the number of seconds in the given input timeval struct */
long Curl_tvlong(struct timeval t1)
{
  return t1.tv_sec;
}

#define  CURL_CSELECT_OUT 4
#define  CURL_CSELECT_ERR 8


#ifndef POLLRDNORM
#define POLLRDNORM POLLIN
#endif

#ifndef POLLWRNORM
#define POLLWRNORM POLLOUT
#endif

#ifndef POLLRDBAND
#define POLLRDBAND POLLPRI
#endif

#define SOCKERRNO (errno)

#define elapsed_ms  (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
#define error_not_EINTR (error != EINTR)

int socket_ready(int writefd, int timeout_ms)
{
	struct pollfd pfd[2];
	int num;
	struct timeval initial_tv = {0,0};
	int pending_ms = 0;
	int error;
	int r=-1;
	int ret;

	if(timeout_ms > 0)
	{
		pending_ms = timeout_ms;
		initial_tv = curlx_tvnow();
    }

	num = 0;
	pfd[num].fd = writefd;
	pfd[num].events = POLLWRNORM|POLLOUT;
	pfd[num].revents = 0;
	num++;

  do {
    if(timeout_ms < 0)
      pending_ms = -1;
    else if(!timeout_ms)
      pending_ms = 0;
    r = poll(pfd, num, pending_ms);
    if(r != -1)
      break;
    error = SOCKERRNO;
    if(error && error_not_EINTR)
      break;
    if(timeout_ms > 0) {
      pending_ms = timeout_ms - elapsed_ms;
      if(pending_ms <= 0)
        break;
    }
  } while(r == -1);

  if(r < 0)
    return -1;
  if(r == 0)
    return 0;

  ret = 0;
  num = 0;

    if(pfd[num].revents & (POLLWRNORM|POLLOUT))
      ret |= CURL_CSELECT_OUT;
    if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
      ret |= CURL_CSELECT_ERR;

  return ret;
}


int main()
{
	int r;
 
   so_pri = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (so_pri == -1)
	{
		perror("socket() failed");
		return 1;
	}

	so_opt = 1;
	if( setsockopt(so_pri, SOL_SOCKET, SO_REUSEADDR, (char *)&so_opt, sizeof(so_opt)) == -1)
	{
		perror("setsockopt failed");
		return 2;
	}

	printf("Resolving host: %s ...\n", p_host );
	host = gethostbyname( p_host );
	if (!host)
	{
		perror("gethostbyname() failed");
		return 3;
	}
	addr.sin_addr = *(struct in_addr*) host->h_addr;
	addr.sin_port = htons(80);
	addr.sin_family = AF_INET;
	printf("%s\n", inet_ntoa(addr.sin_addr) );
	fcntl(so_pri, F_SETFL, O_NONBLOCK );


	printf("C0nnecting...");

	if( connect(so_pri, (struct sockaddr*) &addr, sizeof(addr)) == -1)
	{
		perror("connect()");
	}
	int c = 0;
	while( 1 ) {
		if( c > 100 )
			return( 16 );
		r = socket_ready( so_pri, 10 );
		if( r == -1 )
		{
			printf("Select error!");
			return( 5 );
		}
		else if( r == 0)
		{
			printf("Connect timeout!\n");
		}
		else if( r & CURL_CSELECT_ERR )
		{
			printf("CURL_CSELECT_ERR!\n");
		}
		else if( r & CURL_CSELECT_OUT ) {
			break;
		}
		c++;
	}
	printf("Connect OK!\n");

	send(so_pri, request, strlen(request), 0);

	//usleep(50000);
	nbytes = recv(so_pri, buf, sizeof(buf) - 1, 0);
    if (nbytes == -1)
    {
        perror("recv() failed");
        printf("Errno: %d", errno );
        return 5;
    }

    printf("Done\n");

    close(so_pri);

    return 0;
}