]> git.proxmox.com Git - mirror_corosync-qdevice.git/blob - qdevices/qnetd-client-net.c
qnetd: Improve dead peer detection
[mirror_corosync-qdevice.git] / qdevices / qnetd-client-net.c
1 /*
2 * Copyright (c) 2015-2020 Red Hat, Inc.
3 *
4 * All rights reserved.
5 *
6 * Author: Jan Friesse (jfriesse@redhat.com)
7 *
8 * This software licensed under BSD license, the text of which follows:
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Red Hat, Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/types.h>
36
37 #include "log.h"
38 #include "msgio.h"
39 #include "msg.h"
40 #include "nss-sock.h"
41 #include "qnetd-client-dpd-timer.h"
42 #include "qnetd-client-net.h"
43 #include "qnetd-client-send.h"
44 #include "qnetd-client-msg-received.h"
45
46 #define CLIENT_ADDR_STR_LEN_COLON_PORT (1 + 5 + 1)
47 #define CLIENT_ADDR_STR_LEN (INET6_ADDRSTRLEN + CLIENT_ADDR_STR_LEN_COLON_PORT)
48
49 static int
50 qnetd_client_net_write_finished(struct qnetd_instance *instance, struct qnetd_client *client)
51 {
52
53 /*
54 * Callback is currently unused
55 */
56
57 return (0);
58 }
59
60 static int
61 qnetd_client_net_socket_poll_loop_set_events_cb(PRFileDesc *prfd, short *events,
62 void *user_data1, void *user_data2)
63 {
64 struct qnetd_instance *instance = (struct qnetd_instance *)user_data1;
65 struct qnetd_client *client = (struct qnetd_client *)user_data2;
66
67 if (client->schedule_disconnect) {
68 qnetd_instance_client_disconnect(instance, client, 0);
69
70 if (pr_poll_loop_del_prfd(&instance->main_poll_loop, prfd) == -1) {
71 log(LOG_ERR, "pr_poll_loop_del_prfd for client socket failed");
72
73 return (-2);
74 }
75
76 return (-1);
77 }
78
79 if (!send_buffer_list_empty(&client->send_buffer_list)) {
80 *events |= POLLOUT;
81 }
82
83 return (0);
84 }
85
86
87 static int
88 qnetd_client_net_socket_poll_loop_read_cb(PRFileDesc *prfd, const PRPollDesc *pd,
89 void *user_data1, void *user_data2)
90 {
91 struct qnetd_instance *instance = (struct qnetd_instance *)user_data1;
92 struct qnetd_client *client = (struct qnetd_client *)user_data2;
93
94 if (!client->schedule_disconnect) {
95 if (qnetd_client_net_read(instance, client) == -1) {
96 client->schedule_disconnect = 1;
97 }
98 }
99
100 return (0);
101 }
102
103 static int
104 qnetd_client_net_socket_poll_loop_write_cb(PRFileDesc *prfd, const PRPollDesc *pd,
105 void *user_data1, void *user_data2)
106 {
107 struct qnetd_instance *instance = (struct qnetd_instance *)user_data1;
108 struct qnetd_client *client = (struct qnetd_client *)user_data2;
109
110 if (!client->schedule_disconnect) {
111 if (qnetd_client_net_write(instance, client) == -1) {
112 client->schedule_disconnect = 1;
113 }
114 }
115
116 return (0);
117 }
118
119 static int
120 qnetd_client_net_socket_poll_loop_err_cb(PRFileDesc *prfd, short revents,
121 const PRPollDesc *pd, void *user_data1, void *user_data2)
122 {
123 struct qnetd_client *client = (struct qnetd_client *)user_data2;
124
125 if (!client->schedule_disconnect) {
126 log(LOG_DEBUG, "POLL_ERR (%u) on client socket. "
127 "Disconnecting.", revents);
128
129 client->schedule_disconnect = 1;
130 }
131
132 return (0);
133 }
134
135 int
136 qnetd_client_net_write(struct qnetd_instance *instance, struct qnetd_client *client)
137 {
138 int res;
139 struct send_buffer_list_entry *send_buffer;
140
141 send_buffer = send_buffer_list_get_active(&client->send_buffer_list);
142 if (send_buffer == NULL) {
143 log_nss(LOG_CRIT, "send_buffer_list_get_active returned NULL");
144
145 return (-1);
146 }
147
148 res = msgio_write(client->socket, &send_buffer->buffer,
149 &send_buffer->msg_already_sent_bytes);
150
151 if (res == 1) {
152 send_buffer_list_delete(&client->send_buffer_list, send_buffer);
153
154 if (qnetd_client_net_write_finished(instance, client) == -1) {
155 return (-1);
156 }
157 }
158
159 if (res == -1) {
160 log_nss(LOG_CRIT, "PR_Send returned 0");
161
162 return (-1);
163 }
164
165 if (res == -2) {
166 log_nss(LOG_ERR, "Unhandled error when sending message to client");
167
168 return (-1);
169 }
170
171 return (0);
172 }
173
174
175 /*
176 * -1 means end of connection (EOF) or some other unhandled error. 0 = success
177 */
178 int
179 qnetd_client_net_read(struct qnetd_instance *instance, struct qnetd_client *client)
180 {
181 int res;
182 int ret_val;
183 int orig_skipping_msg;
184
185 orig_skipping_msg = client->skipping_msg;
186
187 res = msgio_read(client->socket, &client->receive_buffer,
188 &client->msg_already_received_bytes, &client->skipping_msg);
189
190 if (!orig_skipping_msg && client->skipping_msg) {
191 log(LOG_DEBUG, "msgio_read set skipping_msg");
192 }
193
194 ret_val = 0;
195
196 switch (res) {
197 case 0:
198 /*
199 * Partial read
200 */
201 break;
202 case -1:
203 log(LOG_DEBUG, "Client closed connection");
204 ret_val = -1;
205 break;
206 case -2:
207 log_nss(LOG_ERR, "Unhandled error when reading from client. "
208 "Disconnecting client");
209 ret_val = -1;
210 break;
211 case -3:
212 log(LOG_ERR, "Can't store message header from client. Disconnecting client");
213 ret_val = -1;
214 break;
215 case -4:
216 log(LOG_ERR, "Can't store message from client. Skipping message");
217 client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_ERROR_DECODING_MSG;
218 break;
219 case -5:
220 log(LOG_WARNING, "Client sent unsupported msg type %u. Skipping message",
221 msg_get_type(&client->receive_buffer));
222 client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_UNSUPPORTED_MESSAGE;
223 break;
224 case -6:
225 log(LOG_WARNING,
226 "Client wants to send too long message %u bytes. Skipping message",
227 msg_get_len(&client->receive_buffer));
228 client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_MESSAGE_TOO_LONG;
229 break;
230 case 1:
231 /*
232 * Full message received / skipped
233 */
234 if (!client->skipping_msg) {
235 if (qnetd_client_msg_received(instance, client) == -1) {
236 ret_val = -1;
237 }
238 } else {
239 if (qnetd_client_send_err(client, 0, 0, client->skipping_msg_reason) != 0) {
240 ret_val = -1;
241 }
242 }
243
244 client->skipping_msg = 0;
245 client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_NO_ERROR;
246 client->msg_already_received_bytes = 0;
247 dynar_clean(&client->receive_buffer);
248 break;
249 default:
250 log(LOG_ERR, "Unhandled msgio_read error %d\n", res);
251 exit(EXIT_FAILURE);
252 break;
253 }
254
255 return (ret_val);
256 }
257
258 int
259 qnetd_client_net_accept(struct qnetd_instance *instance)
260 {
261 PRNetAddr client_addr;
262 PRFileDesc *client_socket;
263 struct qnetd_client *client;
264 char *client_addr_str;
265 int res_err;
266
267 client_addr_str = NULL;
268
269 res_err = -1;
270
271 if ((client_socket = PR_Accept(instance->server.socket, &client_addr,
272 PR_INTERVAL_NO_TIMEOUT)) == NULL) {
273 log_nss(LOG_ERR, "Can't accept connection");
274 return (-1);
275 }
276
277 if (nss_sock_set_non_blocking(client_socket) != 0) {
278 log_nss(LOG_ERR, "Can't set client socket to non blocking mode");
279 goto exit_close;
280 }
281
282 if (instance->max_clients != 0 &&
283 qnetd_client_list_no_clients(&instance->clients) >= instance->max_clients) {
284 log(LOG_ERR, "Maximum clients reached. Not accepting connection");
285 goto exit_close;
286 }
287
288 client_addr_str = malloc(CLIENT_ADDR_STR_LEN);
289 if (client_addr_str == NULL) {
290 log(LOG_ERR, "Can't alloc client addr str memory. Not accepting connection");
291 goto exit_close;
292 }
293
294 if (PR_NetAddrToString(&client_addr, client_addr_str, CLIENT_ADDR_STR_LEN) != PR_SUCCESS) {
295 log_nss(LOG_ERR, "Can't convert client address to string. Not accepting connection");
296 goto exit_close;
297 }
298
299 if (snprintf(client_addr_str + strlen(client_addr_str),
300 CLIENT_ADDR_STR_LEN_COLON_PORT, ":%"PRIu16,
301 ntohs(client_addr.ipv6.port)) >= CLIENT_ADDR_STR_LEN_COLON_PORT) {
302 log(LOG_ERR, "Can't store port to client addr str. Not accepting connection");
303 goto exit_close;
304 }
305
306 client = qnetd_client_list_add(&instance->clients, client_socket, &client_addr,
307 client_addr_str,
308 instance->advanced_settings->max_client_receive_size,
309 instance->advanced_settings->max_client_send_buffers,
310 instance->advanced_settings->max_client_send_size,
311 pr_poll_loop_get_timer_list(&instance->main_poll_loop));
312 if (client == NULL) {
313 log(LOG_ERR, "Can't add client to list");
314 res_err = -2;
315 goto exit_close;
316 }
317
318 if (pr_poll_loop_add_prfd(&instance->main_poll_loop, client_socket, POLLIN,
319 qnetd_client_net_socket_poll_loop_set_events_cb,
320 qnetd_client_net_socket_poll_loop_read_cb,
321 qnetd_client_net_socket_poll_loop_write_cb,
322 qnetd_client_net_socket_poll_loop_err_cb,
323 instance, client) == -1) {
324 log(LOG_ERR, "Can't add client to main poll loop");
325 res_err = -2;
326 goto exit_client_list_del_close;
327 }
328
329 if (qnetd_client_dpd_timer_init(instance, client) == -1) {
330 res_err = -2;
331 goto exit_client_nspr_list_del_close;
332 }
333
334 return (0);
335
336 exit_client_nspr_list_del_close:
337 if (pr_poll_loop_del_prfd(&instance->main_poll_loop, client_socket) == -1) {
338 log(LOG_ERR, "pr_poll_loop_del_prfd for client socket failed");
339 }
340
341 exit_client_list_del_close:
342 qnetd_client_list_del(&instance->clients, client);
343 /*
344 * client_addr_str is passed to qnetd_client_list_add and becomes part of client struct.
345 * qnetd_client_list_del calls qnetd_client_destroy which frees this memory
346 */
347 client_addr_str = NULL;
348
349 exit_close:
350 free(client_addr_str);
351 PR_Close(client_socket);
352
353 return (res_err);
354 }