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
32 #include <openssl/ssl.h>
33 #include <openssl/err.h>
35 #include "openconnect.h"
38 * Data packets are encapsulated in the SSL stream as follows:
40 * 0000: Magic "STF\x1"
41 * 0004: Big-endian 16-bit length (not including 8-byte header)
42 * 0006: Byte packet type (see openconnect.h)
46 static char data_hdr[8] = {
49 AC_PKT_DATA, /* Type */
53 static struct pkt keepalive_pkt = {
54 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_KEEPALIVE, 0 },
57 static struct pkt dpd_pkt = {
58 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_OUT, 0 },
61 static struct pkt dpd_resp_pkt = {
62 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_RESP, 0 },
66 static int start_cstp_connection(struct openconnect_info *vpninfo)
71 struct vpn_option **next_dtls_option = &vpninfo->dtls_options;
72 struct vpn_option **next_cstp_option = &vpninfo->cstp_options;
73 struct vpn_option *old_cstp_opts = vpninfo->cstp_options;
74 struct vpn_option *old_dtls_opts = vpninfo->dtls_options;
75 const char *old_addr = vpninfo->vpn_addr;
76 const char *old_netmask = vpninfo->vpn_netmask;
77 struct split_include *inc;
79 /* Clear old options which will be overwritten */
80 vpninfo->vpn_addr = vpninfo->vpn_netmask = NULL;
81 vpninfo->cstp_options = vpninfo->dtls_options = NULL;
82 vpninfo->vpn_domain = vpninfo->vpn_proxy_pac = NULL;
83 vpninfo->dtls_cipher = NULL;
86 vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
88 for (inc = vpninfo->split_includes; inc; ) {
89 struct split_include *next = inc->next;
93 for (inc = vpninfo->split_excludes; inc; ) {
94 struct split_include *next = inc->next;
98 vpninfo->split_includes = vpninfo->split_excludes = NULL;
100 openconnect_SSL_printf(vpninfo->https_ssl, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
101 openconnect_SSL_printf(vpninfo->https_ssl, "Host: %s\r\n", vpninfo->hostname);
102 openconnect_SSL_printf(vpninfo->https_ssl, "User-Agent: %s\r\n", vpninfo->useragent);
103 openconnect_SSL_printf(vpninfo->https_ssl, "Cookie: webvpn=%s\r\n", vpninfo->cookie);
104 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Version: 1\r\n");
105 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Hostname: %s\r\n", vpninfo->localname);
106 if (vpninfo->deflate)
107 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
108 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-MTU: %d\r\n", vpninfo->mtu);
109 /* To enable IPv6, send 'IPv6,IPv4'.
110 We don't know how most of that works yet though. */
111 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Address-Type: IPv4\r\n");
112 openconnect_SSL_printf(vpninfo->https_ssl, "X-DTLS-Master-Secret: ");
113 for (i = 0; i < sizeof(vpninfo->dtls_secret); i++)
114 openconnect_SSL_printf(vpninfo->https_ssl, "%02X", vpninfo->dtls_secret[i]);
115 openconnect_SSL_printf(vpninfo->https_ssl, "\r\nX-DTLS-CipherSuite: %s\r\n\r\n",
116 vpninfo->dtls_ciphers?:"AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA");
118 if (openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536) < 0) {
119 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching HTTPS response\n");
122 openconnect_close_https(vpninfo);
124 if (openconnect_open_https(vpninfo)) {
125 vpninfo->progress(vpninfo, PRG_ERR,
126 "Failed to open HTTPS connection to %s\n",
135 if (strncmp(buf, "HTTP/1.1 200 ", 13)) {
136 if (!strncmp(buf, "HTTP/1.1 503 ", 13)) {
137 /* "Service Unavailable. Why? */
138 char *reason = "<unknown>";
139 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
140 if (!strncmp(buf, "X-Reason: ", 10)) {
145 vpninfo->progress(vpninfo, PRG_ERR, "VPN service unavailable; reason: %s\n",
149 vpninfo->progress(vpninfo, PRG_ERR,
150 "Got inappropriate HTTP CONNECT response: %s\n",
152 if (!strncmp(buf, "HTTP/1.1 401 ", 13))
157 vpninfo->progress(vpninfo, PRG_INFO,
158 "Got CONNECT response: %s\n", buf);
160 /* We may have advertised it, but we only do it if the server agrees */
161 vpninfo->deflate = 0;
163 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
164 struct vpn_option *new_option;
165 char *colon = strchr(buf, ':');
174 if (strncmp(buf, "X-DTLS-", 7) &&
175 strncmp(buf, "X-CSTP-", 7))
178 new_option = malloc(sizeof(*new_option));
180 vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
183 new_option->option = strdup(buf);
184 new_option->value = strdup(colon);
185 new_option->next = NULL;
187 if (!new_option->option || !new_option->value) {
188 vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
192 vpninfo->progress(vpninfo, PRG_TRACE, "%s: %s\n", buf, colon);
194 if (!strncmp(buf, "X-DTLS-", 7)) {
195 *next_dtls_option = new_option;
196 next_dtls_option = &new_option->next;
199 /* CSTP options... */
200 *next_cstp_option = new_option;
201 next_cstp_option = &new_option->next;
204 if (!strcmp(buf + 7, "Keepalive")) {
205 vpninfo->ssl_times.keepalive = atol(colon);
206 } else if (!strcmp(buf + 7, "DPD")) {
207 vpninfo->ssl_times.dpd = atol(colon);
208 } else if (!strcmp(buf + 7, "Content-Encoding")) {
209 if (!strcmp(colon, "deflate"))
210 vpninfo->deflate = 1;
212 vpninfo->progress(vpninfo, PRG_ERR,
213 "Unknown CSTP-Content-Encoding %s\n",
217 } else if (!strcmp(buf + 7, "MTU")) {
218 vpninfo->mtu = atol(colon);
219 } else if (!strcmp(buf + 7, "Address")) {
220 vpninfo->vpn_addr = new_option->value;
221 } else if (!strcmp(buf + 7, "Netmask")) {
222 vpninfo->vpn_netmask = new_option->value;
223 } else if (!strcmp(buf + 7, "DNS")) {
225 for (j = 0; j < 3; j++) {
226 if (!vpninfo->vpn_dns[j]) {
227 vpninfo->vpn_dns[j] = new_option->value;
231 } else if (!strcmp(buf + 7, "NBNS")) {
233 for (j = 0; j < 3; j++) {
234 if (!vpninfo->vpn_nbns[j]) {
235 vpninfo->vpn_nbns[j] = new_option->value;
239 } else if (!strcmp(buf + 7, "Default-Domain")) {
240 vpninfo->vpn_domain = new_option->value;
241 } else if (!strcmp(buf + 7, "MSIE-Proxy-PAC-URL")) {
242 vpninfo->vpn_proxy_pac = new_option->value;
243 } else if (!strcmp(buf + 7, "Split-Include")) {
244 struct split_include *inc = malloc(sizeof(*inc));
247 inc->route = new_option->value;
248 inc->next = vpninfo->split_includes;
249 vpninfo->split_includes = inc;
250 } else if (!strcmp(buf + 7, "Split-Exclude")) {
251 struct split_include *exc = malloc(sizeof(*exc));
254 exc->route = new_option->value;
255 exc->next = vpninfo->split_excludes;
256 vpninfo->split_excludes = exc;
260 if (!vpninfo->vpn_addr) {
261 vpninfo->progress(vpninfo, PRG_ERR, "No IP address received. Aborting\n");
264 if (!vpninfo->vpn_netmask)
265 vpninfo->vpn_netmask = "255.255.255.255";
267 if (strcmp(old_addr, vpninfo->vpn_addr)) {
268 vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different IP address (%s != %s)\n",
269 vpninfo->vpn_addr, old_addr);
274 if (strcmp(old_netmask, vpninfo->vpn_netmask)) {
275 vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different netmask (%s != %s)\n",
276 vpninfo->vpn_netmask, old_netmask);
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\n", ret);
530 report_ssl_errors(vpninfo);
534 if (ret != vpninfo->current_ssl_pkt->len + 8) {
535 vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
536 vpninfo->current_ssl_pkt->len + 8, ret);
537 vpninfo->quit_reason = "Internal error";
540 /* Don't free the 'special' packets */
541 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
542 vpninfo->current_ssl_pkt != &dpd_pkt &&
543 vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
544 vpninfo->current_ssl_pkt != &keepalive_pkt)
545 free(vpninfo->current_ssl_pkt);
547 vpninfo->current_ssl_pkt = NULL;
550 if (vpninfo->owe_ssl_dpd_response) {
551 vpninfo->owe_ssl_dpd_response = 0;
552 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
553 goto handle_outgoing;
556 switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
558 /* Not that this will ever happen; we don't even process
559 the setting when we're asked for it. */
560 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
561 time(&vpninfo->ssl_times.last_rekey);
567 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 = "CSTP 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 /* FIXME: Don't know how to handle IPv6 yet */
613 if (this->type != AF_INET)
616 if (vpninfo->deflate) {
617 unsigned char *adler;
620 vpninfo->deflate_strm.next_in = this->data;
621 vpninfo->deflate_strm.avail_in = this->len;
622 vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
623 vpninfo->deflate_strm.avail_out = 2040;
624 vpninfo->deflate_strm.total_out = 0;
626 ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
628 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
632 vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
633 vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
635 /* Add ongoing adler32 to tail of compressed packet */
636 vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
637 this->data, this->len);
639 adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
640 *(adler++) = vpninfo->deflate_adler32 >> 24;
641 *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
642 *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
643 *(adler) = vpninfo->deflate_adler32 & 0xff;
645 vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
647 vpninfo->progress(vpninfo, PRG_TRACE,
648 "Sending compressed data packet of %d bytes\n",
651 vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
654 memcpy(this->hdr, data_hdr, 8);
655 this->hdr[4] = this->len >> 8;
656 this->hdr[5] = this->len & 0xff;
658 vpninfo->progress(vpninfo, PRG_TRACE,
659 "Sending uncompressed data packet of %d bytes\n",
662 vpninfo->current_ssl_pkt = this;
664 goto handle_outgoing;
667 /* Work is not done if we just got rid of packets off the queue */
671 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
673 unsigned char *bye_pkt;
676 /* already lost connection? */
677 if (!vpninfo->https_ssl)
680 reason_len = strlen(reason);
681 bye_pkt = malloc(reason_len + 8);
685 memcpy(bye_pkt, data_hdr, 8);
686 memcpy(bye_pkt + 8, reason, reason_len);
688 bye_pkt[4] = reason_len >> 8;
689 bye_pkt[5] = reason_len & 0xff;
690 bye_pkt[6] = AC_PKT_DISCONN;
692 SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
695 vpninfo->progress(vpninfo, PRG_INFO,
696 "Send BYE packet: %s\n", reason);