+ char *name;
+ int level;
+ int option;
+ int value;
+ int opttype;
+} socket_options[] = {
+ {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
+ {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
+ {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
+#ifdef TCP_NODELAY
+ {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
+#endif
+#ifdef IPTOS_LOWDELAY
+ {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
+#endif
+#ifdef IPTOS_THROUGHPUT
+ {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
+#endif
+#ifdef SO_SNDBUF
+ {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
+#endif
+#ifdef SO_RCVBUF
+ {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
+#endif
+#ifdef SO_SNDLOWAT
+ {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_RCVLOWAT
+ {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_SNDTIMEO
+ {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
+#endif
+#ifdef SO_RCVTIMEO
+ {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
+#endif
+ {NULL,0,0,0,0}};
+
+
+
+/****************************************************************************
+set user socket options
+****************************************************************************/
+void set_socket_options(int fd, char *options)
+{
+ char *tok;
+ if (!options || !*options) return;
+
+ options = strdup(options);
+
+ if (!options) out_of_memory("set_socket_options");
+
+ for (tok=strtok(options, " \t,"); tok; tok=strtok(NULL," \t,")) {
+ int ret=0,i;
+ int value = 1;
+ char *p;
+ int got_value = 0;
+
+ if ((p = strchr(tok,'='))) {
+ *p = 0;
+ value = atoi(p+1);
+ got_value = 1;
+ }
+
+ for (i=0;socket_options[i].name;i++)
+ if (strcmp(socket_options[i].name,tok)==0)
+ break;
+
+ if (!socket_options[i].name) {
+ rprintf(FERROR,"Unknown socket option %s\n",tok);
+ continue;
+ }
+
+ switch (socket_options[i].opttype) {
+ case OPT_BOOL:
+ case OPT_INT:
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,(char *)&value,sizeof(int));
+ break;
+
+ case OPT_ON:
+ if (got_value)
+ rprintf(FERROR,"syntax error - %s does not take a value\n",tok);
+
+ {
+ int on = socket_options[i].value;
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,(char *)&on,sizeof(int));
+ }
+ break;
+ }
+
+ if (ret != 0)
+ rprintf(FERROR, "failed to set socket option %s: %s\n", tok,
+ strerror(errno));
+ }
+
+ free(options);
+}
+
+/****************************************************************************
+become a daemon, discarding the controlling terminal
+****************************************************************************/
+void become_daemon(void)
+{
+ int i;
+
+ if (fork()) {
+ _exit(0);
+ }
+
+ /* detach from the terminal */
+#ifdef HAVE_SETSID
+ setsid();
+#else
+#ifdef TIOCNOTTY
+ i = open("/dev/tty", O_RDWR);
+ if (i >= 0) {
+ ioctl(i, (int) TIOCNOTTY, (char *)0);
+ close(i);
+ }
+#endif /* TIOCNOTTY */
+#endif
+ /* make sure that stdin, stdout an stderr don't stuff things
+ up (library functions, for example) */
+ for (i=0;i<3;i++) {
+ close(i);
+ open("/dev/null", O_RDWR);
+ }
+}
+
+/**
+ * Return the IP addr of the client as a string
+ **/
+char *client_addr(int fd)
+{
+ struct sockaddr_storage ss;
+ int length = sizeof(ss);
+ static char addr_buf[100];
+ static int initialised;
+
+ if (initialised) return addr_buf;
+
+ initialised = 1;
+
+ if (getpeername(fd, (struct sockaddr *)&ss, &length)) {
+ exit_cleanup(RERR_SOCKETIO);
+ }
+
+ getnameinfo((struct sockaddr *)&ss, length,
+ addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
+ return addr_buf;
+}
+
+
+static sa_family_t get_sockaddr_family(const struct sockaddr_storage *ss)
+{
+ return ((struct sockaddr *) ss)->sa_family;
+}
+
+
+/**
+ * Return the DNS name of the client
+ **/
+char *client_name(int fd)
+{
+ struct sockaddr_storage ss;
+ int length = sizeof(ss);
+ static char name_buf[100];
+ static char port_buf[100];
+ char *def = "UNKNOWN";
+ static int initialised;
+ struct addrinfo hints, *res, *res0;
+ int error;
+
+ if (initialised) return name_buf;
+
+ initialised = 1;
+
+ strcpy(name_buf,def);
+
+ if (getpeername(fd, (struct sockaddr *)&ss, &length)) {
+ /* FIXME: Can we really not continue? */
+ rprintf(FERROR, RSYNC_NAME ": getpeername on fd%d failed: %s\n",
+ fd, strerror(errno));
+ exit_cleanup(RERR_SOCKETIO);
+ }
+
+#ifdef INET6
+ if (get_sockaddr_family(&ss) == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&ss)->sin6_addr)) {
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in *sin;
+
+ memcpy(&sin6, &ss, sizeof(sin6));
+ sin = (struct sockaddr_in *)&ss;
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_family = AF_INET;
+ length = sizeof(struct sockaddr_in);
+#ifdef HAVE_SOCKADDR_LEN
+ sin->sin_len = length;
+#endif
+ sin->sin_port = sin6.sin6_port;
+ memcpy(&sin->sin_addr, &sin6.sin6_addr.s6_addr[12],
+ sizeof(sin->sin_addr));
+ }
+#endif
+
+ /* reverse lookup */
+ if (getnameinfo((struct sockaddr *)&ss, length,
+ name_buf, sizeof(name_buf), port_buf, sizeof(port_buf),
+ NI_NAMEREQD | NI_NUMERICSERV) != 0) {
+ strcpy(name_buf, def);
+ rprintf(FERROR, "reverse name lookup failed\n");
+ }
+
+ /* forward lookup */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(name_buf, port_buf, &hints, &res0);
+ if (error) {
+ strcpy(name_buf, def);
+ rprintf(FERROR,
+ RSYNC_NAME ": forward name lookup for %s failed: %s\n",
+ port_buf,
+ gai_strerror(error));
+ return name_buf;
+ }
+
+ /* XXX sin6_flowinfo and other fields */
+ for (res = res0; res; res = res->ai_next) {
+ if (res->ai_family != get_sockaddr_family(&ss))
+ continue;
+ if (res->ai_addrlen != length)
+ continue;
+ if (memcmp(res->ai_addr, &ss, res->ai_addrlen) == 0)
+ break;
+ }
+
+ /* TODO: Do a forward lookup as well to prevent spoofing */
+
+ if (res == NULL) {
+ strcpy(name_buf, def);
+ rprintf(FERROR, RSYNC_NAME ": "
+ "reverse name lookup mismatch on fd%d - spoofed address?\n",
+ fd);
+ }
+
+ freeaddrinfo(res0);
+ return name_buf;
+}
+
+
+/*******************************************************************
+this is like socketpair but uses tcp. It is used by the Samba
+regression test code
+The function guarantees that nobody else can attach to the socket,
+or if they do that this function fails and the socket gets closed
+returns 0 on success, -1 on failure
+the resulting file descriptors are symmetrical
+ ******************************************************************/
+static int socketpair_tcp(int fd[2])
+{
+ int listener;
+ struct sockaddr_in sock;
+ struct sockaddr_in sock2;
+ socklen_t socklen = sizeof(sock);
+ int connect_done = 0;
+
+ fd[0] = fd[1] = listener = -1;
+
+ memset(&sock, 0, sizeof(sock));
+
+ if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+ memset(&sock2, 0, sizeof(sock2));
+#ifdef HAVE_SOCK_SIN_LEN
+ sock2.sin_len = sizeof(sock2);
+#endif
+ sock2.sin_family = PF_INET;
+
+ bind(listener, (struct sockaddr *)&sock2, sizeof(sock2));
+
+ if (listen(listener, 1) != 0) goto failed;
+
+ if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed;
+
+ if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed;
+
+ set_nonblocking(fd[1]);
+
+ sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) == -1) {
+ if (errno != EINPROGRESS) goto failed;
+ } else {
+ connect_done = 1;
+ }
+
+ if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed;
+
+ close(listener);
+ if (connect_done == 0) {
+ if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) != 0
+ && errno != EISCONN) goto failed;
+ }
+
+ set_blocking (fd[1]);
+
+ /* all OK! */
+ return 0;
+
+ failed:
+ if (fd[0] != -1) close(fd[0]);
+ if (fd[1] != -1) close(fd[1]);
+ if (listener != -1) close(listener);