io.cpp 44 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/******************************************************************************
 *
 * This file is part of the Castor project.
 * See http://castor.web.cern.ch/castor
 *
 * Copyright (C) 2003  CERN
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *
 *
 *
22
 * @author Castor Dev team, castor-dev@cern.ch
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 *****************************************************************************/

#include "castor/exception/Exception.hpp"
#include "castor/exception/InvalidArgument.hpp"
#include "castor/io/io.hpp"
#include "castor/utils/SmartFd.hpp"
#include "castor/utils/utils.hpp"
#include "h/serrno.h"
#include "h/net.h"

#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <sstream>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
42
#include <list>
43

44
//------------------------------------------------------------------------------
45
// createListenerSock
46
//------------------------------------------------------------------------------
47
int castor::io::createListenerSock(const unsigned short port)
48
   {
49
50
51
52
53
  const unsigned short lowPort = port;
  const unsigned short highPort = port;
  unsigned short chosenPort = 0;

  struct in_addr networkAddress;
54
  memset(&networkAddress, '\0', sizeof(networkAddress));
55
56
57
58
59
  networkAddress.s_addr = INADDR_ANY;

  return createListenerSock(networkAddress, lowPort, highPort, chosenPort);
}

60
//------------------------------------------------------------------------------
61
// createListenerSock
62
//------------------------------------------------------------------------------
63
64
65
66
int castor::io::createListenerSock(
  const unsigned short lowPort,
  const unsigned short highPort,
  unsigned short       &chosenPort)
67
   {
68
69

  struct in_addr networkAddress;
70
  memset(&networkAddress, '\0', sizeof(networkAddress));
71
72
73
74
75
  networkAddress.s_addr = INADDR_ANY;

  return createListenerSock(networkAddress, lowPort, highPort, chosenPort);
}

76
//------------------------------------------------------------------------------
77
// createListenerSock
78
//------------------------------------------------------------------------------
79
80
81
82
83
int castor::io::createListenerSock(
  const std::string    &addr,
  const unsigned short lowPort,
  const unsigned short highPort,
  unsigned short       &chosenPort)
84
   {
85
86
87
88
89
90
91
92
93
94
95
96
97
98

  struct in_addr networkAddress;

  const int rc = inet_pton(AF_INET, addr.c_str(), &networkAddress);
  if(0 >= rc) {
    castor::exception::Exception ex(errno);
    ex.getMessage() << "Failed to create listener socket:"
      " Failed to convert string to network address: value=" << addr;
    throw ex;
  }

  return createListenerSock(networkAddress, lowPort, highPort, chosenPort);
}

99
//------------------------------------------------------------------------------
100
// createListenerSock
101
//------------------------------------------------------------------------------
102
103
int castor::io::createListenerSock(
  const struct in_addr &addr,
104
105
106
  const unsigned short lowPort,
  const unsigned short highPort,
  unsigned short       &chosenPort)
