ConnectionFileDescriptor.cpp revision d976b0ee3aa6d32105ac7a9183f4601544563089
1//===-- ConnectionFileDescriptor.cpp ----------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "lldb/Core/ConnectionFileDescriptor.h" 11 12// C Includes 13#include <errno.h> 14#include <fcntl.h> 15#include <arpa/inet.h> 16#include <netdb.h> 17#include <netinet/in.h> 18#include <netinet/tcp.h> 19#include <sys/socket.h> 20#include <sys/un.h> 21#include <sys/types.h> 22#include <string.h> 23#include <stdlib.h> 24 25// C++ Includes 26// Other libraries and framework includes 27// Project includes 28#include "lldb/lldb-private-log.h" 29#include "lldb/Interpreter/Args.h" 30#include "lldb/Core/Communication.h" 31#include "lldb/Core/Log.h" 32#include "lldb/Core/RegularExpression.h" 33#include "lldb/Core/Timer.h" 34 35using namespace lldb; 36using namespace lldb_private; 37 38ConnectionFileDescriptor::ConnectionFileDescriptor () : 39 Connection(), 40 m_fd (-1), 41 m_is_socket (false), 42 m_should_close_fd (false) 43{ 44 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT, 45 "%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", 46 this); 47} 48 49ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : 50 Connection(), 51 m_fd (fd), 52 m_is_socket (false), 53 m_should_close_fd (owns_fd) 54{ 55 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT, 56 "%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", 57 this, fd, owns_fd); 58} 59 60 61ConnectionFileDescriptor::~ConnectionFileDescriptor () 62{ 63 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT, 64 "%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", 65 this); 66 Disconnect (NULL); 67} 68 69bool 70ConnectionFileDescriptor::IsConnected () const 71{ 72 return m_fd >= 0; 73} 74 75ConnectionStatus 76ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) 77{ 78 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, 79 "%p ConnectionFileDescriptor::Connect (url = '%s')", 80 this, s); 81 82 if (s && s[0]) 83 { 84 char *end = NULL; 85 if (strstr(s, "listen://")) 86 { 87 // listen://HOST:PORT 88 unsigned long listen_port = ::strtoul(s + strlen("listen://"), &end, 0); 89 return SocketListen (listen_port, error_ptr); 90 } 91 else if (strstr(s, "unix-accept://")) 92 { 93 // unix://SOCKNAME 94 return NamedSocketAccept (s + strlen("unix-accept://"), error_ptr); 95 } 96 else if (strstr(s, "connect://")) 97 { 98 return SocketConnect (s + strlen("connect://"), error_ptr); 99 } 100 else if (strstr(s, "fd://")) 101 { 102 // Just passing a native file descriptor within this current process 103 // that is already opened (possibly from a service or other source). 104 s += strlen ("fd://"); 105 bool success = false; 106 m_fd = Args::StringToSInt32 (s, -1, 0, &success); 107 if (success) 108 { 109 // We have what looks to be a valid file descriptor, but we 110 // should make it is. We currently are doing this by trying to 111 // get the flags from the file descriptor and making sure it 112 // isn't a bad fd. We also need to enable non blocking mode for 113 // the fd if it already isn't. 114 errno = 0; 115 int flags = ::fcntl (m_fd, F_GETFL, 0); 116 if (flags == -1 || errno == EBADF) 117 { 118 if (error_ptr) 119 error_ptr->SetErrorStringWithFormat ("stale file descriptor: %s", s); 120 m_fd = -1; 121 return eConnectionStatusError; 122 } 123 else 124 { 125 if ((flags & O_NONBLOCK) == 0) 126 { 127 flags |= O_NONBLOCK; 128 ::fcntl (m_fd, F_SETFL, flags); 129 } 130 m_should_close_fd = true; 131 return eConnectionStatusSuccess; 132 } 133 } 134 135 if (error_ptr) 136 error_ptr->SetErrorStringWithFormat ("invalid file descriptor: \"fd://%s\"", s); 137 m_fd = -1; 138 return eConnectionStatusError; 139 } 140 else if (strstr(s, "file://")) 141 { 142 // file:///PATH 143 const char *path = s + strlen("file://"); 144 m_fd = ::open (path, O_RDWR); 145 if (m_fd == -1) 146 { 147 if (error_ptr) 148 error_ptr->SetErrorToErrno(); 149 return eConnectionStatusError; 150 } 151 152 int flags = ::fcntl (m_fd, F_GETFL, 0); 153 if (flags >= 0) 154 { 155 if ((flags & O_NONBLOCK) == 0) 156 { 157 flags |= O_NONBLOCK; 158 ::fcntl (m_fd, F_SETFL, flags); 159 } 160 } 161 m_should_close_fd = true; 162 return eConnectionStatusSuccess; 163 } 164 if (error_ptr) 165 error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); 166 return eConnectionStatusError; 167 } 168 if (error_ptr) 169 error_ptr->SetErrorString("invalid connect arguments"); 170 return eConnectionStatusError; 171} 172 173ConnectionStatus 174ConnectionFileDescriptor::Disconnect (Error *error_ptr) 175{ 176 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, 177 "%p ConnectionFileDescriptor::Disconnect ()", 178 this); 179 if (m_should_close_fd == false) 180 { 181 m_fd = -1; 182 return eConnectionStatusSuccess; 183 } 184 return Close (m_fd, error_ptr); 185} 186 187size_t 188ConnectionFileDescriptor::Read (void *dst, size_t dst_len, ConnectionStatus &status, Error *error_ptr) 189{ 190 LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); 191 if (log) 192 log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %zu)...", 193 this, m_fd, dst, dst_len); 194 195 Error error; 196 ssize_t bytes_read = ::read (m_fd, dst, dst_len); 197 if (bytes_read == 0) 198 { 199 error.Clear(); // End-of-file. Do not automatically close; pass along for the end-of-file handlers. 200 status = eConnectionStatusEndOfFile; 201 } 202 else if (bytes_read < 0) 203 { 204 error.SetErrorToErrno(); 205 } 206 else 207 { 208 error.Clear(); 209 } 210 211 if (log) 212 log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %zu) => %zi, error = %s", 213 this, 214 m_fd, 215 dst, 216 dst_len, 217 bytes_read, 218 error.AsCString()); 219 220 if (error_ptr) 221 *error_ptr = error; 222 223 if (error.Fail()) 224 { 225 uint32_t error_value = error.GetError(); 226 switch (error_value) 227 { 228 case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read. 229 status = eConnectionStatusSuccess; 230 return 0; 231 232 case EFAULT: // Buf points outside the allocated address space. 233 case EINTR: // A read from a slow device was interrupted before any data arrived by the delivery of a signal. 234 case EINVAL: // The pointer associated with fildes was negative. 235 case EIO: // An I/O error occurred while reading from the file system. 236 // The process group is orphaned. 237 // The file is a regular file, nbyte is greater than 0, 238 // the starting position is before the end-of-file, and 239 // the starting position is greater than or equal to the 240 // offset maximum established for the open file 241 // descriptor associated with fildes. 242 case EISDIR: // An attempt is made to read a directory. 243 case ENOBUFS: // An attempt to allocate a memory buffer fails. 244 case ENOMEM: // Insufficient memory is available. 245 status = eConnectionStatusError; 246 break; // Break to close.... 247 248 case ENOENT: // no such file or directory 249 case EBADF: // fildes is not a valid file or socket descriptor open for reading. 250 case ENXIO: // An action is requested of a device that does not exist.. 251 // A requested action cannot be performed by the device. 252 case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. 253 case ENOTCONN: // A read is attempted on an unconnected socket. 254 status = eConnectionStatusLostConnection; 255 break; // Break to close.... 256 257 case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. 258 status = eConnectionStatusTimedOut; 259 return 0; 260 } 261 262// if (log) 263// error->Log(log, "::read ( %i, %p, %zu ) => %i", m_fd, dst, dst_len, bytesread); 264 Close (m_fd, NULL); 265 return 0; 266 } 267 return bytes_read; 268} 269 270size_t 271ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) 272{ 273 LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); 274 if (log) 275 log->Printf ("%p ConnectionFileDescriptor::Write (src = %p, src_len = %zu)", this, src, src_len); 276 277 if (!IsConnected ()) 278 { 279 if (error_ptr) 280 error_ptr->SetErrorString("not connected"); 281 status = eConnectionStatusNoConnection; 282 return 0; 283 } 284 285 286 Error error; 287 288 ssize_t bytes_sent = 0; 289 290 if (m_is_socket) 291 bytes_sent = ::send (m_fd, src, src_len, 0); 292 else 293 bytes_sent = ::write (m_fd, src, src_len); 294 295 if (bytes_sent < 0) 296 error.SetErrorToErrno (); 297 else 298 error.Clear (); 299 300 if (log) 301 { 302 if (m_is_socket) 303 log->Printf ("%p ConnectionFileDescriptor::Write() ::send (socket = %i, src = %p, src_len = %zu, flags = 0) => %zi (error = %s)", 304 this, m_fd, src, src_len, bytes_sent, error.AsCString()); 305 else 306 log->Printf ("%p ConnectionFileDescriptor::Write() ::write (fd = %i, src = %p, src_len = %zu) => %zi (error = %s)", 307 this, m_fd, src, src_len, bytes_sent, error.AsCString()); 308 } 309 310 if (error_ptr) 311 *error_ptr = error; 312 313 if (error.Fail()) 314 { 315 switch (error.GetError()) 316 { 317 case EAGAIN: 318 case EINTR: 319 status = eConnectionStatusSuccess; 320 return 0; 321 322 case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. 323 case ENOTCONN: // A read is attempted on an unconnected socket. 324 status = eConnectionStatusLostConnection; 325 break; // Break to close.... 326 327 default: 328 status = eConnectionStatusError; 329 break; // Break to close.... 330 } 331 332 Close (m_fd, NULL); 333 return 0; 334 } 335 336 status = eConnectionStatusSuccess; 337 return bytes_sent; 338} 339 340ConnectionStatus 341ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) 342{ 343 LogSP log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); 344 if (log) 345 log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); 346 struct timeval *tv_ptr; 347 struct timeval tv; 348 if (timeout_usec == UINT32_MAX) 349 { 350 // Infinite wait... 351 tv_ptr = NULL; 352 } 353 else 354 { 355 TimeValue time_value; 356 time_value.OffsetWithMicroSeconds (timeout_usec); 357 tv = time_value.GetAsTimeVal(); 358 tv_ptr = &tv; 359 } 360 361 while (IsConnected()) 362 { 363 fd_set read_fds; 364 FD_ZERO (&read_fds); 365 FD_SET (m_fd, &read_fds); 366 int nfds = m_fd + 1; 367 368 Error error; 369 370 371 log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION); 372 if (log) 373 log->Printf("%p ConnectionFileDescriptor::Write() ::select (nfds = %i, fd = %i, NULL, NULL, timeout = %p)...", 374 this, nfds, m_fd, tv_ptr); 375 376 const int num_set_fds = ::select (nfds, &read_fds, NULL, NULL, tv_ptr); 377 if (num_set_fds < 0) 378 error.SetErrorToErrno(); 379 else 380 error.Clear(); 381 382 log = lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION); 383 if (log) 384 log->Printf("%p ConnectionFileDescriptor::Write() ::select (nfds = %i, fd = %i, NULL, NULL, timeout = %p) => %d, error = %s", 385 this, nfds, m_fd, tv_ptr, num_set_fds, error.AsCString()); 386 387 if (error_ptr) 388 *error_ptr = error; 389 390 if (error.Fail()) 391 { 392 switch (error.GetError()) 393 { 394 case EBADF: // One of the descriptor sets specified an invalid descriptor. 395 return eConnectionStatusLostConnection; 396 397 case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. 398 default: // Other unknown error 399 return eConnectionStatusError; 400 401 case EAGAIN: // The kernel was (perhaps temporarily) unable to 402 // allocate the requested number of file descriptors, 403 // or we have non-blocking IO 404 case EINTR: // A signal was delivered before the time limit 405 // expired and before any of the selected events 406 // occurred. 407 break; // Lets keep reading to until we timeout 408 } 409 } 410 else if (num_set_fds == 0) 411 { 412 return eConnectionStatusTimedOut; 413 } 414 else if (num_set_fds > 0) 415 { 416 return eConnectionStatusSuccess; 417 } 418 } 419 420 if (error_ptr) 421 error_ptr->SetErrorString("not connected"); 422 return eConnectionStatusLostConnection; 423} 424 425ConnectionStatus 426ConnectionFileDescriptor::Close (int& fd, Error *error_ptr) 427{ 428 if (error_ptr) 429 error_ptr->Clear(); 430 bool success = true; 431 if (fd >= 0) 432 { 433 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, 434 "%p ConnectionFileDescriptor::Close (fd = %i)", 435 this, 436 fd); 437 438 success = ::close (fd) == 0; 439 if (!success && error_ptr) 440 { 441 // Only set the error if we have been asked to since something else 442 // might have caused us to try and shut down the connection and may 443 // have already set the error. 444 error_ptr->SetErrorToErrno(); 445 } 446 fd = -1; 447 } 448 m_is_socket = false; 449 if (success) 450 return eConnectionStatusSuccess; 451 else 452 return eConnectionStatusError; 453} 454 455ConnectionStatus 456ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *error_ptr) 457{ 458 ConnectionStatus result = eConnectionStatusError; 459 struct sockaddr_un saddr_un; 460 461 m_is_socket = true; 462 463 int listen_socket = ::socket (AF_UNIX, SOCK_STREAM, 0); 464 if (listen_socket == -1) 465 { 466 if (error_ptr) 467 error_ptr->SetErrorToErrno(); 468 return eConnectionStatusError; 469 } 470 471 saddr_un.sun_family = AF_UNIX; 472 ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); 473 saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; 474#if defined(__APPLE__) || defined(__FreeBSD__) 475 saddr_un.sun_len = SUN_LEN (&saddr_un); 476#endif 477 478 if (::bind (listen_socket, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) == 0) 479 { 480 if (::listen (listen_socket, 5) == 0) 481 { 482 m_fd = ::accept (listen_socket, NULL, 0); 483 if (m_fd > 0) 484 { 485 m_should_close_fd = true; 486 487 if (error_ptr) 488 error_ptr->Clear(); 489 result = eConnectionStatusSuccess; 490 } 491 } 492 } 493 494 if (result != eConnectionStatusSuccess) 495 { 496 if (error_ptr) 497 error_ptr->SetErrorToErrno(); 498 } 499 // We are done with the listen port 500 Close (listen_socket, NULL); 501 return result; 502} 503 504ConnectionStatus 505ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *error_ptr) 506{ 507 Close (m_fd, NULL); 508 m_is_socket = true; 509 510 // Open the socket that was passed in as an option 511 struct sockaddr_un saddr_un; 512 m_fd = ::socket (AF_UNIX, SOCK_STREAM, 0); 513 if (m_fd == -1) 514 { 515 if (error_ptr) 516 error_ptr->SetErrorToErrno(); 517 return eConnectionStatusError; 518 } 519 520 saddr_un.sun_family = AF_UNIX; 521 ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); 522 saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; 523#if defined(__APPLE__) || defined(__FreeBSD__) 524 saddr_un.sun_len = SUN_LEN (&saddr_un); 525#endif 526 527 if (::connect (m_fd, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0) 528 { 529 if (error_ptr) 530 error_ptr->SetErrorToErrno(); 531 Close (m_fd, NULL); 532 return eConnectionStatusError; 533 } 534 if (error_ptr) 535 error_ptr->Clear(); 536 return eConnectionStatusSuccess; 537} 538 539ConnectionStatus 540ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_ptr) 541{ 542 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, 543 "%p ConnectionFileDescriptor::SocketListen (port = %i)", 544 this, listen_port_num); 545 546 Close (m_fd, NULL); 547 m_is_socket = true; 548 int listen_port = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 549 if (listen_port == -1) 550 { 551 if (error_ptr) 552 error_ptr->SetErrorToErrno(); 553 return eConnectionStatusError; 554 } 555 556 // enable local address reuse 557 SetSocketOption (listen_port, SOL_SOCKET, SO_REUSEADDR, 1); 558 559 struct sockaddr_in sa; 560 ::memset (&sa, 0, sizeof sa); 561 sa.sin_family = AF_INET; 562 sa.sin_port = htons (listen_port_num); 563 sa.sin_addr.s_addr = htonl (INADDR_ANY); 564 565 int err = ::bind (listen_port, (struct sockaddr *) &sa, sizeof(sa)); 566 if (err == -1) 567 { 568 if (error_ptr) 569 error_ptr->SetErrorToErrno(); 570 Close (listen_port, NULL); 571 return eConnectionStatusError; 572 } 573 574 err = ::listen (listen_port, 1); 575 if (err == -1) 576 { 577 if (error_ptr) 578 error_ptr->SetErrorToErrno(); 579 Close (listen_port, NULL); 580 return eConnectionStatusError; 581 } 582 583 m_fd = ::accept (listen_port, NULL, 0); 584 if (m_fd == -1) 585 { 586 if (error_ptr) 587 error_ptr->SetErrorToErrno(); 588 Close (listen_port, NULL); 589 return eConnectionStatusError; 590 } 591 592 // We are done with the listen port 593 Close (listen_port, NULL); 594 595 m_should_close_fd = true; 596 597 // Keep our TCP packets coming without any delays. 598 SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); 599 if (error_ptr) 600 error_ptr->Clear(); 601 return eConnectionStatusSuccess; 602} 603 604ConnectionStatus 605ConnectionFileDescriptor::SocketConnect (const char *host_and_port, Error *error_ptr) 606{ 607 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION, 608 "%p ConnectionFileDescriptor::SocketConnect (host/port = %s)", 609 this, host_and_port); 610 Close (m_fd, NULL); 611 m_is_socket = true; 612 613 RegularExpression regex ("([^:]+):([0-9]+)"); 614 if (regex.Execute (host_and_port, 2) == false) 615 { 616 if (error_ptr) 617 error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port); 618 return eConnectionStatusError; 619 } 620 std::string host_str; 621 std::string port_str; 622 if (regex.GetMatchAtIndex (host_and_port, 1, host_str) == false || 623 regex.GetMatchAtIndex (host_and_port, 2, port_str) == false) 624 { 625 if (error_ptr) 626 error_ptr->SetErrorStringWithFormat("invalid host:port specification '%s'", host_and_port); 627 return eConnectionStatusError; 628 } 629 630 int32_t port = Args::StringToSInt32 (port_str.c_str(), INT32_MIN); 631 if (port == INT32_MIN) 632 { 633 if (error_ptr) 634 error_ptr->SetErrorStringWithFormat("invalid port '%s'", port_str.c_str()); 635 return eConnectionStatusError; 636 } 637 // Create the socket 638 m_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 639 if (m_fd == -1) 640 { 641 if (error_ptr) 642 error_ptr->SetErrorToErrno(); 643 return eConnectionStatusError; 644 } 645 646 m_should_close_fd = true; 647 648 // Enable local address reuse 649 SetSocketOption (m_fd, SOL_SOCKET, SO_REUSEADDR, 1); 650 651 struct sockaddr_in sa; 652 ::memset (&sa, 0, sizeof (sa)); 653 sa.sin_family = AF_INET; 654 sa.sin_port = htons (port); 655 656 int inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); 657 658 if (inet_pton_result <= 0) 659 { 660 struct hostent *host_entry = gethostbyname (host_str.c_str()); 661 if (host_entry) 662 host_str = ::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list); 663 inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); 664 if (inet_pton_result <= 0) 665 { 666 667 if (error_ptr) 668 { 669 if (inet_pton_result == -1) 670 error_ptr->SetErrorToErrno(); 671 else 672 error_ptr->SetErrorStringWithFormat("invalid host string: '%s'", host_str.c_str()); 673 } 674 Close (m_fd, NULL); 675 return eConnectionStatusError; 676 } 677 } 678 679 if (-1 == ::connect (m_fd, (const struct sockaddr *)&sa, sizeof(sa))) 680 { 681 if (error_ptr) 682 error_ptr->SetErrorToErrno(); 683 Close (m_fd, NULL); 684 return eConnectionStatusError; 685 } 686 687 // Keep our TCP packets coming without any delays. 688 SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); 689 if (error_ptr) 690 error_ptr->Clear(); 691 return eConnectionStatusSuccess; 692} 693 694int 695ConnectionFileDescriptor::SetSocketOption(int fd, int level, int option_name, int option_value) 696{ 697#if defined(__MINGW32__) || defined(__MINGW64__) 698 const char* option_value_p = static_cast<const char*>(&option_value); 699#else // #if defined(__MINGW32__) || defined(__MINGW64__) 700 const void* option_value_p = &option_name; 701#endif // #if defined(__MINGW32__) || defined(__MINGW64__) 702 703 return ::setsockopt(fd, level, option_name, option_value_p, sizeof(option_value)); 704} 705 706 707