📄 client.c
字号:
/*
* stunnel Universal SSL tunnel
* Copyright (c) 1998-2004 Michal Trojnara <Michal.Trojnara@mirt.net>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* In addition, as a special exception, Michal Trojnara gives
* permission to link the code of this program with the OpenSSL
* library (or with modified versions of OpenSSL that use the same
* license as OpenSSL), and distribute linked combinations including
* the two. You must obey the GNU General Public License in all
* respects for all of the code used other than OpenSSL. If you modify
* this file, you may extend this exception to your version of the
* file, but you are not obligated to do so. If you do not wish to
* do so, delete this exception statement from your version.
*/
/* Undefine if you have problems with make_sockets() */
#define INET_SOCKET_PAIR
#include "common.h"
#include "prototypes.h"
#ifndef SHUT_RD
#define SHUT_RD 0
#endif
#ifndef SHUT_WR
#define SHUT_WR 1
#endif
#ifndef SHUT_RDWR
#define SHUT_RDWR 2
#endif
/* TCP wrapper */
#ifdef USE_LIBWRAP
#include <tcpd.h>
int allow_severity=LOG_NOTICE;
int deny_severity=LOG_WARNING;
#endif
#if SSLEAY_VERSION_NUMBER >= 0x0922
static unsigned char *sid_ctx=(unsigned char *)"stunnel SID";
/* const allowed here */
#endif
extern SSL_CTX *ctx; /* global SSL context defined in ssl.c */
static int do_client(CLI *);
static int init_local(CLI *);
static int init_remote(CLI *);
static int init_ssl(CLI *);
static int transfer(CLI *);
static void cleanup(CLI *, int);
static void print_cipher(CLI *);
static int auth_libwrap(CLI *);
static int auth_user(CLI *);
static int connect_local(CLI *c);
#ifndef USE_WIN32
static int make_sockets(int [2]);
#endif
static int connect_remote(CLI *c);
static void reset(int, char *);
int max_clients;
#ifndef USE_WIN32
int max_fds;
#endif
/* Allocate local data structure for the new thread */
void *alloc_client_session(LOCAL_OPTIONS *opt, int rfd, int wfd) {
CLI *c;
c=calloc(1, sizeof(CLI));
if(!c) {
log(LOG_ERR, "Memory allocation failed");
return NULL;
}
c->opt=opt;
c->local_rfd.fd=rfd;
c->local_wfd.fd=wfd;
return c;
}
void *client(void *arg) {
CLI *c=arg;
#ifdef DEBUG_STACK_SIZE
stack_info(1); /* initialize */
#endif
log(LOG_DEBUG, "%s started", c->opt->servname);
#ifndef USE_WIN32
if(c->opt->option.remote && c->opt->option.program)
c->local_rfd.fd=c->local_wfd.fd=connect_local(c);
/* connect and exec options specified together */
/* spawn local program instead of stdio */
#endif
c->remote_fd.fd=-1;
c->ssl=NULL;
cleanup(c, do_client(c));
#ifdef USE_FORK
if(!c->opt->option.remote) /* 'exec' specified */
exec_status(); /* null SIGCHLD handler was used */
#else
enter_critical_section(CRIT_CLIENTS); /* for multi-cpu machines */
log(LOG_DEBUG, "%s finished (%d left)", c->opt->servname,
--num_clients);
leave_critical_section(CRIT_CLIENTS);
#endif
free(c);
#ifdef DEBUG_STACK_SIZE
stack_info(0); /* display computed value */
#endif
return NULL;
}
static int do_client(CLI *c) {
int result;
if(init_local(c))
return -1;
if(!options.option.client && !c->opt->protocol) {
/* Server mode and no protocol negotiation needed */
if(init_ssl(c))
return -1;
if(init_remote(c))
return -1;
} else {
if(init_remote(c))
return -1;
if(negotiate(c)<0) {
log(LOG_ERR, "Protocol negotiations failed");
return -1;
}
if(init_ssl(c))
return -1;
}
result=transfer(c);
log(LOG_NOTICE,
"Connection %s: %d bytes sent to SSL, %d bytes sent to socket",
result ? "reset" : "closed", c->ssl_bytes, c->sock_bytes);
return result;
}
static int init_local(CLI *c) {
int addrlen;
addrlen=sizeof(c->addr);
if(getpeername(c->local_rfd.fd, (struct sockaddr *)&c->addr, &addrlen)<0) {
strcpy(c->accepting_address, "NOT A SOCKET");
c->local_rfd.is_socket=0;
c->local_wfd.is_socket=0; /* TODO: It's not always true */
#ifdef USE_WIN32
if(get_last_socket_error()!=ENOTSOCK) {
#else
if(c->opt->option.transparent || get_last_socket_error()!=ENOTSOCK) {
#endif
sockerror("getpeerbyname");
return -1;
}
/* Ignore ENOTSOCK error so 'local' doesn't have to be a socket */
} else {
safe_ntoa(c->accepting_address, c->addr.sin_addr);
c->local_rfd.is_socket=1;
c->local_wfd.is_socket=1; /* TODO: It's not always true */
/* It's a socket: lets setup options */
if(set_socket_options(c->local_rfd.fd, 1)<0)
return -1;
if(auth_libwrap(c)<0)
return -1;
if(auth_user(c)<0) {
log(LOG_WARNING, "Connection from %s:%d REFUSED by IDENT",
c->accepting_address, ntohs(c->addr.sin_port));
return -1;
}
log(LOG_NOTICE, "%s connected from %s:%d", c->opt->servname,
c->accepting_address, ntohs(c->addr.sin_port));
}
return 0; /* OK */
}
static int init_remote(CLI *c) {
int fd;
/* create connection to host/service */
if(c->opt->local_ip)
c->bind_ip=*c->opt->local_ip;
#ifndef USE_WIN32
else if(c->opt->option.transparent)
c->bind_ip=c->addr.sin_addr.s_addr;
#endif
else
c->bind_ip=0;
/* Setup c->remote_fd, now */
if(c->opt->option.remote) {
c->resolved_addresses=NULL;
fd=connect_remote(c);
if(c->resolved_addresses) /* allocated */
free(c->resolved_addresses);
} else /* NOT in remote mode */
fd=connect_local(c);
if(fd<0) {
log(LOG_ERR, "Failed to initialize remote connection");
return -1;
}
#ifndef USE_WIN32
if(fd>=max_fds) {
log(LOG_ERR, "Remote file descriptor out of range (%d>=%d)",
fd, max_fds);
closesocket(fd);
return -1;
}
#endif
log(LOG_DEBUG, "Remote FD=%d initialized", fd);
c->remote_fd.fd=fd;
c->remote_fd.is_socket=1; /* Always! */
if(set_socket_options(fd, 2)<0)
return -1;
return 0; /* OK */
}
static int init_ssl(CLI *c) {
int i, err;
if(!(c->ssl=SSL_new(ctx))) {
sslerror("SSL_new");
return -1;
}
#if SSLEAY_VERSION_NUMBER >= 0x0922
SSL_set_session_id_context(c->ssl, sid_ctx, strlen(sid_ctx));
#endif
if(options.option.client) {
/* Attempt to use the most recent id in the session cache */
#ifndef HAVE_YASSL /* yassl add, ctx members available */
if(ctx->session_cache_head)
if(!SSL_set_session(c->ssl, ctx->session_cache_head))
log(LOG_WARNING, "Cannot set SSL session id to most recent used");
#endif /* yassl end add */
SSL_set_fd(c->ssl, c->remote_fd.fd);
SSL_set_connect_state(c->ssl);
} else {
if(c->local_rfd.fd==c->local_wfd.fd)
SSL_set_fd(c->ssl, c->local_rfd.fd);
else {
/* Does it make sence to have SSL on STDIN/STDOUT? */
SSL_set_rfd(c->ssl, c->local_rfd.fd);
SSL_set_wfd(c->ssl, c->local_wfd.fd);
}
SSL_set_accept_state(c->ssl);
}
/* Setup some values for transfer() function */
if(options.option.client) {
c->sock_rfd=&(c->local_rfd);
c->sock_wfd=&(c->local_wfd);
c->ssl_rfd=c->ssl_wfd=&(c->remote_fd);
} else {
c->sock_rfd=c->sock_wfd=&(c->remote_fd);
c->ssl_rfd=&(c->local_rfd);
c->ssl_wfd=&(c->local_wfd);
}
while(1) {
if(options.option.client)
i=SSL_connect(c->ssl);
else
i=SSL_accept(c->ssl);
err=SSL_get_error(c->ssl, i);
if(err==SSL_ERROR_NONE)
break; /* ok -> done */
if(err==SSL_ERROR_WANT_READ) {
if(waitforsocket(c->ssl_rfd->fd, 0, c->opt->timeout_busy)==1)
continue; /* ok -> retry */
return -1; /* timeout or error */
}
if(err==SSL_ERROR_WANT_WRITE) {
if(waitforsocket(c->ssl_wfd->fd, 1, c->opt->timeout_busy)==1)
continue; /* ok -> retry */
return -1; /* timeout or error */
}
if(err==SSL_ERROR_SYSCALL) {
switch(get_last_socket_error()) {
case EINTR:
case EAGAIN:
continue;
}
}
if(options.option.client)
sslerror("SSL_connect");
else
sslerror("SSL_accept");
return -1;
}
print_cipher(c);
return 0; /* OK */
}
static int transfer(CLI *c) { /* transfer data */
fd_set rd_set, wr_set;
int num, err, fdno;
int check_SSL_pending;
int ssl_closing;
/* 0=not closing SSL, 1=initiate SSL_shutdown,
* 2=retry SSL_shutdown, 3=SSL_shutdown done */
int ready;
struct timeval tv;
/* fdno=max(c->sock_rfd->fd, c->sock_wfd->fd,
* fdno=c->ssl_rfd->fd, fdno=c->ssl_wfd->fd)+1 */
fdno=c->sock_rfd->fd;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -