2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008-2012 Intel Corporation.
6 * Author: David Woodhouse <dwmw2@infradead.org>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * version 2.1, as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301 USA
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
37 #if defined(__linux__)
39 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__APPLE__)
40 #include <sys/param.h>
41 #include <sys/mount.h>
42 #elif defined (__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
43 #include <sys/statvfs.h>
44 #elif defined (__GNU__)
45 #include <sys/statfs.h>
48 #include "openconnect-internal.h"
50 /* OSX < 1.6 doesn't have AI_NUMERICSERV */
51 #ifndef AI_NUMERICSERV
52 #define AI_NUMERICSERV 0
55 static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd,
56 const struct sockaddr *addr, socklen_t addrlen)
58 struct sockaddr_storage peer;
59 socklen_t peerlen = sizeof(peer);
60 fd_set wr_set, rd_set;
63 fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
65 if (connect(sockfd, addr, addrlen) < 0 && errno != EINPROGRESS)
70 FD_SET(sockfd, &wr_set);
71 if (vpninfo->cancel_fd != -1) {
72 FD_SET(vpninfo->cancel_fd, &rd_set);
73 if (vpninfo->cancel_fd > sockfd)
74 maxfd = vpninfo->cancel_fd;
77 /* Later we'll render this whole exercise non-pointless by
78 including a 'cancelfd' here too. */
79 select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
80 if (vpninfo->cancel_fd != -1 && FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
81 vpn_progress(vpninfo, PRG_ERR, _("Socket connect cancelled\n"));
86 /* Check whether connect() succeeded or failed by using
87 getpeername(). See http://cr.yp.to/docs/connect.html */
88 return getpeername(sockfd, (void *)&peer, &peerlen);
91 int connect_https_socket(struct openconnect_info *vpninfo)
99 if (vpninfo->peer_addr) {
101 ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_IP);
105 ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
108 fcntl(ssl_sock, F_SETFD, fcntl(ssl_sock, F_GETFD) | FD_CLOEXEC);
110 if (cancellable_connect(vpninfo, ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen)) {
112 if (vpninfo->proxy) {
113 vpn_progress(vpninfo, PRG_ERR,
114 _("Failed to reconnect to proxy %s\n"),
117 vpn_progress(vpninfo, PRG_ERR,
118 _("Failed to reconnect to host %s\n"),
125 struct addrinfo hints, *result, *rp;
129 memset(&hints, 0, sizeof(struct addrinfo));
130 hints.ai_family = AF_UNSPEC;
131 hints.ai_socktype = SOCK_STREAM;
132 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
133 hints.ai_protocol = 0;
134 hints.ai_canonname = NULL;
135 hints.ai_addr = NULL;
136 hints.ai_next = NULL;
138 /* The 'port' variable is a string because it's easier
139 this way than if we pass NULL to getaddrinfo() and
140 then try to fill in the numeric value into
141 different types of returned sockaddr_in{6,}. */
143 if (vpninfo->proxy_factory) {
148 free(vpninfo->proxy_type);
149 vpninfo->proxy_type = NULL;
150 free(vpninfo->proxy);
151 vpninfo->proxy = NULL;
153 if (vpninfo->port == 443)
154 i = asprintf(&url, "https://%s/%s", vpninfo->hostname,
155 vpninfo->urlpath?:"");
157 i = asprintf(&url, "https://%s:%d/%s", vpninfo->hostname,
158 vpninfo->port, vpninfo->urlpath?:"");
162 proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
166 while (proxies && proxies[i]) {
167 if (!vpninfo->proxy &&
168 (!strncmp(proxies[i], "http://", 7) ||
169 !strncmp(proxies[i], "socks://", 8) ||
170 !strncmp(proxies[i], "socks5://", 9)))
171 internal_parse_url(proxies[i], &vpninfo->proxy_type,
172 &vpninfo->proxy, &vpninfo->proxy_port,
179 vpn_progress(vpninfo, PRG_TRACE,
180 _("Proxy from libproxy: %s://%s:%d/\n"),
181 vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
184 if (vpninfo->proxy) {
185 hostname = vpninfo->proxy;
186 snprintf(port, 6, "%d", vpninfo->proxy_port);
188 hostname = vpninfo->hostname;
189 snprintf(port, 6, "%d", vpninfo->port);
192 if (hostname[0] == '[' && hostname[strlen(hostname)-1] == ']') {
193 /* Solaris has no strndup(). */
194 int len = strlen(hostname) - 2;
195 char *new_hostname = malloc(len + 1);
198 memcpy(new_hostname, hostname + 1, len);
199 new_hostname[len] = 0;
201 hostname = new_hostname;
202 hints.ai_flags |= AI_NUMERICHOST;
205 err = getaddrinfo(hostname, port, &hints, &result);
206 if (hints.ai_flags & AI_NUMERICHOST)
210 vpn_progress(vpninfo, PRG_ERR,
211 _("getaddrinfo failed for host '%s': %s\n"),
212 hostname, gai_strerror(err));
216 for (rp = result; rp ; rp = rp->ai_next) {
219 if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
220 sizeof(host), NULL, 0, NI_NUMERICHOST))
221 vpn_progress(vpninfo, PRG_INFO,
222 _("Attempting to connect to %s%s%s:%s\n"),
223 rp->ai_family == AF_INET6?"[":"",
225 rp->ai_family == AF_INET6?"]":"",
228 ssl_sock = socket(rp->ai_family, rp->ai_socktype,
232 if (cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0) {
233 /* Store the peer address we actually used, so that DTLS can
234 use it again later */
235 vpninfo->peer_addr = malloc(rp->ai_addrlen);
236 if (!vpninfo->peer_addr) {
237 vpn_progress(vpninfo, PRG_ERR,
238 _("Failed to allocate sockaddr storage\n"));
242 vpninfo->peer_addrlen = rp->ai_addrlen;
243 memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
249 freeaddrinfo(result);
252 vpn_progress(vpninfo, PRG_ERR,
253 _("Failed to connect to host %s\n"),
254 vpninfo->proxy?:vpninfo->hostname);
259 if (vpninfo->proxy) {
260 err = process_proxy(vpninfo, ssl_sock);
270 int __attribute__ ((format (printf, 2, 3)))
271 openconnect_SSL_printf(struct openconnect_info *vpninfo, const char *fmt, ...)
279 vsnprintf(buf, 1023, fmt, args);
281 return openconnect_SSL_write(vpninfo, buf, strlen(buf));
285 #if defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
286 int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
290 if (statvfs(vpninfo->sslkey, &buf)) {
292 vpn_progress(vpninfo, PRG_ERR, _("statvfs: %s\n"),
296 if (asprintf(&vpninfo->cert_password, "%lx", buf.f_fsid))
301 int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
304 unsigned *fsid = (unsigned *)&buf.f_fsid;
305 unsigned long long fsid64;
307 if (statfs(vpninfo->sslkey, &buf)) {
309 vpn_progress(vpninfo, PRG_ERR, _("statfs: %s\n"),
313 fsid64 = ((unsigned long long)fsid[0] << 32) | fsid[1];
315 if (asprintf(&vpninfo->cert_password, "%llx", fsid64))