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

📄 sip_transport_tls_ossl.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
/* $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');

⌨️ 快捷键说明

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