diff --git a/src/mux_spop.c b/src/mux_spop.c index 1dc4b3588..953692756 100644 --- a/src/mux_spop.c +++ b/src/mux_spop.c @@ -605,7 +605,7 @@ static int spop_avail_streams(struct connection *conn) int ret1, ret2; /* Don't open new stream if the connection is closed */ - if (spop_conn->state == SPOP_CS_CLOSED) + if (spop_conn->state >= SPOP_CS_ERROR || (spop_conn->flags & (SPOP_CF_ERR_PENDING|SPOP_CF_ERROR))) return 0; /* May be negative if this setting has changed */ @@ -1291,7 +1291,8 @@ static void spop_strm_wake_one_stream(struct spop_strm *spop_strm) spop_strm_close(spop_strm); } - if (spop_conn->state == SPOP_CS_CLOSED || (spop_conn->flags & (SPOP_CF_ERR_PENDING|SPOP_CF_ERROR))) { + + if (spop_conn->state >= SPOP_CS_ERROR || (spop_conn->flags & (SPOP_CF_ERR_PENDING|SPOP_CF_ERROR))) { se_fl_set_error(spop_strm->sd); spop_strm_propagate_term_flags(spop_conn, spop_strm); if (!spop_strm->sd->abort_info.info) { @@ -1555,6 +1556,7 @@ static int spop_conn_send_disconnect(struct spop_conn *spop_conn) spop_set_frame_size(outbuf.area, outbuf.data - 4); b_add(mbuf, outbuf.data); spop_conn->flags |= SPOP_CF_DISCO_SENT; + spop_conn->state = SPOP_CS_CLOSING; ret = 1; switch (spop_conn->errcode) { @@ -1782,12 +1784,11 @@ static int spop_conn_handle_hello(struct spop_conn *spop_conn) TRACE_PROTO("SPOP AGENT HELLO frame rcvd", SPOP_EV_RX_FRAME|SPOP_EV_RX_HELLO, spop_conn->conn, 0, 0, (size_t[]){spop_conn->dfl}); b_del(&spop_conn->dbuf, spop_conn->dfl); spop_conn->dfl = 0; + spop_conn->state = SPOP_CS_FRAME_H; spop_wake_unassigned_streams(spop_conn); TRACE_LEAVE(SPOP_EV_RX_FRAME|SPOP_EV_RX_HELLO, spop_conn->conn); return 1; fail: - spop_conn->state = SPOP_CS_CLOSED; - TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_HELLO, spop_conn->conn); TRACE_DEVEL("leaving on error", SPOP_EV_RX_FRAME|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); return 0; } @@ -1973,7 +1974,6 @@ static int spop_conn_handle_ack(struct spop_conn *spop_conn, struct spop_strm *s sent = b_xfer(rxbuf, dbuf, flen); BUG_ON(sent != flen); - /* b_del(&spop_conn->dbuf, sent); */ spop_conn->dfl -= sent; end: @@ -2062,27 +2062,35 @@ static void spop_process_demux(struct spop_conn *spop_conn) TRACE_ENTER(SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); - if (spop_conn->state >= SPOP_CS_ERROR) + if (spop_conn->state == SPOP_CS_CLOSED) goto out; - if (unlikely(spop_conn->state < SPOP_CS_FRAME_H)) { + if (unlikely(spop_conn->state < SPOP_CS_FRAME_H || spop_conn->state == SPOP_CS_CLOSING)) { if (spop_conn->state == SPOP_CS_HA_HELLO) { TRACE_STATE("waiting AGENT HELLO frame to be sent", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO, spop_conn->conn); goto out; } - if (spop_conn->state == SPOP_CS_AGENT_HELLO) { - /* ensure that what is pending is a valid AGENT HELLO frame. */ - TRACE_STATE("receiving AGENT HELLO frame header", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); + else { /* AGENT_HELLO OR CLOSING */ + /* ensure that what is pending is a valid AGENT HELLO/DISCONNECT frame. */ + TRACE_STATE("receiving AGENT HELLO/DISCONNECT frame header", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); if (!spop_get_frame_hdr(&spop_conn->dbuf, &hdr)) { spop_conn->flags |= SPOP_CF_DEM_SHORT_READ; TRACE_ERROR("header frame not available yet", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); goto done; } - if (hdr.sid || hdr.fid || hdr.type != SPOP_FRM_T_AGENT_HELLO || !(hdr.flags & SPOP_FRM_FL_FIN)) { + if (spop_conn->state == SPOP_CS_AGENT_HELLO && hdr.type != SPOP_FRM_T_AGENT_HELLO) { spop_conn_error(spop_conn, SPOP_ERR_INVALID); spop_conn->state = SPOP_CS_CLOSED; - TRACE_ERROR("unexpected frame type or flags", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); + TRACE_ERROR("unexpected frame type (AGENT HELLO expected)", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); + TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); + goto done; + } + + if ((hdr.type == SPOP_FRM_T_AGENT_HELLO || hdr.type == SPOP_FRM_T_AGENT_DISCON) && (hdr.sid || hdr.fid || !(hdr.flags & SPOP_FRM_FL_FIN))) { + spop_conn_error(spop_conn, SPOP_ERR_INVALID); + spop_conn->state = SPOP_CS_CLOSED; + TRACE_ERROR("unexpected frame meta-data", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); goto done; } @@ -2128,13 +2136,14 @@ static void spop_process_demux(struct spop_conn *spop_conn) break; } + spop_conn->state = SPOP_CS_FRAME_P; + new_frame: spop_conn->dsi = hdr.sid; spop_conn->dfi = hdr.fid; spop_conn->dft = hdr.type; spop_conn->dfl = hdr.len; spop_conn->dff = hdr.flags; - spop_conn->state = SPOP_CS_FRAME_P; TRACE_STATE("SPOP frame header rcvd, switching to FRAME_P", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); /* Perform sanity check on the frame header */ @@ -2212,12 +2221,10 @@ static void spop_process_demux(struct spop_conn *spop_conn) switch (spop_conn->dft) { case SPOP_FRM_T_AGENT_HELLO: - if (spop_conn->state == SPOP_CS_FRAME_P) - ret = spop_conn_handle_hello(spop_conn); + ret = spop_conn_handle_hello(spop_conn); break; case SPOP_FRM_T_AGENT_DISCON: - if (spop_conn->state == SPOP_CS_FRAME_P) - ret = spop_conn_handle_disconnect(spop_conn); + ret = spop_conn_handle_disconnect(spop_conn); break; case SPOP_FRM_T_AGENT_ACK: if (spop_conn->state == SPOP_CS_FRAME_P) @@ -2252,7 +2259,7 @@ static void spop_process_demux(struct spop_conn *spop_conn) b_del(&spop_conn->dbuf, ret); spop_conn->dfl -= ret; } - if (!spop_conn->dfl) { + if (!spop_conn->dfl && spop_conn->state != SPOP_CS_FRAME_P) { TRACE_STATE("switching to FRAME_H", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); spop_conn->state = SPOP_CS_FRAME_H; } @@ -2590,7 +2597,7 @@ static int spop_process(struct spop_conn *spop_conn) (b_data(&spop_conn->dbuf) || (spop_conn->flags & SPOP_CF_RCVD_SHUT))) { spop_process_demux(spop_conn); - if (spop_conn->state >= SPOP_CS_ERROR || (spop_conn->flags & SPOP_CF_ERROR)) + if (spop_conn->state == SPOP_CS_CLOSED || (spop_conn->flags & SPOP_CF_ERROR)) b_reset(&spop_conn->dbuf); if (b_room(&spop_conn->dbuf))