Mon Oct 8 12:39:30 2012

Asterisk developer's documentation


tcptls.h File Reference

Generic support for tcp/tls servers in Asterisk. More...

#include "asterisk/netsock2.h"
#include "asterisk/utils.h"
#include <openssl/ssl.h>
#include <openssl/err.h>

Go to the source code of this file.

Data Structures

struct  ast_tcptls_session_args
 arguments for the accepting thread More...
struct  ast_tcptls_session_instance
struct  ast_tls_config

Defines

#define AST_CERTFILE   "asterisk.pem"
#define DO_SSL
#define HOOK_T   ssize_t
#define LEN_T   size_t

Enumerations

enum  ast_ssl_flags {
  AST_SSL_VERIFY_CLIENT = (1 << 0), AST_SSL_DONT_VERIFY_SERVER = (1 << 1), AST_SSL_IGNORE_COMMON_NAME = (1 << 2), AST_SSL_SSLV2_CLIENT = (1 << 3),
  AST_SSL_SSLV3_CLIENT = (1 << 4), AST_SSL_TLSV1_CLIENT = (1 << 5)
}

Functions

int ast_ssl_setup (struct ast_tls_config *cfg)
 Set up an SSL server.
void ast_ssl_teardown (struct ast_tls_config *cfg)
 free resources used by an SSL server
ast_tcptls_session_instanceast_tcptls_client_create (struct ast_tcptls_session_args *desc)
ast_tcptls_session_instanceast_tcptls_client_start (struct ast_tcptls_session_instance *tcptls_session)
 attempts to connect and start tcptls session, on error the tcptls_session's ref count is decremented, fd and file are closed, and NULL is returned.
void ast_tcptls_close_session_file (struct ast_tcptls_session_instance *tcptls_session)
 Closes a tcptls session instance's file and/or file descriptor. The tcptls_session will be set to NULL and it's file descriptor will be set to -1 by this function.
HOOK_T ast_tcptls_server_read (struct ast_tcptls_session_instance *ser, void *buf, size_t count)
void * ast_tcptls_server_root (void *)
void ast_tcptls_server_start (struct ast_tcptls_session_args *desc)
 This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a thread for handling accept().
void ast_tcptls_server_stop (struct ast_tcptls_session_args *desc)
 Shutdown a running server if there is one.
HOOK_T ast_tcptls_server_write (struct ast_tcptls_session_instance *ser, const void *buf, size_t count)
int ast_tls_read_conf (struct ast_tls_config *tls_cfg, struct ast_tcptls_session_args *tls_desc, const char *varname, const char *value)
 Used to parse conf files containing tls/ssl options.


Detailed Description

Generic support for tcp/tls servers in Asterisk.

Note:
In order to have TLS/SSL support, we need the openssl libraries. Still we can decide whether or not to use them by commenting in or out the DO_SSL macro.
TLS/SSL support is basically implemented by reading from a config file (currently http.conf and sip.conf) the names of the certificate and cipher to use, and then run ssl_setup() to create an appropriate SSL_CTX (ssl_ctx) If we support multiple domains, presumably we need to read multiple certificates.

When we are requested to open a TLS socket, we run make_file_from_fd() on the socket, to do the necessary setup. At the moment the context's name is hardwired in the function, but we can certainly make it into an extra parameter to the function.

We declare most of ssl support variables unconditionally, because their number is small and this simplifies the code.

Note:
The ssl-support variables (ssl_ctx, do_ssl, certfile, cipher) and their setup should be moved to a more central place, e.g. asterisk.conf and the source files that processes it. Similarly, ssl_setup() should be run earlier in the startup process so modules have it available.

Definition in file tcptls.h.


Define Documentation

#define AST_CERTFILE   "asterisk.pem"

SSL support

Definition at line 68 of file tcptls.h.

Referenced by __ast_http_load(), __init_manager(), and reload_config().