107
   {
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

  // Check range validity
  if(lowPort < 1) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<  "lowPort must be greater than 0"
      ": lowPort=" << lowPort;
    throw ex;
  }
  if(highPort < 1) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() << "highPort must be greater than 0"
      ": highPort=" << lowPort;
    throw ex;
  }
  if(lowPort > highPort) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<  "lowPort must be less than or equal to highPort"
      ": lowPort=" << lowPort << " highPort=" << highPort;
    throw ex;
  }

  // Create a socket
  utils::SmartFd sock(socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
  if(sock.get() < 0) {
132
133
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
134
    castor::exception::Exception ex;
135
    ex.getMessage() << ": Failed to create socket: " << errBuf;
136
137
138
139
140
141
142
143
    throw ex;
  }

  // Set the SO_REUSEADDR socket option before calling bind
  {
    int reuseaddrOptval = 1;
    if(0 > setsockopt(sock.get(), SOL_SOCKET, SO_REUSEADDR,
      (char *)&reuseaddrOptval, sizeof(reuseaddrOptval))) {
144
145
      char errBuf[100];
      sstrerror_r(errno, errBuf, sizeof(errBuf));
146
      castor::exception::Exception ex;
147
148
149
150
151
152
      ex.getMessage() <<
        ": Failed to set socket option"
        ": file-descriptor=" << sock.get() <<
        " level=SOL_SOCKET"
        " optname=SO_REUSEADDR"
        " optval=" << reuseaddrOptval <<
153
        ": " << errBuf;
154
155
156
157
158
159
160
161
162
163
164
165
      throw ex;
    }
  }

  // Address structure to be used to bind the socket
  struct sockaddr_in address;

  // For each port in the range
  for(unsigned short port=lowPort; port<=highPort; ++port) {

    // Try to bind the socket to the port
    utils::setBytes(address, '\0');
166
167
168
    address.sin_family = AF_INET;
    address.sin_addr   = addr;
    address.sin_port   = htons(port);
169
170
171
172
173
174
175
176
177
178
179
180
181

    const int bindRc = bind(sock.get(), (struct sockaddr *) &address,
      sizeof(address));
    const int bindErrno = errno;

    // If the bind was successful
    if(bindRc == 0) {

      // Record the port number of the succesful bind
      chosenPort = port;

      // Mark the socket as being a listener
      if(listen(sock.get(), LISTENBACKLOG) < 0) {
182
183
        char errBuf[100];
        sstrerror_r(errno, errBuf, sizeof(errBuf));
184
        castor::exception::Exception ex;
185
186
187
        ex.getMessage() <<
          ": Failed to mark socket as being a listener"
          ": listenSocketFd=" << sock.get() <<
188
          ": " << errBuf;
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
        throw ex;
      }

      // Release and return the socket descriptor
      return(sock.release());

    // Else the bind failed
    } else {

      // If the bind failed because the address was in use, then continue
      if(bindErrno == EADDRINUSE) {
        continue;

      // Else throw an exception
      } else {
204
205
        char errBuf[100];
        sstrerror_r(bindErrno, errBuf, sizeof(errBuf));
206
        castor::exception::Exception ex;
207
208
209
        ex.getMessage() <<
          ": Failed to bind listener socket"
          ": listenSocketFd=" << sock.get() <<
210
          ": " << errBuf;
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
        throw ex;
      }
    }
  }

  // If this line is reached then all ports in the specified range are in use

  // Throw an exception
  castor::exception::NoPortInRange ex(lowPort, highPort);
  ex.getMessage() <<
    "All ports within the specified range are in use"
    ": listenSocketFd=" << sock.get() <<
    ": lowPort=" << lowPort <<
    ": highPort=" << highPort;

  throw ex;
}

229
230
231
232
//------------------------------------------------------------------------------
// createLocalhostListenerSockBoundToLocalhost
//------------------------------------------------------------------------------
int castor::io::createLocalhostListenerSock(const unsigned short port)
233
   {
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  const unsigned short lowPort = port;
  const unsigned short highPort = port;
  unsigned short chosenPort = 0;

  const char *addr = "127.0.0.1";
  struct in_addr networkAddress;
  const int rc = inet_pton(AF_INET, addr, &networkAddress);
  if(0 >= rc) {
    castor::exception::Exception ex(errno);
    ex.getMessage() << "Failed to create listener socket:"
      " Failed to convert string to network address: value=" << addr;
    throw ex;
  }

  return createListenerSock(networkAddress, lowPort, highPort, chosenPort);
}

