* A few other things you can already do in s_client and s_server :-)
- * Support (and control over) session resuming, particular when functioning as
- an SSL client.
+ * Support (and control over) session resuming, particularly when functioning
+ as an SSL client.
If you have a particular environment where this model might work to let you "do
SSL" without having OpenSSL be aware of the transport, then you should find you
First, it's assumed that OpenSSL has already built, and that you are building
inside the ./demos/tunala/ directory. If not - please correct the paths and
-flags inside the Makefile.
+flags inside the Makefile. Likewise, if you want to tweak the building, it's
+best to try and do so in the makefile (eg. removing the debug flags and adding
+optimisation flags).
Secondly, this code so far has only ever been built and run on Linux - network
specifics are more than likely to create little glitches on other unixen,
particularly Solaris in my experience. If you're not on Linux, please read the
code wherever compilation flares up and try to make the necessary changes -
usually the man-page associated with the relevant function is enough (eg. all
-that AF_INET/PF_INET stuff, subtely different parameters to various IPv4-related
-functions like socket(), bind(), fcntl(), etc).
+that AF_INET/PF_INET stuff, subtlely different parameters to various
+IPv4-related functions like socket(), bind(), fcntl(), etc).
Thirdly, if you are Win32, you probably need to do some *major* rewriting of
ip.c to stand a hope in hell. Good luck, and please mail me the diff if you do
Inside one console, try typing;
(i) ./tunala -listen localhost:8080 -proxy localhost:8081 -cacert CA.pem \
- -cert A-client.pem
+ -cert A-client.pem -out_totals -v_peer -v_strict
In another console, type;
(ii) ./tunala -listen localhost:8081 -proxy localhost:23 -cacert CA.pem \
- -cert A-server.pem -server 1
+ -cert A-server.pem -server 1 -out_totals -v_peer -v_strict
Now if you open another console and "telnet localhost 8080", you should be
tunneled through to the telnet service on your local machine. Feel free to
void buffer_init(buffer_t *buf)
{
buf->used = 0;
+ buf->total_in = buf->total_out = 0;
}
void buffer_close(buffer_t *buf)
{
- /* Our data is static - nothing needs "release", just reset */
- buffer_init(buf);
+ /* Our data is static - nothing needs "release", just reset it */
+ buf->used = 0;
}
/* Code these simple ones in compact form */
return (buf->used == 0 ? 1 : 0); }
int buffer_notempty(buffer_t *buf) {
return (buf->used > 0 ? 1 : 0); }
-
-unsigned int buffer_adddata(buffer_t *buf, const unsigned char *ptr,
+unsigned long buffer_total_in(buffer_t *buf) {
+ return buf->total_in; }
+unsigned long buffer_total_out(buffer_t *buf) {
+ return buf->total_out; }
+
+/* These 3 static (internal) functions don't adjust the "total" variables as
+ * it's not sure when they're called how it should be interpreted. Only the
+ * higher-level "buffer_[to|from]_[fd|SSL|BIO]" functions should alter these
+ * values. */
+static unsigned int buffer_adddata(buffer_t *buf, const unsigned char *ptr,
unsigned int size)
{
unsigned int added = MAX_DATA_SIZE - buf->used;
return 0;
memcpy(buf->data + buf->used, ptr, added);
buf->used += added;
+ buf->total_in += added;
return added;
}
-unsigned int buffer_takedata(buffer_t *buf, unsigned char *ptr,
+static unsigned int buffer_takedata(buffer_t *buf, unsigned char *ptr,
unsigned int size)
{
unsigned int taken = buf->used;
return taken;
}
-unsigned int buffer_tobuffer(buffer_t *to, buffer_t *from, int cap)
+static unsigned int buffer_tobuffer(buffer_t *to, buffer_t *from, int cap)
{
unsigned int moved, tomove = from->used;
if((int)tomove > cap)
/* Shouldn't be called in this case! */
abort();
toread = read(fd, buf->data + buf->used, toread);
- if(toread > 0)
+ if(toread > 0) {
buf->used += toread;
+ buf->total_in += toread;
+ }
return toread;
}
/* Shouldn't be called in this case! */
abort();
towrite = write(fd, buf->data, towrite);
- if(towrite > 0)
+ if(towrite > 0) {
buffer_takedata(buf, NULL, towrite);
+ buf->total_out += towrite;
+ }
return towrite;
}
if(!ssl || buffer_full(buf))
return;
ret = SSL_read(ssl, buf->data + buf->used, buffer_unused(buf));
- if(ret > 0)
+ if(ret > 0) {
buf->used += ret;
+ buf->total_in += ret;
+ }
if(ret < 0)
int_ssl_check(ssl, ret);
}
if(!ssl || buffer_empty(buf))
return;
ret = SSL_write(ssl, buf->data, buf->used);
- if(ret > 0)
+ if(ret > 0) {
buffer_takedata(buf, NULL, ret);
+ buf->total_out += ret;
+ }
if(ret < 0)
int_ssl_check(ssl, ret);
}
if(!bio || buffer_full(buf))
return;
ret = BIO_read(bio, buf->data + buf->used, buffer_unused(buf));
- if(ret > 0)
+ if(ret > 0) {
buf->used += ret;
+ buf->total_in += ret;
+ }
}
void buffer_to_BIO(buffer_t *buf, BIO *bio)
if(!bio || buffer_empty(buf))
return;
ret = BIO_write(bio, buf->data, buf->used);
- if(ret > 0)
+ if(ret > 0) {
buffer_takedata(buf, NULL, ret);
+ buf->total_out += ret;
+ }
}
#endif /* !defined(NO_OPENSSL) */
static const char *def_cipher_list = NULL;
static int def_out_state = 0;
static unsigned int def_out_verify = 0;
+static int def_out_totals = 0;
static int def_verify_mode = 0;
static unsigned int def_verify_depth = 10;
" -cipher <list> (specifies cipher list to use)\n"
" -out_state (prints SSL handshake states)\n"
" -out_verify <0|1|2|3> (prints certificate verification states: def=1)\n"
+" -out_totals (prints out byte-totals when a tunnel closes)\n"
" -v_peer (verify the peer certificate)\n"
" -v_strict (do not continue if peer doesn't authenticate)\n"
" -v_once (no verification in renegotiates)\n"
return 1;
}
+/* Some fprintf format strings used when tunnels close */
+static const char *io_stats_client_dirty =
+" SSL (network) traffic to/from server; %8lu bytes in, %8lu bytes out\n";
+static const char *io_stats_client_clean =
+" tunnelled data to/from server; %8lu bytes in, %8lu bytes out\n";
+static const char *io_stats_server_dirty =
+" SSL (network) traffic to/from client; %8lu bytes in, %8lu bytes out\n";
+static const char *io_stats_server_clean =
+" tunnelled data to/from client; %8lu bytes in, %8lu bytes out\n";
+
int main(int argc, char *argv[])
{
unsigned int loop;
const char *cipher_list = def_cipher_list;
int out_state = def_out_state;
unsigned int out_verify = def_out_verify;
+ int out_totals = def_out_totals;
int verify_mode = def_verify_mode;
unsigned int verify_depth = def_verify_depth;
if(!parse_verify_level(*argv, &out_verify))
return 1;
goto next_arg;
+ } else if(strcmp(*argv, "-out_totals") == 0) {
+ out_totals = 1;
+ goto next_arg;
} else if(strcmp(*argv, "-v_peer") == 0) {
verify_mode |= SSL_VERIFY_PEER;
goto next_arg;
/* We're closing whether for reasons of an error or a
* natural close. Don't increment loop or t_item because
* the next item is moving to us! */
+ if(!out_totals)
+ goto skip_totals;
+ fprintf(stderr, "Tunnel closing, traffic stats follow\n");
+ /* Display the encrypted (over the network) stats */
+ fprintf(stderr, (server_mode ? io_stats_server_dirty :
+ io_stats_client_dirty),
+ buffer_total_in(state_machine_get_buffer(
+ &t_item->sm,SM_DIRTY_IN)),
+ buffer_total_out(state_machine_get_buffer(
+ &t_item->sm,SM_DIRTY_OUT)));
+ /* Display the local (tunnelled) stats. NB: Data we
+ * *receive* is data sent *out* of the state_machine on
+ * its 'clean' side. Hence the apparent back-to-front
+ * OUT/IN mixup here :-) */
+ fprintf(stderr, (server_mode ? io_stats_server_clean :
+ io_stats_client_clean),
+ buffer_total_out(state_machine_get_buffer(
+ &t_item->sm,SM_CLEAN_OUT)),
+ buffer_total_in(state_machine_get_buffer(
+ &t_item->sm,SM_CLEAN_IN)));
+skip_totals:
tunala_world_del_item(&world, loop);
fprintf(stderr, "Info, tunnel closed, down to %d\n",
world.tunnels_used);
typedef struct _buffer_t {
unsigned char data[MAX_DATA_SIZE];
unsigned int used;
+ /* Statistical values - counts the total number of bytes read in and
+ * read out (respectively) since "buffer_init()" */
+ unsigned long total_in, total_out;
} buffer_t;
/* Initialise a buffer structure before use */
int buffer_notfull(buffer_t *buf); /* Boolean, is it not full? */
int buffer_empty(buffer_t *buf); /* Boolean, is it empty? */
int buffer_notempty(buffer_t *buf); /* Boolean, is it not empty? */
+unsigned long buffer_total_in(buffer_t *buf); /* Total bytes written to buffer */
+unsigned long buffer_total_out(buffer_t *buf); /* Total bytes read from buffer */
+#if 0 /* Currently used only within buffer.c - better to expose only
+ * higher-level functions anyway */
/* Add data to the tail of the buffer, returns the amount that was actually
* added (so, you need to check if return value is less than size) */
unsigned int buffer_adddata(buffer_t *buf, const unsigned char *ptr,
* buffer. Return value is the amount moved. The amount moved can be restricted
* to a maximum by specifying "cap" - setting it to -1 means no limit. */
unsigned int buffer_tobuffer(buffer_t *to, buffer_t *from, int cap);
+#endif
#ifndef NO_IP
/* Read or write between a file-descriptor and a buffer */