#define DO_SSL

Definition at line 55 of file tcptls.h.

#define HOOK_T   ssize_t

Definition at line 158 of file tcptls.h.

#define LEN_T   size_t

Definition at line 159 of file tcptls.h.


Enumeration Type Documentation

enum ast_ssl_flags

Enumerator:
AST_SSL_VERIFY_CLIENT  Verify certificate when acting as server
AST_SSL_DONT_VERIFY_SERVER  Don't verify certificate when connecting to a server
AST_SSL_IGNORE_COMMON_NAME  Don't compare "Common Name" against IP or hostname
AST_SSL_SSLV2_CLIENT  Use SSLv2 for outgoing client connections
AST_SSL_SSLV3_CLIENT  Use SSLv3 for outgoing client connections
AST_SSL_TLSV1_CLIENT  Use TLSv1 for outgoing client connections

Definition at line 70 of file tcptls.h.

00070                    {
00071    /*! Verify certificate when acting as server */
00072    AST_SSL_VERIFY_CLIENT = (1 << 0),
00073    /*! Don't verify certificate when connecting to a server */
00074    AST_SSL_DONT_VERIFY_SERVER = (1 << 1),
00075    /*! Don't compare "Common Name" against IP or hostname */
00076    AST_SSL_IGNORE_COMMON_NAME = (1 << 2),
00077    /*! Use SSLv2 for outgoing client connections */
00078    AST_SSL_SSLV2_CLIENT = (1 << 3),
00079    /*! Use SSLv3 for outgoing client connections */
00080    AST_SSL_SSLV3_CLIENT = (1 << 4),
00081    /*! Use TLSv1 for outgoing client connections */
00082    AST_SSL_TLSV1_CLIENT = (1 << 5)
00083 };


Function Documentation

int ast_ssl_setup ( struct ast_tls_config cfg  ) 

Set up an SSL server.

Parameters:
cfg Configuration for the SSL server
Return values:
1 Success
0 Failure

Definition at line 401 of file tcptls.c.

References __ssl_setup().

00402 {
00403    return __ssl_setup(cfg, 0);
00404 }

void ast_ssl_teardown ( struct ast_tls_config cfg  ) 

free resources used by an SSL server

Note:
This only needs to be called if ast_ssl_setup() was directly called first.
Parameters:
cfg Configuration for the SSL server

Definition at line 406 of file tcptls.c.

References ast_tls_config::ssl_ctx.

Referenced by unload_module().

00407 {
00408 #ifdef DO_SSL
00409    if (cfg->ssl_ctx) {
00410       SSL_CTX_free(cfg->ssl_ctx);
00411       cfg->ssl_ctx = NULL;
00412    }
00413 #endif
00414 }

struct ast_tcptls_session_instance* ast_tcptls_client_create ( struct ast_tcptls_session_args desc  ) 

Definition at line 453 of file tcptls.c.

References ao2_alloc, ao2_ref, ast_bind(), ast_debug, ast_log(), ast_mutex_init, ast_sockaddr_cmp(), ast_sockaddr_copy(), ast_sockaddr_is_ipv6(), ast_sockaddr_isnull(), ast_sockaddr_setnull(), ast_sockaddr_stringify(), desc, errno, LOG_ERROR, LOG_WARNING, and session_instance_destructor().

Referenced by sip_prepare_socket().

