/********************************************** * Pierre A. Humblet. Jan 24, 2002 This demo program exposes a bug where there are two listen on the same tcp port. New connections may or may not be accepted **********************************************/ #include #include #include #include #include #include #include #include enum {FALSE, TRUE}; static int sighup_seen; static int call_seen; /************************************************* * SIGHUP Handler * *************************************************/ static void sighup_handler(int sig) { sig = sig; /* Keep picky compilers happy */ sighup_seen = TRUE; signal(SIGHUP, sighup_handler); } /************************************************* * main * *************************************************/ main(int argc, char * argv[]) { int on = 1, sockfd, newsockfd, lcount, peer_size, pid; struct sockaddr_in addr, peer; fd_set select_listen; FILE *outfd; /************************************************ * Open a socket and listen on port 999 ************************************************/ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) printf("socket: %s\n", strerror(errno)); if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) printf("setsockopt: %s\n", strerror(errno)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(999); addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) printf("bind: %s\n", strerror(errno)); if (listen(sockfd, 2) < 0) printf("listen: %d %s\n", errno, strerror(errno)); sighup_seen = FALSE; signal(SIGHUP, sighup_handler); call_seen = FALSE; pid = getpid(); /************************************************ * Now wait for a call or sighup, causing a re-exec After a re-exec following a call, there are two listen on port 999 (netstat -a) Incoming calls may or may not work. ************************************************/ while(1) { FD_ZERO(&select_listen); FD_SET(sockfd, &select_listen); if (getenv("EXEC") != NULL) { printf("netstat -a will show two listen on port 999\n"); } else if (call_seen == FALSE) { printf("telnet localhost 999\n"); } else printf("kill -HUP %d\n", pid); lcount = select(sockfd + 1, &select_listen, NULL, NULL, NULL); if (lcount == 1) { peer_size = sizeof(peer); newsockfd = accept(sockfd, (struct sockaddr *) &peer, &peer_size); if (newsockfd < 0) { printf("accept: %s\n", strerror(errno)); } else { printf("Got a call\n"); call_seen = TRUE; outfd = fdopen(newsockfd, "wb"); if (outfd == NULL) printf("fdopen: %s\n", strerror(errno)); if (fork() == 0) { signal(SIGHUP, SIG_IGN); close(sockfd); /* Close the listening socket */ fprintf(outfd, "quit with ESC CTRL-] and quit then\n"); fprintf(outfd, "kill -HUP %d\n", pid); fprintf(outfd, "netstat -a will show two listen on port 999\n"); fflush(outfd); /* In this demo we sleep. In reality the child is working */ sleep(60); printf("Child exiting\n"); fclose(outfd); /* Close the accepted socket */ exit(0); } else { fclose(outfd); /* Close the accepted socket */ } } } else if (sighup_seen == TRUE) { if (call_seen == TRUE) putenv("EXEC=1"); close(sockfd); /* Close the listening socket */ signal(SIGHUP, SIG_IGN); printf("Signal seen. Execing\n"); execv(argv[0], argv); } else printf("Error %s\n", strerror(errno)); } }