| Summary: | Error in accept(2) man page | ||
|---|---|---|---|
| Product: | Documentation | Reporter: | William Sloan <wsloan> |
| Component: | Books & Articles | Assignee: | Remko Lodder <remko> |
| Status: | Closed FIXED | ||
| Severity: | Affects Only Me | ||
| Priority: | Normal | ||
| Version: | Latest | ||
| Hardware: | Any | ||
| OS: | Any | ||
State Changed From-To: open->patched An appropriate fix has been committed to -CURRENT Responsible Changed From-To: freebsd-doc->bms I was responsible for making the last change to the code concerned. State Changed From-To: patched->closed I MFC'ed it to RELENG_5 as per request of rionda. Responsible Changed From-To: bms->remko Grab replies on this, since i should be the one that gets slapped if things go wrong and not bms. |
The accept(2) man pages states that the new socket has the same properties as the orginal socket. This appears not to be the case as the output from attached C program shows. Server Info: Status of socket 3 Server Info: Socket send timeout is 5 Server Info: Socket recv timeout is 5 Server Info: REUSEADDR flag is 4 Server Info: Linger options are 128 and 5 Server Info: Nonblocking IO is 4 Server Info: Async IO is 64 Server Info: close-on-exec flag is 1 Server Info: Pid owner is 59559 Server Info: After accept Server Info: Orignal socket Server Info: Status of socket 3 Server Info: Socket send timeout is 5 Server Info: Socket recv timeout is 5 Server Info: REUSEADDR flag is 4 Server Info: Linger options are 128 and 5 Server Info: Nonblocking IO is 4 Server Info: Async IO is 64 Server Info: close-on-exec flag is 1 Server Info: Pid owner is 59559 Server Info: Accepted socket Server Info: Status of socket 4 Server Info: Socket send timeout is 0 Server Info: Socket recv timeout is 0 Server Info: REUSEADDR flag is 4 Server Info: Linger options are 128 and 5 Server Info: Nonblocking IO is 4 Server Info: Async IO is 64 Server Info: close-on-exec flag is 0 Server Info: Pid owner is 59559 Fix: Detail in the accept(2) man page which socket properties are changed durning an accept call. How-To-Repeat: Compile and run the attached program and make a TCP connection to port 64000. Output like that of the previous section should show some properties changing across exec calls. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define SYSLOG_NAMES #include <syslog.h> #define WILDCARD_ADDRESS "*" void logger (int level, char *s, ...) { int i = 0; va_list args; fprintf(stderr, "Server "); while (prioritynames[i].c_name != NULL) { if (level == prioritynames[i].c_val) { fprintf(stderr, "%c", toupper(prioritynames[i].c_name[0])); fprintf(stderr, "%s: ", &(prioritynames[i].c_name[1])); break; } i++; } va_start(args, s); vfprintf(stderr, s, args); va_end(args); return; } void set_socket_options(int socket) { int enable = 1, val; struct timeval timeout; struct linger linger_opts; bzero (&timeout, sizeof(timeout)); timeout.tv_sec = 5; if (setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout))) { logger(LOG_CRIT, "Could not set send timeout option\n"); exit(EXIT_FAILURE); } if (setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout))) { logger(LOG_CRIT, "Could not set recv timeout option\n"); exit(EXIT_FAILURE); } if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable))) { logger (LOG_CRIT, "Could not set reuse addr flag\n"); exit (EXIT_FAILURE); } linger_opts.l_onoff = 1; linger_opts.l_linger = 5; if (setsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts, sizeof(linger_opts))) { logger (LOG_CRIT, "Could not set linger options\n"); exit(EXIT_FAILURE); } val = fcntl(socket, F_GETFL); val |= O_NONBLOCK; if (fcntl (socket, F_SETFL, val) == -1) { logger (LOG_CRIT, "Could not set nonblocking I/O because %s\n", strerror(errno)); exit (EXIT_FAILURE); } if (fcntl (socket, F_SETFD, FD_CLOEXEC) == -1) { logger (LOG_CRIT, "Could not set close on exec flag because %s\n", strerror(errno)); exit (EXIT_FAILURE); } if (fcntl (socket, F_SETOWN, getpid()) == -1) { logger (LOG_CRIT, "Could not set owner because %s\n", strerror(errno)); } val = fcntl(socket, F_GETFL); val |= O_ASYNC; if (fcntl (socket, F_SETFL, val) == -1) { logger (LOG_CRIT, "Could not set ASYNC because %s\n", strerror(errno)); exit (EXIT_FAILURE); } return; } void print_socket_options(int socket) { int val, enable; struct timeval timeout; struct linger linger_opts; logger (LOG_INFO, "Status of socket %d\n", socket); val = sizeof(timeout); if (getsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, &val)) { logger(LOG_CRIT, "Could not get send timeout option\n"); exit(EXIT_FAILURE); } logger (LOG_INFO, "Socket send timeout is %d\n", timeout.tv_sec); val = sizeof(timeout); if (getsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, &val)) { logger(LOG_CRIT, "Could not get recv timeout option\n"); exit(EXIT_FAILURE); } logger (LOG_INFO, "Socket recv timeout is %d\n", timeout.tv_sec); val = sizeof(enable); if (getsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &enable, &val)) { logger (LOG_CRIT, "Could not get reuse addr flag\n"); exit (EXIT_FAILURE); } logger (LOG_INFO, "REUSEADDR flag is %d\n", enable); val = sizeof(linger_opts); if (getsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts, &val)) { logger (LOG_CRIT, "Could not set linger options\n"); exit(EXIT_FAILURE); } logger (LOG_INFO, "Linger options are %d and %d\n", linger_opts.l_onoff, linger_opts.l_linger); val = fcntl(socket, F_GETFL); logger (LOG_INFO, "Nonblocking IO is %d\n", val & O_NONBLOCK); logger (LOG_INFO, "Async IO is %d\n", val & O_ASYNC); val = fcntl (socket, F_GETFD); logger (LOG_INFO, "close-on-exec flag is %d\n", val); val = fcntl (socket, F_GETOWN); logger (LOG_INFO, "Pid owner is %d\n", val); return; } int opensocket(char *address, int port, int timeout_sec, int linger_sec) { int s, enable; struct sockaddr_in name; if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { logger(LOG_CRIT, "Could not open socket: %s\n", strerror(errno)); return s; } bzero (&name, sizeof(name)); set_socket_options(s); name.sin_family = AF_INET; if (strcmp(address, WILDCARD_ADDRESS) == 0) { name.sin_addr.s_addr = htonl(INADDR_ANY); } else { if ((name.sin_addr.s_addr = inet_addr(address)) == INADDR_NONE) { logger(LOG_CRIT, "Inet_addr failed: %s\n", strerror(errno)); exit (EXIT_FAILURE); } } name.sin_port = htons(port); if (bind(s, (struct sockaddr *)&name, sizeof(name)) == -1) { logger(LOG_CRIT, "Could not bind socket: %s\n", strerror(errno)); close(s); return -1; } if (listen(s, 4) == -1) { logger(LOG_CRIT, "Could not listen on socket: %s\n", strerror(errno)); close(s); return -1; } return s; } int main (void) { int s, ns, len; struct sockaddr addr; s = opensocket("*", 64000, 3, 5); print_socket_options(s); do { len = sizeof (addr); ns = accept(s, &addr, &len); } while ((ns == -1) && (errno == EWOULDBLOCK)); logger (LOG_INFO, "\nAfter accept\n"); logger (LOG_INFO, "Orignal socket\n"); print_socket_options(s); logger (LOG_INFO, "\nAccepted socket\n"); print_socket_options(ns); close (s); close(ns); }