00454 {
00455    int x = 1;
00456    struct ast_tcptls_session_instance *tcptls_session = NULL;
00457 
00458    /* Do nothing if nothing has changed */
00459    if (!ast_sockaddr_cmp(&desc->old_address, &desc->remote_address)) {
00460       ast_debug(1, "Nothing changed in %s\n", desc->name);
00461       return NULL;
00462    }
00463 
00464    /* If we return early, there is no connection */
00465    ast_sockaddr_setnull(&desc->old_address);
00466 
00467    if (desc->accept_fd != -1)
00468       close(desc->accept_fd);
00469 
00470    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->remote_address) ?
00471              AF_INET6 : AF_INET, SOCK_STREAM, IPPROTO_TCP);
00472    if (desc->accept_fd < 0) {
00473       ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
00474          desc->name, strerror(errno));
00475       return NULL;
00476    }
00477 
00478    /* if a local address was specified, bind to it so the connection will
00479       originate from the desired address */
00480    if (!ast_sockaddr_isnull(&desc->local_address)) {
00481       setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00482       if (ast_bind(desc->accept_fd, &desc->local_address)) {
00483          ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
00484             desc->name,
00485             ast_sockaddr_stringify(&desc->local_address),
00486             strerror(errno));
00487          goto error;
00488       }
00489    }
00490 
00491    if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor)))
00492       goto error;
00493 
00494    ast_mutex_init(&tcptls_session->lock);
00495    tcptls_session->client = 1;
00496    tcptls_session->fd = desc->accept_fd;
00497    tcptls_session->parent = desc;
00498    tcptls_session->parent->worker_fn = NULL;
00499    ast_sockaddr_copy(&tcptls_session->remote_address,
00500            &desc->remote_address);
00501 
00502    /* Set current info */
00503    ast_sockaddr_copy(&desc->old_address, &desc->remote_address);
00504    return tcptls_session;
00505 
00506 error:
00507    close(desc->accept_fd);
00508    desc->accept_fd = -1;
00509    if (tcptls_session)
00510       ao2_ref(tcptls_session, -1);
00511    return NULL;
00512 }

struct ast_tcptls_session_instance* ast_tcptls_client_start ( struct ast_tcptls_session_instance tcptls_session  ) 

attempts to connect and start tcptls session, on error the tcptls_session's ref count is decremented, fd and file are closed, and NULL is returned.

Definition at line 416 of file tcptls.c.

References __ssl_setup(), ao2_ref, ast_connect(), ast_log(), ast_sockaddr_stringify(), desc, errno, handle_tcptls_connection(), LOG_ERROR, and ast_tcptls_session_instance::parent.

Referenced by _sip_tcp_helper_thread().

00417 {
00418    struct ast_tcptls_session_args *desc;
00419    int flags;
00420 
00421    if (!(desc = tcptls_session->parent)) {
00422       goto client_start_error;
00423    }
00424 
00425    if (ast_connect(desc->accept_fd, &desc->remote_address)) {
00426       ast_log(LOG_ERROR, "Unable to connect %s to %s: %s\n",
00427          desc->name,
00428          ast_sockaddr_stringify(&desc->remote_address),
00429          strerror(errno));
00430       goto client_start_error;
00431    }
00432 
00433    flags = fcntl(desc->accept_fd, F_GETFL);
00434    fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
00435 
00436    if (desc->tls_cfg) {
00437       desc->tls_cfg->enabled = 1;
00438       __ssl_setup(desc->tls_cfg, 1);
00439    }
00440 
00441    return handle_tcptls_connection(tcptls_session);
00442 
00443 client_start_error:
00444    if (desc) {
00445       close(desc->accept_fd);
00446       desc->accept_fd = -1;
00447    }
00448    ao2_ref(tcptls_session, -1);
00449    return NULL;
00450 
00451 }

void ast_tcptls_close_session_file ( struct ast_tcptls_session_instance tcptls_session  ) 

Closes a tcptls session instance's file and/or file descriptor. The tcptls_session will be set to NULL and it's file descriptor will be set to -1 by this function.

Definition at line 583 of file tcptls.c.

References ast_log(), errno, ast_tcptls_session_instance::f, ast_tcptls_session_instance::fd, and LOG_ERROR.

Referenced by ast_tcptls_server_root(), handle_tcptls_connection(), and sip_prepare_socket().