251
//------------------------------------------------------------------------------
252
// acceptConnection
253
//------------------------------------------------------------------------------
254
int castor::io::acceptConnection(const int listenSocketFd)
255
   {
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

  // Throw an exception if listenSocketFd is invalid
  if(listenSocketFd < 0) {
    exception::InvalidArgument ex;
    ex.getMessage() <<
      ": Invalid listen socket file-descriptor"
      ": listenSocketFd=" << listenSocketFd;
    throw ex;
  }

  struct sockaddr_in peerAddress;
  unsigned int       peerAddressLen = sizeof(peerAddress);

  const int connectedSocketFd = accept(listenSocketFd,
    (struct sockaddr *)&peerAddress, &peerAddressLen);
  const int savedErrno = errno;

  if(connectedSocketFd < 0) {
    std::stringstream reason;

    reason <<
      ": Accept failed"
      ": listenSocketFd=" << listenSocketFd;

    if(savedErrno == EINVAL) {
      reason << ": Socket is not listening for connections";
    } else {
283
284
285
      char errBuf[100];
      sstrerror_r(savedErrno, errBuf, sizeof(errBuf));
      reason << ": " << errBuf;
286
287
    }

288
    castor::exception::Exception ex;
289
290
291
292
293
294
295
    ex.getMessage() << reason.str();
    throw ex;
  }

  return connectedSocketFd;
}

296
//------------------------------------------------------------------------------
297
// acceptConnection
298
//------------------------------------------------------------------------------
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
int castor::io::acceptConnection(const int listenSocketFd,
  const time_t timeout) throw(
    castor::exception::TimeOut,
    castor::exception::AcceptConnectionInterrupted,
    castor::exception::Exception) {

  // Throw an exception if listenSocketFd is invalid
  if(listenSocketFd < 0) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<
      "Invalid listen socket file-descriptor"
      ": listenSocketFd=" << listenSocketFd;
    throw ex;
  }

  const time_t   startTime = time(NULL);
  fd_set         fdSet;
  struct timeval selectTimeout;

  FD_ZERO(&fdSet);
  FD_SET(listenSocketFd, &fdSet);

  selectTimeout.tv_sec  = timeout;
  selectTimeout.tv_usec = 0;

  const int selectRc = select(listenSocketFd + 1, &fdSet, NULL, NULL,
    &selectTimeout);
  const int selectErrno = errno;

  switch(selectRc) {
  case 0: // Select timed out
    {
      castor::exception::TimeOut ex;
      ex.getMessage() <<
333
           "Failed to accept connection: Timed out after " << timeout
334
335
336
337
338
339
340
341
342
343
344
        << " seconds whilst trying to accept a connection";
      throw ex;
    }
    break;
  case -1: // Select encountered an error
    // If select was interrupted
    if(selectErrno == EINTR) {
      const time_t remainingTime = timeout - (time(NULL) - startTime);

      castor::exception::AcceptConnectionInterrupted ex(remainingTime);

345
      throw ex;
346
    } else {
347
348
      char errBuf[100];
      sstrerror_r(selectErrno, errBuf, sizeof(errBuf));
349
      castor::exception::Exception ex;
350
351
      ex.getMessage() << "Failed to accept connection: Select failed: " <<
        errBuf;
352
353
354
355
356
357
      throw ex;
    }
    break;
  default: // Select found a file descriptor awaiting attention
    // If it is not the expected connection request
    if(!FD_ISSET(listenSocketFd, &fdSet)) {
358
      castor::exception::Exception ex;
359
      ex.getMessage() << "Failed to accept connection "
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
        ": Invalid file descriptor set";
      throw ex;
    }
  }

  struct sockaddr_in peerAddress;
  unsigned int       peerAddressLen = sizeof(peerAddress);

  const int connectedSocketFd = accept(listenSocketFd,
    (struct sockaddr *)&peerAddress, &peerAddressLen);
  const int acceptErrno = errno;

  if(connectedSocketFd < 0) {
    std::stringstream reason;

    reason <<
376
      "Failed to accept connection"
377
378
379
380
381
      ": listenSocketFd=" << listenSocketFd;

    if(acceptErrno == EINVAL) {
      reason << ": Socket is not listening for connections";
    } else {
382
383
384
      char errBuf[100];
      sstrerror_r(acceptErrno, errBuf, sizeof(errBuf));
      reason << ": " << errBuf;
385
386
    }

387
    castor::exception::Exception ex;
388
389
390
391
392
393
394
    ex.getMessage() << reason.str();
    throw ex;
  }

  return connectedSocketFd;
}

