Rename to openconnect to avoid potential trademark issues
[platform/upstream/openconnect.git] / cstp.c
1 /*
2  * Open AnyConnect (SSL + DTLS) client
3  *
4  * © 2008 David Woodhouse <dwmw2@infradead.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to:
18  *
19  *   Free Software Foundation, Inc.
20  *   51 Franklin Street, Fifth Floor,
21  *   Boston, MA 02110-1301 USA
22  */
23 #include <netdb.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <time.h>
27
28 #include <openssl/ssl.h>
29 #include <openssl/err.h>
30
31 #include "openconnect.h"
32
33 /*
34  * Data packets are encapsulated in the SSL stream as follows:
35  * 
36  * 0000: Magic "STF\x1"
37  * 0004: Big-endian 16-bit length (not including 8-byte header)
38  * 0006: Byte packet type (see openconnect.h)
39  * 0008: data payload
40  */
41
42 static char data_hdr[8] = {
43         'S', 'T', 'F', 1,
44         0, 0,           /* Length */
45         AC_PKT_DATA,    /* Type */
46         0               /* Unknown */
47 };
48
49 static struct pkt keepalive_pkt = {
50         .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_KEEPALIVE, 0 },
51 };
52
53 static struct pkt dpd_pkt = {
54         .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_OUT, 0 },
55 };
56
57 static struct pkt dpd_resp_pkt = {
58         .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_RESP, 0 },
59 };
60
61
62 static int start_cstp_connection(struct openconnect_info *vpninfo)
63 {
64         char buf[65536];
65         int i;
66         int retried = 0;
67         struct vpn_option **next_dtls_option = &vpninfo->dtls_options;
68         struct vpn_option **next_cstp_option = &vpninfo->cstp_options;
69         struct vpn_option *old_cstp_opts = vpninfo->cstp_options;
70         struct vpn_option *old_dtls_opts = vpninfo->dtls_options;
71         const char *old_addr = vpninfo->vpn_addr;
72         const char *old_netmask = vpninfo->vpn_netmask;
73
74         /* Clear old options which will be overwritten */
75         vpninfo->vpn_addr = vpninfo->vpn_netmask = NULL;
76         vpninfo->cstp_options = vpninfo->dtls_options = NULL;
77         for (i=0; i<3; i++)
78                 vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
79
80  retry:
81         openconnect_SSL_printf(vpninfo->https_ssl, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
82         openconnect_SSL_printf(vpninfo->https_ssl, "Host: %s\r\n", vpninfo->hostname);
83         openconnect_SSL_printf(vpninfo->https_ssl, "User-Agent: %s\r\n", vpninfo->useragent);
84         openconnect_SSL_printf(vpninfo->https_ssl, "Cookie: webvpn=%s\r\n", vpninfo->cookie);
85         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Version: 1\r\n");
86         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Hostname: %s\r\n", vpninfo->localname);
87         if (vpninfo->deflate)
88                 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
89         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-MTU: %d\r\n", vpninfo->mtu);
90         /* To enable IPv6, send 'IPv6,IPv4'.
91            We don't know how most of that works yet though. */
92         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Address-Type: IPv4\r\n");
93         openconnect_SSL_printf(vpninfo->https_ssl, "X-DTLS-Master-Secret: ");
94         for (i = 0; i < sizeof(vpninfo->dtls_secret); i++)
95                 openconnect_SSL_printf(vpninfo->https_ssl, "%02X", vpninfo->dtls_secret[i]);
96         openconnect_SSL_printf(vpninfo->https_ssl, "\r\nX-DTLS-CipherSuite: AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA\r\n\r\n");
97
98         if (openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536) < 0) {
99                 fprintf(stderr, "Error fetching HTTPS response\n");
100                 if (!retried) {
101                         retried = 1;
102                         SSL_free(vpninfo->https_ssl);
103                         close(vpninfo->ssl_fd);
104                 
105                         if (openconnect_open_https(vpninfo)) {
106                                 fprintf(stderr, "Failed to open HTTPS connection to %s\n",
107                                         vpninfo->hostname);
108                                 exit(1);
109                         }
110                         goto retry;
111                 }
112                 return -EINVAL;
113         }
114
115         if (strncmp(buf, "HTTP/1.1 200 ", 13)) {
116                 fprintf(stderr, "Got inappropriate HTTP CONNECT response: %s\n",
117                         buf);
118                 if (!strncmp(buf, "HTTP/1.1 401 ", 13))
119                         exit(2);
120                 openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536);
121                 return -EINVAL;
122         }
123
124         if (verbose)
125                 printf("Got CONNECT response: %s\n", buf);
126
127         /* We may have advertised it, but we only do it if the server agrees */
128         vpninfo->deflate = 0;
129
130         while ((i=openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
131                 struct vpn_option *new_option;
132                 char *colon = strchr(buf, ':');
133                 if (!colon)
134                         continue;
135
136                 *colon = 0;
137                 colon++;
138                 if (*colon == ' ')
139                         colon++;
140
141                 if (strncmp(buf, "X-DTLS-", 7) &&
142                     strncmp(buf, "X-CSTP-", 7))
143                         continue;
144
145                 new_option = malloc(sizeof(*new_option));
146                 if (!new_option) {
147                         fprintf(stderr, "No memory for options\n");
148                         return -ENOMEM;
149                 }
150                 new_option->option = strdup(buf);
151                 new_option->value = strdup(colon);
152                 new_option->next = NULL;
153
154                 if (!new_option->option || !new_option->value) {
155                         fprintf(stderr, "No memory for options\n");
156                         return -ENOMEM;
157                 }
158
159                 if (verbose)
160                         printf("%s: %s\n", buf, colon);
161
162                 if (!strncmp(buf, "X-DTLS-", 7)) {
163                         *next_dtls_option = new_option;
164                         next_dtls_option = &new_option->next;
165                         continue;
166                 }
167                 /* CSTP options... */
168                 *next_cstp_option = new_option;
169                 next_cstp_option = &new_option->next;
170
171
172                 if (!strcmp(buf + 7, "Keepalive")) {
173                         vpninfo->ssl_times.keepalive = atol(colon);
174                 } else if (!strcmp(buf + 7, "DPD")) {
175                         vpninfo->ssl_times.dpd = atol(colon);
176                 } else if (!strcmp(buf + 7, "Content-Encoding")) {
177                         if (!strcmp(colon, "deflate"))
178                                 vpninfo->deflate = 1;
179                         else {
180                                 fprintf(stderr, 
181                                         "Unknown CSTP-Content-Encoding %s\n",
182                                         colon);
183                                 return -EINVAL;
184                         }
185                 } else if (!strcmp(buf + 7, "MTU")) {
186                         vpninfo->mtu = atol(colon);
187                 } else if (!strcmp(buf + 7, "Address")) {
188                         vpninfo->vpn_addr = new_option->value;
189                 } else if (!strcmp(buf + 7, "Netmask")) {
190                         vpninfo->vpn_netmask = new_option->value;
191                 } else if (!strcmp(buf + 7, "DNS")) {
192                         int j;
193                         for (j = 0; j < 3; j++) {
194                                 if (!vpninfo->vpn_dns[j]) {
195                                         vpninfo->vpn_dns[j] = new_option->value;
196                                         break;
197                                 }
198                         }
199                 } else if (!strcmp(buf + 7, "NBNS")) {
200                         int j;
201                         for (j = 0; j < 3; j++) {
202                                 if (!vpninfo->vpn_nbns[j]) {
203                                         vpninfo->vpn_nbns[j] = new_option->value;
204                                         break;
205                                 }
206                         }
207                 } else if (!strcmp(buf + 7, "Default-Domain")) {
208                         vpninfo->vpn_domain = new_option->value;
209                 }
210         }
211
212         if (!vpninfo->vpn_addr) {
213                 fprintf(stderr, "No IP address received. Aborting\n");
214                 return -EINVAL;
215         }
216         if (!vpninfo->vpn_netmask)
217                 vpninfo->vpn_netmask = "255.255.255.255";
218         if (old_addr) {
219                 if (strcmp(old_addr, vpninfo->vpn_addr)) {
220                         fprintf(stderr, "Reconnect gave different IP address (%s != %s)\n",
221                                 vpninfo->vpn_addr, old_addr);
222                         return -EINVAL;
223                 }
224         }
225         if (old_netmask) {
226                 if (strcmp(old_netmask, vpninfo->vpn_netmask)) {
227                         fprintf(stderr, "Reconnect gave different netmask (%s != %s)\n",
228                                 vpninfo->vpn_netmask, old_netmask);
229                         return -EINVAL;
230                 }
231         }
232
233         while (old_dtls_opts) {
234                 struct vpn_option *tmp = old_dtls_opts;
235                 old_dtls_opts = old_dtls_opts->next;
236                 free(tmp->value);
237                 free(tmp->option);
238                 free(tmp);
239         }
240         while (old_cstp_opts) {
241                 struct vpn_option *tmp = old_cstp_opts;
242                 old_cstp_opts = old_cstp_opts->next;
243                 free(tmp->value);
244                 free(tmp->option);
245                 free(tmp);
246         }
247         if (verbose)
248                 printf("CSTP connected. DPD %d, Keepalive %d\n",
249                        vpninfo->ssl_times.dpd, vpninfo->ssl_times.keepalive);
250
251         BIO_set_nbio(SSL_get_rbio(vpninfo->https_ssl),1);
252         BIO_set_nbio(SSL_get_wbio(vpninfo->https_ssl),1);
253
254         fcntl(vpninfo->ssl_fd, F_SETFL, fcntl(vpninfo->ssl_fd, F_GETFL) | O_NONBLOCK);
255         vpninfo->ssl_pfd = vpn_add_pollfd(vpninfo, vpninfo->ssl_fd, POLLIN|POLLHUP|POLLERR);
256
257         vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL);
258         return 0;
259 }
260
261
262 int make_cstp_connection(struct openconnect_info *vpninfo)
263 {
264         if (!vpninfo->https_ssl && openconnect_open_https(vpninfo))
265                 exit(1);
266
267         if (vpninfo->deflate) {
268                 vpninfo->deflate_adler32 = 1;
269                 vpninfo->inflate_adler32 = 1;
270
271                 if (inflateInit2(&vpninfo->inflate_strm, -12) ||
272                     deflateInit2(&vpninfo->deflate_strm, Z_DEFAULT_COMPRESSION,
273                                  Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY)) {
274                         fprintf(stderr, "Compression setup failed\n");
275                         vpninfo->deflate = 0;
276                 }
277
278                 if (!vpninfo->deflate_pkt) {
279                         vpninfo->deflate_pkt = malloc(sizeof(struct pkt) + 2048);
280                         if (!vpninfo->deflate_pkt) {
281                                 fprintf(stderr, "Allocation of deflate buffer failed\n");
282                                 vpninfo->deflate = 0;
283                         }
284                         memset(vpninfo->deflate_pkt, 0, sizeof(struct pkt));
285                         memcpy(vpninfo->deflate_pkt->hdr, data_hdr, 8);
286                         vpninfo->deflate_pkt->hdr[6] = AC_PKT_COMPRESSED;
287                 }
288         }
289
290         if (start_cstp_connection(vpninfo))
291                 return -EINVAL;
292
293         return 0;
294 }
295
296
297 static int inflate_and_queue_packet(struct openconnect_info *vpninfo, int type, void *buf, int len)
298 {
299         struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->mtu);
300
301         if (!new)
302                 return -ENOMEM;
303
304         new->type = type;
305         new->next = NULL;
306
307         vpninfo->inflate_strm.next_in = buf;
308         vpninfo->inflate_strm.avail_in = len - 4;
309
310         vpninfo->inflate_strm.next_out = new->data;
311         vpninfo->inflate_strm.avail_out = vpninfo->mtu;
312         vpninfo->inflate_strm.total_out = 0;
313
314         if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
315                 fprintf(stderr, "inflate failed\n");
316                 free(new);
317                 return -EINVAL;
318         }
319
320         new->len = vpninfo->inflate_strm.total_out;
321
322         vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
323                                            new->data, new->len);
324
325         if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *)(buf + len - 4))) {
326                 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
327         }
328
329         if (verbose) {
330                 printf("Received compressed data packet of %ld bytes\n",
331                        vpninfo->inflate_strm.total_out);
332         }
333
334         queue_packet(&vpninfo->incoming_queue, new);
335         return 0;
336 }
337
338 int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
339 {
340         unsigned char buf[16384];
341         int len, ret;
342         int work_done = 0;
343
344         /* FIXME: The poll() handling here is fairly simplistic. Actually,
345            if the SSL connection stalls it could return a WANT_WRITE error
346            on _either_ of the SSL_read() or SSL_write() calls. In that case,
347            we should probably remove POLLIN from the events we're looking for,
348            and add POLLOUT. As it is, though, it'll just chew CPU time in that
349            fairly unlikely situation, until the write backlog clears. */
350         while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
351                 int payload_len;
352
353                 if (buf[0] != 'S' || buf[1] != 'T' ||
354                     buf[2] != 'F' || buf[3] != 1 || buf[7])
355                         goto unknown_pkt;
356
357                 payload_len = (buf[4] << 8) + buf[5];
358                 if (len != 8 + payload_len) {
359                         printf("Unexpected packet length. SSL_read returned %d but packet is\n",
360                                len);
361                         printf("%02x %02x %02x %02x %02x %02x %02x %02x\n",
362                                buf[0], buf[1], buf[2], buf[3],
363                                buf[4], buf[5], buf[6], buf[7]);
364                         continue;
365                 }
366                 vpninfo->ssl_times.last_rx = time(NULL);
367                 switch(buf[6]) {
368                 case AC_PKT_DPD_OUT:
369                         if (verbose)
370                                 printf("Got CSTP DPD request\n");
371                         vpninfo->owe_ssl_dpd_response = 1;
372                         continue;
373
374                 case AC_PKT_DPD_RESP:
375                         if (verbose)
376                                 printf("Got CSTP DPD response\n");
377                         continue;
378
379                 case AC_PKT_KEEPALIVE:
380                         if (verbose)
381                                 printf("Got CSTP Keepalive\n");
382                         continue;
383
384                 case AC_PKT_DATA:
385                         if (verbose) {
386                                 printf("Received uncompressed data packet of %d bytes\n",
387                                        payload_len);
388                         }
389                         queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
390                                          payload_len);
391                         work_done = 1;
392                         continue;
393
394                 case AC_PKT_COMPRESSED:
395                         if (!vpninfo->deflate) {
396                                 fprintf(stderr, "Compressed packet received in !deflate mode\n");
397                                 goto unknown_pkt;
398                         }
399                         inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, payload_len);
400                         work_done = 1;
401                         continue;
402
403                 case AC_PKT_TERM_SERVER:
404                         fprintf(stderr, "received server terminate packet\n");
405                         vpninfo->quit_reason = "Server request";
406                         return 1;
407                 }
408
409         unknown_pkt:
410                 printf("Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
411                        buf[0], buf[1], buf[2], buf[3],
412                        buf[4], buf[5], buf[6], buf[7]);
413                 vpninfo->quit_reason = "Unknown packet received";
414                 return 1;
415         }
416
417
418         /* If SSL_write() fails we are expected to try again. With exactly
419            the same data, at exactly the same location. So we keep the 
420            packet we had before.... */
421         if (vpninfo->current_ssl_pkt) {
422         handle_outgoing:
423                 vpninfo->ssl_times.last_tx = time(NULL);
424                 vpninfo->pfds[vpninfo->ssl_pfd].events &= ~POLLOUT;
425                 ret = SSL_write(vpninfo->https_ssl,
426                                 vpninfo->current_ssl_pkt->hdr,
427                                 vpninfo->current_ssl_pkt->len + 8);
428                 if (ret <= 0) {
429                         ret = SSL_get_error(vpninfo->https_ssl, ret);
430                         switch (ret) {
431                         case SSL_ERROR_WANT_WRITE:
432                                 /* Waiting for the socket to become writable -- it's
433                                    probably stalled, and/or the buffers are full */
434                                 vpninfo->pfds[vpninfo->ssl_pfd].events |= POLLOUT;
435                         case SSL_ERROR_WANT_READ:
436                                 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
437                                         goto peer_dead;
438                                 return work_done;
439                         default:
440                                 fprintf(stderr, "SSL_write failed: %d", ret);
441                                 ERR_print_errors_fp(stderr);
442                                 vpninfo->quit_reason = "SSL write error";
443                                 return 1;
444                         }
445                 }
446                 if (ret != vpninfo->current_ssl_pkt->len + 8) {
447                         fprintf(stderr, "SSL wrote too few bytes! Asked for %d, sent %d\n",
448                                 vpninfo->current_ssl_pkt->len + 8, ret);
449                         vpninfo->quit_reason = "Internal error";
450                         return 1;
451                 }
452                 /* Don't free the 'special' packets */
453                 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
454                     vpninfo->current_ssl_pkt != &dpd_pkt &&
455                     vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
456                     vpninfo->current_ssl_pkt != &keepalive_pkt)
457                         free(vpninfo->current_ssl_pkt);
458
459                 vpninfo->current_ssl_pkt = NULL;
460         }
461
462         if (vpninfo->owe_ssl_dpd_response) {
463                 vpninfo->owe_ssl_dpd_response = 0;
464                 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
465                 goto handle_outgoing;
466         }
467
468         switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
469         case KA_REKEY:
470                 /* Not that this will ever happen; we don't even process
471                    the setting when we're asked for it. */
472                 fprintf(stderr, "CSTP rekey due but we don't know how\n");
473                 time(&vpninfo->ssl_times.last_rekey);
474                 work_done = 1;
475                 break;
476
477         case KA_DPD_DEAD:
478         peer_dead:
479                 fprintf(stderr, "CSTP Dead Peer Detection detected dead peer!\n");
480                 SSL_free(vpninfo->https_ssl);
481                 vpninfo->https_ssl = NULL;
482                 close(vpninfo->ssl_fd);
483
484                 /* It's already deflated in the old stream. Extremely 
485                    non-trivial to reconstitute it; just throw it away */
486                 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
487                         vpninfo->current_ssl_pkt = NULL;
488
489                 if (make_cstp_connection(vpninfo)) {
490                         fprintf(stderr, "Reconnect failed\n");
491                         vpninfo->quit_reason = "SSL DPD detected dead peer; reconnect failed";
492                         return 1;
493                 }
494                 /* I think we can leave DTLS to its own devices; when we reconnect
495                    with the same master secret, we do seem to get the same sessid */
496                 return 1;
497
498         case KA_DPD:
499                 if (verbose)
500                         printf("Send CSTP DPD\n");
501
502                 vpninfo->current_ssl_pkt = &dpd_pkt;
503                 goto handle_outgoing;
504
505         case KA_KEEPALIVE:
506                 /* No need to send an explicit keepalive
507                    if we have real data to send */
508                 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
509                         break;
510
511                 if (verbose)
512                         printf("Send CSTP Keepalive\n");
513
514                 vpninfo->current_ssl_pkt = &keepalive_pkt;
515                 goto handle_outgoing;
516
517         case KA_NONE:
518                 ;
519         }
520
521         /* Service outgoing packet queue, if no DTLS */
522         while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
523                 struct pkt *this = vpninfo->outgoing_queue;
524                 vpninfo->outgoing_queue = this->next;
525
526                 if (vpninfo->deflate) {
527                         unsigned char *adler;
528                         int ret;
529
530                         vpninfo->deflate_strm.next_in = this->data;
531                         vpninfo->deflate_strm.avail_in = this->len;
532                         vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
533                         vpninfo->deflate_strm.avail_out = 2040;
534                         vpninfo->deflate_strm.total_out = 0;
535
536                         ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
537                         if (ret) {
538                                 fprintf(stderr, "deflate failed %d\n", ret);
539                                 goto uncompr;
540                         }
541
542                         vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
543                         vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
544
545                         /* Add ongoing adler32 to tail of compressed packet */
546                         vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
547                                                            this->data, this->len);
548
549                         adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
550                         *(adler++) =  vpninfo->deflate_adler32 >> 24;
551                         *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
552                         *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
553                         *(adler)   =  vpninfo->deflate_adler32 & 0xff;
554
555                         vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
556
557                         if (verbose) {
558                                 printf("Sending compressed data packet of %d bytes\n",
559                                        this->len);
560                         }
561                         vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
562                 } else {
563                 uncompr:
564                         memcpy(this->hdr, data_hdr, 8);
565                         this->hdr[4] = this->len >> 8;
566                         this->hdr[5] = this->len & 0xff;
567
568                         if (verbose) {
569                                 printf("Sending uncompressed data packet of %d bytes\n",
570                                        this->len);
571                         }
572                         vpninfo->current_ssl_pkt = this;
573                 }
574                 goto handle_outgoing;
575         }
576
577         /* Work is not done if we just got rid of packets off the queue */
578         return work_done;
579 }
580
581 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
582 {
583         unsigned char *bye_pkt;
584         int reason_len = strlen(reason);
585         bye_pkt = malloc(reason_len + 8);
586         if (!bye_pkt)
587                 return -ENOMEM;
588         
589         memcpy(bye_pkt, data_hdr, 8);
590         memcpy(bye_pkt + 8, reason, strlen(reason));
591
592         bye_pkt[4] = reason_len >> 8;
593         bye_pkt[5] = reason_len & 0xff;
594         bye_pkt[6] = 5;
595
596         SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
597         free(bye_pkt);
598
599         if (verbose)
600                 printf("Send BYE packet: %s\n", reason);
601
602         return 0;
603 }