00584 {
00585    if (tcptls_session->f) {
00586       if (fclose(tcptls_session->f)) {
00587          ast_log(LOG_ERROR, "fclose() failed: %s\n", strerror(errno));
00588       }
00589       tcptls_session->f = NULL;
00590       tcptls_session->fd = -1;
00591    } else if (tcptls_session->fd != -1) {
00592       if (close(tcptls_session->fd)) {
00593          ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
00594       }
00595       tcptls_session->fd = -1;
00596    } else {
00597       ast_log(LOG_ERROR, "ast_tcptls_close_session_file invoked on session instance without file or file descriptor\n");
00598    }
00599 }

HOOK_T ast_tcptls_server_read ( struct ast_tcptls_session_instance ser,
void *  buf,
size_t  count 
)

Definition at line 104 of file tcptls.c.

References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, ast_tcptls_session_instance::ssl, and ssl_read().

00105 {
00106    if (tcptls_session->fd == -1) {
00107       ast_log(LOG_ERROR, "server_read called with an fd of -1\n");
00108       errno = EIO;
00109       return -1;
00110    }
00111 
00112 #ifdef DO_SSL
00113    if (tcptls_session->ssl)
00114       return ssl_read(tcptls_session->ssl, buf, count);
00115 #endif
00116    return read(tcptls_session->fd, buf, count);
00117 }

void* ast_tcptls_server_root ( void *   ) 

Definition at line 256 of file tcptls.c.

References ao2_alloc, ao2_ref, ast_accept(), ast_log(), ast_mutex_init, ast_pthread_create_detached_background, ast_sockaddr_copy(), ast_tcptls_close_session_file(), ast_wait_for_input(), desc, errno, handle_tcptls_connection(), LOG_ERROR, LOG_WARNING, and session_instance_destructor().

00257 {
00258    struct ast_tcptls_session_args *desc = data;
00259    int fd;
00260    struct ast_sockaddr addr;
00261    struct ast_tcptls_session_instance *tcptls_session;
00262    pthread_t launched;
00263 
00264    for (;;) {
00265       int i, flags;
00266 
00267       if (desc->periodic_fn)
00268          desc->periodic_fn(desc);
00269       i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
00270       if (i <= 0)
00271          continue;
00272       fd = ast_accept(desc->accept_fd, &addr);
00273       if (fd < 0) {
00274          if ((errno != EAGAIN) && (errno != EINTR))
00275             ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
00276          continue;
00277       }
00278       tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor);
00279       if (!tcptls_session) {
00280          ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
00281          if (close(fd)) {
00282             ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno));
00283          }
00284          continue;
00285       }
00286 
00287       ast_mutex_init(&tcptls_session->lock);
00288 
00289       flags = fcntl(fd, F_GETFL);
00290       fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
00291       tcptls_session->fd = fd;
00292       tcptls_session->parent = desc;
00293       ast_sockaddr_copy(&tcptls_session->remote_address, &addr);
00294 
00295       tcptls_session->client = 0;
00296 
00297       /* This thread is now the only place that controls the single ref to tcptls_session */
00298       if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) {
00299          ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
00300          ast_tcptls_close_session_file(tcptls_session);
00301          ao2_ref(tcptls_session, -1);
00302       }
00303    }
00304    return NULL;
00305 }

void ast_tcptls_server_start ( struct ast_tcptls_session_args desc  ) 

This is a generic (re)start routine for a TCP server, which does the socket/bind/listen and starts a thread for handling accept().

Version:
1.6.1 changed desc parameter to be of ast_tcptls_session_args type

Definition at line 514 of file tcptls.c.

References ast_bind(), ast_debug, ast_log(), ast_pthread_create_background, AST_PTHREADT_NULL, ast_sockaddr_cmp(), ast_sockaddr_copy(), ast_sockaddr_is_ipv6(), ast_sockaddr_isnull(), ast_sockaddr_setnull(), ast_sockaddr_stringify(), desc, errno, and LOG_ERROR.

