瀏覽代碼

Merge branch 'candidate-5.2.0'

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 10 年之前
父節點
當前提交
f7f67ac3be

+ 2 - 0
common/remote/CMakeLists.txt

@@ -55,6 +55,7 @@ include_directories (
          ./../../system/mp 
          ./../../system/include 
          ./../../system/jlib 
+         ./../../system/security/securesocket
     )
 
 ADD_DEFINITIONS( -D_USRDLL -DREMOTE_EXPORTS )
@@ -65,5 +66,6 @@ install ( TARGETS remote RUNTIME DESTINATION ${EXEC_DIR} LIBRARY DESTINATION ${L
 target_link_libraries ( remote 
     jlib 
     mp
+    securesocket
     ${URIPARSER_LIBRARIES}
     )

+ 15 - 2
common/remote/rmtfile.cpp

@@ -33,10 +33,23 @@
 
 //#define TEST_DAFILESRV_FOR_UNIX_PATHS     // probably not needed
 
+static class CSecuritySettings
+{
+    bool useSSL;
+    unsigned short daliServixPort;
+public:
+    CSecuritySettings()
+    {
+        querySecuritySettings(&useSSL, &daliServixPort, NULL, NULL);
+    }
+
+    unsigned short queryDaliServixPort() { return daliServixPort; }
+} securitySettings;
+
 
 unsigned short getDaliServixPort()
 {
-    return DAFILESRV_PORT;
+    return securitySettings.queryDaliServixPort();
 }
 
 
@@ -209,7 +222,7 @@ public:
         SocketEndpoint ep = filename.queryEndpoint();
         bool noport = (ep.port==0);
         setDafsEndpointPort(ep);
-        if (!filename.isLocal()||(ep.port!=DAFILESRV_PORT)) // assume standard port is running on local machine
+        if (!filename.isLocal()||(ep.port!=DAFILESRV_PORT && ep.port!=SECURE_DAFILESRV_PORT)) // assume standard port is running on local machine
         {
 #ifdef __linux__
 #ifndef USE_SAMBA

+ 144 - 9
common/remote/sockfile.cpp

@@ -29,6 +29,7 @@
 #include "jmisc.hpp"
 #include "jthread.hpp"
 
+#include "securesocket.hpp"
 #include "sockfile.hpp"
 #include "portlist.h"
 #include "jsocket.hpp"
@@ -173,6 +174,37 @@ typedef int RemoteFileIOHandle;
 static unsigned maxConnectTime = 0;
 static unsigned maxReceiveTime = 0;
 
+//Security and default port attributes
+#define PORT_UNKNOWN -1
+static unsigned short configepPort = PORT_UNKNOWN;
+static struct _ClientCertificate
+{
+    const char * certificate;
+    const char * privateKey;
+} clientCertificate;
+
+static CriticalSection              secureContextCrit;
+static Owned<ISecureSocketContext>  secureContext;
+
+static ISecureSocket *createSecureSocket(ISocket *sock,SecureSocketType type)
+{
+    {
+        CriticalBlock b(secureContextCrit);
+        if (!secureContext)
+        {
+            if (clientCertificate.certificate)
+                secureContext.setown(createSecureSocketContextEx(clientCertificate.certificate,clientCertificate.privateKey, NULL, type));
+            else
+                secureContext.setown(createSecureSocketContext(type));
+        }
+    }
+#ifdef _DEBUG
+    return secureContext->createSecureSocket(sock, SSLogMax);
+#else
+    return secureContext->createSecureSocket(sock);
+#endif
+}
+
 void clientSetRemoteFileTimeouts(unsigned maxconnecttime,unsigned maxreadtime)
 {
     maxConnectTime = maxconnecttime;
@@ -400,7 +432,11 @@ void setDafsEndpointPort(SocketEndpoint &ep)
         }
     }
     if (ep.port==0)
-        ep.port = DAFILESRV_PORT;
+    {
+        if (configepPort == (unsigned short)PORT_UNKNOWN)
+            querySecuritySettings(NULL, &configepPort, &clientCertificate.certificate, &clientCertificate.privateKey);
+        ep.port = configepPort;
+    }
 }
 
 
@@ -948,9 +984,14 @@ class CRemoteBase: public CInterface
     Owned<ISocket>          socket;
     static  SocketEndpoint  lastfailep;
     static unsigned         lastfailtime;
-
+    bool                    useSSL;
     void connectSocket(SocketEndpoint &ep)
     {
+#ifdef _DEBUG
+        StringBuffer sb;
+        ep.getUrlStr(sb);
+        DBGLOG("Client connecting %sto dafilesrv %s", useSSL?"SECURE ":"", sb.str());
+#endif
         sRFTM tm;
         // called in CConnectionTable::crit
         unsigned retries = 3;
@@ -968,7 +1009,7 @@ class CRemoteBase: public CInterface
             StringBuffer eps;
             if (TF_TRACE_CLIENT_CONN) {
                 ep.getUrlStr(eps);
-                PROGLOG("Connecting to %s",eps.str());
+                PROGLOG("Connecting %sto %s",useSSL?"SECURE ":"",eps.str());
                 //PrintStackReport();
             }
             bool ok = true;
@@ -980,6 +1021,14 @@ class CRemoteBase: public CInterface
                 }
                 else
                     socket.setown(ISocket::connect(ep));
+                if (useSSL)
+                {
+                    Owned<ISecureSocket> ssock = createSecureSocket(socket.getClear(), ClientSocket);
+                    int status = ssock->secure_connect();
+                    if (status < 0)
+                        throw createDafsException(DAFSERR_connection_failed,"Failure to establish secure connection");
+                    socket.setown(ssock.getLink());
+                }
             } 
             catch (IJSOCK_Exception *e) {
                 ok = false;
@@ -1026,7 +1075,7 @@ class CRemoteBase: public CInterface
                     sleeptime = remaining/2;
             }
             Sleep(sleeptime);       // prevent multiple retries beating
-            PROGLOG("Retrying connect");
+            PROGLOG("Retrying %sconnect",useSSL?"SECURE ":"");
         }  
         if (ConnectionTable)
             ConnectionTable->addLink(ep,socket);
@@ -1114,7 +1163,19 @@ protected: friend class CRemoteFileIO;
                 }
             }
             if (!socket) {
-                connectSocket(tep);
+                if (useSSL) {
+                    try {
+                        connectSocket(tep);//first try secure connect
+                    }
+                    catch(...) {
+                        useSSL = false;
+                        tep.port = DAFILESRV_PORT;
+                        PROGLOG("Secure connect failed, retrying on legacy port");
+                        connectSocket(tep);
+                    }
+                }
+                else
+                    connectSocket(tep);
             }
 
         }
@@ -1256,6 +1317,8 @@ public:
         : filename(_filename)
     {
         ep = _ep;
+        //Determine whether or not client should use SSL
+        querySecuritySettings(&useSSL, &configepPort, &clientCertificate.certificate, &clientCertificate.privateKey);
     }
 
 
@@ -2960,6 +3023,7 @@ class CRemoteFileServer : public CInterface, implements IRemoteFileServer, imple
     int                 lasthandle;
     CriticalSection     sect;
     Owned<ISocket>      acceptsock;
+    Owned<ISocket>      rejectsock;
     Owned<ISocketSelectHandler> selecthandler;
     Owned<IThreadPool>  threads;    // for commands
     bool stopping;
@@ -3020,7 +3084,7 @@ class CRemoteFileServer : public CInterface, implements IRemoteFileServer, imple
 
 
         IMPLEMENT_IINTERFACE;
-        
+
         CRemoteClientHandler(CRemoteFileServer *_parent,ISocket *_socket,IAuthenticatedUser *_user,atomic_t &_globallasttick)
             : socket(_socket), user(_user), globallasttick(_globallasttick)
         {
@@ -4149,6 +4213,8 @@ public:
         stopping = true;
         if (acceptsock) 
             acceptsock->cancel_accept();
+        if (rejectsock)
+            rejectsock->cancel_accept();
         reply.append((unsigned)RFEnoerror);
         return false;
     }
@@ -4355,7 +4421,7 @@ public:
     }
 
 
-    void run(SocketEndpoint &listenep)
+    void run(SocketEndpoint &listenep, bool useSSL)
     {
         if (listenep.isNull())
             acceptsock.setown(ISocket::create(listenep.port));
@@ -4365,12 +4431,71 @@ public:
             listenep.getIpText(ips);
             acceptsock.setown(ISocket::create_ip(listenep.port,ips.str()));
         }
+        if (useSSL) {
+            querySecuritySettings(&useSSL, &configepPort, &clientCertificate.certificate, &clientCertificate.privateKey);
+            if (!clientCertificate.certificate)
+                throw createDafsException(DAFSERR_connection_failed,"SSL Certificate information not found in environment.conf");
+            if (listenep.port <= 0)
+            {
+                assertex(FALSE);
+                listenep.port = configepPort;
+            }
+            //Create unsecure socket to reject non-ssl client requests
+            if (listenep.isNull())
+                rejectsock.setown(ISocket::create(DAFILESRV_PORT));
+            else {
+                StringBuffer ips;
+                listenep.getIpText(ips);
+                rejectsock.setown(ISocket::create_ip(DAFILESRV_PORT,ips.str()));
+            }
+        }
+#ifdef _DEBUG
+        StringBuffer sb;
+        listenep.getUrlStr(sb);
+        DBGLOG("Server accepting %sfrom %s", useSSL?"SECURE ":"", sb.str());
+#endif
         selecthandler->start();
