--- ./stunnel-4.43.orig/src/client.c	2011-10-21 12:03:53.000000000 +0500
+++ ./stunnel-4.43/src/client.c	2011-10-21 12:36:46.000000000 +0500
@@ -261,6 +261,230 @@
         longjmp(c->err, 1);
 }
 
+
+static char *get_peer_ip(CLI *c) {
+char *peer_ip = 0;
+         peer_ip = strtok(c->accepted_address, ":");
+     return(strdup(peer_ip));
+}
+
+
+
+
+static char *get_peer_name(CLI *c) {
+
+struct addrinfo *result;
+    struct addrinfo *res;
+    int error;
+    char hostname[NI_MAXHOST] = "";
+    char *peer_ip = 0;
+    peer_ip = strtok(c->accepted_address, ":");
+    /* resolve the domain name into a list of addresses */
+
+   error = getaddrinfo(peer_ip, NULL, NULL, &result);
+    if (error != 0)
+    {
+	s_log(LOG_ERR, "Post check: getaddr failed '%s'",gai_strerror(error));
+         return(0);
+    }
+
+    /* loop over all returned results and do inverse lookup */
+    for (res = result; res != NULL; res = res->ai_next)
+    {
+
+        error = getnameinfo(res->ai_addr, res->ai_addrlen, hostname, NI_MAXHOST, NULL, 0, 0); 
+        if (error != 0)
+        {
+    	    s_log(LOG_ERR, "Post check: getnameinfo failed '%s'",gai_strerror(error));
+	    return(0);
+        }
+        if (*hostname != '\0')
+    	    s_log(LOG_NOTICE, "Post Peer Name: '%s'", hostname);
+    }
+
+    freeaddrinfo(result);
+
+    return(strdup(hostname));
+
+}
+
+static char *get_remote_ip(CLI *c) {
+    struct sockaddr *from = &(c->opt->remote_addr.addr[c->opt->remote_addr.cur].sa);
+    socklen_t from_len = sizeof(struct sockaddr_in);  // only INET is of interest, no INET6 support
+    char ip[NI_MAXHOST];
+
+    if(0 != getnameinfo(from, from_len, ip, sizeof(ip), 0, 0, NI_NUMERICHOST)) {
+        s_log(LOG_ERR, "getnameinfo NI_NUMERICHOST failed");
+        return(NULL);
+    }
+
+    return(strdup(ip));
+}
+
+
+static char *get_remote_name(CLI *c) {
+    struct addrinfo *result;
+	struct addrinfo *res;
+	int error;
+	char hostname[NI_MAXHOST] = "";
+	char *ip = 0;
+    ip = get_remote_ip(c);
+
+    error = getaddrinfo(ip, NULL, NULL, &result);
+	if (error != 0)
+	{
+	    s_log(LOG_ERR, "Post check: getaddr failed '%s'",gai_strerror(error));
+    	     return(0);
+	}
+    /* loop over all returned results and do inverse lookup */
+    for (res = result; res != NULL; res = res->ai_next)
+    {
+        error = getnameinfo(res->ai_addr, res->ai_addrlen, hostname, NI_MAXHOST, NULL, 0, 0); 
+        if (error != 0)
+        {
+    	    s_log(LOG_ERR, "Post check: getnameinfo failed '%s'",gai_strerror(error));
+	    return(0);
+        }
+        if (*hostname != '\0')
+    	    s_log(LOG_NOTICE, "Post Peer Name: '%s'", hostname);
+    }
+
+    freeaddrinfo(result);
+
+    return(strdup(hostname));
+
+}
+
+
+
+static int verify_cert_hostname(X509 *cert, const char *hostname) {
+    int ok = 0;
+
+    { // check against subjectAltName
+        GENERAL_NAMES *subjectAltNames;
+        int i;
+
+        if(subjectAltNames = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0)) {
+            for(i = 0; !ok && i < sk_GENERAL_NAME_num(subjectAltNames); ++i) {
+                GENERAL_NAME *name;
+                unsigned char *dns;
+                int dnsLen;
+
+                name = sk_GENERAL_NAME_value(subjectAltNames, i);
+                if(name->type != GEN_DNS) continue;
+                dnsLen = ASN1_STRING_to_UTF8(&dns, name->d.dNSName);
+
+                s_log(LOG_NOTICE, "Post check: subjectAltName: \"%s\","
+                    "length: %d, strlen: %d", dns, dnsLen, strlen(dns));
+
+                if(strlen(hostname) == dnsLen)
+                    if(!strcasecmp(dns, hostname)) ok = 1;
+
+                OPENSSL_free(dns);
+            }
+        }
+    }
+
+    { // check against CommonName
+        X509_NAME *subj;
+        int cnIdx;
+
+        if(!ok
+            && (subj = X509_get_subject_name(cert))
+            && (-1 != (cnIdx = X509_NAME_get_index_by_NID(subj, NID_commonName, -1)))) {
+            X509_NAME_ENTRY *cnEntry;
+            ASN1_STRING *cnASN1;
+            int cnLen;
+            unsigned char *cn;
+
+            cnEntry = X509_NAME_get_entry(subj, cnIdx);
+            cnASN1 = X509_NAME_ENTRY_get_data(cnEntry);
+            cnLen = ASN1_STRING_to_UTF8(&cn, cnASN1);
+            s_log(LOG_NOTICE, "Post check: Cert CommonName: \"%s\","
+                "length: %d, strlen: %d", cn, cnLen, strlen(cn));
+
+            /* only if the lengths are equal do we need to perform a comparison,
+             * also, if there are embedded NULLs in cn then cnLen > strlen(cn) */
+            if(strlen(hostname) == cnLen)
+                if(!strcasecmp(cn, hostname)) ok = 1;
+            OPENSSL_free(cn);
+        }
+
+        return ok;
+    }
+}
+
+static int post_connection_check(CLI *c) {
+    int ok = 0;
+    char *rem_ip = 0;
+    char *rem_name = 0;
+    char *peer_ip = 0;
+    char *peer_name = 0;
+    X509 *cert = 0;
+
+    if(c->opt->verify_level <= SSL_VERIFY_PEER) {
+        s_log(LOG_NOTICE, "Post check: verification level is low, skipping check");
+        return X509_V_OK;
+    }
+
+    if(!(cert = SSL_get_peer_certificate(c->ssl))) {
+        s_log(LOG_NOTICE, "Post check: No peer certificate!");
+        return X509_V_ERR_APPLICATION_VERIFICATION;
+    }
+
+    if(c->opt->option.verify_peer_name){
+	peer_name = get_peer_name(c);
+	if(peer_name && verify_cert_hostname(cert, peer_name))
+        ok = 1;
+        s_log(LOG_NOTICE, "Post check CN: By Client Name: %s", peer_name);
+	if(peer_name) free(peer_name);
+	X509_free(cert);
+
+
+	if(ok)
+    	    return SSL_get_verify_result(c->ssl);
+	else
+    	    return X509_V_ERR_APPLICATION_VERIFICATION;
+    }
+    
+  else if(c->opt->option.verify_peer_ip){
+	peer_ip = get_peer_ip(c);
+	if(peer_ip && verify_cert_hostname(cert, peer_ip))
+        ok = 1;
+        s_log(LOG_NOTICE, "Post check CN: By Client IP: %s", peer_ip);
+	if(peer_ip) free(peer_ip);
+	X509_free(cert);
+	if(ok)
+    	    return SSL_get_verify_result(c->ssl);
+	else
+    	    return X509_V_ERR_APPLICATION_VERIFICATION;
+    }
+  else if(c->opt->option.verify_server_ip){
+	rem_ip = get_remote_ip(c);
+	if(rem_ip && verify_cert_hostname(cert, rem_ip))
+        ok = 1;
+        s_log(LOG_NOTICE, "Post check CN: By Remote IP: %s", rem_ip);
+	if(rem_ip) free(rem_ip);
+	X509_free(cert);
+	if(ok)
+    	    return SSL_get_verify_result(c->ssl);
+	else
+    	    return X509_V_ERR_APPLICATION_VERIFICATION;
+    }
+  else if(c->opt->option.verify_server_name){
+	rem_name = get_remote_name(c);
+	if(rem_name && verify_cert_hostname(cert, rem_name))
+        ok = 1;
+        s_log(LOG_NOTICE, "Post check CN: By Remote Name: %s", rem_name);
+	if(rem_name) free(rem_name);
+	X509_free(cert);
+	if(ok)
+    	    return SSL_get_verify_result(c->ssl);
+	else
+    	    return X509_V_ERR_APPLICATION_VERIFICATION;
+    }
+
+}
 static void init_ssl(CLI *c) {
     int i, err;
     SSL_SESSION *old_session;
@@ -326,8 +550,13 @@
         leave_critical_section(CRIT_SSL);
 #endif /* OpenSSL version < 1.0.0b */
         err=SSL_get_error(c->ssl, i);
-        if(err==SSL_ERROR_NONE)
+        if(err==SSL_ERROR_NONE) {
+            if(post_connection_check(c) != X509_V_OK) {
+                s_log(LOG_NOTICE, "Post connection cert verification failed");
+                longjmp(c->err, 1); // bail
+            }
             break; /* ok -> done */
+        }
         if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) {
             s_poll_init(&c->fds);
             s_poll_add(&c->fds, c->ssl_rfd->fd,
--- ./stunnel-4.43.orig/src/options.c	2011-10-21 12:03:53.000000000 +0500
+++ ./stunnel-4.43/src/options.c	2011-10-21 13:06:30.000000000 +0500
@@ -1516,6 +1516,45 @@
         break;
     }
 
+     /* verify_peer*/
+     switch(cmd) {
+     case CMD_INIT:
+         section->option.verify_peer_name=0;
+         section->option.verify_peer_ip=0;
+         section->option.verify_server_name=0;
+         section->option.verify_server_ip=0;
+         break;
+     case CMD_EXEC:
+         if(strcasecmp(opt, "verify_peer"))
+             break;
+         if(!strcasecmp(arg, "client_name"))
+             section->option.verify_peer_name=1;
+         else if(!strcasecmp(arg, "client_ip"))
+             section->option.verify_peer_ip=1;
+         else if(!strcasecmp(arg, "remote_name"))
+             section->option.verify_server_name=1;
+         else if(!strcasecmp(arg, "remote_ip"))
+             section->option.verify_server_ip=1;
+         else if(!strcasecmp(arg, "no")) {
+             section->option.verify_peer_name=0;
+             section->option.verify_peer_ip=0;
+             section->option.verify_server_name=0;
+             section->option.verify_server_ip=0;
+    	    }
+         else
+             return "Argument should be 'client_name' 'client_ip' 'remote_name' 'remote_ip 'or 'no'";
+         return NULL; /* OK */
+     case CMD_DEFAULT:
+         break;
+     case CMD_HELP:
+         s_log(LOG_NOTICE, "%-15s = client_name - verify CN by Client Name", "verify_peer");
+         s_log(LOG_NOTICE, "%18sclient_ip - verify CN by Client IP", "");
+         s_log(LOG_NOTICE, "%18sremote_name - verify CN by Server Name", "");
+         s_log(LOG_NOTICE, "%18sremote_ip - verify CN by Server IP", "");
+         s_log(LOG_NOTICE, "%18sno - Disable", "");
+         break;
+     }
+
     if(cmd==CMD_EXEC)
         return option_not_found;
     return NULL; /* OK */
--- ./stunnel-4.43.orig/src/prototypes.h	2011-10-21 12:03:53.000000000 +0500
+++ ./stunnel-4.43/src/prototypes.h	2011-10-21 13:08:18.000000000 +0500
@@ -197,6 +197,11 @@
         unsigned int transparent_dst:1; /* endpoint: transparent destination */
 #endif
         unsigned int ocsp:1;
+        /* verify_peer */
+        unsigned int verify_peer_name:1;
+        unsigned int verify_peer_ip:1;
+        unsigned int verify_server_name:1;
+        unsigned int verify_server_ip:1;
     } option;
 } SERVICE_OPTIONS;
 
