>From 2b32ecc410550045c6fe9b2dd046a1dbf3e61bb6 Mon Sep 17 00:00:00 2001
From: Henrik Riomar <henrik.riomar@gmail.com>
Date: Tue, 25 Sep 2012 19:14:59 +0200
Subject: [PATCH] adds send_client_info option

Allows passing of crypto parameters for the current client to the
server "behind" stunnel. This allows the application in the server
to take the crypto level and client-ip:port into account when
offering a service (or not).

The client info is passed as the *first* line before the normal
unencryped data stream from the client.

Example "192.168.10.53:53353;ECDHE-RSA-RC4-SHA"
---
 src/client.c     |   34 ++++++++++++++++++++++++++++++++++
 src/options.c    |   23 +++++++++++++++++++++++
 src/prototypes.h |    1 +
 3 files changed, 58 insertions(+)

diff --git a/src/client.c b/src/client.c
index 851caae..ccf1d35 100644
--- a/src/client.c
+++ b/src/client.c
@@ -211,12 +211,46 @@ static void client_run(CLI *c) {
     c->fds=NULL;
 }
 
+static void send_client_info(CLI *c) {
+    size_t len;
+    SSL_CIPHER *cipher;
+    char *buf, *i, *j;
+    char *accepted_address;
+
+    accepted_address = s_ntop(&c->peer_addr, c->peer_addr_len);
+    len = strlen (accepted_address);
+
+    write (c->remote_fd.fd, accepted_address, len);
+    write (c->remote_fd.fd, ";", 1);
+
+    cipher = (SSL_CIPHER *)SSL_get_current_cipher(c->ssl);
+    buf = SSL_CIPHER_description(cipher, NULL, 0);
+    i = j = buf;
+    do {
+        switch (*i) {
+            case ' ':
+            case '\n':
+                *j = 0;
+                break;
+            default:
+                *j++ = *i;
+        }
+    } while (*i++);
+
+    len = strlen (buf);
+    write (c->remote_fd.fd, buf, len);
+    write (c->remote_fd.fd, "\n", 1);
+}
+
 static void client_try(CLI *c) {
     init_local(c);
     if(!c->opt->option.client && c->opt->protocol<0) {
         /* server mode and no protocol negotiation needed */
         init_ssl(c);
         init_remote(c);
+        if(c->opt->option.send_client_info ){
+            send_client_info(c);
+        }
     } else { /* client mode or protocol negotiation enabled */
         protocol(c, PROTOCOL_PRE_CONNECT);
         init_remote(c);
diff --git a/src/options.c b/src/options.c
index ffedd03..becf02b 100644
--- a/src/options.c
+++ b/src/options.c
@@ -692,6 +692,29 @@ static char *parse_service_option(CMD cmd, SERVICE_OPTIONS *section,
         break;
     }
 
+    /* send client info */
+    switch(cmd) {
+    case CMD_INIT:
+        section->option.send_client_info=0;
+        break;
+    case CMD_EXEC:
+        if(strcasecmp(opt, "send_client_info"))
+            break;
+        if(!strcasecmp(arg, "yes"))
+            section->option.send_client_info=1;
+        else if(!strcasecmp(arg, "no"))
+            section->option.send_client_info=0;
+        else
+            return "Argument should be either 'yes' or 'no'";
+        return NULL; /* OK */
+    case CMD_DEFAULT:
+        break;
+    case CMD_HELP:
+        s_log(LOG_NOTICE, "%-15s = yes|no send client info (to server)",
+            "send_client_info");
+        break;
+    }
+
     /* client */
     switch(cmd) {
     case CMD_INIT:
diff --git a/src/prototypes.h b/src/prototypes.h
index fbb470c..7f16e12 100644
--- a/src/prototypes.h
+++ b/src/prototypes.h
@@ -198,6 +198,7 @@ typedef struct service_options_struct {
     struct {
         unsigned int accept:1;          /* endpoint: accept */
         unsigned int client:1;
+        unsigned int send_client_info:1;
         unsigned int delayed_lookup:1;
 #ifdef USE_LIBWRAP
         unsigned int libwrap:1;
-- 
1.7.10.4