00515 {
00516    int flags;
00517    int x = 1;
00518 
00519    /* Do nothing if nothing has changed */
00520    if (!ast_sockaddr_cmp(&desc->old_address, &desc->local_address)) {
00521       ast_debug(1, "Nothing changed in %s\n", desc->name);
00522       return;
00523    }
00524 
00525    /* If we return early, there is no one listening */
00526    ast_sockaddr_setnull(&desc->old_address);
00527 
00528    /* Shutdown a running server if there is one */
00529    if (desc->master != AST_PTHREADT_NULL) {
00530       pthread_cancel(desc->master);
00531       pthread_kill(desc->master, SIGURG);
00532       pthread_join(desc->master, NULL);
00533    }
00534 
00535    if (desc->accept_fd != -1)
00536       close(desc->accept_fd);
00537 
00538    /* If there's no new server, stop here */
00539    if (ast_sockaddr_isnull(&desc->local_address)) {
00540       ast_debug(2, "Server disabled:  %s\n", desc->name);
00541       return;
00542    }
00543 
00544    desc->accept_fd = socket(ast_sockaddr_is_ipv6(&desc->local_address) ?
00545              AF_INET6 : AF_INET, SOCK_STREAM, 0);
00546    if (desc->accept_fd < 0) {
00547       ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno));
00548       return;
00549    }
00550 
00551    setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00552    if (ast_bind(desc->accept_fd, &desc->local_address)) {
00553       ast_log(LOG_ERROR, "Unable to bind %s to %s: %s\n",
00554          desc->name,
00555          ast_sockaddr_stringify(&desc->local_address),
00556          strerror(errno));
00557       goto error;
00558    }
00559    if (listen(desc->accept_fd, 10)) {
00560       ast_log(LOG_ERROR, "Unable to listen for %s!\n", desc->name);
00561       goto error;
00562    }
00563    flags = fcntl(desc->accept_fd, F_GETFL);
00564    fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
00565    if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
00566       ast_log(LOG_ERROR, "Unable to launch thread for %s on %s: %s\n",
00567          desc->name,
00568          ast_sockaddr_stringify(&desc->local_address),
00569          strerror(errno));
00570       goto error;
00571    }
00572 
00573    /* Set current info */
00574    ast_sockaddr_copy(&desc->old_address, &desc->local_address);
00575 
00576    return;
00577 
00578 error:
00579    close(desc->accept_fd);
00580    desc->accept_fd = -1;
00581 }

void ast_tcptls_server_stop ( struct ast_tcptls_session_args desc  ) 

Shutdown a running server if there is one.

Version:
1.6.1 changed desc parameter to be of ast_tcptls_session_args type

Definition at line 601 of file tcptls.c.

References ast_debug, AST_PTHREADT_NULL, and desc.

Referenced by unload_module().

00602 {
00603    if (desc->master != AST_PTHREADT_NULL) {
00604       pthread_cancel(desc->master);
00605       pthread_kill(desc->master, SIGURG);
00606       pthread_join(desc->master, NULL);
00607       desc->master = AST_PTHREADT_NULL;
00608    }
00609    if (desc->accept_fd != -1)
00610       close(desc->accept_fd);
00611    desc->accept_fd = -1;
00612    ast_debug(2, "Stopped server :: %s\n", desc->name);
00613 }

HOOK_T ast_tcptls_server_write ( struct ast_tcptls_session_instance ser,
const void *  buf,
size_t  count 
)

Definition at line 119 of file tcptls.c.

References ast_log(), errno, ast_tcptls_session_instance::fd, LOG_ERROR, ast_tcptls_session_instance::ssl, and ssl_write().

00120 {
00121    if (tcptls_session->fd == -1) {
00122       ast_log(LOG_ERROR, "server_write called with an fd of -1\n");
00123       errno = EIO;
00124       return -1;
00125    }
00126 
00127 #ifdef DO_SSL
00128    if (tcptls_session->ssl)
00129       return ssl_write(tcptls_session->ssl, buf, count);
00130 #endif
00131    return write(tcptls_session->fd, buf, count);
00132 }

