--- /dev/null 1970-01-01 00:00:00 +0000 +++ cvs-1.11.22/README.tunnel 2007-03-08 06:05:30 +0000 @@ -0,0 +1,111 @@ +A new feature is now available that CVS client will refer to the +environment variables PSERVER_PROXY and NO_PSERVER_PROXY. It has been +added by courtesy of TSUCHIYA Masatoshi, 28 January 2002. You may use +them to make CVS tunnel through firewalls without any difficulties. + +setenv PSERVER_PROXY proxy.my-company.com:8080 + +It specifies that CVS client should always use the HTTP proxy server +proxy.my-company.com on the port 8080 for the pserver access method. +And then, you can connect to the foreign server by simply typing like: + +cvs -d :pserver:user@cvs.dingnus.net:/cvsroot checkout dingnus + +If you have pserver access to some foreign hosts without tunneling, +specify NO_PSERVER_PROXY with those host names using semicolons to +separate them. You could say something like: + +setenv NO_PSERVER_PROXY 'cvs.my-company.com;cvs.nearby.com' + +Since semicolon is the special charcter for some shells, never forget +to quote names as shown above if it is needed. + +In addition, you can also use the lower case variable names +pserver_proxy and no_pserver_proxy for this feature. + + +This patch is based on the patch for CVS 1.10.5. You may still find it +in http://www.cvshome.org/dev/patches/http. Here is the document which +is extracted from there: + +[I haven't had a chance to look at this in any detail. + +The usual concerns apply (for example, whether having CVS deal with +"proxyport=8080" makes sense or whether that should be handled by some +non-CVS config file somewhere). But I should shut up until I've had a +chance to think about it more. + +At least one call for send() needs to be checking for errors and isn't. + +When I tried to look at the cited web page at Netscape, I got "file +not found". + +-kingdon] + +Date: Mon, 04 Jan 1999 16:45:20 +0000 +To: bug-cvs@gnu.org +From: Andy Piper +Subject: patch to tunnel CVS connections through firewalls. + +Hi there, + +Many open source projects provide access to their source code via +remote cvs (usually pserver). Unfortunately, many people are prevented +from using these facilities because they are behind firewalls which +prevent uncontrolled tcp access outside of their LAN. However, this is +also often the case for things like HTTPS and so Netscape have defined +a way of circumventing these restrictions by tunnelling connections +through HTTP. See +http://home.netscape.com/newsref/std/tunneling_ssl.html. This facility +can easily be added to cvs and the attached patch achieves this. To use the +proxy www on port 8080 to connect to the external machine there.somedomain +use: + +cvs -d +:pserver;proxy=www;proxyport=8080:me@there.somedomain:/somerepository + +The patch includes Jim Kingdon's port patch as many web servers +restrict the use of CONNECT to well known ports. A way of +circumventing this is to make a cvs server listen on one of these +ports - for instance 443 the HTTPS port. To access a server set up in this +way do: + +cvs -d +:pserver;proxy=www;proxyport=8080;port=443:me@there.somedomain:/somerepository + +The patch was inspired by ssh-tunnel.pl written by Urban Kaveus +. It adheres to HACKING so I hope it can be +incorporated in the main development tree. + +andy +ChangeLog and patch follow. The patch was inspired by ssh-tunnel.pl +written by Urban Kaveus . + +1998-12-26 Andy Piper + and Jim Kingdon + + * cvs.h: declare proxy variables CVSroot_proxy, + CVSroot_proxy_port, CVSroot_port. + * root.c: define proxy variables. + (parse_cvsroot): pick up semicolon expressions port=3D, proxy=3D, + proxyport=3D from CVSROOT. + * client.h (CVS_PROXY_PORT): default port for a proxy. + * client.c (connect_to_pserver): connect to a proxy before doing + anything else if one was specified. + (auth_server_port_number): return CVSroot_port if set. + +NEWS entry: + +* CVS can now tunnel connections through web servers (and hence a +firewall) that support the CONNECT command. The syntax augments= + the +connection syntax, for= + instance +:pserver#proxy=3Dwww#proxyport=3D8080#port=3D2401:, where proxy is the +name of the web server proxy, proxyport is the port to connect to on +the proxy and port is the remote port to connect to. proxyport +defaults to 80 and port defaults to 2401. + +[This is a tidied version of the patch from Johannes Stezenbach. The +contents haven't changed, just tidying various bits which were +mangled by the mailer -kingdon] --- cvs-1.11.22/src/client.c~ 2006-06-08 14:36:03 +0000 +++ cvs-1.11.22/src/client.c 2007-03-08 06:05:30 +0000 @@ -3623,10 +3623,16 @@ } return port; } - else if (portname && (s = getservbyname (portname, "tcp"))) - return ntohs (s->s_port); else - return defaultport; + { + /* If the user has explicitly specified a port, use that one.*/ + if (CVSroot_port != 0) + return CVSroot_port; + if (portname && (s = getservbyname (portname, "tcp"))) + return ntohs (s->s_port); + else + return defaultport; + } } @@ -3744,6 +3750,58 @@ +/* Read a line from socket SOCK. Result does not include the + terminating linefeed. This is only used by the authentication + protocol, which we call before we set up all the buffering stuff. + It is possible it should use the buffers too, which would be faster + (unlike the server, there isn't really a security issue in terms of + separating authentication from the rest of the code). + + Space for the result is malloc'd and should be freed by the caller. + + Returns number of bytes read. */ +static int +recv_line (sock, resultp) + int sock; + char **resultp; +{ + char *result; + size_t input_index = 0; + size_t result_size = 80; + + result = (char *) xmalloc (result_size); + + while (1) + { + char ch; + int n; + n = recv (sock, &ch, 1, 0); + if (n <= 0) + error (1, 0, "recv() from server %s: %s", current_parsed_root->hostname, + n == 0 ? "EOF" : SOCK_STRERROR (SOCK_ERRNO)); + + if (ch == '\012') + break; + + result[input_index++] = ch; + while (input_index + 1 >= result_size) + { + result_size *= 2; + result = (char *) xrealloc (result, result_size); + } + } + + if (resultp) + *resultp = result; + + /* Terminate it just for kicks, but we *can* deal with embedded NULs. */ + result[input_index] = '\0'; + + if (resultp == NULL) + free (result); + return input_index; +} + #if defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI) /* Connect to the authenticating server. @@ -3777,7 +3835,16 @@ error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO)); } port_number = get_cvs_port_number (root); - hostinfo = init_sockaddr (&client_sai, root->hostname, port_number); + /* if we have a proxy connection to that instead */ + if (CVSroot_proxy) + { + hostinfo = init_sockaddr (&client_sai, CVSroot_proxy, CVSroot_proxy_port); + } + else + { + hostinfo = init_sockaddr (&client_sai, root->hostname, port_number); + } + if (trace) { fprintf (stderr, " -> Connecting to %s(%s):%d\n", @@ -3787,9 +3854,51 @@ if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai)) < 0) error (1, 0, "connect to %s(%s):%d failed: %s", - root->hostname, + CVSroot_proxy ? CVSroot_proxy : root->hostname, inet_ntoa (client_sai.sin_addr), - port_number, SOCK_STRERROR (SOCK_ERRNO)); + CVSroot_proxy ? CVSroot_proxy_port : port_number, + SOCK_STRERROR (SOCK_ERRNO)); + + /* if we have proxy then connect to the proxy first */ + if (CVSroot_proxy) + { +#define CONNECT_STRING "CONNECT %s:%d HTTP/1.0\r\n\r\n" + /* Send a "CONNECT" command to proxy: */ + char* read_buf; + int codenum, count; + /* 4 characters for port covered by the length of %s & %d */ + char* write_buf; + + fprintf (stderr, "(HTTP tunneling through %s:%d)\n", + CVSroot_proxy, CVSroot_proxy_port); + + write_buf = xmalloc (strlen (CONNECT_STRING) + + strlen (current_parsed_root->hostname) + 1); + sprintf (write_buf, CONNECT_STRING, root->hostname, port_number); + send (sock, write_buf, strlen (write_buf), 0); + + /* Wait for HTTP status code, bail out if you don't get back a 2xx code.*/ + count = recv_line (sock, &read_buf); + sscanf (read_buf, "%s %d", write_buf, &codenum); + + if ((codenum / 100) != 2) + error (1, 0, "proxy server %s:%d does not support http tunnelling", + CVSroot_proxy, CVSroot_proxy_port); + free (read_buf); + free (write_buf); + + /* Skip through remaining part of MIME header, recv_line + consumes the trailing \n */ + while(recv_line (sock, &read_buf) > 0) + { + if (read_buf[0] == '\r' || read_buf[0] == 0) + { + free (read_buf); + break; + } + free (read_buf); + } + } make_bufs_from_fds (sock, sock, 0, &to_server, &from_server, 1); --- cvs-1.11.22/src/client.h~ 2005-08-02 20:46:57 +0000 +++ cvs-1.11.22/src/client.h 2007-03-08 06:05:30 +0000 @@ -83,6 +83,9 @@ # ifndef CVS_AUTH_PORT # define CVS_AUTH_PORT 2401 # endif /* CVS_AUTH_PORT */ +# ifndef CVS_PROXY_PORT +# define CVS_PROXY_PORT 80 +# endif /* CVS_PROXY_PORT */ # endif /* (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) */ # if HAVE_KERBEROS --- cvs-1.11.22/src/cvs.h~ 2006-05-16 00:59:21 +0000 +++ cvs-1.11.22/src/cvs.h 2007-03-08 06:05:30 +0000 @@ -390,6 +390,13 @@ from the CVSROOT environment variable or from a CVS/Root file. */ extern char *CVSroot_cmdline; +/* + * Options for non-standard ports etc + */ +extern int CVSroot_port; /* port on the remote cvs server */ +extern int CVSroot_proxy_port; /* the port on the proxy through which to tunnel */ +extern char* CVSroot_proxy; /* proxy to tunnel through */ + /* These variables keep track of all of the CVSROOT directories that have been seen by the client and the current one of those selected. */ extern List *root_directories; --- cvs-1.11.22/src/root.c~ 2005-12-07 19:29:19 +0000 +++ cvs-1.11.22/src/root.c 2007-03-08 06:05:30 +0000 @@ -269,6 +269,9 @@ /* FIXME - Deglobalize this. */ cvsroot_t *current_parsed_root = NULL; +char *CVSroot_proxy = NULL; /* the proxy to tunnel through */ +int CVSroot_port = 0; /* the remote port to use */ +int CVSroot_proxy_port = CVS_PROXY_PORT; /* the proxy port to tunnel through */ @@ -371,6 +374,7 @@ char *cvsroot_copy, *p, *q; /* temporary pointers for parsing */ #ifdef CLIENT_SUPPORT int check_hostname, no_port, no_password; + int ignore_pserver_proxy = 0; #endif /* CLIENT_SUPPORT */ assert (root_in); @@ -387,6 +391,7 @@ if (*cvsroot_copy == ':') { char *method = ++cvsroot_copy; + int have_semicolon; /* Access method specified, as in * "cvs -d :(gserver|kserver|pserver):[[user][:password]@]host[:[port]]/path", @@ -397,14 +402,20 @@ * rest of it. */ - if (! (p = strchr (method, ':'))) + p = strpbrk (method, ":;#"); + if (p == NULL) { error (0, 0, "No closing `:' on method in CVSROOT."); goto error_exit; } + have_semicolon = (*p == ';' || *p == '#'); *p = '\0'; cvsroot_copy = ++p; + /* When the access method includes semicolon, the environment variable + * pserver_proxy is ignored. */ + ignore_pserver_proxy = have_semicolon; + #ifdef CLIENT_SUPPORT /* Look for method options, for instance, proxy, proxyport. * We don't handle these, but we like to try and warn the user that @@ -445,6 +456,59 @@ error (0, 0, "Unknown method (`%s') in CVSROOT.", method); goto error_exit; } + + while (have_semicolon) + { + /* More elaborate implementation would allow multiple + semicolons, for example: + + :server;rsh=34;command=cvs-1.6: + + we will allow + :server;port=22;proxy=www-proxy;proxyport=8080: + + we will also allow # as well as ; as a separator to + avoid having to quote the root in a shell. + */ + /* FIXME: lots of error conditions should be better handled, + e.g. garbage after the number or no valid number. + + Would be nice to have testcases for some of these cases + including the error cases. */ + p = strpbrk (cvsroot_copy, ":;#"); + if (p == NULL) + error (1, 0, "[semi]colon missing in %s", root_in); + + /* pick up more options if we have them */ + have_semicolon = (*p == ';' || *p == '#'); + *p = '\0'; + + if (strncmp (cvsroot_copy, "port=", 5) == 0) + { + if (cvsroot_copy [5] == '\0') + error (1, 0, "no port specified in CVSROOT: %s", + cvsroot_copy); + CVSroot_port = atoi (cvsroot_copy + 5); + } + else if (strncmp (cvsroot_copy, "proxy=", 6) == 0) + { + CVSroot_proxy = xstrdup (cvsroot_copy + 6); + if (*CVSroot_proxy == '\0') + error (1, 0, "no proxy specified in CVSROOT: %s", + cvsroot_copy); + } + else if (strncmp (cvsroot_copy, "proxyport=", 10) == 0) + { + if (cvsroot_copy [10] == '\0') + error (1, 0, "no proxy port specified in CVSROOT: %s", + cvsroot_copy); + CVSroot_proxy_port = atoi (cvsroot_copy + 10); + } + else + error (1, 0, "invalid semicolon expression in CVSROOT: %s", + cvsroot_copy); + cvsroot_copy = ++p; + } } else { @@ -707,6 +771,38 @@ error (0, 0, "Missing directory in CVSROOT."); goto error_exit; } + + /* Process the environment variable pserver_proxy. */ + if ((newroot->method == pserver_method) && (!ignore_pserver_proxy)) { + char *x, *y, *z; + + if ((x = getenv("no_pserver_proxy")) || (x = getenv("NO_PSERVER_PROXY"))) { + while((y = strpbrk(x, ";"))){ + *y = '\0'; + if ((z = strstr(newroot->hostname, x)) && (z[strlen(x)] == '\0')) + goto processed_pserver_proxy; + x = y + 1; + } + if ((z = strstr(newroot->hostname, x)) && (z[strlen(x)] == '\0')) + goto processed_pserver_proxy; + } + + if ((x = getenv("pserver_proxy")) || (x = getenv("PSERVER_PROXY"))) { + for (y = x; *y; y++) { + if (*y == ':') { + *y = '\0'; + for (z = ++y; *z && isdigit(*z); z++); + if (*z == '\0') { + CVSroot_proxy = strdup(x); + CVSroot_proxy_port = atoi(y); + } + break; + } + } + } + processed_pserver_proxy: + ; + } /* Hooray! We finally parsed it! */ free (cvsroot_save);