Skip to content

Commit

Permalink
Add proxy support (based on Pál Dorogi's version)
Browse files Browse the repository at this point in the history
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Jan 1, 2010
1 parent ee22fdf commit 9c6d3f1
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 16 deletions.
91 changes: 91 additions & 0 deletions http.c
Expand Up @@ -708,3 +708,94 @@ char *openconnect_create_useragent(char *base)

return uagent;
}

static int proxy_gets(int fd, char *buf, size_t len)
{
int i = 0;
int ret;

if (len < 2)
return -EINVAL;

while ( (ret = read(fd, buf + i, 1)) == 1) {
if (buf[i] == '\n') {
buf[i] = 0;
if (i && buf[i-1] == '\r') {
buf[i-1] = 0;
i--;
}
return i;
}
i++;

if (i >= len - 1) {
buf[i] = 0;
return i;
}
}
if (ret < 0)
ret = -errno;

buf[i] = 0;
return i ?: ret;
}


int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock)
{
char buf[MAX_BUF_LEN];
int count, buflen, result;

sprintf(buf, "CONNECT %s:%d HTTP/1.1\r\n", vpninfo->hostname, vpninfo->port);
sprintf(buf + strlen(buf), "Host: %s\r\n", vpninfo->hostname);
sprintf(buf + strlen(buf), "User-Agent: %s\r\n", vpninfo->useragent);
sprintf(buf + strlen(buf), "Proxy-Connection: keep-alive\r\n");
sprintf(buf + strlen(buf), "Connection: keep-alive\r\n");
sprintf(buf + strlen(buf), "Accept-Encoding: identity\r\n");
sprintf(buf + strlen(buf), "\r\n");

vpninfo->progress(vpninfo, PRG_INFO, "Requesting proxy connection to %s:%d\n",
vpninfo->hostname, vpninfo->port);

buflen = strlen(buf);
for (count = 0; count < buflen; ) {
int i = write(ssl_sock, buf + count, buflen - count);
if (i < 0) {
i = -errno;
vpninfo->progress(vpninfo, PRG_ERR, "Sending proxy request failed: %s\n",
strerror(errno));
return i;
}
count += i;
}

if (proxy_gets(ssl_sock, buf, sizeof(buf)) < 0) {
vpninfo->progress(vpninfo, PRG_ERR, "Error fetching proxy response\n");
return -EIO;
}

if (strncmp(buf, "HTTP/1.", 7) || (buf[7] != '0' && buf[7] != '1') ||
buf[8] != ' ' || !(result = atoi(buf+9))) {
vpninfo->progress(vpninfo, PRG_ERR, "Failed to parse proxy response '%s'\n",
buf);
return -EINVAL;
}

if (result != 200) {
vpninfo->progress(vpninfo, PRG_ERR, "Proxy CONNECT request failed: %s\n",
buf);
return -EIO;
}

while ((count = proxy_gets(ssl_sock, buf, sizeof(buf)))) {
if (count < 0) {
vpninfo->progress(vpninfo, PRG_ERR, "Failed to read proxy response\n");
return -EIO;
}
vpninfo->progress(vpninfo, PRG_ERR,
"Unexpected continuation line after CONNECT response: '%s'\n",
buf);
}

return 0;
}
24 changes: 22 additions & 2 deletions main.c
Expand Up @@ -68,6 +68,7 @@ static struct option long_options[] = {
{"syslog", 0, 0, 'l'},
{"key-type", 1, 0, 'K'},
{"key-password", 1, 0, 'p'},
{"proxy", 1, 0, 'P'},
{"user", 1, 0, 'u'},
{"verbose", 0, 0, 'v'},
{"version", 0, 0, 'V'},
Expand All @@ -89,6 +90,7 @@ static struct option long_options[] = {
{"useragent", 1, 0, 0x03},
{"csd-user", 1, 0, 0x04},
{"disable-ipv6", 0, 0, 0x05},
{"no-proxy", 0, 0, 0x06},
{NULL, 0, 0, 0},
};

Expand All @@ -109,10 +111,12 @@ void usage(void)
printf(" -i, --interface=IFNAME Use IFNAME for tunnel interface\n");
printf(" -l, --syslog Use syslog for progress messages\n");
printf(" -U, --setuid=USER Drop privileges after connecting\n");
printf(" --csd-user=USER Drop privileges during CSD execution\n");
printf(" --csd-user=USER Drop privileges during CSD execution\n");
printf(" -m, --mtu=MTU Request MTU from server\n");
printf(" -p, --key-password=PASS Set key passphrase or TPM SRK PIN\n");
printf(" --key-password-from-fsid Key passphrase is fsid of file system\n");
printf(" -P, --proxy=URL Set proxy server\n");
printf(" --no-proxy Disable proxy\n");
printf(" -q, --quiet Less output\n");
printf(" -Q, --queue-len=LEN Set packet queue limit to LEN pkts\n");
printf(" -s, --script=SCRIPT Use vpnc-compatible config script\n");
Expand Down Expand Up @@ -202,7 +206,7 @@ int main(int argc, char **argv)
else
vpninfo->localname = "localhost";

while ((opt = getopt_long(argc, argv, "bC:c:Ddg:hi:k:K:lp:Q:qSs:U:u:Vvx:",
while ((opt = getopt_long(argc, argv, "bC:c:Ddg:hi:k:K:lpP:Q:qSs:U:u:Vvx:",
long_options, NULL))) {
if (opt < 0)
break;
Expand Down Expand Up @@ -298,6 +302,22 @@ int main(int argc, char **argv)
case 'p':
vpninfo->cert_password = optarg;
break;
case 'P': {
char *url = strdup(optarg);
char *scheme;
parse_url(url, &scheme, &vpninfo->proxy, &vpninfo->proxy_port, NULL);
if (scheme && strcmp(scheme, "http")) {
fprintf(stderr, "Non-http proxy not supported\n");
exit(1);
}
free(scheme);
free(url);
vpninfo->dtls_attempt_period = 0;
break;
}
case 0x06:
free(vpninfo->proxy);
vpninfo->proxy = NULL;
case 's':
vpninfo->vpnc_script = optarg;
break;
Expand Down
13 changes: 13 additions & 0 deletions openconnect.8
Expand Up @@ -62,6 +62,13 @@ openconnect \- Connect to Cisco AnyConnect VPN
.I PASS
]
[
.B -P,--proxy
.I PROXY
]
[
.B --no-proxy
]
[
.B --key-password-from-fsid
]
[
Expand Down Expand Up @@ -221,6 +228,12 @@ from server
.B -p,--key-password=PASS
Provide passphrase for certificate file, or SRK (System Root Key) PIN for TPM
.TP
.B -P,--proxy=PROXY
Use HTTP proxy for connection
.TP
.B --no-proxy
Disable use of HTTP proxy
.TP
.B --key-password-from-fsid
Passphrase for certificate file is automatically generated from the fsid of
the file system on which it is stored
Expand Down
5 changes: 5 additions & 0 deletions openconnect.h
Expand Up @@ -143,6 +143,9 @@ struct openconnect_info {
char sid_tokencode[9];
char sid_nexttokencode[9];

char *proxy;
int proxy_port;

const char *localname;
char *hostname;
int port;
Expand Down Expand Up @@ -320,6 +323,8 @@ int parse_xml_response(struct openconnect_info *vpninfo, char *response,
/* http.c */
int openconnect_obtain_cookie(struct openconnect_info *vpninfo);
char *openconnect_create_useragent(char *base);
int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock);
int parse_url(char *url, char **res_proto, char **res_host, int *res_port, char **res_path);

/* ssl_ui.c */
int set_openssl_ui(void);
Expand Down
3 changes: 2 additions & 1 deletion openconnect.html
Expand Up @@ -173,6 +173,7 @@ <H2>Release Notes / Changelog</H2>
<UL>
<LI><B>OpenConnect HEAD</B><BR>
<UL>
<LI>Support connection through HTTP proxy.</LI>
<LI>Handle HTTP redirection with port numbers.</LI>
<LI>Handle HTTP redirection with IPv6 literal addresses.</LI>
</UL><BR>
Expand Down Expand Up @@ -386,6 +387,6 @@ <H3>FreeBSD</H3>
<hr>
<address>David Woodhouse &lt;<A HREF="mailto:dwmw2@infradead.org">dwmw2@infradead.org</A>&gt;</address>
<!-- hhmts start -->
Last modified: Wed Dec 23 22:32:16 GMT 2009
Last modified: Fri Jan 1 22:08:53 GMT 2010
<!-- hhmts end -->
</body> </html>
52 changes: 39 additions & 13 deletions ssl.c
Expand Up @@ -484,16 +484,17 @@ int openconnect_open_https(struct openconnect_info *vpninfo)
ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
if (ssl_sock < 0) {
reconn_err:
vpninfo->progress(vpninfo, PRG_ERR, "Failed to reconnect to host %s\n", vpninfo->hostname);
vpninfo->progress(vpninfo, PRG_ERR, "Failed to reconnect to %s %s\n",
vpninfo->proxy?"proxy":"host",
vpninfo->proxy?:vpninfo->hostname);
return -EINVAL;
}
if (connect(ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen))
goto reconn_err;



} else {
struct addrinfo hints, *result, *rp;
char *hostname;
char port[6];

memset(&hints, 0, sizeof(struct addrinfo));
Expand All @@ -505,20 +506,37 @@ int openconnect_open_https(struct openconnect_info *vpninfo)
hints.ai_addr = NULL;
hints.ai_next = NULL;

/* We do this because it's easier than passing NULL as the
port and then having to fill it in differently for IPv4
and IPv6 destinations later. */
snprintf(port, 5, "%d", vpninfo->port);
err = getaddrinfo(vpninfo->hostname, port, &hints, &result);
/* The 'port' variable is a string because it's easier
this way than if we pass NULL to getaddrinfo() and
then try to fill in the numeric value into
different types of returned sockaddr_in{6,}. */
if (vpninfo->proxy) {
hostname = vpninfo->proxy;
snprintf(port, 5, "%d", vpninfo->proxy_port);
} else {
hostname = vpninfo->hostname;
snprintf(port, 5, "%d", vpninfo->port);
}

err = getaddrinfo(hostname, port, &hints, &result);
if (err) {
vpninfo->progress(vpninfo, PRG_ERR, "getaddrinfo failed: %s\n", gai_strerror(err));
vpninfo->progress(vpninfo, PRG_ERR, "getaddrinfo failed: %s\n",
gai_strerror(err));
return -EINVAL;
}

vpninfo->progress(vpninfo, PRG_INFO,
"Attempting to connect to %s\n", vpninfo->hostname);

for (rp = result; rp ; rp = rp->ai_next) {
char host[80];

if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
sizeof(host), NULL, 0, NI_NUMERICHOST))
vpninfo->progress(vpninfo, PRG_INFO,
"Attempting to connect to %s%s%s:%s\n",
rp->ai_family == AF_INET6?"[":"",
host,
rp->ai_family == AF_INET6?"]":"",
port);

ssl_sock = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (ssl_sock < 0)
Expand All @@ -542,12 +560,20 @@ int openconnect_open_https(struct openconnect_info *vpninfo)
freeaddrinfo(result);

if (ssl_sock < 0) {
vpninfo->progress(vpninfo, PRG_ERR, "Failed to connect to host %s\n", vpninfo->hostname);
vpninfo->progress(vpninfo, PRG_ERR, "Failed to connect to host %s\n", hostname);
return -EINVAL;
}
}
fcntl(ssl_sock, F_SETFD, FD_CLOEXEC);

if (vpninfo->proxy) {
err = process_http_proxy(vpninfo, ssl_sock);
if (err) {
close(ssl_sock);
return err;
}
}

ssl3_method = TLSv1_client_method();
if (!vpninfo->https_ctx) {
vpninfo->https_ctx = SSL_CTX_new(ssl3_method);
Expand Down

0 comments on commit 9c6d3f1

Please sign in to comment.