+/*
+ * Prepare a socket for sending RPC requests
+ *
+ * Returns a bound datagram socket file descriptor, or -1 if
+ * an error occurs.
+ */
+static int
+smn_create_socket(const char *srcaddr, const uint16_t srcport)
+{
+ struct sockaddr_storage address;
+ struct sockaddr *local_addr = (struct sockaddr *)&address;
+ int sock, retry_cnt = 0;
+
+retry:
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ xlog(L_ERROR, "Failed to create RPC socket: %m");
+ return -1;
+ }
+ fcntl(sock, F_SETFL, O_NONBLOCK);
+
+ memset(&address, 0, sizeof(address));
+ local_addr->sa_family = AF_INET; /* Default to IPv4 */
+
+ /* Bind source IP if provided on command line */
+ if (srcaddr) {
+ struct addrinfo *ai = smn_lookup(srcaddr);
+ if (!ai) {
+ xlog(L_ERROR,
+ "Not a valid hostname or address: \"%s\"",
+ srcaddr);
+ (void)close(sock);
+ return -1;
+ }
+
+ /* We know it's IPv4 at this point */
+ memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
+
+ freeaddrinfo(ai);
+ }
+
+ /* Use source port if provided on the command line,
+ * otherwise use bindresvport */
+ if (srcport) {
+ nfs_set_port(local_addr, srcport);
+ if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
+ xlog(L_ERROR, "Failed to bind RPC socket: %m");
+ (void)close(sock);
+ return -1;
+ }
+ } else {
+ struct servent *se;
+ struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
+ (void) bindresvport(sock, sin);
+ /* try to avoid known ports */
+ se = getservbyport(sin->sin_port, "udp");
+ if (se && retry_cnt < 100) {
+ retry_cnt++;
+ close(sock);
+ goto retry;
+ }
+ }
+
+ return sock;
+}
+