/* -*- c-file-style: "linux" -*-
- Copyright (C) 1998-2001 by Andrew Tridgell
+ Copyright (C) 1992-2001 by Andrew Tridgell <tridge@samba.org>
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
}
+
/* open a socket to a tcp remote host with the specified port
based on code from Warren
proxy support by Stephen Rothwell */
-int open_socket_out(char *host, int port, struct in_addr *address)
+static int open_socket_out (char *host,
+ int port,
+ struct in_addr *address)
{
int type = SOCK_STREAM;
struct sockaddr_in sock_out;
hp = gethostbyname(h);
if (!hp) {
- rprintf(FERROR,"unknown host: %s\n", h);
+ rprintf(FERROR,"unknown host: \"%s\"\n", h);
close(res);
return -1;
}
}
if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) {
- rprintf(FERROR,"failed to connect to %s - %s\n", h, strerror(errno));
+ rprintf (FERROR, RSYNC_NAME ": failed to connect to host %s: %s\n",
+ h, strerror(errno));
close(res);
return -1;
}
}
+/**
+ * Open an outgoing socket, but allow for it to be intercepted by
+ * $RSYNC_CONNECT_PROG, which will execute a program across a TCP
+ * socketpair rather than really opening a socket.
+ *
+ * We use this primarily in testing to detect TCP flow bugs, but not
+ * cause security problems by really opening remote connections.
+ *
+ * This is based on the Samba LIBSMB_PROG feature.
+ **/
+int open_socket_out_wrapped (char *host,
+ int port,
+ struct in_addr *address)
+{
+ char *prog;
+
+ if ((prog = getenv ("RSYNC_CONNECT_PROG")) != NULL)
+ return sock_exec (prog);
+ else
+ return open_socket_out (host, port, address);
+}
+
+
+
/****************************************************************************
open a socket of the specified type, port and address for incoming data
****************************************************************************/
static int open_socket_in(int type, int port, struct in_addr *address)
{
- struct hostent *hp;
struct sockaddr_in sock;
- char host_name[MAXHOSTNAMELEN];
int res;
int one=1;
- /* get my host name */
- if (gethostname(host_name, sizeof(host_name)) == -1) {
- rprintf(FERROR,"gethostname failed\n");
- return -1;
- }
-
- /* get host info */
- if ((hp = gethostbyname(host_name)) == 0) {
- rprintf(FERROR,"gethostbyname: Unknown host %s\n",host_name);
- return -1;
- }
-
memset((char *)&sock,0,sizeof(sock));
- memcpy((char *)&sock.sin_addr,(char *)hp->h_addr, hp->h_length);
sock.sin_port = htons(port);
- sock.sin_family = hp->h_addrtype;
+ sock.sin_family = AF_INET;
if (address) {
sock.sin_addr = *address;
} else {
sock.sin_addr.s_addr = INADDR_ANY;
}
- res = socket(hp->h_addrtype, type, 0);
+ res = socket(AF_INET, type, 0);
if (res == -1) {
- rprintf(FERROR,"socket failed: %s\n",
+ rprintf(FERROR, RSYNC_NAME ": socket failed: %s\n",
strerror(errno));
return -1;
}
{
struct sockaddr sa;
struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa);
- int length = sizeof(sa);
+ socklen_t length = sizeof(sa);
static char addr_buf[100];
static int initialised;
/* do a forward lookup as well to prevent spoofing */
hp = gethostbyname(name_buf);
if (!hp) {
- strcpy(name_buf,def);
- rprintf(FERROR,"reverse name lookup failed\n");
+ strcpy (name_buf,def);
+ rprintf (FERROR, "reverse name lookup for \"%s\" failed\n",
+ name_buf);
} else {
for (p=hp->h_addr_list;*p;p++) {
if (memcmp(*p, &sockin->sin_addr, hp->h_length) == 0) {
return name_buf;
}
-/*******************************************************************
-convert a string to an IP address. The string can be a name or
-dotted decimal number
- ******************************************************************/
+/**
+ Convert a string to an IP address. The string can be a name or
+ dotted decimal number.
+
+ Returns a pointer to a static in_addr struct -- if you call this
+ more than once then you should copy it.
+*/
struct in_addr *ip_address(const char *str)
{
static struct in_addr ret;
struct hostent *hp;
+ if (!str) {
+ rprintf (FERROR, "ip_address received NULL name\n");
+ return NULL;
+ }
+
/* try as an IP address */
if (inet_aton(str, &ret) != 0) {
return &ret;
/* otherwise assume it's a network name of some sort and use
gethostbyname */
- if ((hp = gethostbyname(str)) == 0) {
- rprintf(FERROR, "gethostbyname: Unknown host. %s\n",str);
+ if ((hp = gethostbyname (str)) == 0) {
+ rprintf(FERROR, "gethostbyname failed for \"%s\": unknown host?\n",str);
return NULL;
}
if (hp->h_addr == NULL) {
- rprintf(FERROR, "gethostbyname: host address is invalid for host %s\n",str);
+ rprintf(FERROR, "gethostbyname: host address is invalid for host \"%s\"\n",str);
+ return NULL;
+ }
+
+ if (hp->h_length > sizeof ret) {
+ rprintf(FERROR, "gethostbyname: host address for \"%s\" is too large\n",
+ str);
return NULL;
}
- if (hp->h_length > sizeof(ret)) {
- rprintf(FERROR, "gethostbyname: host address is too large\n");
+ if (hp->h_addrtype != AF_INET) {
+ rprintf (FERROR, "gethostname: host address for \"%s\" is not IPv4\n",
+ str);
return NULL;
}
- memcpy(&ret.s_addr, hp->h_addr, hp->h_length);
+ /* This is kind of difficult. The only field in ret is
+ s_addr, which is the IP address as a 32-bit int. On
+ UNICOS, s_addr is in fact a *bitfield* for reasons best
+ know to Cray. This means we can't memcpy in to it. On the
+ other hand, h_addr is a char*, so we can't just assign.
+
+ Since there's meant to be only one field inside the in_addr
+ structure we will try just copying over the top and see how
+ that goes. */
+ memcpy (&ret, hp->h_addr, hp->h_length);
+
+ return &ret;
+}
+
+
+
+/*******************************************************************
+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);
+ return -1;
+}
+
- return(&ret);
+/*******************************************************************
+run a program on a local tcp socket, this is used to launch smbd
+when regression testing
+the return value is a socket which is attached to a subprocess
+running "prog". stdin and stdout are attached. stderr is left
+attached to the original stderr
+ ******************************************************************/
+int sock_exec(const char *prog)
+{
+ int fd[2];
+ if (socketpair_tcp(fd) != 0) {
+ rprintf (FERROR, RSYNC_NAME
+ ": socketpair_tcp failed (%s)\n",
+ strerror(errno));
+ return -1;
+ }
+ if (fork() == 0) {
+ close(fd[0]);
+ close(0);
+ close(1);
+ dup(fd[1]);
+ dup(fd[1]);
+ if (verbose > 3)
+ fprintf (stderr,
+ RSYNC_NAME ": execute socket program \"%s\"\n",
+ prog);
+ exit (system (prog));
+ }
+ close (fd[1]);
+ return fd[0];
}
+
+
+