Prevent erred TCP connection to be rescheduled ...

for reading (or writing) when an reply comes in.

Thanks Maddie!
This commit is contained in:
Willem Toorop 2018-01-30 15:21:46 +01:00
parent 1f401f7253
commit 97b056c355
1 changed files with 68 additions and 53 deletions

View File

@ -127,21 +127,25 @@ static void tcp_connection_destroy(tcp_connection *conn)
tcp_to_write *cur, *next; tcp_to_write *cur, *next;
if (!(mf = &conn->super.l->set->context->mf)) mf = &conn->super.l->set->context->mf;
return;
if (getdns_context_get_eventloop(conn->super.l->set->context, &loop)) if (getdns_context_get_eventloop(conn->super.l->set->context, &loop))
return; return;
if (conn->event.read_cb||conn->event.write_cb||conn->event.timeout_cb) if (conn->event.ev)
loop->vmt->clear(loop, &conn->event); loop->vmt->clear(loop, &conn->event);
if (conn->fd >= 0) if (conn->event.read_cb||conn->event.write_cb||conn->event.timeout_cb) {
conn->event.read_cb = conn->event.write_cb =
conn->event.timeout_cb = NULL;
}
if (conn->fd >= 0) {
(void) _getdns_closesocket(conn->fd); (void) _getdns_closesocket(conn->fd);
conn->fd = -1;
}
if (conn->read_buf) { if (conn->read_buf) {
GETDNS_FREE(*mf, conn->read_buf); GETDNS_FREE(*mf, conn->read_buf);
conn->read_buf = NULL; conn->read_buf = conn->read_pos = NULL;
conn->to_read = 0;
} }
if ((cur = conn->to_write)) { if ((cur = conn->to_write)) {
while (cur) { while (cur) {
@ -198,15 +202,14 @@ static void tcp_write_cb(void *userarg)
(const void *)&to_write->write_buf[to_write->written], (const void *)&to_write->write_buf[to_write->written],
to_write->write_buf_len - to_write->written, 0)) == -1) { to_write->write_buf_len - to_write->written, 0)) == -1) {
if (_getdns_socketerror_wants_retry()) if (conn->fd != -1) {
return; if (_getdns_socketerror_wants_retry())
return;
DEBUG_SERVER("I/O error from send(): %s\n",
_getdns_errnostr());
DEBUG_SERVER("I/O error from send(): %s\n",
_getdns_errnostr());
}
/* IO error, close connection */ /* IO error, close connection */
conn->event.read_cb = conn->event.write_cb =
conn->event.timeout_cb = NULL;
tcp_connection_destroy(conn); tcp_connection_destroy(conn);
return; return;
} }
@ -317,6 +320,7 @@ getdns_reply(
tcp_to_write **to_write_p; tcp_to_write **to_write_p;
tcp_to_write *to_write; tcp_to_write *to_write;
loop->vmt->clear(loop, &conn->event);
if (conn->fd == -1) { if (conn->fd == -1) {
if (conn->to_answer > 0) if (conn->to_answer > 0)
--conn->to_answer; --conn->to_answer;
@ -324,8 +328,10 @@ getdns_reply(
return GETDNS_RETURN_GOOD; return GETDNS_RETURN_GOOD;
} }
if (!(to_write = (tcp_to_write *)GETDNS_XMALLOC( if (!(to_write = (tcp_to_write *)GETDNS_XMALLOC(
*mf, uint8_t, sizeof(tcp_to_write) + len + 2))) *mf, uint8_t, sizeof(tcp_to_write) + len + 2))) {
tcp_connection_destroy(conn);
return GETDNS_RETURN_MEMORY_ERROR; return GETDNS_RETURN_MEMORY_ERROR;
}
to_write->write_buf_len = len + 2; to_write->write_buf_len = len + 2;
to_write->write_buf[0] = (len >> 8) & 0xFF; to_write->write_buf[0] = (len >> 8) & 0xFF;
@ -341,7 +347,6 @@ getdns_reply(
; /* pass */ ; /* pass */
*to_write_p = to_write; *to_write_p = to_write;
loop->vmt->clear(loop, &conn->event);
conn->event.write_cb = tcp_write_cb; conn->event.write_cb = tcp_write_cb;
if (conn->to_answer > 0) if (conn->to_answer > 0)
conn->to_answer--; conn->to_answer--;
@ -373,18 +378,17 @@ static void tcp_read_cb(void *userarg)
/* Reset tcp_connection idle timeout */ /* Reset tcp_connection idle timeout */
loop->vmt->clear(loop, &conn->event); loop->vmt->clear(loop, &conn->event);
(void) loop->vmt->schedule(loop, conn->fd, if (conn->fd == -1 ||
DOWNSTREAM_IDLE_TIMEOUT, &conn->event); (bytes_read = recv(conn->fd,
if ((bytes_read = recv(conn->fd,
(void *)conn->read_pos, conn->to_read, 0)) < 0) { (void *)conn->read_pos, conn->to_read, 0)) < 0) {
if (_getdns_socketerror_wants_retry()) if (conn->fd != -1) {
return; /* Come back to do the read later */ if (_getdns_socketerror_wants_retry())
return; /* Come back to do the read later */
/* IO error, close connection */
DEBUG_SERVER("I/O error from recv(): %s\n",
_getdns_errnostr());
/* IO error, close connection */
DEBUG_SERVER("I/O error from recv(): %s\n",
_getdns_errnostr());
}
tcp_connection_destroy(conn); tcp_connection_destroy(conn);
return; return;
} }
@ -398,9 +402,9 @@ static void tcp_read_cb(void *userarg)
conn->to_read -= bytes_read; conn->to_read -= bytes_read;
conn->read_pos += bytes_read; conn->read_pos += bytes_read;
if (conn->to_read) if (conn->to_read)
return; /* More to read */ ; /* Schedule for more reading */
if (conn->read_pos - conn->read_buf == 2) { else if (conn->read_pos - conn->read_buf == 2) {
/* read length of dns msg to read */ /* read length of dns msg to read */
conn->to_read = (conn->read_buf[0] << 8) | conn->read_buf[1]; conn->to_read = (conn->read_buf[0] << 8) | conn->read_buf[1];
if (conn->to_read > conn->read_buf_len) { if (conn->to_read > conn->read_buf_len) {
@ -420,47 +424,49 @@ static void tcp_read_cb(void *userarg)
return; return;
} }
conn->read_pos = conn->read_buf; conn->read_pos = conn->read_buf;
return; /* Read DNS message */ ; /* Schedule for more reading */
}
if ((r = getdns_wire2msg_dict(conn->read_buf,
(conn->read_pos - conn->read_buf), &request_dict)))
; /* FROMERR on input, ignore */
else { } else {
conn->to_answer++; /* Ready for reading a new packet */
/* TODO: wish list item: if (!(r = getdns_wire2msg_dict(conn->read_buf,
* (void) getdns_dict_set_int64( (conn->read_pos - conn->read_buf), &request_dict))) {
* request_dict, "request_id", intptr_t)conn);
*/
/* Call request handler */
conn->super.l->set->handler(
conn->super.l->set->context, GETDNS_CALLBACK_COMPLETE,
request_dict, conn->super.l->set->userarg, (intptr_t)conn);
conn->to_answer++;
/* TODO: wish list item:
* (void) getdns_dict_set_int64(
* request_dict, "request_id", intptr_t)conn);
*/
/* Call request handler */
conn->super.l->set->handler(
conn->super.l->set->context,
GETDNS_CALLBACK_COMPLETE, request_dict,
conn->super.l->set->userarg, (intptr_t)conn);
}
conn->read_pos = conn->read_buf; conn->read_pos = conn->read_buf;
conn->to_read = 2; conn->to_read = 2;
return; /* Read more requests */ ; /* Schedule for more reading */
} }
conn->read_pos = conn->read_buf;
conn->to_read = 2;
/* Read more requests */ /* Read more requests */
(void) loop->vmt->schedule(loop, conn->fd,
DOWNSTREAM_IDLE_TIMEOUT, &conn->event);
} }
static void tcp_timeout_cb(void *userarg) static void tcp_timeout_cb(void *userarg)
{ {
tcp_connection *conn = (tcp_connection *)userarg; tcp_connection *conn = (tcp_connection *)userarg;
getdns_eventloop *loop;
assert(userarg); assert(userarg);
if (conn->to_answer) { if (getdns_context_get_eventloop(
getdns_eventloop *loop; conn->super.l->set->context, &loop))
return;
if (getdns_context_get_eventloop( loop->vmt->clear(loop, &conn->event);
conn->super.l->set->context, &loop))
return;
loop->vmt->clear(loop, &conn->event); if (conn->to_answer && conn->fd >= 0) {
(void) loop->vmt->schedule(loop, (void) loop->vmt->schedule(loop,
conn->fd, DOWNSTREAM_IDLE_TIMEOUT, conn->fd, DOWNSTREAM_IDLE_TIMEOUT,
&conn->event); &conn->event);
@ -737,8 +743,17 @@ static void remove_listeners(listen_set *set)
conn_p = (tcp_connection **)&l->connections; conn_p = (tcp_connection **)&l->connections;
while (*conn_p) { while (*conn_p) {
tcp_connection *prev_conn_p = *conn_p;
loop->vmt->clear(loop, &(*conn_p)->event);
tcp_connection_destroy(*conn_p); tcp_connection_destroy(*conn_p);
if (*conn_p && (*conn_p)->to_answer > 0) /* tcp_connection_destroy() updates the pointer to the
* connection. For the first connection this is
* l->connections. When the connection is not actually
* destroyed, the value of *conn_p thus remains the
* same. When it is destroyed it is updated.
*/
if (*conn_p == prev_conn_p)
conn_p = (tcp_connection **) conn_p = (tcp_connection **)
&(*conn_p)->super.next; &(*conn_p)->super.next;
} }