2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008 Intel Corporation.
5 * Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
7 * Author: David Woodhouse <dwmw2@infradead.org>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * version 2.1, as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to:
21 * Free Software Foundation, Inc.
22 * 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301 USA
31 #include <openssl/ssl.h>
32 #include <openssl/err.h>
34 #include "openconnect.h"
37 * Data packets are encapsulated in the SSL stream as follows:
39 * 0000: Magic "STF\x1"
40 * 0004: Big-endian 16-bit length (not including 8-byte header)
41 * 0006: Byte packet type (see openconnect.h)
45 static char data_hdr[8] = {
48 AC_PKT_DATA, /* Type */
52 static struct pkt keepalive_pkt = {
53 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_KEEPALIVE, 0 },
56 static struct pkt dpd_pkt = {
57 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_OUT, 0 },
60 static struct pkt dpd_resp_pkt = {
61 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_RESP, 0 },
65 static int start_cstp_connection(struct openconnect_info *vpninfo)
70 struct vpn_option **next_dtls_option = &vpninfo->dtls_options;
71 struct vpn_option **next_cstp_option = &vpninfo->cstp_options;
72 struct vpn_option *old_cstp_opts = vpninfo->cstp_options;
73 struct vpn_option *old_dtls_opts = vpninfo->dtls_options;
74 const char *old_addr = vpninfo->vpn_addr;
75 const char *old_netmask = vpninfo->vpn_netmask;
76 struct split_include *inc;
78 /* Clear old options which will be overwritten */
79 vpninfo->vpn_addr = vpninfo->vpn_netmask = NULL;
80 vpninfo->cstp_options = vpninfo->dtls_options = NULL;
81 vpninfo->vpn_domain = vpninfo->vpn_proxy_pac = NULL;
84 vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
86 for (inc = vpninfo->split_includes; inc; inc = inc->next) {
87 struct split_include *next = inc->next;
91 for (inc = vpninfo->split_excludes; inc; inc = inc->next) {
92 struct split_include *next = inc->next;
97 openconnect_SSL_printf(vpninfo->https_ssl, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
98 openconnect_SSL_printf(vpninfo->https_ssl, "Host: %s\r\n", vpninfo->hostname);
99 openconnect_SSL_printf(vpninfo->https_ssl, "User-Agent: %s\r\n", vpninfo->useragent);
100 openconnect_SSL_printf(vpninfo->https_ssl, "Cookie: webvpn=%s\r\n", vpninfo->cookie);
101 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Version: 1\r\n");
102 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Hostname: %s\r\n", vpninfo->localname);
103 if (vpninfo->deflate)
104 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
105 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-MTU: %d\r\n", vpninfo->mtu);
106 /* To enable IPv6, send 'IPv6,IPv4'.
107 We don't know how most of that works yet though. */
108 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Address-Type: IPv4\r\n");
109 openconnect_SSL_printf(vpninfo->https_ssl, "X-DTLS-Master-Secret: ");
110 for (i = 0; i < sizeof(vpninfo->dtls_secret); i++)
111 openconnect_SSL_printf(vpninfo->https_ssl, "%02X", vpninfo->dtls_secret[i]);
112 openconnect_SSL_printf(vpninfo->https_ssl, "\r\nX-DTLS-CipherSuite: %s\r\n\r\n",
113 vpninfo->dtls_ciphers?:"AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA");
115 if (openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536) < 0) {
116 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching HTTPS response\n");
119 openconnect_close_https(vpninfo);
121 if (openconnect_open_https(vpninfo)) {
122 vpninfo->progress(vpninfo, PRG_ERR,
123 "Failed to open HTTPS connection to %s\n",
132 if (strncmp(buf, "HTTP/1.1 200 ", 13)) {
133 if (!strncmp(buf, "HTTP/1.1 503 ", 13)) {
134 /* "Service Unavailable. Why? */
135 char *reason = "<unknown>";
136 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
137 if (!strncmp(buf, "X-Reason: ", 10)) {
142 vpninfo->progress(vpninfo, PRG_ERR, "VPN service unavailable; reason: %s\n",
146 vpninfo->progress(vpninfo, PRG_ERR,
147 "Got inappropriate HTTP CONNECT response: %s\n",
149 if (!strncmp(buf, "HTTP/1.1 401 ", 13))
154 vpninfo->progress(vpninfo, PRG_INFO,
155 "Got CONNECT response: %s\n", buf);
157 /* We may have advertised it, but we only do it if the server agrees */
158 vpninfo->deflate = 0;
160 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
161 struct vpn_option *new_option;
162 char *colon = strchr(buf, ':');
171 if (strncmp(buf, "X-DTLS-", 7) &&
172 strncmp(buf, "X-CSTP-", 7))
175 new_option = malloc(sizeof(*new_option));
177 vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
180 new_option->option = strdup(buf);
181 new_option->value = strdup(colon);
182 new_option->next = NULL;
184 if (!new_option->option || !new_option->value) {
185 vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
189 vpninfo->progress(vpninfo, PRG_TRACE, "%s: %s\n", buf, colon);
191 if (!strncmp(buf, "X-DTLS-", 7)) {
192 *next_dtls_option = new_option;
193 next_dtls_option = &new_option->next;
196 /* CSTP options... */
197 *next_cstp_option = new_option;
198 next_cstp_option = &new_option->next;
201 if (!strcmp(buf + 7, "Keepalive")) {
202 vpninfo->ssl_times.keepalive = atol(colon);
203 } else if (!strcmp(buf + 7, "DPD")) {
204 vpninfo->ssl_times.dpd = atol(colon);
205 } else if (!strcmp(buf + 7, "Content-Encoding")) {
206 if (!strcmp(colon, "deflate"))
207 vpninfo->deflate = 1;
209 vpninfo->progress(vpninfo, PRG_ERR,
210 "Unknown CSTP-Content-Encoding %s\n",
214 } else if (!strcmp(buf + 7, "MTU")) {
215 vpninfo->mtu = atol(colon);
216 } else if (!strcmp(buf + 7, "Address")) {
217 vpninfo->vpn_addr = new_option->value;
218 } else if (!strcmp(buf + 7, "Netmask")) {
219 vpninfo->vpn_netmask = new_option->value;
220 } else if (!strcmp(buf + 7, "DNS")) {
222 for (j = 0; j < 3; j++) {
223 if (!vpninfo->vpn_dns[j]) {
224 vpninfo->vpn_dns[j] = new_option->value;
228 } else if (!strcmp(buf + 7, "NBNS")) {
230 for (j = 0; j < 3; j++) {
231 if (!vpninfo->vpn_nbns[j]) {
232 vpninfo->vpn_nbns[j] = new_option->value;
236 } else if (!strcmp(buf + 7, "Default-Domain")) {
237 vpninfo->vpn_domain = new_option->value;
238 } else if (!strcmp(buf + 7, "MSIE-Proxy-PAC-URL")) {
239 vpninfo->vpn_proxy_pac = new_option->value;
240 } else if (!strcmp(buf + 7, "Split-Include")) {
241 struct split_include *inc = malloc(sizeof(*inc));
244 inc->route = new_option->value;
245 inc->next = vpninfo->split_includes;
246 vpninfo->split_includes = inc;
247 } else if (!strcmp(buf + 7, "Split-Exclude")) {
248 struct split_include *exc = malloc(sizeof(*exc));
251 exc->route = new_option->value;
252 exc->next = vpninfo->split_includes;
253 vpninfo->split_excludes = exc;
257 if (!vpninfo->vpn_addr) {
258 vpninfo->progress(vpninfo, PRG_ERR, "No IP address received. Aborting\n");
261 if (!vpninfo->vpn_netmask)
262 vpninfo->vpn_netmask = "255.255.255.255";
264 if (strcmp(old_addr, vpninfo->vpn_addr)) {
265 vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different IP address (%s != %s)\n",
266 vpninfo->vpn_addr, old_addr);
271 if (strcmp(old_netmask, vpninfo->vpn_netmask)) {
272 vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different netmask (%s != %s)\n",
273 vpninfo->vpn_netmask, old_netmask);
278 free(vpninfo->dtls_cipher);
279 vpninfo->dtls_cipher = NULL;
281 while (old_dtls_opts) {
282 struct vpn_option *tmp = old_dtls_opts;
283 old_dtls_opts = old_dtls_opts->next;
288 while (old_cstp_opts) {
289 struct vpn_option *tmp = old_cstp_opts;
290 old_cstp_opts = old_cstp_opts->next;
295 vpninfo->progress(vpninfo, PRG_INFO, "CSTP connected. DPD %d, Keepalive %d\n",
296 vpninfo->ssl_times.dpd, vpninfo->ssl_times.keepalive);
298 BIO_set_nbio(SSL_get_rbio(vpninfo->https_ssl), 1);
299 BIO_set_nbio(SSL_get_wbio(vpninfo->https_ssl), 1);
301 fcntl(vpninfo->ssl_fd, F_SETFL, fcntl(vpninfo->ssl_fd, F_GETFL) | O_NONBLOCK);
302 if (vpninfo->select_nfds <= vpninfo->ssl_fd)
303 vpninfo->select_nfds = vpninfo->ssl_fd + 1;
305 FD_SET(vpninfo->ssl_fd, &vpninfo->select_rfds);
306 FD_SET(vpninfo->ssl_fd, &vpninfo->select_efds);
308 vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL);
313 int make_cstp_connection(struct openconnect_info *vpninfo)
317 if (!vpninfo->https_ssl && (ret = openconnect_open_https(vpninfo)))
320 if (vpninfo->deflate) {
321 vpninfo->deflate_adler32 = 1;
322 vpninfo->inflate_adler32 = 1;
324 if (inflateInit2(&vpninfo->inflate_strm, -12) ||
325 deflateInit2(&vpninfo->deflate_strm, Z_DEFAULT_COMPRESSION,
326 Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY)) {
327 vpninfo->progress(vpninfo, PRG_ERR, "Compression setup failed\n");
328 vpninfo->deflate = 0;
331 if (!vpninfo->deflate_pkt) {
332 vpninfo->deflate_pkt = malloc(sizeof(struct pkt) + 2048);
333 if (!vpninfo->deflate_pkt) {
334 vpninfo->progress(vpninfo, PRG_ERR, "Allocation of deflate buffer failed\n");
335 vpninfo->deflate = 0;
337 memset(vpninfo->deflate_pkt, 0, sizeof(struct pkt));
338 memcpy(vpninfo->deflate_pkt->hdr, data_hdr, 8);
339 vpninfo->deflate_pkt->hdr[6] = AC_PKT_COMPRESSED;
343 return start_cstp_connection(vpninfo);
346 static int cstp_reconnect(struct openconnect_info *vpninfo)
352 timeout = vpninfo->reconnect_timeout;
353 interval = vpninfo->reconnect_interval;
355 while ((ret = make_cstp_connection(vpninfo))) {
358 vpninfo->progress(vpninfo, PRG_INFO,
359 "sleep %ds, remaining timeout %ds\n",
365 interval += vpninfo->reconnect_interval;
366 if (interval > RECONNECT_INTERVAL_MAX)
367 interval = RECONNECT_INTERVAL_MAX;
372 static int inflate_and_queue_packet(struct openconnect_info *vpninfo, int type, void *buf, int len)
374 struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->mtu);
382 vpninfo->inflate_strm.next_in = buf;
383 vpninfo->inflate_strm.avail_in = len - 4;
385 vpninfo->inflate_strm.next_out = new->data;
386 vpninfo->inflate_strm.avail_out = vpninfo->mtu;
387 vpninfo->inflate_strm.total_out = 0;
389 if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
390 vpninfo->progress(vpninfo, PRG_ERR, "inflate failed\n");
395 new->len = vpninfo->inflate_strm.total_out;
397 vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
398 new->data, new->len);
400 if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *) (buf + len - 4) )) {
401 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
404 vpninfo->progress(vpninfo, PRG_TRACE,
405 "Received compressed data packet of %ld bytes\n",
406 vpninfo->inflate_strm.total_out);
408 queue_packet(&vpninfo->incoming_queue, new);
412 int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
414 unsigned char buf[16384];
418 /* FIXME: The poll() handling here is fairly simplistic. Actually,
419 if the SSL connection stalls it could return a WANT_WRITE error
420 on _either_ of the SSL_read() or SSL_write() calls. In that case,
421 we should probably remove POLLIN from the events we're looking for,
422 and add POLLOUT. As it is, though, it'll just chew CPU time in that
423 fairly unlikely situation, until the write backlog clears. */
424 while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
427 if (buf[0] != 'S' || buf[1] != 'T' ||
428 buf[2] != 'F' || buf[3] != 1 || buf[7])
431 payload_len = (buf[4] << 8) + buf[5];
432 if (len != 8 + payload_len) {
433 vpninfo->progress(vpninfo, PRG_ERR,
434 "Unexpected packet length. SSL_read returned %d but packet is\n",
436 vpninfo->progress(vpninfo, PRG_ERR,
437 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
438 buf[0], buf[1], buf[2], buf[3],
439 buf[4], buf[5], buf[6], buf[7]);
442 vpninfo->ssl_times.last_rx = time(NULL);
445 vpninfo->progress(vpninfo, PRG_TRACE,
446 "Got CSTP DPD request\n");
447 vpninfo->owe_ssl_dpd_response = 1;
450 case AC_PKT_DPD_RESP:
451 vpninfo->progress(vpninfo, PRG_TRACE,
452 "Got CSTP DPD response\n");
455 case AC_PKT_KEEPALIVE:
456 vpninfo->progress(vpninfo, PRG_TRACE,
457 "Got CSTP Keepalive\n");
461 vpninfo->progress(vpninfo, PRG_TRACE,
462 "Received uncompressed data packet of %d bytes\n",
464 queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
469 case AC_PKT_DISCONN: {
471 for (i = 0; i < payload_len; i++) {
472 if (!isprint(buf[payload_len + 8 + i]))
473 buf[payload_len + 8 + i] = '.';
475 buf[payload_len + 8] = 0;
476 vpninfo->progress(vpninfo, PRG_ERR,
477 "Received server disconnect: %02x '%s'\n", buf[8], buf + 9);
478 vpninfo->quit_reason = "Server request";
481 case AC_PKT_COMPRESSED:
482 if (!vpninfo->deflate) {
483 vpninfo->progress(vpninfo, PRG_ERR, "Compressed packet received in !deflate mode\n");
486 inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, payload_len);
490 case AC_PKT_TERM_SERVER:
491 vpninfo->progress(vpninfo, PRG_ERR, "received server terminate packet\n");
492 vpninfo->quit_reason = "Server request";
497 vpninfo->progress(vpninfo, PRG_ERR,
498 "Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
499 buf[0], buf[1], buf[2], buf[3],
500 buf[4], buf[5], buf[6], buf[7]);
501 vpninfo->quit_reason = "Unknown packet received";
506 /* If SSL_write() fails we are expected to try again. With exactly
507 the same data, at exactly the same location. So we keep the
508 packet we had before.... */
509 if (vpninfo->current_ssl_pkt) {
511 vpninfo->ssl_times.last_tx = time(NULL);
512 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
513 ret = SSL_write(vpninfo->https_ssl,
514 vpninfo->current_ssl_pkt->hdr,
515 vpninfo->current_ssl_pkt->len + 8);
517 ret = SSL_get_error(vpninfo->https_ssl, ret);
519 case SSL_ERROR_WANT_WRITE:
520 /* Waiting for the socket to become writable -- it's
521 probably stalled, and/or the buffers are full */
522 FD_SET(vpninfo->ssl_fd, &vpninfo->select_wfds);
524 case SSL_ERROR_WANT_READ:
525 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
529 vpninfo->progress(vpninfo, PRG_ERR, "SSL_write failed: %d", ret);
530 ERR_print_errors_fp(stderr);
531 vpninfo->quit_reason = "SSL write error";
535 if (ret != vpninfo->current_ssl_pkt->len + 8) {
536 vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
537 vpninfo->current_ssl_pkt->len + 8, ret);
538 vpninfo->quit_reason = "Internal error";
541 /* Don't free the 'special' packets */
542 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
543 vpninfo->current_ssl_pkt != &dpd_pkt &&
544 vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
545 vpninfo->current_ssl_pkt != &keepalive_pkt)
546 free(vpninfo->current_ssl_pkt);
548 vpninfo->current_ssl_pkt = NULL;
551 if (vpninfo->owe_ssl_dpd_response) {
552 vpninfo->owe_ssl_dpd_response = 0;
553 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
554 goto handle_outgoing;
557 switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
559 /* Not that this will ever happen; we don't even process
560 the setting when we're asked for it. */
561 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
562 time(&vpninfo->ssl_times.last_rekey);
568 vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
569 openconnect_close_https(vpninfo);
571 /* It's already deflated in the old stream. Extremely
572 non-trivial to reconstitute it; just throw it away */
573 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
574 vpninfo->current_ssl_pkt = NULL;
576 if (cstp_reconnect(vpninfo)) {
577 vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
578 vpninfo->quit_reason = "SSL DPD detected dead peer; reconnect failed";
581 /* I think we can leave DTLS to its own devices; when we reconnect
582 with the same master secret, we do seem to get the same sessid */
586 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP DPD\n");
588 vpninfo->current_ssl_pkt = &dpd_pkt;
589 goto handle_outgoing;
592 /* No need to send an explicit keepalive
593 if we have real data to send */
594 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
597 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP Keepalive\n");
599 vpninfo->current_ssl_pkt = &keepalive_pkt;
600 goto handle_outgoing;
606 /* Service outgoing packet queue, if no DTLS */
607 while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
608 struct pkt *this = vpninfo->outgoing_queue;
609 vpninfo->outgoing_queue = this->next;
610 vpninfo->outgoing_qlen--;
612 if (vpninfo->deflate) {
613 unsigned char *adler;
616 vpninfo->deflate_strm.next_in = this->data;
617 vpninfo->deflate_strm.avail_in = this->len;
618 vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
619 vpninfo->deflate_strm.avail_out = 2040;
620 vpninfo->deflate_strm.total_out = 0;
622 ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
624 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
628 vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
629 vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
631 /* Add ongoing adler32 to tail of compressed packet */
632 vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
633 this->data, this->len);
635 adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
636 *(adler++) = vpninfo->deflate_adler32 >> 24;
637 *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
638 *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
639 *(adler) = vpninfo->deflate_adler32 & 0xff;
641 vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
643 vpninfo->progress(vpninfo, PRG_TRACE,
644 "Sending compressed data packet of %d bytes\n",
647 vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
650 memcpy(this->hdr, data_hdr, 8);
651 this->hdr[4] = this->len >> 8;
652 this->hdr[5] = this->len & 0xff;
654 vpninfo->progress(vpninfo, PRG_TRACE,
655 "Sending uncompressed data packet of %d bytes\n",
658 vpninfo->current_ssl_pkt = this;
660 goto handle_outgoing;
663 /* Work is not done if we just got rid of packets off the queue */
667 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
669 unsigned char *bye_pkt;
672 /* already lost connection? */
673 if (!vpninfo->https_ssl)
676 reason_len = strlen(reason);
677 bye_pkt = malloc(reason_len + 8);
681 memcpy(bye_pkt, data_hdr, 8);
682 memcpy(bye_pkt + 8, reason, reason_len);
684 bye_pkt[4] = reason_len >> 8;
685 bye_pkt[5] = reason_len & 0xff;
686 bye_pkt[6] = AC_PKT_DISCONN;
688 SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
691 vpninfo->progress(vpninfo, PRG_INFO,
692 "Send BYE packet: %s\n", reason);