esock.c

来自「OTP是开放电信平台的简称」· C语言 代码 · 共 1,902 行 · 第 1/4 页

C
1,902
字号
/* ``The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved via the world wide web at http://www.erlang.org/. *  * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. *  * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' *  *     $Id$ *//* * Purpose:  Implementation of Secure Socket Layer (SSL). * * This is an "SSL proxy" for Erlang in the form of a port * program.  * * The implementation has borrowed somewhat from the original * implementation of `socket' by Claes Wikstr鰉, and the former * implementation of `ssl_socket' by Helen Ariyan. * * All I/O is now non-blocking.  * * When a connection (cp) is in the state JOINED we have the following * picture: * *            proxy->fd                          fd *               |                               | *  proxy->eof   |  -------->  wq  ----------->  |   bp  *               |                               | *  Erlang       |                               |   SSL *               |                               | *  proxy->bp    |  <------ proxy->wq ---------  |   eof *               |                               | *  * We read from Erlang (proxy->fd) and write to SSL (fd); and read from  * SSL (fd) and write to Erlang (proxy->fd).   * * The variables bp (broken pipe) and eof (end of file) take the * values 0 and 1. * * What has been read and cannot be immediately written is put in a * write queue (wq). A wq is emptied before reads are continued, which * means that at most one chunk that is read can be in a wq. * * The proxy-to-ssl part of a cp is valid iff  * *         !bp && (wq.len > 0 || !proxy->eof). * * The ssl-to-proxy part of a cp is valid iff  * *         !proxy->bp && (proxy->wq.len > 0 || !eof).  * * The connection is valid if any of the above parts are valid, i.e. * invalid if both parts are invalid. * * Every SELECT_TIMEOUT second we try to write to those file * descriptors that have non-empty wq's (the only way to detect that a * far end has gone away is to write to it). * * STATE TRANSITIONS * * Below (*) means that the corresponding file descriptor is published * (i.e. kwown outside this port program) when the state is entered, * and thus cannot be closed without synchronization with the * ssl_server. * * Listen: * * STATE_NONE ---> (*) PASSIVE_LISTENING <---> ACTIVE_LISTENING * * Accept: * * STATE_NONE ---> SSL_ACCEPT ---> (*) CONNECTED ---> JOINED --->  *  ---> SSL_SHUTDOWN ---> DEFUNCT * * Connect: * * STATE_NONE ---> (*) WAIT_CONNECT ---> SSL_CONNECT ---> CONNECTED --->  *  ---> JOINED ---> SSL_SHUTDOWN ---> DEFUNCT *  * In states where file descriptors has been published, and where * something goes wrong, the state of the connection is set to * DEFUNCT. A connection in such a state can only be closed by a CLOSE * message from Erlang (a reception of such a message is registered in * cp->closed). The possible states are: WAIT_CONNECT, SSL_CONNECT, * CONNECTED, JOINED, and SSL_SHUTDOWN. * * A connection in state SSL_ACCEPT can be closed and removed without * synchronization. * */#ifdef HAVE_CONFIG_H#include "config.h"#endif#ifdef __WIN32__#include "esock_winsock.h"#endif#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <time.h>#include <ctype.h>#include <sys/types.h>#include <errno.h>#ifdef __WIN32__#include <process.h>#else#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/tcp.h>#include <sys/time.h>#include <netdb.h>#include <arpa/inet.h>#include <fcntl.h>#endif#ifndef INADDR_NONE#define INADDR_NONE 0xffffffff  /* Should be in <netinet/in.h>.  */#endif#include "esock.h"#include "debuglog.h"#include "esock_utils.h"#include "esock_ssl.h"#include "esock_osio.h"#include "esock_posix_str.h"#include "esock_poll.h"#define MAJOR_VERSION   2#define MINOR_VERSION   0#define MAXREPLYBUF	256#define RWBUFLEN	(32*1024)#define IS_CLIENT       0#define IS_SERVER       1#define SELECT_TIMEOUT  2	/* seconds */#define psx_errstr()	esock_posix_str(sock_errno())#define ssl_errstr()	esock_ssl_errstr#define PROXY_TO_SSL_VALID(cp) (!(cp)->bp && \				((cp)->wq.len > 0 || !(cp)->proxy->eof))#define SSL_TO_PROXY_VALID(cp) (!(cp)->proxy->bp && \				((cp)->proxy->wq.len > 0 || !(cp)->eof))#define JOINED_STATE_INVALID(cp) (!(PROXY_TO_SSL_VALID(cp)) && \				!(SSL_TO_PROXY_VALID(cp)))static int loop(void);static int set_poll_conns(Connection *cp, EsockPoll *ep, int verbose);static Connection *next_polled_conn(Connection *cp, Connection **cpnext,				    EsockPoll *ep, int set_wq_fds);static void leave_joined_state(Connection *cp);static void do_shutdown(Connection *cp);static void close_and_remove_connection(Connection *cp);static int reply(int cmd, char *fmt, ...);static int input(char *fmt, ...);static int put_pars(unsigned char *buf, char *fmt, va_list args);static int get_pars(unsigned char *buf, char *fmt, va_list args);static FD do_connect(char *lipstring, int lport, char *fipstring, int fport);static FD do_listen(char *ipstring, int lport, int backlog, int *aport);static FD do_accept(FD listensock, struct sockaddr *saddr, int *len);static void print_connections(void);static void dump_connections(void);static int check_num_sock_fds(FD fd); static void safe_close(FD fd);static Connection *new_connection(int state, FD fd);static Connection *get_connection(FD fd);static void remove_connection(Connection *conn);static Proxy *get_proxy_by_peerport(int port);static Proxy *new_proxy(FD fd);static void remove_proxy(Proxy *proxy);static void ensure_write_queue(WriteQueue *wq, int size);static void clean_up(void);static Connection  *connections = NULL;static int num_sock_fds;	/* On UNIX all file descriptors */static Proxy *proxies = NULL;static int proxy_listensock = INVALID_FD;static int proxy_listenport = 0;static int proxy_backlog = 128;static int proxysock_last_err = 0;static int proxysock_err_cnt = 0;static char rwbuf[RWBUFLEN];static unsigned char *ebuf = NULL; /* Set by read_ctrl() */static char *connstr[] = {    "STATE_NONE",     "ACTIVE_LISTENING",    "PASSIVE_LISTENING",    "CONNECTED",    "WAIT_CONNECT",    "SSL_CONNECT",    "SSL_ACCEPT",    "TRANSPORT_ACCEPT",    "JOINED",    "SSL_SHUTDOWN",    "DEFUNCT"};static char *originstr[] = {    "listen",    "accept",    "connect"};int main(int argc, char **argv) {    char *logfile = NULL;    int i;    esock_version *vsn;    char *ciphers; #ifdef __WIN32__    int pid;    WORD version;    WSADATA wsa_data;    set_binary_mode();    setvbuf(stderr, NULL, _IONBF, 0);    /* Two sockets for the stdin socket pipe (local thread). */    num_sock_fds = 2;		#else    pid_t pid;    num_sock_fds = 3;		/* 0, 1, 2 */#endif    pid = getpid();    i = 1;    while (i < argc) {	if (strcmp(argv[i], "-d") == 0) {	    debug = 1;	    i++;	} else if (strcmp(argv[i], "-dm") == 0) {	    debugmsg = 1;	    i++;	} else if (strcmp(argv[i], "-pp") == 0) {	    i++;	    proxy_listenport = atoi(argv[i]);	    i++;	} else if (strcmp(argv[i], "-pb") == 0) {	    i++;	    proxy_backlog = atoi(argv[i]);	    i++;	} else if (strcmp(argv[i], "-pv") == 0) {	    i++;	    protocol_version = atoi(argv[i]);	    i++;	} else if (strcmp(argv[i], "-dd") == 0) {	    i++;	    logfile = esock_malloc(strlen(argv[i]) + 64);	    sprintf(logfile, "%s/ssl_esock.%d.log", argv[i], (int)pid);	    i++;	} else if (strcmp(argv[i], "-ersa") == 0) {	    ephemeral_rsa = 1;	    i++;	} else if (strcmp(argv[i], "-edh") == 0) {	    ephemeral_dh = 1;	    i++;	}    }    if (debug || debugmsg) {	DEBUGF(("Starting ssl_esock\n"));	if (logfile) {	    open_ssllog(logfile);#ifndef __WIN32__	    num_sock_fds++;#endif	}	atexit(close_ssllog);	DEBUGF(("pid = %d\n", getpid()));    }    if (esock_ssl_init() < 0) {	fprintf(stderr, "esock: Could not do esock_ssl_init\n");	exit(EXIT_FAILURE);    }    atexit(esock_ssl_finish);#ifdef __WIN32__    /* Start Windows' sockets */    version = MAKEWORD(MAJOR_VERSION, MINOR_VERSION);    if (WSAStartup(version, &wsa_data) != 0) {	fprintf(stderr, "esock: Could not start up Windows' sockets\n");	exit(EXIT_FAILURE);    }    atexit((void (*)(void))WSACleanup);    if (LOBYTE(wsa_data.wVersion) < MAJOR_VERSION ||	(LOBYTE(wsa_data.wVersion) == MAJOR_VERSION && 	 HIBYTE(wsa_data.wVersion) < MINOR_VERSION)) {	fprintf(stderr, "esock: Windows socket version error. "		"Requested version:"		"%d.%d, version found: %d.%d\n", MAJOR_VERSION, 		MINOR_VERSION, LOBYTE(wsa_data.wVersion), 		HIBYTE(wsa_data.wVersion));	exit(EXIT_FAILURE);    }    DEBUGF(("Using Windows socket version: %d.%d\n", 	   LOBYTE(wsa_data.wVersion), HIBYTE(wsa_data.wVersion)));    DEBUGF(("Maximum number of sockets available: %d\n", 	    wsa_data.iMaxSockets));     if (esock_osio_init() < 0) {	fprintf(stderr, "esock: Could not init osio\n");	exit(EXIT_FAILURE);    }    atexit(esock_osio_finish);#endif    /* Create the local proxy listen socket and set it to non-blocking */    proxy_listensock = do_listen("127.0.0.1", proxy_listenport, 				 proxy_backlog, &proxy_listenport);    if (proxy_listensock == INVALID_FD) {	fprintf(stderr, "esock: Cannot create local listen socket\n");	exit(EXIT_FAILURE);    }    SET_NONBLOCKING(proxy_listensock);    DEBUGF(("Local proxy listen socket: fd = %d, port = %d\n", 	   proxy_listensock, proxy_listenport));    vsn = esock_ssl_version();    ciphers = esock_ssl_ciphers();    /* Report: port number of the local proxy listen socket, the native     * os pid, the compile and lib versions of the ssl library, and      * the list of available ciphers. */    reply(ESOCK_PROXY_PORT_REP, "24sss", proxy_listenport, (int)pid, 	  vsn->compile_version, vsn->lib_version, ciphers);    atexit(clean_up);    loop();    if (logfile) 	esock_free(logfile);    exit(EXIT_SUCCESS);}/* * Local functions * */static int loop(void){    EsockPoll pollfd;    FD fd, msgsock, listensock, connectsock, proxysock;    int cc, wc, fport, lport, pport, length, backlog, intref, op;    int value;    char *lipstring, *fipstring;    char *flags;    char *protocol_vsn, *cipher;    unsigned char *cert, *bin;    int certlen, binlen;    struct sockaddr_in iserv_addr;    int sret = 1;    Connection *cp, *cpnext, *newcp;    Proxy *pp;    time_t last_time = 0, now = 0;    int set_wq_fds;    esock_poll_init(&pollfd);    while(1) {	esock_poll_zero(&pollfd);	esock_poll_fd_set_read(&pollfd, proxy_listensock);	esock_poll_fd_set_read(&pollfd, local_read_fd);	set_wq_fds = 0;	if (sret)		/* sret == 1 the first time. */	    DEBUGF(("==========LOOP=============\n"));	cc = set_poll_conns(connections, &pollfd, sret) + 1;	if (sret) {	    print_connections();	    DEBUGF(("Before poll/select: %d descriptor%s (total %d)\n",		    cc, (cc == 1) ? "" : "s", num_sock_fds));	}	sret = esock_poll(&pollfd, SELECT_TIMEOUT);	if (sret < 0) {	    DEBUGF(("select/poll error: %s\n", psx_errstr()));	    continue;	}		time(&now);	if (now >= last_time + SELECT_TIMEOUT) {	    set_wq_fds = 1;	    last_time = now;	}	/*	 * First accept as many connections as possible on the	 * proxy listen socket. We record the peer port, which	 * is later used as a reference for joining a proxy 	 * connection with a network connection.	 */	if (esock_poll_fd_isset_read(&pollfd, proxy_listensock)) {	    while (1) {		length = sizeof(iserv_addr);		proxysock = do_accept(proxy_listensock, 				      (struct sockaddr *)&iserv_addr, 				      (int*)&length);		if(proxysock == INVALID_FD) {		    if (sock_errno() != ERRNO_BLOCK) {			/* We can here for example get the error			 * EMFILE, i.e. no more file descriptors			 * available, but we do not have any specific			 * connection to report the error to.  We			 * increment the error counter and saves the			 * last err.  			 */			proxysock_err_cnt++;			proxysock_last_err = sock_errno();			DEBUGF(("accept error (proxy_listensock): %s\n", 				psx_errstr()));		    }		    break;		} else {		    /* Get peer port number *//* 		    length = sizeof(iserv_addr); *//* 		    if (getpeername(proxysock, (struct sockaddr *)&iserv_addr,  *//* 				    &length) < 0) { *//* 			DEBUGF(("Can't get peername of proxy socket")); *//* 			safe_close(proxysock); *//* 		    } else { */			/* Add to pending proxy connections */			SET_NONBLOCKING(proxysock);			pp = new_proxy(proxysock);			pp->peer_port = ntohs(iserv_addr.sin_port);			DEBUGF(("-----------------------------------\n"));			DEBUGF(("[PROXY_LISTEN_SOCK] conn accepted: "				"proxyfd = %d, "			       "peer port = %d\n", proxysock, pp->peer_port));/* 		    } */		}	    }	}	/* 	 * Read control messages from Erlang	 */	if (esock_poll_fd_isset_read(&pollfd, local_read_fd)) {  	    cc = read_ctrl(&ebuf);	    if ( cc < 0 ) {		DEBUGF(("Read loop -1 or 0\n"));		return -1;	    } else if (cc == 0) {  /* not eof  */		DEBUGF(("GOT empty string \n"));	    } else {		switch((int)*ebuf) {		case ESOCK_SET_SEED_CMD:		    /* 		     * ebuf = {cmd(1), binary(N) }		     */		    input("b", &binlen, &bin);		    DEBUGF(("[SET_SEED_CMD]\n"));		    esock_ssl_seed(bin, binlen);		    /* no reply */

⌨️ 快捷键说明

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