libftc
|
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