int ast_tls_read_conf ( struct ast_tls_config tls_cfg,
struct ast_tcptls_session_args tls_desc,
const char *  varname,
const char *  value 
)

Used to parse conf files containing tls/ssl options.

Definition at line 615 of file tcptls.c.

References ast_clear_flag, ast_free, ast_log(), ast_parse_arg(), ast_set2_flag, ast_set_flag, AST_SSL_DONT_VERIFY_SERVER, AST_SSL_SSLV2_CLIENT, AST_SSL_SSLV3_CLIENT, AST_SSL_TLSV1_CLIENT, AST_SSL_VERIFY_CLIENT, ast_strdup, ast_true(), ast_tls_config::cafile, ast_tls_config::capath, ast_tls_config::certfile, ast_tls_config::cipher, ast_tls_config::enabled, ast_tls_config::flags, ast_tcptls_session_args::local_address, LOG_WARNING, PARSE_ADDR, and ast_tls_config::pvtfile.

Referenced by __ast_http_load(), __init_manager(), and reload_config().

00616 {
00617    if (!strcasecmp(varname, "tlsenable") || !strcasecmp(varname, "sslenable")) {
00618       tls_cfg->enabled = ast_true(value) ? 1 : 0;
00619    } else if (!strcasecmp(varname, "tlscertfile") || !strcasecmp(varname, "sslcert") || !strcasecmp(varname, "tlscert")) {
00620       ast_free(tls_cfg->certfile);
00621       tls_cfg->certfile = ast_strdup(value);
00622    } else if (!strcasecmp(varname, "tlsprivatekey") || !strcasecmp(varname, "sslprivatekey")) {
00623       ast_free(tls_cfg->pvtfile);
00624       tls_cfg->pvtfile = ast_strdup(value);
00625    } else if (!strcasecmp(varname, "tlscipher") || !strcasecmp(varname, "sslcipher")) {
00626       ast_free(tls_cfg->cipher);
00627       tls_cfg->cipher = ast_strdup(value);
00628    } else if (!strcasecmp(varname, "tlscafile")) {
00629       ast_free(tls_cfg->cafile);
00630       tls_cfg->cafile = ast_strdup(value);
00631    } else if (!strcasecmp(varname, "tlscapath") || !strcasecmp(varname, "tlscadir")) {
00632       ast_free(tls_cfg->capath);
00633       tls_cfg->capath = ast_strdup(value);
00634    } else if (!strcasecmp(varname, "tlsverifyclient")) {
00635       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_VERIFY_CLIENT);
00636    } else if (!strcasecmp(varname, "tlsdontverifyserver")) {
00637       ast_set2_flag(&tls_cfg->flags, ast_true(value), AST_SSL_DONT_VERIFY_SERVER);
00638    } else if (!strcasecmp(varname, "tlsbindaddr") || !strcasecmp(varname, "sslbindaddr")) {
00639       if (ast_parse_arg(value, PARSE_ADDR, &tls_desc->local_address))
00640          ast_log(LOG_WARNING, "Invalid %s '%s'\n", varname, value);
00641    } else if (!strcasecmp(varname, "tlsclientmethod") || !strcasecmp(varname, "sslclientmethod")) {
00642       if (!strcasecmp(value, "tlsv1")) {
00643          ast_set_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00644          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00645          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00646       } else if (!strcasecmp(value, "sslv3")) {
00647          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00648          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00649          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00650       } else if (!strcasecmp(value, "sslv2")) {
00651          ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
00652          ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
00653          ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
00654       }
00655    } else {
00656       return -1;
00657    }
00658 
00659    return 0;
00660 }


Generated on Mon Oct 8 12:39:30 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7