#include <limits.h>
#include <sys/select.h>
#include <signal.h>
+#include <arpa/inet.h>
#include "anyconnect.h"
+int inflate_and_queue_packet(struct anyconnect_info *vpninfo, int type, void *buf, int len)
+{
+ struct pkt **q = &vpninfo->incoming_queue;
+
+ while (*q)
+ q = &(*q)->next;
+
+ *q = malloc(sizeof(struct pkt) + vpninfo->mtu);
+ if (!*q)
+ return -ENOMEM;
+
+ (*q)->type = type;
+ (*q)->next = NULL;
+
+ vpninfo->inflate_strm.next_in = buf;
+ vpninfo->inflate_strm.avail_in = len - 4;
+
+ vpninfo->inflate_strm.next_out = (*q)->data;
+ vpninfo->inflate_strm.avail_out = vpninfo->mtu;
+ vpninfo->inflate_strm.total_out = 0;
+
+ if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
+ fprintf(stderr, "inflate failed\n");
+ free(*q);
+ *q = NULL;
+ return -EINVAL;
+ }
+
+ (*q)->len = vpninfo->inflate_strm.total_out;
+
+ vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
+ (*q)->data, (*q)->len);
+
+ if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *)(buf + len - 4))) {
+ vpninfo->quit_reason = "Compression (inflate) adler32 failure";
+ }
+
+ return 0;
+}
+
int queue_new_packet(struct pkt **q, int type, void *buf, int len)
{
while (*q)
sa.sa_handler = handle_sigint;
sigaction(SIGINT, &sa, NULL);
- while (!killed) {
+ while (!killed && !vpninfo->quit_reason) {
int did_work = 0;
int timeout = INT_MAX;
printf("Did no work; sleeping for %d ms...\n", timeout);
poll(vpninfo->pfds, vpninfo->nfds, timeout);
+ if (vpninfo->pfds[vpninfo->ssl_pfd].revents & POLL_HUP) {
+ fprintf(stderr, "Server closed connection!\n");
+ /* OpenSSL doesn't seem to cope properly with this... */
+ exit(1);
+ }
}
- ssl_bye(vpninfo, "Client received SIGINT\n");
+ if (!vpninfo->quit_reason)
+ vpninfo->quit_reason = "Client received SIGINT";
+
+ ssl_bye(vpninfo, vpninfo->quit_reason);
return 0;
}
return -EINVAL;
}
+ /* We may have advertised it, but we only do it if the server agrees */
+ vpninfo->deflate = 0;
while ((i=my_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
struct vpn_option *new_option;
return -ENOMEM;
}
- if (!strcmp(buf, "X-CSTP-Keepalive"))
- vpninfo->ssl_keepalive = atol(colon);
-
if (!strncmp(buf, "X-DTLS-", 7)) {
*next_dtls_option = new_option;
next_dtls_option = &new_option->next;
- } else {
- *next_cstp_option = new_option;
- next_cstp_option = &new_option->next;
+ continue;
+ }
+ /* CSTP options... */
+ *next_cstp_option = new_option;
+ next_cstp_option = &new_option->next;
+
+ if (!strcmp(buf + 7, "Keepalive")) {
+ vpninfo->ssl_keepalive = atol(colon);
+ } else if (!strcmp(buf + 7, "Content-Encoding")) {
+ if (!strcmp(colon, "deflate"))
+ vpninfo->deflate = 1;
+ else {
+ fprintf(stderr,
+ "Unknown CSTP-Content-Encoding %s\n",
+ colon);
+ return -EINVAL;
+ }
}
}
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);
+ vpninfo->ssl_pfd = vpn_add_pollfd(vpninfo, vpninfo->ssl_fd, POLLIN|POLLHUP|POLLERR);
vpninfo->last_ssl_tx = time(NULL);
return 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[7]) {
- unknown_pkt:
- 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;
- }
+ buf[2] != 'F' || buf[3] != 1 || buf[7])
+ goto unknown_pkt;
if (len != 8 + (buf[4] << 8) + buf[5]) {
printf("Unexpected packet length. SSL_read returned %d but packet is\n",
len);
buf[4], buf[5], buf[6], buf[7]);
continue;
}
- if (buf[6] == 4) {
- /* Keepalive response */
+ switch(buf[6]) {
+ case 4: /* Keepalive response */
if (verbose)
printf("Got keepalive response\n");
continue;
+
+ case 0: /* Uncompressed Data */
+ queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
+ (buf[4] << 8) + buf[5]);
+ work_done = 1;
+ continue;
+
+ case 8: /* Compressed data */
+ if (!vpninfo->deflate) {
+ fprintf(stderr, "Compressed packet received in !deflate mode\n");
+ goto unknown_pkt;
+ }
+ inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, len - 8);
+ work_done = 1;
+ continue;
+
+ case 9:
+ fprintf(stderr, "received server terminate packet\n");
+ vpninfo->quit_reason = "Server request";
+ /* Do not pass Go. Do not collect £200 */
+ exit(1);
}
- if (buf[6] != 0) /* Data */
- goto unknown_pkt;
-
- queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
- (buf[4] << 8) + buf[5]);
- work_done = 1;
+
+ unknown_pkt:
+ 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]);
+ exit(1);
}
+
+
while (vpninfo->outgoing_queue) {
struct pkt *this = vpninfo->outgoing_queue;
+ char buf[2048];
+
+ memcpy(buf, data_hdr, 8);
+
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);
+
+ if (vpninfo->deflate) {
+ int ret;
+ vpninfo->deflate_strm.next_in = this->data;
+ vpninfo->deflate_strm.avail_in = this->len;
+ vpninfo->deflate_strm.next_out = (void *)buf + 8;
+ vpninfo->deflate_strm.avail_out = 2040;
+ vpninfo->deflate_strm.total_out = 0;
+
+ ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
+ if (ret) {
+ fprintf(stderr, "deflate failed %d\n", ret);
+ goto uncompr;
+ }
+
+ buf[6] = 8;
+ buf[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
+ buf[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
+
+ /* Add ongoing adler32 to tail of compressed packet */
+ vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
+ this->data, this->len);
+
+ buf[8 + vpninfo->deflate_strm.total_out] = vpninfo->deflate_adler32 >> 24;
+ buf[9 + vpninfo->deflate_strm.total_out] = (vpninfo->deflate_adler32 >> 16) & 0xff;
+ buf[10 + vpninfo->deflate_strm.total_out] = (vpninfo->deflate_adler32 >> 8) & 0xff;
+ buf[11 + vpninfo->deflate_strm.total_out] = vpninfo->deflate_adler32 & 0xff;
+
+ SSL_write(vpninfo->https_ssl, buf,
+ vpninfo->deflate_strm.total_out + 12);
+ } else {
+ uncompr:
+ buf[4] = this->len >> 8;
+ buf[5] = this->len & 0xff;
+ memcpy(buf + 8, this->data, this->len);
+ SSL_write(vpninfo->https_ssl, buf, this->len + 8);
+ }
vpninfo->last_ssl_tx = time(NULL);
}