/* $Id$ */ /* * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Only build when PJSIP_HAS_TLS_TRANSPORT is enabled */ #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0 /* * Include OpenSSL headers */ #include #include #include /* * With VisualC++, it's not possible to dynamically link against some * libraries when some macros are defined (unlike "make" based build * system where this can be easily manipulated). * * So for VisualC++ IDE, include OpenSSL libraries in the linking by * using the #pragma lib construct below. */ #ifdef _MSC_VER # ifdef _DEBUG # pragma comment( lib, "libeay32MTd") # pragma comment( lib, "ssleay32MTd") #else # pragma comment( lib, "libeay32MT") # pragma comment( lib, "ssleay32MT") # endif #endif #define THIS_FILE "transport_tls_ossl.c" #define MAX_ASYNC_CNT 16 #define POOL_LIS_INIT 512 #define POOL_LIS_INC 512 #define POOL_TP_INIT 512 #define POOL_TP_INC 512 /** * Get the number of descriptors in the set. This is defined in sock_select.c * This function will only return the number of sockets set from PJ_FD_SET * operation. When the set is modified by other means (such as by select()), * the count will not be reflected here. * * That's why don't export this function in the header file, to avoid * misunderstanding. * * @param fdsetp The descriptor set. * * @return Number of descriptors in the set. */ PJ_DECL(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp); struct tls_listener; struct tls_transport; /* * This structure is "descendant" of pj_ioqueue_op_key_t, and it is used to * track pending/asynchronous accept() operation. TLS transport may have * more than one pending accept() operations, depending on the value of * async_cnt. */ struct pending_accept { pj_ioqueue_op_key_t op_key; struct tls_listener *listener; unsigned index; pj_pool_t *pool; pj_sock_t new_sock; int addr_len; pj_sockaddr_in local_addr; pj_sockaddr_in remote_addr; }; /* * This is the TLS listener, which is a "descendant" of pjsip_tpfactory (the * SIP transport factory). */ struct tls_listener { pjsip_tpfactory factory; pj_bool_t is_registered; pjsip_tls_setting setting; pjsip_endpoint *endpt; pjsip_tpmgr *tpmgr; pj_sock_t sock; pj_ioqueue_key_t *key; unsigned async_cnt; struct pending_accept *accept_op[MAX_ASYNC_CNT]; SSL_CTX *ctx; }; /* * This structure is used to keep delayed transmit operation in a list. * A delayed transmission occurs when application sends tx_data when * the TLS connect/establishment is still in progress. These delayed * transmission will be "flushed" once the socket is connected (either * successfully or with errors). */ struct delayed_tdata { PJ_DECL_LIST_MEMBER(struct delayed_tdata); pjsip_tx_data_op_key *tdata_op_key; }; /* * This structure describes the TLS transport, and it's descendant of * pjsip_transport. */ struct tls_transport { pjsip_transport base; pj_bool_t is_server; /* Do not save listener instance in the transport, because * listener might be destroyed during transport's lifetime. * See http://trac.pjsip.org/repos/ticket/491 struct tls_listener *listener; */ /* TLS settings, copied from listener */ struct { pj_str_t server_name; pj_time_val timeout; } setting; pj_bool_t is_registered; pj_bool_t is_closing; pj_status_t close_reason; pj_sock_t sock; pj_ioqueue_key_t *key; pj_bool_t has_pending_connect; /* SSL connection */ SSL *ssl; pj_bool_t ssl_shutdown_called; /* Keep alive */ pj_timer_entry ka_timer; pj_time_val last_activity; /* TLS transport can only have one rdata! * Otherwise chunks of incoming PDU may be received on different * buffer. */ pjsip_rx_data rdata; /* Pending transmission list. */ struct delayed_tdata delayed_list; }; /**************************************************************************** * PROTOTYPES */ /* This callback is called when pending accept() operation completes. */ static void on_accept_complete( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t sock, pj_status_t status); /* This callback is called by transport manager to destroy listener */ static pj_status_t lis_destroy(pjsip_tpfactory *factory); /* This callback is called by transport manager to create transport */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_transport **transport); /* Common function to create and initialize transport */ static pj_status_t tls_create(struct tls_listener *listener, pj_pool_t *pool, pj_sock_t sock, pj_bool_t is_server, const pj_sockaddr_in *local, const pj_sockaddr_in *remote, struct tls_transport **p_tls); /**************************************************************************** * SSL FUNCTIONS */ /* ssl_report_error() */ static void ssl_report_error(const char *sender, int level, pj_status_t status, const char *format, ...) { va_list marker; va_start(marker, format); #if PJ_LOG_MAX_LEVEL > 0 if (status != PJ_SUCCESS) { char err_format[PJ_ERR_MSG_SIZE + 512]; int len; len = pj_ansi_snprintf(err_format, sizeof(err_format), "%s: ", format); pj_strerror(status, err_format+len, sizeof(err_format)-len); pj_log(sender, level, err_format, marker); } else { unsigned long ssl_err; ssl_err = ERR_get_error(); if (ssl_err == 0) { pj_log(sender, level, format, marker); } else { char err_format[512]; int len; len = pj_ansi_snprintf(err_format, sizeof(err_format), "%s: ", format); ERR_error_string(ssl_err, err_format+len); pj_log(sender, level, err_format, marker); } } #endif va_end(marker); } static void sockaddr_to_host_port( pj_pool_t *pool, pjsip_host_port *host_port, const pj_sockaddr_in *addr ) { enum { M = 48 }; host_port->host.ptr = (char*)pj_pool_alloc(pool, M); host_port->host.slen = pj_ansi_snprintf( host_port->host.ptr, M, "%s", pj_inet_ntoa(addr->sin_addr)); host_port->port = pj_ntohs(addr->sin_port); } /* SSL password callback. */ static int password_cb(char *buf, int num, int rwflag, void *user_data) { struct tls_listener *lis = (struct tls_listener*) user_data; PJ_UNUSED_ARG(rwflag); if(num < lis->setting.password.slen+1) return 0; pj_memcpy(buf, lis->setting.password.ptr, lis->setting.password.slen); return lis->setting.password.slen; } /* OpenSSL library initialization counter */ static int openssl_init_count; /* Initialize OpenSSL */ static pj_status_t init_openssl(void) { if (++openssl_init_count != 1) return PJ_SUCCESS; SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); return PJ_SUCCESS; } /* Shutdown OpenSSL */ static void shutdown_openssl(void) { if (--openssl_init_count != 0) return; } /* Create and initialize new SSL context */ static pj_status_t create_ctx( struct tls_listener *lis, SSL_CTX **p_ctx) { struct pjsip_tls_setting *opt = &lis->setting; int method; char *lis_name = lis->factory.obj_name; SSL_METHOD *ssl_method; SSL_CTX *ctx; int mode, rc; *p_ctx = NULL; /* Make sure OpenSSL library has been initialized */ init_openssl(); /* Determine SSL method to use */ method = opt->method; if (method == PJSIP_SSL_UNSPECIFIED_METHOD) method = PJSIP_SSL_DEFAULT_METHOD; switch (method) { case PJSIP_SSLV23_METHOD: ssl_method = SSLv23_method(); break; case PJSIP_TLSV1_METHOD: ssl_method = TLSv1_method(); break; case PJSIP_SSLV2_METHOD: ssl_method = SSLv2_method(); break; case PJSIP_SSLV3_METHOD: ssl_method = SSLv3_method(); break; default: ssl_report_error(lis_name, 4, PJSIP_TLS_EINVMETHOD, "Error creating SSL context"); return PJSIP_TLS_EINVMETHOD; } /* Create SSL context for the listener */ ctx = SSL_CTX_new(ssl_method); if (ctx == NULL) { ssl_report_error(lis_name, 4, PJ_SUCCESS, "Error creating SSL context"); return PJSIP_TLS_ECTX; } /* Load CA list if one is specified. */ if (opt->ca_list_file.slen) { /* Can only take a NULL terminated filename in the setting */ pj_assert(opt->ca_list_file.ptr[opt->ca_list_file.slen] == '\0'); rc = SSL_CTX_load_verify_locations(ctx, opt->ca_list_file.ptr, NULL); if (rc != 1) { ssl_report_error(lis_name, 4, PJ_SUCCESS, "Error loading/verifying CA list file '%.*s'", (int)opt->ca_list_file.slen, opt->ca_list_file.ptr); SSL_CTX_free(ctx); return PJSIP_TLS_ECACERT; } PJ_LOG(5,(lis_name, "TLS CA file successfully loaded from '%.*s'", (int)opt->ca_list_file.slen, opt->ca_list_file.ptr)); } /* Set password callback */ SSL_CTX_set_default_passwd_cb(ctx, password_cb); SSL_CTX_set_default_passwd_cb_userdata(ctx, lis); /* Load certificate if one is specified */ if (opt->cert_file.slen) { /* Can only take a NULL terminated filename in the setting */ pj_assert(opt->cert_file.ptr[opt->cert_file.slen] == '\0'); /* Load certificate chain from file into ctx */ rc = SSL_CTX_use_certificate_chain_file(ctx, opt->cert_file.ptr); if(rc != 1) { ssl_report_error(lis_name, 4, PJ_SUCCESS, "Error loading certificate chain file '%.*s'", (int)opt->cert_file.slen, opt->cert_file.ptr); SSL_CTX_free(ctx); return PJSIP_TLS_ECERTFILE; } PJ_LOG(5,(lis_name, "TLS certificate successfully loaded from '%.*s'", (int)opt->cert_file.slen, opt->cert_file.ptr)); } /* Load private key if one is specified */ if (opt->privkey_file.slen) { /* Can only take a NULL terminated filename in the setting */ pj_assert(opt->privkey_file.ptr[opt->privkey_file.slen] == '\0'); /* Adds the first private key found in file to ctx */ rc = SSL_CTX_use_PrivateKey_file(ctx, opt->privkey_file.ptr, SSL_FILETYPE_PEM); if(rc != 1) { ssl_report_error(lis_name, 4, PJ_SUCCESS, "Error adding private key from '%.*s'", (int)opt->privkey_file.slen, opt->privkey_file.ptr); SSL_CTX_free(ctx); return PJSIP_TLS_EKEYFILE; } PJ_LOG(5,(lis_name, "TLS private key successfully loaded from '%.*s'", (int)opt->privkey_file.slen, opt->privkey_file.ptr)); } /* SSL verification options */ if (lis->setting.verify_client || lis->setting.verify_server) { mode = SSL_VERIFY_PEER; if (lis->setting.require_client_cert) mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; } else { mode = SSL_VERIFY_NONE; } SSL_CTX_set_verify(ctx, mode, NULL); PJ_LOG(5,(lis_name, "TLS verification mode set to %d", mode)); /* Optionally set cipher list if one is specified */ if (opt->ciphers.slen) { /* Can only take a NULL terminated cipher list in the setting */ pj_assert(opt->cert_file.ptr[opt->cert_file.slen] == '\0'); rc = SSL_CTX_set_cipher_list(ctx, opt->ciphers.ptr); if (rc != 1) { ssl_report_error(lis_name, 4, PJ_SUCCESS, "Error setting cipher list '%.*s'", (int)opt->ciphers.slen, opt->ciphers.ptr); SSL_CTX_free(ctx); return PJSIP_TLS_ECIPHER; } PJ_LOG(5,(lis_name, "TLS ciphers set to '%.*s'", (int)opt->ciphers.slen, opt->ciphers.ptr)); } /* Done! */ *p_ctx = ctx; return PJ_SUCCESS; } /* Destroy SSL context */ static void destroy_ctx(SSL_CTX *ctx) { SSL_CTX_free(ctx); /* Potentially shutdown OpenSSL library if this is the last * context exists. */ shutdown_openssl(); } /* * Perform SSL_connect upon completion of socket connect() */ static pj_status_t ssl_connect(struct tls_transport *tls) { SSL *ssl = tls->ssl; int status; if (SSL_is_init_finished (ssl)) return PJ_SUCCESS; if (SSL_get_fd(ssl) < 0) SSL_set_fd(ssl, (int)tls->sock); if (!SSL_in_connect_init(ssl)) SSL_set_connect_state(ssl); #ifdef SSL_set_tlsext_host_name if (tls->setting.server_name.slen) { char server_name[PJ_MAX_HOSTNAME]; if (tls->setting.server_name.slen >= PJ_MAX_HOSTNAME) return PJ_ENAMETOOLONG; pj_memcpy(server_name, tls->setting.server_name.ptr, tls->setting.server_name.slen); server_name[tls->setting.server_name.slen] = '\0'; if (!SSL_set_tlsext_host_name(ssl, server_name)) { PJ_LOG(4,(tls->base.obj_name, "SSL_set_tlsext_host_name() failed")); } } #endif PJ_LOG(5,(tls->base.obj_name, "Starting SSL_connect() negotiation")); do { /* These handle sets are used to set up for whatever SSL_connect * says it wants next. They're reset on each pass around the loop. */ pj_fd_set_t rd_set; pj_fd_set_t wr_set; PJ_FD_ZERO(&rd_set); PJ_FD_ZERO(&wr_set); status = SSL_connect (ssl); switch (SSL_get_error (ssl, status)) { case SSL_ERROR_NONE: /* Success */ status = 0; PJ_LOG(5,(tls->base.obj_name, "SSL_connect() negotiation completes successfully")); break; case SSL_ERROR_WANT_WRITE: /* Wait for more activity */ PJ_FD_SET(tls->sock, &wr_set); status = 1; break; case SSL_ERROR_WANT_READ: /* Wait for more activity */ PJ_FD_SET(tls->sock, &rd_set); status = 1; break; case SSL_ERROR_ZERO_RETURN: /* The peer has notified us that it is shutting down via * the SSL "close_notify" message so we need to * shutdown, too. */ PJ_LOG(4,(THIS_FILE, "SSL connect() failed, remote has" "shutdown connection.")); return PJ_STATUS_FROM_OS(OSERR_ENOTCONN); case SSL_ERROR_SYSCALL: /* On some platforms (e.g. MS Windows) OpenSSL does not * store the last error in errno so explicitly do so. * * Explicitly check for EWOULDBLOCK since it doesn't get * converted to an SSL_ERROR_WANT_{READ,WRITE} on some * platforms. If SSL_connect failed outright, though, don't * bother checking more. This can happen if the socket gets * closed during the handshake. */ if (pj_get_netos_error() == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && status == -1) { /* Although the SSL_ERROR_WANT_READ/WRITE isn't getting * set correctly, the read/write state should be valid. * Use that to decide what to do. */ status = 1; /* Wait for more activity */ if (SSL_want_write (ssl)) PJ_FD_SET(tls->sock, &wr_set); else if (SSL_want_read (ssl)) PJ_FD_SET(tls->sock, &rd_set); else status = -1; /* Doesn't want anything - bail out */ } else { status = -1; } break; default: ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS, "SSL_connect() error"); status = -1; break; } if (status == 1) { pj_time_val timeout, *p_timeout; /* Must have at least one handle to wait for at this point. */ pj_assert(PJ_FD_COUNT(&rd_set) == 1 || PJ_FD_COUNT(&wr_set) == 1); /* This will block the whole stack!!! */ PJ_TODO(SUPPORT_SSL_ASYNCHRONOUS_CONNECT); if (tls->setting.timeout.sec == 0 && tls->setting.timeout.msec == 0) { p_timeout = NULL; } else { timeout = tls->setting.timeout; p_timeout = &timeout; } /* Block indefinitely if timeout pointer is zero. */ status = pj_sock_select(tls->sock+1, &rd_set, &wr_set, NULL, p_timeout); /* 0 is timeout, so we're done. * -1 is error, so we're done. * Could be both handles set (same handle in both masks) so set to 1. */ if (status >= 1) status = 1; else if (status == 0) return PJSIP_TLS_ETIMEDOUT; else status = -1; } } while (status == 1 && !SSL_is_init_finished (ssl)); return (status == -1 ? PJSIP_TLS_ECONNECT : PJ_SUCCESS); } /* * Perform SSL_accept() on the newly established incoming TLS connection. */ static pj_status_t ssl_accept(struct tls_transport *tls) { SSL *ssl = tls->ssl; int rc; if (SSL_is_init_finished (ssl)) return PJ_SUCCESS; if (!SSL_in_accept_init(ssl)) SSL_set_accept_state(ssl); PJ_LOG(5,(tls->base.obj_name, "Starting SSL_accept() negotiation")); /* Repeat retrying SSL_accept() procedure until it completes either * successfully or with failure. */ do { /* These handle sets are used to set up for whatever SSL_accept says * it wants next. They're reset on each pass around the loop. */ pj_fd_set_t rd_set; pj_fd_set_t wr_set; PJ_FD_ZERO(&rd_set); PJ_FD_ZERO(&wr_set); rc = SSL_accept (ssl); switch (SSL_get_error (ssl, rc)) { case SSL_ERROR_NONE: /* Success! */ rc = 0; PJ_LOG(5,(tls->base.obj_name, "SSL_accept() negotiation completes successfully")); break; case SSL_ERROR_WANT_WRITE: PJ_FD_SET(tls->sock, &wr_set); rc = 1; /* Wait for more activity */ break; case SSL_ERROR_WANT_READ: PJ_FD_SET(tls->sock, &rd_set); rc = 1; /* Need to read more data */ break; case SSL_ERROR_ZERO_RETURN: /* The peer has notified us that it is shutting down via the SSL * "close_notify" message so we need to shutdown, too. */ PJ_LOG(4,(tls->base.obj_name, "Incoming SSL connection closed prematurely by client")); return PJ_STATUS_FROM_OS(OSERR_ENOTCONN); case SSL_ERROR_SYSCALL: /* Explicitly check for EWOULDBLOCK since it doesn't get converted * to an SSL_ERROR_WANT_{READ,WRITE} on some platforms. * If SSL_accept failed outright, though, don't bother checking * more. This can happen if the socket gets closed during the * handshake. */ if (pj_get_netos_error()==PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) && rc==-1) { /* Although the SSL_ERROR_WANT_READ/WRITE isn't getting set * correctly, the read/write state should be valid. Use that * to decide what to do. */ rc = 1; /* Wait for more activity */ if (SSL_want_write(ssl)) PJ_FD_SET(tls->sock, &wr_set); else if (SSL_want_read(ssl)) PJ_FD_SET(tls->sock, &rd_set); else { /* Doesn't want anything - bail out */ return PJ_STATUS_FROM_OS(OSERR_ENOTCONN); } } else { return PJSIP_TLS_EUNKNOWN; } break; default: ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS, "Error calling SSL_accept()"); return pj_get_netos_error() ? pj_get_netos_error() : PJSIP_TLS_EUNKNOWN; } if (rc == 1) { pj_time_val timeout, *p_timeout; /* Must have at least one handle to wait for at this point. */ pj_assert(PJ_FD_COUNT(&rd_set) == 1 || PJ_FD_COUNT(&wr_set) == 1); if (tls->setting.timeout.sec == 0 && tls->setting.timeout.msec == 0) { p_timeout = NULL; } else { timeout = tls->setting.timeout; p_timeout = &timeout; } rc = pj_sock_select(tls->sock+1, &rd_set, &wr_set, NULL, p_timeout); if (rc >= 1) rc = 1; else if (rc == 0) return PJSIP_TLS_ETIMEDOUT; else return pj_get_netos_error(); } } while (rc == 1 && !SSL_is_init_finished(ssl)); return (rc == -1 ? PJSIP_TLS_EUNKNOWN : PJ_SUCCESS); } /* Send outgoing data with SSL connection */ static pj_status_t ssl_write_bytes(struct tls_transport *tls, const void *data, int size, const char *data_name) { int sent = 0; do { const int fragment_sent = SSL_write(tls->ssl, ((pj_uint8_t*)data) + sent, size - sent); switch( SSL_get_error(tls->ssl, fragment_sent)) { case SSL_ERROR_NONE: sent += fragment_sent; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: /* For now, we can't handle situation where WANT_READ/WANT_WRITE * is raised after some data has been sent, since we don't have * mechanism to keep track of how many bytes have been sent * inside the individual tdata. */ pj_assert(sent == 0); PJ_TODO(PARTIAL_SSL_SENT); return PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK); case SSL_ERROR_ZERO_RETURN: /* The peer has notified us that it is shutting down via the SSL * "close_notify" message. Tell the transport manager that it * shouldn't use this transport any more and return ENOTCONN * to caller. */ /* It is safe to call this multiple times. */ pjsip_transport_shutdown(&tls->base); return PJ_STATUS_FROM_OS(OSERR_ENOTCONN); case SSL_ERROR_SYSCALL: if (fragment_sent == 0) { /* An EOF occured but the SSL "close_notify" message was not * sent. Shutdown the transport and return ENOTCONN. */ /* It is safe to call this multiple times. */ pjsip_transport_shutdown(&tls->base); return PJ_STATUS_FROM_OS(OSERR_ENOTCONN); } /* Other error */ return pj_get_netos_error(); default: ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS, "Error sending %s with SSL_write()", data_name); return pj_get_netos_error() ? pj_get_netos_error() : PJSIP_TLS_ESEND; } } while (sent < size); return PJ_SUCCESS; } /* Send outgoing tdata with SSL connection */ static pj_status_t ssl_write(struct tls_transport *tls, pjsip_tx_data *tdata) { return ssl_write_bytes(tls, tdata->buf.start, tdata->buf.cur - tdata->buf.start, pjsip_tx_data_get_info(tdata)); } /* Read data from SSL connection */ static pj_status_t ssl_read(struct tls_transport *tls) { pjsip_rx_data *rdata = &tls->rdata; int bytes_read, max_size; max_size = sizeof(rdata->pkt_info.packet) - rdata->pkt_info.len; bytes_read = SSL_read(tls->ssl, rdata->pkt_info.packet+rdata->pkt_info.len, max_size); switch (SSL_get_error(tls->ssl, bytes_read)) { case SSL_ERROR_NONE: /* Data successfully read */ rdata->pkt_info.len += bytes_read; return PJ_SUCCESS; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: return PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK); case SSL_ERROR_ZERO_RETURN: /* The peer has notified us that it is shutting down via the SSL * "close_notify" message. */ pjsip_transport_shutdown(&tls->base); return PJ_STATUS_FROM_OS(OSERR_ENOTCONN); case SSL_ERROR_SYSCALL: if (bytes_read == 0) { /* An EOF occured but the SSL "close_notify" message was not * sent. */ pjsip_transport_shutdown(&tls->base); return PJ_STATUS_FROM_OS(OSERR_ENOTCONN); } /* Other error */ return pj_get_netos_error(); default: ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS, "Error reading data with SSL_read()"); return pj_get_netos_error() ? pj_get_netos_error() : PJSIP_TLS_EREAD; } /* Should not reach here */ } /**************************************************************************** * The TLS listener/transport factory. */ /* * This is the public API to create, initialize, register, and start the * TLS listener. */ PJ_DEF(pj_status_t) pjsip_tls_transport_start( pjsip_endpoint *endpt, const pjsip_tls_setting *opt, const pj_sockaddr_in *local, const pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) { pj_pool_t *pool; struct tls_listener *listener; pj_ioqueue_callback listener_cb; pj_sockaddr_in *listener_addr; int addr_len; unsigned i; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); /* Verify that address given in a_name (if any) is valid */ if (a_name && a_name->host.slen) { pj_sockaddr_in tmp; status = pj_sockaddr_in_init(&tmp, &a_name->host, (pj_uint16_t)a_name->port); if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY || tmp.sin_addr.s_addr == PJ_INADDR_NONE) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tlslis", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tls_listener); listener->factory.pool = pool; listener->factory.type = PJSIP_TRANSPORT_TLS; listener->factory.type_name = "tls"; listener->factory.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS); listener->sock = PJ_INVALID_SOCKET; /* Create object name */ pj_ansi_snprintf(listener->factory.obj_name, sizeof(listener->factory.obj_name), "tls%p", listener); /* Create duplicate of TLS settings */ if (opt) pjsip_tls_setting_copy(pool, &listener->setting, opt); else pjsip_tls_setting_default(&listener->setting); /* Initialize SSL context to be used by this listener */ status = create_ctx(listener, &listener->ctx); if (status != PJ_SUCCESS) goto on_error; status = pj_lock_create_recursive_mutex(pool, "tlslis", &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; /* Create and bind socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &listener->sock); if (status != PJ_SUCCESS) goto on_error; listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr; if (local) { pj_memcpy(listener_addr, local, sizeof(pj_sockaddr_in)); } else { pj_sockaddr_in_init(listener_addr, NULL, 0); } status = pj_sock_bind(listener->sock, listener_addr, sizeof(pj_sockaddr_in)); if (status != PJ_SUCCESS) goto on_error; /* Retrieve the bound address */ addr_len = sizeof(pj_sockaddr_in); status = pj_sock_getsockname(listener->sock, listener_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (a_name && a_name->host.slen) { /* Copy the address */ listener->factory.addr_name = *a_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &a_name->host); listener->factory.addr_name.port = a_name->port; } else { /* No published address is given, use the bound address */ /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ if (listener_addr->sin_addr.s_addr == 0) { pj_sockaddr hostip; status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) goto on_error; listener_addr->sin_addr = hostip.ipv4.sin_addr; } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, &listener->factory.addr_name, listener_addr); } /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { listener->factory.addr_name.port = pj_ntohs(listener_addr->sin_port); } /* Start listening to the address */ status = pj_sock_listen(listener->sock, PJSIP_TLS_TRANSPORT_BACKLOG); if (status != PJ_SUCCESS) goto on_error; /* Register socket to ioqeuue */ pj_bzero(&listener_cb, sizeof(listener_cb)); listener_cb.on_accept_complete = &on_accept_complete; status = pj_ioqueue_register_sock(pool, pjsip_endpt_get_ioqueue(endpt), listener->sock, listener, &listener_cb, &listener->key); if (status != PJ_SUCCESS) goto on_error; /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport = lis_create_transport; listener->factory.destroy = lis_destroy; listener->is_registered = PJ_TRUE; status = pjsip_tpmgr_register_tpfactory(listener->tpmgr, &listener->factory); if (status != PJ_SUCCESS) { listener->is_registered = PJ_FALSE; goto on_error; } /* Start pending accept() operations */ if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT; listener->async_cnt = async_cnt; for (i=0; iaccept_op[i] = PJ_POOL_ZALLOC_T(pool, struct pending_accept); pj_ioqueue_op_key_init(&listener->accept_op[i]->op_key, sizeof(listener->accept_op[i]->op_key)); listener->accept_op[i]->pool = pool; listener->accept_op[i]->listener = listener; listener->accept_op[i]->index = i; on_accept_complete(listener->key, &listener->accept_op[i]->op_key, listener->sock, PJ_EPENDING); } PJ_LOG(4,(listener->factory.obj_name, "SIP TLS listener ready for incoming connections at %.*s:%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port)); /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: lis_destroy(&listener->factory); return status; } /* This callback is called by transport manager to destroy listener */ static pj_status_t lis_destroy(pjsip_tpfactory *factory) { struct tls_listener *listener = (struct tls_listener *)factory; unsigned i; if (listener->is_registered) { pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory); listener->is_registered = PJ_FALSE; } if (listener->key) { pj_ioqueue_unregister(listener->key); listener->key = NULL; listener->sock = PJ_INVALID_SOCKET; } if (listener->sock != PJ_INVALID_SOCKET) { pj_sock_close(listener->sock); listener->sock = PJ_INVALID_SOCKET; } if (listener->factory.lock) { pj_lock_destroy(listener->factory.lock); listener->factory.lock = NULL; } for (i=0; iaccept_op); ++i) { if (listener->accept_op[i] && listener->accept_op[i]->pool) { pj_pool_t *pool = listener->accept_op[i]->pool; listener->accept_op[i]->pool = NULL; pj_pool_release(pool); } } if (listener->ctx) { destroy_ctx(listener->ctx); listener->ctx = NULL; } if (listener->factory.pool) { pj_pool_t *pool = listener->factory.pool; PJ_LOG(4,(listener->factory.obj_name, "SIP TLS listener destroyed")); listener->factory.pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } /***************************************************************************/ /* * TLS Transport */ /* * Prototypes. */ /* Called by transport manager to send message */ static pj_status_t tls_send_msg(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, void (*callback)(pjsip_transport *transport, void *token, pj_ssize_t sent_bytes)); /* Called by transport manager to shutdown */ static pj_status_t tls_shutdown(pjsip_transport *transport); /* Called by transport manager to destroy transport */ static pj_status_t tls_destroy_transport(pjsip_transport *transport); /* Utility to destroy transport */ static pj_status_t tls_destroy(pjsip_transport *transport, pj_status_t reason); /* Callback from ioqueue on incoming packet */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read); /* Callback from ioqueue when packet is sent */ static void on_write_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent); /* Callback from ioqueue when connect completes */ static void on_connect_complete(pj_ioqueue_key_t *key, pj_status_t status); /* TLS keep-alive timer callback */ static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e); /* * Common function to create TLS transport, called when pending accept() and * pending connect() complete. */ static pj_status_t tls_create( struct tls_listener *listener, pj_pool_t *pool, pj_sock_t sock, pj_bool_t is_server, const pj_sockaddr_in *local, const pj_sockaddr_in *remote, struct tls_transport **p_tls) { struct tls_transport *tls; pj_ioqueue_t *ioqueue; pj_ioqueue_callback tls_callback; int rc; pj_status_t status; PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_EINVAL); if (pool == NULL) { pool = pjsip_endpt_create_pool(listener->endpt, "tls", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); } /* * Create and initialize basic transport structure. */ tls = PJ_POOL_ZALLOC_T(pool, struct tls_transport); tls->sock = sock; tls->is_server = is_server; /*tls->listener = listener;*/ pj_list_init(&tls->delayed_list); tls->base.pool = pool; tls->setting.timeout = listener->setting.timeout; pj_strdup(pool, &tls->setting.server_name, &listener->setting.server_name); pj_ansi_snprintf(tls->base.obj_name, PJ_MAX_OBJ_NAME, (is_server ? "tlss%p" :"tlsc%p"), tls); /* Initialize transport reference counter to 1 */ status = pj_atomic_create(pool, 0, &tls->base.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, "tls", &tls->base.lock); if (status != PJ_SUCCESS) { goto on_error; } tls->base.key.type = PJSIP_TRANSPORT_TLS; pj_memcpy(&tls->base.key.rem_addr, remote, sizeof(pj_sockaddr_in)); tls->base.type_name = "tls"; tls->base.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TLS); tls->base.info = (char*) pj_pool_alloc(pool, 64); pj_ansi_snprintf(tls->base.info, 64, "TLS to %s:%d", pj_inet_ntoa(remote->sin_addr), (int)pj_ntohs(remote->sin_port)); tls->base.addr_len = sizeof(pj_sockaddr_in); pj_memcpy(&tls->base.local_addr, local, sizeof(pj_sockaddr_in)); sockaddr_to_host_port(pool, &tls->base.local_name, local); sockaddr_to_host_port(pool, &tls->base.remote_name, remote); tls->base.endpt = listener->endpt; tls->base.tpmgr = listener->tpmgr; tls->base.send_msg = &tls_send_msg; tls->base.do_shutdown = &tls_shutdown; tls->base.destroy = &tls_destroy_transport; /* Create SSL connection object */ tls->ssl = SSL_new(listener->ctx); if (tls->ssl == NULL) { ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS, "Error creating SSL connection object"); status = PJSIP_TLS_ESSLCONN; goto on_error; } /* Associate network socket with SSL connection object */ rc = SSL_set_fd(tls->ssl, (int)sock); if (rc != 1) { ssl_report_error(tls->base.obj_name, 4, PJ_SUCCESS, "Error calling SSL_set_fd"); status = PJSIP_TLS_ESSLCONN; goto on_error; } /* Register socket to ioqueue */ pj_bzero(&tls_callback, sizeof(pj_ioqueue_callback)); tls_callback.on_read_complete = &on_read_complete; tls_callback.on_write_complete = &on_write_complete; tls_callback.on_connect_complete = &on_connect_complete; ioqueue = pjsip_endpt_get_ioqueue(listener->endpt); status = pj_ioqueue_register_sock(pool, ioqueue, sock, tls, &tls_callback, &tls->key); if (status != PJ_SUCCESS) { goto on_error; } /* Register transport to transport manager */ status = pjsip_transport_register(listener->tpmgr, &tls->base); if (status != PJ_SUCCESS) { goto on_error; } tls->is_registered = PJ_TRUE; /* Initialize keep-alive timer */ tls->ka_timer.user_data = (void*) tls; tls->ka_timer.cb = &tls_keep_alive_timer; /* Done setting up basic transport. */ *p_tls = tls; PJ_LOG(4,(tls->base.obj_name, "TLS %s transport created", (tls->is_server ? "server" : "client"))); return PJ_SUCCESS; on_error: tls_destroy(&tls->base, status); return status; } /* Flush all delayed transmision once the socket is connected. * Return non-zero if pending transmission list is empty after * the function returns. */ static pj_bool_t tls_flush_pending_tx(struct tls_transport *tls) { pj_bool_t empty; pj_lock_acquire(tls->base.lock); while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; pjsip_tx_data *tdata; pj_ioqueue_op_key_t *op_key; pj_ssize_t size; pj_status_t status; pending_tx = tls->delayed_list.next; tdata = pending_tx->tdata_op_key->tdata; op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; /* send the txdata */ status = ssl_write(tls, tdata); /* On EWOULDBLOCK, suspend further transmissions */ if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { break; } /* tdata has been transmitted (successfully or with failure). * In any case, remove it from pending transmission list. */ pj_list_erase(pending_tx); /* Notify callback */ if (status == PJ_SUCCESS) size = tdata->buf.cur - tdata->buf.start; else size = -status; on_write_complete(tls->key, op_key, size); } empty = pj_list_empty(&tls->delayed_list); pj_lock_release(tls->base.lock); return empty; } /* Called by transport manager to destroy transport */ static pj_status_t tls_destroy_transport(pjsip_transport *transport) { struct tls_transport *tls = (struct tls_transport*)transport; /* Transport would have been unregistered by now since this callback * is called by transport manager. */ tls->is_registered = PJ_FALSE; return tls_destroy(transport, tls->close_reason); } /* Destroy TLS transport */ static pj_status_t tls_destroy(pjsip_transport *transport, pj_status_t reason) { struct tls_transport *tls = (struct tls_transport*)transport; if (tls->close_reason == 0) tls->close_reason = reason; if (tls->is_registered) { tls->is_registered = PJ_FALSE; pjsip_transport_destroy(transport); /* pjsip_transport_destroy will recursively call this function * again. */ return PJ_SUCCESS; } /* Mark transport as closing */ ++tls->is_closing; /* Stop keep-alive timer. */ if (tls->ka_timer.id) { pjsip_endpt_cancel_timer(tls->base.endpt, &tls->ka_timer); tls->ka_timer.id = PJ_FALSE; } /* Cancel all delayed transmits */ while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tls->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_write_complete(tls->key, op_key, -reason); } if (tls->rdata.tp_info.pool) { pj_pool_release(tls->rdata.tp_info.pool); tls->rdata.tp_info.pool = NULL; } if (tls->key) { pj_ioqueue_unregister(tls->key); tls->key = NULL; tls->sock = PJ_INVALID_SOCKET; } if (tls->sock != PJ_INVALID_SOCKET) { pj_sock_close(tls->sock); tls->sock = PJ_INVALID_SOCKET; } if (tls->base.lock) { pj_lock_destroy(tls->base.lock); tls->base.lock = NULL; } if (tls->base.ref_cnt) { pj_atomic_destroy(tls->base.ref_cnt); tls->base.ref_cnt = NULL; } if (tls->ssl) { SSL_free(tls->ssl); tls->ssl = NULL; } if (tls->base.pool) { pj_pool_t *pool; if (reason != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(reason, errmsg, sizeof(errmsg)); PJ_LOG(4,(tls->base.obj_name, "TLS transport destroyed with reason %d: %s", reason, errmsg)); } else { PJ_LOG(4,(tls->base.obj_name, "TLS transport destroyed normally")); } pool = tls->base.pool; tls->base.pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; } /* * This utility function creates receive data buffers and start * asynchronous recv() operations from the socket. It is called after * accept() or connect() operation complete. */ static pj_status_t tls_start_read(struct tls_transport *tls) { pj_pool_t *pool; pj_ssize_t size; pj_sockaddr_in *rem_addr; pj_status_t status; /* Init rdata */ pool = pjsip_endpt_create_pool(tls->base.endpt, "rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC); if (!pool) { ssl_report_error(tls->base.obj_name, 4, PJ_ENOMEM, "Unable to create pool for listener rxdata"); return PJ_ENOMEM; } tls->rdata.tp_info.pool = pool; tls->rdata.tp_info.transport = &tls->base; tls->rdata.tp_info.tp_data = tls; tls->rdata.tp_info.op_key.rdata = &tls->rdata; pj_ioqueue_op_key_init(&tls->rdata.tp_info.op_key.op_key, sizeof(pj_ioqueue_op_key_t)); tls->rdata.pkt_info.src_addr = tls->base.key.rem_addr; tls->rdata.pkt_info.src_addr_len = sizeof(pj_sockaddr_in); rem_addr = (pj_sockaddr_in*) &tls->base.key.rem_addr; pj_ansi_strcpy(tls->rdata.pkt_info.src_name, pj_inet_ntoa(rem_addr->sin_addr)); tls->rdata.pkt_info.src_port = pj_ntohs(rem_addr->sin_port); /* Here's the real trick with OpenSSL. * Since asynchronous socket operation with OpenSSL uses select() like * mechanism, it's not really compatible with PJLIB's ioqueue. So to * make them "talk" together, we simulate select() by using MSG_PEEK * when we call pj_ioqueue_recv(). */ size = 1; status = pj_ioqueue_recv(tls->key, &tls->rdata.tp_info.op_key.op_key, tls->rdata.pkt_info.packet, &size, PJ_IOQUEUE_ALWAYS_ASYNC | PJ_MSG_PEEK); if (status != PJ_SUCCESS && status != PJ_EPENDING) { ssl_report_error(tls->base.obj_name, 4, status, "ioqueue_recv() error"); return status; } return PJ_SUCCESS; } /* This callback is called by transport manager for the TLS factory * to create outgoing transport to the specified destination. */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_transport **p_transport) { struct tls_listener *listener; struct tls_transport *tls; pj_sock_t sock; pj_sockaddr_in local_addr; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); /* Check that address is a sockaddr_in */ PJ_ASSERT_RETURN(rem_addr->addr.sa_family == pj_AF_INET() && addr_len == sizeof(pj_sockaddr_in), PJ_EINVAL); listener = (struct tls_listener*)factory; /* Create socket */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) return status; /* Bind to any port */ status = pj_sock_bind_in(sock, 0, 0); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Get the local port */ addr_len = sizeof(pj_sockaddr_in); status = pj_sock_getsockname(sock, &local_addr, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Initially set the address from the listener's address */ local_addr.sin_addr.s_addr = ((pj_sockaddr_in*)&listener->factory.local_addr)->sin_addr.s_addr; /* Create the transport descriptor */ status = tls_create(listener, NULL, sock, PJ_FALSE, &local_addr, (pj_sockaddr_in*)rem_addr, &tls); if (status != PJ_SUCCESS) return status; /* Start asynchronous connect() operation */ tls->has_pending_connect = PJ_TRUE; status = pj_ioqueue_connect(tls->key, rem_addr, sizeof(pj_sockaddr_in)); if (status == PJ_SUCCESS) { /* Immediate socket connect() ! */ tls->has_pending_connect = PJ_FALSE; /* Perform SSL_connect() */ status = ssl_connect(tls); if (status != PJ_SUCCESS) { tls_destroy(&tls->base, status); return status; } } else if (status != PJ_EPENDING) { tls_destroy(&tls->base, status); return status; } /* Update (again) local address, just in case local address currently * set is different now that asynchronous connect() is started. */ addr_len = sizeof(pj_sockaddr_in); if (pj_sock_getsockname(tls->sock, &local_addr, &addr_len)==PJ_SUCCESS) { pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tls->base.local_addr; /* Some systems (like old Win32 perhaps) may not set local address * properly before socket is fully connected. */ if (tp_addr->sin_addr.s_addr != local_addr.sin_addr.s_addr && local_addr.sin_addr.s_addr != 0) { tp_addr->sin_addr.s_addr = local_addr.sin_addr.s_addr; tp_addr->sin_port = local_addr.sin_port; sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, &local_addr); } } if (tls->has_pending_connect) { PJ_LOG(4,(tls->base.obj_name, "TLS transport %.*s:%d is connecting to %.*s:%d...", (int)tls->base.local_name.host.slen, tls->base.local_name.host.ptr, tls->base.local_name.port, (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port)); } /* Done */ *p_transport = &tls->base; return PJ_SUCCESS; } /* * This callback is called by ioqueue when pending accept() operation has * completed. */ static void on_accept_complete( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_sock_t sock, pj_status_t status) { struct tls_listener *listener; struct tls_transport *tls; struct pending_accept *accept_op; int err_cnt = 0; listener = (struct tls_listener*) pj_ioqueue_get_user_data(key); accept_op = (struct pending_accept*) op_key; /* * Loop while there is immediate connection or when there is error. */ do { if (status == PJ_EPENDING) { /* * This can only happen when this function is called during * initialization to kick off asynchronous accept(). */ } else if (status != PJ_SUCCESS) { /* * Error in accept(). */ ssl_report_error(listener->factory.obj_name, 4, status, "Error in asynchronous accept() completion"); /* * Prevent endless accept() error loop by limiting the * number of consecutive errors. Once the number of errors * is equal to maximum, we treat this as permanent error, and * we stop the accept() operation. */ ++err_cnt; if (err_cnt >= 10) { PJ_LOG(1, (listener->factory.obj_name, "Too many errors, listener stopping")); } } else { pj_pool_t *pool; struct pending_accept *new_op; if (sock == PJ_INVALID_SOCKET) { sock = accept_op->new_sock; } if (sock == PJ_INVALID_SOCKET) { pj_assert(!"Should not happen. status should be error"); goto next_accept; } PJ_LOG(4,(listener->factory.obj_name, "TLS listener %.*s:%d: got incoming TCP connection " "from %s:%d, sock=%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port, pj_inet_ntoa(accept_op->remote_addr.sin_addr), pj_ntohs(accept_op->remote_addr.sin_port), sock)); /* Create new accept_opt */ pool = pjsip_endpt_create_pool(listener->endpt, "tlss%p", POOL_TP_INIT, POOL_TP_INC); new_op = PJ_POOL_ZALLOC_T(pool, struct pending_accept); new_op->pool = pool; new_op->listener = listener; new_op->index = accept_op->index; pj_ioqueue_op_key_init(&new_op->op_key, sizeof(new_op->op_key)); listener->accept_op[accept_op->index] = new_op; /* * Incoming connections! * Create TLS transport for the new socket. */ status = tls_create( listener, accept_op->pool, sock, PJ_TRUE, &accept_op->local_addr, &accept_op->remote_addr, &tls); if (status == PJ_SUCCESS) { /* Complete SSL_accept() */ status = ssl_accept(tls); } if (status == PJ_SUCCESS) { /* Start asynchronous read from the socket */ status = tls_start_read(tls); } if (status != PJ_SUCCESS) { ssl_report_error(tls->base.obj_name, 4, status, "Error creating incoming TLS transport"); pjsip_transport_shutdown(&tls->base); } else { /* Start keep-alive timer */ if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) { pj_time_val delay = {PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0}; pjsip_endpt_schedule_timer(listener->endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tls->last_activity); } } accept_op = new_op; } next_accept: /* * Start the next asynchronous accept() operation. */ accept_op->addr_len = sizeof(pj_sockaddr_in); accept_op->new_sock = PJ_INVALID_SOCKET; status = pj_ioqueue_accept(listener->key, &accept_op->op_key, &accept_op->new_sock, &accept_op->local_addr, &accept_op->remote_addr, &accept_op->addr_len); /* * Loop while we have immediate connection or when there is error. */ } while (status != PJ_EPENDING); } /* * Callback from ioqueue when packet is sent. */ static void on_write_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { struct tls_transport *tls; pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key; tls = (struct tls_transport*) pj_ioqueue_get_user_data(key); tdata_op_key->tdata = NULL; if (tdata_op_key->callback) { /* * Notify sip_transport.c that packet has been sent. */ if (bytes_sent == 0) bytes_sent = -PJ_RETURN_OS_ERROR(OSERR_ENOTCONN); tdata_op_key->callback(&tls->base, tdata_op_key->token, bytes_sent); } /* Check for error/closure */ if (bytes_sent <= 0) { pj_status_t status; ssl_report_error(tls->base.obj_name, 4, -bytes_sent, "TLS send() error"); status = (bytes_sent == 0) ? PJ_STATUS_FROM_OS(OSERR_ENOTCONN) : -bytes_sent; if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status; pjsip_transport_shutdown(&tls->base); } else { /* Mark last activity */ pj_gettimeofday(&tls->last_activity); } } /* Add tdata to pending list */ static void add_pending_tx(struct tls_transport *tls, pjsip_tx_data *tdata) { struct delayed_tdata *delayed_tdata; delayed_tdata = PJ_POOL_ALLOC_T(tdata->pool, struct delayed_tdata); delayed_tdata->tdata_op_key = &tdata->op_key; pj_list_push_back(&tls->delayed_list, delayed_tdata); } /* * This callback is called by transport manager to send SIP message */ static pj_status_t tls_send_msg(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, void (*callback)(pjsip_transport *transport, void *token, pj_ssize_t sent_bytes)) { struct tls_transport *tls = (struct tls_transport*)transport; pj_ssize_t size; pj_bool_t delayed = PJ_FALSE; pj_status_t status = PJ_SUCCESS; /* Sanity check */ PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL); /* Check that there's no pending operation associated with the tdata */ PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX); /* Check the address is supported */ PJ_ASSERT_RETURN(rem_addr && addr_len==sizeof(pj_sockaddr_in), PJ_EINVAL); /* Init op key. */ tdata->op_key.tdata = tdata; tdata->op_key.token = token; tdata->op_key.callback = callback; /* If asynchronous connect() has not completed yet, just put the * transmit data in the pending transmission list since we can not * use the socket yet. */ if (tls->has_pending_connect) { /* * Looks like connect() is still in progress. Check again (this time * with holding the lock) to be sure. */ pj_lock_acquire(tls->base.lock); if (tls->has_pending_connect) { /* * connect() is still in progress. Put the transmit data to * the delayed list. */ add_pending_tx(tls, tdata); status = PJ_EPENDING; /* Prevent ssl_write() to be called below */ delayed = PJ_TRUE; } pj_lock_release(tls->base.lock); } if (!delayed) { pj_bool_t no_pending_tx; /* Make sure that we've flushed pending tx first so that * stream is in order. */ no_pending_tx = tls_flush_pending_tx(tls); /* Send data immediately with SSL_write() if we don't have * pending data in our list. */ if (no_pending_tx) { status = ssl_write(tls, tdata); /* On EWOULDBLOCK, put this tdata in the list */ if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { add_pending_tx(tls, tdata); status = PJ_EPENDING; } if (status != PJ_EPENDING) { /* Not pending (could be immediate success or error) */ tdata->op_key.tdata = NULL; /* Shutdown transport on closure/errors */ if (status != PJ_SUCCESS) { size = -status; ssl_report_error(tls->base.obj_name, 4, status, "TLS send() error"); if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status; pjsip_transport_shutdown(&tls->base); } } } else { /* We have pending data in our list, so queue the txdata * in the pending tx list. */ add_pending_tx(tls, tdata); status = PJ_EPENDING; } } return status; } /* * This callback is called by transport manager to shutdown transport. * This normally is only used by UDP transport. */ static pj_status_t tls_shutdown(pjsip_transport *transport) { struct tls_transport *tls = (struct tls_transport*)transport; /* Shutdown SSL */ if (!tls->ssl_shutdown_called) { /* shutdown SSL */ SSL_shutdown(tls->ssl); tls->ssl_shutdown_called = PJ_TRUE; /* Stop keep-alive timer. */ if (tls->ka_timer.id) { pjsip_endpt_cancel_timer(tls->base.endpt, &tls->ka_timer); tls->ka_timer.id = PJ_FALSE; } PJ_LOG(4,(transport->obj_name, "TLS transport shutdown")); } return PJ_SUCCESS; } /* * Callback from ioqueue that an incoming data is received from the socket. */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read_unused) { enum { MAX_IMMEDIATE_PACKET = 10 }; pjsip_rx_data_op_key *rdata_op_key = (pjsip_rx_data_op_key*) op_key; pjsip_rx_data *rdata = rdata_op_key->rdata; struct tls_transport *tls = (struct tls_transport*)rdata->tp_info.transport; int i; pj_status_t status; /* Don't do anything if transport is closing. */ if (tls->is_closing) { tls->is_closing++; return; } /* Recall that we use MSG_PEEK when calling ioqueue_recv(), so * when this callback is called, data has not actually been read * from socket buffer. */ for (i=0;; ++i) { pj_uint32_t flags; /* Read data from SSL connection */ status = ssl_read(tls); if (status == PJ_SUCCESS) { /* * We have packet! */ pj_size_t size_eaten; /* Mark last activity */ pj_gettimeofday(&tls->last_activity); /* Init pkt_info part. */ rdata->pkt_info.zero = 0; pj_gettimeofday(&rdata->pkt_info.timestamp); /* Report to transport manager. * The transport manager will tell us how many bytes of the packet * have been processed (as valid SIP message). */ size_eaten = pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, rdata); pj_assert(size_eaten <= (pj_size_t)rdata->pkt_info.len); /* Move unprocessed data to the front of the buffer */ if (size_eaten>0 && size_eaten<(pj_size_t)rdata->pkt_info.len) { pj_memmove(rdata->pkt_info.packet, rdata->pkt_info.packet + size_eaten, rdata->pkt_info.len - size_eaten); } rdata->pkt_info.len -= size_eaten; } else if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { /* Ignore EWOULDBLOCK error (?) */ } else { /* For other errors, treat as transport being closed */ ssl_report_error(tls->base.obj_name, 4, status, "Error reading SSL stream"); /* We can not destroy the transport since high level objects may * still keep reference to this transport. So we can only * instruct transport manager to gracefully start the shutdown * procedure for this transport. */ if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status; pjsip_transport_shutdown(&tls->base); return; } /* Reset pool. */ pj_pool_reset(rdata->tp_info.pool); /* If we have pending data in SSL buffer, read it. */ if (SSL_pending(tls->ssl)) { /* Check that we have enough space in buffer */ if (rdata->pkt_info.len >= PJSIP_MAX_PKT_LEN-1) { PJ_LOG(4,(tls->base.obj_name, "Incoming packet dropped from tls:%.*s:%d " "because it's too big (%d bytes)", (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port, rdata->pkt_info.len)); rdata->pkt_info.len = 0; } continue; } /* If we've reached maximum number of packets received on a single * poll, force the next reading to be asynchronous. */ if (i >= MAX_IMMEDIATE_PACKET) { /* Receive quota reached. Force ioqueue_recv() to * return PJ_EPENDING */ flags = PJ_IOQUEUE_ALWAYS_ASYNC; } else { /* This doesn't work too well when IOCP is used, as it seems that * IOCP refuses to do MSG_PEEK when WSARecv() is called without * OVERLAP structure. With OpenSSL it gives this error: * error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version * number * * flags = 0 */ flags = PJ_IOQUEUE_ALWAYS_ASYNC; } /* Read next packet from the network. Remember, we need to use * MSG_PEEK or otherwise the packet will be eaten by us! */ bytes_read_unused = 1; status = pj_ioqueue_recv(key, op_key, rdata->pkt_info.packet+rdata->pkt_info.len, &bytes_read_unused, flags | PJ_MSG_PEEK); if (status == PJ_SUCCESS) { /* Continue loop. */ pj_assert(i < MAX_IMMEDIATE_PACKET); } else if (status == PJ_EPENDING) { break; } else { /* Socket error */ /* We can not destroy the transport since high level objects may * still keep reference to this transport. So we can only * instruct transport manager to gracefully start the shutdown * procedure for this transport. */ if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status; pjsip_transport_shutdown(&tls->base); return; } } } /* * Callback from ioqueue when asynchronous connect() operation completes. */ static void on_connect_complete(pj_ioqueue_key_t *key, pj_status_t status) { struct tls_transport *tls; pj_sockaddr_in addr; int addrlen; tls = (struct tls_transport*) pj_ioqueue_get_user_data(key); /* Check connect() status */ if (status != PJ_SUCCESS) { /* Mark that pending connect() operation has completed. */ tls->has_pending_connect = PJ_FALSE; ssl_report_error(tls->base.obj_name, 4, status, "Error connecting to %.*s:%d", (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port); /* Cancel all delayed transmits */ while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tls->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_write_complete(tls->key, op_key, -status); } /* We can not destroy the transport since high level objects may * still keep reference to this transport. So we can only * instruct transport manager to gracefully start the shutdown * procedure for this transport. */ if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status; pjsip_transport_shutdown(&tls->base); return; } PJ_LOG(4,(tls->base.obj_name, "TCP transport %.*s:%d is connected to %.*s:%d", (int)tls->base.local_name.host.slen, tls->base.local_name.host.ptr, tls->base.local_name.port, (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port)); /* Update (again) local address, just in case local address currently * set is different now that the socket is connected (could happen * on some systems, like old Win32 probably?). */ addrlen = sizeof(pj_sockaddr_in); if (pj_sock_getsockname(tls->sock, &addr, &addrlen)==PJ_SUCCESS) { pj_sockaddr_in *tp_addr = (pj_sockaddr_in*)&tls->base.local_addr; if (tp_addr->sin_addr.s_addr != addr.sin_addr.s_addr) { tp_addr->sin_addr.s_addr = addr.sin_addr.s_addr; tp_addr->sin_port = addr.sin_port; sockaddr_to_host_port(tls->base.pool, &tls->base.local_name, tp_addr); } } /* Perform SSL_connect() */ status = ssl_connect(tls); if (status != PJ_SUCCESS) { /* Cancel all delayed transmits */ while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tls->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_write_complete(tls->key, op_key, -status); } if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status; pjsip_transport_shutdown(&tls->base); return; } /* Mark that pending connect() operation has completed. */ tls->has_pending_connect = PJ_FALSE; /* Start pending read */ status = tls_start_read(tls); if (status != PJ_SUCCESS) { /* Cancel all delayed transmits */ while (!pj_list_empty(&tls->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tls->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_write_complete(tls->key, op_key, -status); } /* We can not destroy the transport since high level objects may * still keep reference to this transport. So we can only * instruct transport manager to gracefully start the shutdown * procedure for this transport. */ if (tls->close_reason==PJ_SUCCESS) tls->close_reason = status; pjsip_transport_shutdown(&tls->base); return; } /* Flush all pending send operations */ tls_flush_pending_tx(tls); /* Start keep-alive timer */ if (PJSIP_TLS_KEEP_ALIVE_INTERVAL) { pj_time_val delay = { PJSIP_TLS_KEEP_ALIVE_INTERVAL, 0 }; pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tls->last_activity); } } /* Transport keep-alive timer callback */ static void tls_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e) { struct tls_transport *tls = (struct tls_transport*) e->user_data; const pj_str_t ka_data = PJSIP_TLS_KEEP_ALIVE_DATA; pj_time_val delay; pj_time_val now; pj_status_t status; PJ_UNUSED_ARG(th); tls->ka_timer.id = PJ_TRUE; pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, tls->last_activity); if (now.sec > 0 && now.sec < PJSIP_TLS_KEEP_ALIVE_INTERVAL) { /* There has been activity, so don't send keep-alive */ delay.sec = PJSIP_TLS_KEEP_ALIVE_INTERVAL - now.sec; delay.msec = 0; pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; return; } PJ_LOG(5,(tls->base.obj_name, "Sending %d byte(s) keep-alive to %.*s:%d", (int)ka_data.slen, (int)tls->base.remote_name.host.slen, tls->base.remote_name.host.ptr, tls->base.remote_name.port)); /* Send the data */ status = ssl_write_bytes(tls, ka_data.ptr, (int)ka_data.slen, "keep-alive"); if (status != PJ_SUCCESS && status != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { ssl_report_error(tls->base.obj_name, 1, status, "Error sending keep-alive packet"); return; } /* Register next keep-alive */ delay.sec = PJSIP_TLS_KEEP_ALIVE_INTERVAL; delay.msec = 0; pjsip_endpt_schedule_timer(tls->base.endpt, &tls->ka_timer, &delay); tls->ka_timer.id = PJ_TRUE; } #endif /* PJSIP_HAS_TLS_TRANSPORT */