#include "sockets.h" #include "Tools/Exceptions.h" #include "Tools/time-func.h" #include "Processor/OnlineOptions.h" #include #include using namespace std; void error(const char *str, bool interrupted, size_t length) { int old_errno = errno; char err[1000]; gethostname(err, 1000); err[999] = 0; string message = string() + "Fatal communication error on " + err + ": " + str + " (" + strerror(old_errno) + ")"; if (length) message += " when sending " + to_string(length) + " bytes"; if (interrupted) message += "\nThis is probably because another party encountered a problem."; exit_error(message); } void set_up_client_socket(int& mysocket,const char* hostname,int Portnum) { struct addrinfo hints, *ai=NULL,*rp; memset (&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_flags = AI_CANONNAME; char my_name[512]; memset(my_name,0,512*sizeof(octet)); gethostname((char*)my_name,512); int erp; for (int i = 0; i < CONNECTION_TIMEOUT; i++) { erp=getaddrinfo (hostname, NULL, &hints, &ai); if (erp == 0) { break; } else { cerr << "getaddrinfo on " << my_name << " has returned '" << gai_strerror(erp) << "' for " << hostname << ", trying again in a second ..." << endl; if (ai) freeaddrinfo(ai); sleep(1); } } if (erp!=0) { error("set_up_socket:getaddrinfo"); } bool success = false; socklen_t len = 0; const struct sockaddr* addr = 0; for (rp=ai; rp!=NULL; rp=rp->ai_next) { addr = ai->ai_addr; if (ai->ai_family == AF_INET) { len = ai->ai_addrlen; success = true; continue; } } if (not success) { for (rp = ai; rp != NULL; rp = rp->ai_next) cerr << "Family on offer: " << ai->ai_family << endl; exit_error(string("No AF_INET for ") + (char*)hostname + " on " + (char*)my_name); } Timer timer; timer.start(); struct sockaddr_in* addr4 = (sockaddr_in*) addr; addr4->sin_port = htons(Portnum); // set destination port number #ifdef DEBUG_IPV4 cout << "connect to ip " << hex << addr4->sin_addr.s_addr << " port " << addr4->sin_port << dec << endl; #endif int attempts = 0; long wait = 1; int fl; int connect_errno; do { mysocket = socket(AF_INET, SOCK_STREAM, 0); if (mysocket < 0) error("set_up_socket:socket"); fl = connect(mysocket, addr, len); connect_errno = errno; attempts++; if (fl != 0) { close(mysocket); usleep(wait < 1000 ? wait *= 2 : wait); if (OnlineOptions::singleton.has_option("debug_networking")) { string msg = "Connecting to " + string(hostname) + ":" + to_string(Portnum) + " failed"; errno = connect_errno; perror(msg.c_str()); } } errno = connect_errno; } while (fl == -1 && (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EINPROGRESS || errno == ECONNRESET) && timer.elapsed() < CONNECTION_TIMEOUT); if (fl < 0) { exit_error( string() + "cannot connect from " + my_name + " to " + hostname + ":" + to_string(Portnum) + " after " + to_string(attempts) + " attempts in " + to_string(CONNECTION_TIMEOUT) + " seconds because " + strerror(connect_errno) + ". " "https://mp-spdz.readthedocs.io/en/latest/troubleshooting.html#" "connection-failures has more information on port requirements."); } freeaddrinfo(ai); if (OnlineOptions::singleton.has_option("time_connect")) cerr << "Connection took " << timer.elapsed() << " seconds." << endl; /* disable Nagle's algorithm */ int one=1; fl= setsockopt(mysocket, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(int)); if (fl<0) { error("set_up_socket:setsockopt"); } /* * The following code block is either taken directly from or derived from the solutions posted at: * https://stackoverflow.com/questions/20188718/configuring-tcp-keep-alive-with-boostasio * https://stackoverflow.com/questions/23669005/tcp-keepalive-protocol-not-available */ unsigned int timeout_milli = 10000; #if (defined _WIN32 || defined WIN32 || defined OS_WIN64 || defined _WIN64 || defined WIN64 || defined WINNT) int32_t timeout = timeout_milli; fl = setsockopt(mysocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); if (fl<0) { error("set_tcp_keepalive:setsockopt(SOL_SOCKET, SO_RCVTIMEO)"); } fl = setsockopt(mysocket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); if (fl<0) { error("set_tcp_keepalive:setsockopt(SOL_SOCKET, SO_SNDTIMEO)"); } #else struct timeval tv; tv.tv_sec = timeout_milli / 1000; tv.tv_usec = (timeout_milli % 1000) * 1000; fl = setsockopt(mysocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); if (fl<0) { error("set_tcp_keepalive:setsockopt(SOL_SOCKET, SO_RCVTIMEO)"); } fl = setsockopt(mysocket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); if (fl<0) { error("set_tcp_keepalive:setsockopt(SOL_SOCKET, SO_RCVTIMEO)"); } int enable_keepalive = 1; int keepalive_strobe_interval_secs = 3; int num_keepalive_strobes = 5; fl = setsockopt(mysocket, SOL_SOCKET, SO_KEEPALIVE,(char *)&enable_keepalive, sizeof(enable_keepalive)); if (fl<0) { error("set_tcp_keepalive:setsockopt(SOL_SOCKET, SO_KEEPALIVE)"); } #ifdef TCP_KEEPIDLE int keepalive_idle_time_secs = 1; fl = setsockopt(mysocket, IPPROTO_TCP, TCP_KEEPIDLE, (char *)&keepalive_idle_time_secs, sizeof(keepalive_idle_time_secs)); if (fl<0) { error("set_tcp_keepalive:setsockopt(IPPROTO_TCP, TCP_KEEPIDLE)"); } #endif fl = setsockopt(mysocket, IPPROTO_TCP, TCP_KEEPINTVL, (char *)&keepalive_strobe_interval_secs, sizeof(keepalive_strobe_interval_secs)); if (fl<0) { error("set_tcp_keepalive:setsockopt(IPPROTO_TCP, TCP_KEEPINTVL)"); } setsockopt(mysocket, IPPROTO_TCP, TCP_KEEPCNT, (char *)&num_keepalive_strobes, sizeof(num_keepalive_strobes)); if (fl<0) { error("set_tcp_keepalive:setsockopt(IPPROTO_TCP, TCP_KEEPCNT)"); } #endif /* End third-party code */ #ifdef __APPLE__ int flags = fcntl(mysocket, F_GETFL, 0); fl = fcntl(mysocket, F_SETFL, O_NONBLOCK | flags); if (fl < 0) error("set non-blocking on client"); #endif } void close_client_socket(int socket) { if (close(socket)) { char tmp[1000]; snprintf(tmp, 1000, "close(%d)", socket); error(tmp); } }