// @(#)root/net:$Id: TSSLSocket.cxx
// Author: Alejandro Alvarez 16/09/2011

/*************************************************************************
 * Copyright (C) 1995-2011, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TSSLSocket                                                           //
//                                                                      //
// A TSocket wrapped in by SSL.                                         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <openssl/ssl.h>
#include <stdio.h>
#include "TROOT.h"
#include "TSSLSocket.h"
#include "TSystem.h"

// Static properties
char TSSLSocket::fgSSLCAFile[FILENAME_MAX] = "";
char TSSLSocket::fgSSLCAPath[FILENAME_MAX] = "";
char TSSLSocket::fgSSLUCert[FILENAME_MAX]  = "";
char TSSLSocket::fgSSLUKey[FILENAME_MAX]   = "";

//______________________________________________________________________________
void TSSLSocket::WrapWithSSL(void)
{
   // Wraps the socket with OpenSSL.

   SSL_library_init();

   // New context
   if (!(fSSLCtx = SSL_CTX_new(SSLv23_method()))) {
      Error("WrapWithSSL", "the context could not be created");
      goto wrapFailed;
   }

   if ((fgSSLCAFile[0] || fgSSLCAPath[0]) && SSL_CTX_load_verify_locations(fSSLCtx, fgSSLCAFile, fgSSLCAPath) == 0) {
      Error("WrapWithSSL", "could not set the CA file and/or the CA path");
      goto wrapFailed;
   }

   if (fgSSLUCert[0] && SSL_CTX_use_certificate_chain_file(fSSLCtx, fgSSLUCert) == 0) {
      Error("WrapWithSSL", "could not set the client certificate");
      goto wrapFailed;
   }

   if (fgSSLUKey[0] && SSL_CTX_use_PrivateKey_file(fSSLCtx, fgSSLUKey, SSL_FILETYPE_PEM) == 0) {
      Error("WrapWithSSL", "could not set the client private key");
      goto wrapFailed;
   }

   // New SSL structure
   if (!(fSSL = SSL_new(fSSLCtx))) {
      Error("WrapWithSSL", "cannot create the ssl struct");
      goto wrapFailed;
   }

   // Bind to the socket
   if (SSL_set_fd(fSSL, fSocket) != 1) {
      Error("WrapWithSSL", "cannot bind to the socket %d", fSocket);
      goto wrapFailed;
   }

   // Open connection
   if (SSL_connect(fSSL) != 1) {
      Error("WrapWithSSL", "cannot connect");
      goto wrapFailed;
   }

   return;

wrapFailed:
   Close();
   return;
}

//______________________________________________________________________________
ClassImp(TSSLSocket)

//______________________________________________________________________________
TSSLSocket::TSSLSocket(TInetAddress addr, const char *service, Int_t tcpwindowsize)
   : TSocket(addr, service, tcpwindowsize)
{
   WrapWithSSL();
}

//______________________________________________________________________________
TSSLSocket::TSSLSocket(TInetAddress addr, Int_t port, Int_t tcpwindowsize)
   : TSocket(addr, port, tcpwindowsize)
{
   WrapWithSSL();
}

//______________________________________________________________________________
TSSLSocket::TSSLSocket(const char *host, const char *service, Int_t tcpwindowsize)
   : TSocket(host, service, tcpwindowsize)
{
   WrapWithSSL();
}

//______________________________________________________________________________
TSSLSocket::TSSLSocket(const char *url, Int_t port, Int_t tcpwindowsize)
   : TSocket(url, port, tcpwindowsize)
{
   WrapWithSSL();
}

//______________________________________________________________________________
TSSLSocket::TSSLSocket(const char *sockpath) : TSocket(sockpath)
{
   WrapWithSSL();
}

//______________________________________________________________________________
TSSLSocket::TSSLSocket(Int_t desc) : TSocket(desc)
{
   WrapWithSSL();
}

//______________________________________________________________________________
TSSLSocket::TSSLSocket(Int_t desc, const char *sockpath) : TSocket(desc, sockpath)
{
   WrapWithSSL();
}

//______________________________________________________________________________
TSSLSocket::TSSLSocket(const TSSLSocket &s) : TSocket(s)
{
   WrapWithSSL();
}

//______________________________________________________________________________
TSSLSocket::~TSSLSocket()
{
   // Close gracefully the connection, and free SSL structures.

   Close();
   if (fSSL)
     SSL_free(fSSL);
   if (fSSLCtx)
     SSL_CTX_free(fSSLCtx);
}

//______________________________________________________________________________
void TSSLSocket::Close(Option_t *option)
{
   // Close the SSL connection.

   if (fSSL)
      SSL_shutdown(fSSL);
   TSocket::Close(option);
}

//______________________________________________________________________________
void TSSLSocket::SetUpSSL(const char *cafile, const char *capath,
                          const char *ucert,  const char *ukey)
{
   // Set up the static configuration variables.

   if (cafile)
      strlcpy(fgSSLCAFile, cafile, FILENAME_MAX);
   if (capath)
      strlcpy(fgSSLCAPath, capath, FILENAME_MAX);
   if (ucert)
      strlcpy(fgSSLUCert,  ucert,  FILENAME_MAX);
   if (ukey)
      strlcpy(fgSSLUKey,   ukey,   FILENAME_MAX);
}

//______________________________________________________________________________
Int_t TSSLSocket::Recv(TMessage *& /*mess */)
{
   Error("Recv", "not implemented");
   return -1;
}

