⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_transport_tls_ossl.c

📁 基于sip协议的网络电话源码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* $Id: sip_transport_tls_ossl.c 974 2007-02-19 01:13:53Z bennylp $ *//*  * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> * * 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 <pjsip/sip_transport_tls.h>#include <pjsip/sip_endpoint.h>#include <pjsip/sip_errno.h>#include <pj/compat/socket.h>#include <pj/addr_resolv.h>#include <pj/assert.h>#include <pj/ioqueue.h>#include <pj/lock.h>#include <pj/log.h>#include <pj/os.h>#include <pj/pool.h>#include <pj/compat/socket.h>#include <pj/sock_select.h>#include <pj/string.h>/* Only build when PJSIP_HAS_TLS_TRANSPORT is enabled */#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0/*  * Include OpenSSL headers  */#include <openssl/bio.h>#include <openssl/ssl.h>#include <openssl/err.h>/* * 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	4000#define POOL_LIS_INC	4001#define POOL_TP_INIT	4000#define POOL_TP_INC	4002/** * 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;    struct tls_listener	    *listener;    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;    /* 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 (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);	}    }    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 = 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 = 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;    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 */    switch (opt->method) {    case PJSIP_SSL_DEFAULT_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);    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);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -