Upstream sync related ecore_con (r66414, 66462, sync after r65934)
authorTae-Hwan Kim <the81.kim@samsung.com>
Fri, 23 Dec 2011 01:47:04 +0000 (10:47 +0900)
committerTae-Hwan Kim <the81.kim@samsung.com>
Fri, 23 Dec 2011 02:25:38 +0000 (11:25 +0900)
Add ecore_con_socks
Add timeout/proxy APIs in ecore_con_url

12 files changed:
configure.ac
src/lib/ecore_con/Ecore_Con.h
src/lib/ecore_con/Makefile.am
src/lib/ecore_con/ecore_con.c
src/lib/ecore_con/ecore_con_alloc.c
src/lib/ecore_con/ecore_con_ares.c
src/lib/ecore_con/ecore_con_info.c
src/lib/ecore_con/ecore_con_private.h
src/lib/ecore_con/ecore_con_socks.c [new file with mode: 0644]
src/lib/ecore_con/ecore_con_ssl.c
src/lib/ecore_con/ecore_con_url.c
src/lib/ecore_file/ecore_file_download.c

index a1b17de..4cb88d5 100644 (file)
@@ -1190,7 +1190,7 @@ esac
 
 # ecore_con
 
-AC_CHECK_HEADERS([arpa/inet.h arpa/nameser.h netinet/tcp.h netinet/in.h sys/socket.h sys/un.h ws2tcpip.h netdb.h])
+AC_CHECK_HEADERS([arpa/inet.h arpa/nameser.h netinet/tcp.h net/if.h netinet/in.h sys/socket.h sys/un.h ws2tcpip.h netdb.h])
 
 if test "x${ac_cv_header_netdb_h}" = "xyes" ; then
    have_addrinfo="yes"
index e3b68c4..bf58f86 100644 (file)
@@ -234,6 +234,14 @@ typedef struct _Ecore_Con_Server Ecore_Con_Server;
 typedef struct _Ecore_Con_Client Ecore_Con_Client;
 
 /**
+ * @typedef Ecore_Con_Socks
+ * An object representing a SOCKS proxy
+ * @ingroup Ecore_Con_Socks_Group
+ * @since 1.2
+ */
+typedef struct Ecore_Con_Socks Ecore_Con_Socks;
+
+/**
  * @typedef Ecore_Con_Url
  * A handle to an http upload/download object
  * @ingroup Ecore_Con_Url_Group
@@ -325,6 +333,13 @@ typedef struct _Ecore_Con_Event_Client_Write Ecore_Con_Event_Client_Write;
 typedef struct _Ecore_Con_Event_Server_Write Ecore_Con_Event_Server_Write;
 
 /**
+ * @typedef Ecore_Con_Event_Proxy_Bind
+ * Used as the @p data param for the corresponding event
+ * @since 1.2
+ */
+typedef struct _Ecore_Con_Event_Proxy_Bind Ecore_Con_Event_Proxy_Bind;
+
+/**
  * @typedef Ecore_Con_Event_Url_Data
  * Used as the @p data param for the corresponding event
  * @ingroup Ecore_Con_Url_Group
@@ -464,6 +479,19 @@ struct _Ecore_Con_Event_Server_Write
 };
 
 /**
+ * @struct _Ecore_Con_Event_Proxy_Bind
+ * Used as the @p data param for the @ref ECORE_CON_EVENT_PROXY_BIND event
+ * @ingroup Ecore_Con_Socks_Group
+ * @since 1.2
+ */
+struct _Ecore_Con_Event_Proxy_Bind
+{
+   Ecore_Con_Server *server; /**< the server object connected to the proxy */
+   const char *ip;           /**< the proxy-bound ip address */
+   int port;                 /**< the proxy-bound port */ 
+};
+
+/**
  * @struct _Ecore_Con_Event_Url_Data
  * Used as the @p data param for the @ref ECORE_CON_EVENT_URL_DATA event
  * @ingroup Ecore_Con_Url_Group
@@ -542,6 +570,10 @@ EAPI extern int ECORE_CON_EVENT_SERVER_WRITE;
 EAPI extern int ECORE_CON_EVENT_CLIENT_DATA;
 /** A server connection object has data */
 EAPI extern int ECORE_CON_EVENT_SERVER_DATA;
+/** A server connection has successfully negotiated an ip:port binding
+ * @since 1.2
+ */
+EAPI extern int ECORE_CON_EVENT_PROXY_BIND;
 /** A URL object has data */
 EAPI extern int ECORE_CON_EVENT_URL_DATA;
 /** A URL object has completed its transfer to and from the server and can be reused */
@@ -605,7 +637,13 @@ typedef enum _Ecore_Con_Type
    ECORE_CON_REMOTE_UDP = 5,
    /** Remote broadcast using UDP */
    ECORE_CON_REMOTE_BROADCAST = 6,
+   /** Remote connection sending packets immediately */
    ECORE_CON_REMOTE_NODELAY = 7,
+   /** Remote connection sending data in large chunks
+    * @note Only available on Linux
+    * @since 1.2
+    */
+   ECORE_CON_REMOTE_CORK = 8,
    /** Use SSL2: UNSUPPORTED. **/
    ECORE_CON_USE_SSL2 = (1 << 4),
    /** Use SSL3 */
@@ -675,6 +713,8 @@ EAPI Eina_Bool         ecore_con_ssl_server_crl_add(Ecore_Con_Server *svr, const
 EAPI Eina_Bool         ecore_con_ssl_server_cafile_add(Ecore_Con_Server *svr, const char *ca_file);
 EAPI void              ecore_con_ssl_server_verify(Ecore_Con_Server *svr);
 EAPI void              ecore_con_ssl_server_verify_basic(Ecore_Con_Server *svr);
+EAPI void              ecore_con_ssl_server_verify_name_set(Ecore_Con_Server *svr, const char *name);
+EAPI const char       *ecore_con_ssl_server_verify_name_get(Ecore_Con_Server *svr);
 EAPI Eina_Bool         ecore_con_ssl_server_upgrade(Ecore_Con_Server *svr, Ecore_Con_Type compl_type);
 EAPI Eina_Bool         ecore_con_ssl_client_upgrade(Ecore_Con_Client *cl, Ecore_Con_Type compl_type);
 
@@ -682,6 +722,18 @@ EAPI Eina_Bool         ecore_con_ssl_client_upgrade(Ecore_Con_Client *cl, Ecore_
  * @}
  */
 
+EAPI Ecore_Con_Socks *ecore_con_socks4_remote_add(const char *ip, int port, const char *username);
+EAPI void             ecore_con_socks4_lookup_set(Ecore_Con_Socks *ecs, Eina_Bool enable);
+EAPI Eina_Bool        ecore_con_socks4_lookup_get(Ecore_Con_Socks *ecs);
+EAPI Eina_Bool        ecore_con_socks4_remote_exists(const char *ip, int port, const char *username);
+EAPI void             ecore_con_socks4_remote_del(const char *ip, int port, const char *username);
+EAPI void             ecore_con_socks_bind_set(Ecore_Con_Socks *ecs, Eina_Bool is_bind);
+EAPI Eina_Bool        ecore_con_socks_bind_get(Ecore_Con_Socks *ecs);
+EAPI unsigned int     ecore_con_socks_version_get(Ecore_Con_Socks *ecs);
+EAPI void             ecore_con_socks_remote_del(Ecore_Con_Socks *ecs);
+EAPI void             ecore_con_socks_apply_once(Ecore_Con_Socks *ecs);
+EAPI void             ecore_con_socks_apply_always(Ecore_Con_Socks *ecs);
+
 /**
  * @defgroup Ecore_Con_Server_Group Ecore Connection Server Functions
  *
@@ -1185,6 +1237,8 @@ EAPI Eina_Bool         ecore_con_client_connected_get(Ecore_Con_Client *cl);
  */
 EAPI int               ecore_con_client_port_get(Ecore_Con_Client *cl);
 
+
+
 /**
  * @}
  */
@@ -1791,6 +1845,74 @@ EAPI int               ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con,
                                                 const char *ca_path);
 
 /**
+ * Set HTTP proxy to use.
+ *
+ * The parameter should be a char * to a zero terminated string holding
+ * the host name or dotted IP address. To specify port number in this string,
+ * append :[port] to the end of the host name.
+ * The proxy string may be prefixed with [protocol]:// since any such prefix
+ * will be ignored.
+ * The proxy's port number may optionally be specified with the separate option.
+ * If not specified, libcurl will default to using port 1080 for proxies.
+ *
+ * @param url_con Connection object that will use the proxy.
+ * @param proxy Porxy string or @c NULL to disable
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on error.
+ * @since 1.2
+ */
+EAPI Eina_Bool ecore_con_url_proxy_set(Ecore_Con_Url *url_con, const char *proxy);
+
+/**
+ * Set zero terminated username to use for proxy.
+ *
+ * if socks protocol is used for proxy, protocol should be socks5 and above.
+ *
+ * @param url_con Connection object that will use the proxy.
+ * @param username Username string.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on error.
+ *
+ * @see ecore_con_url_proxy_set()
+ *
+ * @since 1.2 
+ */
+EAPI Eina_Bool ecore_con_url_proxy_username_set(Ecore_Con_Url *url_con, const char *username);
+
+/**
+ * Set zero terminated password to use for proxy.
+ *
+ * if socks protocol is used for proxy, protocol should be socks5 and above.
+ *
+ * @param url_con Connection object that will use the proxy.
+ * @param password Password string.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on error.
+ *
+ * @see ecore_con_url_proxy_set()
+ *
+ * @since 1.2
+ */
+EAPI Eina_Bool ecore_con_url_proxy_password_set(Ecore_Con_Url *url_con, const char *password);
+
+/**
+ * Set timeout in seconds.
+ *
+ * the maximum time in seconds that you allow the ecore con url transfer
+ * operation to take. Normally, name lookups can take a considerable time
+ * and limiting operations to less than a few minutes risk aborting perfectly
+ * normal operations.
+ *
+ * @param url_con Connection object that will use the timeout.
+ * @param timeout time in seconds.
+ *
+ * @see ecore_con_url_cookies_jar_file_set()
+ *
+ * @since 1.2
+ */
+EAPI void ecore_con_url_timeout_set(Ecore_Con_Url *url_con, double timeout);
+
+/**
  * @}
  */
 
index 300586d..929b30e 100644 (file)
@@ -19,6 +19,7 @@ includesdir = $(includedir)/ecore-@VMAJ@
 
 libecore_con_la_SOURCES = \
 ecore_con.c \
+ecore_con_socks.c \
 ecore_con_ssl.c \
 ecore_con_url.c \
 ecore_con_alloc.c
index a05f7b3..d665d80 100644 (file)
@@ -45,7 +45,7 @@
 
 static Eina_Bool _ecore_con_client_timer(Ecore_Con_Client *cl);
 static void      _ecore_con_cl_timer_update(Ecore_Con_Client *cl);
-
+static void      _ecore_con_client_kill(Ecore_Con_Client *cl);
 static Eina_Bool _ecore_con_server_timer(Ecore_Con_Server *svr);
 static void      _ecore_con_server_timer_update(Ecore_Con_Server *svr);
 
@@ -104,6 +104,45 @@ static void _ecore_con_lookup_done(void           *data,
 
 static const char * _ecore_con_pretty_ip(struct sockaddr *client_addr);
 
+
+static void
+_ecore_con_client_kill(Ecore_Con_Client *cl)
+{
+   if (cl->delete_me)
+     DBG("Multi kill request for client %p", cl);
+   else
+     ecore_con_event_client_del(cl);
+   INF("Lost client %s", (cl->ip) ? cl->ip : "");
+   if (cl->fd_handler)
+     ecore_main_fd_handler_del(cl->fd_handler);
+
+   cl->fd_handler = NULL;
+}
+
+void
+_ecore_con_server_kill(Ecore_Con_Server *svr)
+{
+   if (svr->delete_me)
+     DBG("Multi kill request for svr %p", svr);
+   else
+     ecore_con_event_server_del(svr);
+
+   if (svr->fd_handler)
+     ecore_main_fd_handler_del(svr->fd_handler);
+
+   svr->fd_handler = NULL;
+}
+
+#define _ecore_con_server_kill(svr) do { \
+   DBG("KILL %p", (svr)); \
+   _ecore_con_server_kill((svr)); \
+} while (0)
+
+#define _ecore_con_client_kill(cl) do { \
+   DBG("KILL %p", (cl)); \
+   _ecore_con_client_kill((cl)); \
+} while (0)
+
 EAPI int ECORE_CON_EVENT_CLIENT_ADD = 0;
 EAPI int ECORE_CON_EVENT_CLIENT_DEL = 0;
 EAPI int ECORE_CON_EVENT_SERVER_ADD = 0;
@@ -114,11 +153,14 @@ EAPI int ECORE_CON_EVENT_CLIENT_WRITE = 0;
 EAPI int ECORE_CON_EVENT_SERVER_WRITE = 0;
 EAPI int ECORE_CON_EVENT_CLIENT_ERROR = 0;
 EAPI int ECORE_CON_EVENT_SERVER_ERROR = 0;
+EAPI int ECORE_CON_EVENT_PROXY_BIND = 0;
 
 static Eina_List *servers = NULL;
 static int _ecore_con_init_count = 0;
 static int _ecore_con_event_count = 0;
 int _ecore_con_log_dom = -1;
+Ecore_Con_Socks *_ecore_con_proxy_once = NULL;
+Ecore_Con_Socks *_ecore_con_proxy_global = NULL;
 
 EAPI int
 ecore_con_init(void)
@@ -156,6 +198,7 @@ ecore_con_init(void)
    ECORE_CON_EVENT_SERVER_WRITE = ecore_event_type_new();
    ECORE_CON_EVENT_CLIENT_ERROR = ecore_event_type_new();
    ECORE_CON_EVENT_SERVER_ERROR = ecore_event_type_new();
+   ECORE_CON_EVENT_PROXY_BIND = ecore_event_type_new();
 
 
    eina_magic_string_set(ECORE_MAGIC_CON_SERVER, "Ecore_Con_Server");
@@ -163,6 +206,7 @@ ecore_con_init(void)
    eina_magic_string_set(ECORE_MAGIC_CON_URL, "Ecore_Con_Url");
 
    /* TODO Remember return value, if it fails, use gethostbyname() */
+   ecore_con_socks_init();
    ecore_con_ssl_init();
    ecore_con_info_init();
 
@@ -182,7 +226,7 @@ ecore_con_shutdown(void)
      {
         Ecore_Con_Event_Server_Add *ev;
 
-        svr->delete_me = svr->dead = EINA_TRUE;
+        svr->delete_me = EINA_TRUE;
         INF("svr %p is dead", svr);
         /* some pointer hacks here to prevent double frees if people are being stupid */
         EINA_LIST_FREE(svr->event_count, ev)
@@ -190,6 +234,7 @@ ecore_con_shutdown(void)
         _ecore_con_server_free(svr);
      }
 
+   ecore_con_socks_shutdown();
    if (!_ecore_con_event_count) ecore_con_mempool_shutdown();
 
    ecore_con_info_shutdown();