395
//------------------------------------------------------------------------------
396
// getSockIpPort
397
//------------------------------------------------------------------------------
398
castor::io::IpAndPort castor::io::getSockIpPort(
399
  const int socketFd)  {
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414

  // Throw an exception if socketFd is invalid
  if(socketFd < 0) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<
      "Invalid socket file-descriptor"
      ": socketFd=" << socketFd;
    throw ex;
  }

  struct sockaddr_in address;
  memset(&address, '\0', sizeof(address));
  socklen_t addressLen = sizeof(address);

  if(getsockname(socketFd, (struct sockaddr*)&address, &addressLen) < 0) {
415
416
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
417
    castor::exception::Exception ex;
418
419
    ex.getMessage() << "Failed to get socket name: socketFd=" << socketFd <<
      ": " << errBuf;
420
421
422
423
424
425
    throw ex;
  }

  return IpAndPort(ntohl(address.sin_addr.s_addr), ntohs(address.sin_port));
}

426
//------------------------------------------------------------------------------
427
// getPeerIpPort
428
//------------------------------------------------------------------------------
429
castor::io::IpAndPort  castor::io::getPeerIpPort(
430
  const int socketFd)  {
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445

  // Throw an exception if socketFd is invalid
  if(socketFd < 0) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<
      "Invalid socket file-descriptor"
      ": socketFd=" << socketFd;
    throw ex;
  }

  struct sockaddr_in address;
  memset(&address, '\0', sizeof(address));
  socklen_t addressLen = sizeof(address);

  if(getpeername(socketFd, (struct sockaddr*)&address, &addressLen) < 0) {
446
447
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
448
    castor::exception::Exception ex;
449
450
    ex.getMessage() << ": Failed to get peer name: socketFd=" << socketFd <<
      ": " << errBuf;
451
452
453
454
455
456
457
458
459
460
461
462
463
    throw ex;
  }

  return IpAndPort(ntohl(address.sin_addr.s_addr), ntohs(address.sin_port));
}

//------------------------------------------------------------------------------
// getSockHostName
//------------------------------------------------------------------------------
void castor::io::getSockHostName(
  const int    socketFd,
  char *const  buf,
  const size_t len)
464
   {
465
466
467
468

  // Throw an exception if socketFd is invalid
  if(socketFd < 0) {
    castor::exception::InvalidArgument ex;
469
470
    ex.getMessage() << "Failed to get socket hostname"
      ": Invalid socket file-descriptor"
471
472
473
474
475
476
477
478
      ": socketFd=" << socketFd;
    throw ex;
  }

  struct sockaddr_in address;
  socklen_t addressLen = sizeof(address);

  if(getsockname(socketFd, (struct sockaddr*)&address, &addressLen) < 0) {
479
480
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
481
    castor::exception::Exception ex;
482
483
    ex.getMessage() << "Failed to get socket hostname"
      ": socketFd=" << socketFd << ": " << errBuf;
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
    throw ex;
  }

  char hostName[HOSTNAMEBUFLEN];
  char serviceName[SERVICENAMEBUFLEN];
  const int error = getnameinfo((const struct sockaddr*)&address, addressLen,
    hostName, sizeof(hostName), serviceName, sizeof(serviceName), 0);

  if(error != 0) {
    castor::exception::Exception ex(SENOSHOST);
    ex.getMessage() <<
      ": Failed to get host information by address"
      ": socketFd=" << socketFd <<
      ": " << gai_strerror(error);
    throw ex;
  }

  utils::copyString(buf, len, hostName);
}

//------------------------------------------------------------------------------
// getSockIpHostnamePort
//------------------------------------------------------------------------------
void castor::io::getSockIpHostnamePort(
  const int      socketFd,
  unsigned long  &ip,
  char *const    hostName,
  const size_t   hostNameLen,
  unsigned short &port)
513
   {
514
515
516
517
518
519
520
521
522
523
524
525
526
527

  // Throw an exception if socketFd is invalid
  if(socketFd < 0) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<
      "Invalid socket file-descriptor"
      ": socketFd=" << socketFd;
    throw ex;
  }

  struct sockaddr_in address;
  socklen_t addressLen = sizeof(address);

  if(getsockname(socketFd, (struct sockaddr*)&address, &addressLen) < 0) {
528
529
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
530
    castor::exception::Exception ex;
531
532
533
    ex.getMessage() <<
      ": Failed to get socket name"
      ": socketFd=" << socketFd <<
534
      ": " << errBuf;
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
    throw ex;
  }

  ip   = ntohl(address.sin_addr.s_addr);
  port = ntohs(address.sin_port);

  {
    char serviceName[SERVICENAMEBUFLEN];
    const int rc = getnameinfo((const struct sockaddr*)&address, addressLen,
      hostName, hostNameLen, serviceName, sizeof(serviceName), 0);

    if(rc != 0) {
      castor::exception::Exception ex(SENOSHOST);
      ex.getMessage() <<
        ": Failed to get host information by address"
        ": socketFd=" << socketFd <<
        ": " << gai_strerror(rc);
      throw ex;
    }
  }
}

//------------------------------------------------------------------------------
// getPeerHostName
//------------------------------------------------------------------------------
void castor::io::getPeerHostName(
  const int    socketFd,
  char *const  buf,
  const size_t len)
564
   {
565
566
567
568
569
570
571
572
573
574
575
576
577
578

  // Throw an exception if socketFd is invalid
  if(socketFd < 0) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<
      "Invalid socket file-descriptor"
      ": socketFd=" << socketFd;
    throw ex;
  }

  struct sockaddr_in address;
  socklen_t addressLen = sizeof(address);

  if(getpeername(socketFd, (struct sockaddr*)&address, &addressLen) < 0) {
579
580
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
581
    castor::exception::Exception ex;
582
583
584
    ex.getMessage() <<
      ": Failed to get peer name"
      ": socketFd=" << socketFd <<
585
      ": " << errBuf;
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
    throw ex;
  }

  {
    char hostName[HOSTNAMEBUFLEN];
    char serviceName[SERVICENAMEBUFLEN];
    const int rc = getnameinfo((const struct sockaddr*)&address, addressLen,
      hostName, sizeof(hostName), serviceName, sizeof(serviceName), 0);

    if(rc != 0) {
      castor::exception::Exception ex(SENOSHOST);
      ex.getMessage() <<
        ": Failed to get host information by address"
        ": socketFd=" << socketFd <<
        ": " << gai_strerror(rc);
      throw ex;
    }

    utils::copyString(buf, len, hostName);
  }
}

//------------------------------------------------------------------------------
// writeIp
//------------------------------------------------------------------------------
void castor::io::writeIp(
  std::ostream        &os,
  const unsigned long ip)
  throw() {
  os << ((ip >> 24) & 0x000000FF) << "."
     << ((ip >> 16) & 0x000000FF) << "."
     << ((ip >>  8) & 0x000000FF) << "."
     << ( ip        & 0x000000FF);
}

//------------------------------------------------------------------------------
// writeSockDescription
//------------------------------------------------------------------------------
void castor::io::writeSockDescription(
  std::ostream &os,
  const int    socketFd)
  throw() {

  // Throw an exception if socketFd is invalid
  if(socketFd < 0) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<
      "Invalid socket file-descriptor"
      ": socketFd=" << socketFd;
    throw ex;
  }

  IpAndPort localIpAndPort(0, 0);
  try {
    localIpAndPort = getSockIpPort(socketFd);
  } catch(castor::exception::Exception &e) {
    localIpAndPort.setIp(0);
    localIpAndPort.setPort(0);
  }

  IpAndPort peerIpAndPort(0, 0);
  try {
    peerIpAndPort = getPeerIpPort(socketFd);
  } catch(castor::exception::Exception &e) {
    peerIpAndPort.setIp(0);
    peerIpAndPort.setPort(0);
  }

  os << "{local=";
  writeIp(os, localIpAndPort.getIp());
  os << ":" << localIpAndPort.getPort();
  os << ",peer=";
  writeIp(os, peerIpAndPort.getIp());
  os << ":" << peerIpAndPort.getPort();
  os << "}";
}

