#include "openconnect-internal.h"
-/* OSX < 1.6 doesn't have AI_NUMERICSERV */
-#ifndef AI_NUMERICSERV
-#define AI_NUMERICSERV 0
-#endif
-
/* Helper functions for reading/writing lines over SSL.
We could use cURL for the HTTP stuff, but it's overkill */
return err;
}
-static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd,
- const struct sockaddr *addr, socklen_t addrlen)
-{
- struct sockaddr_storage peer;
- socklen_t peerlen = sizeof(peer);
- fd_set wr_set, rd_set;
- int maxfd = sockfd;
-
- fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
-
- if (connect(sockfd, addr, addrlen) < 0 && errno != EINPROGRESS)
- return -1;
-
- FD_ZERO(&wr_set);
- FD_ZERO(&rd_set);
- FD_SET(sockfd, &wr_set);
- if (vpninfo->cancel_fd != -1) {
- FD_SET(vpninfo->cancel_fd, &rd_set);
- if (vpninfo->cancel_fd > sockfd)
- maxfd = vpninfo->cancel_fd;
- }
-
- /* Later we'll render this whole exercise non-pointless by
- including a 'cancelfd' here too. */
- select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
- if (vpninfo->cancel_fd != -1 && FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
- vpn_progress(vpninfo, PRG_ERR, _("Socket connect cancelled\n"));
- errno = EINTR;
- return -1;
- }
-
- /* Check whether connect() succeeded or failed by using
- getpeername(). See http://cr.yp.to/docs/connect.html */
- return getpeername(sockfd, (void *)&peer, &peerlen);
-}
int openconnect_open_https(struct openconnect_info *vpninfo)
{
if (vpninfo->https_sess)
return 0;
- if (!vpninfo->port)
- vpninfo->port = 443;
-
- if (vpninfo->peer_addr) {
-#ifdef SOCK_CLOEXEC
- ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_IP);
- if (ssl_sock < 0)
-#endif
- {
- ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
- if (ssl_sock < 0)
- goto reconn_err;
- fcntl(ssl_sock, F_SETFD, fcntl(ssl_sock, F_GETFD) | FD_CLOEXEC);
- }
- if (cancellable_connect(vpninfo, ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen)) {
- reconn_err:
- if (vpninfo->proxy) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to reconnect to proxy %s\n"),
- vpninfo->proxy);
- } else {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to reconnect to host %s\n"),
- vpninfo->hostname);
- }
- return -EINVAL;
- }
-
- } else {
- struct addrinfo hints, *result, *rp;
- char *hostname;
- char port[6];
-
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
- hints.ai_protocol = 0;
- hints.ai_canonname = NULL;
- hints.ai_addr = NULL;
- hints.ai_next = NULL;
-
- /* 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,}. */
-#ifdef LIBPROXY_HDR
- if (vpninfo->proxy_factory) {
- char *url;
- char **proxies;
- int i = 0;
-
- free(vpninfo->proxy_type);
- vpninfo->proxy_type = NULL;
- free(vpninfo->proxy);
- vpninfo->proxy = NULL;
-
- if (vpninfo->port == 443)
- i = asprintf(&url, "https://%s/%s", vpninfo->hostname,
- vpninfo->urlpath?:"");
- else
- i = asprintf(&url, "https://%s:%d/%s", vpninfo->hostname,
- vpninfo->port, vpninfo->urlpath?:"");
- if (i == -1)
- return -ENOMEM;
-
- proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
- url);
-
- i = 0;
- while (proxies && proxies[i]) {
- if (!vpninfo->proxy &&
- (!strncmp(proxies[i], "http://", 7) ||
- !strncmp(proxies[i], "socks://", 8) ||
- !strncmp(proxies[i], "socks5://", 9)))
- internal_parse_url(proxies[i], &vpninfo->proxy_type,
- &vpninfo->proxy, &vpninfo->proxy_port,
- NULL, 0);
- i++;
- }
- free(url);
- free(proxies);
- if (vpninfo->proxy)
- vpn_progress(vpninfo, PRG_TRACE,
- _("Proxy from libproxy: %s://%s:%d/\n"),
- vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
- }
-#endif
- if (vpninfo->proxy) {
- hostname = vpninfo->proxy;
- snprintf(port, 6, "%d", vpninfo->proxy_port);
- } else {
- hostname = vpninfo->hostname;
- snprintf(port, 6, "%d", vpninfo->port);
- }
-
- if (hostname[0] == '[' && hostname[strlen(hostname)-1] == ']') {
- /* Solaris has no strndup(). */
- int len = strlen(hostname) - 2;
- char *new_hostname = malloc(len + 1);
- if (!new_hostname)
- return -ENOMEM;
- memcpy(new_hostname, hostname + 1, len);
- new_hostname[len] = 0;
-
- hostname = new_hostname;
- hints.ai_flags |= AI_NUMERICHOST;
- }
-
- err = getaddrinfo(hostname, port, &hints, &result);
- if (hints.ai_flags & AI_NUMERICHOST)
- free(hostname);
-
- if (err) {
- vpn_progress(vpninfo, PRG_ERR,
- _("getaddrinfo failed for host '%s': %s\n"),
- hostname, gai_strerror(err));
- return -EINVAL;
- }
-
- 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))
- vpn_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)
- continue;
- if (cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0) {
- /* Store the peer address we actually used, so that DTLS can
- use it again later */
- vpninfo->peer_addr = malloc(rp->ai_addrlen);
- if (!vpninfo->peer_addr) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to allocate sockaddr storage\n"));
- close(ssl_sock);
- return -ENOMEM;
- }
- vpninfo->peer_addrlen = rp->ai_addrlen;
- memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
- break;
- }
- close(ssl_sock);
- ssl_sock = -1;
- }
- freeaddrinfo(result);
-
- if (ssl_sock < 0) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to connect to host %s\n"),
- vpninfo->proxy?:vpninfo->hostname);
- return -EINVAL;
- }
- }
-
- if (vpninfo->proxy) {
- err = process_proxy(vpninfo, ssl_sock);
- if (err) {
- close(ssl_sock);
- return err;
- }
- }
+ ssl_sock = connect_https_socket(vpninfo);
+ if (ssl_sock < 0)
+ return ssl_sock;
if (!vpninfo->https_cred) {
gnutls_certificate_allocate_credentials(&vpninfo->https_cred);