int stream_server( int family, in_port_t *portp, size_t sendsize, size_t recvsize, int priv) { int server_socket, retries; socklen_t_equiv len; #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR) const int on = 1; int r; #endif sockaddr_union server; int save_errno; int *portrange; socklen_t_equiv socklen; int socket_family; *portp = USHRT_MAX; /* in case we error exit */ if (family == -1) { socket_family = AF_NATIVE; } else { socket_family = family; } g_debug("stream_server opening socket with family %d (requested family was %d)", socket_family, family); server_socket = socket(socket_family, SOCK_STREAM, 0); #ifdef WORKING_IPV6 /* if that address family actually isn't supported, just try AF_INET */ if (server_socket == -1 && errno == EAFNOSUPPORT) { g_debug("stream_server retrying socket with AF_INET"); socket_family = AF_INET; server_socket = socket(AF_INET, SOCK_STREAM, 0); } #endif if (server_socket == -1) { save_errno = errno; g_debug(_("stream_server: socket() failed: %s"), strerror(save_errno)); errno = save_errno; return -1; } if(server_socket < 0 || server_socket >= (int)FD_SETSIZE) { aclose(server_socket); errno = EMFILE; /* out of range */ save_errno = errno; g_debug(_("stream_server: socket out of range: %d"), server_socket); errno = save_errno; return -1; } SU_INIT(&server, socket_family); SU_SET_INADDR_ANY(&server); #ifdef USE_REUSEADDR r = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&on, (socklen_t_equiv)sizeof(on)); if (r < 0) { g_debug(_("stream_server: setsockopt(SO_REUSEADDR) failed: %s"), strerror(errno)); } #endif try_socksize(server_socket, SO_SNDBUF, sendsize); try_socksize(server_socket, SO_RCVBUF, recvsize); /* * If a port range was specified, we try to get a port in that * range first. Next, we try to get a reserved port. If that * fails, we just go for any port. * * In all cases, not to use port that's assigned to other services. * * It is up to the caller to make sure we have the proper permissions * to get the desired port, and to make sure we return a port that * is within the range it requires. */ for (retries = 0; ; retries++) { if (priv) { portrange = getconf_intrange(CNF_RESERVED_TCP_PORT); } else { portrange = getconf_intrange(CNF_UNRESERVED_TCP_PORT); } if (portrange[0] != 0 && portrange[1] != 0) { if (bind_portrange(server_socket, &server, (in_port_t)portrange[0], (in_port_t)portrange[1], "tcp") == 0) goto out; g_debug(_("stream_server: Could not bind to port in range: %d - %d."), portrange[0], portrange[1]); } else { socklen = SS_LEN(&server); if (bind(server_socket, (struct sockaddr *)&server, socklen) == 0) goto out; g_debug(_("stream_server: Could not bind to any port: %s"), strerror(errno)); } if (retries >= BIND_CYCLE_RETRIES) break; g_debug(_("stream_server: Retrying entire range after 10 second delay.")); sleep(15); } save_errno = errno; g_debug(_("stream_server: bind(in6addr_any) failed: %s"), strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; out: listen(server_socket, 1); /* find out what port was actually used */ len = sizeof(server); if(getsockname(server_socket, (struct sockaddr *)&server, &len) == -1) { save_errno = errno; g_debug(_("stream_server: getsockname() failed: %s"), strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; } #ifdef SO_KEEPALIVE r = setsockopt(server_socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, (socklen_t_equiv)sizeof(on)); if(r == -1) { save_errno = errno; g_debug(_("stream_server: setsockopt(SO_KEEPALIVE) failed: %s"), strerror(save_errno)); aclose(server_socket); errno = save_errno; return -1; } #endif *portp = SU_GET_PORT(&server); g_debug(_("stream_server: waiting for connection: %s"), str_sockaddr(&server)); return server_socket; }
int dgram_bind( dgram_t * dgram, sa_family_t family, in_port_t * portp) { int s, retries; socklen_t_equiv len; sockaddr_union name; int save_errno; int *portrange; int sndbufsize = MAX_DGRAM; portrange = getconf_intrange(CNF_RESERVED_UDP_PORT); *portp = (in_port_t)0; g_debug("dgram_bind: setting up a socket with family %d", family); if((s = socket(family, SOCK_DGRAM, 0)) == -1) { save_errno = errno; dbprintf(_("dgram_bind: socket() failed: %s\n"), strerror(save_errno)); errno = save_errno; return -1; } if(s < 0 || s >= (int)FD_SETSIZE) { dbprintf(_("dgram_bind: socket out of range: %d\n"), s); aclose(s); errno = EMFILE; /* out of range */ return -1; } /* try setting the buffer size (= maximum allowable UDP packet size) */ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *) &sndbufsize, sizeof(sndbufsize)) < 0) { dbprintf("dgram_bind: could not set udp send buffer to %d: %s (ignored)\n", sndbufsize, strerror(errno)); } SU_INIT(&name, family); SU_SET_INADDR_ANY(&name); /* * If a port range was specified, we try to get a port in that * range first. Next, we try to get a reserved port. If that * fails, we just go for any port. * * In all cases, not to use port that's assigned to other services. * * It is up to the caller to make sure we have the proper permissions * to get the desired port, and to make sure we return a port that * is within the range it requires. */ for (retries = 0; ; retries++) { if (bind_portrange(s, &name, portrange[0], portrange[1], "udp") == 0) goto out; dbprintf(_("dgram_bind: Could not bind to port in range: %d - %d.\n"), portrange[0], portrange[1]); if (retries >= BIND_CYCLE_RETRIES) { dbprintf(_("dgram_bind: Giving up...\n")); break; } dbprintf(_("dgram_bind: Retrying entire range after 10 second delay.\n")); sleep(15); } save_errno = errno; dbprintf(_("dgram_bind: bind(in6addr_any) failed: %s\n"), strerror(save_errno)); aclose(s); errno = save_errno; return -1; out: /* find out what name was actually used */ len = (socklen_t_equiv)sizeof(name); if(getsockname(s, (struct sockaddr *)&name, &len) == -1) { save_errno = errno; dbprintf(_("dgram_bind: getsockname() failed: %s\n"), strerror(save_errno)); errno = save_errno; aclose(s); return -1; } *portp = SU_GET_PORT(&name); dgram->socket = s; dbprintf(_("dgram_bind: socket %d bound to %s\n"), dgram->socket, str_sockaddr(&name)); return 0; }