This is the mail archive of the cygwin@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

bug: tcp RST instead of FIN if child exists after parent closes path


I came across this while trying to get amanda to compile for CygWin and
wrote up a small test program (attached) to demonstrate the problem.

If a process opens a socket and then passes that path to a child process,
the path is now open by both.  As long as the child program exits _before_
the parent closes the socket, then everything is fine:

root at watertown ~/build
$ ./tcptest --before griffon 9
connection established
child process has exited and closed socket path
closing main program's socket path
test complete

Here's the output from tcpdump of the session.  Everything is correct.
Notice that the FINs don't occur until 1 second after the initial handshake
is completed, or after the child "sleep 1" is completed.

12:18:06.014140 watertown.ott.precidia.com.3669 > griffon.ott.precidia.com.discard: S 2389465615:2389465615(0) win 16384 <mss 1460,nop,nop,sackOK> (DF)
12:18:06.014247 griffon.ott.precidia.com.discard > watertown.ott.precidia.com.3669: S 690327584:690327584(0) ack 2389465616 win 5840 <mss 1460,nop,nop,sackOK> (DF)
12:18:06.014654 griffon.ott.precidia.com.discard > watertown.ott.precidia.com.3669: S 690327584:690327584(0) ack 2389465616 win 5840 <mss 1460,nop,nop,sackOK> (DF)
12:18:06.014660 watertown.ott.precidia.com.3669 > griffon.ott.precidia.com.discard: . ack 1 win 17520 (DF)
12:18:07.141048 watertown.ott.precidia.com.3669 > griffon.ott.precidia.com.discard: F 1:1(0) ack 1 win 17520 (DF)
12:18:07.141676 griffon.ott.precidia.com.discard > watertown.ott.precidia.com.3669: F 1:1(0) ack 2 win 5840 (DF)
12:18:07.141976 griffon.ott.precidia.com.discard > watertown.ott.precidia.com.3669: F 1:1(0) ack 2 win 5840 (DF)
12:18:07.142235 watertown.ott.precidia.com.3669 > griffon.ott.precidia.com.discard: . ack 2 win 17520 (DF)


Next I run the test program telling it to have the child exit _after_ the
main program has closed the socket.

root at watertown ~/build
$ ./tcptest --after griffon 9
connection established
closing main program's socket path
child process has exited and closed socket path
test complete

This time, when the parent closes the socket path with the child still running,
then the connection is aborted with a RST instead of a FIN.  This is not good.
Note that the reset is still 1 second after the initial handshake showing that
it only happens at the point when the child exits.

12:18:14.554661 watertown.ott.precidia.com.3768 > griffon.ott.precidia.com.discard: S 2396409222:2396409222(0) win 16384 <mss 1460,nop,nop,sackOK> (DF)
12:18:14.554774 griffon.ott.precidia.com.discard > watertown.ott.precidia.com.3768: S 693255112:693255112(0) ack 2396409223 win 5840 <mss 1460,nop,nop,sackOK> (DF)
12:18:14.555174 griffon.ott.precidia.com.discard > watertown.ott.precidia.com.3768: S 693255112:693255112(0) ack 2396409223 win 5840 <mss 1460,nop,nop,sackOK> (DF)
12:18:14.555180 watertown.ott.precidia.com.3768 > griffon.ott.precidia.com.discard: . ack 1 win 17520 (DF)
12:18:15.646499 watertown.ott.precidia.com.3768 > griffon.ott.precidia.com.discard: R 2396409223:2396409223(0) win 0 (DF)


Running the same program under Linux shows that in both cases the session
closes with a FIN.

                                          Brian
                                 ( bcwhite at precidia dot com )

-------------------------------------------------------------------------------
Seize the moment!  Live now.  Make "now" always the most important time. -- JLP
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <cygwin/in.h>
#include <netdb.h>


int main(int argc, char* argv[])
{
	struct sockaddr_in	addr;
	struct hostent*		host;
	int			before;
	int			sock;
	int			pid;
	int			status;

	if (argc != 4 || (strcmp(argv[1],"--before") != 0 && strcmp(argv[1],"--after") != 0)) {
		fprintf(stderr,"Use: %s <--before|--after> <host> <port>\n",argv[0]);
		exit(1);
	}

	if (strcmp(argv[1],"--before") != 0) {
		before = 1;
	} else {
		before = 0;
	}

	sock = socket(AF_INET,SOCK_STREAM,0);
	if (sock == -1) {
		perror("could not create socket");
		exit(1);
	}

	host = gethostbyname(argv[2]);
	if (host == NULL) {
		herror("could not resolve hostname");
		exit(1);
	}

	bzero(&addr,sizeof(addr));
	addr.sin_family = host->h_addrtype;
	memcpy(&addr.sin_addr,host->h_addr,sizeof(addr.sin_addr));
	addr.sin_port = htons(atoi(argv[3]));

	if (connect(sock,(struct sockaddr*)&addr,sizeof(addr)) == -1) {
		perror("could not connect to host");
		exit(1);
	}

	fprintf(stderr,"connection established\n");

	pid = fork();
	switch (pid) {
	case -1: /* error */
		perror("could not fork");
		exit(1);
	case 0: /* child */
		execlp("sleep","sleep","1",NULL);
		perror("could not execlp 'sleep 1'");
		exit(1);
	default: /* parent */
		break;
	}

	if (before) {
		fprintf(stderr,"closing main program's socket path\n");
		close(sock);
	}

	if (waitpid(pid,&status,0) == -1) {
		perror("could not wait for child");
		exit(1);
	}
	fprintf(stderr,"child process has exited and closed socket path\n");

	if (!before) {
		fprintf(stderr,"closing main program's socket path\n");
		close(sock);
	}

	fprintf(stderr,"test complete\n");
}

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Bug reporting:         http://cygwin.com/bugs.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]