//------------------------------------------------------------------------------
// readBytes
//------------------------------------------------------------------------------
void castor::io::readBytes(
  const int   socketFd,
  const int   timeout,
  const int   nbBytes,
  char *const buf)
671
   {
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703

  // Throw an exception if socketFd is invalid
  if(socketFd < 0) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<
      "Invalid socket file-descriptor"
      ": socketFd=" << socketFd;
    throw ex;
  }

  const bool connClosed = readBytesFromCloseable(socketFd, timeout, nbBytes,
    buf);

  if(connClosed) {
    std::stringstream oss;
    oss << "Failed to read " << nbBytes << " bytes from socket: ";
    writeSockDescription(oss, socketFd);
    oss << ": Connection was closed by the remote end";

    castor::exception::Exception ex(SECONNDROP);
    ex.getMessage() << oss.str();
    throw ex;
  }
}

//------------------------------------------------------------------------------
// readBytesFromCloseable
//------------------------------------------------------------------------------
bool castor::io::readBytesFromCloseable(
  const int   socketFd,
  const int   timeout,
  const int   nbBytes,
704
  char *const buf) {
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722

  // Throw an exception if socketFd is invalid
  if(socketFd < 0) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<
      "Invalid socket file-descriptor"
      ": socketFd=" << socketFd;
    throw ex;
  }

  bool connClosed = false;
  const int rc = netread_timeout(socketFd, buf, nbBytes, timeout);
  int savedSerrno = serrno;

  switch(rc) {
  case -1:
    {
      std::stringstream oss;
Steven Murray's avatar
Steven Murray committed
723
      oss << "Failed to read " << nbBytes << " bytes from socket: " <<
724
725
726
727
728
729
730
        " socketFd=" << socketFd << ": ";
      writeSockDescription(oss, socketFd);
      // netread_timeout can return -1 with serrno set to 0
      if(0 == savedSerrno) {
        savedSerrno = SEINTERNAL;
        oss << ": Unknown error";
      } else {
731
732
733
        char errBuf[100];
        sstrerror_r(savedSerrno, errBuf, sizeof(errBuf));
        oss << ": " << errBuf;
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
      }
      if(SETIMEDOUT == savedSerrno) {
        oss << ": timeout=" << timeout;
      }

      castor::exception::Exception ex(savedSerrno);
      ex.getMessage() << oss.str();
      throw ex;
    }
    break;
  case 0:
    {
      connClosed = true;
    }
    break;
  default:
    if (rc != nbBytes) {

      std::stringstream oss;
      oss << "Failed to read " << nbBytes << " bytes from socket: ";
      writeSockDescription(oss, socketFd);
      oss
        << ": Read the wrong number of bytes"
        << ": Expected: " << nbBytes
        << ": Read: " << rc;

      castor::exception::Exception ex(SECOMERR);
      ex.getMessage() << oss.str();
      throw ex;
    }
  }

  return connClosed;
}

//------------------------------------------------------------------------------
// writeBytes
//------------------------------------------------------------------------------
void castor::io::writeBytes(
  const int   socketFd,
  const int   timeout,
  const int   nbBytes,
  char *const buf)
777
   {
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794

  // Throw an exception if socketFd is invalid
  if(socketFd < 0) {
    castor::exception::InvalidArgument ex;
    ex.getMessage() <<
      "Invalid socket file-descriptor"
      ": socketFd=" << socketFd;
    throw ex;
  }

  const int rc = netwrite_timeout(socketFd, buf, nbBytes, timeout);
  int savedSerrno = serrno;

  switch(rc) {
  case -1:
    {
      std::stringstream oss;
Steven Murray's avatar
Steven Murray committed
795
      oss << "Failed to write " << nbBytes << " bytes to socket: " <<
796
797
798
799
800
801
802
        " socketFd=" << socketFd << ": ";
      writeSockDescription(oss, socketFd);
      // netwrite_timeout can return -1 with serrno set to 0
      if(0 == savedSerrno) {
        savedSerrno = SEINTERNAL;
        oss << ": Unknown error";
      } else {
803
804
805
        char errBuf[100];
        sstrerror_r(savedSerrno, errBuf, sizeof(errBuf));
        oss << ": " << errBuf;
806
807
808
809
810
811
812
813
814
815
816
817
      }
      if(savedSerrno == SETIMEDOUT) {
        oss << ": timeout=" << timeout;
      }

      castor::exception::Exception ex(SECOMERR);
      ex.getMessage() << oss.str();
      throw ex;
    }
  case 0:
    {
      std::stringstream oss;
Steven Murray's avatar
Steven Murray committed
818
      oss << "Failed to write " << nbBytes << " bytes to socket: ";
819
820
821
822
823
824
825
826
827
828
      writeSockDescription(oss, socketFd);
      oss << ": Connection dropped";

      castor::exception::Exception ex(SECONNDROP);
      ex.getMessage() << oss.str();
      throw ex;
    }
  default:
    if(rc != nbBytes) {
      std::stringstream oss;
Steven Murray's avatar
Steven Murray committed
829
      oss << "Failed to write " << nbBytes << " bytes to socket: ";
830
831
832
833
834
835
836
837
838
839
840
841
      writeSockDescription(oss, socketFd);
      oss
        << ": Wrote the wrong number of bytes"
        << ": Expected: " << nbBytes
        << ": Wrote: " << rc;
      castor::exception::Exception ex(SECOMERR);
      ex.getMessage() << oss.str();
      throw ex;
    }
  }
}

842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
//------------------------------------------------------------------------------
// getAddrInfoErrorString
// 
// Helper function used to know which string is associated with a specific errno
// returned by getaddrinfo. This function is needed because the standard one
// (gai_strerror) is not thread-safe on all systems.
//------------------------------------------------------------------------------
static void getAddrInfoErrorString(const int rc, char *buf, const size_t size) {
  switch(rc) {
    case EAI_BADFLAGS:
      strncpy(buf, "Invalid value for `ai_flags' field.", size);
      break;
    case EAI_NONAME:
      strncpy(buf, "NAME or SERVICE is unknown.", size);
      break;
    case EAI_AGAIN:
      strncpy(buf, "Temporary failure in name resolution.", size);
      break;
    case EAI_FAIL:
      strncpy(buf, "Non-recoverable failure in name res.", size);
      break;
    case EAI_NODATA:
      strncpy(buf, "No address associated with NAME.", size);
      break;
    case EAI_FAMILY:
      strncpy(buf, "`ai_family' not supported.", size);
      break;
    case EAI_SOCKTYPE:
      strncpy(buf, "`ai_socktype' not supported.", size);
      break;
    case EAI_SERVICE:
      strncpy(buf, "SERVICE not supported for `ai_socktype'.", size);
      break;
    case EAI_ADDRFAMILY:
      strncpy(buf, "Address family for NAME not supported.", size);
      break;
    case EAI_MEMORY:
      strncpy(buf, "Memory allocation failure.", size);
      break;
    case EAI_SYSTEM:
      strncpy(buf, "System error returned in `errno'.", size);
      break;
    case EAI_OVERFLOW:
      strncpy(buf, "Argument buffer overflow.", size);
      break;
    case EAI_INPROGRESS:
      strncpy(buf, "Processing request in progress.", size);
      break;
    case EAI_CANCELED:
      strncpy(buf, "Request canceled.", size);
      break;
    case EAI_NOTCANCELED:
      strncpy(buf, "Request not canceled.", size);
      break;
    case EAI_ALLDONE:
      strncpy(buf, "All requests done.", size);
      break;
    case EAI_INTR:
      strncpy(buf, "Interrupted by a signal.", size);
      break;
    case EAI_IDN_ENCODE:
      strncpy(buf, "IDN encoding failed.", size);
      break;
    default:
      strncpy(buf, "Unknown error", size);
      break;
  }
}

