First attempt at a mainloop
authorDavid Woodhouse <David.Woodhouse@intel.com>
Mon, 22 Sep 2008 04:51:47 +0000 (21:51 -0700)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Mon, 22 Sep 2008 04:51:47 +0000 (21:51 -0700)
Makefile
dtls.c
main.c
ssl.c
tun.c

index a7018f1..b8d4829 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ endif
 CFLAGS += -I/usr/include/openssl #-I/usr/include/libxml2
 LDFLAGS += -lssl
 
-OBJECTS := main.o tun.o dtls.o ssl.o
+OBJECTS := main.o tun.o dtls.o ssl.o mainloop.o
 
 anyconnect: $(OBJECTS)
        $(CC) -o $@ $(LDFLAGS) $^
diff --git a/dtls.c b/dtls.c
index 10f481e..07ce8ac 100644 (file)
--- a/dtls.c
+++ b/dtls.c
@@ -34,3 +34,9 @@ int setup_dtls(struct anyconnect_info *vpninfo)
        return -EINVAL;
 }
 
+int dtls_mainloop(struct anyconnect_info *vpninfo, int *timeout)
+{
+       return 0;
+}
+
+
diff --git a/main.c b/main.c
index 23e85c3..eae6009 100644 (file)
--- a/main.c
+++ b/main.c
@@ -66,7 +66,9 @@ int main(int argc, char **argv)
                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) {
@@ -133,53 +135,9 @@ int main(int argc, char **argv)
        }
 
        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
 }
diff --git a/ssl.c b/ssl.c
index aeff770..152526a 100644 (file)
--- a/ssl.c
+++ b/ssl.c
 #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;
@@ -37,10 +90,10 @@ SSL *open_https(struct anyconnect_info *vpninfo)
        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;
@@ -48,7 +101,7 @@ SSL *open_https(struct anyconnect_info *vpninfo)
        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) {
@@ -57,16 +110,25 @@ SSL *open_https(struct anyconnect_info *vpninfo)
                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();
@@ -81,59 +143,15 @@ SSL *open_https(struct anyconnect_info *vpninfo)
                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];
@@ -214,18 +232,25 @@ int start_ssl_connection(struct anyconnect_info *vpninfo)
 
        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;
 }
 
@@ -237,3 +262,46 @@ void vpn_init_openssl(void)
        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;
+}
diff --git a/tun.c b/tun.c
index d1cc880..7f4cbcf 100644 (file)
--- a/tun.c
+++ b/tun.c
@@ -25,6 +25,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <unistd.h>
 
 #include "anyconnect.h"
 
@@ -34,6 +35,7 @@ int setup_tun(struct anyconnect_info *vpninfo)
        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) {
@@ -57,6 +59,29 @@ int setup_tun(struct anyconnect_info *vpninfo)
        /* 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;
+}