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

📄 conn.c

📁 The Kannel Open Source WAP and SMS gateway works as both an SMS gateway, for implementing keyword b
💻 C
📖 第 1 页 / 共 3 页
字号:
/* ====================================================================  * The Kannel Software License, Version 1.0  *  * Copyright (c) 2001-2004 Kannel Group   * Copyright (c) 1998-2001 WapIT Ltd.    * All rights reserved.  *  * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions  * are met:  *  * 1. Redistributions of source code must retain the above copyright  *    notice, this list of conditions and the following disclaimer.  *  * 2. Redistributions in binary form must reproduce the above copyright  *    notice, this list of conditions and the following disclaimer in  *    the documentation and/or other materials provided with the  *    distribution.  *  * 3. The end-user documentation included with the redistribution,  *    if any, must include the following acknowledgment:  *       "This product includes software developed by the  *        Kannel Group (http://www.kannel.org/)."  *    Alternately, this acknowledgment may appear in the software itself,  *    if and wherever such third-party acknowledgments normally appear.  *  * 4. The names "Kannel" and "Kannel Group" must not be used to  *    endorse or promote products derived from this software without  *    prior written permission. For written permission, please   *    contact org@kannel.org.  *  * 5. Products derived from this software may not be called "Kannel",  *    nor may "Kannel" appear in their name, without prior written  *    permission of the Kannel Group.  *  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,   * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT   * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE   * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  * ====================================================================  *  * This software consists of voluntary contributions made by many  * individuals on behalf of the Kannel Group.  For more information on   * the Kannel Group, please see <http://www.kannel.org/>.  *  * Portions of this software are based upon software originally written at   * WapIT Ltd., Helsinki, Finland for the Kannel project.   */ /* conn.c - implement Connection type * * This file implements the interface defined in conn.h. * * Richard Braakman * * SSL client implementation contributed by * Jarkko Kovala <jarkko.kovala@iki.fi> * * SSL server implementation contributed by * Stipe Tolj <tolj@wapme-systems.de> for Wapme Systems AG *//* TODO: unlocked_close() on error *//* TODO: have I/O functions check if connection is open *//* TODO: have conn_open_tcp do a non-blocking connect() */#include <signal.h>#include <unistd.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <string.h>#include "gwlib/gwlib.h"#ifdef HAVE_LIBSSL#include <openssl/ssl.h>#include <openssl/err.h>static SSL_CTX *global_ssl_context = NULL;static SSL_CTX *global_server_ssl_context = NULL;#endif /* HAVE_LIBSSL */typedef unsigned long (*CRYPTO_CALLBACK_PTR)(void);/* * This used to be 4096.  It is now 0 so that callers don't have to * deal with the complexities of buffering (i.e. deciding when to * flush) unless they want to. * FIXME: Figure out how to combine buffering sensibly with use of * conn_register. */#define DEFAULT_OUTPUT_BUFFERING 0#define SSL_CONN_TIMEOUT         30struct Connection{    /* We use separate locks for input and ouput fields, so that     * read and write activities don't have to get in each other's     * way.  If you need both, then acquire the outlock first. */    Mutex *inlock;    Mutex *outlock;    volatile sig_atomic_t claimed;#ifndef NO_GWASSERT    long claiming_thread;#endif    /* fd value is read-only and is not locked */    int fd;    /* socket state */    enum {yes,no} connected;    /* Protected by outlock */    Octstr *outbuf;    long outbufpos;   /* start of unwritten data in outbuf */    /* Try to buffer writes until there are this many octets to send.     * Set it to 0 to get an unbuffered connection. */    unsigned int output_buffering;    /* Protected by inlock */    Octstr *inbuf;    long inbufpos;    /* start of unread data in inbuf */    int read_eof;     /* we encountered eof on read */    int io_error;   /* we encountered error on IO operation */    /* Protected by both locks when updating, so you need only one     * of the locks when reading. */    FDSet *registered;    conn_callback_t *callback;    void *callback_data;    /* Protected by inlock */    int listening_pollin;    /* Protected by outlock */    int listening_pollout;#ifdef HAVE_LIBSSL    SSL *ssl;    X509 *peer_certificate;#endif /* HAVE_LIBSSL */};static void unlocked_register_pollin(Connection *conn, int onoff);static void unlocked_register_pollout(Connection *conn, int onoff);/* There are a number of functions that play with POLLIN and POLLOUT flags. * The general rule is that we always want to poll for POLLIN except when * we have detected eof (which may be reported as eternal POLLIN), and * we want to poll for POLLOUT only if there's data waiting in the * output buffer.  If output buffering is set, we may not want to poll for * POLLOUT if there's not enough data waiting, which is why we have * unlocked_try_write. *//* Macros to get more information for debugging purposes */#define unlock_in(conn) unlock_in_real(conn, __FILE__, __LINE__, __func__)#define unlock_out(conn) unlock_out_real(conn, __FILE__, __LINE__, __func__)/* Lock a Connection's read direction, if the Connection is unclaimed */static void lock_in(Connection *conn){    gw_assert(conn != NULL);    if (conn->claimed)        gw_assert(gwthread_self() == conn->claiming_thread);    else        mutex_lock(conn->inlock);}/* Unlock a Connection's read direction, if the Connection is unclaimed */static void unlock_in_real(Connection *conn, char *file, int line, const char *func){    int ret;    gw_assert(conn != NULL);    if (!conn->claimed) {        if ((ret = mutex_unlock(conn->inlock)) != 0) {            panic(0, "%s:%ld: %s: Mutex unlock failed. " \		             "(Called from %s:%ld:%s.)", \			         __FILE__, (long) __LINE__, __func__, \			         file, (long) line, func);        }     }}/* Lock a Connection's write direction, if the Connection is unclaimed */static void lock_out(Connection *conn){    gw_assert(conn != NULL);    if (conn->claimed)        gw_assert(gwthread_self() == conn->claiming_thread);    else        mutex_lock(conn->outlock);}/* Unlock a Connection's write direction, if the Connection is unclaimed */static void unlock_out_real(Connection *conn, char *file, int line, const char *func){    int ret;    gw_assert(conn != NULL);    if (!conn->claimed) {        if ((ret = mutex_unlock(conn->outlock)) != 0) {            panic(0, "%s:%ld: %s: Mutex unlock failed. " \		             "(Called from %s:%ld:%s.)", \			         __FILE__, (long) __LINE__, __func__, \			         file, (long) line, func);        }     }}/* Return the number of bytes in the Connection's output buffer */static long unlocked_outbuf_len(Connection *conn){    return octstr_len(conn->outbuf) - conn->outbufpos;}/* Return the number of bytes in the Connection's input buffer */static long unlocked_inbuf_len(Connection *conn){    return octstr_len(conn->inbuf) - conn->inbufpos;}/* Send as much data as can be sent without blocking.  Return the number * of bytes written, or -1 in case of error. */static long unlocked_write(Connection *conn){    long ret = 0;#ifdef HAVE_LIBSSL    if (conn->ssl != NULL) {        if (octstr_len(conn->outbuf) - conn->outbufpos > 0)            ret = SSL_write(conn->ssl,                        octstr_get_cstr(conn->outbuf) + conn->outbufpos,                        octstr_len(conn->outbuf) - conn->outbufpos);        if (ret < 0) {            int SSL_error = SSL_get_error(conn->ssl, ret);            if (SSL_error == SSL_ERROR_WANT_READ || SSL_error == SSL_ERROR_WANT_WRITE) {                  ret = 0; /* no error */            } else {                error(errno, "SSL write failed: OpenSSL error %d: %s",                      SSL_error, ERR_error_string(SSL_error, NULL));                return -1;            }        }    } else#endif /* HAVE_LIBSSL */        ret = octstr_write_data(conn->outbuf, conn->fd, conn->outbufpos);    if (ret < 0) {        conn->io_error = 1;        return -1;    }    conn->outbufpos += ret;    /* Heuristic: Discard the already-written data if it's more than     * half of the total.  This should keep the buffer size small     * without wasting too many cycles on moving data around. */    if (conn->outbufpos > octstr_len(conn->outbuf) / 2) {        octstr_delete(conn->outbuf, 0, conn->outbufpos);        conn->outbufpos = 0;    }    if (conn->registered)        unlocked_register_pollout(conn, unlocked_outbuf_len(conn) > 0);    return ret;}/* Try to empty the output buffer without blocking.  Return 0 for success, * 1 if there is still data left in the buffer, and -1 for errors. */static int unlocked_try_write(Connection *conn){    long len;    len = unlocked_outbuf_len(conn);    if (len == 0)        return 0;    if (len < (long) conn->output_buffering)        return 1;    if (unlocked_write(conn) < 0)        return -1;    if (unlocked_outbuf_len(conn) > 0)        return 1;    return 0;}/* Read whatever data is currently available, up to an internal maximum. */static void unlocked_read(Connection *conn){    unsigned char buf[4096];    long len;    if (conn->inbufpos > 0) {        octstr_delete(conn->inbuf, 0, conn->inbufpos);        conn->inbufpos = 0;    }#ifdef HAVE_LIBSSL    if (conn->ssl != NULL) {        len = SSL_read(conn->ssl, buf, sizeof(buf));    } else#endif /* HAVE_LIBSSL */        len = read(conn->fd, buf, sizeof(buf));    if (len < 0) {        if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)            return;#ifdef HAVE_LIBSSL        if (conn->ssl) {            int SSL_error = SSL_get_error(conn->ssl, len);            if (SSL_error == SSL_ERROR_WANT_WRITE || SSL_error == SSL_ERROR_WANT_READ)                return; /* no error */            error(errno, "SSL read failed: OpenSSL error %d: %s",                      SSL_error, ERR_error_string(SSL_error, NULL));        }        else#endif /* HAVE_LIBSSL */            error(errno, "Error reading from fd %d:", conn->fd);        conn->io_error = 1;        if (conn->registered)            unlocked_register_pollin(conn, 0);        return;    } else if (len == 0) {        conn->read_eof = 1;        if (conn->registered)            unlocked_register_pollin(conn, 0);    } else {        octstr_append_data(conn->inbuf, buf, len);    }}/* Cut "length" octets from the input buffer and return them as an Octstr */static Octstr *unlocked_get(Connection *conn, long length){    Octstr *result = NULL;    gw_assert(unlocked_inbuf_len(conn) >= length);    result = octstr_copy(conn->inbuf, conn->inbufpos, length);    conn->inbufpos += length;    return result;}/* Tell the fdset whether we are interested in POLLIN events, but only * if the status changed.  (Calling fdset_listen can be expensive if * it requires synchronization with the polling thread.) * We must already have the inlock. */static void unlocked_register_pollin(Connection *conn, int onoff){    gw_assert(conn->registered);    if (onoff == 1 && !conn->listening_pollin) {        /* Turn it on */        conn->listening_pollin = 1;        fdset_listen(conn->registered, conn->fd, POLLIN, POLLIN);    } else if (onoff == 0 && conn->listening_pollin) {        /* Turn it off */        conn->listening_pollin = 0;        fdset_listen(conn->registered, conn->fd, POLLIN, 0);    }}/* Tell the fdset whether we are interested in POLLOUT events, but only * if the status changed.  (Calling fdset_listen can be expensive if * it requires synchronization with the polling thread.) * We must already have the outlock. */static void unlocked_register_pollout(Connection *conn, int onoff){    gw_assert(conn->registered);    if (onoff == 1 && !conn->listening_pollout) {        /* Turn it on */        conn->listening_pollout = 1;        fdset_listen(conn->registered, conn->fd, POLLOUT, POLLOUT);    } else if (onoff == 0 && conn->listening_pollout) {        /* Turn it off */        conn->listening_pollout = 0;        fdset_listen(conn->registered, conn->fd, POLLOUT, 0);    }}#ifdef HAVE_LIBSSLConnection *conn_open_ssl(Octstr *host, int port, Octstr *certkeyfile,			  Octstr *our_host){    Connection *ret;    /* open the TCP connection */    if (!(ret = conn_open_tcp(host, port, our_host))) {        return NULL;    }    ret->ssl = SSL_new(global_ssl_context);    /*     * The current thread's error queue must be empty before     * the TLS/SSL I/O operation is attempted, or SSL_get_error()     * will not work reliably.     */    ERR_clear_error();    if (certkeyfile != NULL) {        SSL_use_certificate_file(ret->ssl, octstr_get_cstr(certkeyfile),                                 SSL_FILETYPE_PEM);        SSL_use_PrivateKey_file(ret->ssl, octstr_get_cstr(certkeyfile),                                SSL_FILETYPE_PEM);        if (SSL_check_private_key(ret->ssl) != 1) {            error(0, "conn_open_ssl: private key isn't consistent with the "                     "certificate from file %s (or failed reading the file)",                  octstr_get_cstr(certkeyfile));            goto error;        }    }    /* SSL_set_fd can fail, so check it */    if (SSL_set_fd(ret->ssl, ret->fd) == 0) {        /* SSL_set_fd failed, log error */        error(errno, "SSL: OpenSSL: %.256s", ERR_error_string(ERR_get_error(), NULL));        goto error;    }    /*     * make sure the socket is non-blocking while we do SSL_connect     */    if (socket_set_blocking(ret->fd, 0) < 0) {        goto error;    }    BIO_set_nbio(SSL_get_rbio(ret->ssl), 1);    BIO_set_nbio(SSL_get_wbio(ret->ssl), 1);    SSL_set_connect_state(ret->ssl);    return ret;error:    conn_destroy(ret);    return NULL;}#endif /* HAVE_LIBSSL */Connection *conn_open_tcp(Octstr *host, int port, Octstr *our_host){    return conn_open_tcp_with_port(host, port, 0, our_host);}Connection *conn_open_tcp_nb(Octstr *host, int port, Octstr *our_host){

⌨️ 快捷键说明

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