//______________________________________________________________________________
Int_t TSSLSocket::RecvRaw(void *buffer, Int_t length, ESendRecvOptions opt)
{
   // Receive a raw buffer of specified length bytes.

   TSystem::ResetErrno();

   if (fSocket == -1) return -1;
   if (length == 0)   return 0;

   ResetBit(TSocket::kBrokenConn);

   Int_t n;
   Int_t offset = 0;
   Int_t remain = length;

   // SSL_read/SSL_peek may not return the total length at once
   while (remain > 0) {
     if (opt == kPeek)
        n = SSL_peek(fSSL, (char*)buffer + offset, (int)remain);
     else
        n = SSL_read(fSSL, (char*)buffer + offset, (int)remain);

     if (n <= 0) {
        if (gDebug > 0)
           Error("RecvRaw", "failed to read from the socket");

        if (SSL_get_error(fSSL, n) == SSL_ERROR_ZERO_RETURN || SSL_get_error(fSSL, n) == SSL_ERROR_SYSCALL) {
           // Connection closed, reset or broken
           SetBit(TSocket::kBrokenConn);
           SSL_set_quiet_shutdown(fSSL, 1); // Socket is gone, sending "close notify" will fail
           Close();
        }
        return n;
     }

     // When peeking, just return the available data, don't loop. Otherwise,
     // we may copy the same chunk of data multiple times into the
     // output buffer, for instance when there is no more recent data
     // in the socket's internal reception buffers.
     // Note that in this case we don't update the counters of data received
     // through this socket. They will be updated when the data is actually
     // read. This avoids double counting.
     if (opt == kPeek) return n;

     offset += n;
     remain -= n;
   }

   fBytesRecv  += length;
   fgBytesRecv += length;

   Touch();  // update usage timestamp

   return offset;
}

//______________________________________________________________________________
Int_t TSSLSocket::Send(const TMessage & /* mess */)
{
   Error("Send", "not implemented");
   return -1;
}

