libftc
src/ftc.cpp
Vá para a documentação deste arquivo.
00001 
00002 #define TEC_FTC_SOURCE
00003 #define WIN32_LEAN_AND_MEAN
00004 
00005 #include "ftc.h"
00006 
00007 #ifdef TEC_WINDOWS
00008 #include <winsock2.h>
00009 #include <windows.h>
00010 #else
00011 #include <inttypes.h>
00012 #include <arpa/inet.h>
00013 #include <sys/types.h>
00014 #include <sys/socket.h>
00015 #include <netinet/in.h>
00016 #include <netdb.h>
00017 #include <errno.h>
00018 #include <unistd.h>
00019 #endif
00020 
00021 #include <stdexcept>
00022 #include <cstdlib>
00023 #include <cstdio>
00024 #include <cstring>
00025 #include <cassert>
00026 
00027 #include <vector>
00028 
00029 namespace ftc_detail {
00030 
00031 #ifdef TEC_WINDOWS
00032 typedef SOCKET socket_type;
00033 #define TEC_INVALID_SOCKET INVALID_SOCKET
00034 #define TEC_SOCKET_ERROR SOCKET_ERROR
00035 #else
00036 typedef int socket_type;
00037 #define TEC_INVALID_SOCKET (-1)
00038 #define TEC_SOCKET_ERROR (-1)
00039 #endif
00040 
00041 namespace {
00042 
00043 const std::size_t win32_minimum_buffer_size = 16;
00044 
00045 void send(socket_type socket, const char* buffer, std::size_t size);
00046 void recv(socket_type socket, char* buffer, std::size_t size);
00047 
00048 struct errno_buffer_ref
00049 {
00050   errno_buffer_ref(const char* buffer)
00051     : buffer(buffer) {}
00052   const char* buffer;
00053 };
00054 
00055 // errno_buffer has move semantics
00056 struct errno_buffer
00057 {
00058   errno_buffer(errno_buffer_ref r)
00059     : buffer(r.buffer) {}
00060   explicit errno_buffer(const char* buffer) : buffer(buffer) {}
00061   errno_buffer(errno_buffer& o)
00062     : buffer(o.release())
00063   {}
00064   errno_buffer& operator=(errno_buffer& o)
00065   {
00066     buffer = o.release();
00067     return *this;
00068   }
00069 #ifdef TEC_WINDOWS
00070   ~errno_buffer()
00071   {
00072     if(buffer)
00073       ::LocalFree(static_cast<HLOCAL>
00074                   (const_cast<void*>
00075                    (static_cast<const void*>(buffer))));
00076   }
00077 #endif
00078   operator errno_buffer_ref ()
00079   {
00080     return errno_buffer_ref(release());
00081   }
00082   const char* message() const { return buffer; }
00083   const char* release()
00084   {
00085     const char* b = buffer;
00086     buffer = 0;
00087     return b;
00088   }
00089 private:
00090   const char* buffer;
00091 };
00092 
00093 errno_buffer errno_message()
00094 {
00095 #ifdef linux
00096   return errno_buffer(sys_errlist[errno]);
00097 #elif defined(TEC_WINDOWS)
00098   const char* buffer = 0;
00099   DWORD r = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
00100                             | FORMAT_MESSAGE_FROM_SYSTEM
00101                             | FORMAT_MESSAGE_IGNORE_INSERTS
00102                             , 0, WSAGetLastError()
00103                             , 0, static_cast<LPTSTR>(static_cast<void*>(&buffer))
00104                             , 0, 0);
00105   if(buffer)
00106     return errno_buffer(buffer);
00107   else
00108   {
00109     std::abort();
00110     throw -1;
00111   }
00112 #else
00113   return errno_buffer("A system error");
00114 #endif
00115 }
00116 
00117 void close(socket_type s)
00118 {
00119 #ifdef TEC_WINDOWS
00120   ::closesocket(s);
00121 #else
00122   ::close(s);
00123 #endif  
00124 }
00125 
00126 struct socket
00127 {
00128   socket(int s)
00129     : sock(s) {}
00130 
00131   ~socket()
00132   {
00133     if(sock != TEC_INVALID_SOCKET)
00134       close(sock);
00135   }
00136 
00137   int release() 
00138   {
00139     int s = sock;
00140     sock = TEC_INVALID_SOCKET; 
00141     return s; 
00142   }
00143   int get() const { return sock; }
00144   
00145   int sock;
00146 };
00147 
00148 void throw_exception(int status, const char* message)
00149 {
00150   switch(status)
00151   {
00152   case -1:
00153     throw FailureException(message);
00154   case -2:
00155     throw InvalidKeyException(message);
00156   case -3:
00157     throw FileNotFoundException(message);
00158   case -4:
00159     throw NoPermissionException(message);
00160   case -5:
00161     throw FileLockedException(message);
00162   case -6:
00163     throw MaxClientsReachedException(message);
00164   case -7:
00165     throw FileNotOpenException(message);
00166   default:
00167     throw FtcException(static_cast<FtcErrorCode>(static_cast<unsigned char>(static_cast<char>(status))), message);
00168   }
00169 }
00170 
00171 void throw_exception(const char* message)
00172 {
00173   throw FailureException(message);
00174 }
00175 
00176 int recv_status(socket_type socket)
00177 {
00178 #ifdef TEC_WINDOWS
00179   char status = 0;
00180   WSABUF buffers = {1, &status};
00181   DWORD count = 0, flags = 0;
00182   int r = WSARecv(socket, &buffers, 1, &count, &flags
00183                   , 0, 0);
00184   if(r != 0)
00185   {
00186     errno_buffer b = errno_message();
00187     throw_exception(b.message());
00188   }
00189   else if(count == 0)
00190     throw_exception("EOF");
00191   return status;
00192 #else
00193   char status = 0;
00194   ssize_t r = ::recv(socket, &status, 1, 0);
00195   if(r == -1)
00196   {
00197     errno_buffer b = errno_message();
00198     throw_exception(b.message());
00199   }
00200   else if(r == 0)
00201     throw_exception("EOF");
00202   return status;
00203 #endif
00204 }
00205 
00206 #if defined(LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) || defined(TEC_WINDOWS)
00207 void endian_swap(char* buffer, std::size_t size)
00208 {
00209   std::vector<char> reverse_endian(size);
00210   std::copy(buffer, buffer + size, reverse_endian.rbegin());
00211   std::memcpy(buffer, &reverse_endian[0], size);
00212 }
00213 #else
00214 void endian_swap(char*, std::size_t) {}
00215 #endif
00216 
00217 unsigned long long return_long_long_command(socket_type socket, char command)
00218 {
00219   // This is a compile-time assertion that unsigned long long is 8 bytes
00220   char v[sizeof(unsigned long long) == 8?1:0];
00221   (void)v;
00222 
00223   send(socket, &command, 1);
00224 
00225   char recv_buffer[8];
00226   recv(socket, recv_buffer, sizeof(recv_buffer));
00227 
00228   endian_swap(recv_buffer, 8);
00229 
00230   unsigned long long return_value;
00231   std::memcpy(&return_value, recv_buffer
00232               , (std::min)(sizeof(return_value),sizeof(recv_buffer)));
00233   return return_value;
00234 }
00235 
00236 void send(socket_type socket, const char* buffer, std::size_t size)
00237 {
00238   std::size_t total = 0;
00239   while(total != size)
00240   {
00241 #ifdef TEC_WINDOWS
00242     WSABUF buffers = {size, const_cast<char*>(buffer)};
00243     DWORD count = 0;
00244     int r = WSASend(socket, &buffers, 1, &count, 0, 0, 0);
00245     if(r != 0)
00246     {
00247       errno_buffer b = errno_message();
00248       throw_exception(b.message());
00249     }
00250     else if(count == 0)
00251       throw_exception("EOF");
00252     total += count;
00253 #else
00254     ssize_t r = 0;
00255     do
00256     {
00257       r = ::send(socket, &buffer[total], size-total, 0);
00258     } while(r == -1 && errno == EINTR);
00259     if(r == -1)
00260     {
00261       errno_buffer b = errno_message();
00262       throw_exception(b.message());
00263     }
00264     else if(r == 0)
00265       throw_exception("EOF");
00266     total += r;
00267 #endif
00268   }
00269 }
00270 
00271 void recv(socket_type socket, char* buffer, std::size_t size)
00272 {
00273   std::size_t total = 0;
00274   while(total != size)
00275   {
00276 #ifdef TEC_WINDOWS
00277     WSABUF buffers = {size, buffer};
00278     DWORD count = 0;
00279     DWORD flags = 0;
00280     int r = ::WSARecv(socket, &buffers, 1, &count, &flags, 0, 0);
00281     if(r != 0)
00282     {
00283       errno_buffer b = errno_message();
00284       throw_exception(b.message());
00285     }
00286     else if(count == 0)
00287       throw_exception("EOF");
00288     total += count;
00289 #else
00290     ssize_t r = 0;
00291     do
00292     {
00293       r = ::recv(socket, &buffer[total], size-total, 0);
00294     } while(r == -1 && errno == EINTR);
00295     if(r == -1)
00296     {
00297       errno_buffer b = errno_message();
00298       ftc_detail::throw_exception(b.message());
00299     }
00300     else if(r == 0)
00301       ftc_detail::throw_exception("EOF");
00302     total += r;
00303 #endif
00304   }
00305 }
00306 
00307 
00308 }
00309 
00310 }
00311 
00312 ftc::ftc(const char* id, const char id_size, bool writable, const char* host
00313         , unsigned short port, const char* accessKey, const char key_size)
00314   : identifier (id)
00315   , identifier_size (id_size)
00316   , hostname(host)
00317   , tcp_port(port)
00318   , sock(TEC_INVALID_SOCKET)
00319   , writable(writable)
00320   , is_open(false)
00321   , access_key(accessKey)
00322   , access_key_size(key_size)
00323 {
00324 }
00325 
00326 void ftc::open(bool readonly)
00327 {
00328   if(!writable && !readonly)
00329     throw NoPermissionException("Permission set in constructor");
00330 
00331   writable = !readonly;
00332 
00333 #ifdef TEC_WINDOWS
00334   WORD wVersionRequested = MAKEWORD(2,2);
00335   WSADATA wsaData;
00336   int err = WSAStartup(wVersionRequested, &wsaData);
00337   if(err != 0)
00338   {
00339     ftc_detail::errno_buffer b = ftc_detail::errno_message();
00340     ftc_detail::throw_exception(b.message());
00341   }
00342 #endif  
00343   ftc_detail::socket s(::socket(AF_INET, SOCK_STREAM, 0));
00344   if(s.get() == TEC_INVALID_SOCKET)
00345   {
00346     ftc_detail::errno_buffer b = ftc_detail::errno_message();
00347     ftc_detail::throw_exception(b.message());
00348   }
00349 
00350   hostent* h = gethostbyname(hostname);
00351 
00352   if(!h)
00353   {
00354     ftc_detail::errno_buffer b = ftc_detail::errno_message();
00355     ftc_detail::throw_exception(b.message());
00356   }
00357 
00358   ::sockaddr_in addr = 
00359       {AF_INET, htons(tcp_port)}; // htons can't be qualified, because it can be a macro
00360 
00361   std::memcpy(&addr.sin_addr.s_addr, h->h_addr_list[0], sizeof(addr.sin_addr.s_addr));
00362 
00363   if(::connect(s.get(), static_cast<struct sockaddr*>(static_cast<void*>(&addr))
00364                , sizeof addr) == TEC_SOCKET_ERROR)
00365   {
00366     ftc_detail::errno_buffer b = ftc_detail::errno_message();
00367     ftc_detail::throw_exception(b.message());
00368   }
00369 
00370   sock = s.release();
00371 
00372   // authenticate
00373   {
00374     char size = access_key_size;
00375     ftc_detail::send(sock, &size, 1);
00376     ftc_detail::send(sock, access_key, access_key_size);
00377     int status = ftc_detail::recv_status(sock);
00378     if(status != 0)
00379       ftc_detail::throw_exception(status, "Authentication");
00380   }
00381 
00382   // open
00383   char buffer[2];
00384   buffer[0] = writable;
00385   buffer[1] = identifier_size;
00386   ftc_detail::send(sock, buffer, sizeof(buffer));
00387   ftc_detail::send(sock, identifier, buffer[1]);
00388   int status = ftc_detail::recv_status(sock);
00389   if(status != 0)
00390     ftc_detail::throw_exception(status, "open");
00391   is_open = true;
00392 }
00393 
00394 ftc::~ftc()
00395 {
00396   if(sock != -1)
00397     ftc_detail::close(sock);
00398 #ifdef TEC_WINDOWS
00399   WSACleanup();
00400 #endif
00401 }
00402 
00403 void ftc::close()
00404 {
00405   if(!is_open)
00406     throw FileNotOpenException("file not open - close");
00407 
00408   char command = 2;
00409   ftc_detail::send(sock, &command, sizeof(command));
00410   int status = ftc_detail::recv_status(sock);
00411   if(status < 0)
00412     ftc_detail::throw_exception(status, "close");
00413   is_open = false;
00414 }
00415 
00416 bool ftc::isOpen()
00417 {
00418   return is_open;
00419 }
00420 
00421 void ftc::setPosition( unsigned long long p)
00422 {
00423   if(!is_open)
00424     throw FileNotOpenException("file not open - close");
00425 
00426   char buf_start[1+8] = {5};
00427   std::memcpy(&buf_start[1], static_cast<const char*>(static_cast<const void*>(&p))
00428               , (std::min<std::size_t>)(sizeof(p), 8u));
00429   ftc_detail::endian_swap(&buf_start[1]
00430                           , (std::min<std::size_t>)(8u, sizeof(p)));
00431   ftc_detail::send(sock, buf_start, sizeof(buf_start));
00432   int status = ftc_detail::recv_status(sock);
00433   if(status < 0)
00434     ftc_detail::throw_exception(status, "setPosition");
00435 }
00436 
00437 void ftc::setReadBufferSize( unsigned long long size )
00438 {
00439 }
00440 
00441 unsigned long long ftc::getPosition()
00442 {
00443   if(!is_open)
00444     throw FileNotOpenException("file not open - close");
00445   return ftc_detail::return_long_long_command(sock, 4);
00446 }
00447 
00448 unsigned long long ftc::getReadBufferSize()
00449 {
00450   return 0;
00451 }
00452 
00453 void ftc::setSize(unsigned long long s)
00454 {
00455   if(!is_open)
00456     throw FileNotOpenException("file not open - close");
00457   else if(!writable)
00458     throw NoPermissionException("mutable operation on read-only file");
00459   char buf_start[1+8] = {3};
00460   std::memcpy(&buf_start[1], static_cast<const char*>(static_cast<const void*>(&s))
00461               , (std::min<std::size_t>)(sizeof(s), 8u));
00462   ftc_detail::endian_swap(&buf_start[1]
00463                           , (std::min<std::size_t>)(8u, sizeof(s)));
00464   ftc_detail::send(sock, buf_start, sizeof(buf_start));
00465   int status = ftc_detail::recv_status(sock);
00466   if(status < 0)
00467     ftc_detail::throw_exception(status, "setSize");
00468 }
00469 
00470 unsigned long long ftc::getSize() const
00471 {
00472   if(!is_open)
00473     throw FileNotOpenException("file not open - close");
00474   return ftc_detail::return_long_long_command(sock, 6);
00475 }
00476 
00477 unsigned long long ftc::read(char* data, unsigned long long size, unsigned long long p)
00478 {
00479   if(!is_open)
00480     throw FileNotOpenException("file not open - close");
00481   char buf_start[1+8+8] = {7};
00482   std::memcpy(&buf_start[1], static_cast<const char*>(static_cast<const void*>(&p))
00483               , (std::min<std::size_t>)(sizeof(p), sizeof(buf_start)-1));
00484   std::memcpy(&buf_start[1+8], static_cast<const char*>(static_cast<const void*>(&size))
00485               , (std::min<std::size_t>)(sizeof(size), sizeof(buf_start)-1));
00486   ftc_detail::endian_swap(&buf_start[1]
00487                           , (std::min<std::size_t>)(8u, sizeof(p)));
00488   ftc_detail::endian_swap(&buf_start[1+8]
00489                           , (std::min<std::size_t>)(8u, sizeof(p)));
00490   ftc_detail::send(sock, buf_start, sizeof(buf_start));
00491 
00492   char* current = data;
00493   char* last = data + size;
00494   while(current != last)
00495   {
00496 #ifdef TEC_WINDOWS
00497     WSABUF buffers = {last-current, current};
00498     DWORD count = 0, flags = 0;
00499     bool retry = false;
00500     int r = 0;
00501     do
00502     {
00503       r = ::WSARecv(sock, &buffers, 1, &count, &flags, 0, 0);
00504       if(r == -1 && WSAGetLastError() == WSAENOBUFS
00505          && buffers.len > ftc_detail::win32_minimum_buffer_size*2)
00506       {
00507         buffers.len /= 2;
00508         retry = true;
00509       }
00510       else
00511          retry = false;
00512     } while(retry);
00513     if(r != 0)
00514     {
00515       ftc_detail::errno_buffer b = ftc_detail::errno_message();
00516       ftc_detail::throw_exception(b.message());
00517     }
00518     else if(count == 0)
00519       return current - data;
00520     current += count;
00521 #else
00522     ssize_t r = 0;
00523     do
00524     {
00525       r = ::recv(sock, current, last-current, 0);
00526     } while(r == -1 && errno == EINTR);
00527     if(r == -1)
00528     {
00529       ftc_detail::errno_buffer b = ftc_detail::errno_message();
00530       ftc_detail::throw_exception(b.message());
00531     }
00532     else if(r == 0)
00533       return current - data;
00534     current += r;
00535 #endif
00536     assert(current <= last);
00537   }
00538 
00539   return current-data;
00540 }
00541 
00542 unsigned long long ftc::write(char const* data, unsigned long long size, unsigned long long p)
00543 {
00544   if(!is_open)
00545     throw FileNotOpenException("file not open - close");
00546   else if(!writable)
00547     throw NoPermissionException("mutable operation on read-only file");
00548   char buf_start[1+8+8] = {8};
00549   std::memcpy(&buf_start[1], static_cast<const char*>(static_cast<const void*>(&p))
00550               , (std::min<std::size_t>)(sizeof(p), 8u));
00551   std::memcpy(&buf_start[1+8], static_cast<const char*>(static_cast<const void*>(&size))
00552               , (std::min<std::size_t>)(sizeof(size), 8u));
00553   ftc_detail::endian_swap(&buf_start[1]
00554                           , (std::min<std::size_t>)(8u, sizeof(p)));
00555   ftc_detail::endian_swap(&buf_start[1+8]
00556                           , (std::min<std::size_t>)(8u, sizeof(size)));
00557   ftc_detail::send(sock, buf_start, sizeof(buf_start));
00558 
00559   const char* current = data
00560     , *last = data + size;
00561   while(current != last)
00562   {
00563 #ifdef TEC_WINDOWS
00564     WSABUF buffers = {last-current, const_cast<char*>(current)};
00565     DWORD count = 0;
00566     bool retry = false;
00567     int r = 0;
00568     do
00569     {
00570       r = ::WSASend(sock, &buffers, 1, &count, 0, 0, 0);
00571       if(r == -1 && WSAGetLastError() == WSAENOBUFS
00572          && buffers.len > ftc_detail::win32_minimum_buffer_size*2)
00573       {
00574         buffers.len /= 2;
00575         retry = true;
00576       }
00577       else
00578          retry = false;
00579     } while(retry);
00580     if(r != 0)
00581     {
00582       ftc_detail::errno_buffer b = ftc_detail::errno_message();
00583       ftc_detail::throw_exception(b.message());
00584     }
00585     else if(count == 0)
00586       return current - data;
00587     current += count;
00588 #else
00589     ssize_t r = 0;
00590     do
00591     {
00592       r = ::send(sock, current, last-current, 0);
00593     } while(r == -1 && errno == EINTR);
00594     if(r < 0)
00595     {
00596       ftc_detail::errno_buffer b = ftc_detail::errno_message();
00597       ftc_detail::throw_exception(b.message());
00598     }
00599     else if(r == 0)
00600       return current - data;
00601     current += r;
00602 #endif
00603     assert(current <= last);
00604   }
00605 
00606   int status = ftc_detail::recv_status(sock);
00607   if(status < 0)
00608     ftc_detail::throw_exception(status, "write");
00609 
00610   return current-data;
00611 }
00612 
00613 unsigned long long ftc::transferTo(unsigned long long position, unsigned long long nbytes, FILE* fd, char* buffer)
00614 {
00615   if(!is_open)
00616     throw FileNotOpenException("file not open - close");
00617 
00618   if(nbytes == 0)
00619     return 0;
00620 
00621   std::vector<char> new_buffer((std::min)(1024u*1024u, static_cast<unsigned int>(nbytes)));
00622 
00623   unsigned long long bytes_to_read = nbytes;
00624 
00625   while(bytes_to_read != 0)
00626   {
00627     unsigned long long r = read(&new_buffer[0], new_buffer.size(), position);
00628     if(r == 0)
00629       return nbytes - bytes_to_read;
00630     position += r;
00631     bytes_to_read -= r;
00632 #ifdef _MSC_VER
00633 #pragma warning(push)
00634 #pragma warning(disable:4244) // warning C4244: 'argument' : 
00635                               // conversion from 'unsigned __int64' to 'size_t', possible loss of data
00636 #endif
00637     fwrite(&new_buffer[0], r, 1, fd);
00638 #ifdef _MSC_VER
00639 #pragma warning(pop)
00640 #endif
00641   }
00642 
00643   return nbytes;
00644 }
00645 
00646