This makes openconnect_obtain_cookie() shorter, and easier to follow. Signed-off-by: Kevin Cernekee <cernekee at gmail.com> --- http.c | 189 +++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 103 insertions(+), 86 deletions(-) diff --git a/http.c b/http.c index 38fc9a5..1df6488 100644 --- a/http.c +++ b/http.c @@ -591,6 +591,104 @@ int internal_parse_url(char *url, char **res_proto, char **res_host, return 0; } +static void clear_cookies(struct openconnect_info *vpninfo) +{ + struct vpn_option *opt, *next; + + for (opt = vpninfo->cookies; opt; opt = next) { + next = opt->next; + + free(opt->option); + free(opt->value); + free(opt); + } + vpninfo->cookies = NULL; +} + +/* Return value: + * < 0, on error + * = 0, on success (go ahead and retry with the latest vpninfo->{hostname,urlpath,port,...}) + */ +static int handle_redirect(struct openconnect_info *vpninfo) +{ + if (!strncmp(vpninfo->redirect_url, "https://", 8)) { + /* New host. Tear down the existing connection and make a new one */ + char *host; + int port; + int ret; + + free(vpninfo->urlpath); + vpninfo->urlpath = NULL; + + ret = internal_parse_url(vpninfo->redirect_url, NULL, &host, &port, &vpninfo->urlpath, 0); + if (ret) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to parse redirected URL '%s': %s\n"), + vpninfo->redirect_url, strerror(-ret)); + free(vpninfo->redirect_url); + vpninfo->redirect_url = NULL; + return ret; + } + + if (strcasecmp(vpninfo->hostname, host) || port != vpninfo->port) { + free(vpninfo->hostname); + vpninfo->hostname = host; + vpninfo->port = port; + + /* Kill the existing connection, and a new one will happen */ + free(vpninfo->peer_addr); + vpninfo->peer_addr = NULL; + openconnect_close_https(vpninfo, 0); + clear_cookies(vpninfo); + } else + free(host); + + free(vpninfo->redirect_url); + vpninfo->redirect_url = NULL; + + return 0; + } else if (strstr(vpninfo->redirect_url, "://")) { + vpn_progress(vpninfo, PRG_ERR, + _("Cannot follow redirection to non-https URL '%s'\n"), + vpninfo->redirect_url); + free(vpninfo->redirect_url); + vpninfo->redirect_url = NULL; + return -EINVAL; + } else if (vpninfo->redirect_url[0] == '/') { + /* Absolute redirect within same host */ + free(vpninfo->urlpath); + vpninfo->urlpath = strdup(vpninfo->redirect_url + 1); + free(vpninfo->redirect_url); + vpninfo->redirect_url = NULL; + return 0; + } else { + char *lastslash = NULL; + if (vpninfo->urlpath) + lastslash = strrchr(vpninfo->urlpath, '/'); + if (!lastslash) { + free(vpninfo->urlpath); + vpninfo->urlpath = vpninfo->redirect_url; + vpninfo->redirect_url = NULL; + } else { + char *oldurl = vpninfo->urlpath; + *lastslash = 0; + vpninfo->urlpath = NULL; + if (asprintf(&vpninfo->urlpath, "%s/%s", + oldurl, vpninfo->redirect_url) == -1) { + int err = -errno; + vpn_progress(vpninfo, PRG_ERR, + _("Allocating new path for relative redirect failed: %s\n"), + strerror(-err)); + return err; + } + free(oldurl); + free(vpninfo->redirect_url); + vpninfo->redirect_url = NULL; + } + return 0; + } +} + /* Return value: * < 0, on error * > 0, no cookie (user cancel) @@ -598,7 +696,7 @@ int internal_parse_url(char *url, char **res_proto, char **res_host, */ int openconnect_obtain_cookie(struct openconnect_info *vpninfo) { - struct vpn_option *opt, *next; + struct vpn_option *opt; char buf[MAX_BUF_LEN]; char *form_buf = NULL; int result, buflen; @@ -676,92 +774,11 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo) if (result != 200 && vpninfo->redirect_url) { redirect: - if (!strncmp(vpninfo->redirect_url, "https://", 8)) { - /* New host. Tear down the existing connection and make a new one */ - char *host; - int port; - int ret; - - free(vpninfo->urlpath); - vpninfo->urlpath = NULL; - - ret = internal_parse_url(vpninfo->redirect_url, NULL, &host, &port, &vpninfo->urlpath, 0); - if (ret) { - vpn_progress(vpninfo, PRG_ERR, - _("Failed to parse redirected URL '%s': %s\n"), - vpninfo->redirect_url, strerror(-ret)); - free(vpninfo->redirect_url); - vpninfo->redirect_url = NULL; - free(form_buf); - return ret; - } - - if (strcasecmp(vpninfo->hostname, host) || port != vpninfo->port) { - free(vpninfo->hostname); - vpninfo->hostname = host; - vpninfo->port = port; - - /* Kill the existing connection, and a new one will happen */ - free(vpninfo->peer_addr); - vpninfo->peer_addr = NULL; - openconnect_close_https(vpninfo, 0); - - for (opt = vpninfo->cookies; opt; opt = next) { - next = opt->next; - - free(opt->option); - free(opt->value); - free(opt); - } - vpninfo->cookies = NULL; - } else - free(host); - - free(vpninfo->redirect_url); - vpninfo->redirect_url = NULL; - + result = handle_redirect(vpninfo); + if (result == 0) goto retry; - } else if (strstr(vpninfo->redirect_url, "://")) { - vpn_progress(vpninfo, PRG_ERR, - _("Cannot follow redirection to non-https URL '%s'\n"), - vpninfo->redirect_url); - free(vpninfo->redirect_url); - vpninfo->redirect_url = NULL; - free(form_buf); - return -EINVAL; - } else if (vpninfo->redirect_url[0] == '/') { - /* Absolute redirect within same host */ - free(vpninfo->urlpath); - vpninfo->urlpath = strdup(vpninfo->redirect_url + 1); - free(vpninfo->redirect_url); - vpninfo->redirect_url = NULL; - goto retry; - } else { - char *lastslash = NULL; - if (vpninfo->urlpath) - lastslash = strrchr(vpninfo->urlpath, '/'); - if (!lastslash) { - free(vpninfo->urlpath); - vpninfo->urlpath = vpninfo->redirect_url; - vpninfo->redirect_url = NULL; - } else { - char *oldurl = vpninfo->urlpath; - *lastslash = 0; - vpninfo->urlpath = NULL; - if (asprintf(&vpninfo->urlpath, "%s/%s", - oldurl, vpninfo->redirect_url) == -1) { - int err = -errno; - vpn_progress(vpninfo, PRG_ERR, - _("Allocating new path for relative redirect failed: %s\n"), - strerror(-err)); - return err; - } - free(oldurl); - free(vpninfo->redirect_url); - vpninfo->redirect_url = NULL; - } - goto retry; - } + free(form_buf); + return result; } if (!form_buf || result != 200) { vpn_progress(vpninfo, PRG_ERR, -- 1.7.10.4