911
912
913
914
915
916
917
918
//------------------------------------------------------------------------------
// connectWithTimeout
//------------------------------------------------------------------------------
int castor::io::connectWithTimeout(
  const std::string    &hostName,
  const unsigned short port,
  const int            timeout)
  throw(castor::exception::TimeOut, castor::exception::Exception) {
919
920
  std::ostringstream portStream;
  portStream << port;
921
922
923
924
  struct addrinfo hints;
  memset(&hints, '\0', sizeof(hints));
  hints.ai_family = AF_INET;
  hints.ai_socktype = SOCK_STREAM;
925
926
927
928
929
930
931
  
  struct addrinfo *res = NULL;
  int rc = getaddrinfo(hostName.c_str(), portStream.str().c_str(), &hints, &res);
  // If getaddrinfo() returned a negative value
  if (0!=rc) {
    char errBuf[100];
    getAddrInfoErrorString(rc, errBuf, sizeof(errBuf));
932
    castor::exception::Exception ex;
933
    ex.getMessage() << "getaddrinfo() failed: " << errBuf;
934
935
    throw ex;
  }
936
937
  struct sockaddr_in s_in;
  if(AF_INET != res->ai_family or SOCK_STREAM != res->ai_socktype or sizeof(s_in) != res->ai_addrlen) {
938
    castor::exception::Exception ex;
939
940
941
942
943
944
945
946
947
948
949
    ex.getMessage() << "getaddrinfo() bad result: either ai_family or ai_socktype or ai_addrlen are wrong";
    freeaddrinfo(res);
    throw ex;
  }
  memcpy(&s_in, res->ai_addr, sizeof(s_in));
  int protocol = res->ai_protocol;
  socklen_t length = res->ai_addrlen;
  freeaddrinfo(res);
  
  rc = connectWithTimeout(AF_INET, SOCK_STREAM, protocol,
          (struct sockaddr *)(&s_in), length, timeout);
950
  return rc;
951
952
}

953
//------------------------------------------------------------------------------
954
// connectWithTimeout
955
//------------------------------------------------------------------------------
956
957
958
959
960
961
962
963
964
965
966
967
int castor::io::connectWithTimeout(
  const int             sockDomain,
  const int             sockType,
  const int             sockProtocol,
  const struct sockaddr *address,
  const socklen_t       address_len,
  const int             timeout)
  throw(castor::exception::TimeOut, castor::exception::Exception) {

  // Create the socket for the new connection
  utils::SmartFd smartSock(socket(sockDomain, sockType, sockProtocol));
  if(-1 == smartSock.get()) {
968
969
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
970
    castor::exception::Exception ex;
971
    ex.getMessage() <<
Steven Murray's avatar
Steven Murray committed
972
      "Failed to create socket for new connection"
973
      ": Call to socket() failed: " << errBuf;
974
975
976
977
978
979
    throw ex;
  }

  // Get the orginal file-control flags of the socket
  const int orginalFileControlFlags = fcntl(smartSock.get(), F_GETFL, 0);
  if(-1 == orginalFileControlFlags) {
980
981
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
982
    castor::exception::Exception ex;
983
    ex.getMessage() <<
Steven Murray's avatar
Steven Murray committed
984
      "Failed to get the original file-control flags of the socket"
985
      ": Call to fcntl() failed: " << errBuf;
986
987
988
989
990
991
    throw ex;
  }

  // Set the O_NONBLOCK file-control flag of the socket
  if(-1 == fcntl(smartSock.get(), F_SETFL,
    orginalFileControlFlags | O_NONBLOCK)) {
992
993
    char errBuf[100];
    sstrerror_r(errno, errBuf, sizeof(errBuf));
994
    castor::exception::Exception ex;
995
    ex.getMessage() <<
Steven Murray's avatar
Steven Murray committed
996
      "Failed to set the O_NONBLOCK file-control flag"
997
      ": Call to fcntl() failed: " << errBuf;
998
999
1000
    throw ex;
  }