2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008-2010 Intel Corporation.
5 * Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
7 * Author: David Woodhouse <dwmw2@infradead.org>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * version 2.1, as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to:
21 * Free Software Foundation, Inc.
22 * 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301 USA
35 #include <sys/types.h>
37 #include <openssl/ssl.h>
38 #include <openssl/err.h>
39 #include <openssl/engine.h>
41 #include "openconnect.h"
43 #define MAX_BUF_LEN 131072
45 * We didn't really want to have to do this for ourselves -- one might have
46 * thought that it would be available in a library somewhere. But neither
47 * cURL nor Neon have reliable cross-platform ways of either using a cert
48 * from the TPM, or just reading from / writing to a transport which is
49 * provided by their caller.
52 int http_add_cookie(struct openconnect_info *vpninfo, const char *option, const char *value)
54 struct vpn_option *new, **this;
57 new = malloc(sizeof(*new));
59 vpninfo->progress(vpninfo, PRG_ERR, "No memory for allocating cookies\n");
63 new->option = strdup(option);
64 new->value = strdup(value);
65 if (!new->option || !new->value) {
72 /* Kill cookie; don't replace it */
75 for (this = &vpninfo->cookies; *this; this = &(*this)->next) {
76 if (!strcmp(option, (*this)->option)) {
77 /* Replace existing cookie */
79 new->next = (*this)->next;
83 free((*this)->option);
97 static int process_http_response(struct openconnect_info *vpninfo, int *result,
98 int (*header_cb)(struct openconnect_info *, char *, char *),
99 char *body, int buf_len)
101 char buf[MAX_BUF_LEN];
104 int http10 = 0, closeconn = 0;
107 if (openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)) < 0) {
108 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching HTTPS response\n");
113 if (!strncmp(buf, "HTTP/1.0 ", 9)) {
118 if ((!http10 && strncmp(buf, "HTTP/1.1 ", 9)) || !(*result = atoi(buf+9))) {
119 vpninfo->progress(vpninfo, PRG_ERR, "Failed to parse HTTP response '%s'\n", buf);
123 vpninfo->progress(vpninfo, PRG_TRACE, "Got HTTP response: %s\n", buf);
126 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
129 vpninfo->progress(vpninfo, PRG_TRACE, "%s\n", buf);
132 vpninfo->progress(vpninfo, PRG_ERR, "Error processing HTTP response\n");
135 colon = strchr(buf, ':');
137 vpninfo->progress(vpninfo, PRG_ERR, "Ignoring unknown HTTP response line '%s'\n", buf);
144 if (!strcmp(buf, "Connection") && !strcmp(colon, "Close"))
147 if (!strcmp(buf, "Location")) {
148 vpninfo->redirect_url = strdup(colon);
149 if (!vpninfo->redirect_url)
152 if (!strcmp(buf, "Content-Length")) {
153 bodylen = atoi(colon);
154 if (bodylen < 0 || bodylen > buf_len) {
155 vpninfo->progress(vpninfo, PRG_ERR, "Response body too large for buffer (%d > %d)\n",
160 if (!strcmp(buf, "Set-Cookie")) {
161 char *semicolon = strchr(colon, ';');
162 char *equals = strchr(colon, '=');
169 vpninfo->progress(vpninfo, PRG_ERR, "Invalid cookie offered: %s\n", buf);
174 ret = http_add_cookie(vpninfo, colon, equals);
178 if (!strcmp(buf, "Transfer-Encoding")) {
179 if (!strcmp(colon, "chunked"))
182 vpninfo->progress(vpninfo, PRG_ERR, "Unknown Transfer-Encoding: %s\n", colon);
186 if (header_cb && !strncmp(buf, "X-", 2))
187 header_cb(vpninfo, buf, colon);
190 /* Handle 'HTTP/1.1 100 Continue'. Not that we should ever see it */
194 /* Now the body, if there is one */
199 /* HTTP 1.0 response. Just eat all we can. */
201 i = SSL_read(vpninfo->https_ssl, body + done, bodylen - done);
207 /* If we were given Content-Length, it's nice and easy... */
209 while (done < bodylen) {
210 i = SSL_read(vpninfo->https_ssl, body + done, bodylen - done);
212 vpninfo->progress(vpninfo, PRG_ERR, "Error reading HTTP response body\n");
220 /* ... else, chunked */
221 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
222 int chunklen, lastchunk = 0;
225 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching chunk header\n");
228 chunklen = strtol(buf, NULL, 16);
233 if (chunklen + done > buf_len) {
234 vpninfo->progress(vpninfo, PRG_ERR, "Response body too large for buffer (%d > %d)\n",
235 chunklen + done, buf_len);
239 i = SSL_read(vpninfo->https_ssl, body + done, chunklen);
241 vpninfo->progress(vpninfo, PRG_ERR, "Error reading HTTP response body\n");
248 if ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
250 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching HTTP response body\n");
252 vpninfo->progress(vpninfo, PRG_ERR, "Error in chunked decoding. Expected '', got: '%s'",
262 if (closeconn && vpninfo->https_ssl) {
263 SSL_free(vpninfo->https_ssl);
264 vpninfo->https_ssl = NULL;
265 close(vpninfo->ssl_fd);
266 vpninfo->ssl_fd = -1;
272 static int fetch_config(struct openconnect_info *vpninfo, char *fu, char *bu,
275 struct vpn_option *opt;
276 char buf[MAX_BUF_LEN];
278 unsigned char local_sha1_bin[SHA_DIGEST_LENGTH];
279 char local_sha1_ascii[(SHA_DIGEST_LENGTH * 2)+1];
283 sprintf(buf, "GET %s%s HTTP/1.1\r\n", fu, bu);
284 sprintf(buf + strlen(buf), "Host: %s\r\n", vpninfo->hostname);
285 sprintf(buf + strlen(buf), "User-Agent: %s\r\n", vpninfo->useragent);
286 sprintf(buf + strlen(buf), "Accept: */*\r\n");
287 sprintf(buf + strlen(buf), "Accept-Encoding: identity\r\n");
289 if (vpninfo->cookies) {
290 sprintf(buf + strlen(buf), "Cookie: ");
291 for (opt = vpninfo->cookies; opt; opt = opt->next)
292 sprintf(buf + strlen(buf), "%s=%s%s", opt->option,
293 opt->value, opt->next ? "; " : "\r\n");
295 sprintf(buf + strlen(buf), "X-Transcend-Version: 1\r\n\r\n");
297 SSL_write(vpninfo->https_ssl, buf, strlen(buf));
299 buflen = process_http_response(vpninfo, &result, NULL, buf, MAX_BUF_LEN);
301 /* We'll already have complained about whatever offended us */
310 EVP_Digest(buf, buflen, local_sha1_bin, NULL, EVP_sha1(), NULL);
311 EVP_MD_CTX_cleanup(&c);
313 for (i = 0; i < SHA_DIGEST_LENGTH; i++)
314 sprintf(&local_sha1_ascii[i*2], "%02x", local_sha1_bin[i]);
316 if (strcasecmp(server_sha1, local_sha1_ascii)) {
317 vpninfo->progress(vpninfo, PRG_ERR, "Downloaded config file did not match intended SHA1\n");
321 return vpninfo->write_new_config(vpninfo, buf, buflen);
324 static int run_csd_script(struct openconnect_info *vpninfo, char *buf, int buflen)
329 if (!vpninfo->uid_csd_given) {
330 vpninfo->progress(vpninfo, PRG_ERR, "Error: You are trying to "
331 "run insecure CSD code without specifying the CSD user.\n"
332 " Use command line option \"--csd-user\"\n");
337 vpninfo->progress(vpninfo, PRG_INFO,
338 "Trying to run Linux CSD trojan script.");
341 sprintf(fname, "/tmp/csdXXXXXX");
345 vpninfo->progress(vpninfo, PRG_ERR, "Failed to open temporary CSD script file: %s\n",
349 write(fd, buf, buflen);
354 X509 *scert = SSL_get_peer_certificate(vpninfo->https_ssl);
355 X509 *ccert = SSL_get_certificate(vpninfo->https_ssl);
356 char scertbuf[EVP_MAX_MD_SIZE * 2 + 1];
357 char ccertbuf[EVP_MAX_MD_SIZE * 2 + 1];
361 if (vpninfo->uid_csd != getuid()) {
364 if (setuid(vpninfo->uid_csd)) {
365 fprintf(stderr, "Failed to set uid %d\n",
369 if (!(pw = getpwuid(vpninfo->uid_csd))) {
370 fprintf(stderr, "Invalid user uid=%d\n",
374 setenv("HOME", pw->pw_dir, 1);
377 if (vpninfo->uid_csd == 0) {
378 fprintf(stderr, "Warning: you are running insecure "
379 "CSD code with root privileges\n"
380 "\t Use command line option \"--csd-user\"\n");
383 csd_argv[i++] = fname;
384 csd_argv[i++] = "-ticket";
385 asprintf(&csd_argv[i++], "\"%s\"", vpninfo->csd_ticket);
386 csd_argv[i++] = "-stub";
387 csd_argv[i++] = "\"0\"";
388 csd_argv[i++] = "-group";
389 asprintf(&csd_argv[i++], "\"%s\"", vpninfo->authgroup?:"");
390 get_cert_md5_fingerprint(vpninfo, scert, scertbuf);
392 get_cert_md5_fingerprint(vpninfo, ccert, ccertbuf);
396 csd_argv[i++] = "-certhash";
397 asprintf(&csd_argv[i++], "\"%s:%s\"", scertbuf, ccertbuf);
398 csd_argv[i++] = "-url";
399 asprintf(&csd_argv[i++], "\"https://%s%s\"", vpninfo->hostname, vpninfo->csd_starturl);
400 /* WTF would it want to know this for? */
401 csd_argv[i++] = "-vpnclient";
402 csd_argv[i++] = "\"/opt/cisco/vpn/bin/vpnui";
403 csd_argv[i++] = "-connect";
404 asprintf(&csd_argv[i++], "https://%s/%s", vpninfo->hostname, vpninfo->csd_preurl);
405 csd_argv[i++] = "-connectparam";
406 asprintf(&csd_argv[i++], "#csdtoken=%s\"", vpninfo->csd_token);
407 csd_argv[i++] = "-langselen";
408 csd_argv[i++] = NULL;
410 execv(fname, csd_argv);
411 vpninfo->progress(vpninfo, PRG_ERR, "Failed to exec CSD script %s\n", fname);
415 free(vpninfo->csd_stuburl);
416 vpninfo->csd_stuburl = NULL;
417 vpninfo->urlpath = strdup(vpninfo->csd_waiturl +
418 (vpninfo->csd_waiturl[0] == '/' ? 1 : 0));
419 vpninfo->csd_waiturl = NULL;
420 vpninfo->csd_scriptname = strdup(fname);
422 http_add_cookie(vpninfo, "sdesktop", vpninfo->csd_token);
428 char *local_strcasestr(const char *haystack, const char *needle)
430 int hlen = strlen(haystack);
431 int nlen = strlen(needle);
434 for (i = 0; i < hlen - nlen + 1; i++) {
435 for (j = 0; j < nlen; j++) {
436 if (tolower(haystack[i + j]) !=
441 return (char *)haystack + i;
445 #define strcasestr local_strcasestr
448 int parse_url(char *url, char **res_proto, char **res_host, int *res_port,
449 char **res_path, int default_port)
452 char *host, *path, *port_str;
455 host = strstr(url, "://");
460 if (!strcasecmp(proto, "https"))
462 else if (!strcasecmp(proto, "http"))
464 else if (!strcasecmp(proto, "socks") ||
465 !strcasecmp(proto, "socks4") ||
466 !strcasecmp(proto, "socks5"))
469 return -EPROTONOSUPPORT;
479 path = strchr(host, '/');
486 port_str = strrchr(host, ':');
489 int new_port = strtol(port_str + 1, &end, 10);
498 *res_proto = proto ? strdup(proto) : NULL;
500 *res_host = strdup(host);
504 *res_path = path ? strdup(path) : NULL;
510 * = 0, no cookie (user cancel)
511 * = 1, obtained cookie
513 int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
515 struct vpn_option *opt, *next;
516 char buf[MAX_BUF_LEN];
518 char request_body[2048];
519 char *request_body_type = NULL;
520 char *method = "GET";
523 if (!vpninfo->https_ssl && openconnect_open_https(vpninfo)) {
524 vpninfo->progress(vpninfo, PRG_ERR, "Failed to open HTTPS connection to %s\n",
530 * It would be nice to use cURL for this, but we really need to guarantee
531 * that we'll be using OpenSSL (for the TPM stuff), and it doesn't seem
532 * to have any way to let us provide our own socket read/write functions.
533 * We can only provide a socket _open_ function. Which would require having
534 * a socketpair() and servicing the "other" end of it.
536 * So we process the HTTP for ourselves...
538 sprintf(buf, "%s /%s HTTP/1.1\r\n", method, vpninfo->urlpath ?: "");
539 sprintf(buf + strlen(buf), "Host: %s\r\n", vpninfo->hostname);
540 sprintf(buf + strlen(buf), "User-Agent: %s\r\n", vpninfo->useragent);
541 sprintf(buf + strlen(buf), "Accept: */*\r\n");
542 sprintf(buf + strlen(buf), "Accept-Encoding: identity\r\n");
544 if (vpninfo->cookies) {
545 sprintf(buf + strlen(buf), "Cookie: ");
546 for (opt = vpninfo->cookies; opt; opt = opt->next)
547 sprintf(buf + strlen(buf), "%s=%s%s", opt->option,
548 opt->value, opt->next ? "; " : "\r\n");
550 if (request_body_type) {
551 sprintf(buf + strlen(buf), "Content-Type: %s\r\n",
553 sprintf(buf + strlen(buf), "Content-Length: %zd\r\n",
554 strlen(request_body));
556 sprintf(buf + strlen(buf), "X-Transcend-Version: 1\r\n\r\n");
557 if (request_body_type)
558 sprintf(buf + strlen(buf), "%s", request_body);
560 if (vpninfo->port == 443)
561 vpninfo->progress(vpninfo, PRG_INFO, "%s https://%s/%s\n",
562 method, vpninfo->hostname,
563 vpninfo->urlpath ?: "");
565 vpninfo->progress(vpninfo, PRG_INFO, "%s https://%s:%d/%s\n",
566 method, vpninfo->hostname, vpninfo->port,
567 vpninfo->urlpath ?: "");
569 SSL_write(vpninfo->https_ssl, buf, strlen(buf));
571 buflen = process_http_response(vpninfo, &result, NULL, buf, MAX_BUF_LEN);
573 /* We'll already have complained about whatever offended us */
577 if (result != 200 && vpninfo->redirect_url) {
579 if (!strncmp(vpninfo->redirect_url, "https://", 8)) {
580 /* New host. Tear down the existing connection and make a new one */
585 free(vpninfo->urlpath);
586 vpninfo->urlpath = NULL;
588 ret = parse_url(vpninfo->redirect_url, NULL, &host, &port, &vpninfo->urlpath, 0);
590 vpninfo->progress(vpninfo, PRG_ERR, "Failed to parse redirected URL '%s': %s\n",
591 vpninfo->redirect_url, strerror(-ret));
592 free(vpninfo->redirect_url);
596 if (strcmp(vpninfo->hostname, host) || port != vpninfo->port) {
597 free(vpninfo->hostname);
598 vpninfo->hostname = host;
599 vpninfo->port = port;
601 /* Kill the existing connection, and a new one will happen */
602 free(vpninfo->peer_addr);
603 vpninfo->peer_addr = NULL;
604 SSL_free(vpninfo->https_ssl);
605 vpninfo->https_ssl = NULL;
606 close(vpninfo->ssl_fd);
607 vpninfo->ssl_fd = -1;
609 for (opt = vpninfo->cookies; opt; opt = next) {
616 vpninfo->cookies = NULL;
620 free(vpninfo->redirect_url);
621 vpninfo->redirect_url = NULL;
624 } else if (vpninfo->redirect_url[0] == '/') {
625 /* Absolute redirect within same host */
626 free(vpninfo->urlpath);
627 vpninfo->urlpath = strdup(vpninfo->redirect_url + 1);
628 free(vpninfo->redirect_url);
629 vpninfo->redirect_url = NULL;
632 vpninfo->progress(vpninfo, PRG_ERR, "Relative redirect (to '%s') not supported\n",
633 vpninfo->redirect_url);
638 if (vpninfo->csd_stuburl) {
639 /* This is the CSD stub script, which we now need to run */
640 result = run_csd_script(vpninfo, buf, buflen);
644 /* Now we'll be redirected to the waiturl */
647 if (strncmp(buf, "<?xml", 5)) {
648 /* Not XML? Perhaps it's HTML with a refresh... */
649 if (strcasestr(buf, "http-equiv=\"refresh\"")) {
650 vpninfo->progress(vpninfo, PRG_INFO, "Refreshing %s after 1 second...\n",
655 vpninfo->progress(vpninfo, PRG_ERR, "Unknown response from server\n");
659 result = parse_xml_response(vpninfo, buf, request_body, sizeof(request_body),
660 &method, &request_body_type);
666 /* A return value of 2 means the XML form indicated
667 success. We _should_ have a cookie... */
669 for (opt = vpninfo->cookies; opt; opt = opt->next) {
671 if (!strcmp(opt->option, "webvpn"))
672 vpninfo->cookie = opt->value;
673 else if (vpninfo->write_new_config && !strcmp(opt->option, "webvpnc")) {
674 char *tok = opt->value;
675 char *bu = NULL, *fu = NULL, *sha = NULL;
678 if (tok != opt->value)
681 if (!strncmp(tok, "bu:", 3))
683 else if (!strncmp(tok, "fu:", 3))
685 else if (!strncmp(tok, "fh:", 3)) {
686 if (!strncasecmp(tok+3, vpninfo->xmlsha1,
687 SHA_DIGEST_LENGTH * 2))
691 } while ((tok = strchr(tok, '&')));
694 fetch_config(vpninfo, bu, fu, sha);
697 if (vpninfo->csd_scriptname) {
698 unlink(vpninfo->csd_scriptname);
699 free(vpninfo->csd_scriptname);
700 vpninfo->csd_scriptname = NULL;
705 char *openconnect_create_useragent(char *base)
709 if (asprintf(&uagent, "%s %s", base, openconnect_version) < 0)
715 static int proxy_gets(int fd, char *buf, size_t len)
723 while ( (ret = read(fd, buf + i, 1)) == 1) {
724 if (buf[i] == '\n') {
726 if (i && buf[i-1] == '\r') {
746 static int proxy_write(int fd, unsigned char *buf, size_t len)
750 for (count = 0; count < len; ) {
751 int i = write(fd, buf + count, len - count);
760 static int proxy_read(int fd, unsigned char *buf, size_t len)
764 for (count = 0; count < len; ) {
765 int i = read(fd, buf + count, len - count);
774 static const char *socks_errors[] = {
777 "connection not allowed by ruleset",
778 "network unreachable",
780 "connection refused by destination host",
782 "command not supported / protocol error",
783 "address type not supported"
786 static int process_socks_proxy(struct openconnect_info *vpninfo, int ssl_sock)
788 unsigned char buf[1024];
791 buf[0] = 5; /* SOCKS version */
792 buf[1] = 1; /* # auth methods */
793 buf[2] = 0; /* No auth supported */
795 if ((i = proxy_write(ssl_sock, buf, 3))) {
796 vpninfo->progress(vpninfo, PRG_ERR,
797 "Error writing auth request to SOCKS proxy: %s\n",
802 if ((i = proxy_read(ssl_sock, buf, 2))) {
803 vpninfo->progress(vpninfo, PRG_ERR,
804 "Error reading auth response from SOCKS proxy: %s\n",
809 vpninfo->progress(vpninfo, PRG_ERR,
810 "Unexpected auth response from SOCKS proxy: %02x %02x\n",
816 if (buf[1] < sizeof(socks_errors) / sizeof(socks_errors[0]))
817 vpninfo->progress(vpninfo, PRG_ERR,
818 "SOCKS proxy error %02x: %s\n",
819 buf[1], socks_errors[buf[1]]);
821 vpninfo->progress(vpninfo, PRG_ERR,
822 "SOCKS proxy error %02x\n",
827 vpninfo->progress(vpninfo, PRG_INFO, "Requesting SOCKS proxy connection to %s:%d\n",
828 vpninfo->hostname, vpninfo->port);
830 buf[0] = 5; /* SOCKS version */
831 buf[1] = 1; /* CONNECT */
832 buf[2] = 0; /* Reserved */
833 buf[3] = 3; /* Address type is domain name */
834 buf[4] = strlen(vpninfo->hostname);
835 strcpy((char *)buf + 5, vpninfo->hostname);
836 i = strlen(vpninfo->hostname) + 5;
837 buf[i++] = vpninfo->port >> 8;
838 buf[i++] = vpninfo->port & 0xff;
840 if ((i = proxy_write(ssl_sock, buf, i))) {
841 vpninfo->progress(vpninfo, PRG_ERR,
842 "Error writing connect request to SOCKS proxy: %s\n",
846 /* Read 5 bytes -- up to and including the first byte of the returned
847 address (which might be the length byte of a domain name) */
848 if ((i = proxy_read(ssl_sock, buf, 5))) {
849 vpninfo->progress(vpninfo, PRG_ERR,
850 "Error reading connect response from SOCKS proxy: %s\n",
855 vpninfo->progress(vpninfo, PRG_ERR,
856 "Unexpected connect response from SOCKS proxy: %02x %02x...\n",
863 /* Connect responses contain an address */
865 case 1: /* Legacy IP */
868 case 3: /* Domain name */
875 vpninfo->progress(vpninfo, PRG_ERR,
876 "Unexpected address type %02x in SOCKS connect response\n",
881 if ((i = proxy_read(ssl_sock, buf, i))) {
882 vpninfo->progress(vpninfo, PRG_ERR,
883 "Error reading connect response from SOCKS proxy: %s\n",
890 static int process_http_proxy(struct openconnect_info *vpninfo, int ssl_sock)
892 char buf[MAX_BUF_LEN];
895 sprintf(buf, "CONNECT %s:%d HTTP/1.1\r\n", vpninfo->hostname, vpninfo->port);
896 sprintf(buf + strlen(buf), "Host: %s\r\n", vpninfo->hostname);
897 sprintf(buf + strlen(buf), "User-Agent: %s\r\n", vpninfo->useragent);
898 sprintf(buf + strlen(buf), "Proxy-Connection: keep-alive\r\n");
899 sprintf(buf + strlen(buf), "Connection: keep-alive\r\n");
900 sprintf(buf + strlen(buf), "Accept-Encoding: identity\r\n");
901 sprintf(buf + strlen(buf), "\r\n");
903 vpninfo->progress(vpninfo, PRG_INFO, "Requesting HTTP proxy connection to %s:%d\n",
904 vpninfo->hostname, vpninfo->port);
906 if (proxy_write(ssl_sock, (unsigned char *)buf, strlen(buf))) {
908 vpninfo->progress(vpninfo, PRG_ERR, "Sending proxy request failed: %s\n",
913 if (proxy_gets(ssl_sock, buf, sizeof(buf)) < 0) {
914 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching proxy response\n");
918 if (strncmp(buf, "HTTP/1.", 7) || (buf[7] != '0' && buf[7] != '1') ||
919 buf[8] != ' ' || !(result = atoi(buf+9))) {
920 vpninfo->progress(vpninfo, PRG_ERR, "Failed to parse proxy response '%s'\n",
926 vpninfo->progress(vpninfo, PRG_ERR, "Proxy CONNECT request failed: %s\n",
931 while ((buflen = proxy_gets(ssl_sock, buf, sizeof(buf)))) {
933 vpninfo->progress(vpninfo, PRG_ERR, "Failed to read proxy response\n");
936 vpninfo->progress(vpninfo, PRG_ERR,
937 "Unexpected continuation line after CONNECT response: '%s'\n",
944 int process_proxy(struct openconnect_info *vpninfo, int ssl_sock)
946 if (!vpninfo->proxy_type || !strcmp(vpninfo->proxy_type, "http"))
947 return process_http_proxy(vpninfo, ssl_sock);
949 if (!strcmp(vpninfo->proxy_type, "socks") ||
950 !strcmp(vpninfo->proxy_type, "socks5"))
951 return process_socks_proxy(vpninfo, ssl_sock);
953 vpninfo->progress(vpninfo, PRG_ERR, "Unknown proxy type '%s'\n",
954 vpninfo->proxy_type);
958 int set_http_proxy(struct openconnect_info *vpninfo, char *proxy)
960 char *url = strdup(proxy);
966 free(vpninfo->proxy_type);
967 vpninfo->proxy_type = NULL;
968 free(vpninfo->proxy);
969 vpninfo->proxy = NULL;
971 ret = parse_url(url, &vpninfo->proxy_type, &vpninfo->proxy,
972 &vpninfo->proxy_port, NULL, 80);
976 if (vpninfo->proxy_type &&
977 strcmp(vpninfo->proxy_type, "http") &&
978 strcmp(vpninfo->proxy_type, "socks") &&
979 strcmp(vpninfo->proxy_type, "socks5")) {
980 vpninfo->progress(vpninfo, PRG_ERR,
981 "Only http or socks(5) proxies supported\n");
982 free(vpninfo->proxy_type);
983 vpninfo->proxy_type = NULL;
984 free(vpninfo->proxy);
985 vpninfo->proxy = NULL;