@@ -302,8 +347,7 @@ ecore_con_server_add(Ecore_Con_Type compl_type,
    svr->port = port;
    svr->data = (void *)data;
    svr->created = EINA_TRUE;
-   if (compl_type & ECORE_CON_LOAD_CERT)
-     svr->use_cert = EINA_TRUE;
+   svr->use_cert = (compl_type & ECORE_CON_SSL & ECORE_CON_LOAD_CERT) == ECORE_CON_LOAD_CERT;
    svr->reject_excess_clients = EINA_FALSE;
    svr->client_limit = -1;
    svr->clients = NULL;
@@ -326,7 +370,8 @@ ecore_con_server_add(Ecore_Con_Type compl_type,
 #endif
 
    if ((type == ECORE_CON_REMOTE_TCP) ||
-       (type == ECORE_CON_REMOTE_NODELAY))
+       (type == ECORE_CON_REMOTE_NODELAY) ||
+       (type == ECORE_CON_REMOTE_CORK))
      {
         /* TCP */
          if (!ecore_con_info_tcp_listen(svr, _ecore_con_cb_tcp_listen,
@@ -396,17 +441,37 @@ ecore_con_server_connect(Ecore_Con_Type compl_type,
    svr->port = port;
    svr->data = (void *)data;
    svr->created = EINA_FALSE;
-   svr->use_cert = (compl_type & ECORE_CON_LOAD_CERT);
+   svr->use_cert = (compl_type & ECORE_CON_SSL & ECORE_CON_LOAD_CERT) == ECORE_CON_LOAD_CERT;
    svr->reject_excess_clients = EINA_FALSE;
    svr->clients = NULL;
    svr->client_limit = -1;
-   if (ecore_con_ssl_server_prepare(svr, compl_type & ECORE_CON_SSL))
-     goto error;
 
    type = compl_type & ECORE_CON_TYPE;
 
+   if (type > ECORE_CON_LOCAL_ABSTRACT)
+     {
+        /* never use proxies on local connections */
+        if (_ecore_con_proxy_once)
+          svr->ecs = _ecore_con_proxy_once;
+        else if (_ecore_con_proxy_global)
+          svr->ecs = _ecore_con_proxy_global;
+        _ecore_con_proxy_once = NULL;
+        if (svr->ecs)
+          {
+             if ((!svr->ecs->lookup) &&
+                 (!ecore_con_lookup(svr->name, (Ecore_Con_Dns_Cb)ecore_con_socks_dns_cb, svr)))
+               goto error;
+             if (svr->ecs->lookup)
+               svr->ecs_state = ECORE_CON_SOCKS_STATE_RESOLVED;
+          }
+          
+     }
+   if (ecore_con_ssl_server_prepare(svr, compl_type & ECORE_CON_SSL))
+     goto error;
+
    if (((type == ECORE_CON_REMOTE_TCP) ||
         (type == ECORE_CON_REMOTE_NODELAY) ||
+        (type == ECORE_CON_REMOTE_CORK) ||
         (type == ECORE_CON_REMOTE_UDP) ||
         (type == ECORE_CON_REMOTE_BROADCAST)) &&
        (port < 0))
@@ -425,7 +490,8 @@ ecore_con_server_connect(Ecore_Con_Type compl_type,
 #endif
 
    if ((type == ECORE_CON_REMOTE_TCP) ||
-       (type == ECORE_CON_REMOTE_NODELAY))
+       (type == ECORE_CON_REMOTE_NODELAY) ||
+       (type == ECORE_CON_REMOTE_CORK))
      {
         /* TCP */
          if (!ecore_con_info_tcp_connect(svr, _ecore_con_cb_tcp_connect,
@@ -493,8 +559,6 @@ ecore_con_server_timeout_get(Ecore_Con_Server *svr)
 EAPI void *
 ecore_con_server_del(Ecore_Con_Server *svr)
 {
-   void *data;
-
    if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
      {
         ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER, "ecore_con_server_del");
@@ -504,20 +568,8 @@ ecore_con_server_del(Ecore_Con_Server *svr)
    if (svr->delete_me)
      return NULL;
 
-   data = svr->data;
-   svr->delete_me = EINA_TRUE;
-   if (svr->event_count)
-     {
-        if (svr->fd_handler)
-          {
-             ecore_main_fd_handler_del(svr->fd_handler);
-             svr->fd_handler = NULL;
-          }
-     }
-   else
-     _ecore_con_server_free(svr);
-
-   return data;
+   _ecore_con_server_kill(svr);
+   return svr->data;
 }
 
 EAPI void *
@@ -525,9 +577,7 @@ ecore_con_server_data_get(Ecore_Con_Server *svr)
 {
    if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
      {
-        ECORE_MAGIC_FAIL(svr,
-                         ECORE_MAGIC_CON_SERVER,
-                         "ecore_con_server_data_get");
+        ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER, "ecore_con_server_data_get");
         return NULL;
      }
 
@@ -542,9 +592,7 @@ ecore_con_server_data_set(Ecore_Con_Server *svr,
 
    if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
      {
-        ECORE_MAGIC_FAIL(svr,
-                         ECORE_MAGIC_CON_SERVER,
-                         "ecore_con_server_data_get");
+        ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER, "ecore_con_server_data_get");
         return NULL;
      }
 
@@ -558,8 +606,7 @@ ecore_con_server_connected_get(Ecore_Con_Server *svr)
 {
    if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
      {
-        ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER,
-                         "ecore_con_server_connected_get");
+        ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER, "ecore_con_server_connected_get");
         return EINA_FALSE;
      }
 
@@ -618,7 +665,7 @@ ecore_con_server_send(Ecore_Con_Server *svr,
         return 0;
      }
 
-   EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->dead, 0);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->delete_me, 0);
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(data, 0);
 
@@ -631,6 +678,15 @@ ecore_con_server_send(Ecore_Con_Server *svr,
      {
         svr->buf = eina_binbuf_new();
         EINA_SAFETY_ON_NULL_RETURN_VAL(svr->buf, 0);
+#ifdef TCP_CORK
+        if ((svr->fd >= 0) && ((svr->type & ECORE_CON_TYPE) == ECORE_CON_REMOTE_CORK))
+          {
+             int state = 1;
+             if (setsockopt(svr->fd, IPPROTO_TCP, TCP_CORK, (char *)&state, sizeof(int)) < 0)
+               /* realistically this isn't anything serious so we can just log and continue */
+               ERR("corking failed! %s", strerror(errno));
+          }
+#endif
      }
    eina_binbuf_append_length(svr->buf, data, size);
 
@@ -717,7 +773,7 @@ ecore_con_client_send(Ecore_Con_Client *cl,
         return 0;
      }
 
-   EINA_SAFETY_ON_TRUE_RETURN_VAL(cl->dead, 0);
+   EINA_SAFETY_ON_TRUE_RETURN_VAL(cl->delete_me, 0);
 
    EINA_SAFETY_ON_NULL_RETURN_VAL(data, 0);
 
@@ -733,6 +789,15 @@ ecore_con_client_send(Ecore_Con_Client *cl,
      {
         cl->buf = eina_binbuf_new();
         EINA_SAFETY_ON_NULL_RETURN_VAL(cl->buf, 0);
+#ifdef TCP_CORK
+        if ((cl->fd >= 0) && ((cl->host_server->type & ECORE_CON_TYPE) == ECORE_CON_REMOTE_CORK))
+          {
+             int state = 1;
+             if (setsockopt(cl->fd, IPPROTO_TCP, TCP_CORK, (char *)&state, sizeof(int)) < 0)
+               /* realistically this isn't anything serious so we can just log and continue */
+               ERR("corking failed! %s", strerror(errno));
+          }
+#endif
      }
    eina_binbuf_append_length(cl->buf, data, size);
 
@@ -762,7 +827,7 @@ ecore_con_client_connected_get(Ecore_Con_Client *cl)
         return EINA_FALSE;
      }
 
-   return !cl->dead;
+   return !cl->delete_me;
 }
 
 EAPI void
@@ -796,36 +861,14 @@ ecore_con_client_timeout_get(Ecore_Con_Client *cl)
 EAPI void *
 ecore_con_client_del(Ecore_Con_Client *cl)
 {
-   void *data = NULL;
-
    if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
      {
         ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT, "ecore_con_client_del");
         return NULL;
      }
 
-   data = cl->data;
-   cl->delete_me = EINA_TRUE;
-   if (cl->event_count)
-     {
-        if (cl->fd_handler)
-          {
-             ecore_main_fd_handler_del(cl->fd_handler);
-             cl->fd_handler = NULL;
-          }
-     }
-   else
-     {
-        if (cl->host_server)
-          {
-             cl->host_server->clients = eina_list_remove(cl->host_server->clients, cl);
-             --cl->host_server->client_count;
-          }
-
-        _ecore_con_client_free(cl);
-     }
-
-   return data;
+   _ecore_con_client_kill(cl);
+   return cl->data;
 }
 
 EAPI void
@@ -834,9 +877,7 @@ ecore_con_client_data_set(Ecore_Con_Client *cl,
 {
    if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
      {
-        ECORE_MAGIC_FAIL(cl,
-                         ECORE_MAGIC_CON_CLIENT,
-                         "ecore_con_client_data_set");
+        ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT, "ecore_con_client_data_set");
         return;
      }
 
@@ -848,9 +889,7 @@ ecore_con_client_data_get(Ecore_Con_Client *cl)
 {
    if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
      {
-        ECORE_MAGIC_FAIL(cl,
-                         ECORE_MAGIC_CON_CLIENT,
-                         "ecore_con_client_data_get");
+        ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT, "ecore_con_client_data_get");
         return NULL;
      }
 
@@ -940,6 +979,25 @@ ecore_con_client_fd_get(Ecore_Con_Client *cl)
  */
 
 void
+ecore_con_event_proxy_bind(Ecore_Con_Server *svr)
+{
+    Ecore_Con_Event_Proxy_Bind *e;
+    int ev = ECORE_CON_EVENT_PROXY_BIND;
+
+    e = ecore_con_event_proxy_bind_alloc();
+    EINA_SAFETY_ON_NULL_RETURN(e);
+
+    svr->event_count = eina_list_append(svr->event_count, e);
+    _ecore_con_server_timer_update(svr);
+    e->server = svr;
+    e->ip = svr->proxyip;
+    e->port = svr->proxyport;
+    ecore_event_add(ev, e,
+                    _ecore_con_event_server_add_free, NULL);
+   _ecore_con_event_count++;
+}
+
+void
 ecore_con_event_server_add(Ecore_Con_Server *svr)
 {
     /* we got our server! */
@@ -949,6 +1007,8 @@ ecore_con_event_server_add(Ecore_Con_Server *svr)
     e = ecore_con_event_server_add_alloc();
     EINA_SAFETY_ON_NULL_RETURN(e);
 
+    svr->connecting = EINA_FALSE;
+    svr->start_time = ecore_time_get();
     svr->event_count = eina_list_append(svr->event_count, e);
     _ecore_con_server_timer_update(svr);
     e->server = svr;
@@ -963,12 +1023,20 @@ ecore_con_event_server_del(Ecore_Con_Server *svr)
 {
     Ecore_Con_Event_Server_Del *e;
 
+    svr->delete_me = EINA_TRUE;
+    INF("svr %p is dead", svr);
     e = ecore_con_event_server_del_alloc();
     EINA_SAFETY_ON_NULL_RETURN(e);
 
     svr->event_count = eina_list_append(svr->event_count, e);
     _ecore_con_server_timer_update(svr);
     e->server = svr;
+    if (svr->ecs)
+      {
+         svr->ecs_state = svr->ecs->lookup ? ECORE_CON_SOCKS_STATE_RESOLVED : ECORE_CON_SOCKS_STATE_DONE;
+         eina_stringshare_replace(&svr->proxyip, NULL);
+         svr->proxyport = 0;
+      }
     ecore_event_add(ECORE_CON_EVENT_SERVER_DEL, e,
                     _ecore_con_event_server_del_free, NULL);
    _ecore_con_event_count++;
@@ -982,6 +1050,7 @@ ecore_con_event_server_write(Ecore_Con_Server *svr, int num)
    e = ecore_con_event_server_write_alloc();
    EINA_SAFETY_ON_NULL_RETURN(e);
 
+   INF("Wrote %d bytes", num);
    svr->event_count = eina_list_append(svr->event_count, e);
    e->server = svr;
    e->size = num;
@@ -1045,6 +1114,8 @@ ecore_con_event_client_del(Ecore_Con_Client *cl)
     Ecore_Con_Event_Client_Del *e;
 
     if (!cl) return;
+    cl->delete_me = EINA_TRUE;
+    INF("cl %p is dead", cl);
     e = ecore_con_event_client_del_alloc();
     EINA_SAFETY_ON_NULL_RETURN(e);
     cl->event_count = eina_list_append(cl->event_count, e);
@@ -1113,7 +1184,7 @@ ecore_con_server_infos_del(Ecore_Con_Server *svr, void *info)
 }
 
 void
-ecore_con_event_server_error(Ecore_Con_Server *svr, const char *error)
+_ecore_con_event_server_error(Ecore_Con_Server *svr, char *error, Eina_Bool duplicate)
 {
    Ecore_Con_Event_Server_Error *e;
 
@@ -1121,7 +1192,7 @@ ecore_con_event_server_error(Ecore_Con_Server *svr, const char *error)
    EINA_SAFETY_ON_NULL_RETURN(e);
 
    e->server = svr;
-   e->error = strdup(error);
+   e->error = duplicate ? strdup(error) : error;
    ERR("%s", error);
    svr->event_count = eina_list_append(svr->event_count, e);
    ecore_event_add(ECORE_CON_EVENT_SERVER_ERROR, e, (Ecore_End_Cb)_ecore_con_event_server_error_free, NULL);
@@ -1158,17 +1229,9 @@ _ecore_con_server_free(Ecore_Con_Server *svr)
         ecore_con_info_data_clear(svr->infos->data);
         svr->infos = eina_list_remove_list(svr->infos, svr->infos);
      }
-   if ((!svr->buf) && svr->delete_me && (!svr->dead) && (!svr->event_count))
-     {
-        /* this is a catch-all for cases when a server is not properly killed. */
-        svr->dead = EINA_TRUE;
-        INF("svr %p is dead", svr);
-        ecore_con_event_server_del(svr);
-        return;
-     }
 
    t_start = ecore_time_get();
-   while (svr->buf && (!svr->dead))
+   while (svr->buf && (!svr->delete_me))
      {
         _ecore_con_server_flush(svr);
         t = ecore_time_get();
@@ -1198,7 +1261,7 @@ _ecore_con_server_free(Ecore_Con_Server *svr)
         /* some pointer hacks here to prevent double frees if people are being stupid */
         EINA_LIST_FREE(cl->event_count, ev)
           ev->server = NULL;
-        cl->delete_me = cl->dead = EINA_TRUE;
+        cl->delete_me = EINA_TRUE;
         INF("cl %p is dead", cl);
         _ecore_con_client_free(cl);
      }
@@ -1211,6 +1274,10 @@ _ecore_con_server_free(Ecore_Con_Server *svr)
    free(svr->path);
 
    eina_stringshare_del(svr->ip);
+   eina_stringshare_del(svr->verify_name);
+
+   if (svr->ecs_buf) eina_binbuf_free(svr->ecs_buf);
+   if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
 
    if (svr->fd_handler)
      ecore_main_fd_handler_del(svr->fd_handler);
@@ -1233,18 +1300,8 @@ _ecore_con_client_free(Ecore_Con_Client *cl)
 
    if (cl->event_count) return;
 
-   if (cl->delete_me && (!cl->dead) && (!cl->event_count))
-     {
-        /* this is a catch-all for cases when a client is not properly killed. */
-           cl->dead = EINA_TRUE;
-           INF("cl %p is dead", cl);
-           ecore_con_event_client_del(cl);
-           return;
-     }
-
-
    t_start = ecore_time_get();
-   while ((cl->buf) && (!cl->dead))
+   while ((cl->buf) && (!cl->delete_me))
      {
         _ecore_con_client_flush(cl);
         t = ecore_time_get();
@@ -1288,20 +1345,6 @@ _ecore_con_client_free(Ecore_Con_Client *cl)
    return;
 }
 
-static void
-_ecore_con_server_kill(Ecore_Con_Server *svr)
-{
-   if (!svr->delete_me)
-     ecore_con_event_server_del(svr);
-
-   svr->dead = EINA_TRUE;
-   INF("svr %p is dead", svr);
-   if (svr->fd_handler)
-     ecore_main_fd_handler_del(svr->fd_handler);
-
-   svr->fd_handler = NULL;
-}
-
 static Eina_Bool
 _ecore_con_server_timer(Ecore_Con_Server *svr)
 {
@@ -1389,40 +1432,25 @@ _ecore_con_cb_tcp_listen(void           *data,
 {
    Ecore_Con_Server *svr;
    struct linger lin;
+   const char *memerr = NULL;
 
    svr = data;
 
+   errno = 0;
    if (!net_info) /* error message has already been handled */
      goto error;
 
    svr->fd = socket(net_info->info.ai_family, net_info->info.ai_socktype,
                     net_info->info.ai_protocol);
-   if (svr->fd < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
-
-   if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
-
-   if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+   if (svr->fd < 0) goto error;
+   if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
+   if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
 
    lin.l_onoff = 1;
    lin.l_linger = 0;
    if (setsockopt(svr->fd, SOL_SOCKET, SO_LINGER, (const void *)&lin,
                   sizeof(struct linger)) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+     goto error;
 
    if ((svr->type & ECORE_CON_TYPE) == ECORE_CON_REMOTE_NODELAY)
      {
@@ -1433,34 +1461,27 @@ _ecore_con_cb_tcp_listen(void           *data,
                        sizeof(int)) < 0)
 #endif
           {
-             ecore_con_event_server_error(svr, strerror(errno));
              goto error;
           }
      }
 
-   if (bind(svr->fd, net_info->info.ai_addr,
-            net_info->info.ai_addrlen) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
-   if (listen(svr->fd, 4096) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+   if (bind(svr->fd, net_info->info.ai_addr, net_info->info.ai_addrlen) < 0)
+     goto error;
+
+   if (listen(svr->fd, 4096) < 0) goto error;
 
    svr->fd_handler = ecore_main_fd_handler_add(svr->fd, ECORE_FD_READ,
                                                _ecore_con_svr_tcp_handler, svr, NULL, NULL);
    if (!svr->fd_handler)
      {
-        ecore_con_event_server_error(svr, "Memory allocation failure");
+        memerr = "Memory allocation failure";
         goto error;
      }
 
    return;
 
 error:
+   if (errno || memerr) ecore_con_event_server_error(svr, memerr ?: strerror(errno));
    ecore_con_ssl_server_shutdown(svr);
    _ecore_con_server_kill(svr);
 }
@@ -1476,21 +1497,19 @@ _ecore_con_cb_udp_listen(void           *data,
    struct ipv6_mreq mreq6;
 #endif
    const int on = 1;
+   const char *memerr = NULL;
 
    svr = data;
    type = svr->type;
    type &= ECORE_CON_TYPE;
 
+   errno = 0;
    if (!net_info) /* error message has already been handled */
      goto error;
 
    svr->fd = socket(net_info->info.ai_family, net_info->info.ai_socktype,
                     net_info->info.ai_protocol);
-   if (svr->fd < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+   if (svr->fd < 0) goto error;
 
    if (type == ECORE_CON_REMOTE_MCAST)
      {
@@ -1498,69 +1517,41 @@ _ecore_con_cb_udp_listen(void           *data,
           {
              if (!inet_pton(net_info->info.ai_family, net_info->ip,
                             &mreq.imr_multiaddr))
-               {
-                  ecore_con_event_server_error(svr, strerror(errno));
-                  goto error;
-               }
+               goto error;
 
              mreq.imr_interface.s_addr = htonl(INADDR_ANY);
              if (setsockopt(svr->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                             (const void *)&mreq, sizeof(mreq)) != 0)
-               {
-                  ecore_con_event_server_error(svr, strerror(errno));
-                  goto error;
-               }
+               goto error;
           }
 #ifdef HAVE_IPV6
         else if (net_info->info.ai_family == AF_INET6)
           {
              if (!inet_pton(net_info->info.ai_family, net_info->ip,
                             &mreq6.ipv6mr_multiaddr))
-               {
-                  ecore_con_event_server_error(svr, strerror(errno));
-                  goto error;
-               }
+               goto error;
              mreq6.ipv6mr_interface = htonl(INADDR_ANY);
              if (setsockopt(svr->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                             (const void *)&mreq6, sizeof(mreq6)) != 0)
-               {
-                  ecore_con_event_server_error(svr, strerror(errno));
-                  goto error;
-               }
+               goto error;
           }
 #endif
      }
 
    if (setsockopt(svr->fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on)) != 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
-
-   if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
-
-   if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+     goto error;
+   if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
+   if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
 
    if (bind(svr->fd, net_info->info.ai_addr, net_info->info.ai_addrlen) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+     goto error;
 
    svr->fd_handler =
      ecore_main_fd_handler_add(svr->fd, ECORE_FD_READ,
                                _ecore_con_svr_udp_handler, svr, NULL, NULL);
    if (!svr->fd_handler)
      {
-        ecore_con_event_server_error(svr, "Memory allocation failure");
+        memerr = "Memory allocation failure";
         goto error;
      }
 
@@ -1569,6 +1560,7 @@ _ecore_con_cb_udp_listen(void           *data,
    return;
 
 error:
+   if (errno || memerr) ecore_con_event_server_error(svr, memerr ?: strerror(errno));
    ecore_con_ssl_server_shutdown(svr);
    _ecore_con_server_kill(svr);
 }
@@ -1580,37 +1572,23 @@ _ecore_con_cb_tcp_connect(void           *data,
    Ecore_Con_Server *svr;
    int res;
    int curstate = 0;
+   const char *memerr = NULL;
 
    svr = data;
 
+   errno = 0;
    if (!net_info) /* error message has already been handled */
      goto error;
 
    svr->fd = socket(net_info->info.ai_family, net_info->info.ai_socktype,
                     net_info->info.ai_protocol);
-   if (svr->fd < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+   if (svr->fd < 0) goto error;
 
-   if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
-
-   if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+   if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
+   if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
 
    if (setsockopt(svr->fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate, sizeof(curstate)) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+     goto error;
 
    if ((svr->type & ECORE_CON_TYPE) == ECORE_CON_REMOTE_NODELAY)
      {
@@ -1620,7 +1598,6 @@ _ecore_con_cb_tcp_connect(void           *data,
         if (setsockopt(svr->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) < 0)
 #endif
           {
-             ecore_con_event_server_error(svr, strerror(errno));
              goto error;
           }
      }
@@ -1630,17 +1607,19 @@ _ecore_con_cb_tcp_connect(void           *data,
    if (res == SOCKET_ERROR)
      {
         if (WSAGetLastError() != WSAEINPROGRESS)
-          goto error; /* FIXME: strerror on windows? */
+          {
+             char *err;
+             err = evil_format_message(WSAGetLastError());
+             _ecore_con_event_server_error(svr, err, EINA_FALSE);
+             ecore_con_ssl_server_shutdown(svr);
+             _ecore_con_server_kill(svr);
+             return;
+          }
 
 #else
    if (res < 0)
      {
-        if (errno != EINPROGRESS)
-          {
-             ecore_con_event_server_error(svr, strerror(errno));
-             goto error;
-          }
-
+        if (errno != EINPROGRESS) goto error;
 #endif
         svr->connecting = EINA_TRUE;
         svr->fd_handler =
@@ -1655,22 +1634,24 @@ _ecore_con_cb_tcp_connect(void           *data,
      {
         svr->handshaking = EINA_TRUE;
         svr->ssl_state = ECORE_CON_SSL_STATE_INIT;
-        DBG("beginning ssl handshake");
-        if (ecore_con_ssl_server_init(svr))
+        DBG("%s ssl handshake", svr->ecs_state ? "Queuing" : "Beginning");
+        if ((!svr->ecs_state) && ecore_con_ssl_server_init(svr))
           goto error;
      }
 
    if (!svr->fd_handler)
      {
-        ecore_con_event_server_error(svr, "Memory allocation failure");
+        memerr = "Memory allocation failure";
         goto error;
      }
 
-   svr->ip = eina_stringshare_add(net_info->ip);
+   if ((!svr->ecs) || (svr->ecs->lookup))
+     svr->ip = eina_stringshare_add(net_info->ip);
 
    return;
 
 error:
+   if (errno || memerr) ecore_con_event_server_error(svr, memerr ?: strerror(errno));
    ecore_con_ssl_server_shutdown(svr);
    _ecore_con_server_kill(svr);
 }
@@ -1682,68 +1663,50 @@ _ecore_con_cb_udp_connect(void           *data,
    Ecore_Con_Server *svr;
    int curstate = 0;
    int broadcast = 1;
+   const char *memerr = NULL;
    svr = data;
 
+   errno = 0;
    if (!net_info) /* error message has already been handled */
      goto error;
 
    svr->fd = socket(net_info->info.ai_family, net_info->info.ai_socktype,
                     net_info->info.ai_protocol);
-   if (svr->fd < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
-
-   if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
-
-   if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
-
+   if (svr->fd < 0) goto error;
+   if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
+   if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
    if ((svr->type & ECORE_CON_TYPE) == ECORE_CON_REMOTE_BROADCAST)
      {
         if (setsockopt(svr->fd, SOL_SOCKET, SO_BROADCAST,
                        (const void *)&broadcast,
                        sizeof(broadcast)) < 0)
           {
-             ecore_con_event_server_error(svr, strerror(errno));
              goto error;
           }
      }
-   else if (setsockopt(svr->fd, SOL_SOCKET, SO_REUSEADDR,
-                       (const void *)&curstate, sizeof(curstate)) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+   if (setsockopt(svr->fd, SOL_SOCKET, SO_REUSEADDR,
+                  (const void *)&curstate, sizeof(curstate)) < 0)
+     goto error;
 
    if (connect(svr->fd, net_info->info.ai_addr, net_info->info.ai_addrlen) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto error;
-     }
+     goto error;
 
    svr->fd_handler = ecore_main_fd_handler_add(svr->fd, ECORE_FD_READ | ECORE_FD_WRITE,
                                                _ecore_con_cl_udp_handler, svr, NULL, NULL);
 
    if (!svr->fd_handler)
      {
-        ecore_con_event_server_error(svr, "Memory allocation failure");
+        memerr = "Memory allocation failure";
         goto error;
      }
 
-   svr->ip = eina_stringshare_add(net_info->ip);
+   if ((!svr->ecs) || (svr->ecs->lookup))
+     svr->ip = eina_stringshare_add(net_info->ip);
 
    return;
 
 error:
+   if (errno || memerr) ecore_con_event_server_error(svr, memerr ?: strerror(errno));
    ecore_con_ssl_server_shutdown(svr);
    _ecore_con_server_kill(svr);
 }
@@ -1760,14 +1723,14 @@ svr_try_connect_plain(Ecore_Con_Server *svr)
    if (res == SOCKET_ERROR)
      so_err = WSAGetLastError();
 
-   if ((so_err == WSAEINPROGRESS) && !svr->dead)
+   if ((so_err == WSAEINPROGRESS) && !svr->delete_me)
      return ECORE_CON_INPROGRESS;
 
 #else
    if (res < 0)
      so_err = errno;
 
-   if ((so_err == EINPROGRESS) && !svr->dead)
+   if ((so_err == EINPROGRESS) && !svr->delete_me)
      return ECORE_CON_INPROGRESS;
 
 #endif
@@ -1783,15 +1746,19 @@ svr_try_connect_plain(Ecore_Con_Server *svr)
 
    if ((!svr->delete_me) && (!svr->handshaking) && svr->connecting)
      {
-         svr->connecting = EINA_FALSE;
-         svr->start_time = ecore_time_get();
-         ecore_con_event_server_add(svr);
+         if (svr->ecs)
+           {
+              if (ecore_con_socks_svr_init(svr))
+                return ECORE_CON_INPROGRESS;
+           }
+         else
+           ecore_con_event_server_add(svr);
      }
 
    if (svr->fd_handler && (!svr->buf))
      ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ);
 
-   if (!svr->dead)
+   if (!svr->delete_me)
      return ECORE_CON_CONNECTED;
    else
      return ECORE_CON_DISCONNECTED;
@@ -1843,9 +1810,10 @@ _ecore_con_svr_tcp_handler(void                        *data,
    Ecore_Con_Client *cl = NULL;
    unsigned char client_addr[256];
    unsigned int client_addr_len;
+   const char *clerr = NULL;
 
    svr = data;
-   if (svr->dead)
+   if (svr->delete_me)
      return ECORE_CALLBACK_RENEW;
 
    if (svr->delete_me)
@@ -1868,34 +1836,19 @@ _ecore_con_svr_tcp_handler(void                        *data,
    client_addr_len = sizeof(client_addr);
    memset(&client_addr, 0, client_addr_len);
    cl->fd = accept(svr->fd, (struct sockaddr *)&client_addr, (socklen_t *)&client_addr_len);
-   if (cl->fd < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto free_cl;
-     }
-
+   if (cl->fd < 0) goto error;
    if ((svr->client_limit >= 0) && (svr->reject_excess_clients) &&
        (svr->client_count >= (unsigned int)svr->client_limit))
      {
-        ecore_con_event_server_error(svr, "Maximum client limit reached");
-        goto close_fd;
+        clerr = "Maximum client limit reached";
+        goto error;
      }
 
-   if (fcntl(cl->fd, F_SETFL, O_NONBLOCK) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto close_fd;
-     }
-   if (fcntl(cl->fd, F_SETFD, FD_CLOEXEC) < 0)
-     {
-        ecore_con_event_server_error(svr, strerror(errno));
-        goto close_fd;
-     }
+   if (fcntl(cl->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
+   if (fcntl(cl->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
    cl->fd_handler = ecore_main_fd_handler_add(cl->fd, ECORE_FD_READ,
                                               _ecore_con_svr_cl_handler, cl, NULL, NULL);
-   if (!cl->fd_handler)
-     goto close_fd;
-
+   if (!cl->fd_handler) goto error;
    ECORE_MAGIC_SET(cl, ECORE_MAGIC_CON_CLIENT);
 
    if ((!svr->upgrade) && (svr->type & ECORE_CON_SSL))
@@ -1903,14 +1856,14 @@ _ecore_con_svr_tcp_handler(void                        *data,
         cl->handshaking = EINA_TRUE;
         cl->ssl_state = ECORE_CON_SSL_STATE_INIT;
         if (ecore_con_ssl_client_init(cl))
-          goto del_handler;
+          goto error;
      }
 
    cl->client_addr = malloc(client_addr_len);
    if (!cl->client_addr)
      {
-        ecore_con_event_server_error(svr, "Memory allocation failure when attempting to add a new client");
-        goto del_handler;
+        clerr = "Memory allocation failure when attempting to add a new client";
+        goto error;
      }
    cl->client_addr_len = client_addr_len;
    memcpy(cl->client_addr, &client_addr, client_addr_len);
@@ -1923,29 +1876,28 @@ _ecore_con_svr_tcp_handler(void                        *data,
 
    return ECORE_CALLBACK_RENEW;
 
- del_handler:
-   ecore_main_fd_handler_del(cl->fd_handler);
- close_fd:
-   close(cl->fd);
- free_cl:
+error:
+   if (cl->fd_handler) ecore_main_fd_handler_del(cl->fd_handler);
+   if (cl->fd >= 0) close(cl->fd);
    free(cl);
-
+   if (clerr || errno) ecore_con_event_server_error(svr, clerr ?: strerror(errno));
    return ECORE_CALLBACK_RENEW;
 }
 
 static void
 _ecore_con_cl_read(Ecore_Con_Server *svr)
 {
-   DBG("svr=%p", svr);
    int num = 0;
    Eina_Bool lost_server = EINA_TRUE;
    unsigned char buf[READBUFSIZ];
 
+   DBG("svr=%p", svr);
+
    /* only possible with non-ssl connections */
    if (svr->connecting && (svr_try_connect_plain(svr) != ECORE_CON_CONNECTED))
       return;
 
-   if (svr->handshaking)
+   if (svr->handshaking && (!svr->ecs_state))
      {
         DBG("Continuing ssl handshake");
         if (!ecore_con_ssl_server_init(svr))
@@ -1953,8 +1905,9 @@ _ecore_con_cl_read(Ecore_Con_Server *svr)
         _ecore_con_server_timer_update(svr);
      }
 
-   if (!(svr->type & ECORE_CON_SSL))
+   if (svr->ecs_state || !(svr->type & ECORE_CON_SSL))
      {
+        errno = 0;
         num = read(svr->fd, buf, sizeof(buf));
         /* 0 is not a valid return value for a tcp socket */
         if ((num > 0) || ((num < 0) && (errno == EAGAIN)))
@@ -1971,7 +1924,12 @@ _ecore_con_cl_read(Ecore_Con_Server *svr)
      }
 
    if ((!svr->delete_me) && (num > 0))
-     ecore_con_event_server_data(svr, buf, num, EINA_TRUE);
+     {
+        if (svr->ecs_state)
+          ecore_con_socks_read(svr, buf, num);
+        else
+          ecore_con_event_server_data(svr, buf, num, EINA_TRUE);
+     }
 
    if (lost_server)
       _ecore_con_server_kill(svr);
@@ -1985,7 +1943,7 @@ _ecore_con_cl_handler(void             *data,
    Eina_Bool want_read, want_write;
 
    svr = data;
-   if (svr->dead)
+   if (svr->delete_me)
      return ECORE_CALLBACK_RENEW;
 
    if (svr->delete_me)
@@ -1994,7 +1952,7 @@ _ecore_con_cl_handler(void             *data,
    want_read = ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ);
    want_write = ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE);
 
-   if (svr->handshaking && (want_read || want_write))
+   if ((!svr->ecs_state) && svr->handshaking && (want_read || want_write))
      {
         DBG("Continuing ssl handshake: preparing to %s...", want_read ? "read" : "write");
 #ifdef ISCOMFITOR
@@ -2010,20 +1968,25 @@ _ecore_con_cl_handler(void             *data,
           {
              ERR("ssl handshaking failed!");
              svr->handshaking = EINA_FALSE;
-
           }
         else if (!svr->ssl_state)
+          ecore_con_event_server_add(svr);
+        return ECORE_CALLBACK_RENEW;
+     }
+   if (svr->ecs && svr->ecs_state && (svr->ecs_state < ECORE_CON_SOCKS_STATE_READ) && (!svr->ecs_buf))
+     {
+        if (svr->ecs_state < ECORE_CON_SOCKS_STATE_INIT)
           {
-              svr->connecting = EINA_FALSE;
-              svr->start_time = ecore_time_get();
-              ecore_con_event_server_add(svr);
+             INF("PROXY STATE++");
+             svr->ecs_state++;
           }
+        if (ecore_con_socks_svr_init(svr)) return ECORE_CALLBACK_RENEW;
      }
-   else if (want_read)
+   if (want_read)
      _ecore_con_cl_read(svr);
    else if (want_write) /* only possible with non-ssl connections */
      {
-        if (svr->connecting && (!svr_try_connect_plain(svr)))
+        if (svr->connecting && (!svr_try_connect_plain(svr)) && (!svr->ecs_state))
           return ECORE_CALLBACK_RENEW;
 
         _ecore_con_server_flush(svr);
@@ -2045,7 +2008,7 @@ _ecore_con_cl_udp_handler(void             *data,
    want_write = ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE);
 
    svr = data;
-   if (svr->dead || svr->delete_me || ((!want_read) && (!want_write)))
+   if (svr->delete_me || svr->delete_me || ((!want_read) && (!want_write)))
      return ECORE_CALLBACK_RENEW;
 
    if (want_write)
@@ -2081,7 +2044,7 @@ _ecore_con_svr_udp_handler(void             *data,
 
    svr = data;
 
-   if (svr->delete_me || svr->dead)
+   if (svr->delete_me)
      return ECORE_CALLBACK_RENEW;
 
    if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE))
@@ -2111,10 +2074,7 @@ _ecore_con_svr_udp_handler(void             *data,
         ecore_con_event_server_error(svr, strerror(errno));
         if (!svr->delete_me)
           ecore_con_event_client_del(NULL);
-
-        svr->dead = EINA_TRUE;
-        INF("svr %p is dead", svr);
-        svr->fd_handler = NULL;
+        _ecore_con_server_kill(svr);
         return ECORE_CALLBACK_CANCEL;
      }
 
@@ -2183,19 +2143,7 @@ _ecore_con_svr_cl_read(Ecore_Con_Client *cl)
    if ((!cl->delete_me) && (num > 0))
      ecore_con_event_client_data(cl, buf, num, EINA_TRUE);
 
-   if (lost_client)
-     {
-        if (!cl->delete_me)
-          ecore_con_event_client_del(cl);
-        INF("Lost client %s", (cl->ip) ? cl->ip : "");
-        cl->dead = EINA_TRUE;
-        INF("cl %p is dead", cl);
-        if (cl->fd_handler)
-          ecore_main_fd_handler_del(cl->fd_handler);
-
-        cl->fd_handler = NULL;
-        return;
-     }
+   if (lost_client) _ecore_con_client_kill(cl);
 }
 
 static Eina_Bool
@@ -2205,9 +2153,6 @@ _ecore_con_svr_cl_handler(void             *data,
    Ecore_Con_Client *cl;
 
    cl = data;
-   if (cl->dead)
-     return ECORE_CALLBACK_RENEW;
-
    if (cl->delete_me)
      return ECORE_CALLBACK_RENEW;
 
@@ -2216,11 +2161,8 @@ _ecore_con_svr_cl_handler(void             *data,
         if (ecore_con_ssl_client_init(cl))
           {
              ERR("ssl handshaking failed!");
-             cl->handshaking = EINA_FALSE;
-             cl->dead = EINA_TRUE;
-             INF("cl %p is dead", cl);
-             INF("Lost client %s", (cl->ip) ? cl->ip : "");
-             ecore_con_event_client_del(cl);
+             _ecore_con_client_kill(cl);
+             return ECORE_CALLBACK_RENEW;
           }
         else if (!cl->ssl_state)
           ecore_con_event_client_add(cl);
@@ -2238,19 +2180,25 @@ static void
 _ecore_con_server_flush(Ecore_Con_Server *svr)
 {
    int count, num;
+   size_t buf_len, buf_offset;
+   const void *buf;
 
+   DBG("(svr=%p,buf=%p)", svr, svr->buf);
 #ifdef _WIN32
    if (ecore_con_local_win32_server_flush(svr))
      return;
 #endif
 
-   if ((!svr->buf) && svr->fd_handler)
+   if ((!svr->buf) && (!svr->ecs_buf) && svr->fd_handler)
      {
         ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ);
         return;
      }
 
-   num = eina_binbuf_length_get(svr->buf) - svr->write_buf_offset;
+   buf = svr->buf ? eina_binbuf_string_get(svr->buf) : eina_binbuf_string_get(svr->ecs_buf);
+   buf_len = svr->buf ? eina_binbuf_length_get(svr->buf) : eina_binbuf_length_get(svr->ecs_buf);
+   buf_offset = svr->buf ? svr->write_buf_offset : svr->ecs_buf_offset;
+   num = buf_len - buf_offset;
 
    /* check whether we need to write anything at all.
     * we must not write zero bytes with SSL_write() since it
@@ -2261,7 +2209,7 @@ _ecore_con_server_flush(Ecore_Con_Server *svr)
     */
    if (num <= 0) return;
 
-   if (svr->handshaking)
+   if ((!svr->ecs_state) && svr->handshaking)
      {
         DBG("Continuing ssl handshake");
         if (ecore_con_ssl_server_init(svr))
@@ -2270,10 +2218,10 @@ _ecore_con_server_flush(Ecore_Con_Server *svr)
         return;
      }
 
-   if (!(svr->type & ECORE_CON_SSL))
-     count = write(svr->fd, eina_binbuf_string_get(svr->buf) + svr->write_buf_offset, num);
+   if (svr->ecs_state || (!(svr->type & ECORE_CON_SSL)))
+     count = write(svr->fd, buf + buf_offset, num);
    else
-     count = ecore_con_ssl_server_write(svr, eina_binbuf_string_get(svr->buf) + svr->write_buf_offset, num);
+     count = ecore_con_ssl_server_write(svr, buf + buf_offset, num);
 
    if (count < 0)
      {
@@ -2285,13 +2233,37 @@ _ecore_con_server_flush(Ecore_Con_Server *svr)
          return;
      }
 
-   if (count) ecore_con_event_server_write(svr, count);
-   svr->write_buf_offset += count;
-   if (svr->write_buf_offset >= eina_binbuf_length_get(svr->buf))
+   if (count && (!svr->ecs_state)) ecore_con_event_server_write(svr, count);
+   if (svr->ecs_buf)
+     buf_offset = svr->ecs_buf_offset += count;
+   else
+     buf_offset = svr->write_buf_offset += count;
+   if (buf_offset >= buf_len)
      {
-        svr->write_buf_offset = 0;
-        eina_binbuf_free(svr->buf);
-        svr->buf = NULL;
+        if (svr->ecs_buf)
+          {
+             svr->ecs_buf_offset = 0;
+             eina_binbuf_free(svr->ecs_buf);
+             svr->ecs_buf = NULL;
+             INF("PROXY STATE++");
+             svr->ecs_state++;
+          }
+        else
+          {
+             svr->write_buf_offset = 0;
+             eina_binbuf_free(svr->buf);
+             svr->buf = NULL;
+#ifdef TCP_CORK
+             if ((svr->type & ECORE_CON_TYPE) == ECORE_CON_REMOTE_CORK)
+               {
+                  int state = 0;
+                  if (setsockopt(svr->fd, IPPROTO_TCP, TCP_CORK, (char *)&state, sizeof(int)) < 0)
+                    /* realistically this isn't anything serious so we can just log and continue */
+                    ERR("uncorking failed! %s", strerror(errno));
+               }
+#endif
+          }
+        
         if (svr->fd_handler)
           ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ);
      }
@@ -2338,14 +2310,7 @@ _ecore_con_client_flush(Ecore_Con_Client *cl)
         if ((errno != EAGAIN) && (errno != EINTR) && (!cl->delete_me))
           {
               ecore_con_event_client_error(cl, strerror(errno));
-              ecore_con_event_client_del(cl);
-              cl->dead = EINA_TRUE;
-              INF("cl %p is dead", cl);
-              INF("Lost client %s", (cl->ip) ? cl->ip : "");
-              if (cl->fd_handler)
-                ecore_main_fd_handler_del(cl->fd_handler);
-
-              cl->fd_handler = NULL;
+              _ecore_con_client_kill(cl);
           }
 
         return;
@@ -2358,6 +2323,15 @@ _ecore_con_client_flush(Ecore_Con_Client *cl)
         cl->buf_offset = 0;
         eina_binbuf_free(cl->buf);
         cl->buf = NULL;
+#ifdef TCP_CORK
+        if ((cl->host_server->type & ECORE_CON_TYPE) == ECORE_CON_REMOTE_CORK)
+          {
+             int state = 0;
+             if (setsockopt(cl->fd, IPPROTO_TCP, TCP_CORK, (char *)&state, sizeof(int)) < 0)
+               /* realistically this isn't anything serious so we can just log and continue */
+               ERR("uncorking failed! %s", strerror(errno));
+          }
+#endif
         if (cl->fd_handler)
           ecore_main_fd_handler_active_set(cl->fd_handler, ECORE_FD_READ);
      }
@@ -2407,7 +2381,7 @@ _ecore_con_event_client_del_free(Ecore_Con_Server *svr,
              if ((!svr->event_count) && (svr->delete_me))
                _ecore_con_server_free(svr);
           }
-        if ((!e->client->event_count) && (e->client->delete_me))
+        if (!e->client->event_count)
           ecore_con_client_del(e->client);
      }
    ecore_con_event_client_del_free(e);
@@ -2499,7 +2473,7 @@ _ecore_con_event_server_del_free(void *data __UNUSED__,
    if (e->server)
      {
         e->server->event_count = eina_list_remove(e->server->event_count, ev);
-        if ((!e->server->event_count) && (e->server->delete_me))
+        if (!e->server->event_count)
           _ecore_con_server_free(e->server);
      }
    ecore_con_event_server_del_free(e);
index 206948b..d922f20 100644 (file)
@@ -40,6 +40,7 @@ GENERIC_ALLOC_FREE(Ecore_Con_Event_Server_Add, ecore_con_event_server_add);
 GENERIC_ALLOC_FREE(Ecore_Con_Event_Server_Del, ecore_con_event_server_del);
 GENERIC_ALLOC_FREE(Ecore_Con_Event_Server_Write, ecore_con_event_server_write);
 GENERIC_ALLOC_FREE(Ecore_Con_Event_Server_Data, ecore_con_event_server_data);
+GENERIC_ALLOC_FREE(Ecore_Con_Event_Proxy_Bind, ecore_con_event_proxy_bind);
 
 static Ecore_Con_Mempool *mempool_array[] = {
   &ecore_con_event_client_add_mp,
@@ -51,7 +52,8 @@ static Ecore_Con_Mempool *mempool_array[] = {
   &ecore_con_event_server_add_mp,
   &ecore_con_event_server_del_mp,
   &ecore_con_event_server_write_mp,
-  &ecore_con_event_server_data_mp
+  &ecore_con_event_server_data_mp,
+  &ecore_con_event_proxy_bind_mp
 };
 
 void
index dd5a212..5dfe70b 100644 (file)
@@ -309,7 +309,7 @@ ecore_con_info_get(Ecore_Con_Server *svr,
         memcpy(&cares->hints, hints, sizeof(struct addrinfo));
      }
 
-   if (inet_pton(AF_INET, svr->name, &cares->addr.v4) == 1)
+   if (inet_pton(AF_INET, svr->ecs ? svr->ecs->ip : svr->name, &cares->addr.v4) == 1)
      {
         cares->byaddr = EINA_TRUE;
         cares->isv6 = EINA_FALSE;
@@ -320,7 +320,7 @@ ecore_con_info_get(Ecore_Con_Server *svr,
                            cares);
      }
 #ifdef HAVE_IPV6
-   else if (inet_pton(AF_INET6, svr->name, &cares->addr.v6) == 1)
+   else if (inet_pton(AF_INET6, svr->ecs ? svr->ecs->ip : svr->name, &cares->addr.v6) == 1)
      {
         cares->byaddr = EINA_TRUE;
         cares->isv6 = EINA_TRUE;
@@ -334,7 +334,7 @@ ecore_con_info_get(Ecore_Con_Server *svr,
    else
      {
         cares->byaddr = EINA_FALSE;
-        ares_gethostbyname(info_channel, svr->name, ai_family,
+        ares_gethostbyname(info_channel, svr->ecs ? svr->ecs->ip : svr->name, ai_family,
                            (ares_host_callback)_ecore_con_info_ares_host_cb,
                            cares);
      }
@@ -457,7 +457,7 @@ _ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg,
                 goto on_mem_error;
 
               addri->sin_family = AF_INET;
-              addri->sin_port = htons(arg->svr->port);
+              addri->sin_port = htons(arg->svr->ecs ? arg->svr->ecs->port : arg->svr->port);
 
               memcpy(&addri->sin_addr.s_addr,
                      hostent->h_addr_list[0], sizeof(struct in_addr));
@@ -477,7 +477,7 @@ _ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg,
                 goto on_mem_error;
 
               addri6->sin6_family = AF_INET6;
-              addri6->sin6_port = htons(arg->svr->port);
+              addri6->sin6_port = htons(arg->svr->ecs ? arg->svr->ecs->port : arg->svr->port);
               addri6->sin6_flowinfo = 0;
               addri6->sin6_scope_id = 0;
 
@@ -516,7 +516,7 @@ _ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg,
                      goto on_mem_error;
 
                    addri6->sin6_family = AF_INET6;
-                   addri6->sin6_port = htons(arg->svr->port);
+                   addri6->sin6_port = htons(arg->svr->ecs ? arg->svr->ecs->port : arg->svr->port);
                    addri6->sin6_flowinfo = 0;
                    addri6->sin6_scope_id = 0;
 
@@ -537,7 +537,7 @@ _ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg,
                      goto on_mem_error;
 
                    addri->sin_family = AF_INET;
-                   addri->sin_port = htons(arg->svr->port);
+                   addri->sin_port = htons(arg->svr->ecs ? arg->svr->ecs->port : arg->svr->port);
 
                    memcpy(&addri->sin_addr.s_addr,
                           &arg->addr.v4, sizeof(struct in_addr));
index 4ece6b0..f451f6a 100644 (file)
@@ -246,9 +246,9 @@ ecore_con_info_get(Ecore_Con_Server *svr,
         int canonname_len = 0;
         int err;
 
-        eina_convert_itoa(svr->port, service);
+        eina_convert_itoa(svr->ecs ? svr->ecs->port : svr->port, service);
         /* CHILD */
-        if (!getaddrinfo(svr->name, service, hints, &result) && result)
+        if (!getaddrinfo(svr->ecs ? svr->ecs->ip : svr->name, service, hints, &result) && result)
           {
              if (result->ai_canonname)
                canonname_len = strlen(result->ai_canonname) + 1;
index af7ffbd..33ee052 100644 (file)
@@ -56,7 +56,8 @@ extern int _ecore_con_log_dom;
 
 typedef struct _Ecore_Con_Lookup Ecore_Con_Lookup;
 typedef struct _Ecore_Con_Info Ecore_Con_Info;
-
+typedef struct Ecore_Con_Socks_v4 Ecore_Con_Socks_v4;
+typedef struct Ecore_Con_Socks_v5 Ecore_Con_Socks_v5;
 typedef void (*Ecore_Con_Info_Cb)(void *data, Ecore_Con_Info *infos);
 
 typedef enum _Ecore_Con_State
@@ -82,6 +83,14 @@ typedef enum _Ecore_Con_Ssl_Handshake
    ECORE_CON_SSL_STATE_INIT
 } Ecore_Con_Ssl_State;
 
+typedef enum Ecore_Con_Socks_State
+{
+   ECORE_CON_SOCKS_STATE_DONE = 0,
+   ECORE_CON_SOCKS_STATE_RESOLVED,
+   ECORE_CON_SOCKS_STATE_INIT,
+   ECORE_CON_SOCKS_STATE_READ
+} Ecore_Con_Socks_State;
+
 struct _Ecore_Con_Client
 {
    ECORE_MAGIC;
@@ -107,9 +116,8 @@ struct _Ecore_Con_Client
 #endif
    Ecore_Con_Ssl_State ssl_state;
    Eina_Bool handshaking : 1;
-   Eina_Bool upgrade : 1;
-   Eina_Bool dead : 1;
-   Eina_Bool delete_me : 1;
+   Eina_Bool upgrade : 1; /* STARTTLS queued */
+   Eina_Bool delete_me : 1; /* del event has been queued */
 };
 
 struct _Ecore_Con_Server
@@ -130,6 +138,18 @@ struct _Ecore_Con_Server
    Eina_List *event_count;
    int client_limit;
    pid_t ppid;
+   /* socks */
+   Ecore_Con_Socks *ecs;
+   Ecore_Con_Socks_State ecs_state;
+   int ecs_addrlen;
+   unsigned char ecs_addr[16];
+   unsigned int ecs_buf_offset;
+   Eina_Binbuf *ecs_buf;
+   Eina_Binbuf *ecs_recvbuf;
+   const char *proxyip;
+   int proxyport;
+   /* endsocks */
+   const char *verify_name;
 #if USE_GNUTLS
    gnutls_session_t session;
    gnutls_anon_client_credentials_t anoncred_c;
@@ -149,18 +169,17 @@ struct _Ecore_Con_Server
    double disconnect_time;
    double client_disconnect_time;
    const char *ip;
-   Eina_Bool dead : 1;
    Eina_Bool created : 1; /* EINA_TRUE if server is our listening server */
    Eina_Bool connecting : 1; /* EINA_FALSE if just initialized or connected */
    Eina_Bool handshaking : 1; /* EINA_TRUE if server is ssl handshaking */
-   Eina_Bool upgrade : 1;
+   Eina_Bool upgrade : 1;  /* STARTTLS queued */
    Eina_Bool ssl_prepared : 1;
    Eina_Bool use_cert : 1; /* EINA_TRUE if using certificate auth */
    Ecore_Con_Ssl_State ssl_state; /* current state of ssl handshake on the server */
    Eina_Bool verify : 1; /* EINA_TRUE if certificates will be verified */
    Eina_Bool verify_basic : 1; /* EINA_TRUE if certificates will be verified only against the hostname */
    Eina_Bool reject_excess_clients : 1;
-   Eina_Bool delete_me : 1;
+   Eina_Bool delete_me : 1; /* del event has been queued */
 #ifdef _WIN32
    Eina_Bool want_write : 1;
    Eina_Bool read_stop : 1;
@@ -182,6 +201,9 @@ struct _Ecore_Con_Url
    Eina_List *additional_headers;
    Eina_List *response_headers;
    const char *url;
+   long proxy_type;
+
+   Ecore_Timer *timer;
 
    Ecore_Con_Url_Time time_condition;
    double timestamp;
@@ -206,16 +228,67 @@ struct _Ecore_Con_Lookup
    const void *data;
 };
 
+#define ECORE_CON_SOCKS_CAST_ELSE(X) \
+   Ecore_Con_Socks_v4 *v4 = NULL; \
+   Ecore_Con_Socks_v5 *v5 = NULL; \
+   if ((X) && ((X)->version == 4)) \
+     v4 = (Ecore_Con_Socks_v4*)(X); \
+   else if ((X) && ((X)->version == 5)) \
+     v5 = (Ecore_Con_Socks_v5*)(X); \
+   else
+
+struct Ecore_Con_Socks
+{
+   unsigned char version;
+
+   const char *ip;
+   int port;
+   const char *username;
+   Eina_Bool lookup : 1;
+   Eina_Bool bind : 1;
+};
+
+struct Ecore_Con_Socks_v4
+{
+   unsigned char version;
+
+   const char *ip;
+   int port;
+   const char *username;
+   Eina_Bool lookup : 1;
+   Eina_Bool bind : 1;
+};
+
+struct Ecore_Con_Socks_v5
+{
+   unsigned char version;
+
+   const char *ip;
+   int port;
+   const char *username;
+   Eina_Bool lookup : 1;
+   Eina_Bool bind : 1;
+};
+
+extern Ecore_Con_Socks *_ecore_con_proxy_once;
+extern Ecore_Con_Socks *_ecore_con_proxy_global;
+void ecore_con_socks_init(void);
+void ecore_con_socks_shutdown(void);
+Eina_Bool ecore_con_socks_svr_init(Ecore_Con_Server *svr);
+void ecore_con_socks_read(Ecore_Con_Server *svr, unsigned char *buf, int num);
+void ecore_con_socks_dns_cb(const char *canonname, const char *ip, struct sockaddr *addr, int addrlen, Ecore_Con_Server *svr);
 /* from ecore_con.c */
 void ecore_con_server_infos_del(Ecore_Con_Server *svr, void *info);
+void ecore_con_event_proxy_bind(Ecore_Con_Server *svr);
 void ecore_con_event_server_data(Ecore_Con_Server *svr, unsigned char *buf, int num, Eina_Bool duplicate);
 void ecore_con_event_server_del(Ecore_Con_Server *svr);
-void ecore_con_event_server_error(Ecore_Con_Server *svr, const char *error);
+#define ecore_con_event_server_error(svr, error) _ecore_con_event_server_error((svr), (char*)(error), EINA_TRUE)
+void _ecore_con_event_server_error(Ecore_Con_Server *svr, char *error, Eina_Bool duplicate);
 void ecore_con_event_client_add(Ecore_Con_Client *cl);
 void ecore_con_event_client_data(Ecore_Con_Client *cl, unsigned char *buf, int num, Eina_Bool duplicate);
 void ecore_con_event_client_del(Ecore_Con_Client *cl);
 void ecore_con_event_client_error(Ecore_Con_Client *cl, const char *error);
-
+void _ecore_con_server_kill(Ecore_Con_Server *svr);
 /* from ecore_local_win32.c */
 #ifdef _WIN32
 Eina_Bool ecore_con_local_listen(Ecore_Con_Server *svr);
@@ -306,6 +379,7 @@ GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Add, ecore_con_event_server_add
 GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Del, ecore_con_event_server_del);
 GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Write, ecore_con_event_server_write);
 GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Data, ecore_con_event_server_data);
+GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Proxy_Bind, ecore_con_event_proxy_bind);
 
 void ecore_con_mempool_init(void);
 void ecore_con_mempool_shutdown(void);
diff --git a/src/lib/ecore_con/ecore_con_socks.c b/src/lib/ecore_con/ecore_con_socks.c
new file mode 100644 (file)
index 0000000..853c794
--- /dev/null
@@ -0,0 +1,486 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_NETINET_TCP_H
+# include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+# include <ws2tcpip.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+#define _ecore_con_server_kill(svr) do { \
+   DBG("KILL %p", (svr)); \
+   _ecore_con_server_kill((svr)); \
+} while (0)
+
+Eina_List *ecore_con_socks_proxies = NULL;
+
+static Ecore_Con_Socks *
+_ecore_con_socks_find(unsigned char version, const char *ip, int port, const char *username)
+{
+   Eina_List *l;
+   Ecore_Con_Socks *ecs;
+   if (!ecore_con_socks_proxies) return NULL;
+
+   EINA_LIST_FOREACH(ecore_con_socks_proxies, l, ecs)
+     {
+        if (ecs->version != version) continue;
+        if (strcmp(ecs->ip, ip)) continue;
+        if ((port != -1) && (port != ecs->port)) continue;
+        if (username && strcmp(ecs->username, username)) continue;
+        return ecs;
+     }
+   return NULL;
+}
+
+static void
+_ecore_con_socks_free(Ecore_Con_Socks *ecs)
+{
+   ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
+
+   if (_ecore_con_proxy_once == ecs) _ecore_con_proxy_once = NULL;
+   if (_ecore_con_proxy_global == ecs) _ecore_con_proxy_global = NULL;
+   eina_stringshare_del(ecs->ip);
+   eina_stringshare_del(ecs->username);
+   free(ecs);
+}
+/////////////////////////////////////////////////////////////////////////////////////
+void
+ecore_con_socks_shutdown(void)
+{
+   Ecore_Con_Socks *ecs;
+   EINA_LIST_FREE(ecore_con_socks_proxies, ecs)
+     _ecore_con_socks_free(ecs);
+   _ecore_con_proxy_once = NULL;
+   _ecore_con_proxy_global = NULL;
+}
+
+void
+ecore_con_socks_read(Ecore_Con_Server *svr, unsigned char *buf, int num)
+{
+   const unsigned char *data;
+   ECORE_CON_SOCKS_CAST_ELSE(svr->ecs) return;
+
+   if (svr->ecs_state != ECORE_CON_SOCKS_STATE_READ) return;
+
+   if (v4)
+     {
+        DBG("SOCKS: %d bytes", num);
+        if (num < 8)
+          {
+             if (!svr->ecs_recvbuf) svr->ecs_recvbuf = eina_binbuf_new();
+             if (!svr->ecs_recvbuf) goto error;
+             eina_binbuf_append_length(svr->ecs_recvbuf, buf, num);
+             /* the slowest connection on earth */
+             if (eina_binbuf_length_get(svr->ecs_recvbuf) != 8) return;
+             data = eina_binbuf_string_get(svr->ecs_recvbuf);
+          }
+        else if (num > 8) goto error;
+        else
+          data = buf;
+
+     /* http://ufasoft.com/doc/socks4_protocol.htm */
+        if (data[0]) goto error;
+        switch (data[1])
+          {
+           case 90:
+             /* success! */
+             break;
+           case 91:
+             ecore_con_event_server_error(svr, "proxy request rejected or failed");
+             goto error;
+           case 92:
+             ecore_con_event_server_error(svr, "proxying SOCKS server could not perform authentication");
+             goto error;
+           case 93:
+             ecore_con_event_server_error(svr, "proxy request authentication rejected");
+             goto error;
+           default:
+             ecore_con_event_server_error(svr, "garbage data from proxy");
+             goto error;
+          }
+        if (svr->ecs->bind)
+          {
+             unsigned int nport;
+             char naddr[IF_NAMESIZE];
+
+             memcpy(&nport, &data[2], 2);
+             svr->proxyport = ntohl(nport);
+
+             if (!inet_ntop(AF_INET, &data[4], naddr, sizeof(naddr))) goto error;
+             svr->proxyip = eina_stringshare_add(naddr);
+             ecore_con_event_proxy_bind(svr);
+          }
+        svr->ecs_state = ECORE_CON_SOCKS_STATE_DONE;
+        INF("PROXY CONNECTED");
+        if (svr->ecs_recvbuf) eina_binbuf_free(svr->ecs_recvbuf);
+        svr->ecs_recvbuf = NULL;
+        svr->ecs_buf_offset = svr->ecs_addrlen = 0;
+        memset(svr->ecs_addr, 0, sizeof(svr->ecs_addr));
+        if (!svr->ssl_state)
+          ecore_con_event_server_add(svr);
+        if (svr->ssl_state || (svr->buf && eina_binbuf_length_get(svr->buf)))
+          ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ | ECORE_FD_WRITE);
+     }
+   return;
+error:
+   _ecore_con_server_kill(svr);
+}
+
+Eina_Bool
+ecore_con_socks_svr_init(Ecore_Con_Server *svr)
+{
+   unsigned char *sbuf;
+   ECORE_CON_SOCKS_CAST_ELSE(svr->ecs) return EINA_FALSE;
+
+   if (!svr->ip) return EINA_FALSE;
+   if (svr->ecs_buf) return EINA_FALSE;
+   if (svr->ecs_state != ECORE_CON_SOCKS_STATE_INIT) return EINA_FALSE;
+   ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
+   if (v4)
+     {
+        size_t addrlen, buflen, ulen = 1;
+        addrlen = svr->ecs->lookup ? strlen(svr->name) + 1: 0;
+        if (svr->ecs->username) ulen += strlen(svr->ecs->username);
+        buflen = sizeof(char) * (8  + ulen + addrlen);
+        sbuf = malloc(buflen);
+        if (!sbuf)
+          {
+             ecore_con_event_server_error(svr, "Memory allocation failure!");
+             _ecore_con_server_kill(svr);
+             return EINA_FALSE;
+          }
+        /* http://en.wikipedia.org/wiki/SOCKS */
+        sbuf[0] = 4;
+        sbuf[1] = v4->bind ? 2 : 1;
+        sbuf[2] = svr->port >> 8;
+        sbuf[3] = svr->port & 0xff;
+        if (addrlen)
+          {
+             sbuf[4] = sbuf[5] = sbuf[6] = 0;
+             sbuf[7] = 1;
+          }
+        else
+          memcpy(sbuf + 4, svr->ecs_addr, 4);
+        if (svr->ecs->username)
+          memcpy(sbuf + 8, svr->ecs->username, ulen);
+        else
+          sbuf[8] = 0;
+        if (addrlen) memcpy(sbuf + 8 + ulen, svr->name, addrlen);
+        
+        svr->ecs_buf = eina_binbuf_manage_new_length(sbuf, buflen);
+     }
+   return EINA_TRUE;
+}
+
+void
+ecore_con_socks_dns_cb(const char *canonname __UNUSED__, const char *ip, struct sockaddr *addr, int addrlen, Ecore_Con_Server *svr)
+{
+   svr->ip = eina_stringshare_add(ip);
+   svr->ecs_addrlen = addrlen;
+   svr->ecs_state++;
+   if (addr->sa_family == AF_INET)
+     memcpy(svr->ecs_addr, &((struct sockaddr_in *)addr)->sin_addr.s_addr, 4);
+#ifdef HAVE_IPV6
+   else
+     memcpy(svr->ecs_addr, &((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, addrlen);
+#endif
+   ecore_con_socks_svr_init(svr);
+}
+
+void
+ecore_con_socks_init(void)
+{
+   const char *socks;
+   char *u, *h, *p, *l;
+   char buf[64];
+   int port, lookup = 0;
+   Ecore_Con_Socks *ecs;
+   unsigned char addr[sizeof(struct in_addr)];
+
+   /* ECORE_CON_SOCKS_V4=user@host:port:[1|0] */
+   socks = getenv("ECORE_CON_SOCKS_V4");
+   if ((!socks) || (!socks[0]) || (strlen(socks) > 64)) return;
+   strncpy(buf, socks, sizeof(buf));
+   h = strchr(buf, '@');
+   u = NULL;
+   /* username */
+   if (h && (h - buf > 0)) *h++ = 0, u = buf;
+   else h = buf;
+   
+   /* host ip; I ain't resolvin shit here */
+   p = strchr(h, ':');
+   if (!p) return;
+   *p++ = 0;
+   if (!inet_pton(AF_INET, h, addr)) return;
+   
+   errno = 0;
+   port = strtol(p, &l, 10);
+   if (errno || (port < 0) || (port > 65535)) return;
+   if (l && (l[0] == ':'))
+     lookup = (l[1] == '1');
+   ecs = ecore_con_socks4_remote_add(h, port, u);
+   if (!ecs) return;
+   ecore_con_socks4_lookup_set(ecs, lookup);
+   ecore_con_socks_apply_always(ecs);
+   INF("Added global proxy server %s%s%s:%d - DNS lookup %s",
+       u ?: "", u ? "@" : "", h, port, lookup ? "ENABLED" : "DISABLED");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @defgroup Ecore_Con_Socks_Group Ecore Connection SOCKS functions
+ * @{
+ */
+
+/**
+ * Add a SOCKS v4 proxy to the proxy list
+ *
+ * Use this to create (or return, if previously added) a SOCKS proxy
+ * object which can be used by any ecore_con servers.
+ * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
+ * @param port The port to connect to on the proxy
+ * @param username The username to use for the proxy (OPTIONAL)
+ * @return An allocated proxy object, or NULL on failure
+ * @note This object NEVER needs to be explicitly freed.
+ * @since 1.2
+ */
+EAPI Ecore_Con_Socks *
+ecore_con_socks4_remote_add(const char *ip, int port, const char *username)
+{
+   Ecore_Con_Socks *ecs;
+
+   if ((!ip) || (!ip[0]) || (port < 0) || (port > 65535)) return NULL;
+
+   ecs = _ecore_con_socks_find(4, ip, port, username);
+   if (ecs) return ecs;
+
+   ecs = calloc(1, sizeof(Ecore_Con_Socks_v4));
+   if (!ecs) return NULL;
+
+   ecs->version = 4;
+   ecs->ip = eina_stringshare_add(ip);
+   ecs->port = port;
+   ecs->username = eina_stringshare_add(username);
+   ecore_con_socks_proxies = eina_list_append(ecore_con_socks_proxies, ecs);
+   return ecs;
+}
+
+/**
+ * Set DNS lookup mode on an existing SOCKS v4 proxy
+ *
+ * According to RFC, SOCKS v4 does not require that a proxy perform
+ * its own DNS lookups for addresses. SOCKS v4a specifies the protocol
+ * for this. If you want to enable remote DNS lookup and are sure that your
+ * proxy supports it, use this function.
+ * @param ecs The proxy object
+ * @param enable If true, the proxy will perform the dns lookup
+ * @note By default, this setting is DISABLED.
+ * @since 1.2
+ */
+EAPI void
+ecore_con_socks4_lookup_set(Ecore_Con_Socks *ecs, Eina_Bool enable)
+{
+   ECORE_CON_SOCKS_CAST_ELSE(ecs) return;
+   if (v4) v4->lookup = !!enable;
+}
+
+/**
+ * Get DNS lookup mode on an existing SOCKS v4 proxy
+ *
+ * According to RFC, SOCKS v4 does not require that a proxy perform
+ * its own DNS lookups for addresses. SOCKS v4a specifies the protocol
+ * for this. This function returns whether lookups are enabled on a proxy object.
+ * @param ecs The proxy object
+ * @return If true, the proxy will perform the dns lookup
+ * @note By default, this setting is DISABLED.
+ * @since 1.2
+ */
+EAPI Eina_Bool
+ecore_con_socks4_lookup_get(Ecore_Con_Socks *ecs)
+{
+   ECORE_CON_SOCKS_CAST_ELSE(ecs) return EINA_FALSE;
+   return v4 ? v4->lookup : EINA_FALSE;
+}
+
+/**
+ * Find a SOCKS v4 proxy in the proxy list
+ *
+ * Use this to determine if a SOCKS proxy was previously added by checking
+ * the proxy list against the parameters given.
+ * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
+ * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
+ * @param username The username used for the proxy (OPTIONAL)
+ * @return true only if a proxy exists matching the given params
+ * @note This function matches slightly more loosely than ecore_con_socks4_remote_add(), and
+ * ecore_con_socks4_remote_add() should be used to return the actual object.
+ * @since 1.2
+ */
+EAPI Eina_Bool
+ecore_con_socks4_remote_exists(const char *ip, int port, const char *username)
+{
+   if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0])))
+     return EINA_FALSE;
+   return !!_ecore_con_socks_find(4, ip, port, username);
+}
+
+/**
+ * Remove a SOCKS v4 proxy from the proxy list and delete it
+ *
+ * Use this to remove a SOCKS proxy from the proxy list by checking
+ * the list against the parameters given. The proxy will then be deleted.
+ * @param ip The ip address of the proxy (NOT DOMAIN NAME. IP ADDRESS.)
+ * @param port The port to connect to on the proxy, or -1 to match the first proxy with @p ip
+ * @param username The username used for the proxy (OPTIONAL)
+ * @note This function matches in the same way as ecore_con_socks4_remote_exists().
+ * @warning Be aware that deleting a proxy which is being used WILL ruin your life.
+ * @since 1.2
+ */
+EAPI void
+ecore_con_socks4_remote_del(const char *ip, int port, const char *username)
+{
+   Ecore_Con_Socks_v4 *v4;
+
+   if ((!ip) || (!ip[0]) || (port < -1) || (port > 65535) || (username && (!username[0]))) return;
+   if (!ecore_con_socks_proxies) return;
+
+   v4 = (Ecore_Con_Socks_v4*)_ecore_con_socks_find(4, ip, port, username);
+   if (!v4) return;
+   ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, v4);
+   _ecore_con_socks_free((Ecore_Con_Socks*)v4);
+}
+
+/**
+ * Enable bind mode on a SOCKS proxy
+ *
+ * Use this function to enable binding a remote port for use with a remote server.
+ * For more information, see http://ufasoft.com/doc/socks4_protocol.htm
+ * @param ecs The proxy object
+ * @param is_bind If true, the connection established will be a port binding
+ * @warning Be aware that changing the operation mode of an active proxy may result in undefined behavior
+ * @since 1.2
+ */
+EAPI void
+ecore_con_socks_bind_set(Ecore_Con_Socks *ecs, Eina_Bool is_bind)
+{
+   EINA_SAFETY_ON_NULL_RETURN(ecs);
+   ecs->bind = !!is_bind;
+}
+
+/**
+ * Return bind mode of a SOCKS proxy
+ *
+ * Use this function to return bind mode of a proxy (binding a remote port for use with a remote server).
+ * For more information, see http://ufasoft.com/doc/socks4_protocol.htm
+ * @param ecs The proxy object
+ * @return If true, the connection established will be a port binding
+ * @since 1.2
+ */
+EAPI Eina_Bool
+ecore_con_socks_bind_get(Ecore_Con_Socks *ecs)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ecs, EINA_FALSE);
+   return ecs->bind;
+}
+
+EAPI unsigned int
+ecore_con_socks_version_get(Ecore_Con_Socks *ecs)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ecs, 0);
+   return ecs->version;
+}
+
+/**
+ * Remove a SOCKS v4 proxy from the proxy list and delete it
+ *
+ * Use this to remove a SOCKS proxy from the proxy list by directly deleting the object given.
+ * @param ecs The proxy object to delete
+ * @warning Be aware that deleting a proxy which is being used WILL ruin your life.
+ * @since 1.2
+ */
+EAPI void
+ecore_con_socks_remote_del(Ecore_Con_Socks *ecs)
+{
+   EINA_SAFETY_ON_NULL_RETURN(ecs);
+   if (!ecore_con_socks_proxies) return;
+
+   ecore_con_socks_proxies = eina_list_remove(ecore_con_socks_proxies, ecs);
+   _ecore_con_socks_free(ecs);
+}
+
+/**
+ * Set a proxy object to be used with the next server created with ecore_con_server_connect()
+ *
+ * This function sets a proxy for the next ecore_con connection. After the next server is created,
+ * the proxy will NEVER be applied again unless explicitly enabled.
+ * @param ecs The proxy object
+ * @see ecore_con_socks_apply_always()
+ * @since 1.2
+ */
+EAPI void
+ecore_con_socks_apply_once(Ecore_Con_Socks *ecs)
+{
+   _ecore_con_proxy_once = ecs;
+}
+
+/**
+ * Set a proxy object to be used with all servers created with ecore_con_server_connect()
+ *
+ * This function sets a proxy for all ecore_con connections. It will always be used.
+ * @param ecs The proxy object
+ * @see ecore_con_socks_apply_once()
+ * @since 1.2
+ * @note ecore-con supports setting this through environment variables like so:
+ *   ECORE_CON_SOCKS_V4=[user@]server:port:lookup
+ * user is the OPTIONAL string that would be passed to the proxy as the username
+ * server is the IP_ADDRESS of the proxy server
+ * port is the port to connect to on the proxy server
+ * lookup is 1 if the proxy should perform all DNS lookups, otherwise 0 or omitted
+ */
+EAPI void
+ecore_con_socks_apply_always(Ecore_Con_Socks *ecs)
+{
+   _ecore_con_proxy_global = ecs;
+}
+/** @} */
index 1ef92c5..374f722 100644 (file)
@@ -16,6 +16,7 @@
 # include <ws2tcpip.h>
 #endif
 
+#include <sys/stat.h>
 #include "Ecore.h"
 #include "ecore_con_private.h"
 
@@ -60,12 +61,34 @@ _gnutls_print_errors(void *conn, int type, int ret)
      ecore_con_event_server_error(conn, buf);
 }
 
+static void
+_gnutls_print_session(const gnutls_datum_t *cert_list, unsigned int cert_list_size)
+{
+   char *c = NULL;
+   gnutls_x509_crt_t crt;
+   unsigned int x;
+
+   if (!eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG)) return;
+   for (x = 0; x < cert_list_size; x++)
+     {
+        gnutls_x509_crt_init(&crt);
+        gnutls_x509_crt_import(crt, &cert_list[x], GNUTLS_X509_FMT_DER);
+        gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_FULL, (gnutls_datum_t*)&c);
+        INF("CERTIFICATE:\n%s", c);
+        gnutls_free(c);
+        gnutls_x509_crt_deinit(crt);
+        crt = NULL;
+     }
+}
+
 #ifdef ISCOMFITOR
 static void
 _gnutls_log_func(int         level,
                  const char *str)
 {
-   DBG("|<%d>| %s", level, str);
+   char buf[128];
+   strncat(buf, str, strlen(str) - 1);
+   DBG("|<%d>| %s", level, buf);
 }
 #endif
 
@@ -116,6 +139,185 @@ SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_description_t status)
 #elif USE_OPENSSL
 
 static void
+_openssl_print_verify_error(int error)
+{
+   switch (error)
+     {
+#define ERROR(X) \
+  case (X): \
+    ERR("%s", #X); \
+    break
+#ifdef X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
+      ERROR(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT);
+#endif
+#ifdef X509_V_ERR_UNABLE_TO_GET_CRL
+      ERROR(X509_V_ERR_UNABLE_TO_GET_CRL);
+#endif
+#ifdef X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE
+      ERROR(X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE);
+#endif
+#ifdef X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE
+      ERROR(X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE);
+#endif
+#ifdef X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
+      ERROR(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY);
+#endif
+#ifdef X509_V_ERR_CERT_SIGNATURE_FAILURE
+      ERROR(X509_V_ERR_CERT_SIGNATURE_FAILURE);
+#endif
+#ifdef X509_V_ERR_CRL_SIGNATURE_FAILURE
+      ERROR(X509_V_ERR_CRL_SIGNATURE_FAILURE);
+#endif
+#ifdef X509_V_ERR_CERT_NOT_YET_VALID
+      ERROR(X509_V_ERR_CERT_NOT_YET_VALID);
+#endif
+#ifdef X509_V_ERR_CERT_HAS_EXPIRED
+      ERROR(X509_V_ERR_CERT_HAS_EXPIRED);
+#endif
+#ifdef X509_V_ERR_CRL_NOT_YET_VALID
+      ERROR(X509_V_ERR_CRL_NOT_YET_VALID);
+#endif
+#ifdef X509_V_ERR_CRL_HAS_EXPIRED
+      ERROR(X509_V_ERR_CRL_HAS_EXPIRED);
+#endif
+#ifdef X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD
+      ERROR(X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD);
+#endif
+#ifdef X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD
+      ERROR(X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD);
+#endif
+#ifdef X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD
+      ERROR(X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD);
+#endif
+#ifdef X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD
+      ERROR(X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+#endif
+#ifdef X509_V_ERR_OUT_OF_MEM
+      ERROR(X509_V_ERR_OUT_OF_MEM);
+#endif
+#ifdef X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
+      ERROR(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
+#endif
+#ifdef X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
+      ERROR(X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN);
+#endif
+#ifdef X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
+      ERROR(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY);
+#endif
+#ifdef X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
+      ERROR(X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
+#endif
+#ifdef X509_V_ERR_CERT_CHAIN_TOO_LONG
+      ERROR(X509_V_ERR_CERT_CHAIN_TOO_LONG);
+#endif
+#ifdef X509_V_ERR_CERT_REVOKED
+      ERROR(X509_V_ERR_CERT_REVOKED);
+#endif
+#ifdef X509_V_ERR_INVALID_CA
+      ERROR(X509_V_ERR_INVALID_CA);
+#endif
+#ifdef X509_V_ERR_PATH_LENGTH_EXCEEDED
+      ERROR(X509_V_ERR_PATH_LENGTH_EXCEEDED);
+#endif
+#ifdef X509_V_ERR_INVALID_PURPOSE
+      ERROR(X509_V_ERR_INVALID_PURPOSE);
+#endif
+#ifdef X509_V_ERR_CERT_UNTRUSTED
+      ERROR(X509_V_ERR_CERT_UNTRUSTED);
+#endif
+#ifdef X509_V_ERR_CERT_REJECTED
+      ERROR(X509_V_ERR_CERT_REJECTED);
+#endif
+      /* These are 'informational' when looking for issuer cert */
+#ifdef X509_V_ERR_SUBJECT_ISSUER_MISMATCH
+      ERROR(X509_V_ERR_SUBJECT_ISSUER_MISMATCH);
+#endif
+#ifdef X509_V_ERR_AKID_SKID_MISMATCH
+      ERROR(X509_V_ERR_AKID_SKID_MISMATCH);
+#endif
+#ifdef X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH
+      ERROR(X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH);
+#endif
+#ifdef X509_V_ERR_KEYUSAGE_NO_CERTSIGN
+      ERROR(X509_V_ERR_KEYUSAGE_NO_CERTSIGN);
+#endif
+
+#ifdef X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER
+      ERROR(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER);
+#endif
+#ifdef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION
+      ERROR(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION);
+#endif
+#ifdef X509_V_ERR_KEYUSAGE_NO_CRL_SIGN
+      ERROR(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN);
+#endif
+#ifdef X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION
+      ERROR(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION);
+#endif
+#ifdef X509_V_ERR_INVALID_NON_CA
+      ERROR(X509_V_ERR_INVALID_NON_CA);
+#endif
+#ifdef X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED
+      ERROR(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED);
+#endif
+#ifdef X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE
+      ERROR(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE);
+#endif
+#ifdef X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED
+      ERROR(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED);
+#endif
+
+#ifdef X509_V_ERR_INVALID_EXTENSION
+      ERROR(X509_V_ERR_INVALID_EXTENSION);
+#endif
+#ifdef X509_V_ERR_INVALID_POLICY_EXTENSION
+      ERROR(X509_V_ERR_INVALID_POLICY_EXTENSION);
+#endif
+#ifdef X509_V_ERR_NO_EXPLICIT_POLICY
+      ERROR(X509_V_ERR_NO_EXPLICIT_POLICY);
+#endif
+#ifdef X509_V_ERR_DIFFERENT_CRL_SCOPE
+      ERROR(X509_V_ERR_DIFFERENT_CRL_SCOPE);
+#endif
+#ifdef X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE
+      ERROR(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE);
+#endif
+
+#ifdef X509_V_ERR_UNNESTED_RESOURCE
+      ERROR(X509_V_ERR_UNNESTED_RESOURCE);
+#endif
+
+#ifdef X509_V_ERR_PERMITTED_VIOLATION
+      ERROR(X509_V_ERR_PERMITTED_VIOLATION);
+#endif
+#ifdef X509_V_ERR_EXCLUDED_VIOLATION
+      ERROR(X509_V_ERR_EXCLUDED_VIOLATION);
+#endif
+#ifdef X509_V_ERR_SUBTREE_MINMAX
+      ERROR(X509_V_ERR_SUBTREE_MINMAX);
+#endif
+#ifdef X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE
+      ERROR(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE);
+#endif
+#ifdef X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX
+      ERROR(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX);
+#endif
+#ifdef X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
+      ERROR(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX);
+#endif
+#ifdef X509_V_ERR_CRL_PATH_VALIDATION_ERROR
+      ERROR(X509_V_ERR_CRL_PATH_VALIDATION_ERROR);
+#endif
+
+      /* The application is not happy */
+#ifdef X509_V_ERR_APPLICATION_VERIFICATION
+      ERROR(X509_V_ERR_APPLICATION_VERIFICATION);
+#endif
+     }
+#undef ERROR
+}
+
+static void
 _openssl_print_errors(void *conn, int type)
 {
    char buf[1024];
@@ -152,13 +354,57 @@ _openssl_name_verify(const char *name, const char *svrname)
         EINA_SAFETY_ON_TRUE_RETURN_VAL(!s, EINA_FALSE);
         /* same as above for the stored name */
         EINA_SAFETY_ON_TRUE_RETURN_VAL(!strchr(s + 1, '.'), EINA_FALSE);
-        EINA_SAFETY_ON_TRUE_RETURN_VAL(strcasecmp(s, name + 1), EINA_FALSE);
+        if (strcasecmp(s, name + 1))
+          {
+             ERR("%s != %s", s, name + 1);
+             return EINA_FALSE;
+          }
      }
    else
-     EINA_SAFETY_ON_TRUE_RETURN_VAL(strcasecmp(name, svrname), EINA_FALSE);
+     if (strcasecmp(name, svrname))
+       {
+          ERR("%s != %s", name, svrname);
+          return EINA_FALSE;
+       }
    return EINA_TRUE;
 }
 
+static void
+_openssl_print_session(SSL *ssl)
+{
+   /* print session info into DBG */
+   SSL_SESSION *s;
+   STACK_OF(X509) *sk;
+   BIO *b;
+   char log[4096], *p;
+   int x;
+
+   if (!eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG)) return;
+
+   memset(log, 0, sizeof(log));
+   b = BIO_new(BIO_s_mem());
+   sk = SSL_get_peer_cert_chain(ssl);
+   if (sk)
+     {
+        DBG("CERTIFICATES:");
+        for (x = 0; x < sk_X509_num(sk); x++)
+          {
+             p = X509_NAME_oneline(X509_get_subject_name(sk_X509_value(sk, x)), log, sizeof(log));
+             DBG("%2d s:%s", x, p);
+             p = X509_NAME_oneline(X509_get_issuer_name(sk_X509_value(sk, x)), log, sizeof(log));
+             DBG("   i:%s", p);
+             PEM_write_X509(stderr, sk_X509_value(sk, x));
+          }
+     }
+   s = SSL_get_session(ssl);
+   SSL_SESSION_print(b, s);
+   fprintf(stderr, "\n");
+   while (BIO_read(b, log, sizeof(log)) > 0)
+     fprintf(stderr, "%s", log);
+
+   BIO_free(b);
+}
+
 #endif
 
 #define SSL_ERROR_CHECK_GOTO_ERROR(X)                                           \
@@ -356,6 +602,51 @@ ecore_con_ssl_server_verify_basic(Ecore_Con_Server *svr)
 }
 
 /**
+ * @brief Set the hostname to verify against in certificate verification
+ *
+ * Sometimes the certificate hostname will not match the hostname that you are
+ * connecting to, and will instead match a different name. An example of this is
+ * that if you connect to talk.google.com to use Google Talk, you receive Google's
+ * certificate for gmail.com. This certificate should be trusted, and so you must call
+ * this function with "gmail.com" as @p name.
+ * See RFC2818 for more details.
+ * @param svr The server object
+ * @param name The hostname to verify against
+ * @since 1.2
+ */
+EAPI void
+ecore_con_ssl_server_verify_name_set(Ecore_Con_Server *svr, const char *name)
+{
+   if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
+     {
+        ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER, __func__);
+        return;
+     }
+   eina_stringshare_replace(&svr->verify_name, name);
+}
+
+/**
+ * @brief Get the hostname to verify against in certificate verification
+ *
+ * This function returns the name which will be used to validate the SSL certificate
+ * common name (CN) or alt name (subjectAltName). It will default to the @p name
+ * param in ecore_con_server_connect(), but can be changed with ecore_con_ssl_server_verify_name_set().
+ * @param svr The server object
+ * @return The hostname which will be used
+ * @since 1.2
+ */
+EAPI const char *
+ecore_con_ssl_server_verify_name_get(Ecore_Con_Server *svr)
+{
+   if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
+     {
+        ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER, __func__);
+        return NULL;
+     }
+   return svr->verify_name ?: svr->name;
+}
+
+/**
  * @brief Add an ssl certificate for use in ecore_con functions.
  *
  * Use this function to add a SSL PEM certificate.
@@ -375,6 +666,14 @@ ecore_con_ssl_server_cert_add(Ecore_Con_Server *svr,
         return EINA_FALSE;
      }
 
+   if (!svr->ssl_prepared)
+     {
+        svr->use_cert = EINA_TRUE;
+        svr->type |= ECORE_CON_USE_MIXED | ECORE_CON_LOAD_CERT;
+        if (ecore_con_ssl_server_prepare(svr, svr->type & ECORE_CON_SSL))
+          return EINA_FALSE;
+     }
+
    return SSL_SUFFIX(_ecore_con_ssl_server_cert_add) (svr, cert);
 }
 
@@ -386,6 +685,7 @@ ecore_con_ssl_server_cert_add(Ecore_Con_Server *svr,
  * If there is an error loading the CAs, an error will automatically be logged.
  * @param ca_file The path to the CA file.
  * @return EINA_FALSE if the file cannot be loaded, otherwise EINA_TRUE.
+ * @note since 1.2, this function can load directores
  */
 
 EAPI Eina_Bool
@@ -398,6 +698,14 @@ ecore_con_ssl_server_cafile_add(Ecore_Con_Server *svr,
         return EINA_FALSE;
      }
 
+   if (!svr->ssl_prepared)
+     {
+        svr->use_cert = EINA_TRUE;
+        svr->type |= ECORE_CON_USE_MIXED | ECORE_CON_LOAD_CERT;
+        if (ecore_con_ssl_server_prepare(svr, svr->type & ECORE_CON_SSL))
+          return EINA_FALSE;
+     }
+
    return SSL_SUFFIX(_ecore_con_ssl_server_cafile_add) (svr, ca_file);
 }
 
@@ -422,6 +730,14 @@ ecore_con_ssl_server_privkey_add(Ecore_Con_Server *svr,
         return EINA_FALSE;
      }
 
+   if (!svr->ssl_prepared)
+     {
+        svr->use_cert = EINA_TRUE;
+        svr->type |= ECORE_CON_USE_MIXED | ECORE_CON_LOAD_CERT;
+        if (ecore_con_ssl_server_prepare(svr, svr->type & ECORE_CON_SSL))
+          return EINA_FALSE;
+     }
+
    return SSL_SUFFIX(_ecore_con_ssl_server_privkey_add) (svr, key_file);
 }
 
@@ -446,6 +762,14 @@ ecore_con_ssl_server_crl_add(Ecore_Con_Server *svr,
         return EINA_FALSE;
      }
 
+   if (!svr->ssl_prepared)
+     {
+        svr->use_cert = EINA_TRUE;
+        svr->type |= ECORE_CON_USE_MIXED | ECORE_CON_LOAD_CERT;
+        if (ecore_con_ssl_server_prepare(svr, svr->type & ECORE_CON_SSL))
+          return EINA_FALSE;
+     }
+
    return SSL_SUFFIX(_ecore_con_ssl_server_crl_add) (svr, crl_file);
 }
 
@@ -480,7 +804,8 @@ ecore_con_ssl_server_upgrade(Ecore_Con_Server *svr, Ecore_Con_Type ssl_type)
         if (ecore_con_ssl_server_prepare(svr, ssl_type))
           return EINA_FALSE;
      }
-   svr->type |= ssl_type;
+   if (!svr->use_cert)
+     svr->type |= ssl_type;
    svr->upgrade = EINA_TRUE;
    svr->handshaking = EINA_TRUE;
    svr->ssl_state = ECORE_CON_SSL_STATE_INIT;
@@ -517,7 +842,8 @@ ecore_con_ssl_client_upgrade(Ecore_Con_Client *cl, Ecore_Con_Type ssl_type)
         if (ecore_con_ssl_server_prepare(cl->host_server, ssl_type))
           return EINA_FALSE;
      }
-   cl->host_server->type |= ssl_type;
+   if (!cl->host_server->use_cert)
+     cl->host_server->type |= ssl_type;
    cl->upgrade = EINA_TRUE;
    cl->host_server->upgrade = EINA_TRUE;
    cl->handshaking = EINA_TRUE;
@@ -546,8 +872,11 @@ _ecore_con_ssl_init_gnutls(void)
      return ECORE_CON_SSL_ERROR_INIT_FAILED;
 
 #ifdef ISCOMFITOR
-   gnutls_global_set_log_level(9);
-   gnutls_global_set_log_function(_gnutls_log_func);
+   if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+     {
+        gnutls_global_set_log_level(9);
+        gnutls_global_set_log_function(_gnutls_log_func);
+     }
 #endif
    return ECORE_CON_SSL_ERROR_NONE;
 }
@@ -728,10 +1057,12 @@ _ecore_con_ssl_server_init_gnutls(Ecore_Con_Server *svr)
    SSL_ERROR_CHECK_GOTO_ERROR(!(cert_list = gnutls_certificate_get_peers(svr->session, &cert_list_size)));
    SSL_ERROR_CHECK_GOTO_ERROR(!cert_list_size);
 
+   _gnutls_print_session(cert_list, cert_list_size);
+
    SSL_ERROR_CHECK_GOTO_ERROR(gnutls_x509_crt_init(&cert));
    SSL_ERROR_CHECK_GOTO_ERROR(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER));
 
-   SSL_ERROR_CHECK_GOTO_ERROR(!gnutls_x509_crt_check_hostname(cert, svr->name));
+   SSL_ERROR_CHECK_GOTO_ERROR(!gnutls_x509_crt_check_hostname(cert, svr->verify_name ?: svr->name));
    gnutls_x509_crt_deinit(cert);
    DBG("SSL certificate verification succeeded!");
    return ECORE_CON_SSL_ERROR_NONE;
@@ -755,10 +1086,32 @@ static Eina_Bool
 _ecore_con_ssl_server_cafile_add_gnutls(Ecore_Con_Server *svr,
                                         const char       *ca_file)
 {
-   SSL_ERROR_CHECK_GOTO_ERROR(gnutls_certificate_set_x509_trust_file(svr->cert, ca_file,
-                                                                     GNUTLS_X509_FMT_PEM) < 1);
+   struct stat st;
+   Eina_Iterator *it;
+   const char *file;
+   Eina_Bool error = EINA_FALSE;
 
-   return EINA_TRUE;
+   if (stat(ca_file, &st)) return EINA_FALSE;
+   if (S_ISDIR(st.st_mode))
+     {
+        it = eina_file_ls(ca_file);
+        SSL_ERROR_CHECK_GOTO_ERROR(!it);
+        EINA_ITERATOR_FOREACH(it, file)
+          {
+             if (!error)
+               {
+                  if (gnutls_certificate_set_x509_trust_file(svr->cert, file, GNUTLS_X509_FMT_PEM) < 1)
+                    error++;
+               }
+             eina_stringshare_del(file);
+          }
+        eina_iterator_free(it);
+     }
+   else
+     SSL_ERROR_CHECK_GOTO_ERROR(gnutls_certificate_set_x509_trust_file(svr->cert, ca_file,
+                                                                       GNUTLS_X509_FMT_PEM) < 1);
+
+   return !error;
 error:
    ERR("Could not load CA file!");
    return EINA_FALSE;
@@ -1026,6 +1379,7 @@ _ecore_con_ssl_client_init_gnutls(Ecore_Con_Client *cl)
    SSL_ERROR_CHECK_GOTO_ERROR(!(cert_list = gnutls_certificate_get_peers(cl->session, &cert_list_size)));
    SSL_ERROR_CHECK_GOTO_ERROR(!cert_list_size);
 
+   _gnutls_print_session(cert_list, cert_list_size);
 /*
    gnutls_x509_crt_t cert = NULL;
    SSL_ERROR_CHECK_GOTO_ERROR(gnutls_x509_crt_init(&cert));
@@ -1282,23 +1636,7 @@ _ecore_con_ssl_server_init_openssl(Ecore_Con_Server *svr)
         break;
      }
 
-#ifdef ISCOMFITOR
-   {
-      /* print session info into DBG */
-       SSL_SESSION *s;
-       BIO *b;
-       char log[4096];
-
-       memset(log, 0, sizeof(log));
-       s = SSL_get_session(svr->ssl);
-       b = BIO_new(BIO_s_mem());
-       SSL_SESSION_print(b, s);
-       while (BIO_read(b, log, sizeof(log)) > 0)
-         DBG("%s", log);
-
-       BIO_free(b);
-   }
-#endif
+   _openssl_print_session(svr->ssl);
    if ((!svr->verify) && (!svr->verify_basic))
      /* not verifying certificates, so we're done! */
      return ECORE_CON_SSL_ERROR_NONE;
@@ -1310,17 +1648,29 @@ _ecore_con_ssl_server_init_openssl(Ecore_Con_Server *svr)
       cert = SSL_get_peer_certificate(svr->ssl);
       if (cert)
         {
-           char buf[256] = {0};
+           char *c;
+           int clen;
+           int name = 0;
+
            if (svr->verify)
-             SSL_ERROR_CHECK_GOTO_ERROR(SSL_get_verify_result(svr->ssl));
-           X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_subject_alt_name, buf, sizeof(buf));
-           if (buf[0])
-             SSL_ERROR_CHECK_GOTO_ERROR(!_openssl_name_verify(buf, svr->name));
-           else
              {
-                X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, buf, sizeof(buf));
-                SSL_ERROR_CHECK_GOTO_ERROR(!_openssl_name_verify(buf, svr->name));
+               int err;
+
+               err = SSL_get_verify_result(svr->ssl);
+               _openssl_print_verify_error(err);
+               SSL_ERROR_CHECK_GOTO_ERROR(err);
              }
+           clen = X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_subject_alt_name, NULL, 0);
+           if (clen > 0)
+             name = NID_subject_alt_name;
+           else
+             clen = X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, NULL, 0);
+           SSL_ERROR_CHECK_GOTO_ERROR(clen < 1);
+           if (!name) name = NID_commonName;
+           c = alloca(++clen);
+           X509_NAME_get_text_by_NID(X509_get_subject_name(cert), name, c, clen);
+           INF("CERT NAME: %s\n", c);
+           SSL_ERROR_CHECK_GOTO_ERROR(!_openssl_name_verify(c, svr->verify_name ?: svr->name));
         }
    }
 
@@ -1338,7 +1688,13 @@ static Eina_Bool
 _ecore_con_ssl_server_cafile_add_openssl(Ecore_Con_Server *svr,
                                          const char       *ca_file)
 {
-   SSL_ERROR_CHECK_GOTO_ERROR(!SSL_CTX_load_verify_locations(svr->ssl_ctx, ca_file, NULL));
+   struct stat st;
+
+   if (stat(ca_file, &st)) return EINA_FALSE;
+   if (S_ISDIR(st.st_mode))
+     SSL_ERROR_CHECK_GOTO_ERROR(!SSL_CTX_load_verify_locations(svr->ssl_ctx, NULL, ca_file));
+   else
+     SSL_ERROR_CHECK_GOTO_ERROR(!SSL_CTX_load_verify_locations(svr->ssl_ctx, ca_file, NULL));
    return EINA_TRUE;
 
 error:
@@ -1543,31 +1899,20 @@ _ecore_con_ssl_client_init_openssl(Ecore_Con_Client *cl)
         break;
      }
 
-#ifdef ISCOMFITOR
-   {
-      /* print session info into DBG */
-       SSL_SESSION *s;
-       BIO *b;
-       char log[4096];
-
-       memset(log, 0, sizeof(log));
-       s = SSL_get_session(cl->ssl);
-       b = BIO_new(BIO_s_mem());
-       SSL_SESSION_print(b, s);
-       while (BIO_read(b, log, sizeof(log)) > 0)
-         DBG("%s", log);
-
-       BIO_free(b);
-   }
-#endif
-
+   _openssl_print_session(cl->ssl);
    if (!cl->host_server->verify)
      /* not verifying certificates, so we're done! */
      return ECORE_CON_SSL_ERROR_NONE;
    SSL_set_verify(cl->ssl, SSL_VERIFY_PEER, NULL);
    /* use CRL/CA lists to verify */
    if (SSL_get_peer_certificate(cl->ssl))
-     SSL_ERROR_CHECK_GOTO_ERROR(SSL_get_verify_result(cl->ssl));
+     {
+        int err;
+
+        err = SSL_get_verify_result(cl->ssl);
+        _openssl_print_verify_error(err);
+        SSL_ERROR_CHECK_GOTO_ERROR(err);
+     }
 
    return ECORE_CON_SSL_ERROR_NONE;
 
index cd614b2..959c0db 100644 (file)
@@ -57,6 +57,7 @@ static void      _ecore_con_event_url_free(void *data __UNUSED__,
                                            void      *ev);
 static Eina_Bool _ecore_con_url_idler_handler(void *data);
 static Eina_Bool _ecore_con_url_fd_handler(void *data __UNUSED__, Ecore_Fd_Handler *fd_handler __UNUSED__);
+static Eina_Bool _ecore_con_url_timeout_cb(void *data);
 
 static Eina_List *_url_con_list = NULL;
 static Eina_List *_fd_hd_list = NULL;
@@ -195,6 +196,24 @@ ecore_con_url_new(const char *url)
         return NULL;
      }
 
+   url_con->proxy_type = -1;
+   if (_ecore_con_proxy_global)
+     {
+        if (_ecore_con_proxy_global->ip)
+          {
+             char host[128];
+             if (_ecore_con_proxy_global->port > 0 &&
+                 _ecore_con_proxy_global->port <= 65535)
+                snprintf(host, sizeof(host), "socks4://%s:%d",
+                         _ecore_con_proxy_global->ip,
+                         _ecore_con_proxy_global->port);
+             else
+                snprintf(host, sizeof(host), "socks4://%s",
+                         _ecore_con_proxy_global->ip);
+                ecore_con_url_proxy_set(url_con, host);
+          }
+     }
+
    ret = curl_easy_setopt(url_con->curl_easy, CURLOPT_ENCODING, "gzip,deflate");
    if (ret != CURLE_OK)
      {
@@ -295,12 +314,13 @@ ecore_con_url_free(Ecore_Con_Url *url_con)
           {
              ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
              if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
+             _url_con_list = eina_list_remove(_url_con_list, url_con);
           }
 
         curl_easy_cleanup(url_con->curl_easy);
      }
+   if (url_con->timer) ecore_timer_del(url_con->timer);
 
-   _url_con_list = eina_list_remove(_url_con_list, url_con);
    curl_slist_free_all(url_con->headers);
    EINA_LIST_FREE(url_con->additional_headers, s)
      free(s);
@@ -1068,12 +1088,182 @@ ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con, const char *ca_path)
    return res;
 }
 
+EAPI Eina_Bool
+ecore_con_url_proxy_set(Ecore_Con_Url *url_con, const char *proxy)
+{
+#ifdef HAVE_CURL
+   int res = -1;
+   curl_version_info_data *vers = NULL;
+
+   if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
+     {
+        ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_proxy_set");
+        return EINA_FALSE;
+     }
+
+   if (eina_list_data_find(_url_con_list, url_con)) return EINA_FALSE;
+   if (!url_con->url) return EINA_FALSE;
+
+   if (!proxy) res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PROXY, "");
+   else
+     {
+        // before curl version 7.21.7, socks protocol:// prefix is not supported
+        // (e.g. socks4://, socks4a://, socks5:// or socks5h://, etc.)
+        vers = curl_version_info(CURLVERSION_NOW);
+        if (vers->version_num < 0x71507)
+          {
+             url_con->proxy_type = CURLPROXY_HTTP;
+             if (strstr(proxy, "socks4"))       url_con->proxy_type = CURLPROXY_SOCKS4;
+             else if (strstr(proxy, "socks4a")) url_con->proxy_type = CURLPROXY_SOCKS4A;
+             else if (strstr(proxy, "socks5"))  url_con->proxy_type = CURLPROXY_SOCKS5;
+             else if (strstr(proxy, "socks5h")) url_con->proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
+             res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PROXYTYPE, url_con->proxy_type);
+             if (res != CURLE_OK)
+               {
+                  ERR("curl proxy type setting failed: %s", curl_easy_strerror(res));
+                  url_con->proxy_type = -1;
+                  return EINA_FALSE;
+               }
+          }
+        res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PROXY, proxy);
+     }
+   if (res != CURLE_OK)
+     {
+        ERR("curl proxy setting failed: %s", curl_easy_strerror(res));
+        url_con->proxy_type = -1;
+        return EINA_FALSE;
+     }
+   return EINA_TRUE;
+#else
+   return EINA_FALSE;
+   (void)url_con;
+   (void)proxy;
+#endif
+}
+
+EAPI void
+ecore_con_url_timeout_set(Ecore_Con_Url *url_con, double timeout)
+{
+#ifdef HAVE_CURL
+   if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
+     {
+        ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_timeout_set");
+        return;
+     }
+
+   if (eina_list_data_find(_url_con_list, url_con)) return;
+   if (!url_con->url || timeout < 0) return;
+   if (url_con->timer) ecore_timer_del(url_con->timer);
+   url_con->timer = ecore_timer_add(timeout, _ecore_con_url_timeout_cb, url_con);
+#else
+   return;
+   (void)url_con;
+   (void)timeout;
+#endif
+}
+
+EAPI Eina_Bool
+ecore_con_url_proxy_username_set(Ecore_Con_Url *url_con, const char *username)
+{
+#ifdef HAVE_CURL
+   int res = -1;
+   if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
+     {
+        ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_proxy_username_set");
+        return EINA_FALSE;
+     }
+
+   if (eina_list_data_find(_url_con_list, url_con)) return EINA_FALSE;
+   if (!url_con->url) return EINA_FALSE;
+   if (!username) return EINA_FALSE;
+   if (url_con->proxy_type == CURLPROXY_SOCKS4 || url_con->proxy_type == CURLPROXY_SOCKS4A)
+     {
+        ERR("Proxy type should be socks5 and above");
+        return EINA_FALSE;
+     }
+
+   res = curl_easy_setopt(url_con->curl_easy, CURLOPT_USERNAME, username);
+   if (res != CURLE_OK)
+     {
+        ERR("curl_easy_setopt() failed: %s", curl_easy_strerror(res));
+        return EINA_FALSE;
+     }
+   return EINA_TRUE;
+#else
+   return EINA_FALSE;
+   (void)url_con;
+   (void)username;
+#endif
+}
+
+EAPI Eina_Bool
+ecore_con_url_proxy_password_set(Ecore_Con_Url *url_con, const char *password)
+{
+#ifdef HAVE_CURL
+   int res = -1;
+   if (!ECORE_MAGIC_CHECK(url_con, ECORE_MAGIC_CON_URL))
+     {
+        ECORE_MAGIC_FAIL(url_con, ECORE_MAGIC_CON_URL, "ecore_con_url_proxy_password_set");
+        return EINA_FALSE;
+     }
+   if (eina_list_data_find(_url_con_list, url_con)) return EINA_FALSE;
+   if (!url_con->url) return EINA_FALSE;
+   if (!password) return EINA_FALSE;
+   if (url_con->proxy_type == CURLPROXY_SOCKS4 || url_con->proxy_type == CURLPROXY_SOCKS4A)
+     {
+        ERR("Proxy type should be socks5 and above");
+        return EINA_FALSE;
+     }
+
+   res = curl_easy_setopt(url_con->curl_easy, CURLOPT_PASSWORD, password);
+   if (res != CURLE_OK)
+     {
+        ERR("curl_easy_setopt() failed: %s", curl_easy_strerror(res));
+        return EINA_FALSE;
+     }
+   return EINA_TRUE;
+#else
+   return EINA_FALSE;
+   (void)url_con;
+   (void)password;
+#endif
+}
 
 /**
  * @}
  */
 
 #ifdef HAVE_CURL
