diff --git a/backend/src/client.c b/backend/src/client.c index 43df739..c7aac14 100644 --- a/backend/src/client.c +++ b/backend/src/client.c @@ -77,12 +77,14 @@ static char token[PB_TOKEN_LEN + 1]; static int hub_port; static int hello_done; static long last_svc, last_retry; +static long ctl_last_rx; // last control data; the hub PINGs every 30 s static volatile sig_atomic_t sig_stop; #define BUF_LIMIT (256 * 1024) #define UDP_IDLE 120 #define READ_CHUNK 65536 +#define CTL_TIMEOUT 90 static long now_s(void) { @@ -687,8 +689,14 @@ static int process_ctl_input(void) { char line[PB_MAX_LINE]; int rc; + if (ctl && ctl->from_ch.len) + ctl_last_rx = now_s(); while (ctl && (rc = line_next(&ctl->from_ch, line, sizeof(line))) == 1) ctl_line(line); + if (now_s() - ctl_last_rx > CTL_TIMEOUT) { + log_warn("no data from target for %d s", CTL_TIMEOUT); + return -1; + } if (ctl && ctl->ch_eof) { log_warn("control channel closed by target"); return -1; @@ -861,7 +869,10 @@ static int session_open(void) ctl->ch = ch; ctl->state = S_ACTIVE; - libssh2_keepalive_config(sess, 1, 30); + // No reply requested: replies are never read and would pile up in + // libssh2's packet queue. Liveness comes from the hub's PINGs instead. + libssh2_keepalive_config(sess, 0, 30); + ctl_last_rx = now_s(); libssh2_session_set_blocking(sess, 0); log_info("connected to %s:%d as %s", cfg->target_host, cfg->target_port, cfg->ssh_user); return 0; @@ -921,10 +932,12 @@ static int ssh_pass(int *data) } // True if libssh2 already holds decrypted data for a channel we can accept. +// libssh2_poll_channel_read() returns a negative error for short packets in +// the queue, so only 1 counts. static int ssh_buffered(void) { for (struct cc *c = ccs; c; c = c->next) - if (c->state == S_ACTIVE && c->ch && c->from_ch.len < BUF_LIMIT && libssh2_poll_channel_read(c->ch, 0)) + if (c->state == S_ACTIVE && c->ch && c->from_ch.len < BUF_LIMIT && libssh2_poll_channel_read(c->ch, 0) == 1) return 1; return 0; }