+
+        UnsignedArray readSocks;
+        if (useSSL) {
+            readSocks.append(acceptsock->OShandle());
+            readSocks.append(rejectsock->OShandle());
+        }
+
         loop {
             Owned<ISocket> sock;
-            bool sockavail;
+            bool sockavail = false;
             try {
-                sockavail = acceptsock->wait_read(1000*60*1)!=0;
+                if (!useSSL)
+                    sockavail = acceptsock->wait_read(1000*60*1)!=0;
+                else
+                {
+                    UnsignedArray waitingSocks;
+                    //SSL Enabled. Listen for non SSL connection on DAFILESRV_PORT and reject them
+                    int numReady = wait_read_multiple(readSocks, 1000*60*1, waitingSocks);
+                    if (numReady)
+                    {
+                        for (int idx = 0; idx < numReady; idx++)
+                        {
+                            if (waitingSocks.item(idx) == rejectsock->OShandle())
+                            {
+                                //Unsecure connection attemped, reject !
+                                Owned<ISocket> s;
+                                s.setown(rejectsock->accept(true));
+                                IpAddress ip;
+                                StringBuffer sb;
+                                s->getPeerAddress(ip);
+                                ip.getIpText(sb);
+                                DBGLOG("Rejecting nonsecure connect from %s",sb.str());
+                                s->close();
+                            }
+                            else
+                            {
+                                sockavail = true;
+                            }
+                        }
+                    }
+                }
 #if 0
                 if (!sockavail) {
                     JSocketStatistics stats;
@@ -4392,6 +4517,14 @@ public:
             if (sockavail) {
                 try {
                     sock.setown(acceptsock->accept(true));
+                    if (useSSL)
+                    {
+                        Owned<ISecureSocket> ssock = createSecureSocket(sock.getClear(), ServerSocket);
+                        int status = ssock->secure_accept();
+                        if (status < 0)
+                            throw createDafsException(DAFSERR_connection_failed,"Failure to establish secure connection");
+                        sock.setown(ssock.getLink());
+                    }
                     if (!sock||stopping)
                         break;
                 }
@@ -4524,6 +4657,8 @@ public:
             PROGLOG("CRemoteFileServer::stop");
         if (acceptsock) 
             acceptsock->cancel_accept();
+        if (rejectsock)
+            rejectsock->cancel_accept();
         threads->stopAll();
         threads->joinAll(true,60*1000);
     }

+ 1 - 1
common/remote/sockfile.hpp

@@ -32,7 +32,7 @@
 interface IRemoteFileServer : public IInterface
 {
 public:
-    virtual void run(SocketEndpoint &listenep) = 0;
+    virtual void run(SocketEndpoint &listenep, bool useSSL = false) = 0;
     virtual void stop() = 0;
     virtual unsigned idleTime() = 0; // in ms
 };

+ 1 - 1
dali/base/dafdesc.cpp

@@ -2900,7 +2900,7 @@ IFileDescriptor *createFileDescriptorFromRoxieXML(IPropertyTree *tree,const char
                 ForEachItemIn(d,locdirs) {
                     if (strcmp(rfn.getLocalPath(locpath.clear()).str(),locdirs.item(d))==0) {
                         SocketEndpoint ep = rfn.queryEndpoint();
-                        if (ep.port==DAFILESRV_PORT)
+                        if (ep.port==DAFILESRV_PORT || ep.port==SECURE_DAFILESRV_PORT)
                             ep.port = 0;
                         epa[d].append(ep);
                         found = true;

+ 35 - 10
dali/dafilesrv/dafilesrv.cpp

@@ -36,7 +36,7 @@
 void usage()
 {
     printf("dafilesrv usage:\n");
-    printf("    dafilesrv -T<n> <port> [<send-buff-size-kb> <recv-buff-size-kb>]\n");
+    printf("    dafilesrv -T<n> <port> <-NOSSL> [<send-buff-size-kb> <recv-buff-size-kb>]\n");
     printf("                                                  -- run test local\n");
     printf("    dafilesrv -D [ -L <log-dir> ] [ -LOCAL ]      -- run as linux daemon\n");
     printf("    dafilesrv -R                                  -- run remote (linux daemon, windows standalone)\n");
@@ -45,7 +45,9 @@ void usage()
     
     printf("add -A to enable authentication to the above \n\n");
     printf("add -I <instance name>  to specify an instance name\n\n");
+    printf("add -NOSSL to disable SSL sockets, even when specified in configuration\n\n");
     printf("Standard port is %d\n",DAFILESRV_PORT);
+    printf("Standard SSL port is %d (certificate specs required in environment.conf)\n",SECURE_DAFILESRV_PORT);
     printf("Version:  %s\n\n",remoteServerVersionString());
 }
 
@@ -345,6 +347,13 @@ int main(int argc,char **argv)
     bool requireauthenticate = false;
     StringBuffer logDir;
     StringBuffer instanceName;
+
+    //Get SSL Settings
+    const char *    sslCertFile;
+    bool            useSSL;
+    unsigned short  dafsPort;//DAFILESRV_PORT or SECURE_DAFILESRV_PORT
+    querySecuritySettings(&useSSL, &dafsPort, &sslCertFile, NULL);
+
     while (argc>i) {
         if (stricmp(argv[i],"-D")==0) {
             i++;
@@ -380,10 +389,25 @@ int main(int argc,char **argv)
             i++;
             locallisten = true;
         }
+        else if (stricmp(argv[i],"-NOSSL")==0) {//overrides config setting
+            i++;
+            if (useSSL)
+            {
+                PROGLOG("DaFileSrv SSL specified in config but overridden by -NOSSL in command line");
+                useSSL = false;
+                dafsPort = DAFILESRV_PORT;
+            }
+        }
         else
             break;
     }
 
+    if (useSSL && !sslCertFile)
+    {
+        ERRLOG("DaFileSrv SSL specified but certificate file information missing from environment.conf");
+        exit(-1);
+    }
+
     if (0 == logDir.length())
     {
         getConfigurationDirectory(NULL,"log","dafilesrv",instanceName.str(),logDir);
@@ -413,10 +437,10 @@ int main(int argc,char **argv)
     }
 #endif
     if (argc == i)
-      listenep.port = DAFILESRV_PORT;
+        listenep.port = dafsPort;
     else {
         if (strchr(argv[i],'.')||!isdigit(argv[i][0]))
-            listenep.set(argv[i],DAFILESRV_PORT);
+            listenep.set(argv[i], dafsPort);
         else
             listenep.port = atoi(argv[i]);
         if (listenep.port==0) {
@@ -433,6 +457,7 @@ int main(int argc,char **argv)
             bool stopped;
             bool started;
             SocketEndpoint listenep;
+            bool useSSL;
             bool requireauthenticate;
 
             
@@ -456,8 +481,8 @@ int main(int argc,char **argv)
 
         public:
 
-            cserv(SocketEndpoint _listenep) 
-                : listenep(_listenep),pollthread(this)
+            cserv(SocketEndpoint _listenep, bool _useSSL)
+                : listenep(_listenep),useSSL(_useSSL),pollthread(this)
             {
                 stopped = false;
                 started = false;
@@ -518,14 +543,14 @@ int main(int argc,char **argv)
                 else
                     listenep.getUrlStr(eps);
                 enableDafsAuthentication(requireauthenticate!=0);
-                PROGLOG("Opening "DAFS_SERVICE_DISPLAY_NAME" on %s", eps.str());
+                PROGLOG("Opening "DAFS_SERVICE_DISPLAY_NAME" on %s%s", useSSL?"SECURE ":"",eps.str());
                 const char * verstring = remoteServerVersionString();
                 PROGLOG("Version: %s", verstring);
                 PROGLOG("Authentication:%s required",requireauthenticate?"":" not");
                 PROGLOG(DAFS_SERVICE_DISPLAY_NAME " Running");
                 server.setown(createRemoteFileServer());
                 try {
-                    server->run(listenep);
+                    server->run(listenep, useSSL);
                 }
                 catch (IException *e) {
                     EXCLOG(e,DAFS_SERVICE_NAME);
@@ -534,7 +559,7 @@ int main(int argc,char **argv)
                 PROGLOG(DAFS_SERVICE_DISPLAY_NAME " Stopped");
                 stopped = true;
             }
-        } service(listenep);
+        } service(listenep, useSSL);
         service.start();
         return 0;
 #else
@@ -556,14 +581,14 @@ int main(int argc,char **argv)
     else
         listenep.getUrlStr(eps);
     enableDafsAuthentication(requireauthenticate);
-    PROGLOG("Opening Dali File Server on %s", eps.str());
+    PROGLOG("Opening Dali File Server on %s%s", useSSL?"SECURE ":"",eps.str());
     PROGLOG("Version: %s", verstring);
     PROGLOG("Authentication:%s required",requireauthenticate?"":" not");
     startPerformanceMonitor(10*60*1000, PerfMonStandard);
     server.setown(createRemoteFileServer());
     writeSentinelFile(sentinelFile);
     try {
-        server->run(listenep);
+        server->run(listenep, useSSL);
     }
     catch (IException *e) {
         EXCLOG(e,"DAFILESRV");

+ 18 - 4
dali/dfuplus/dfuplus.cpp

@@ -32,6 +32,20 @@
 #include "rmtfile.hpp"
 #include "sockfile.hpp"
 
+
+static class CSecuritySettings
+{
+    bool useSSL;
+    unsigned short daliServixPort;
+public:
+    CSecuritySettings()
+    {
+        querySecuritySettings(&useSSL, &daliServixPort, NULL, NULL);
+    }
+
+    unsigned short queryDaliServixPort() { return daliServixPort; }
+} securitySettings;
+
 class CDafsThread: public Thread
 {
     Owned<IRemoteFileServer> server;
@@ -43,7 +57,7 @@ public:
         : listenep(_listenep)
     {
         if (listenep.port==0)
-            listenep.port = DAFILESRV_PORT;
+            listenep.port = securitySettings.queryDaliServixPort();
         StringBuffer eps;
         if (listenep.isNull())
             eps.append(listenep.port);
@@ -96,7 +110,7 @@ bool CDfuPlusHelper::runLocalDaFileSvr(SocketEndpoint &listenep,bool requireauth
     thr->start();
     StringBuffer eps;
     if (listenep.isNull())
-        progress("Started local Dali file server on port %d\n", listenep.port?listenep.port:DAFILESRV_PORT);
+        progress("Started local Dali file server on port %d\n", listenep.port?listenep.port:securitySettings.queryDaliServixPort());
     else
         progress("Started local Dali file server on %s\n", listenep.getUrlStr(eps).str());
     if (timeout==0) {
@@ -118,9 +132,9 @@ bool CDfuPlusHelper::runLocalDaFileSvr(SocketEndpoint &listenep,bool requireauth
 bool CDfuPlusHelper::checkLocalDaFileSvr(const char *eps,SocketEndpoint &epout)
 {
     if (!eps||!*eps)
-        epout.setLocalHost(DAFILESRV_PORT);
+        epout.setLocalHost(securitySettings.queryDaliServixPort());
     else {
-        epout.set(eps,DAFILESRV_PORT);
+        epout.set(eps,securitySettings.queryDaliServixPort());
         if (!epout.isLocal())
             return false;
     }

+ 0 - 1
esp/src/eclwatch/LZBrowseWidget.js

@@ -293,7 +293,6 @@ define([
                     lang.mixin(request, {
                         sourceIP: item.DropZone.NetAddress,
                         sourcePath: item.fullPath,
-                        sourceRowTag: item.targetRowTag,
                         destLogicalName: request.namePrefix + item.targetName
                     });
                     doSpray(request, item);

+ 5 - 0
initfiles/etc/DIR_NAME/environment.conf.in

@@ -21,3 +21,8 @@ interface=*
 use_epoll=true
 # allow kernel pagecache flushing where enabled (true/false)
 allow_pgcache_flush=true
+
+#enable SSL for dafilesrv remote file access
+#dfsUseSSL=false
+#dfsSSLCertFile=/certfilepath/certfile
+#dfsSSLPrivateKeyFile=/keyfilepath/keyfile

+ 2 - 0
system/include/portlist.h

@@ -57,6 +57,8 @@
 #define MP_START_PORT                   7101 // Default range for MP ports
 #define MP_END_PORT                     7500
 
+#define SECURE_DAFILESRV_PORT           7600 // aka daliservix
+
 //ESP SERVICES
 //INSECURE
 #define WS_ECL_DEFAULT_PORT             8002

+ 1 - 1
system/jlib/jfile.cpp

@@ -4411,7 +4411,7 @@ StringBuffer & RemoteFilename::getRemotePath(StringBuffer & out) const
 
 bool RemoteFilename::isLocal() const
 {
-    if (ep.port&&(ep.port!=DAFILESRV_PORT))
+    if (ep.port&&(ep.port!=DAFILESRV_PORT && ep.port!=SECURE_DAFILESRV_PORT))
         return false;  // treat non-dafilesrv port as remote
     return ep.isLocal() || ep.isNull();
 }

+ 94 - 1
system/jlib/jsocket.cpp

@@ -372,7 +372,7 @@ protected:
     SOCKETMODE      sockmode;
     IpAddress       targetip;
     SocketEndpoint  returnep;   // set by set_return_addr
-    
+
     MCASTREQ    *   mcastreq;
     size32_t        nextblocksize;
     unsigned        blockflags;
@@ -6066,3 +6066,96 @@ ISocketConnectWait *nonBlockingConnect(SocketEndpoint &ep,unsigned connecttimeou
 {
     return new CSocketConnectWait(ep,connecttimeoutms);
 }
+
+
+
+int wait_multiple(bool isRead,               //IN   true if wait read, false it wait write
+                  UnsignedArray &socks,      //IN   sockets to be checked for readiness
+                  unsigned timeoutMS,        //IN   timeout
+                  UnsignedArray &readySocks) //OUT  sockets ready
+{
+    aindex_t numSocks = socks.length();
+    if (numSocks == 0)
+        THROWJSOCKEXCEPTION2(JSOCKERR_bad_address);
+
+    SOCKET maxSocket = 0;
+    T_FD_SET fds;
+    XFD_ZERO(&fds);
+
+    //Add each SOCKET in array to T_FD_SET
+#ifdef _DEBUG
+    StringBuffer dbgSB("wait_multiple() on sockets :");
+#endif
+    for (aindex_t idx = 0; idx < numSocks; idx++)
+    {
+#ifdef _DEBUG
+        dbgSB.appendf(" %d",socks.item(idx));
+#endif
+        maxSocket = socks.item(idx) > maxSocket ? socks.item(idx) : maxSocket;
+        FD_SET((unsigned)socks.item(idx), &fds);
+    }
+#ifdef _DEBUG
+    DBGLOG("%s",dbgSB.str());
+#endif
+
+    //Check socket states
+    int res;
+    if (timeoutMS == WAIT_FOREVER)
+        res = ::select( maxSocket + 1, isRead ? (fd_set *)&fds : NULL, isRead ? NULL : (fd_set *)&fds, NULL, NULL );
+    else
+    {
+        struct timeval tv;
+        tv.tv_sec = timeoutMS / 1000;
+        tv.tv_usec = (timeoutMS % 1000)*1000;
+        res = ::select( maxSocket + 1,  isRead ? (fd_set *)&fds : NULL, isRead ? NULL : (fd_set *)&fds, NULL, &tv );
+    }
+
+    if (res != SOCKET_ERROR)
+    {
+#ifdef _DEBUG
+        StringBuffer dbgSB("wait_multiple() ready socket(s) :");
+#endif
+        //Build up list of socks which are ready for accept read/write without blocking
+        for (aindex_t idx = 0; res && idx < socks.length(); idx++)
+        {
+            if (FD_ISSET(socks.item(idx), &fds))
+            {
+#ifdef _DEBUG
+                dbgSB.appendf(" %d",socks.item(idx));
+#endif
+                readySocks.append(socks.item(idx));
+                if (readySocks.length() == res)
+                    break;
+            }
+        }
+#ifdef _DEBUG
+        if (res)
+            DBGLOG("%s",dbgSB.str());
+#endif
+    }
+    else
+    {
+        int err = ERRNO();
+        if (err != EINTRCALL)
+        {
+            throw MakeStringException(-1,"wait_multiple::select error %d", err);
+        }
+    }
+    return res;
+}
+
+//Given a list of sockets, wait until any one or more are ready to be read (wont block)
+//returns 0 if timeout, number of waiting sockets otherwise
+int wait_read_multiple(UnsignedArray &socks,        //IN   sockets to be checked for readiness
+                       unsigned timeoutMS,          //IN   timeout
+                       UnsignedArray &readySocks)   //OUT  sockets ready to be read
+{
+    return wait_multiple(true, socks, timeoutMS, readySocks);
+}
+
+int wait_write_multiple(UnsignedArray &socks,       //IN   sockets to be checked for readiness
+                       unsigned timeoutMS,          //IN   timeout
+                       UnsignedArray &readySocks)   //OUT  sockets ready to be written
+{
+    return wait_multiple(false, socks, timeoutMS, readySocks);
+}

+ 9 - 0
system/jlib/jsocket.hpp

@@ -603,5 +603,14 @@ extern jlib_decl StringBuffer lookupHostName(const IpAddress &ip,StringBuffer &r
 extern jlib_decl bool isInterfaceIp(const IpAddress &ip, const char *ifname);
 extern jlib_decl bool getInterfaceIp(IpAddress &ip, const char *ifname);
 
+//Given a list of server sockets, wait until any one or more are ready to be read/written (wont block)
+//return array of ready sockets
+extern jlib_decl int wait_read_multiple(UnsignedArray  &socks,      //IN   sockets to be checked for read readiness
+                                        unsigned timeoutMS,         //IN   timeout
+                                        UnsignedArray  &readySocks);//OUT  sockets ready to be read
+extern jlib_decl int wait_write_multiple(UnsignedArray  &socks,     //IN   sockets to be checked for write readiness
+                                        unsigned timeoutMS,         //IN   timeout
+                                        UnsignedArray  &readySocks);//OUT  sockets ready to be written
+
 #endif
 

+ 61 - 0
system/jlib/jutil.cpp

@@ -48,6 +48,8 @@
 #include "build-config.h"
 #endif
 
+#include "portlist.h"
+
 static SpinLock * cvtLock;
 
 #ifdef _WIN32
@@ -2280,6 +2282,65 @@ IPropertyTree *getHPCCEnvironment(const char *configFileName)
     return NULL;
 }
 
+jlib_decl bool querySecuritySettings(bool *          _useSSL,
+                                     unsigned short *_port,
+                                     const char * *  _certificate,
+                                     const char * *  _privateKey)
+{
+    static CriticalSection securitySettingsCrit;
+    static bool useSSL = false;
+    static StringAttr certificate;
+    static StringAttr privateKey;
+    static bool retrieved = false;
+
+    if (!retrieved)
+    {
+        CriticalBlock b(securitySettingsCrit);
+        if (!retrieved)
+        {
+            try
+            {
+                StringBuffer configFileSpec;
+#ifndef _WIN32
+                configFileSpec.set(CONFIG_DIR).append(PATHSEPSTR).append("environment.conf");
+#endif
+                Owned<IProperties> conf = createProperties(configFileSpec.str(), true);
+                useSSL = conf->getPropBool("dfsUseSSL", false);
+                if (useSSL)
+                {
+                    certificate.set(conf->queryProp("dfsSSLCertFile"));
+                    privateKey.set(conf->queryProp("dfsSSLPrivateKeyFile"));
+                }
+                retrieved = true;
+            }
+            catch (IException *e)
+            {
+                EXCLOG(e, "Error processing environment.conf\n");
+                throwUnexpected();
+            }
+        }
+    }
+    if (retrieved)
+    {
+        if (_useSSL)
+            *_useSSL = useSSL;
+        if (_port)
+            *_port = useSSL ? SECURE_DAFILESRV_PORT : DAFILESRV_PORT;
+        if (_certificate)
+            *_certificate = certificate.get();
+        if (_privateKey)
+            *_privateKey = privateKey.get();
+    }
+    else
+    {
+        if (_useSSL)
+            *_useSSL = false;
+        if (_port)
+            *_port = DAFILESRV_PORT;
+    }
+    return retrieved;
+}
+
 static IPropertyTree *getOSSdirTree()
 {
     Owned<IPropertyTree> envtree = getHPCCEnvironment();

+ 5 - 0
system/jlib/jutil.hpp

@@ -312,6 +312,11 @@ extern jlib_decl bool getConfigurationDirectory(const IPropertyTree *dirtree, //
                                                 const char *instance, 
                                                 StringBuffer &dirout);
 
+extern jlib_decl bool querySecuritySettings(bool *          _useSSL,
+                                            unsigned short *_port,
+                                            const char * *  _certificate,
+                                            const char * *  _privateKey);
+
 extern jlib_decl const char * matchConfigurationDirectoryEntry(const char *path,const char *mask,StringBuffer &name, StringBuffer &component, StringBuffer &instance);
 extern jlib_decl bool replaceConfigurationDirectoryEntry(const char *path,const char *frommask,const char *tomask,StringBuffer &out);
 

+ 11 - 5
system/security/securesocket/securesocket.cpp

@@ -163,7 +163,8 @@ public:
 
     virtual void   read(void* buf, size32_t size)
     {
-        throw MakeStringException(-1, "not implemented");
+        size32_t size_read;
+        readTimeout(buf, size, size, size_read, 0, false);
     }
 
     virtual size32_t get_max_send_size()
@@ -312,7 +313,10 @@ public:
 
     virtual size32_t avail_read()            // called after wait_read to see how much data available
     {
-        throw MakeStringException(-1, "not implemented");
+        int pending = SSL_pending(m_ssl);
+        if(pending > 0)
+            return pending;
+        return m_socket->avail_read();
     }
 
     virtual size32_t write_multiple(unsigned num,const void **buf, size32_t *size)
@@ -556,10 +560,11 @@ int CSecureSocket::secure_accept()
     }
     else if(err < 0)
     {
+        int ret = SSL_get_error(m_ssl, err);
         char errbuf[512];
         ERR_error_string_n(ERR_get_error(), errbuf, 512);
         errbuf[511] = '\0';
-        DBGLOG("SSL_accept returned %d, error - %s", err, errbuf);
+        DBGLOG("SSL_accept returned %d, SSL_get_error=%d, error - %s", err, ret, errbuf);
         if(strstr(errbuf, "error:1408F455:") != NULL)
         {
             DBGLOG("Unrecoverable SSL library error.");
@@ -596,10 +601,11 @@ int CSecureSocket::secure_connect()
     int err = SSL_connect (m_ssl);                     
     if(err <= 0)
     {
+        int ret = SSL_get_error(m_ssl, err);
         char errbuf[512];
         ERR_error_string_n(ERR_get_error(), errbuf, 512);
-        DBGLOG("SSL_connect error - %s", errbuf);
-        throw MakeStringException(-1, "Certificate verification failed: %s", errbuf);
+        DBGLOG("SSL_connect error - %s, SSL_get_error=%d, error - %d", errbuf,ret, err);
+        throw MakeStringException(-1, "SSL_connect failed: %s", errbuf);
     }