-/* $OpenBSD: packet.c,v 1.164 2009/06/12 20:43:22 andreas Exp $ */
+/* $OpenBSD: packet.c,v 1.166 2009/06/27 09:29:06 andreas Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
/* Used in packet_read_poll2() */
u_int packlen;
+ /* Used in packet_send2 */
+ int rekeying;
+
+ /* Used in packet_set_interactive */
+ int set_interactive_called;
+
+ /* Used in packet_set_maxsize */
+ int set_maxsize_called;
+
TAILQ_HEAD(, packet) outgoing;
};
-static struct session_state *active_state;
+static struct session_state *active_state, *backup_state;
static struct session_state *
alloc_session_state(void)
static void
packet_send2(void)
{
- static int rekeying = 0;
struct packet *p;
u_char type, *cp;
type = cp[5];
/* during rekeying we can only send key exchange messages */
- if (rekeying) {
+ if (active_state->rekeying) {
if (!((type >= SSH2_MSG_TRANSPORT_MIN) &&
(type <= SSH2_MSG_TRANSPORT_MAX))) {
debug("enqueue packet: %u", type);
/* rekeying starts with sending KEXINIT */
if (type == SSH2_MSG_KEXINIT)
- rekeying = 1;
+ active_state->rekeying = 1;
packet_send2_wrapped();
/* after a NEWKEYS message we can send the complete queue */
if (type == SSH2_MSG_NEWKEYS) {
- rekeying = 0;
+ active_state->rekeying = 0;
while ((p = TAILQ_FIRST(&active_state->outgoing))) {
type = p->type;
debug("dequeue packet: %u", type);
if ((ret = select(active_state->connection_in + 1, setp,
NULL, NULL, timeoutp)) >= 0)
break;
- if (errno != EAGAIN && errno != EINTR)
+ if (errno != EAGAIN && errno != EINTR &&
+ errno != EWOULDBLOCK)
break;
if (active_state->packet_timeout_ms == -1)
continue;
len = roaming_write(active_state->connection_out,
buffer_ptr(&active_state->output), len, &cont);
if (len == -1) {
- if (errno == EINTR || errno == EAGAIN)
+ if (errno == EINTR || errno == EAGAIN ||
+ errno == EWOULDBLOCK)
return;
fatal("Write failed: %.100s", strerror(errno));
}
if ((ret = select(active_state->connection_out + 1,
NULL, setp, NULL, timeoutp)) >= 0)
break;
- if (errno != EAGAIN && errno != EINTR)
+ if (errno != EAGAIN && errno != EINTR &&
+ errno != EWOULDBLOCK)
break;
if (active_state->packet_timeout_ms == -1)
continue;
void
packet_set_interactive(int interactive)
{
- static int called = 0;
-
- if (called)
+ if (active_state->set_interactive_called)
return;
- called = 1;
+ active_state->set_interactive_called = 1;
/* Record that we are in interactive mode. */
active_state->interactive_mode = interactive;
int
packet_set_maxsize(u_int s)
{
- static int called = 0;
-
- if (called) {
+ if (active_state->set_maxsize_called) {
logit("packet_set_maxsize: called twice: old %d new %d",
active_state->max_packet_size, s);
return -1;
logit("packet_set_maxsize: bad size %d", s);
return -1;
}
- called = 1;
+ active_state->set_maxsize_called = 1;
debug("packet_set_maxsize: setting to %d", s);
active_state->max_packet_size = s;
return s;
{
return (void *)active_state->newkeys[mode];
}
+
+/*
+ * Save the state for the real connection, and use a separate state when
+ * resuming a suspended connection.
+ */
+void
+packet_backup_state(void)
+{
+ struct session_state *tmp;
+
+ close(active_state->connection_in);
+ active_state->connection_in = -1;
+ close(active_state->connection_out);
+ active_state->connection_out = -1;
+ if (backup_state)
+ tmp = backup_state;
+ else
+ tmp = alloc_session_state();
+ backup_state = active_state;
+ active_state = tmp;
+}
+
+/*
+ * Swap in the old state when resuming a connecion.
+ */
+void
+packet_restore_state(void)
+{
+ struct session_state *tmp;
+ void *buf;
+ u_int len;
+
+ tmp = backup_state;
+ backup_state = active_state;
+ active_state = tmp;
+ active_state->connection_in = backup_state->connection_in;
+ backup_state->connection_in = -1;
+ active_state->connection_out = backup_state->connection_out;
+ backup_state->connection_out = -1;
+ len = buffer_len(&backup_state->input);
+ if (len > 0) {
+ buf = buffer_ptr(&backup_state->input);
+ buffer_append(&active_state->input, buf, len);
+ buffer_clear(&backup_state->input);
+ add_recv_bytes(len);
+ }
+}