1
0
forked from aniani/gmnisrv

Bubble up client disconnect state

This will fix the segfaults but won't fix slowlorris
This commit is contained in:
Drew DeVault 2020-09-27 13:54:19 -04:00
parent a9ee61418f
commit 477bf5db81

View File

@ -238,29 +238,34 @@ client_init_ssl(struct gmnisrv_server *server, struct gmnisrv_client *client)
return 0; return 0;
} }
static void enum client_state {
CLIENT_CONNECTED,
CLIENT_DISCONNECTED,
};
static enum client_state
client_readable(struct gmnisrv_server *server, struct gmnisrv_client *client) client_readable(struct gmnisrv_server *server, struct gmnisrv_client *client)
{ {
if (!client->ssl && client_init_ssl(server, client) != 0) { if (!client->ssl && client_init_ssl(server, client) != 0) {
return; return CLIENT_DISCONNECTED;
} }
if (!client->host) { if (!client->host) {
const char *error = "This server requires clients to support the TLS SNI (server name identification) extension"; const char *error = "This server requires clients to support the TLS SNI (server name identification) extension";
client_submit_response(client, client_submit_response(client,
GEMINI_STATUS_BAD_REQUEST, error, NULL); GEMINI_STATUS_BAD_REQUEST, error, NULL);
return; return CLIENT_CONNECTED;
} }
int r = BIO_gets(client->bio, client->buf, sizeof(client->buf)); int r = BIO_gets(client->bio, client->buf, sizeof(client->buf));
if (r <= 0) { if (r <= 0) {
r = SSL_get_error(client->ssl, r); r = SSL_get_error(client->ssl, r);
if (r == SSL_ERROR_WANT_READ) { if (r == SSL_ERROR_WANT_READ) {
return; return CLIENT_CONNECTED;
} }
client_error(&client->addr, "SSL read error %s, disconnecting", client_error(&client->addr, "SSL read error %s, disconnecting",
ERR_error_string(r, NULL)); ERR_error_string(r, NULL));
disconnect_client(server, client); disconnect_client(server, client);
return; return CLIENT_DISCONNECTED;
} }
client->buf[r] = '\0'; client->buf[r] = '\0';
@ -269,18 +274,19 @@ client_readable(struct gmnisrv_server *server, struct gmnisrv_client *client)
const char *error = "Protocol error: malformed request"; const char *error = "Protocol error: malformed request";
client_submit_response(client, client_submit_response(client,
GEMINI_STATUS_BAD_REQUEST, error, NULL); GEMINI_STATUS_BAD_REQUEST, error, NULL);
return; return CLIENT_CONNECTED;
} }
*newline = 0; *newline = 0;
if (!request_validate(client, &client->path)) { if (!request_validate(client, &client->path)) {
return; return CLIENT_CONNECTED;
} }
serve_request(client); serve_request(client);
return CLIENT_CONNECTED;
} }
static void static enum client_state
client_writable(struct gmnisrv_server *server, struct gmnisrv_client *client) client_writable(struct gmnisrv_server *server, struct gmnisrv_client *client)
{ {
int r; int r;
@ -300,24 +306,25 @@ client_writable(struct gmnisrv_server *server, struct gmnisrv_client *client)
if (r <= 0) { if (r <= 0) {
r = SSL_get_error(client->ssl, r); r = SSL_get_error(client->ssl, r);
if (r == SSL_ERROR_WANT_WRITE) { if (r == SSL_ERROR_WANT_WRITE) {
return; return CLIENT_CONNECTED;
} }
client->status = GEMINI_STATUS_NONE; client->status = GEMINI_STATUS_NONE;
client_error(&client->addr, client_error(&client->addr,
"header write error %s, disconnecting", "header write error %s, disconnecting",
ERR_error_string(r, NULL)); ERR_error_string(r, NULL));
disconnect_client(server, client); disconnect_client(server, client);
return; return CLIENT_DISCONNECTED;
} }
client->bufix += r; client->bufix += r;
if (client->bufix >= client->bufln) { if (client->bufix >= client->bufln) {
if (!client->body) { if (!client->body) {
disconnect_client(server, client); disconnect_client(server, client);
return CLIENT_DISCONNECTED;
} else { } else {
client->state = RESPOND_BODY; client->state = RESPOND_BODY;
client->bufix = client->bufln = 0; client->bufix = client->bufln = 0;
return CLIENT_CONNECTED;
} }
return;
} }
break; break;
case RESPOND_BODY: case RESPOND_BODY:
@ -329,12 +336,12 @@ client_writable(struct gmnisrv_server *server, struct gmnisrv_client *client)
"Error reading response body: %s", "Error reading response body: %s",
strerror(errno)); strerror(errno));
disconnect_client(server, client); disconnect_client(server, client);
return; return CLIENT_DISCONNECTED;
} }
if (n == 0) { if (n == 0) {
// EOF // EOF
disconnect_client(server, client); disconnect_client(server, client);
return; return CLIENT_DISCONNECTED;
} }
client->bbytes += n; client->bbytes += n;
client->bufln = n; client->bufln = n;
@ -345,17 +352,18 @@ client_writable(struct gmnisrv_server *server, struct gmnisrv_client *client)
if (r <= 0) { if (r <= 0) {
r = SSL_get_error(client->ssl, r); r = SSL_get_error(client->ssl, r);
if (r == SSL_ERROR_WANT_WRITE) { if (r == SSL_ERROR_WANT_WRITE) {
return; return CLIENT_CONNECTED;
} }
client->status = GEMINI_STATUS_NONE; client->status = GEMINI_STATUS_NONE;
client_error(&client->addr, "body write error %s, disconnecting", client_error(&client->addr, "body write error %s, disconnecting",
ERR_error_string(r, NULL)); ERR_error_string(r, NULL));
disconnect_client(server, client); disconnect_client(server, client);
return; return CLIENT_DISCONNECTED;
} }
client->bufix += r; client->bufix += r;
break; break;
} }
return false;
} }
static long static long
@ -441,15 +449,20 @@ server_run(struct gmnisrv_server *server)
} }
for (size_t i = 0; i < server->nclients; ++i) { for (size_t i = 0; i < server->nclients; ++i) {
if ((server->fds[server->nlisten + i].revents & (POLLHUP | POLLERR))) { int pi = i + server->nlisten;
enum client_state s = CLIENT_CONNECTED;
if ((server->fds[pi].revents & (POLLHUP | POLLERR))) {
disconnect_client(server, &server->clients[i]); disconnect_client(server, &server->clients[i]);
break; s = CLIENT_DISCONNECTED;
} else if ((server->fds[server->nlisten + i].revents & POLLIN)) { }
client_readable(server, &server->clients[i]); if (s == CLIENT_CONNECTED && (server->fds[pi].revents & POLLIN)) {
break; s = client_readable(server, &server->clients[i]);
} else if ((server->fds[server->nlisten + i].revents & POLLOUT)) { }
client_writable(server, &server->clients[i]); if (s == CLIENT_CONNECTED && (server->fds[pi].revents & POLLOUT)) {
break; s = client_writable(server, &server->clients[i]);
}
if (s == CLIENT_DISCONNECTED) {
--i;
} }
} }
} while (server->run); } while (server->run);