Changes in freeDiameter/cnxctx.c [20:277ec00d793e:30:bca243c65b56] in freeDiameter
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
freeDiameter/cnxctx.c
r20 r30 35 35 36 36 #include "fD.h" 37 38 /* Initialize a connection context */ 39 struct cnxctx * fd_cnx_init(int sock, int proto) 37 #include "cnxctx.h" 38 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) 40 83 { 41 84 struct cnxctx * conn = NULL; 42 43 TRACE_ENTRY("%d %d", sock, proto); 44 CHECK_PARAMS_DO( (proto == IPPROTO_TCP) || (proto == IPPROTO_SCTP), return NULL); 45 85 86 TRACE_ENTRY("%d", full); 87 46 88 CHECK_MALLOC_DO( conn = malloc(sizeof(struct cnxctx)), return NULL ); 47 89 memset(conn, 0, sizeof(struct cnxctx)); 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) { 90 91 if (full) { 92 CHECK_FCT_DO( fd_fifo_new ( &conn->cc_incoming ), return NULL ); 93 } 94 95 return conn; 96 } 97 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 56 195 #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; 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); 60 334 #else /* DISABLE_SCTP */ 61 ASSERT(0); 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; 62 389 #endif /* DISABLE_SCTP */ 63 } 64 65 return conn; 66 } 67 68 /* TLS handshake the connection */ 69 int fd_cnx_handshake(struct cnxctx * conn, int mode) 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) 70 773 { 71 774 TRACE_ENTRY( "%p %d", conn, mode); 72 CHECK_PARAMS( conn && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER)) );73 775 CHECK_PARAMS( conn && (!conn->cc_tls) && ( (mode == GNUTLS_CLIENT) || (mode == GNUTLS_SERVER) ) && (!conn->cc_loop) ); 776 74 777 /* Save the mode */ 75 778 conn->cc_tls_para.mode = mode; 76 779 77 /* Create the master session context */ 78 CHECK_GNUTLS_DO( gnutls_init (&conn->cc_tls_para.session, mode), return ENOMEM ); 79 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 ); 82 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); 780 /* Cancel 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 */); 782 783 /* Once TLS handshake is done, we don't stop after the first message */ 784 conn->cc_loop = 1; 785 786 /* Prepare the master session credentials and priority */ 787 CHECK_FCT( fd_tls_prepare(&conn->cc_tls_para.session, mode, priority, alt_creds) ); 93 788 94 789 /* Special case: multi-stream TLS is not natively managed in GNU TLS, we use a wrapper library */ 95 if ( (conn->cc_proto == IPPROTO_SCTP) && (conn->cc_sctp_para.pairs > 0)) {96 #if ndef DISABLE_SCTP97 TODO("Initialize the SCTP TLS wrapper");98 TODO("Set the lowat, push and pull functions");790 if (conn->cc_sctp_para.pairs > 1) { 791 #ifdef DISABLE_SCTP 792 ASSERT(0); 793 CHECK_FCT( ENOTSUP ); 99 794 #else /* DISABLE_SCTP */ 100 ASSERT(0); 795 /* Initialize the wrapper, start the demux thread */ 796 CHECK_FCT( fd_sctps_init(conn) ); 101 797 #endif /* DISABLE_SCTP */ 102 } 103 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 } 802 104 803 /* Handshake master session */ 105 804 { … … 108 807 { 109 808 if (TRACE_BOOL(INFO)) { 110 fd_log_debug("TLS Handshake failed on socket %d : %s\n", conn->cc_socket, gnutls_strerror(ret));809 fd_log_debug("TLS Handshake failed on socket %d (%s) : %s\n", conn->cc_socket, conn->cc_id, gnutls_strerror(ret)); 111 810 } 112 811 return EINVAL; 113 812 } ); 114 813 115 814 /* Now verify the remote credentials are valid -- only simple test here */ 116 815 CHECK_GNUTLS_DO( gnutls_certificate_verify_peers2 (conn->cc_tls_para.session, &ret), return EINVAL ); 117 816 if (ret) { 118 817 if (TRACE_BOOL(INFO)) { 119 fd_log_debug("TLS: Remote certificate invalid on socket %d :\n", conn->cc_socket);818 fd_log_debug("TLS: Remote certificate invalid on socket %d (%s) :\n", conn->cc_socket, conn->cc_id); 120 819 if (ret & GNUTLS_CERT_INVALID) 121 820 fd_log_debug(" - The certificate is not trusted (unknown CA?)\n"); … … 132 831 } 133 832 } 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)) {833 834 /* Multi-stream TLS: handshake other streams as well */ 835 if (conn->cc_sctp_para.pairs > 1) { 137 836 #ifndef DISABLE_SCTP 138 TODO("Init and resume all additional sessions from the master one."); 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)); 139 845 #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 ) ); 140 852 } 141 853 142 854 return 0; 143 855 } 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_SCTP 996 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_SCTP 1049 /* 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_SCTP 1064 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.