2 * Copyright (c) 2015-2020 Red Hat, Inc.
6 * Author: Jan Friesse (jfriesse@redhat.com)
8 * This software licensed under BSD license, the text of which follows:
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
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.
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.
38 #include "qnet-config.h"
39 #include "qdevice-net-msg-received.h"
40 #include "qdevice-net-nss.h"
41 #include "qdevice-net-send.h"
42 #include "qdevice-net-socket.h"
48 socket_set_events_cb(PRFileDesc
*prfd
, short *events
, void *user_data1
, void *user_data2
)
50 struct qdevice_net_instance
*instance
= (struct qdevice_net_instance
*)user_data1
;
52 if (!send_buffer_list_empty(&instance
->send_buffer_list
)) {
60 socket_read_cb(PRFileDesc
*prfd
, const PRPollDesc
*pd
, void *user_data1
, void *user_data2
)
62 struct qdevice_net_instance
*instance
= (struct qdevice_net_instance
*)user_data1
;
64 if (qdevice_net_socket_read(instance
) == -1) {
65 instance
->schedule_disconnect
= 1;
74 socket_write_cb(PRFileDesc
*prfd
, const PRPollDesc
*pd
, void *user_data1
, void *user_data2
)
76 struct qdevice_net_instance
*instance
= (struct qdevice_net_instance
*)user_data1
;
78 if (qdevice_net_socket_write(instance
) == -1) {
79 instance
->schedule_disconnect
= 1;
88 non_blocking_client_socket_write_cb(PRFileDesc
*prfd
, const PRPollDesc
*pd
, void *user_data1
,
93 struct qdevice_net_instance
*instance
= (struct qdevice_net_instance
*)user_data1
;
95 res
= nss_sock_non_blocking_client_succeeded(pd
);
98 * Connect failed -> remove this fd from main loop and try next
100 res
= qdevice_net_socket_del_from_main_poll_loop(instance
);
105 res
= nss_sock_non_blocking_client_try_next(&instance
->non_blocking_client
);
107 log_nss(LOG_ERR
, "Can't connect to qnetd host.");
108 nss_sock_non_blocking_client_destroy(&instance
->non_blocking_client
);
111 res
= qdevice_net_socket_add_to_main_poll_loop(instance
);
115 } else if (res
== 0) {
119 } else if (res
== 1) {
121 * Connect success -> delete socket from main loop and add final one
123 res
= qdevice_net_socket_del_from_main_poll_loop(instance
);
128 instance
->socket
= instance
->non_blocking_client
.socket
;
129 nss_sock_non_blocking_client_destroy(&instance
->non_blocking_client
);
130 instance
->non_blocking_client
.socket
= NULL
;
132 instance
->state
= QDEVICE_NET_INSTANCE_STATE_SENDING_PREINIT_REPLY
;
134 res
= qdevice_net_socket_add_to_main_poll_loop(instance
);
139 log(LOG_DEBUG
, "Sending preinit msg to qnetd");
140 if (qdevice_net_send_preinit(instance
) != 0) {
141 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
145 log(LOG_CRIT
, "Unhandled nss_sock_non_blocking_client_succeeded");
153 socket_err_cb(PRFileDesc
*prfd
, short revents
, const PRPollDesc
*pd
, void *user_data1
, void *user_data2
)
155 struct qdevice_net_instance
*instance
= (struct qdevice_net_instance
*)user_data1
;
157 log(LOG_ERR
, "POLL_ERR (%u) on main socket", revents
);
159 instance
->schedule_disconnect
= 1;
160 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION
;
166 non_blocking_client_socket_err_cb(PRFileDesc
*prfd
, short revents
, const PRPollDesc
*pd
,
167 void *user_data1
, void *user_data2
)
169 struct qdevice_net_instance
*instance
= (struct qdevice_net_instance
*)user_data1
;
172 * Workaround for RHEL<7. Pollout is never set for nonblocking connect (doesn't work
173 * only with poll, select works as expected!???).
174 * So test if client is still valid and if pollout was not already called (ensured
175 * by default because of order in PR_Poll).
176 * If both applies it's possible to emulate pollout set by calling poll_write.
178 if (!instance
->non_blocking_client
.destroyed
) {
179 return (non_blocking_client_socket_write_cb(prfd
, pd
, user_data1
, user_data2
));
189 * -1 means end of connection (EOF) or some other unhandled error. 0 = success
192 qdevice_net_socket_read(struct qdevice_net_instance
*instance
)
196 int orig_skipping_msg
;
198 orig_skipping_msg
= instance
->skipping_msg
;
200 res
= msgio_read(instance
->socket
, &instance
->receive_buffer
,
201 &instance
->msg_already_received_bytes
, &instance
->skipping_msg
);
203 if (!orig_skipping_msg
&& instance
->skipping_msg
) {
204 log(LOG_DEBUG
, "msgio_read set skipping_msg");
216 log(LOG_DEBUG
, "Server closed connection");
217 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION
;
221 log(LOG_ERR
, "Unhandled error when reading from server. "
222 "Disconnecting from server");
223 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE
;
227 log(LOG_ERR
, "Can't store message header from server. "
228 "Disconnecting from server");
229 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE
;
233 log(LOG_ERR
, "Can't store message from server. "
234 "Disconnecting from server");
235 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE
;
239 log(LOG_WARNING
, "Server sent unsupported msg type %u. "
240 "Disconnecting from server", msg_get_type(&instance
->receive_buffer
));
241 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_UNSUPPORTED_MSG
;
246 "Server wants to send too long message %u bytes. Disconnecting from server",
247 msg_get_len(&instance
->receive_buffer
));
248 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE
;
253 * Full message received / skipped
255 if (!instance
->skipping_msg
) {
256 if (qdevice_net_msg_received(instance
) == -1) {
260 log(LOG_CRIT
, "net_socket_read in skipping msg state");
264 instance
->skipping_msg
= 0;
265 instance
->msg_already_received_bytes
= 0;
266 dynar_clean(&instance
->receive_buffer
);
269 log(LOG_CRIT
, "qdevice_net_socket_read unhandled error %d", res
);
278 qdevice_net_socket_write_finished(struct qdevice_net_instance
*instance
)
280 PRFileDesc
*new_pr_fd
;
282 if (instance
->state
== QDEVICE_NET_INSTANCE_STATE_WAITING_STARTTLS_BEING_SENT
) {
284 * StartTLS sent to server. Begin with TLS handshake
286 if ((new_pr_fd
= nss_sock_start_ssl_as_client(instance
->socket
,
287 instance
->advanced_settings
->net_nss_qnetd_cn
,
288 qdevice_net_nss_bad_cert_hook
,
289 qdevice_net_nss_get_client_auth_data
,
290 instance
, 0, NULL
)) == NULL
) {
291 log_nss(LOG_ERR
, "Can't start TLS");
292 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_START_TLS
;
299 if (qdevice_net_send_init(instance
) != 0) {
300 instance
->disconnect_reason
=
301 QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER
;
306 instance
->socket
= new_pr_fd
;
307 instance
->using_tls
= 1;
314 qdevice_net_socket_write(struct qdevice_net_instance
*instance
)
317 struct send_buffer_list_entry
*send_buffer
;
318 enum msg_type sent_msg_type
;
320 send_buffer
= send_buffer_list_get_active(&instance
->send_buffer_list
);
321 if (send_buffer
== NULL
) {
322 log(LOG_CRIT
, "send_buffer_list_get_active returned NULL");
323 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE
;
328 res
= msgio_write(instance
->socket
, &send_buffer
->buffer
,
329 &send_buffer
->msg_already_sent_bytes
);
332 sent_msg_type
= msg_get_type(&send_buffer
->buffer
);
334 send_buffer_list_delete(&instance
->send_buffer_list
, send_buffer
);
336 if (sent_msg_type
!= MSG_TYPE_ECHO_REQUEST
) {
337 if (qdevice_net_socket_write_finished(instance
) == -1) {
344 log_nss(LOG_CRIT
, "PR_Send returned 0");
345 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION
;
350 log_nss(LOG_ERR
, "Unhandled error when sending message to server");
351 instance
->disconnect_reason
= QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE
;
360 qdevice_net_socket_add_to_main_poll_loop(struct qdevice_net_instance
*instance
)
363 if (instance
->state
!= QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT
||
364 !instance
->non_blocking_client
.destroyed
) {
365 if (instance
->state
== QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT
) {
366 if (pr_poll_loop_add_prfd(&instance
->qdevice_instance_ptr
->main_poll_loop
,
367 instance
->non_blocking_client
.socket
,
369 NULL
, NULL
, non_blocking_client_socket_write_cb
,
370 non_blocking_client_socket_err_cb
,
371 instance
, NULL
) != 0) {
372 log(LOG_ERR
, "Can't add net socket (non_blocking_client) "
373 "fd to main poll loop");
378 if (pr_poll_loop_add_prfd(&instance
->qdevice_instance_ptr
->main_poll_loop
,
381 socket_set_events_cb
, socket_read_cb
, socket_write_cb
, socket_err_cb
,
382 instance
, NULL
) != 0) {
383 log(LOG_ERR
, "Can't add net socket fd to main poll loop");
394 qdevice_net_socket_del_from_main_poll_loop(struct qdevice_net_instance
*instance
)
397 if (!instance
->non_blocking_client
.destroyed
) {
398 if (pr_poll_loop_del_prfd(&instance
->qdevice_instance_ptr
->main_poll_loop
,
399 instance
->non_blocking_client
.socket
) != 0) {
400 log(LOG_ERR
, "Can't remove net socket (non_blocking_client) "
401 "fd from main poll loop");
407 if (instance
->socket
!= NULL
) {
408 if (pr_poll_loop_del_prfd(&instance
->qdevice_instance_ptr
->main_poll_loop
,
409 instance
->socket
) != 0) {
410 log(LOG_ERR
, "Can't remove net socket fd from main poll loop");