//______________________________________________________________________________
Int_t TSSLSocket::SendRaw(const void *buffer, Int_t length, ESendRecvOptions /* opt */)
{
   // Send a raw buffer of specified length.

   TSystem::ResetErrno();

   if (fSocket == -1) return -1;

   ResetBit(TSocket::kBrokenConn);

   Int_t nsent;
   if ((nsent = SSL_write(fSSL, buffer, (int)length)) <= 0) {
      if (SSL_get_error(fSSL, nsent) == SSL_ERROR_ZERO_RETURN) {
         // Connection reset or broken: close
         SetBit(TSocket::kBrokenConn);
         Close();
      }
      return nsent;
   }

   fBytesSent  += nsent;
   fgBytesSent += nsent;

   Touch(); // update usage timestamp

   return nsent;
}
 TSSLSocket.cxx:1
 TSSLSocket.cxx:2
 TSSLSocket.cxx:3
 TSSLSocket.cxx:4
 TSSLSocket.cxx:5
 TSSLSocket.cxx:6
 TSSLSocket.cxx:7
 TSSLSocket.cxx:8
 TSSLSocket.cxx:9
 TSSLSocket.cxx:10
 TSSLSocket.cxx:11
 TSSLSocket.cxx:12
 TSSLSocket.cxx:13
 TSSLSocket.cxx:14
 TSSLSocket.cxx:15
 TSSLSocket.cxx:16
 TSSLSocket.cxx:17
 TSSLSocket.cxx:18
 TSSLSocket.cxx:19
 TSSLSocket.cxx:20
 TSSLSocket.cxx:21
 TSSLSocket.cxx:22
 TSSLSocket.cxx:23
 TSSLSocket.cxx:24
 TSSLSocket.cxx:25
 TSSLSocket.cxx:26
 TSSLSocket.cxx:27
 TSSLSocket.cxx:28
 TSSLSocket.cxx:29
 TSSLSocket.cxx:30
 TSSLSocket.cxx:31
 TSSLSocket.cxx:32
 TSSLSocket.cxx:33
 TSSLSocket.cxx:34
 TSSLSocket.cxx:35
 TSSLSocket.cxx:36
 TSSLSocket.cxx:37
 TSSLSocket.cxx:38
 TSSLSocket.cxx:39
 TSSLSocket.cxx:40
 TSSLSocket.cxx:41
 TSSLSocket.cxx:42
 TSSLSocket.cxx:43
 TSSLSocket.cxx:44
 TSSLSocket.cxx:45
 TSSLSocket.cxx:46
 TSSLSocket.cxx:47
 TSSLSocket.cxx:48
 TSSLSocket.cxx:49
 TSSLSocket.cxx:50
 TSSLSocket.cxx:51
 TSSLSocket.cxx:52
 TSSLSocket.cxx:53
 TSSLSocket.cxx:54
 TSSLSocket.cxx:55
 TSSLSocket.cxx:56
 TSSLSocket.cxx:57
 TSSLSocket.cxx:58
 TSSLSocket.cxx:59
 TSSLSocket.cxx:60
 TSSLSocket.cxx:61
 TSSLSocket.cxx:62
 TSSLSocket.cxx:63
 TSSLSocket.cxx:64
 TSSLSocket.cxx:65
 TSSLSocket.cxx:66
 TSSLSocket.cxx:67
 TSSLSocket.cxx:68
 TSSLSocket.cxx:69
 TSSLSocket.cxx:70
 TSSLSocket.cxx:71
 TSSLSocket.cxx:72
 TSSLSocket.cxx:73
 TSSLSocket.cxx:74
 TSSLSocket.cxx:75
 TSSLSocket.cxx:76
 TSSLSocket.cxx:77
 TSSLSocket.cxx:78
 TSSLSocket.cxx:79
 TSSLSocket.cxx:80
 TSSLSocket.cxx:81
 TSSLSocket.cxx:82
 TSSLSocket.cxx:83
 TSSLSocket.cxx:84
 TSSLSocket.cxx:85
 TSSLSocket.cxx:86
 TSSLSocket.cxx:87
 TSSLSocket.cxx:88
 TSSLSocket.cxx:89
 TSSLSocket.cxx:90
 TSSLSocket.cxx:91
 TSSLSocket.cxx:92
 TSSLSocket.cxx:93
 TSSLSocket.cxx:94
 TSSLSocket.cxx:95
 TSSLSocket.cxx:96
 TSSLSocket.cxx:97
 TSSLSocket.cxx:98
 TSSLSocket.cxx:99
 TSSLSocket.cxx:100
 TSSLSocket.cxx:101
 TSSLSocket.cxx:102
 TSSLSocket.cxx:103
 TSSLSocket.cxx:104
 TSSLSocket.cxx:105
 TSSLSocket.cxx:106
 TSSLSocket.cxx:107
 TSSLSocket.cxx:108
 TSSLSocket.cxx:109
 TSSLSocket.cxx:110
 TSSLSocket.cxx:111
 TSSLSocket.cxx:112
 TSSLSocket.cxx:113
 TSSLSocket.cxx:114
 TSSLSocket.cxx:115
 TSSLSocket.cxx:116
 TSSLSocket.cxx:117
 TSSLSocket.cxx:118
 TSSLSocket.cxx:119
 TSSLSocket.cxx:120
 TSSLSocket.cxx:121
 TSSLSocket.cxx:122
 TSSLSocket.cxx:123
 TSSLSocket.cxx:124
 TSSLSocket.cxx:125
 TSSLSocket.cxx:126
 TSSLSocket.cxx:127
 TSSLSocket.cxx:128
 TSSLSocket.cxx:129
 TSSLSocket.cxx:130
 TSSLSocket.cxx:131
 TSSLSocket.cxx:132
 TSSLSocket.cxx:133
 TSSLSocket.cxx:134
 TSSLSocket.cxx:135
 TSSLSocket.cxx:136
 TSSLSocket.cxx:137
 TSSLSocket.cxx:138
 TSSLSocket.cxx:139
 TSSLSocket.cxx:140
 TSSLSocket.cxx:141
 TSSLSocket.cxx:142
 TSSLSocket.cxx:143
 TSSLSocket.cxx:144
 TSSLSocket.cxx:145
 TSSLSocket.cxx:146
 TSSLSocket.cxx:147
 TSSLSocket.cxx:148
 TSSLSocket.cxx:149
 TSSLSocket.cxx:150
 TSSLSocket.cxx:151
 TSSLSocket.cxx:152
 TSSLSocket.cxx:153
 TSSLSocket.cxx:154
 TSSLSocket.cxx:155
 TSSLSocket.cxx:156
 TSSLSocket.cxx:157
 TSSLSocket.cxx:158
 TSSLSocket.cxx:159
 TSSLSocket.cxx:160
 TSSLSocket.cxx:161
 TSSLSocket.cxx:162
 TSSLSocket.cxx:163
 TSSLSocket.cxx:164
 TSSLSocket.cxx:165
 TSSLSocket.cxx:166
 TSSLSocket.cxx:167
 TSSLSocket.cxx:168
 TSSLSocket.cxx:169
 TSSLSocket.cxx:170
 TSSLSocket.cxx:171
 TSSLSocket.cxx:172
 TSSLSocket.cxx:173
 TSSLSocket.cxx:174
 TSSLSocket.cxx:175
 TSSLSocket.cxx:176
 TSSLSocket.cxx:177
 TSSLSocket.cxx:178
 TSSLSocket.cxx:179
 TSSLSocket.cxx:180
 TSSLSocket.cxx:181
 TSSLSocket.cxx:182
 TSSLSocket.cxx:183
 TSSLSocket.cxx:184
 TSSLSocket.cxx:185
 TSSLSocket.cxx:186
 TSSLSocket.cxx:187
 TSSLSocket.cxx:188
 TSSLSocket.cxx:189
 TSSLSocket.cxx:190
 TSSLSocket.cxx:191
 TSSLSocket.cxx:192
 TSSLSocket.cxx:193
 TSSLSocket.cxx:194
 TSSLSocket.cxx:195
 TSSLSocket.cxx:196
 TSSLSocket.cxx:197
 TSSLSocket.cxx:198
 TSSLSocket.cxx:199
 TSSLSocket.cxx:200
 TSSLSocket.cxx:201
 TSSLSocket.cxx:202
 TSSLSocket.cxx:203
 TSSLSocket.cxx:204
 TSSLSocket.cxx:205
 TSSLSocket.cxx:206
 TSSLSocket.cxx:207
 TSSLSocket.cxx:208
 TSSLSocket.cxx:209
 TSSLSocket.cxx:210
 TSSLSocket.cxx:211
 TSSLSocket.cxx:212
 TSSLSocket.cxx:213
 TSSLSocket.cxx:214
 TSSLSocket.cxx:215
 TSSLSocket.cxx:216
 TSSLSocket.cxx:217
 TSSLSocket.cxx:218
 TSSLSocket.cxx:219
 TSSLSocket.cxx:220
 TSSLSocket.cxx:221
 TSSLSocket.cxx:222
 TSSLSocket.cxx:223
 TSSLSocket.cxx:224
 TSSLSocket.cxx:225
 TSSLSocket.cxx:226
 TSSLSocket.cxx:227
 TSSLSocket.cxx:228
 TSSLSocket.cxx:229
 TSSLSocket.cxx:230
 TSSLSocket.cxx:231
 TSSLSocket.cxx:232
 TSSLSocket.cxx:233
 TSSLSocket.cxx:234
 TSSLSocket.cxx:235
 TSSLSocket.cxx:236
 TSSLSocket.cxx:237
 TSSLSocket.cxx:238
 TSSLSocket.cxx:239
 TSSLSocket.cxx:240
 TSSLSocket.cxx:241
 TSSLSocket.cxx:242
 TSSLSocket.cxx:243
 TSSLSocket.cxx:244
 TSSLSocket.cxx:245
 TSSLSocket.cxx:246
 TSSLSocket.cxx:247
 TSSLSocket.cxx:248
 TSSLSocket.cxx:249
 TSSLSocket.cxx:250
 TSSLSocket.cxx:251
 TSSLSocket.cxx:252
 TSSLSocket.cxx:253
 TSSLSocket.cxx:254
 TSSLSocket.cxx:255
 TSSLSocket.cxx:256
 TSSLSocket.cxx:257
 TSSLSocket.cxx:258
 TSSLSocket.cxx:259
 TSSLSocket.cxx:260
 TSSLSocket.cxx:261
 TSSLSocket.cxx:262
 TSSLSocket.cxx:263
 TSSLSocket.cxx:264
 TSSLSocket.cxx:265
 TSSLSocket.cxx:266
 TSSLSocket.cxx:267
 TSSLSocket.cxx:268
 TSSLSocket.cxx:269
 TSSLSocket.cxx:270
 TSSLSocket.cxx:271
 TSSLSocket.cxx:272
 TSSLSocket.cxx:273
 TSSLSocket.cxx:274
 TSSLSocket.cxx:275
 TSSLSocket.cxx:276