Hello.
I've modified the old "conndetails_peter" patch by Peter D. Gray to let it work with stunnel 4.10. I've also added to it information about compression (available compression methods and negotiated method).
Patch creates a file "conn.LOCAL_HOST:LOCAL_PORT" in a configurable temp directory (config file: "infodir" option). The file is deleted when the client disconnects.
Full description of original can be found at: http://www.stunnel.org/patches/desc/conndetails_peter.html
Hope it will be useful for someone.
Best Regards, Oleg Ivanov.
-- Oleg Ivanov mailto: saruman@unigsm.com
diff -cr ../stunnel-4.10.orig/src/client.c src/client.c *** ../stunnel-4.10.orig/src/client.c 2005-05-13 00:43:24.000000000 -0200 --- src/client.c 2005-05-13 01:34:36.000000000 -0200 *************** *** 94,99 **** --- 94,114 ---- c->opt=opt; c->local_rfd.fd=rfd; c->local_wfd.fd=wfd; + + if(options.info_dir) { + sprintf(c->info_fname, "%s/pid.%d", options.info_dir, getpid()); + c->info_file = fopen(c->info_fname, "w"); + if(!c->info_file) { + s_log(LOG_ERR, "failed to create: %s (continuing)", c->info_fname); + c->info_fname[0] = 0; + } else { + s_log(LOG_DEBUG, "connection detail to be written to: %s", + c->info_fname); + fprintf(c->info_file, "# connection details\n" + "STUNNEL_PID=%d\n", getpid()); + } + } + return c; }
*************** *** 203,208 **** --- 218,228 ---- s_log(LOG_NOTICE, "%s connected from %s", c->opt->servname, c->accepting_address); } + if(c->info_file) { + fprintf(c->info_file, "SERVICE="%s"\n", c->opt->servname); + fprintf(c->info_file, "REMOTE_HOST="%s"\nREMOTE_PORT=%d\n", + c->accepting_address, ntohs(c->peer_addr.addr[0].in.sin_port)); + } return 0; /* OK */ }
*************** *** 237,242 **** --- 257,289 ---- } #endif s_log(LOG_DEBUG, "Remote FD=%d initialized", fd); + if(c->info_file) { + struct sockaddr_in addr; + int addrlen = sizeof(addr); + if(getsockname(fd, (struct sockaddr *)&addr, &addrlen)) { + // ignore error + } else { + char newname[STRLEN]; + + /* record our end of the tunnel, and change name of file to match */ + enter_critical_section(CRIT_NTOA); /* inet_ntoa is not mt-safe */ + fprintf(c->info_file, "LOCAL_HOST="%s"\nLOCAL_PORT=%d\n", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + sprintf(newname, "%s/conn.%s:%d", + options.info_dir, + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + leave_critical_section(CRIT_NTOA); + + /* done with file, close it now */ + fprintf(c->info_file, "# EOF\n"); + fclose(c->info_file); + c->info_file = NULL; + + /* rename so it's easy for the client to find */ + rename(c->info_fname, newname); + strcpy(c->info_fname, newname); + } + } c->remote_fd.fd=fd; c->remote_fd.is_socket=1; /* Always! */ if(set_socket_options(fd, 2)<0) *************** *** 674,679 **** --- 721,737 ---- reset(c->local_wfd.fd, "linger (local_wfd)"); } } + /* Cleanup connection info file which isn't useful anymore */ + if(options.info_dir) { + if(c->info_file) { + fclose(c->info_file); + c->info_file = NULL; + } + if(c->info_fname) { + unlink(c->info_fname); /* ignore errors */ + s_log(LOG_INFO, "removed info file: %s", c->info_fname); + } + } }
static void print_cipher(CLI *c) { /* print negotiated cipher */ *************** *** 683,689 **** #else SSL_CIPHER *cipher; char buf[STRLEN]; ! int len;
cipher=SSL_get_current_cipher(c->ssl); SSL_CIPHER_description(cipher, buf, STRLEN); --- 741,748 ---- #else SSL_CIPHER *cipher; char buf[STRLEN]; ! int len, cnt; ! SSL_COMP *comp=NULL;
cipher=SSL_get_current_cipher(c->ssl); SSL_CIPHER_description(cipher, buf, STRLEN); *************** *** 691,696 **** --- 750,809 ---- if(len>0) buf[len-1]='\0'; s_log(LOG_INFO, "Negotiated ciphers: %s", buf); + + if(c->info_file) { + X509 *peer; + + fprintf(c->info_file, "CIPHER_DESC="%s"\n", buf); + fprintf(c->info_file, "CIPHER_ALGO="%s"\n", + SSL_CIPHER_get_name(cipher)); + fprintf(c->info_file, "CIPHER_BITS=%d\n", + SSL_CIPHER_get_bits(cipher, NULL)); + // Show compression info + if (c->ssl->ctx->comp_methods == NULL) { + // No compression methods available + fprintf(c->info_file, "No compression methods available\n"); + } else { + int cntMethods = sk_SSL_COMP_num(c->ssl->ctx->comp_methods); + fprintf(c->info_file, "%d compression method(s) available:\n", cntMethods); + for (cnt=0; cnt<cntMethods; cnt++) { + comp=sk_SSL_COMP_value(c->ssl->ctx->comp_methods, cnt); + fprintf(c->info_file, "%d\t%s ", comp->id, comp->name); + switch (comp->id) { + case 0xe0: + fprintf(c->info_file, "(zlib)\n"); break; + case 0xe1: + fprintf(c->info_file, "(rle)\n"); break; + default: + fprintf(c->info_file, "(unknown)\n"); break; + } + } + } + if (c->ssl->s3->tmp.new_compression == NULL) { + fprintf(c->info_file, "SSL_COMP=NULL\n"); + } else { + fprintf(c->info_file, "SSL_COMP.id=%d ", + c->ssl->s3->tmp.new_compression->id); + switch (comp->id) { + case 0xe0: + fprintf(c->info_file, "(zlib)\n"); break; + case 0xe1: + fprintf(c->info_file, "(rle)\n"); break; + default: + fprintf(c->info_file, "(unknown)\n"); break; + } + } + peer = SSL_get_peer_certificate(c->ssl); + if(peer) { + X509_NAME_oneline(X509_get_subject_name(peer), buf, STRLEN); + fprintf(c->info_file, "SSL_CLIENT_DN="%s"\n", buf); + + X509_NAME_oneline(X509_get_issuer_name(peer), buf, STRLEN); + fprintf(c->info_file, "SSL_CLIENT_I_DN="%s"\n", buf); + + X509_free(peer); + } + } #endif }
*************** *** 953,958 **** --- 1066,1075 ---- s_ntop(c->connecting_address, &addr); s_log(LOG_DEBUG, "%s connecting %s", c->opt->servname, c->connecting_address); + if(c->info_file) { + fprintf(c->info_file, "TUNNEL_HOST="%s"\nTUNNEL_PORT=%d\n", + c->connecting_address, ntohs(addr.in.sin_port)); + } if(!connect(s, &addr.sa, addr_len(addr))) return s; /* no error -> success (should not be possible) */ error=get_last_socket_error(); Only in src: client.c.orig Only in src: client.c~ diff -cr ../stunnel-4.10.orig/src/options.c src/options.c *** ../stunnel-4.10.orig/src/options.c 2005-05-13 00:43:24.000000000 -0200 --- src/options.c 2005-05-13 00:44:16.000000000 -0200 *************** *** 664,669 **** --- 664,693 ---- break; }
+ /* infodir */ + switch(cmd) { + case CMD_INIT: + options.info_dir=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "infodir")) + break; + if(arg[0]) { /* not empty */ + if(strlen(arg) > STRLEN - 20) return "infodir pathname too long"; + options.info_dir=stralloc(arg); + } else { + options.info_dir=NULL; + } + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + log_raw("%-15s = writable directory to store connection info into", + "infodir"); + break; + } + + if(cmd==CMD_EXEC) return option_not_found; return NULL; /* OK */ diff -cr ../stunnel-4.10.orig/src/prototypes.h src/prototypes.h *** ../stunnel-4.10.orig/src/prototypes.h 2005-05-13 00:43:24.000000000 -0200 --- src/prototypes.h 2005-05-13 00:47:03.000000000 -0200 *************** *** 113,118 **** --- 113,119 ---- int verify_level; int verify_use_only_my; long ssl_options; + char *info_dir; /* directory for connection info */
/* some global data for stunnel.c */ #ifndef USE_WIN32 *************** *** 264,269 **** --- 265,272 ---- FD *ssl_rfd, *ssl_wfd; /* Read and write SSL descriptors */ int sock_bytes, ssl_bytes; /* Bytes written to socket and ssl */ s_poll_set fds; /* File descriptors */ + FILE *info_file; /* text file with connection information */ + char info_fname[STRLEN]; /* file name (full path) for info_file */ } CLI;
extern int max_clients; *************** *** 305,311 **** /**************************************** Prototypes for sthreads.c */
typedef enum { ! CRIT_KEYGEN, CRIT_INET, CRIT_CLIENTS, CRIT_WIN_LOG, CRIT_SESSION, CRIT_SECTIONS } SECTION_CODE;
--- 308,314 ---- /**************************************** Prototypes for sthreads.c */
typedef enum { ! CRIT_KEYGEN, CRIT_INET, CRIT_CLIENTS, CRIT_WIN_LOG, CRIT_SESSION, CRIT_NTOA, CRIT_SECTIONS } SECTION_CODE;