📄 ne_socket.c
字号:
/* socket handling routines Copyright (C) 1998-2004, Joe Orton <joe@manyfish.co.uk>, Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA*//* portions were originally under GPL in Mutt, http://www.mutt.org/ Relicensed under LGPL for neon, http://www.webdav.org/neon/*/#include "config.h"#ifdef __hpux/* pick up hstrerror */#define _XOPEN_SOURCE_EXTENDED 1/* don't use the broken getaddrinfo shipped in HP-UX 11.11 */#ifdef USE_GETADDRINFO#undef USE_GETADDRINFO#endif#endif#include <sys/types.h>#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#endif#include <sys/stat.h>#ifdef HAVE_SYS_SELECT_H#include <sys/select.h>#endif#ifdef HAVE_SYS_SOCKET_H#include <sys/socket.h>#endif#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#ifdef HAVE_NETINET_TCP_H#include <netinet/tcp.h>#endif#ifdef HAVE_ARPA_INET_H#include <arpa/inet.h>#endif#ifdef HAVE_NETDB_H#include <netdb.h>#endif#ifdef WIN32#include <winsock2.h>#include <stddef.h>#endif#if defined(NEON_SSL) && defined(HAVE_LIMITS_H)#include <limits.h> /* for INT_MAX */#endif#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_STRINGS_H#include <strings.h>#endif #ifdef HAVE_UNISTD_H#include <unistd.h>#endif#ifdef HAVE_SIGNAL_H#include <signal.h>#endif#ifdef HAVE_ERRNO_H#include <errno.h>#endif#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_SOCKS_H#include <socks.h>#endif#ifdef NEON_SSL#include <openssl/ssl.h>#include <openssl/err.h>#include <openssl/pkcs12.h> /* for PKCS12_PBE_add */#include <openssl/rand.h>#include "ne_privssl.h"#endif#include "ne_i18n.h"#include "ne_utils.h"#include "ne_string.h"#define NE_INET_ADDR_DEFINED/* A slightly ugly hack: change the ne_inet_addr definition to be the * real address type used. The API only exposes ne_inet_addr as a * pointer to an opaque object, so this should be well-defined * behaviour. It avoids the hassle of a real wrapper ne_inet_addr * structure, or losing type-safety by using void *. */#ifdef USE_GETADDRINFOtypedef struct addrinfo ne_inet_addr;/* To avoid doing AAAA queries unless absolutely necessary, either use * AI_ADDRCONFIG where available, or a run-time check for working IPv6 * support; the latter is only known to work on Linux. */#if !defined(USE_GAI_ADDRCONFIG) && defined(__linux__)#define USE_CHECK_IPV6#endif#elsetypedef struct in_addr ne_inet_addr;#endif#include "ne_socket.h"#include "ne_alloc.h"#if defined(__BEOS__) && !defined(BONE_VERSION)/* pre-BONE */#define ne_write(a,b,c) send(a,b,c,0)#define ne_read(a,b,c) recv(a,b,c,0)#define ne_close(s) closesocket(s)#define ne_errno errno#elif defined(WIN32)#define ne_write(a,b,c) send(a,b,c,0)#define ne_read(a,b,c) recv(a,b,c,0)#define ne_close(s) closesocket(s)#define ne_errno WSAGetLastError()#else /* really Unix! */#define ne_write(a,b,c) write(a,b,c)#define ne_read(a,b,c) read(a,b,c)#define ne_close(s) close(s)#define ne_errno errno#endif#ifdef WIN32#define NE_ISRESET(e) ((e) == WSAECONNABORTED || (e) == WSAETIMEDOUT || \ (e) == WSAECONNRESET || (e) == WSAENETRESET)#define NE_ISCLOSED(e) ((e) == WSAESHUTDOWN || (e) == WSAENOTCONN)#define NE_ISINTR(e) (0)#else /* Unix */#define NE_ISRESET(e) ((e) == ECONNRESET)#define NE_ISCLOSED(e) ((e) == EPIPE)#define NE_ISINTR(e) ((e) == EINTR)#endif/* Socket read timeout */#define SOCKET_READ_TIMEOUT 120/* Critical I/O functions on a socket: useful abstraction for easily * handling SSL I/O alongside raw socket I/O. */struct iofns { /* Read up to 'len' bytes into 'buf' from socket. Return <0 on * error or EOF, or >0; number of bytes read. */ ssize_t (*read)(ne_socket *s, char *buf, size_t len); /* Write exactly 'len' bytes from 'buf' to socket. Return zero on * success, <0 on error. */ ssize_t (*write)(ne_socket *s, const char *buf, size_t len); /* Wait up to 'n' seconds for socket to become readable. Returns * 0 when readable, otherwise NE_SOCK_TIMEOUT or NE_SOCK_ERROR. */ int (*readable)(ne_socket *s, int n);};struct ne_socket_s { int fd; char error[200]; void *progress_ud; int rdtimeout; /* read timeout. */ const struct iofns *ops;#ifdef NEON_SSL ne_ssl_socket ssl;#endif /* The read buffer: ->buffer stores byte which have been read; as * these are consumed and passed back to the caller, bufpos * advances through ->buffer. ->bufavail gives the number of * bytes which remain to be consumed in ->buffer (from ->bufpos), * and is hence always <= RDBUFSIZ. */#define RDBUFSIZ 4096 char buffer[RDBUFSIZ]; char *bufpos; size_t bufavail;};/* ne_sock_addr represents an Internet address. */struct ne_sock_addr_s {#ifdef USE_GETADDRINFO struct addrinfo *result, *cursor;#else struct in_addr *addrs; size_t cursor, count;#endif int errnum;};/* set_error: set socket error string to 'str'. */#define set_error(s, str) ne_strnzcpy((s)->error, (str), sizeof (s)->error)/* set_strerror: set socket error to system error string for 'errnum' */#ifdef WIN32/* Print system error message to given buffer. */static void print_error(int errnum, char *buffer, size_t buflen){ if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) errnum, 0, buffer, buflen, NULL) == 0) ne_snprintf(buffer, buflen, "Socket error %d", errnum);}#define set_strerror(s, e) print_error((e), (s)->error, sizeof (s)->error)#else /* not WIN32 */#define set_strerror(s, e) ne_strerror((e), (s)->error, sizeof (s)->error)#endif#ifdef NEON_SSL/* Initialize SSL library. */static void init_ssl(void){ SSL_load_error_strings(); SSL_library_init(); PKCS12_PBE_add(); /* ### not sure why this is needed. */}/* Seed the SSL PRNG, if necessary; returns non-zero on failure. */static int seed_ssl_prng(void){ /* Check whether the PRNG has already been seeded. */ if (RAND_status() == 1) return 0;#ifdef EGD_PATH NE_DEBUG(NE_DBG_SOCKET, "Seeding PRNG from " EGD_PATH "...\n"); if (RAND_egd(EGD_PATH) != -1) return 0;#elif defined(ENABLE_EGD) { static const char *paths[] = { "/var/run/egd-pool", "/dev/egd-pool", "/etc/egd-pool", "/etc/entropy" }; size_t n; for (n = 0; n < sizeof(paths) / sizeof(char *); n++) { NE_DEBUG(NE_DBG_SOCKET, "Seeding PRNG from %s...\n", paths[n]); if (RAND_egd(paths[n]) != -1) return 0; } }#endif /* EGD_PATH */ NE_DEBUG(NE_DBG_SOCKET, "No entropy source found; could not seed PRNG.\n"); return -1;}#endif /* NEON_SSL */#ifdef USE_CHECK_IPV6static int ipv6_disabled = 0;/* On Linux kernels, IPv6 is typically built as a loadable module, and * socket(AF_INET6, ...) will fail if this module is not loaded, so * the slow AAAA lookups can be avoided for this common case. */static void init_ipv6(void){ int fd = socket(AF_INET6, SOCK_STREAM, 0); if (fd < 0) ipv6_disabled = 1; else close(fd);}#else#define ipv6_disabled (0)#endifstatic int init_result = 0;int ne_sock_init(void){#ifdef WIN32 WORD wVersionRequested; WSADATA wsaData; int err;#endif if (init_result > 0) return 0; else if (init_result < 0) return -1;#ifdef WIN32 wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { init_result = -1; return -1; }#endif#ifdef NEON_SOCKS SOCKSinit("neon");#endif#if defined(HAVE_SIGNAL) && defined(SIGPIPE) (void) signal(SIGPIPE, SIG_IGN);#endif#ifdef USE_CHECK_IPV6 init_ipv6();#endif#ifdef NEON_SSL init_ssl();#endif init_result = 1; return 0;}void ne_sock_exit(void){#ifdef WIN32 WSACleanup();#endif init_result = 0;}int ne_sock_block(ne_socket *sock, int n){ if (sock->bufavail) return 0; return sock->ops->readable(sock, n);}/* Cast address object AD to type 'sockaddr_TY' */ #define SACAST(ty, ad) ((struct sockaddr_##ty *)(ad))#define SOCK_ERR(x) do { ssize_t _sock_err = (x); \if (_sock_err < 0) return _sock_err; } while(0)ssize_t ne_sock_read(ne_socket *sock, char *buffer, size_t buflen){ ssize_t bytes;#if 0 NE_DEBUG(NE_DBG_SOCKET, "buf: at %d, %d avail [%s]\n", sock->bufpos - sock->buffer, sock->bufavail, sock->bufpos);#endif if (sock->bufavail > 0) { /* Deliver buffered data. */ if (buflen > sock->bufavail) buflen = sock->bufavail; memcpy(buffer, sock->bufpos, buflen); sock->bufpos += buflen; sock->bufavail -= buflen; return buflen; } else if (buflen >= sizeof sock->buffer) { /* No need for read buffer. */ return sock->ops->read(sock, buffer, buflen); } else { /* Fill read buffer. */ bytes = sock->ops->read(sock, sock->buffer, sizeof sock->buffer); if (bytes <= 0) return bytes; if (buflen > (size_t)bytes) buflen = bytes; memcpy(buffer, sock->buffer, buflen); sock->bufpos = sock->buffer + buflen; sock->bufavail = bytes - buflen; return buflen; }}ssize_t ne_sock_peek(ne_socket *sock, char *buffer, size_t buflen){ ssize_t bytes; if (sock->bufavail) { /* just return buffered data. */ bytes = sock->bufavail; } else { /* fill the buffer. */ bytes = sock->ops->read(sock, sock->buffer, sizeof sock->buffer); if (bytes <= 0) return bytes; sock->bufpos = sock->buffer; sock->bufavail = bytes; } if (buflen > (size_t)bytes) buflen = bytes; memcpy(buffer, sock->bufpos, buflen); return buflen;}/* Await data on raw fd in socket. */static int readable_raw(ne_socket *sock, int secs){ int fdno = sock->fd, ret; fd_set rdfds; struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL); /* Init the fd set */ FD_ZERO(&rdfds); do { FD_SET(fdno, &rdfds); if (tvp) { tvp->tv_sec = secs; tvp->tv_usec = 0; } ret = select(fdno + 1, &rdfds, NULL, NULL, tvp); } while (ret < 0 && NE_ISINTR(ne_errno)); if (ret < 0) { set_strerror(sock, ne_errno); return NE_SOCK_ERROR; } return (ret == 0) ? NE_SOCK_TIMEOUT : 0;}static ssize_t read_raw(ne_socket *sock, char *buffer, size_t len){ ssize_t ret; ret = readable_raw(sock, sock->rdtimeout); if (ret) return ret; do { ret = ne_read(sock->fd, buffer, len); } while (ret == -1 && NE_ISINTR(ne_errno)); if (ret == 0) { set_error(sock, _("Connection closed")); ret = NE_SOCK_CLOSED; } else if (ret < 0) { int errnum = ne_errno; ret = NE_ISRESET(errnum) ? NE_SOCK_RESET : NE_SOCK_ERROR; set_strerror(sock, errnum); } return ret;}#define MAP_ERR(e) (NE_ISCLOSED(e) ? NE_SOCK_CLOSED : \ (NE_ISRESET(e) ? NE_SOCK_RESET : NE_SOCK_ERROR))static ssize_t write_raw(ne_socket *sock, const char *data, size_t length) { ssize_t wrote; do { wrote = ne_write(sock->fd, data, length); if (wrote > 0) { data += wrote; length -= wrote; } } while ((wrote > 0 || NE_ISINTR(ne_errno)) && length > 0); if (wrote < 0) { int errnum = ne_errno; set_strerror(sock, errnum); return MAP_ERR(errnum); } return 0;}static const struct iofns iofns_raw = { read_raw, write_raw, readable_raw };#ifdef NEON_SSL/* OpenSSL I/O function implementations. */static int readable_ossl(ne_socket *sock, int secs){ /* If there is buffered SSL data, then don't block on the socket. * FIXME: make sure that SSL_read *really* won't block if * SSL_pending returns non-zero. Possibly need to do * SSL_read(ssl, buf, SSL_pending(ssl)) */ if (SSL_pending(sock->ssl.ssl)) return 0; return readable_raw(sock, secs);}/* SSL error handling, according to SSL_get_error(3). */static int error_ossl(ne_socket *sock, int sret){ int err = SSL_get_error(sock->ssl.ssl, sret), ret = NE_SOCK_ERROR; switch (err) { case SSL_ERROR_ZERO_RETURN: ret = NE_SOCK_CLOSED; set_error(sock, _("Connection closed")); break; case SSL_ERROR_SYSCALL: err = ERR_get_error(); if (err == 0) { if (sret == 0) { /* EOF without close_notify, possible truncation */ set_error(sock, _("Secure connection truncated")); ret = NE_SOCK_TRUNC; } else { /* Other socket error. */ err = ne_errno; set_strerror(sock, err); ret = MAP_ERR(err); } } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -