exit(1);
}
memset(vpninfo, 0, sizeof(*vpninfo));
+
/* Set up some defaults */
+ vpninfo->tun_fd = vpninfo->ssl_fd = vpninfo->dtls_fd = -1;
vpninfo->useragent = "Open AnyConnect VPN Agent v0.01";
vpninfo->mtu = 1406;
if (RAND_bytes(vpninfo->dtls_secret, sizeof(vpninfo->dtls_secret)) != 1) {
}
if (setup_dtls(vpninfo))
- fprintf(stderr, "Set up DTLS failed\n");
+ fprintf(stderr, "Set up DTLS failedd; using SSL instead\n");
printf("Connected\n");
+ vpn_mainloop(vpninfo);
exit(1);
-
-#if 0
-
- if (fork()) {
- while (1) {
- size_t len;
-
- buf[0] = 'S';
- buf[1] = 'T';
- buf[2] = 'F';
- buf[3] = 1;
- buf[4] = 0;
- buf[5] = 0;
- buf[6] = 0;
- buf[7] = 0;
-
- len = read(tun_fd, &buf[8], 65536);
- if (len >= 0) {
- buf[4] = len >> 8;
- buf[5] = len & 0xff;
- }
- SSL_write(https_info->https_ssl, buf, len + 8);
- }
- } else {
- while (1) {
- int len;
- SSL_read(https_info->https_ssl, buf, 8);
-
- len = (buf[4] << 8) + buf [5];
- SSL_read(https_info->https_ssl, buf + 8, len);
- if (buf[0] != 'S' ||
- buf[1] != 'T' ||
- buf[2] != 'F' ||
- buf[3] != 1 ||
- buf[6] != 0 ||
- buf[7] != 0) {
- printf("Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
- buf[0], buf[1], buf[2], buf[3],
- buf[4], buf[5], buf[6], buf[7]);
- } else {
- write(tun_fd, buf + 8, len);
- }
- }
- }
-#endif
}
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
+#include <fcntl.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "anyconnect.h"
-SSL *open_https(struct anyconnect_info *vpninfo)
+/* Helper functions for reading/writing lines over SSL.
+ We could use cURL for the HTTP stuff, but it's overkill */
+
+static int __attribute__ ((format (printf, 2, 3)))
+ my_SSL_printf(SSL *ssl, const char *fmt, ...)
+{
+ char buf[1024];
+ va_list args;
+
+
+ buf[1023] = 0;
+
+ va_start(args, fmt);
+ vsnprintf(buf, 1023, fmt, args);
+ va_end(args);
+ if (verbose)
+ printf("%s", buf);
+ return SSL_write(ssl, buf, strlen(buf));
+
+}
+
+static int my_SSL_gets(SSL *ssl, char *buf, size_t len)
+{
+ int i = 0;
+ int ret;
+
+ if (len < 2)
+ return -EINVAL;
+
+ while ( (ret = SSL_read(ssl, 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;
+ }
+ }
+
+ buf[i] = 0;
+ return i?:ret;
+}
+
+
+
+int open_https(struct anyconnect_info *vpninfo)
{
SSL_METHOD *ssl3_method;
SSL_CTX *https_ctx;
struct addrinfo hints, *result, *rp;
memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
- hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
- hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
- hints.ai_protocol = 0; /* Any protocol */
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
err = getaddrinfo(vpninfo->hostname, "https", &hints, &result);
if (err) {
fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(err));
- return NULL;
+ return -EINVAL;
}
for (rp = result; rp ; rp = rp->ai_next) {
if (ssl_sock < 0)
continue;
- if (connect(ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0)
+ if (connect(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) {
+ fprintf(stderr, "Failed to allocate sockaddr storage\n");
+ close(ssl_sock);
+ return -ENOMEM;
+ }
+ memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
break;
-
+ }
close(ssl_sock);
}
freeaddrinfo(result);
if (!rp) {
fprintf(stderr, "Failed to connect to host %s\n", vpninfo->hostname);
- return NULL;
+ return -EINVAL;
}
ssl3_method = SSLv23_client_method();
ERR_print_errors_fp(stderr);
SSL_free(https_ssl);
SSL_CTX_free(https_ctx);
- return NULL;
- }
-
- return https_ssl;
-}
-
-
-int __attribute__ ((format (printf, 2, 3))) my_SSL_printf(SSL *ssl, const char *fmt, ...)
-{
- char buf[1024];
- va_list args;
-
-
- buf[1023] = 0;
-
- va_start(args, fmt);
- vsnprintf(buf, 1023, fmt, args);
- va_end(args);
- if (verbose)
- printf("%s", buf);
- return SSL_write(ssl, buf, strlen(buf));
-
-}
-
-int my_SSL_gets(SSL *ssl, char *buf, size_t len)
-{
- int i = 0;
- int ret;
-
- if (len < 2)
return -EINVAL;
-
- while ( (ret = SSL_read(ssl, 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;
- }
}
- buf[i] = 0;
- return i?:ret;
+ vpninfo->ssl_fd = ssl_sock;
+ vpninfo->https_ssl = https_ssl;
+ return 0;
}
+
int start_ssl_connection(struct anyconnect_info *vpninfo)
{
char buf[65536];
if (verbose)
printf("Connected!\n");
+
+ BIO_set_nbio(SSL_get_rbio(vpninfo->https_ssl),1);
+ BIO_set_nbio(SSL_get_wbio(vpninfo->https_ssl),1);
+
+ fcntl(vpninfo->ssl_fd, F_SETFL, fcntl(vpninfo->ssl_fd, F_GETFL) | O_NONBLOCK);
+
+ vpninfo->ssl_pfd = vpn_add_pollfd(vpninfo, vpninfo->ssl_fd, POLLIN);
+
return 0;
}
int make_ssl_connection(struct anyconnect_info *vpninfo)
{
- SSL *https_ssl = open_https(vpninfo);
- if (!https_ssl)
+ if (open_https(vpninfo))
exit(1);
- vpninfo->https_ssl = https_ssl;
if (start_ssl_connection(vpninfo))
exit(1);
+
return 0;
}
SSL_load_error_strings ();
OpenSSL_add_all_algorithms ();
}
+
+static char data_hdr[8] = {'S', 'T', 'F', 1, 0, 0, 0, 0};
+
+int ssl_mainloop(struct anyconnect_info *vpninfo, int *timeout)
+{
+ unsigned char buf[16384];
+ int len;
+ int work_done = 0;
+
+ while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
+
+ if (buf[0] != 'S' || buf[1] != 'T' ||
+ buf[2] != 'F' || buf[3] != 1 ||
+ buf[6] != 0 || buf[7] != 0) {
+ printf("Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+ continue;
+ }
+ if (len != 8 + (buf[4] << 8) + buf[5]) {
+ printf("Unexpected packet length. SSL_read returned %d but packet is\n",
+ len);
+ printf("%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+ }
+ queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
+ (buf[4] << 8) + buf[5]);
+ work_done = 1;
+ }
+
+ while (vpninfo->outgoing_queue) {
+ struct pkt *this = vpninfo->outgoing_queue;
+ vpninfo->outgoing_queue = this->next;
+ memcpy(this->hdr, data_hdr, 8);
+ this->hdr[4] = this->len >> 8;
+ this->hdr[5] = this->len & 0xff;
+
+ SSL_write(vpninfo->https_ssl, this->hdr, this->len + 8);
+ }
+ /* Work is not done if we just got rid of packets off the queue */
+ return work_done;
+}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <unistd.h>
#include "anyconnect.h"
struct vpn_option *cstp_opt = vpninfo->cstp_options;
struct ifreq ifr;
int tun_fd;
+ int pfd;
tun_fd = open("/dev/net/tun", O_RDWR);
if (tun_fd == -1) {
/* Better still, use lwip and just provide a SOCKS server rather than
telling the kernel about it at all */
vpninfo->tun_fd = tun_fd;
+ pfd = vpn_add_pollfd(vpninfo, vpninfo->tun_fd, POLLIN);
+
+ fcntl(vpninfo->tun_fd, F_SETFL, fcntl(vpninfo->tun_fd, F_GETFL) | O_NONBLOCK);
+
return 0;
}
+int tun_mainloop(struct anyconnect_info *vpninfo, int *timeout)
+{
+ char buf[2000];
+ int len;
+ int work_done = 0;
+
+ while ( (len = read(vpninfo->tun_fd, buf, sizeof(buf))) > 0) {
+ queue_new_packet(&vpninfo->outgoing_queue, AF_INET, buf, len);
+ work_done = 1;
+ }
+
+ while (vpninfo->incoming_queue) {
+ struct pkt *this = vpninfo->incoming_queue;
+ vpninfo->incoming_queue = this->next;
+ write(vpninfo->tun_fd, this->data, this->len);
+ }
+ /* Work is not done if we just got rid of packets off the queue */
+ return work_done;
+}