/****************************************************************************** * Copyright © 2014-2017 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * SuperNET software, including this file may be copied, modified, propagated * * or distributed except according to the terms contained in the LICENSE file * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ /** * - we need to include WinSock2.h header to correctly use windows structure * as the application is still using 32bit structure from mingw so, we need to * add the include based on checking * @author - fadedreamz@gmail.com * @remarks - #if (defined(_M_X64) || defined(__amd64__)) && defined(WIN32) * is equivalent to #if defined(_M_X64) as _M_X64 is defined for MSVC only */ #if defined(_M_X64) #define WIN32_LEAN_AND_MEAN #include #endif int32_t LP_socket(int32_t bindflag,char *hostname,uint16_t port) { int32_t opt,sock,result; char ipaddr[64],checkipaddr[64]; struct timeval timeout; struct sockaddr_in saddr; socklen_t addrlen,slen; addrlen = sizeof(saddr); struct hostent *hostent; /** * gethostbyname() is deprecated and cause crash on x64 windows * the solution is to implement similar functionality by using getaddrinfo() * it is standard posix function and is correctly supported in win32/win64/linux * @author - fadedreamz@gmail.com */ #if defined(_M_X64) struct addrinfo *addrresult = NULL; struct addrinfo *returnptr = NULL; struct addrinfo hints; struct sockaddr_in * sockaddr_ipv4; int retVal; int found = 0; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; #endif if ( parse_ipaddr(ipaddr,hostname) != 0 ) port = parse_ipaddr(ipaddr,hostname); #if defined(_M_X64) retVal = getaddrinfo(ipaddr, NULL, &hints, &addrresult); for (returnptr = addrresult; returnptr != NULL && found == 0; returnptr = returnptr->ai_next) { switch (returnptr->ai_family) { case AF_INET: sockaddr_ipv4 = (struct sockaddr_in *) returnptr->ai_addr; // we want to break from the loop after founding the first ipv4 address found = 1; break; } } // if we iterate through the loop and didn't find anything, // that means we failed in the dns lookup if (found == 0) { printf("getaddrinfo(%s) returned error\n", hostname); freeaddrinfo(addrresult); return(-1); } #else hostent = gethostbyname(ipaddr); if ( hostent == NULL ) { printf("gethostbyname(%s) returned error: %d port.%d ipaddr.(%s)\n",hostname,errno,port,ipaddr); return(-1); } #endif saddr.sin_family = AF_INET; saddr.sin_port = htons(port); //#ifdef WIN32 // saddr.sin_addr.s_addr = (uint32_t)calc_ipbits("127.0.0.1"); //#else #if defined(_M_X64) saddr.sin_addr.s_addr = sockaddr_ipv4->sin_addr.s_addr; // graceful cleanup sockaddr_ipv4 = NULL; freeaddrinfo(addrresult); #else memcpy(&saddr.sin_addr.s_addr,hostent->h_addr_list[0],hostent->h_length); #endif expand_ipbits(checkipaddr,saddr.sin_addr.s_addr); if ( strcmp(ipaddr,checkipaddr) != 0 ) printf("bindflag.%d iguana_socket mismatch (%s) -> (%s)?\n",bindflag,checkipaddr,ipaddr); //#endif if ( (sock= socket(AF_INET,SOCK_STREAM,0)) < 0 ) { if ( errno != ETIMEDOUT ) printf("socket() failed: %s errno.%d", strerror(errno),errno); return(-1); } opt = 1; slen = sizeof(opt); //printf("set keepalive.%d\n",setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,(void *)&opt,slen)); #ifndef WIN32 if ( 1 )//&& bindflag != 0 ) { opt = 0; getsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,(void *)&opt,&slen); opt = 1; //printf("keepalive.%d\n",opt); } setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void *)&opt,sizeof(opt)); #ifdef __APPLE__ setsockopt(sock,SOL_SOCKET,SO_NOSIGPIPE,&opt,sizeof(opt)); #endif #endif if ( bindflag == 0 ) { timeout.tv_sec = 10; timeout.tv_usec = 0; setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(void *)&timeout,sizeof(timeout)); result = connect(sock,(struct sockaddr *)&saddr,addrlen); if ( result != 0 ) { if ( errno != ECONNRESET && errno != ENOTCONN && errno != ECONNREFUSED && errno != ETIMEDOUT && errno != EHOSTUNREACH ) { //printf("%s(%s) port.%d failed: %s sock.%d. errno.%d\n",bindflag!=0?"bind":"connect",hostname,port,strerror(errno),sock,errno); } if ( sock >= 0 ) closesocket(sock); return(-1); } timeout.tv_sec = 10000000; timeout.tv_usec = 0; setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(void *)&timeout,sizeof(timeout)); } else { while ( (result= bind(sock,(struct sockaddr*)&saddr,addrlen)) != 0 ) { if ( errno == EADDRINUSE ) { sleep(1); printf("ERROR BINDING PORT.%d. this is normal tcp timeout, unless another process is using port\n",port); fflush(stdout); sleep(3); printf("%s(%s) port.%d try again: %s sock.%d. errno.%d\n",bindflag!=0?"bind":"connect",hostname,port,strerror(errno),sock,errno); if ( bindflag == 1 ) { closesocket(sock); return(-1); } sleep(13); //continue; } if ( errno != ECONNRESET && errno != ENOTCONN && errno != ECONNREFUSED && errno != ETIMEDOUT && errno != EHOSTUNREACH ) { printf("%s(%s) port.%d failed: %s sock.%d. errno.%d\n",bindflag!=0?"bind":"connect",hostname,port,strerror(errno),sock,errno); closesocket(sock); return(-1); } } if ( listen(sock,64) != 0 ) { printf("listen(%s) port.%d failed: %s sock.%d. errno.%d\n",hostname,port,strerror(errno),sock,errno); if ( sock >= 0 ) closesocket(sock); return(-1); } } #ifdef __APPLE__ //timeout.tv_sec = 0; //timeout.tv_usec = 30000; //setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(void *)&timeout,sizeof(timeout)); timeout.tv_sec = 0; timeout.tv_usec = 10000; setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(void *)&timeout,sizeof(timeout)); #endif return(sock); } int32_t LP_socketsend(int32_t sock,uint8_t *serialized,int32_t len) { int32_t numsent,remains,flags = 0; #ifndef _WIN32 flags = MSG_NOSIGNAL; #endif remains = len; while ( remains > 0 ) { if ( (numsent= (int32_t)send(sock,serialized,remains,flags)) < 0 ) { if ( errno == EAGAIN || errno == EWOULDBLOCK ) { sleep(1); continue; } printf("(%s): numsent.%d vs remains.%d len.%d errno.%d (%s) usock.%d\n",serialized,numsent,remains,len,errno,strerror(errno),sock); return(-errno); } else if ( remains > 0 ) { remains -= numsent; serialized += numsent; if ( remains > 0 ) printf("%d LP_socket sent.%d remains.%d of len.%d\n",sock,numsent,remains,len); } printf("numsent.%d vs remains.%d len.%d sock.%d\n",numsent,remains,len,sock); } return(len); } int32_t LP_socketrecv(int32_t sock,uint8_t *recvbuf,int32_t maxlen) { int32_t recvlen = -1; while ( 1 ) { if ( (recvlen= (int32_t)recv(sock,recvbuf,maxlen,0)) < 0 ) { if ( errno == EAGAIN ) { //printf("%s recv errno.%d %s len.%d remains.%d\n",ipaddr,errno,strerror(errno),len,remains); //printf("EAGAIN for len %d, remains.%d\n",len,remains); sleep(1); } else return(-errno); } else break; } return(recvlen); } int32_t LP_recvfunc(char *ipaddr,char *str,int32_t len) { printf("RECV.(%s) from %s\n",str,ipaddr); return(0); } void LP_dedicatedloop(int32_t (*recvfunc)(char *ipaddr,char *str,int32_t len),char **sendstrp,char *ipaddr,uint16_t port) { struct pollfd fds; uint8_t *buf; char *str; int32_t len,sock,bufsize,flag,timeout = 10; bufsize = IGUANA_MAXPACKETSIZE * 2; buf = malloc(bufsize); sock = LP_socket(0,ipaddr,port); while ( sock >= 0 ) { flag = 0; memset(&fds,0,sizeof(fds)); fds.fd = sock; fds.events |= (POLLOUT | POLLIN); if ( poll(&fds,1,timeout) > 0 && (fds.revents & POLLOUT) != 0 && (str= *sendstrp) != 0 ) { *sendstrp = 0; //printf("sending.(%s)\n",str); if ( LP_socketsend(sock,(uint8_t *)str,(int32_t)strlen(str)) <= 0 ) { printf("%s:%u is dead\n",ipaddr,port); closesocket(sock); sock = -1; break; } flag++; } if ( flag == 0 ) { if ( (fds.revents & POLLIN) != 0 ) { if ( (len= LP_socketrecv(sock,buf,bufsize)) > 0 ) { (*recvfunc)(ipaddr,(char *)buf,len); flag++; } } if ( flag == 0 ) usleep(100000); } } free(buf); }