Add compression support
[platform/upstream/openconnect.git] / mainloop.c
1 /*
2  * Open AnyConnect (SSL + DTLS) client
3  *
4  * © 2008 David Woodhouse <dwmw2@infradead.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software
7  * for any purpose with or without fee is hereby granted, provided
8  * that the above copyright notice and this permission notice appear
9  * in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
12  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
16  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
17  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20
21 #include <errno.h>
22 #include <poll.h>
23 #include <limits.h>
24 #include <sys/select.h>
25 #include <signal.h>
26 #include <arpa/inet.h>
27
28 #include "anyconnect.h"
29
30 int inflate_and_queue_packet(struct anyconnect_info *vpninfo, int type, void *buf, int len)
31 {
32         struct pkt **q = &vpninfo->incoming_queue;
33
34         while (*q)
35                 q = &(*q)->next;
36
37         *q = malloc(sizeof(struct pkt) + vpninfo->mtu);
38         if (!*q)
39                 return -ENOMEM;
40
41         (*q)->type = type;
42         (*q)->next = NULL;
43
44         vpninfo->inflate_strm.next_in = buf;
45         vpninfo->inflate_strm.avail_in = len - 4;
46
47         vpninfo->inflate_strm.next_out = (*q)->data;
48         vpninfo->inflate_strm.avail_out = vpninfo->mtu;
49         vpninfo->inflate_strm.total_out = 0;
50
51         if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
52                 fprintf(stderr, "inflate failed\n");
53                 free(*q);
54                 *q = NULL;
55                 return -EINVAL;
56         }
57
58         (*q)->len = vpninfo->inflate_strm.total_out;
59
60         vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
61                                            (*q)->data, (*q)->len);
62
63         if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *)(buf + len - 4))) {
64                 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
65         }
66
67         return 0;
68 }
69
70 int queue_new_packet(struct pkt **q, int type, void *buf, int len)
71 {
72         while (*q)
73                 q = &(*q)->next;
74
75         *q = malloc(sizeof(struct pkt) + len);
76         if (!*q)
77                 return -ENOMEM;
78
79         (*q)->type = type;
80         (*q)->len = len;
81         (*q)->next = NULL;
82         memcpy((*q)->data, buf, len);
83         return 0;
84 }
85
86 int vpn_add_pollfd(struct anyconnect_info *vpninfo, int fd, short events)
87 {
88         vpninfo->nfds++;
89         vpninfo->pfds = realloc(vpninfo->pfds, sizeof(struct pollfd) * vpninfo->nfds);
90         if (!vpninfo->pfds) {
91                 fprintf(stderr, "Failed to reallocate pfds\n");
92                 exit(1);
93         }
94         vpninfo->pfds[vpninfo->nfds - 1].fd = fd;
95         vpninfo->pfds[vpninfo->nfds - 1].events = events;
96
97         return vpninfo->nfds - 1;
98 }
99
100 static int killed;
101
102 static void handle_sigint(int sig)
103 {
104         killed = 1;
105 }
106
107 int vpn_mainloop(struct anyconnect_info *vpninfo)
108 {
109         struct sigaction sa;
110         memset(&sa, 0, sizeof(sa));
111         sa.sa_handler = handle_sigint;
112         
113         sigaction(SIGINT, &sa, NULL);
114         while (!killed && !vpninfo->quit_reason) {
115                 int did_work = 0;
116                 int timeout = INT_MAX;
117
118                 if (vpninfo->dtls_fd != -1)
119                         did_work += dtls_mainloop(vpninfo, &timeout);
120
121                 did_work += ssl_mainloop(vpninfo, &timeout);
122                 did_work += tun_mainloop(vpninfo, &timeout);
123                 
124                 if (did_work)
125                         continue;
126                 
127                 if (verbose)
128                         printf("Did no work; sleeping for %d ms...\n", timeout);
129
130                 poll(vpninfo->pfds, vpninfo->nfds, timeout);
131                 if (vpninfo->pfds[vpninfo->ssl_pfd].revents & POLL_HUP) {
132                         fprintf(stderr, "Server closed connection!\n");
133                         /* OpenSSL doesn't seem to cope properly with this... */
134                         exit(1);
135                 }
136         }
137         if (!vpninfo->quit_reason)
138                 vpninfo->quit_reason = "Client received SIGINT";
139
140         ssl_bye(vpninfo, vpninfo->quit_reason);
141
142         return 0;
143 }