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 + -
显示快捷键?