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