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;
+}
{"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'},
{"useragent", 1, 0, 0x03},
{"csd-user", 1, 0, 0x04},
{"disable-ipv6", 0, 0, 0x05},
+ {"no-proxy", 0, 0, 0x06},
{NULL, 0, 0, 0},
};
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");
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;
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;
.I PASS
]
[
+.B -P,--proxy
+.I PROXY
+]
+[
+.B --no-proxy
+]
+[
.B --key-password-from-fsid
]
[
.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
char sid_tokencode[9];
char sid_nexttokencode[9];
+ char *proxy;
+ int proxy_port;
+
const char *localname;
char *hostname;
int port;
/* 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);
<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>
<hr>
<address>David Woodhouse <<A HREF="mailto:dwmw2@infradead.org">dwmw2@infradead.org</A>></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>
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));
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)
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);