+/*
+ * Attempt to connect a socket, but time out after "timeout" seconds.
+ *
+ * On error return, caller closes the socket.
+ */
+static int connect_to(int fd, struct sockaddr *addr,
+ socklen_t addrlen, int timeout)
+{
+ int ret, saved;
+ fd_set rset, wset;
+ struct timeval tv = {
+ .tv_sec = timeout,
+ };
+
+ saved = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, saved | O_NONBLOCK);
+
+ ret = connect(fd, addr, addrlen);
+ if (ret < 0 && errno != EINPROGRESS)
+ return -1;
+ if (ret == 0)
+ goto out;
+
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+ wset = rset;
+ ret = select(fd + 1, &rset, &wset, NULL, &tv);
+ if (ret == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
+ int error;
+ socklen_t len = sizeof(error);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+ return -1;
+ if (error) {
+ errno = error;
+ return -1;
+ }
+ } else
+ return -1;
+
+out:
+ fcntl(fd, F_SETFL, saved);
+ return 0;
+}
+
+/*
+ * Create a socket that is locally bound to a reserved or non-reserved port.
+ *
+ * The caller should check rpc_createerr to determine the cause of any error.
+ */
+static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot,
+ unsigned int timeout, int resvp, int conn)
+{
+ int so, cc, type;
+ struct sockaddr_in laddr;
+ socklen_t namelen = sizeof(laddr);
+
+ type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
+ if ((so = socket (AF_INET, type, p_prot)) < 0)
+ goto err_socket;
+
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = 0;
+ laddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (resvp) {
+ if (bindresvport(so, &laddr) < 0)
+ goto err_bindresvport;
+ } else {
+ cc = bind(so, (struct sockaddr *)&laddr, namelen);
+ if (cc < 0)
+ goto err_bind;
+ }
+ if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
+ cc = connect_to(so, (struct sockaddr *)saddr, namelen,
+ timeout);
+ if (cc < 0)
+ goto err_connect;
+ }
+ return so;
+
+err_socket:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to create %s socket: errno %d (%s)\n"),
+ progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
+ errno, strerror(errno));
+ }
+ return RPC_ANYSOCK;
+
+err_bindresvport:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to bindresvport %s socket: errno %d"
+ " (%s)\n"),
+ progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
+ errno, strerror(errno));
+ }
+ close(so);
+ return RPC_ANYSOCK;
+
+err_bind:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to bind to %s socket: errno %d (%s)\n"),
+ progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
+ errno, strerror(errno));
+ }
+ close(so);
+ return RPC_ANYSOCK;
+
+err_connect:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to connect to %s:%d, errno %d (%s)\n"),
+ progname, inet_ntoa(saddr->sin_addr),
+ ntohs(saddr->sin_port), errno, strerror(errno));
+ }
+ close(so);
+ return RPC_ANYSOCK;
+}
+