+static Eina_Bool
+_ecore_con_url_timeout_cb(void *data)
+{
+   Ecore_Con_Url *url_con = data;
+   CURLMcode ret;
+   Ecore_Con_Event_Url_Complete *e;
+
+   if (!url_con) return ECORE_CALLBACK_CANCEL;
+   if (!url_con->curl_easy) return ECORE_CALLBACK_CANCEL;
+   if (!eina_list_data_find(_url_con_list, url_con)) return ECORE_CALLBACK_CANCEL;
+
+   ret = curl_multi_remove_handle(_curlm, url_con->curl_easy);
+   if (ret != CURLM_OK) ERR("curl_multi_remove_handle failed: %s", curl_multi_strerror(ret));
+   _url_con_list = eina_list_remove(_url_con_list, url_con);
+
+   curl_slist_free_all(url_con->headers);
+   url_con->headers = NULL;
+
+   url_con->timer = NULL;
+
+   e = calloc(1, sizeof(Ecore_Con_Event_Url_Complete));
+   if (e)
+     {
+        e->url_con = url_con;
+        e->status = 0;
+        ecore_event_add(ECORE_CON_EVENT_URL_COMPLETE, e, _ecore_con_event_url_free, NULL);
+     }
+   return ECORE_CALLBACK_CANCEL;
+}
+
 static size_t
 _ecore_con_url_data_cb(void  *buffer,
                        size_t size,
index c7efc4d..c5e56bd 100644 (file)
@@ -2,6 +2,7 @@
 # include <config.h>
 #endif
 
+#include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -14,6 +15,7 @@
 #ifdef BUILD_ECORE_CON
 
 #define ECORE_MAGIC_FILE_DOWNLOAD_JOB 0xf7427cb8
+#define ECORE_FILE_DOWNLOAD_TIMEOUT 30
 
 struct _Ecore_File_Download_Job
 {
@@ -360,6 +362,7 @@ _ecore_file_download_curl(const char *url, const char *dst,
      }
 
    if (headers) eina_hash_foreach(headers, _ecore_file_download_headers_foreach_cb, job);
+   ecore_con_url_timeout_set(job->url_con, ECORE_FILE_DOWNLOAD_TIMEOUT);
    ecore_con_url_fd_set(job->url_con, fileno(job->file));
    ecore_con_url_data_set(job->url_con, data);