📄 sip_transport_tls_ossl.c
字号:
/* $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 + -