660 lines
16 KiB
C
660 lines
16 KiB
C
/* vim: set et ts=4 sts=4 sw=4 : */
|
|
/********************************************************************\
|
|
* This program is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU General Public License as *
|
|
* published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License*
|
|
* along with this program; if not, contact: *
|
|
* *
|
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
|
* 59 Temple Place - Suite 330 Fax: +1-617-542-2652 *
|
|
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
|
* *
|
|
\********************************************************************/
|
|
|
|
/** @file tcpmux.c
|
|
@brief xfrp tcp mux implemented
|
|
@author Copyright (C) 2016 Dengfeng Liu <liu_df@qq.com>
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "common.h"
|
|
#include "tcpmux.h"
|
|
#include "client.h"
|
|
#include "config.h"
|
|
#include "debug.h"
|
|
#include "control.h"
|
|
#include "proxy.h"
|
|
|
|
static uint8_t proto_version = 0;
|
|
static uint8_t remote_go_away;
|
|
static uint8_t local_go_away;
|
|
static uint32_t g_session_id = 1;
|
|
static struct tmux_stream *cur_stream = NULL;
|
|
static struct tmux_stream *all_stream;
|
|
|
|
|
|
void
|
|
add_stream(struct tmux_stream *stream)
|
|
{
|
|
HASH_ADD_INT(all_stream, id, stream);
|
|
}
|
|
|
|
void
|
|
del_stream(uint32_t id)
|
|
{
|
|
if (!all_stream) return;
|
|
|
|
struct tmux_stream *stream = get_stream_by_id(id);
|
|
if (stream)
|
|
HASH_DEL(all_stream, stream);
|
|
}
|
|
|
|
void
|
|
clear_stream()
|
|
{
|
|
if (!all_stream) return;
|
|
|
|
HASH_CLEAR(hh, all_stream);
|
|
all_stream = NULL;
|
|
}
|
|
|
|
struct tmux_stream *
|
|
get_stream_by_id(uint32_t id)
|
|
{
|
|
if (!all_stream) return NULL;
|
|
|
|
struct tmux_stream *stream = NULL;
|
|
HASH_FIND_INT(all_stream, &id, stream);
|
|
return stream;
|
|
}
|
|
|
|
struct tmux_stream *
|
|
get_cur_stream()
|
|
{
|
|
return cur_stream;
|
|
}
|
|
|
|
void
|
|
set_cur_stream(struct tmux_stream *stream)
|
|
{
|
|
cur_stream = stream;
|
|
}
|
|
|
|
void
|
|
init_tmux_stream(struct tmux_stream *stream, uint32_t id, enum tcp_mux_state state)
|
|
{
|
|
stream->id = id;
|
|
stream->state = state;
|
|
stream->recv_window = MAX_STREAM_WINDOW_SIZE;
|
|
stream->send_window = MAX_STREAM_WINDOW_SIZE;
|
|
|
|
memset(&stream->tx_ring, 0, sizeof(struct ring_buffer));
|
|
memset(&stream->rx_ring, 0, sizeof(struct ring_buffer));
|
|
|
|
add_stream(stream);
|
|
};
|
|
|
|
int
|
|
validate_tcp_mux_protocol(struct tcp_mux_header *tmux_hdr)
|
|
{
|
|
if (tmux_hdr->version != proto_version) return 0;
|
|
|
|
if (tmux_hdr->type > GO_AWAY) return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
tcp_mux_encode(enum tcp_mux_type type, enum tcp_mux_flag flags, uint32_t stream_id, uint32_t length, struct tcp_mux_header *tmux_hdr)
|
|
{
|
|
assert(tmux_hdr);
|
|
tmux_hdr->version = proto_version;
|
|
tmux_hdr->type = type;
|
|
tmux_hdr->flags = htons(flags);
|
|
tmux_hdr->stream_id = htonl(stream_id);
|
|
tmux_hdr->length = length?htonl(length):0;
|
|
}
|
|
|
|
static uint32_t
|
|
tcp_mux_flag()
|
|
{
|
|
struct common_conf *c_conf = get_common_config();
|
|
return c_conf->tcp_mux;
|
|
}
|
|
|
|
void
|
|
reset_session_id() {
|
|
g_session_id = 1;
|
|
}
|
|
|
|
uint32_t
|
|
get_next_session_id() {
|
|
uint32_t id = g_session_id;
|
|
g_session_id += 2;
|
|
return id;
|
|
}
|
|
|
|
static void
|
|
tcp_mux_send_win_update(struct bufferevent *bout, enum tcp_mux_flag flags, uint32_t stream_id, uint32_t delta)
|
|
{
|
|
struct tcp_mux_header tmux_hdr;
|
|
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
|
|
tcp_mux_encode(WINDOW_UPDATE, flags, stream_id, delta, &tmux_hdr);
|
|
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
|
|
}
|
|
|
|
void
|
|
tcp_mux_send_win_update_syn(struct bufferevent *bout, uint32_t stream_id)
|
|
{
|
|
if (!tcp_mux_flag()) return;
|
|
|
|
tcp_mux_send_win_update(bout, SYN, stream_id, 0);
|
|
}
|
|
|
|
void
|
|
tcp_mux_send_win_update_ack(struct bufferevent *bout, uint32_t stream_id, uint32_t delta)
|
|
{
|
|
if (!tcp_mux_flag()) return;
|
|
|
|
tcp_mux_send_win_update(bout, ZERO, stream_id, 0);
|
|
}
|
|
|
|
void
|
|
tcp_mux_send_win_update_fin(struct bufferevent *bout, uint32_t stream_id)
|
|
{
|
|
if (!tcp_mux_flag()) return;
|
|
|
|
tcp_mux_send_win_update(bout, FIN, stream_id, 0);
|
|
}
|
|
|
|
void
|
|
tcp_mux_send_win_update_rst(struct bufferevent *bout, uint32_t stream_id)
|
|
{
|
|
if (!tcp_mux_flag()) return;
|
|
|
|
tcp_mux_send_win_update(bout, RST, stream_id, 0);
|
|
}
|
|
void
|
|
tcp_mux_send_data(struct bufferevent *bout, uint16_t flags, uint32_t stream_id, uint32_t length)
|
|
{
|
|
if (!tcp_mux_flag()) return;
|
|
|
|
struct tcp_mux_header tmux_hdr;
|
|
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
|
|
tcp_mux_encode(DATA, flags, stream_id, length, &tmux_hdr);
|
|
//debug(LOG_DEBUG, "tcp mux [%d] send data len : %d", stream_id, length);
|
|
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
|
|
}
|
|
|
|
void
|
|
tcp_mux_send_ping(struct bufferevent *bout, uint32_t ping_id)
|
|
{
|
|
if (!tcp_mux_flag()) return;
|
|
|
|
struct tcp_mux_header tmux_hdr;
|
|
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
|
|
tcp_mux_encode(PING, SYN, 0, ping_id, &tmux_hdr);
|
|
//debug(LOG_DEBUG, "tcp mux send ping syn : %d", ping_id);
|
|
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
|
|
}
|
|
|
|
static void
|
|
tcp_mux_handle_ping(struct bufferevent *bout, uint32_t ping_id)
|
|
{
|
|
if (!tcp_mux_flag()) return;
|
|
|
|
struct tcp_mux_header tmux_hdr;
|
|
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
|
|
tcp_mux_encode(PING, ACK, 0, ping_id, &tmux_hdr);
|
|
//debug(LOG_DEBUG, "tcp mux send ping ack : %d", ping_id);
|
|
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
|
|
}
|
|
|
|
static void
|
|
tcp_mux_send_go_away(struct bufferevent *bout, uint32_t reason)
|
|
{
|
|
if (!tcp_mux_flag()) return;
|
|
|
|
struct tcp_mux_header tmux_hdr;
|
|
memset(&tmux_hdr, 0, sizeof(tmux_hdr));
|
|
tcp_mux_encode(GO_AWAY, 0, 0, reason, &tmux_hdr);
|
|
//debug(LOG_DEBUG, "tcp mux send ping ack : %d", ping_id);
|
|
bufferevent_write(bout, (uint8_t *)&tmux_hdr, sizeof(tmux_hdr));
|
|
|
|
}
|
|
|
|
static int
|
|
process_flags(uint16_t flags, struct tmux_stream *stream)
|
|
{
|
|
uint32_t close_stream = 0;
|
|
if ( (flags&ACK) == ACK ) {
|
|
if (stream->state == SYN_SEND)
|
|
stream->state = ESTABLISHED;
|
|
} else if ( (flags&FIN) == FIN ) {
|
|
switch(stream->state) {
|
|
case SYN_SEND:
|
|
case SYN_RECEIVED:
|
|
case ESTABLISHED:
|
|
stream->state = REMOTE_CLOSE;
|
|
break;
|
|
case LOCAL_CLOSE:
|
|
stream->state = CLOSED;
|
|
close_stream = 1;
|
|
break;
|
|
default:
|
|
debug(LOG_ERR, "unexpected FIN flag in state %d", stream->state);
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
} else if ( (flags&RST) == RST ) {
|
|
stream->state = RESET;
|
|
close_stream = 1;
|
|
}
|
|
|
|
if (close_stream) {
|
|
debug(LOG_DEBUG, "free stream %d", stream->id);
|
|
del_proxy_client_by_stream_id(stream->id);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static uint16_t
|
|
get_send_flags(struct tmux_stream *stream)
|
|
{
|
|
uint16_t flags = 0;
|
|
|
|
switch (stream->state) {
|
|
case INIT:
|
|
flags |= SYN;
|
|
stream->state = SYN_SEND;
|
|
break;
|
|
case SYN_RECEIVED:
|
|
flags |= ACK;
|
|
stream->state = ESTABLISHED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
void
|
|
send_window_update(struct bufferevent *bout, struct tmux_stream *stream, uint32_t length)
|
|
{
|
|
uint32_t max = MAX_STREAM_WINDOW_SIZE;
|
|
uint32_t delta = (max - length) - stream->recv_window;
|
|
|
|
uint16_t flags = get_send_flags(stream);
|
|
|
|
if (delta < max/2 && flags == 0)
|
|
return;
|
|
|
|
stream->recv_window += delta;
|
|
tcp_mux_send_win_update(bout, flags, stream->id, delta);
|
|
//debug(LOG_DEBUG, "send window update: flags %d, stream_id %d delta %d, recv_window %u length %u",
|
|
// flags, stream->id, delta, stream->recv_window, length);
|
|
}
|
|
|
|
int
|
|
rx_ring_buffer_pop(struct ring_buffer *ring, uint8_t *data, uint32_t len)
|
|
{
|
|
assert(ring->sz >= len);
|
|
assert(data);
|
|
|
|
uint32_t i = 0;
|
|
while(i < len) {
|
|
data[i] = ring->data[ring->cur++];
|
|
if (ring->cur == RBUF_SIZE)
|
|
ring->cur = 0;
|
|
i++;
|
|
ring->sz--;
|
|
}
|
|
|
|
assert(i == len);
|
|
return len;
|
|
}
|
|
|
|
static int
|
|
process_data(struct tmux_stream *stream, uint32_t length, uint16_t flags,
|
|
void (*fn)(uint8_t *, int, void *), void *param)
|
|
{
|
|
if(!stream){
|
|
return 0;
|
|
}
|
|
uint32_t id = stream->id;
|
|
|
|
if (!process_flags(flags, stream)) return 0;
|
|
|
|
if(!get_stream_by_id(id)){
|
|
return length;
|
|
}
|
|
|
|
if (length > stream->recv_window) {
|
|
debug(LOG_ERR, "receive window exceed (remain %d, recv %d)", stream->recv_window, length);
|
|
return 0;
|
|
}
|
|
|
|
stream->recv_window -= length;
|
|
|
|
uint32_t nret = 0;
|
|
struct proxy_client *pc = (struct proxy_client *)param;
|
|
if (!pc || (pc && !pc->local_proxy_bev && !is_socks5_proxy(pc->ps))) {
|
|
uint8_t *data = (uint8_t *)calloc(length, 1);
|
|
nret = rx_ring_buffer_pop(&stream->rx_ring, data, length);
|
|
fn(data, length, pc);
|
|
free(data);
|
|
} else if (is_socks5_proxy(pc->ps)) {
|
|
// if pc's type is socks5, we should send data to socks5 client
|
|
nret = handle_ss5(pc, &stream->rx_ring, length);
|
|
} else {
|
|
nret = tx_ring_buffer_write(pc->local_proxy_bev, &stream->rx_ring, length);
|
|
|
|
}
|
|
|
|
if (nret != length) {
|
|
debug(LOG_INFO, "send data to local proxy not equal, nret %d, length %d", nret, length);
|
|
}
|
|
|
|
struct bufferevent *bout = get_main_control()->connect_bev;
|
|
send_window_update(bout, stream, nret);
|
|
|
|
return length;
|
|
}
|
|
|
|
static int
|
|
incr_send_window(struct bufferevent *bev, struct tcp_mux_header *tmux_hdr, uint16_t flags, struct tmux_stream *stream)
|
|
{
|
|
if(!stream){
|
|
return 0;
|
|
}
|
|
uint32_t id = stream->id;
|
|
|
|
if (!process_flags(flags, stream))
|
|
return 0;
|
|
|
|
if(!get_stream_by_id(id)){
|
|
return 1;
|
|
}
|
|
|
|
uint32_t length = ntohl(tmux_hdr->length);
|
|
|
|
if (stream->send_window == 0) bufferevent_enable(bev, EV_READ);
|
|
stream->send_window += length;
|
|
//debug(LOG_DEBUG, "incr_send_window : stream_id %d length %d send_window %d",
|
|
// stream->id, length, stream->send_window);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
incoming_stream(uint32_t stream_id)
|
|
{
|
|
if (local_go_away) {
|
|
struct bufferevent *bout = get_main_control()->connect_bev;
|
|
tcp_mux_send_win_update_rst(bout, stream_id);
|
|
return 0;
|
|
}
|
|
|
|
// TODO
|
|
// create new stream
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
handle_tcp_mux_ping(struct tcp_mux_header *tmux_hdr)
|
|
{
|
|
uint16_t flags = ntohs(tmux_hdr->flags);
|
|
uint32_t ping_id = ntohl(tmux_hdr->length);
|
|
|
|
if ( (flags&SYN) == SYN) {
|
|
struct bufferevent *bout = get_main_control()->connect_bev;
|
|
tcp_mux_handle_ping(bout, ping_id);
|
|
}
|
|
}
|
|
|
|
void
|
|
handle_tcp_mux_go_away(struct tcp_mux_header *tmux_hdr)
|
|
{
|
|
uint32_t code = ntohl(tmux_hdr->length);
|
|
switch(code) {
|
|
case NORMAL:
|
|
remote_go_away = 1;
|
|
break;
|
|
case PROTO_ERR:
|
|
debug(LOG_ERR, "receive protocol error go away");
|
|
break;
|
|
case INTERNAL_ERR:
|
|
debug(LOG_ERR, "receive internal error go away");
|
|
break;
|
|
default:
|
|
debug(LOG_ERR, "receive unexpected go away");
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
tmux_stream_read(struct bufferevent *bev, struct tmux_stream *stream, uint32_t len)
|
|
{
|
|
assert(stream != NULL);
|
|
if (stream->state != ESTABLISHED) {
|
|
debug(LOG_WARNING, "stream %d state is %d : not ESTABLISHED, discard its data len %d", stream->id, stream->state, len);
|
|
}
|
|
|
|
return rx_ring_buffer_read(bev, &stream->rx_ring, len);
|
|
}
|
|
|
|
int
|
|
handle_tcp_mux_stream(struct tcp_mux_header *tmux_hdr, handle_data_fn_t fn)
|
|
{
|
|
uint32_t stream_id = ntohl(tmux_hdr->stream_id);
|
|
uint16_t flags = ntohs(tmux_hdr->flags);
|
|
|
|
//debug(LOG_DEBUG, "handle_tcp_mux_stream stream_id %d type %d flags %d", stream_id, tmux_hdr->type, flags);
|
|
|
|
if ( (flags&SYN) == SYN) {
|
|
debug(LOG_INFO, "!!!! as xfrpc, it should not be here %d", stream_id);
|
|
if (!incoming_stream(stream_id))
|
|
return 0;
|
|
}
|
|
|
|
struct tmux_stream *stream = get_stream_by_id(stream_id);
|
|
|
|
if(!stream){
|
|
return 0;
|
|
}
|
|
|
|
struct proxy_client *pc = get_proxy_client(stream_id);
|
|
|
|
if (tmux_hdr->type == WINDOW_UPDATE) {
|
|
struct bufferevent *bev = pc?pc->local_proxy_bev: get_main_control()->connect_bev;
|
|
if (!incr_send_window(bev, tmux_hdr, flags, stream)) {
|
|
struct bufferevent *bout = get_main_control()->connect_bev;
|
|
tcp_mux_send_go_away(bout, PROTO_ERR);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
struct bufferevent *bout = get_main_control()->connect_bev;
|
|
if (stream->state != ESTABLISHED) {
|
|
return 0;
|
|
}
|
|
|
|
int32_t length = ntohl(tmux_hdr->length);
|
|
if (!process_data(stream, length, flags, fn, (void *)pc)) {
|
|
tcp_mux_send_go_away(bout, PROTO_ERR);
|
|
return 0;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
static int
|
|
tx_ring_buffer_append(struct ring_buffer *ring, uint8_t *data, uint32_t len)
|
|
{
|
|
uint32_t left = WBUF_SIZE - ring->sz;
|
|
assert(left >= len);
|
|
int i = 0;
|
|
for (; i < len; i++) {
|
|
ring->data[ring->end++] = data[i];
|
|
if (ring->end == WBUF_SIZE) ring->end = 0;
|
|
ring->sz++;
|
|
if (ring->cur == ring->end) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
uint32_t
|
|
rx_ring_buffer_read(struct bufferevent *bev, struct ring_buffer *ring, uint32_t len)
|
|
{
|
|
if (ring->sz == RBUF_SIZE) {
|
|
debug(LOG_ERR, "ring buffer is full");
|
|
return 0;
|
|
}
|
|
|
|
uint32_t cap = RBUF_SIZE - ring->sz;
|
|
if (len > cap) {
|
|
debug(LOG_INFO, "prepare read data [%d] out size ring capacity [%d]", len, cap);
|
|
len = cap;
|
|
}
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
bufferevent_read(bev, &ring->data[ring->end++], 1);
|
|
if (ring->end == RBUF_SIZE) ring->end = 0;
|
|
ring->sz++;
|
|
if (ring->cur == ring->end) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
uint32_t
|
|
tx_ring_buffer_write(struct bufferevent *bev, struct ring_buffer *ring, uint32_t len)
|
|
{
|
|
if (ring->sz == 0) {
|
|
debug(LOG_ERR, "ring buffer is empty");
|
|
return 0;
|
|
}
|
|
|
|
if (len > ring->sz) {
|
|
debug(LOG_INFO, "prepare write data [%d] out size ring data [%d]", len, ring->sz);
|
|
len = ring->sz;
|
|
}
|
|
|
|
uint32_t nwrite = len;
|
|
while(len > 0) {
|
|
bufferevent_write(bev, &ring->data[ring->cur++], 1);
|
|
len--;
|
|
ring->sz--;;
|
|
if (ring->cur == WBUF_SIZE) ring->cur = 0;
|
|
if (ring->cur == ring->end) {
|
|
assert(ring->sz == 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return nwrite - len;
|
|
}
|
|
|
|
uint32_t
|
|
tmux_stream_write(struct bufferevent *bev, uint8_t *data, uint32_t length, struct tmux_stream *stream)
|
|
{
|
|
switch(stream->state) {
|
|
case LOCAL_CLOSE:
|
|
case CLOSED:
|
|
case RESET:
|
|
debug(LOG_INFO, "stream %d state is closed", stream->id);
|
|
return 0;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
struct ring_buffer *tx_ring = &stream->tx_ring;
|
|
uint32_t left = WBUF_SIZE - tx_ring->sz;
|
|
if (stream->send_window == 0) {
|
|
debug(LOG_INFO, "stream %d send_window is zero, length %d left %d", stream->id, length, left);
|
|
tx_ring_buffer_append(tx_ring, data, length);
|
|
return 0;
|
|
}
|
|
|
|
uint16_t flags = get_send_flags(stream);
|
|
uint32_t max = length;
|
|
struct bufferevent *bout = get_main_control()->connect_bev;
|
|
//debug(LOG_DEBUG, "tmux_stream_write stream id %u: send_window %u tx_ring sz %u length %u",
|
|
// stream->id, stream->send_window, tx_ring->sz, length);
|
|
if (stream->send_window < tx_ring->sz) {
|
|
debug(LOG_INFO, " send_window %u less than tx_ring size %u", stream->send_window, tx_ring->sz);
|
|
max = stream->send_window;
|
|
tcp_mux_send_data(bout, flags, stream->id, max);
|
|
tx_ring_buffer_write(bev, tx_ring, max);
|
|
tx_ring_buffer_append(tx_ring, data, length);
|
|
} else if (stream->send_window < tx_ring->sz + length) {
|
|
debug(LOG_INFO, " send_window %u less than %u", stream->send_window, tx_ring->sz+length);
|
|
max = stream->send_window;
|
|
tcp_mux_send_data(bout, flags, stream->id, max);
|
|
if (tx_ring->sz > 0)
|
|
tx_ring_buffer_write(bev, tx_ring, tx_ring->sz);
|
|
bufferevent_write(bev, data, max - tx_ring->sz);
|
|
tx_ring_buffer_append(tx_ring, data + max - tx_ring->sz, length + tx_ring->sz - max);
|
|
} else {
|
|
max = tx_ring->sz + length;
|
|
tcp_mux_send_data(bout, flags, stream->id, max);
|
|
if (tx_ring->sz > 0)
|
|
tx_ring_buffer_write(bev, tx_ring, tx_ring->sz);
|
|
bufferevent_write(bev, data, length);
|
|
}
|
|
|
|
stream->send_window -= max;
|
|
|
|
return max;
|
|
}
|
|
|
|
int
|
|
tmux_stream_close(struct bufferevent *bout, struct tmux_stream *stream)
|
|
{
|
|
uint8_t flag = 0;
|
|
switch(stream->state) {
|
|
case SYN_SEND:
|
|
case SYN_RECEIVED:
|
|
case ESTABLISHED:
|
|
stream->state = LOCAL_CLOSE;
|
|
break;
|
|
case LOCAL_CLOSE:
|
|
case REMOTE_CLOSE:
|
|
flag = 1;
|
|
stream->state = CLOSED;
|
|
break;
|
|
case CLOSED:
|
|
case RESET:
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
uint16_t flags = get_send_flags(stream);
|
|
flags |= FIN;
|
|
tcp_mux_send_win_update(bout, flags, stream->id, 0);
|
|
if (!flag) return 1;
|
|
|
|
debug(LOG_DEBUG, "del proxy client %d", stream->id);
|
|
del_proxy_client_by_stream_id(stream->id);
|
|
return 0;
|
|
}
|
|
|
|
|