Changes in freeDiameter/cnxctx.c [30:bca243c65b56:20:277ec00d793e] in freeDiameter
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
freeDiameter/cnxctx.c
r30 r20 35 35 36 36 #include "fD.h" 37 #include "cnxctx.h"38 37 39 /* The maximum size of Diameter message we accept to receive (<= 2^24) to avoid too big mallocs in case of trashed headers */ 40 #ifndef DIAMETER_MSG_SIZE_MAX 41 #define DIAMETER_MSG_SIZE_MAX 65535 /* in bytes */ 42 #endif /* DIAMETER_MSG_SIZE_MAX */ 43 44 /* Connections contexts (cnxctx) in freeDiameter are wrappers around the sockets and TLS operations . 45 * They are used to hide the details of the processing to the higher layers of the daemon. 46 * They are always oriented on connections (TCP or SCTP), connectionless modes (UDP or SCTP) are not supported. 47 */ 48 49 /* Note: this file could be moved to libfreeDiameter instead, but since it uses gnuTLS we prefer to keep it in the daemon */ 50 51 /* Lifetime of a cnxctx object: 52 * 1) Creation 53 * a) a server socket: 54 * - create the object with fd_cnx_serv_tcp or fd_cnx_serv_sctp 55 * - start listening incoming connections: fd_cnx_serv_listen 56 * - accept new clients with fd_cnx_serv_accept. 57 * b) a client socket: 58 * - connect to a remote server with fd_cnx_cli_connect 59 * 60 * 2) Initialization 61 * - if TLS is started first, call fd_cnx_handshake 62 * - otherwise to receive clear messages, call fd_cnx_start_clear. fd_cnx_handshake can be called later. 63 * 64 * 3) Usage 65 * - fd_cnx_receive, fd_cnx_send : exchange messages on this connection (send is synchronous, receive is not, but blocking). 66 * - fd_cnx_recv_setaltfifo : when a message is received, the event is sent to an external fifo list. fd_cnx_receive does not work when the alt_fifo is set. 67 * - fd_cnx_getid : retrieve a descriptive string for the connection (for debug) 68 * - fd_cnx_getremoteid : identification of the remote peer (IP address or fqdn) 69 * - fd_cnx_getcred : get the remote peer TLS credentials, after handshake 70 * - fd_cnx_getendpoints : get the endpoints (IP) of the connection 71 * 72 * 4) End 73 * - fd_cnx_destroy 74 */ 75 76 77 /*******************************************/ 78 /* Creation of a connection object */ 79 /*******************************************/ 80 81 /* Initialize a context structure */ 82 static struct cnxctx * fd_cnx_init(int full) 38 /* Initialize a connection context */ 39 struct cnxctx * fd_cnx_init(int sock, int proto) 83 40 { 84 41 struct cnxctx * conn = NULL; 85 86 TRACE_ENTRY("%d", full); 87 42 43 TRACE_ENTRY("%d %d", sock, proto); 44 CHECK_PARAMS_DO( (proto == IPPROTO_TCP) || (proto == IPPROTO_SCTP), return NULL); 45 88 46 CHECK_MALLOC_DO( conn = malloc(sizeof(struct cnxctx)), return NULL ); 89 47 memset(conn, 0, sizeof(struct cnxctx)); 90 91 if (full) { 92 CHECK_FCT_DO( fd_fifo_new ( &conn->cc_incoming ), return NULL ); 48 49 conn->cc_socket = sock; 50 conn->cc_proto = proto; 51 52 fd_list_init(&conn->cc_ep_remote, conn); 53 fd_list_init(&conn->cc_ep_local, conn); 54 55 if (proto == IPPROTO_SCTP) { 56 #ifndef DISABLE_SCTP 57 CHECK_FCT_DO( fd_sctp_get_str_info( sock, &conn->cc_sctp_para.str_in, &conn->cc_sctp_para.str_out ), 58 { free(conn); return NULL; } ); 59 conn->cc_sctp_para.pairs = (conn->cc_sctp_para.str_out < conn->cc_sctp_para.str_in) ? conn->cc_sctp_para.str_out : conn->cc_sctp_para.str_in; 60 #else /* DISABLE_SCTP */ 61 ASSERT(0); 62 #endif /* DISABLE_SCTP */ 93 63 } 94 64 95 65 return conn; 96 66 } 97 67 98 /* Create and bind a server socket to the given endpoint and port */ 99 struct cnxctx * fd_cnx_serv_tcp(uint16_t port, int family, struct fd_endpoint * ep) 100 { 101 struct cnxctx * cnx = NULL; 102 sSS dummy; 103 sSA * sa = (sSA *) &dummy; 104 105 TRACE_ENTRY("%hu %d %p", port, family, ep); 106 107 CHECK_PARAMS_DO( port, return NULL ); 108 CHECK_PARAMS_DO( ep || family, return NULL ); 109 CHECK_PARAMS_DO( (! family) || (family == AF_INET) || (family == AF_INET6), return NULL ); 110 CHECK_PARAMS_DO( (! ep) || (!family) || (ep->ss.ss_family == family), return NULL ); 111 112 /* The connection object */ 113 CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL ); 114 115 /* Prepare the socket address information */ 116 if (ep) { 117 memcpy(sa, &ep->ss, sizeof(sSS)); 118 } else { 119 memset(&dummy, 0, sizeof(dummy)); 120 sa->sa_family = family; 121 } 122 if (sa->sa_family == AF_INET) { 123 ((sSA4 *)sa)->sin_port = htons(port); 124 } else { 125 ((sSA6 *)sa)->sin6_port = htons(port); 126 } 127 128 /* Create the socket */ 129 CHECK_FCT_DO( fd_tcp_create_bind_server( &cnx->cc_socket, sa, sizeof(sSS) ), goto error ); 130 131 /* Generate the name for the connection object */ 132 { 133 char addrbuf[INET6_ADDRSTRLEN]; 134 int rc; 135 rc = getnameinfo(sa, sizeof(sSS), addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); 136 if (rc) 137 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); 138 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Srv TCP [%s]:%hu (%d)", addrbuf, port, cnx->cc_socket); 139 } 140 141 cnx->cc_proto = IPPROTO_TCP; 142 143 return cnx; 144 145 error: 146 fd_cnx_destroy(cnx); 147 return NULL; 148 } 149 150 /* Same function for SCTP, with a list of local endpoints to bind to */ 151 struct cnxctx * fd_cnx_serv_sctp(uint16_t port, struct fd_list * ep_list) 152 { 153 #ifdef DISABLE_SCTP 154 TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled..."); 155 ASSERT(0); 156 CHECK_FCT_DO( ENOTSUP, return NULL); 157 #else /* DISABLE_SCTP */ 158 struct cnxctx * cnx = NULL; 159 sSS dummy; 160 sSA * sa = (sSA *) &dummy; 161 162 TRACE_ENTRY("%hu %p", port, ep_list); 163 164 CHECK_PARAMS_DO( port, return NULL ); 165 166 /* The connection object */ 167 CHECK_MALLOC_DO( cnx = fd_cnx_init(0), return NULL ); 168 169 /* Create the socket */ 170 CHECK_FCT_DO( fd_sctp_create_bind_server( &cnx->cc_socket, ep_list, port ), goto error ); 171 172 /* Generate the name for the connection object */ 173 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Srv SCTP :%hu (%d)", port, cnx->cc_socket); 174 175 cnx->cc_proto = IPPROTO_SCTP; 176 177 return cnx; 178 179 error: 180 fd_cnx_destroy(cnx); 181 return NULL; 182 #endif /* DISABLE_SCTP */ 183 } 184 185 /* Allow clients to connect on the server socket */ 186 int fd_cnx_serv_listen(struct cnxctx * conn) 187 { 188 CHECK_PARAMS( conn ); 189 190 switch (conn->cc_proto) { 191 case IPPROTO_TCP: 192 CHECK_FCT(fd_tcp_listen(conn->cc_socket)); 193 break; 194 195 #ifndef DISABLE_SCTP 196 case IPPROTO_SCTP: 197 CHECK_FCT(fd_sctp_listen(conn->cc_socket)); 198 break; 199 #endif /* DISABLE_SCTP */ 200 201 default: 202 CHECK_PARAMS(0); 203 } 204 205 return 0; 206 } 207 208 /* Accept a client (blocking until a new client connects) -- cancelable */ 209 struct cnxctx * fd_cnx_serv_accept(struct cnxctx * serv) 210 { 211 struct cnxctx * cli = NULL; 212 sSS ss; 213 socklen_t ss_len = sizeof(ss); 214 int cli_sock = 0; 215 struct fd_endpoint * ep; 216 217 TRACE_ENTRY("%p", serv); 218 CHECK_PARAMS_DO(serv, return NULL); 219 220 /* Accept the new connection -- this is blocking until new client enters or cancellation */ 221 CHECK_SYS_DO( cli_sock = accept(serv->cc_socket, (sSA *)&ss, &ss_len), return NULL ); 222 223 if (TRACE_BOOL(INFO)) { 224 fd_log_debug("%s : accepted new client [", fd_cnx_getid(serv)); 225 sSA_DUMP_NODE( &ss, NI_NUMERICHOST ); 226 fd_log_debug("].\n"); 227 } 228 229 CHECK_MALLOC_DO( cli = fd_cnx_init(1), { shutdown(cli_sock, SHUT_RDWR); return NULL; } ); 230 cli->cc_socket = cli_sock; 231 cli->cc_proto = serv->cc_proto; 232 233 /* Generate the name for the connection object */ 234 { 235 char addrbuf[INET6_ADDRSTRLEN]; 236 char portbuf[10]; 237 int rc; 238 239 /* Numeric values for debug */ 240 rc = getnameinfo((sSA *)&ss, sizeof(sSS), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV); 241 if (rc) { 242 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); 243 portbuf[0] = '\0'; 244 } 245 246 snprintf(cli->cc_id, sizeof(cli->cc_id), "Incoming %s [%s]:%s (%d) @ serv (%d)", 247 IPPROTO_NAME(cli->cc_proto), 248 addrbuf, portbuf, 249 cli->cc_socket, serv->cc_socket); 250 251 /* Name for log messages */ 252 rc = getnameinfo((sSA *)&ss, sizeof(sSS), cli->cc_remid, sizeof(cli->cc_remid), NULL, 0, 0); 253 if (rc) 254 snprintf(cli->cc_remid, sizeof(cli->cc_remid), "[err:%s]", gai_strerror(rc)); 255 } 256 257 #ifndef DISABLE_SCTP 258 /* SCTP-specific handlings */ 259 if (cli->cc_proto == IPPROTO_SCTP) { 260 /* Retrieve the number of streams */ 261 CHECK_FCT_DO( fd_sctp_get_str_info( cli->cc_socket, &cli->cc_sctp_para.str_in, &cli->cc_sctp_para.str_out, NULL ), goto error ); 262 if (cli->cc_sctp_para.str_out > cli->cc_sctp_para.str_in) 263 cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_out; 264 else 265 cli->cc_sctp_para.pairs = cli->cc_sctp_para.str_in; 266 } 267 #endif /* DISABLE_SCTP */ 268 269 return cli; 270 error: 271 fd_cnx_destroy(cli); 272 return NULL; 273 } 274 275 /* Client side: connect to a remote server -- cancelable */ 276 struct cnxctx * fd_cnx_cli_connect_tcp(sSA * sa /* contains the port already */, socklen_t addrlen) 277 { 278 int sock; 279 struct cnxctx * cnx = NULL; 280 281 TRACE_ENTRY("%p %d", sa, addrlen); 282 CHECK_PARAMS_DO( sa && addrlen, return NULL ); 283 284 /* Create the socket and connect, which can take some time and/or fail */ 285 CHECK_FCT_DO( fd_tcp_client( &sock, sa, addrlen ), return NULL ); 286 287 if (TRACE_BOOL(INFO)) { 288 fd_log_debug("Connection established to server '"); 289 sSA_DUMP_NODE_SERV( sa, NI_NUMERICSERV); 290 fd_log_debug("' (TCP:%d).\n", sock); 291 } 292 293 /* Once the socket is created successfuly, prepare the remaining of the cnx */ 294 CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); return NULL; } ); 295 296 cnx->cc_socket = sock; 297 cnx->cc_proto = IPPROTO_TCP; 298 299 /* Generate the names for the object */ 300 { 301 char addrbuf[INET6_ADDRSTRLEN]; 302 char portbuf[10]; 303 int rc; 304 305 /* Numeric values for debug */ 306 rc = getnameinfo(sa, addrlen, addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV); 307 if (rc) { 308 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); 309 portbuf[0] = '\0'; 310 } 311 312 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Client of TCP server [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); 313 314 /* Name for log messages */ 315 rc = getnameinfo(sa, addrlen, cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); 316 if (rc) 317 snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); 318 } 319 320 return cnx; 321 322 error: 323 fd_cnx_destroy(cnx); 324 return NULL; 325 } 326 327 /* Same for SCTP, accepts a list of remote addresses to connect to (see sctp_connectx for how they are used) */ 328 struct cnxctx * fd_cnx_cli_connect_sctp(int no_ip6, uint16_t port, struct fd_list * list) 329 { 330 #ifdef DISABLE_SCTP 331 TRACE_DEBUG(INFO, "This function should never been called when SCTP is disabled..."); 332 ASSERT(0); 333 CHECK_FCT_DO( ENOTSUP, return NULL); 334 #else /* DISABLE_SCTP */ 335 int sock; 336 struct cnxctx * cnx = NULL; 337 sSS primary; 338 339 TRACE_ENTRY("%p", list); 340 CHECK_PARAMS_DO( list && !FD_IS_LIST_EMPTY(list), return NULL ); 341 342 CHECK_FCT_DO( fd_sctp_client( &sock, no_ip6, port, list ), return NULL ); 343 344 /* Once the socket is created successfuly, prepare the remaining of the cnx */ 345 CHECK_MALLOC_DO( cnx = fd_cnx_init(1), { shutdown(sock, SHUT_RDWR); return NULL; } ); 346 347 cnx->cc_socket = sock; 348 cnx->cc_proto = IPPROTO_SCTP; 349 350 /* Retrieve the number of streams and primary address */ 351 CHECK_FCT_DO( fd_sctp_get_str_info( sock, &cnx->cc_sctp_para.str_in, &cnx->cc_sctp_para.str_out, &primary ), goto error ); 352 if (cnx->cc_sctp_para.str_out > cnx->cc_sctp_para.str_in) 353 cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_out; 354 else 355 cnx->cc_sctp_para.pairs = cnx->cc_sctp_para.str_in; 356 357 if (TRACE_BOOL(INFO)) { 358 fd_log_debug("Connection established to server '"); 359 sSA_DUMP_NODE_SERV( &primary, NI_NUMERICSERV); 360 fd_log_debug("' (SCTP:%d, %d/%d streams).\n", sock, cnx->cc_sctp_para.str_in, cnx->cc_sctp_para.str_out); 361 } 362 363 /* Generate the names for the object */ 364 { 365 char addrbuf[INET6_ADDRSTRLEN]; 366 char portbuf[10]; 367 int rc; 368 369 /* Numeric values for debug */ 370 rc = getnameinfo((sSA *)&primary, sizeof(sSS), addrbuf, sizeof(addrbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV); 371 if (rc) { 372 snprintf(addrbuf, sizeof(addrbuf), "[err:%s]", gai_strerror(rc)); 373 portbuf[0] = '\0'; 374 } 375 376 snprintf(cnx->cc_id, sizeof(cnx->cc_id), "Client of SCTP server [%s]:%s (%d)", addrbuf, portbuf, cnx->cc_socket); 377 378 /* Name for log messages */ 379 rc = getnameinfo((sSA *)&primary, sizeof(sSS), cnx->cc_remid, sizeof(cnx->cc_remid), NULL, 0, 0); 380 if (rc) 381 snprintf(cnx->cc_remid, sizeof(cnx->cc_remid), "[err:%s]", gai_strerror(rc)); 382 } 383 384 return cnx; 385 386 error: 387 fd_cnx_destroy(cnx); 388 return NULL; 389 #endif /* DISABLE_SCTP */ 390 } 391 392 /* Return a string describing the connection, for debug */ 393 char * fd_cnx_getid(struct cnxctx * conn) 394 { 395 CHECK_PARAMS_DO( conn, return "" ); 396 return conn->cc_id; 397 } 398 399 /* Return the protocol of a connection */ 400 int fd_cnx_getproto(struct cnxctx * conn) 401 { 402 CHECK_PARAMS_DO( conn, return 0 ); 403 return conn->cc_proto; 404 } 405 406 /* Return the TLS state of a connection */ 407 int fd_cnx_getTLS(struct cnxctx * conn) 408 { 409 CHECK_PARAMS_DO( conn, return 0 ); 410 return conn->cc_tls; 411 } 412 413 /* Get the list of endpoints (IP addresses) of the local and remote peers on this connection */ 414 int fd_cnx_getendpoints(struct cnxctx * conn, struct fd_list * local, struct fd_list * remote) 415 { 416 TRACE_ENTRY("%p %p %p", conn, local, remote); 417 CHECK_PARAMS(conn); 418 419 if (local) { 420 /* Retrieve the local endpoint(s) of the connection */ 421 switch (conn->cc_proto) { 422 case IPPROTO_TCP: { 423 sSS ss; 424 socklen_t sl; 425 CHECK_FCT(fd_tcp_get_local_ep(conn->cc_socket, &ss, &sl)); 426 CHECK_FCT(fd_ep_add_merge( local, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY)); 427 } 428 break; 429 430 #ifndef DISABLE_SCTP 431 case IPPROTO_SCTP: { 432 CHECK_FCT(fd_sctp_get_local_ep(conn->cc_socket, local)); 433 } 434 break; 435 #endif /* DISABLE_SCTP */ 436 437 default: 438 CHECK_PARAMS(0); 439 } 440 } 441 442 if (remote) { 443 /* Check we have a full connection object, not a listening socket (with no remote) */ 444 CHECK_PARAMS( conn->cc_incoming ); 445 446 /* Retrieve the peer endpoint(s) of the connection */ 447 switch (conn->cc_proto) { 448 case IPPROTO_TCP: { 449 sSS ss; 450 socklen_t sl; 451 CHECK_FCT(fd_tcp_get_remote_ep(conn->cc_socket, &ss, &sl)); 452 CHECK_FCT(fd_ep_add_merge( remote, (sSA *)&ss, sl, EP_FL_LL | EP_FL_PRIMARY )); 453 } 454 break; 455 456 #ifndef DISABLE_SCTP 457 case IPPROTO_SCTP: { 458 CHECK_FCT(fd_sctp_get_remote_ep(conn->cc_socket, remote)); 459 } 460 break; 461 #endif /* DISABLE_SCTP */ 462 463 default: 464 CHECK_PARAMS(0); 465 } 466 } 467 468 return 0; 469 } 470 471 472 /* Get a string describing the remote peer address (ip address or fqdn) */ 473 char * fd_cnx_getremoteid(struct cnxctx * conn) 474 { 475 CHECK_PARAMS_DO( conn, return "" ); 476 return conn->cc_remid; 477 } 478 479 480 /**************************************/ 481 /* Use of a connection object */ 482 /**************************************/ 483 484 /* Receiver thread (TCP & noTLS) : incoming message is directly saved into the target queue */ 485 static void * rcvthr_notls_tcp(void * arg) 486 { 487 struct cnxctx * conn = arg; 488 489 TRACE_ENTRY("%p", arg); 490 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out); 491 492 /* Set the thread name */ 493 { 494 char buf[48]; 495 snprintf(buf, sizeof(buf), "Receiver (%d) TCP/noTLS)", conn->cc_socket); 496 fd_log_threadname ( buf ); 497 } 498 499 ASSERT( conn->cc_proto == IPPROTO_TCP ); 500 ASSERT( conn->cc_tls == 0 ); 501 ASSERT( Target_Queue(conn) ); 502 503 /* Receive from a TCP connection: we have to rebuild the message boundaries */ 504 do { 505 uint8_t header[4]; 506 uint8_t * newmsg; 507 size_t length; 508 ssize_t ret = 0; 509 size_t received = 0; 510 511 do { 512 ret = recv(conn->cc_socket, &header[received], sizeof(header) - received, 0); 513 if (ret <= 0) { 514 CHECK_SYS_DO(ret, /* continue */); 515 goto error; /* Stop the thread, the recipient of the event will cleanup */ 516 } 517 518 received += ret; 519 } while (received < sizeof(header)); 520 521 length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3]; 522 523 /* Check the received word is a valid begining of a Diameter message */ 524 if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */ 525 || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */ 526 /* The message is suspect */ 527 TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length); 528 goto error; /* Stop the thread, the recipient of the event will cleanup */ 529 } 530 531 /* Ok, now we can really receive the data */ 532 CHECK_MALLOC_DO( newmsg = malloc( length ), goto error ); 533 memcpy(newmsg, header, sizeof(header)); 534 535 while (received < length) { 536 pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */ 537 ret = recv(conn->cc_socket, newmsg + received, length - received, 0); 538 pthread_cleanup_pop(0); 539 540 if (ret <= 0) { 541 CHECK_SYS_DO(ret, /* continue */); 542 free(newmsg); 543 goto error; /* Stop the thread, the recipient of the event will cleanup */ 544 } 545 received += ret; 546 } 547 548 /* We have received a complete message, send it */ 549 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */); 550 551 } while (conn->cc_loop); 552 553 out: 554 TRACE_DEBUG(FULL, "Thread terminated"); 555 return NULL; 556 error: 557 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), /* continue or destroy everything? */); 558 goto out; 559 } 560 561 #ifndef DISABLE_SCTP 562 /* Receiver thread (SCTP & noTLS) : incoming message is directly saved into cc_incoming, no need to care for the stream ID */ 563 static void * rcvthr_notls_sctp(void * arg) 564 { 565 struct cnxctx * conn = arg; 566 uint8_t * buf; 567 size_t bufsz; 568 int event; 569 570 TRACE_ENTRY("%p", arg); 571 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto out); 572 573 /* Set the thread name */ 574 { 575 char buf[48]; 576 snprintf(buf, sizeof(buf), "Receiver (%d) SCTP/noTLS)", conn->cc_socket); 577 fd_log_threadname ( buf ); 578 } 579 580 ASSERT( conn->cc_proto == IPPROTO_SCTP ); 581 ASSERT( conn->cc_tls == 0 ); 582 ASSERT( Target_Queue(conn) ); 583 584 do { 585 CHECK_FCT_DO( fd_sctp_recvmeta(conn->cc_socket, NULL, &buf, &bufsz, &event), goto error ); 586 if (event == FDEVP_CNX_ERROR) { 587 goto error; 588 } 589 590 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), event, bufsz, buf), goto error ); 591 592 } while (conn->cc_loop); 593 594 out: 595 TRACE_DEBUG(FULL, "Thread terminated"); 596 return NULL; 597 error: 598 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), /* continue or destroy everything? */); 599 goto out; 600 } 601 #endif /* DISABLE_SCTP */ 602 603 /* Returns 0 on error, received data size otherwise (always >= 0) */ 604 static ssize_t fd_tls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz) 605 { 606 ssize_t ret; 607 again: 608 CHECK_GNUTLS_DO( ret = gnutls_record_recv(session, data, sz), 609 { 610 switch (ret) { 611 case GNUTLS_E_REHANDSHAKE: 612 CHECK_GNUTLS_DO( ret = gnutls_handshake(session), 613 { 614 if (TRACE_BOOL(INFO)) { 615 fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); 616 } 617 ret = 0; 618 goto end; 619 } ); 620 621 case GNUTLS_E_AGAIN: 622 case GNUTLS_E_INTERRUPTED: 623 goto again; 624 625 default: 626 TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error"); 627 ret = 0; 628 } 629 } ); 630 end: 631 return ret; 632 } 633 634 /* The function that receives TLS data and re-builds a Diameter message -- it exits only on error or cancelation */ 635 int fd_tls_rcvthr_core(struct cnxctx * conn, gnutls_session_t session) 636 { 637 /* No guarantee that GnuTLS preserves the message boundaries, so we re-build it as in TCP */ 638 do { 639 uint8_t header[4]; 640 uint8_t * newmsg; 641 size_t length; 642 ssize_t ret = 0; 643 size_t received = 0; 644 645 do { 646 ret = fd_tls_recv_handle_error(conn, session, &header[received], sizeof(header) - received); 647 if (ret == 0) { 648 /* The connection is closed */ 649 goto out; 650 } 651 received += ret; 652 } while (received < sizeof(header)); 653 654 length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3]; 655 656 /* Check the received word is a valid beginning of a Diameter message */ 657 if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */ 658 || (length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */ 659 /* The message is suspect */ 660 TRACE_DEBUG(INFO, "Received suspect header [ver: %d, size: %zd], assume disconnection", (int)header[0], length); 661 goto out; 662 } 663 664 /* Ok, now we can really receive the data */ 665 CHECK_MALLOC( newmsg = malloc( length ) ); 666 memcpy(newmsg, header, sizeof(header)); 667 668 while (received < length) { 669 pthread_cleanup_push(free, newmsg); /* In case we are canceled, clean the partialy built buffer */ 670 ret = fd_tls_recv_handle_error(conn, session, newmsg + received, length - received); 671 pthread_cleanup_pop(0); 672 673 if (ret == 0) { 674 free(newmsg); 675 goto out; /* Stop the thread, the recipient of the event will cleanup */ 676 } 677 received += ret; 678 } 679 680 /* We have received a complete message, send it */ 681 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_MSG_RECV, length, newmsg), /* continue or destroy everything? */); 682 683 } while (1); 684 out: 685 return ENOTCONN; 686 } 687 688 /* Receiver thread (TLS & 1 stream SCTP or TCP) : gnutls directly handles the socket, save records into the target queue */ 689 static void * rcvthr_tls_single(void * arg) 690 { 691 struct cnxctx * conn = arg; 692 693 TRACE_ENTRY("%p", arg); 694 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), goto error); 695 696 /* Set the thread name */ 697 { 698 char buf[48]; 699 snprintf(buf, sizeof(buf), "Receiver (%d) TLS/ single stream)", conn->cc_socket); 700 fd_log_threadname ( buf ); 701 } 702 703 ASSERT( conn->cc_tls == 1 ); 704 ASSERT( Target_Queue(conn) ); 705 706 CHECK_FCT_DO(fd_tls_rcvthr_core(conn, conn->cc_tls_para.session), /* continue */); 707 error: 708 CHECK_FCT_DO( fd_event_send( Target_Queue(conn), FDEVP_CNX_ERROR, 0, NULL), /* continue or destroy everything? */); 709 TRACE_DEBUG(FULL, "Thread terminated"); 710 return NULL; 711 } 712 713 /* Start receving messages in clear (no TLS) on the connection */ 714 int fd_cnx_start_clear(struct cnxctx * conn, int loop) 715 { 716 TRACE_ENTRY("%p %i", conn, loop); 717 718 CHECK_PARAMS( conn && Target_Queue(conn) && (!conn->cc_tls) && (!conn->cc_loop)); 719 720 /* Save the loop request */ 721 conn->cc_loop = loop; 722 723 /* Release resources in case of a previous call was already made */ 724 CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */); 725 726 switch (conn->cc_proto) { 727 case IPPROTO_TCP: 728 /* Start the tcp_notls thread */ 729 CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_tcp, conn ) ); 730 break; 731 #ifndef DISABLE_SCTP 732 case IPPROTO_SCTP: 733 /* Start the tcp_notls thread */ 734 CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_notls_sctp, conn ) ); 735 break; 736 #endif /* DISABLE_SCTP */ 737 default: 738 TRACE_DEBUG(INFO, "Unknown protocol: %d", conn->cc_proto); 739 return ENOTSUP; 740 } 741 742 return 0; 743 } 744 745 /* Prepare a gnutls session object for handshake */ 746 int fd_tls_prepare(gnutls_session_t * session, int mode, char * priority, void * alt_creds) 747 { 748 /* Create the master session context */ 749 CHECK_GNUTLS_DO( gnutls_init (session, mode), return ENOMEM ); 750 751 /* Set the algorithm suite */ 752 if (priority) { 753 const char * errorpos; 754 CHECK_GNUTLS_DO( gnutls_priority_set_direct( *session, priority, &errorpos ), 755 { TRACE_DEBUG(INFO, "Error in priority string '%s' at position: '%s'\n", priority, errorpos); return EINVAL; } ); 756 } else { 757 CHECK_GNUTLS_DO( gnutls_priority_set( *session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL ); 758 } 759 760 /* Set the credentials of this side of the connection */ 761 CHECK_GNUTLS_DO( gnutls_credentials_set (*session, GNUTLS_CRD_CERTIFICATE, alt_creds ?: fd_g_config->cnf_sec_data.credentials), return EINVAL ); 762 763 /* Request the remote credentials as well */ 764 if (mode == GNUTLS_SERVER) { 765 gnutls_certificate_server_set_request (*session, GNUTLS_CERT_REQUIRE); 766 } 767 768 return 0; 769 } 770 771 /* TLS handshake a connection; no need to have called start_clear before. Reception is active if handhsake is successful */ 772 int fd_cnx_handshake(struct cnxctx * conn, int mode, char * priority, void * alt_creds) 68 /* TLS handshake the connection */ 69 int fd_cnx_handshake(struct cnxctx * conn, int mode) 773 70 { 774 71 TRACE_ENTRY( "%p %d", conn, mode); 775 CHECK_PARAMS( conn && ( !conn->cc_tls) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) );776 72 CHECK_PARAMS( conn && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) ); 73 777 74 /* Save the mode */ 778 75 conn->cc_tls_para.mode = mode; 779 76 780 /* C ancel receiving thread if any -- it should already be terminated anyway, we just release the resources*/781 CHECK_ FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */);77 /* Create the master session context */ 78 CHECK_GNUTLS_DO( gnutls_init (&conn->cc_tls_para.session, mode), return ENOMEM ); 782 79 783 /* Once TLS handshake is done, we don't stop after the first message */784 conn->cc_loop = 1;80 /* Set the algorithm suite */ 81 CHECK_GNUTLS_DO( gnutls_priority_set( conn->cc_tls_para.session, fd_g_config->cnf_sec_data.prio_cache ), return EINVAL ); 785 82 786 /* Prepare the master session credentials and priority */ 787 CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, priority, alt_creds) ); 83 /* Set the credentials of this side of the connection */ 84 CHECK_GNUTLS_DO( gnutls_credentials_set (conn->cc_tls_para.session, GNUTLS_CRD_CERTIFICATE, fd_g_config->cnf_sec_data.credentials), return EINVAL ); 85 86 /* Request the remote credentials as well */ 87 if (mode == GNUTLS_SERVER) { 88 gnutls_certificate_server_set_request (conn->cc_tls_para.session, GNUTLS_CERT_REQUIRE); 89 } 90 91 /* Set the socket info in the session */ 92 gnutls_transport_set_ptr (conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn->cc_socket); 788 93 789 94 /* Special case: multi-stream TLS is not natively managed in GNU TLS, we use a wrapper library */ 790 if (conn->cc_sctp_para.pairs > 1) { 791 #ifdef DISABLE_SCTP 95 if ((conn->cc_proto == IPPROTO_SCTP) && (conn->cc_sctp_para.pairs > 0)) { 96 #ifndef DISABLE_SCTP 97 TODO("Initialize the SCTP TLS wrapper"); 98 TODO("Set the lowat, push and pull functions"); 99 #else /* DISABLE_SCTP */ 792 100 ASSERT(0); 793 CHECK_FCT( ENOTSUP );794 #else /* DISABLE_SCTP */795 /* Initialize the wrapper, start the demux thread */796 CHECK_FCT( fd_sctps_init(conn) );797 101 #endif /* DISABLE_SCTP */ 798 } else {799 /* Set the socket info in the session */800 gnutls_transport_set_ptr (conn->cc_tls_para.session, (gnutls_transport_ptr_t) conn->cc_socket);801 102 } 802 103 803 104 /* Handshake master session */ 804 105 { … … 807 108 { 808 109 if (TRACE_BOOL(INFO)) { 809 fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));110 fd_log_debug("TLS Handshake failed on socket %d : %s\n", conn->cc_socket, gnutls_strerror(ret)); 810 111 } 811 112 return EINVAL; 812 113 } ); 813 114 814 115 /* Now verify the remote credentials are valid -- only simple test here */ 815 116 CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (conn->cc_tls_para.session, &ret), return EINVAL ); 816 117 if (ret) { 817 118 if (TRACE_BOOL(INFO)) { 818 fd_log_debug("TLS: Remote certificate invalid on socket %d (%s) :\n", conn->cc_socket, conn->cc_id);119 fd_log_debug("TLS: Remote certificate invalid on socket %d :\n", conn->cc_socket); 819 120 if (ret & GNUTLS_CERT_INVALID) 820 121 fd_log_debug(" - The certificate is not trusted (unknown CA?)\n"); … … 831 132 } 832 133 } 833 834 /* Multi-stream TLS: handshake other streams as well*/835 if ( conn->cc_sctp_para.pairs > 1) {134 135 /* Other sessions in case of multi-stream SCTP are resumed from the master */ 136 if ((conn->cc_proto == IPPROTO_SCTP) && (conn->cc_sctp_para.pairs > 0)) { 836 137 #ifndef DISABLE_SCTP 837 /* Resume all additional sessions from the master one. */ 838 CHECK_FCT(fd_sctps_handshake_others(conn, priority, alt_creds)); 839 840 /* Mark the connection as protected from here */ 841 conn->cc_tls = 1; 842 843 /* Start decrypting the messages from all threads and queuing them in target queue */ 844 CHECK_FCT(fd_sctps_startthreads(conn)); 138 TODO("Init and resume all additional sessions from the master one."); 845 139 #endif /* DISABLE_SCTP */ 846 } else {847 /* Mark the connection as protected from here */848 conn->cc_tls = 1;849 850 /* Start decrypting the data */851 CHECK_POSIX( pthread_create( &conn->cc_rcvthr, NULL, rcvthr_tls_single, conn ) );852 140 } 853 141 854 142 return 0; 855 143 } 856 857 /* Retrieve TLS credentials of the remote peer, after handshake */858 int fd_cnx_getcred(struct cnxctx * conn, const gnutls_datum_t **cert_list, unsigned int *cert_list_size)859 {860 TRACE_ENTRY("%p %p %p", conn, cert_list, cert_list_size);861 CHECK_PARAMS( conn && (conn->cc_tls) && cert_list && cert_list_size );862 863 /* This function only works for X.509 certificates. */864 CHECK_PARAMS( gnutls_certificate_type_get (conn->cc_tls_para.session) == GNUTLS_CRT_X509 );865 866 *cert_list = gnutls_certificate_get_peers (conn->cc_tls_para.session, cert_list_size);867 if (*cert_list == NULL) {868 TRACE_DEBUG(INFO, "No certificate was provided by remote peer / an error occurred.");869 return EINVAL;870 }871 872 TRACE_DEBUG( FULL, "Remote peer provided %d certificates.\n", *cert_list_size);873 874 return 0;875 }876 877 /* Receive next message. if timeout is not NULL, wait only until timeout. This function only pulls from a queue, mgr thread is filling that queue aynchrounously. */878 int fd_cnx_receive(struct cnxctx * conn, struct timespec * timeout, unsigned char **buf, size_t * len)879 {880 int ev;881 size_t ev_sz;882 void * ev_data;883 884 TRACE_ENTRY("%p %p %p %p", conn, timeout, buf, len);885 CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len);886 CHECK_PARAMS(conn->cc_rcvthr != (pthread_t)NULL);887 CHECK_PARAMS(conn->cc_alt == NULL);888 889 /* Now, pull the first event */890 get_next:891 if (timeout) {892 CHECK_FCT( fd_event_timedget(conn->cc_incoming, timeout, FDEVP_PSM_TIMEOUT, &ev, &ev_sz, &ev_data) );893 } else {894 CHECK_FCT( fd_event_get(conn->cc_incoming, &ev, &ev_sz, &ev_data) );895 }896 897 switch (ev) {898 case FDEVP_CNX_MSG_RECV:899 /* We got one */900 *len = ev_sz;901 *buf = ev_data;902 return 0;903 904 case FDEVP_PSM_TIMEOUT:905 TRACE_DEBUG(FULL, "Timeout event received");906 return ETIMEDOUT;907 908 case FDEVP_CNX_EP_CHANGE:909 /* We ignore this event */910 goto get_next;911 912 case FDEVP_CNX_ERROR:913 TRACE_DEBUG(FULL, "Received ERROR event on the connection");914 return ENOTCONN;915 }916 917 TRACE_DEBUG(INFO, "Received unexpected event %d (%s)", ev, fd_pev_str(ev));918 return EINVAL;919 }920 921 /* Set an alternate FIFO list to send FDEVP_CNX_* events to */922 int fd_cnx_recv_setaltfifo(struct cnxctx * conn, struct fifo * alt_fifo)923 {924 TRACE_ENTRY( "%p %p", conn, alt_fifo );925 CHECK_PARAMS( conn && alt_fifo && conn->cc_incoming );926 927 /* The magic function does it all */928 CHECK_FCT( fd_fifo_move( &conn->cc_incoming, alt_fifo, &conn->cc_alt ) );929 930 return 0;931 }932 933 /* Wrapper around gnutls_record_recv to handle some error codes */934 static ssize_t fd_tls_send_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz)935 {936 ssize_t ret;937 again:938 CHECK_GNUTLS_DO( ret = gnutls_record_send(session, data, sz),939 {940 switch (ret) {941 case GNUTLS_E_REHANDSHAKE:942 CHECK_GNUTLS_DO( ret = gnutls_handshake(session),943 {944 if (TRACE_BOOL(INFO)) {945 fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));946 }947 goto end;948 } );949 950 case GNUTLS_E_AGAIN:951 case GNUTLS_E_INTERRUPTED:952 goto again;953 954 default:955 TRACE_DEBUG(INFO, "This TLS error is not handled, assume unrecoverable error");956 }957 } );958 end:959 return ret;960 }961 962 963 964 /* Send function when no multi-stream is involved, or sending on stream #0 (send() always use stream 0)*/965 static int send_simple(struct cnxctx * conn, unsigned char * buf, size_t len)966 {967 ssize_t ret;968 size_t sent = 0;969 TRACE_ENTRY("%p %p %zd", conn, buf, len);970 do {971 if (conn->cc_tls) {972 CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_tls_para.session, buf + sent, len - sent), return ENOTCONN );973 } else {974 CHECK_SYS( ret = send(conn->cc_socket, buf + sent, len - sent, 0) ); /* better to replace with sendmsg for atomic sending? */975 }976 sent += ret;977 } while ( sent < len );978 return 0;979 }980 981 /* Send a message -- this is synchronous -- and we assume it's never called by several threads at the same time, so we don't protect. */982 int fd_cnx_send(struct cnxctx * conn, unsigned char * buf, size_t len)983 {984 TRACE_ENTRY("%p %p %zd", conn, buf, len);985 986 CHECK_PARAMS(conn && (conn->cc_socket > 0) && buf && len);987 988 TRACE_DEBUG(FULL, "Sending %zdb %sdata on connection %s", len, conn->cc_tls ? "TLS-protected ":"", conn->cc_id);989 990 switch (conn->cc_proto) {991 case IPPROTO_TCP:992 CHECK_FCT( send_simple(conn, buf, len) );993 break;994 995 #ifndef DISABLE_SCTP996 case IPPROTO_SCTP: {997 int multistr = 0;998 999 if ((conn->cc_sctp_para.str_out > 1) && ((! conn->cc_tls) || (conn->cc_sctp_para.pairs > 1))) {1000 /* Update the id of the stream we will send this message on */1001 conn->cc_sctp_para.next += 1;1002 conn->cc_sctp_para.next %= (conn->cc_tls ? conn->cc_sctp_para.pairs : conn->cc_sctp_para.str_out);1003 multistr = 1;1004 }1005 1006 if ((!multistr) || (conn->cc_sctp_para.next == 0)) {1007 CHECK_FCT( send_simple(conn, buf, len) );1008 } else {1009 if (!conn->cc_tls) {1010 CHECK_FCT( fd_sctp_sendstr(conn->cc_socket, conn->cc_sctp_para.next, buf, len) );1011 } else {1012 /* push the record to the appropriate session */1013 ssize_t ret;1014 size_t sent = 0;1015 ASSERT(conn->cc_sctps_data.array != NULL);1016 do {1017 CHECK_GNUTLS_DO( ret = fd_tls_send_handle_error(conn, conn->cc_sctps_data.array[conn->cc_sctp_para.next].session, buf + sent, len - sent), return ENOTCONN );1018 sent += ret;1019 } while ( sent < len );1020 }1021 }1022 }1023 break;1024 #endif /* DISABLE_SCTP */1025 1026 default:1027 TRACE_DEBUG(INFO, "Unknwon protocol: %d", conn->cc_proto);1028 return ENOTSUP; /* or EINVAL... */1029 }1030 1031 return 0;1032 }1033 1034 1035 /**************************************/1036 /* Destruction of connection */1037 /**************************************/1038 1039 /* Destroy a conn structure, and shutdown the socket */1040 void fd_cnx_destroy(struct cnxctx * conn)1041 {1042 TRACE_ENTRY("%p", conn);1043 1044 CHECK_PARAMS_DO(conn, return);1045 1046 /* In case of TLS, stop receiver thread, then close properly the gnutls session */1047 if ((conn->cc_tls) && (conn->cc_sctp_para.pairs > 1)) {1048 #ifndef DISABLE_SCTP1049 /* Multi-stream TLS: Stop all decipher threads, but not the demux thread */1050 fd_sctps_stopthreads(conn);1051 #endif /* DISABLE_SCTP */1052 } else {1053 /* Stop the decoding thread */1054 CHECK_FCT_DO( fd_thr_term(&conn->cc_rcvthr), /* continue */ );1055 }1056 1057 /* Terminate properly the TLS session(s) */1058 if (conn->cc_tls) {1059 /* Master session */1060 CHECK_GNUTLS_DO( gnutls_bye(conn->cc_tls_para.session, GNUTLS_SHUT_RDWR), /* Continue */ );1061 gnutls_deinit(conn->cc_tls_para.session);1062 1063 #ifndef DISABLE_SCTP1064 if (conn->cc_sctp_para.pairs > 1) {1065 /* Multi-stream TLS: destroy the wrapper and stop the demux thread */1066 fd_sctps_destroy(conn);1067 }1068 #endif /* DISABLE_SCTP */1069 1070 }1071 1072 /* Shut the connection down */1073 if (conn->cc_socket > 0) {1074 shutdown(conn->cc_socket, SHUT_RDWR);1075 }1076 1077 /* Empty and destroy FIFO list */1078 if (conn->cc_incoming) {1079 fd_event_destroy( &conn->cc_incoming, free );1080 }1081 1082 /* Free the object */1083 free(conn);1084 1085 /* Done! */1086 return;1087 }
Note: See TracChangeset
for help on using the changeset viewer.