]>
Commit | Line | Data |
---|---|---|
74971473 JG |
1 | /* |
2 | * This file is part of the PCEPlib, a PCEP protocol library. | |
3 | * | |
4 | * Copyright (C) 2020 Volta Networks https://voltanet.io/ | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public License | |
17 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
18 | * | |
19 | * Author : Brady Johnson <brady@voltanet.io> | |
20 | * | |
21 | */ | |
22 | ||
23 | ||
24 | /* | |
25 | * Implementation of public API functions. | |
26 | */ | |
27 | ||
28 | #include <zebra.h> | |
29 | ||
30 | #include <errno.h> | |
31 | #include <fcntl.h> | |
32 | #include <netdb.h> // gethostbyname | |
33 | #include <stdbool.h> | |
34 | #include <string.h> | |
35 | #include <unistd.h> // close | |
36 | ||
37 | #include <arpa/inet.h> // sockets etc. | |
38 | #include <sys/types.h> // sockets etc. | |
39 | #include <sys/socket.h> // sockets etc. | |
40 | ||
41 | #include "pcep.h" | |
42 | #include "pcep_socket_comm.h" | |
43 | #include "pcep_socket_comm_internals.h" | |
44 | #include "pcep_utils_logging.h" | |
45 | #include "pcep_utils_memory.h" | |
46 | #include "pcep_utils_ordered_list.h" | |
47 | #include "pcep_utils_queue.h" | |
48 | ||
49 | bool initialize_socket_comm_pre(void); | |
50 | bool socket_comm_session_initialize_post( | |
51 | pcep_socket_comm_session *socket_comm_session); | |
52 | ||
53 | pcep_socket_comm_handle *socket_comm_handle_ = NULL; | |
54 | ||
55 | ||
56 | /* simple compare method callback used by pcep_utils_ordered_list | |
57 | * for ordered list insertion. */ | |
58 | int socket_fd_node_compare(void *list_entry, void *new_entry) | |
59 | { | |
60 | return ((pcep_socket_comm_session *)new_entry)->socket_fd | |
61 | - ((pcep_socket_comm_session *)list_entry)->socket_fd; | |
62 | } | |
63 | ||
64 | ||
2816045a | 65 | bool initialize_socket_comm_pre(void) |
74971473 JG |
66 | { |
67 | socket_comm_handle_ = | |
68 | pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_handle)); | |
69 | memset(socket_comm_handle_, 0, sizeof(pcep_socket_comm_handle)); | |
70 | ||
71 | socket_comm_handle_->active = true; | |
72 | socket_comm_handle_->num_active_sessions = 0; | |
73 | socket_comm_handle_->read_list = | |
74 | ordered_list_initialize(socket_fd_node_compare); | |
75 | socket_comm_handle_->write_list = | |
76 | ordered_list_initialize(socket_fd_node_compare); | |
77 | socket_comm_handle_->session_list = | |
78 | ordered_list_initialize(pointer_compare_function); | |
79 | FD_ZERO(&socket_comm_handle_->except_master_set); | |
80 | FD_ZERO(&socket_comm_handle_->read_master_set); | |
81 | FD_ZERO(&socket_comm_handle_->write_master_set); | |
82 | ||
83 | if (pthread_mutex_init(&(socket_comm_handle_->socket_comm_mutex), NULL) | |
84 | != 0) { | |
85 | pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm mutex.", | |
86 | __func__); | |
87 | pceplib_free(PCEPLIB_INFRA, socket_comm_handle_); | |
88 | socket_comm_handle_ = NULL; | |
89 | ||
90 | return false; | |
91 | } | |
92 | ||
93 | return true; | |
94 | } | |
95 | ||
96 | bool initialize_socket_comm_external_infra( | |
97 | void *external_infra_data, ext_socket_read socket_read_cb, | |
98 | ext_socket_write socket_write_cb, | |
99 | ext_socket_pthread_create_callback thread_create_func) | |
100 | { | |
101 | if (socket_comm_handle_ != NULL) { | |
102 | /* already initialized */ | |
103 | return true; | |
104 | } | |
105 | ||
106 | if (initialize_socket_comm_pre() == false) { | |
107 | return false; | |
108 | } | |
109 | ||
110 | /* Notice: If the thread_create_func is set, then both the | |
111 | * socket_read_cb and the socket_write_cb SHOULD be NULL. */ | |
112 | if (thread_create_func != NULL) { | |
113 | if (thread_create_func( | |
114 | &(socket_comm_handle_->socket_comm_thread), NULL, | |
115 | socket_comm_loop, socket_comm_handle_, | |
116 | "pceplib_timers")) { | |
117 | pcep_log( | |
118 | LOG_ERR, | |
119 | "%s: Cannot initialize external socket_comm thread.", | |
120 | __func__); | |
121 | return false; | |
122 | } | |
123 | } | |
124 | ||
125 | socket_comm_handle_->external_infra_data = external_infra_data; | |
126 | socket_comm_handle_->socket_write_func = socket_write_cb; | |
127 | socket_comm_handle_->socket_read_func = socket_read_cb; | |
128 | ||
129 | return true; | |
130 | } | |
131 | ||
2816045a | 132 | bool initialize_socket_comm_loop(void) |
74971473 JG |
133 | { |
134 | if (socket_comm_handle_ != NULL) { | |
135 | /* already initialized */ | |
136 | return true; | |
137 | } | |
138 | ||
139 | if (initialize_socket_comm_pre() == false) { | |
140 | return false; | |
141 | } | |
142 | ||
143 | /* Launch socket comm loop pthread */ | |
144 | if (pthread_create(&(socket_comm_handle_->socket_comm_thread), NULL, | |
145 | socket_comm_loop, socket_comm_handle_)) { | |
146 | pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm thread.", | |
147 | __func__); | |
148 | return false; | |
149 | } | |
150 | ||
151 | return true; | |
152 | } | |
153 | ||
154 | ||
2816045a | 155 | bool destroy_socket_comm_loop(void) |
74971473 JG |
156 | { |
157 | socket_comm_handle_->active = false; | |
158 | ||
159 | pthread_join(socket_comm_handle_->socket_comm_thread, NULL); | |
160 | ordered_list_destroy(socket_comm_handle_->read_list); | |
161 | ordered_list_destroy(socket_comm_handle_->write_list); | |
162 | ordered_list_destroy(socket_comm_handle_->session_list); | |
163 | pthread_mutex_destroy(&(socket_comm_handle_->socket_comm_mutex)); | |
164 | ||
165 | pceplib_free(PCEPLIB_INFRA, socket_comm_handle_); | |
166 | socket_comm_handle_ = NULL; | |
167 | ||
168 | return true; | |
169 | } | |
170 | ||
171 | /* Internal common init function */ | |
172 | static pcep_socket_comm_session *socket_comm_session_initialize_pre( | |
173 | message_received_handler message_handler, | |
174 | message_ready_to_read_handler message_ready_handler, | |
175 | message_sent_notifier msg_sent_notifier, | |
176 | connection_except_notifier notifier, uint32_t connect_timeout_millis, | |
177 | const char *tcp_authentication_str, bool is_tcp_auth_md5, | |
178 | void *session_data) | |
179 | { | |
180 | /* check that not both message handlers were set */ | |
181 | if (message_handler != NULL && message_ready_handler != NULL) { | |
182 | pcep_log( | |
183 | LOG_WARNING, | |
184 | "%s: Only one of <message_received_handler | message_ready_to_read_handler> can be set.", | |
185 | __func__); | |
186 | return NULL; | |
187 | } | |
188 | ||
189 | /* check that at least one message handler was set */ | |
190 | if (message_handler == NULL && message_ready_handler == NULL) { | |
191 | pcep_log( | |
192 | LOG_WARNING, | |
193 | "%s: At least one of <message_received_handler | message_ready_to_read_handler> must be set.", | |
194 | __func__); | |
195 | return NULL; | |
196 | } | |
197 | ||
198 | if (!initialize_socket_comm_loop()) { | |
199 | pcep_log(LOG_WARNING, | |
200 | "%s: ERROR: cannot initialize socket_comm_loop.", | |
201 | __func__); | |
202 | ||
203 | return NULL; | |
204 | } | |
205 | ||
206 | /* initialize everything for a pcep_session socket_comm */ | |
207 | ||
208 | pcep_socket_comm_session *socket_comm_session = | |
209 | pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_session)); | |
210 | memset(socket_comm_session, 0, sizeof(pcep_socket_comm_session)); | |
211 | ||
212 | socket_comm_handle_->num_active_sessions++; | |
213 | socket_comm_session->close_after_write = false; | |
214 | socket_comm_session->session_data = session_data; | |
215 | socket_comm_session->message_handler = message_handler; | |
216 | socket_comm_session->message_ready_to_read_handler = | |
217 | message_ready_handler; | |
218 | socket_comm_session->message_sent_handler = msg_sent_notifier; | |
219 | socket_comm_session->conn_except_notifier = notifier; | |
220 | socket_comm_session->message_queue = queue_initialize(); | |
221 | socket_comm_session->connect_timeout_millis = connect_timeout_millis; | |
222 | socket_comm_session->external_socket_data = NULL; | |
223 | if (tcp_authentication_str != NULL) { | |
224 | socket_comm_session->is_tcp_auth_md5 = is_tcp_auth_md5; | |
225 | strlcpy(socket_comm_session->tcp_authentication_str, | |
226 | tcp_authentication_str, | |
227 | sizeof(socket_comm_session->tcp_authentication_str)); | |
228 | } | |
229 | ||
230 | return socket_comm_session; | |
231 | } | |
232 | ||
233 | /* Internal common init function */ | |
234 | bool socket_comm_session_initialize_post( | |
235 | pcep_socket_comm_session *socket_comm_session) | |
236 | { | |
237 | /* If we dont use SO_REUSEADDR, the socket will take 2 TIME_WAIT | |
238 | * periods before being closed in the kernel if bind() was called */ | |
239 | int reuse_addr = 1; | |
240 | if (setsockopt(socket_comm_session->socket_fd, SOL_SOCKET, SO_REUSEADDR, | |
241 | &reuse_addr, sizeof(int)) | |
242 | < 0) { | |
243 | pcep_log( | |
244 | LOG_WARNING, | |
245 | "%s: Error in setsockopt() SO_REUSEADDR errno [%d %s].", | |
246 | __func__, errno, strerror(errno)); | |
247 | socket_comm_session_teardown(socket_comm_session); | |
248 | ||
249 | return false; | |
250 | } | |
251 | ||
252 | struct sockaddr *src_sock_addr = | |
253 | (socket_comm_session->is_ipv6 | |
254 | ? (struct sockaddr *)&( | |
255 | socket_comm_session->src_sock_addr | |
256 | .src_sock_addr_ipv6) | |
257 | : (struct sockaddr *)&( | |
258 | socket_comm_session->src_sock_addr | |
259 | .src_sock_addr_ipv4)); | |
260 | int addr_len = (socket_comm_session->is_ipv6 | |
261 | ? sizeof(socket_comm_session->src_sock_addr | |
262 | .src_sock_addr_ipv6) | |
263 | : sizeof(socket_comm_session->src_sock_addr | |
264 | .src_sock_addr_ipv4)); | |
265 | if (bind(socket_comm_session->socket_fd, src_sock_addr, addr_len) | |
266 | == -1) { | |
267 | pcep_log(LOG_WARNING, | |
268 | "%s: Cannot bind address to socket errno [%d %s].", | |
269 | __func__, errno, strerror(errno)); | |
270 | socket_comm_session_teardown(socket_comm_session); | |
271 | ||
272 | return false; | |
273 | } | |
274 | ||
275 | /* Register the session as active with the Socket Comm Loop */ | |
276 | pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); | |
277 | ordered_list_add_node(socket_comm_handle_->session_list, | |
278 | socket_comm_session); | |
279 | pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); | |
280 | ||
281 | /* dont connect to the destination yet, since the PCE will have a timer | |
282 | * for max time between TCP connect and PCEP open. we'll connect later | |
283 | * when we send the PCEP open. */ | |
284 | ||
285 | return true; | |
286 | } | |
287 | ||
288 | ||
289 | pcep_socket_comm_session *socket_comm_session_initialize( | |
290 | message_received_handler message_handler, | |
291 | message_ready_to_read_handler message_ready_handler, | |
292 | message_sent_notifier msg_sent_notifier, | |
293 | connection_except_notifier notifier, struct in_addr *dest_ip, | |
294 | short dest_port, uint32_t connect_timeout_millis, | |
295 | const char *tcp_authentication_str, bool is_tcp_auth_md5, | |
296 | void *session_data) | |
297 | { | |
298 | return socket_comm_session_initialize_with_src( | |
299 | message_handler, message_ready_handler, msg_sent_notifier, | |
300 | notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis, | |
301 | tcp_authentication_str, is_tcp_auth_md5, session_data); | |
302 | } | |
303 | ||
304 | pcep_socket_comm_session *socket_comm_session_initialize_ipv6( | |
305 | message_received_handler message_handler, | |
306 | message_ready_to_read_handler message_ready_handler, | |
307 | message_sent_notifier msg_sent_notifier, | |
308 | connection_except_notifier notifier, struct in6_addr *dest_ip, | |
309 | short dest_port, uint32_t connect_timeout_millis, | |
310 | const char *tcp_authentication_str, bool is_tcp_auth_md5, | |
311 | void *session_data) | |
312 | { | |
313 | return socket_comm_session_initialize_with_src_ipv6( | |
314 | message_handler, message_ready_handler, msg_sent_notifier, | |
315 | notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis, | |
316 | tcp_authentication_str, is_tcp_auth_md5, session_data); | |
317 | } | |
318 | ||
319 | ||
320 | pcep_socket_comm_session *socket_comm_session_initialize_with_src( | |
321 | message_received_handler message_handler, | |
322 | message_ready_to_read_handler message_ready_handler, | |
323 | message_sent_notifier msg_sent_notifier, | |
324 | connection_except_notifier notifier, struct in_addr *src_ip, | |
325 | short src_port, struct in_addr *dest_ip, short dest_port, | |
326 | uint32_t connect_timeout_millis, const char *tcp_authentication_str, | |
327 | bool is_tcp_auth_md5, void *session_data) | |
328 | { | |
329 | if (dest_ip == NULL) { | |
330 | pcep_log(LOG_WARNING, "%s: dest_ipv4 is NULL", __func__); | |
331 | return NULL; | |
332 | } | |
333 | ||
334 | pcep_socket_comm_session *socket_comm_session = | |
335 | socket_comm_session_initialize_pre( | |
336 | message_handler, message_ready_handler, | |
337 | msg_sent_notifier, notifier, connect_timeout_millis, | |
338 | tcp_authentication_str, is_tcp_auth_md5, session_data); | |
339 | if (socket_comm_session == NULL) { | |
340 | return NULL; | |
341 | } | |
342 | ||
343 | socket_comm_session->socket_fd = | |
344 | socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); | |
345 | if (socket_comm_session->socket_fd == -1) { | |
346 | pcep_log(LOG_WARNING, | |
347 | "%s: Cannot create ipv4 socket errno [%d %s].", | |
348 | __func__, errno, strerror(errno)); | |
349 | socket_comm_session_teardown( | |
350 | socket_comm_session); // socket_comm_session freed | |
351 | // inside fn so NOLINT next. | |
352 | ||
353 | return NULL; // NOLINT(clang-analyzer-unix.Malloc) | |
354 | } | |
355 | ||
356 | socket_comm_session->is_ipv6 = false; | |
357 | socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family = | |
358 | AF_INET; | |
359 | socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_family = | |
360 | AF_INET; | |
361 | socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port = | |
362 | htons(dest_port); | |
363 | socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_port = | |
364 | htons(src_port); | |
365 | socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr | |
366 | .s_addr = dest_ip->s_addr; | |
367 | if (src_ip != NULL) { | |
368 | socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr | |
369 | .s_addr = src_ip->s_addr; | |
370 | } else { | |
371 | socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr | |
372 | .s_addr = INADDR_ANY; | |
373 | } | |
374 | ||
375 | if (socket_comm_session_initialize_post(socket_comm_session) == false) { | |
376 | return NULL; | |
377 | } | |
378 | ||
379 | return socket_comm_session; | |
380 | } | |
381 | ||
382 | pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6( | |
383 | message_received_handler message_handler, | |
384 | message_ready_to_read_handler message_ready_handler, | |
385 | message_sent_notifier msg_sent_notifier, | |
386 | connection_except_notifier notifier, struct in6_addr *src_ip, | |
387 | short src_port, struct in6_addr *dest_ip, short dest_port, | |
388 | uint32_t connect_timeout_millis, const char *tcp_authentication_str, | |
389 | bool is_tcp_auth_md5, void *session_data) | |
390 | { | |
391 | if (dest_ip == NULL) { | |
392 | pcep_log(LOG_WARNING, "%s: dest_ipv6 is NULL", __func__); | |
393 | return NULL; | |
394 | } | |
395 | ||
396 | pcep_socket_comm_session *socket_comm_session = | |
397 | socket_comm_session_initialize_pre( | |
398 | message_handler, message_ready_handler, | |
399 | msg_sent_notifier, notifier, connect_timeout_millis, | |
400 | tcp_authentication_str, is_tcp_auth_md5, session_data); | |
401 | if (socket_comm_session == NULL) { | |
402 | return NULL; | |
403 | } | |
404 | ||
405 | socket_comm_session->socket_fd = | |
406 | socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); | |
407 | if (socket_comm_session->socket_fd == -1) { | |
408 | pcep_log(LOG_WARNING, | |
409 | "%s: Cannot create ipv6 socket errno [%d %s].", | |
410 | __func__, errno, strerror(errno)); | |
411 | socket_comm_session_teardown( | |
412 | socket_comm_session); // socket_comm_session freed | |
413 | // inside fn so NOLINT next. | |
414 | ||
415 | return NULL; // NOLINT(clang-analyzer-unix.Malloc) | |
416 | } | |
417 | ||
418 | socket_comm_session->is_ipv6 = true; | |
419 | socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family = | |
420 | AF_INET6; | |
421 | socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_family = | |
422 | AF_INET6; | |
423 | socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port = | |
424 | htons(dest_port); | |
425 | socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_port = | |
426 | htons(src_port); | |
427 | memcpy(&socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6 | |
428 | .sin6_addr, | |
429 | dest_ip, sizeof(struct in6_addr)); | |
430 | if (src_ip != NULL) { | |
431 | memcpy(&socket_comm_session->src_sock_addr.src_sock_addr_ipv6 | |
432 | .sin6_addr, | |
433 | src_ip, sizeof(struct in6_addr)); | |
434 | } else { | |
435 | socket_comm_session->src_sock_addr.src_sock_addr_ipv6 | |
436 | .sin6_addr = in6addr_any; | |
437 | } | |
438 | ||
439 | if (socket_comm_session_initialize_post(socket_comm_session) == false) { | |
440 | return NULL; | |
441 | } | |
442 | ||
443 | return socket_comm_session; | |
444 | } | |
445 | ||
446 | ||
447 | bool socket_comm_session_connect_tcp( | |
448 | pcep_socket_comm_session *socket_comm_session) | |
449 | { | |
450 | if (socket_comm_session == NULL) { | |
451 | pcep_log( | |
452 | LOG_WARNING, | |
453 | "%s: socket_comm_session_connect_tcp NULL socket_comm_session.", | |
454 | __func__); | |
455 | return NULL; | |
456 | } | |
457 | ||
458 | /* Set the socket to non-blocking, so connect() does not block */ | |
459 | int fcntl_arg; | |
460 | if ((fcntl_arg = fcntl(socket_comm_session->socket_fd, F_GETFL, NULL)) | |
461 | < 0) { | |
462 | pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_GETFL) [%d %s]", | |
463 | __func__, errno, strerror(errno)); | |
464 | return false; | |
465 | } | |
466 | ||
467 | fcntl_arg |= O_NONBLOCK; | |
468 | if (fcntl(socket_comm_session->socket_fd, F_SETFL, fcntl_arg) < 0) { | |
469 | pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_SETFL) [%d %s]", | |
470 | __func__, errno, strerror(errno)); | |
471 | return false; | |
472 | } | |
473 | ||
474 | #if HAVE_DECL_TCP_MD5SIG | |
475 | /* TCP authentication, currently only TCP MD5 RFC2385 is supported */ | |
476 | if (socket_comm_session->tcp_authentication_str[0] != '\0') { | |
477 | #if defined(linux) || defined(GNU_LINUX) | |
478 | struct tcp_md5sig sig; | |
479 | memset(&sig, 0, sizeof(sig)); | |
480 | if (socket_comm_session->is_ipv6) { | |
481 | memcpy(&sig.tcpm_addr, | |
482 | &socket_comm_session->dest_sock_addr | |
483 | .dest_sock_addr_ipv6, | |
484 | sizeof(struct sockaddr_in6)); | |
485 | } else { | |
486 | memcpy(&sig.tcpm_addr, | |
487 | &socket_comm_session->dest_sock_addr | |
488 | .dest_sock_addr_ipv4, | |
489 | sizeof(struct sockaddr_in)); | |
490 | } | |
491 | sig.tcpm_keylen = | |
492 | strlen(socket_comm_session->tcp_authentication_str); | |
493 | memcpy(sig.tcpm_key, | |
494 | socket_comm_session->tcp_authentication_str, | |
495 | sig.tcpm_keylen); | |
496 | #else | |
497 | int sig = 1; | |
498 | #endif | |
499 | if (setsockopt(socket_comm_session->socket_fd, IPPROTO_TCP, | |
500 | TCP_MD5SIG, &sig, sizeof(sig)) | |
501 | == -1) { | |
502 | pcep_log(LOG_ERR, "%s: Failed to setsockopt(): [%d %s]", | |
503 | __func__, errno, strerror(errno)); | |
504 | return false; | |
505 | } | |
506 | } | |
507 | #endif | |
508 | ||
509 | int connect_result = 0; | |
510 | if (socket_comm_session->is_ipv6) { | |
511 | connect_result = connect( | |
512 | socket_comm_session->socket_fd, | |
513 | (struct sockaddr *)&(socket_comm_session->dest_sock_addr | |
514 | .dest_sock_addr_ipv6), | |
515 | sizeof(socket_comm_session->dest_sock_addr | |
516 | .dest_sock_addr_ipv6)); | |
517 | } else { | |
518 | connect_result = connect( | |
519 | socket_comm_session->socket_fd, | |
520 | (struct sockaddr *)&(socket_comm_session->dest_sock_addr | |
521 | .dest_sock_addr_ipv4), | |
522 | sizeof(socket_comm_session->dest_sock_addr | |
523 | .dest_sock_addr_ipv4)); | |
524 | } | |
525 | ||
526 | if (connect_result < 0) { | |
527 | if (errno == EINPROGRESS) { | |
528 | /* Calculate the configured timeout in seconds and | |
529 | * microseconds */ | |
530 | struct timeval tv; | |
531 | if (socket_comm_session->connect_timeout_millis | |
532 | > 1000) { | |
533 | tv.tv_sec = socket_comm_session | |
534 | ->connect_timeout_millis | |
535 | / 1000; | |
536 | tv.tv_usec = (socket_comm_session | |
537 | ->connect_timeout_millis | |
538 | - (tv.tv_sec * 1000)) | |
539 | * 1000; | |
540 | } else { | |
541 | tv.tv_sec = 0; | |
542 | tv.tv_usec = socket_comm_session | |
543 | ->connect_timeout_millis | |
544 | * 1000; | |
545 | } | |
546 | ||
547 | /* Use select to wait a max timeout for connect | |
548 | * https://stackoverflow.com/questions/2597608/c-socket-connection-timeout | |
549 | */ | |
550 | fd_set fdset; | |
551 | FD_ZERO(&fdset); | |
552 | FD_SET(socket_comm_session->socket_fd, &fdset); | |
553 | if (select(socket_comm_session->socket_fd + 1, NULL, | |
554 | &fdset, NULL, &tv) | |
555 | > 0) { | |
556 | int so_error; | |
557 | socklen_t len = sizeof(so_error); | |
558 | getsockopt(socket_comm_session->socket_fd, | |
559 | SOL_SOCKET, SO_ERROR, &so_error, | |
560 | &len); | |
561 | if (so_error) { | |
562 | pcep_log( | |
563 | LOG_WARNING, | |
564 | "%s: TCP connect failed on socket_fd [%d].", | |
565 | __func__, | |
566 | socket_comm_session->socket_fd); | |
567 | return false; | |
568 | } | |
569 | } else { | |
570 | pcep_log( | |
571 | LOG_WARNING, | |
572 | "%s: TCP connect timed-out on socket_fd [%d].", | |
573 | __func__, | |
574 | socket_comm_session->socket_fd); | |
575 | return false; | |
576 | } | |
577 | } else { | |
578 | pcep_log( | |
579 | LOG_WARNING, | |
580 | "%s: TCP connect, error connecting on socket_fd [%d] errno [%d %s]", | |
581 | __func__, socket_comm_session->socket_fd, errno, | |
582 | strerror(errno)); | |
583 | return false; | |
584 | } | |
585 | } | |
586 | ||
587 | pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); | |
588 | /* once the TCP connection is open, we should be ready to read at any | |
589 | * time */ | |
590 | ordered_list_add_node(socket_comm_handle_->read_list, | |
591 | socket_comm_session); | |
592 | ||
593 | if (socket_comm_handle_->socket_read_func != NULL) { | |
594 | socket_comm_handle_->socket_read_func( | |
595 | socket_comm_handle_->external_infra_data, | |
596 | &socket_comm_session->external_socket_data, | |
597 | socket_comm_session->socket_fd, socket_comm_handle_); | |
598 | } | |
599 | pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); | |
600 | ||
601 | return true; | |
602 | } | |
603 | ||
604 | ||
605 | bool socket_comm_session_close_tcp( | |
606 | pcep_socket_comm_session *socket_comm_session) | |
607 | { | |
608 | if (socket_comm_session == NULL) { | |
609 | pcep_log( | |
610 | LOG_WARNING, | |
611 | "%s: socket_comm_session_close_tcp NULL socket_comm_session.", | |
612 | __func__); | |
613 | return false; | |
614 | } | |
615 | ||
616 | pcep_log(LOG_DEBUG, | |
617 | "%s: socket_comm_session_close_tcp close() socket fd [%d]", | |
618 | __func__, socket_comm_session->socket_fd); | |
619 | ||
620 | pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); | |
621 | ordered_list_remove_first_node_equals(socket_comm_handle_->read_list, | |
622 | socket_comm_session); | |
623 | ordered_list_remove_first_node_equals(socket_comm_handle_->write_list, | |
624 | socket_comm_session); | |
625 | // TODO should it be close() or shutdown()?? | |
626 | close(socket_comm_session->socket_fd); | |
627 | socket_comm_session->socket_fd = -1; | |
628 | pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); | |
629 | ||
630 | return true; | |
631 | } | |
632 | ||
633 | ||
634 | bool socket_comm_session_close_tcp_after_write( | |
635 | pcep_socket_comm_session *socket_comm_session) | |
636 | { | |
637 | if (socket_comm_session == NULL) { | |
638 | pcep_log( | |
639 | LOG_WARNING, | |
640 | "%s: socket_comm_session_close_tcp_after_write NULL socket_comm_session.", | |
641 | __func__); | |
642 | return false; | |
643 | } | |
644 | ||
645 | pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); | |
646 | socket_comm_session->close_after_write = true; | |
647 | pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); | |
648 | ||
649 | return true; | |
650 | } | |
651 | ||
652 | ||
653 | bool socket_comm_session_teardown(pcep_socket_comm_session *socket_comm_session) | |
654 | { | |
655 | if (socket_comm_handle_ == NULL) { | |
656 | pcep_log(LOG_WARNING, | |
657 | "%s: Cannot teardown NULL socket_comm_handle", | |
658 | __func__); | |
659 | return false; | |
660 | } | |
661 | ||
662 | if (socket_comm_session == NULL) { | |
663 | pcep_log(LOG_WARNING, "%s: Cannot teardown NULL session", | |
664 | __func__); | |
665 | return false; | |
666 | } | |
667 | ||
668 | if (comm_session_exists_locking(socket_comm_handle_, | |
669 | socket_comm_session) | |
670 | == false) { | |
671 | pcep_log(LOG_WARNING, | |
672 | "%s: Cannot teardown session that does not exist", | |
673 | __func__); | |
674 | return false; | |
675 | } | |
676 | ||
677 | if (socket_comm_session->socket_fd >= 0) { | |
678 | shutdown(socket_comm_session->socket_fd, SHUT_RDWR); | |
679 | close(socket_comm_session->socket_fd); | |
680 | } | |
681 | ||
682 | pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); | |
683 | queue_destroy(socket_comm_session->message_queue); | |
684 | ordered_list_remove_first_node_equals(socket_comm_handle_->session_list, | |
685 | socket_comm_session); | |
686 | ordered_list_remove_first_node_equals(socket_comm_handle_->read_list, | |
687 | socket_comm_session); | |
688 | ordered_list_remove_first_node_equals(socket_comm_handle_->write_list, | |
689 | socket_comm_session); | |
690 | socket_comm_handle_->num_active_sessions--; | |
691 | pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); | |
692 | ||
693 | pcep_log( | |
694 | LOG_INFO, | |
695 | "%s: [%ld-%ld] socket_comm_session fd [%d] destroyed, [%d] sessions remaining", | |
696 | __func__, time(NULL), pthread_self(), | |
697 | socket_comm_session->socket_fd, | |
698 | socket_comm_handle_->num_active_sessions); | |
699 | ||
700 | pceplib_free(PCEPLIB_INFRA, socket_comm_session); | |
701 | ||
702 | /* It would be nice to call destroy_socket_comm_loop() here if | |
703 | * socket_comm_handle_->num_active_sessions == 0, but this function | |
704 | * will usually be called from the message_sent_notifier callback, | |
705 | * which gets called in the middle of the socket_comm_loop, and that | |
706 | * is dangerous, so destroy_socket_comm_loop() must be called upon | |
707 | * application exit. */ | |
708 | ||
709 | return true; | |
710 | } | |
711 | ||
712 | ||
713 | void socket_comm_session_send_message( | |
714 | pcep_socket_comm_session *socket_comm_session, | |
715 | const char *encoded_message, unsigned int msg_length, | |
716 | bool free_after_send) | |
717 | { | |
718 | if (socket_comm_session == NULL) { | |
719 | pcep_log( | |
720 | LOG_WARNING, | |
721 | "%s: socket_comm_session_send_message NULL socket_comm_session.", | |
722 | __func__); | |
723 | return; | |
724 | } | |
725 | ||
726 | pcep_socket_comm_queued_message *queued_message = pceplib_malloc( | |
727 | PCEPLIB_MESSAGES, sizeof(pcep_socket_comm_queued_message)); | |
728 | queued_message->encoded_message = encoded_message; | |
729 | queued_message->msg_length = msg_length; | |
730 | queued_message->free_after_send = free_after_send; | |
731 | ||
732 | pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); | |
733 | ||
734 | /* Do not proceed if the socket_comm_session has been deleted */ | |
735 | if (ordered_list_find(socket_comm_handle_->session_list, | |
736 | socket_comm_session) | |
737 | == NULL) { | |
738 | /* Should never get here, only if the session was deleted and | |
739 | * someone still tries to write on it */ | |
740 | pcep_log( | |
741 | LOG_WARNING, | |
742 | "%s: Cannot write a message on a deleted socket comm session, discarding message", | |
743 | __func__); | |
744 | pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); | |
745 | pceplib_free(PCEPLIB_MESSAGES, queued_message); | |
746 | ||
747 | return; | |
748 | } | |
749 | ||
750 | /* Do not proceed if the socket has been closed */ | |
751 | if (socket_comm_session->socket_fd < 0) { | |
752 | /* Should never get here, only if the session was deleted and | |
753 | * someone still tries to write on it */ | |
754 | pcep_log( | |
755 | LOG_WARNING, | |
756 | "%s: Cannot write a message on a closed socket, discarding message", | |
757 | __func__); | |
758 | pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); | |
759 | pceplib_free(PCEPLIB_MESSAGES, queued_message); | |
760 | ||
761 | return; | |
762 | } | |
763 | ||
764 | queue_enqueue(socket_comm_session->message_queue, queued_message); | |
765 | ||
766 | /* Add it to the write list only if its not already there */ | |
767 | if (ordered_list_find(socket_comm_handle_->write_list, | |
768 | socket_comm_session) | |
769 | == NULL) { | |
770 | ordered_list_add_node(socket_comm_handle_->write_list, | |
771 | socket_comm_session); | |
772 | } | |
773 | ||
774 | if (socket_comm_handle_->socket_write_func != NULL) { | |
775 | socket_comm_handle_->socket_write_func( | |
776 | socket_comm_handle_->external_infra_data, | |
777 | &socket_comm_session->external_socket_data, | |
778 | socket_comm_session->socket_fd, socket_comm_handle_); | |
779 | } | |
780 | pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); | |
781 | } |