///////////////////////////////////////////////////////////////////////////////// // // tcp.cc is part of TestModbus-Server. // ///////////////////////////////////////////////////////////////////////////////// #include "tcp.h" #include #if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) #else #include /* close() */ #endif #include #include /* memset() */ #include using namespace std; int tcpinit; // // convert host and port to sockaddr_in6 // int dns_filladdr (string host, string port, int ai_family, struct sockaddr_storage *sAddr) { struct addrinfo hints, *res; int err; bzero (&hints, sizeof (struct addrinfo)); hints.ai_family = ai_family; hints.ai_socktype = SOCK_DGRAM; if ((err = getaddrinfo (host.c_str(), port.c_str(), &hints, &res)) < 0) { fprintf (stdout, "dns_filladdr (getaddrinfo):%s\n", gai_strerror (err)); return -1; } memcpy (sAddr, res->ai_addr, res->ai_addrlen); freeaddrinfo (res); return 1; }; // // convert int to char* // char* itoa(char* buffer, int number, int size) { snprintf (buffer, size, "%d", number); return buffer; } TCP::~TCP() { Close(); } TCP::TCP() { if (tcpinit == 0) { #if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) || defined(BUILD_WINDOWS) WORD wVersionRequested; WSADATA wsaData; int err; /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */ wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { /* Tell the user that we could not find a usable */ /* Winsock DLL. */ printf("WSAStartup failed with error: %d\n", err); return; } #endif tcpinit = 1; } sock = 0; writecnt = 0; readcnt = 0; islisten = 0; }; TCP::TCP(int s) { TCP(); sock = s; // memset (&localaddr, 0x0, sizeof(localaddr)); // memset (&remoteaddr, 0x0, sizeof(remoteaddr)); writecnt = 0; readcnt = 0; islisten = 0; }; TCP::TCP(string h, string p) { TCP(); Connect (h,p); }; int TCP::Listen(int port) { char buffer[NET_BUFFERSIZE]; int err, i; struct addrinfo hints, *res, *rp; if (sock > 0) Close(); // FIXME: solution to get both (IPV4 and IPV6) to work at the same time? bzero (&hints, sizeof (struct addrinfo)); hints.ai_flags = AI_PASSIVE; #if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) hints.ai_family = AF_INET; #else hints.ai_family = AF_INET6; #endif hints.ai_socktype = SOCK_STREAM; if ((err = getaddrinfo (NULL, itoa(buffer, port, 32), &hints, &res)) != 0) { return 0; } // // walk through all results until we could connect // for (rp = res; rp != NULL; rp = rp->ai_next) { sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sock == -1) continue; i = 1; if ((err = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char*)&i, sizeof (i))) != 0) { printf ("%s:%d setsockopt error\n", __FILE__, __LINE__); } if ((err = bind (sock, rp->ai_addr, rp->ai_addrlen)) < 0) { close (sock); sock = -1; continue; } if (listen (sock, 8) < 0) { // maximum of 8 connections at the time close (sock); sock = -1; continue; } break; } freeaddrinfo (res); if (rp == NULL) { sock = -1; return 0; } islisten = 1; return 1; }; TCP* TCP::Accept() { fd_set rfds; struct timeval tv; int retval, err, i; SOCKET newsock; struct sockaddr_storage cliaddr; socklen_t cliaddr_len = sizeof(struct sockaddr_storage); char host[NET_BUFFERSIZE]; char port[NET_BUFFERSIZE]; TCP *tcp = NULL; if (sock <= 0) return NULL; FD_ZERO(&rfds); FD_SET(sock, &rfds); tv.tv_sec = 0; tv.tv_usec = 1000; retval = select (sock+1, &rfds, NULL, NULL, &tv); if (retval == -1 && errno == EINTR) { retval = 0; } else if (retval == -1) { return NULL; } else if (retval) { #if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) newsock = accept (sock, (struct sockaddr *) &cliaddr, (int*) &cliaddr_len); #else newsock = accept (sock, (struct sockaddr *) &cliaddr, &cliaddr_len); #endif if (newsock < 0) return NULL; tcp = new TCP(); tcp->SetSocket(newsock, &cliaddr, cliaddr_len); return tcp; } return NULL; } int TCP::Connect(string h, string p) { int i = 0; remote_host = h; remote_port = p; return Connect(); }; int TCP::Connect(string hostport, int defaultport) { char buffer[32]; int pos = hostport.rfind(':', hostport.length()); if (pos == -1) { remote_port = itoa (buffer, defaultport, 32); remote_host = hostport; } else { remote_port = hostport.substr (pos+1, hostport.length() - pos); remote_host = hostport.substr (0, pos); } return Connect(); }; int TCP::Connect() { int err, s; struct addrinfo hints, *res, *rp; struct timeval timeout; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6 hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = 0; err = getaddrinfo(remote_host.c_str(), remote_port.c_str(), &hints, &res); if (err != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err)); return 0; } // // walk through all results until we could connect // for (rp = res; rp != NULL; rp = rp->ai_next) { s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); // setup timeout to max 2 secs timeout.tv_sec = 2; timeout.tv_usec = 0; setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); if (s == -1) continue; if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) { sock = s; break; } close(s); } freeaddrinfo(res); // // connect not successfull // if (rp == NULL) return 0; writecnt = 0; readcnt = 0; return 1; }; long int TCP::ReadPop (char *buffer, long int pktlen, long int bufferlen) { memmove (buffer, buffer+pktlen, bufferlen-pktlen); return bufferlen-pktlen; } long int TCP::Read(char *buffer, long int len) { long int len_ = len; if (sock <= 0) return -2; len_ = recv (sock, buffer, len, 0); if (len_ < 0 && errno == EAGAIN) len_ = 0; else if (len_ < 0 && errno != EAGAIN) { #ifdef BUILD_WINDOWS printf ("%s ERROR:%s\n", __FUNCTION__, strerror (errno)); #else fprintf (stderr, "%s ERROR:%s\n", __FUNCTION__, strerror (errno)); #endif len_ = -2; } else if (len_ == 0) len_ = -1; if (len_ < 0) Close(); readcnt += len_; return len_; }; long int TCP::ReadTimeout(char *buffer, long int len, int timeout) { int data = IsData (timeout); if (data > 0) return Read (buffer, len); else if (data < 0) return -1; else return 0; }; ////////////////////////////////////////////////////////// // // write data, generate no signal if some error occures long int TCP::Write(char *buffer, long int len) { int i; int to = NET_MAX_RETRY; if (sock <= 0) return -1; do { i = send (sock, buffer, len, MSG_NOSIGNAL); } while (i == -1 && (to--) > 0 && errno == EINTR); if (i < 0) Close (); writecnt += i; return i; }; void TCP::Close() { #ifdef BUILD_WINDOWS closesocket(sock); #else if (sock > 0) close (sock); #endif sock = -1; islisten = false; }; void TCP::SetSocket(SOCKET s, struct sockaddr_storage *saddr, socklen_t saddrlen) { char host[NET_HOSTLEN]; char port[NET_PORTLEN]; int err; if (sock > 0) Close(); if (s > 0) { sock = s; if (saddr != NULL) { memcpy (&peeraddr, saddr, sizeof(peeraddr)); if ((err = getnameinfo ((struct sockaddr*) saddr, saddrlen, host, NET_HOSTLEN, port, NET_PORTLEN, NI_NUMERICHOST | NI_NUMERICSERV)) == 0) { remote_host = host; remote_port = port; } else { printf ("error: getnameinfo"); /* if (err == EAI_AGAIN) printf ("EAI_AGAIN\n"); if (err == EAI_BADFLAGS) printf ("EAI_BADFLAGS\n"); if (err == EAI_FAIL) printf ("EAI_FAIL\n"); if (err == EAI_FAMILY) printf ("EAI_FAMILY\n"); if (err == EAI_MEMORY) printf ("EAI_MEMORY\n"); if (err == EAI_NONAME) printf ("EAI_NONAME\n"); if (err == EAI_OVERFLOW) printf ("EAI_OVERFLOW\n"); if (err == EAI_SYSTEM) printf ("EAI_SYSTEM\n"); */ // windows seem to have different error codes } } } else sock = -1; }; int TCP::IsConnected() { return (sock > 0); }; int TCP::IsData(int timeout) { fd_set sockset; struct timeval tval; if (sock <= 0) return -1; FD_ZERO (&sockset); FD_SET (sock, &sockset); tval.tv_sec = timeout / 1000; tval.tv_usec = (timeout % 1000); if ((select (sock + 1, &sockset, NULL, NULL, &tval)) != -1) { if (FD_ISSET (sock, &sockset)) return 1; return 0; } else { if (errno == EBADF) sock = -1; } return 0; }; int TCP::WebGetFile (string url, char *buffer, int maxsize) { char outdata[NET_BUFFERSIZE]; char indata[NET_BUFFERSIZE]; char host[NET_HOSTLEN]; char port[NET_PORTLEN]; int breakup, len, head, i, buffpos = 0; if (!IsConnected()) { strncpy (host, WebGetURLHost (url).c_str(), NET_HOSTLEN); strncpy (port, WebGetURLPort (url).c_str(), NET_PORTLEN); Connect (host, port); } if (!IsConnected()) return -1; // send http request snprintf (outdata, NET_BUFFERSIZE, "GET %s HTTP/1.0\nUser-Agent: unknown \nHost: %s\n\n", WebGetURLFile (url).c_str(), host); Write (outdata, strlen (outdata)); // wait for data start i = breakup = 0; head = 1; while (!breakup && head && (len = ReadTimeout (indata, NET_BUFFERSIZE-1, 2000)) >= 0) { indata[len] = 0; while (i < len -4 && head && !breakup) { if (indata[i] == 0x0d && indata [i+1] == 0x0a && indata [i+2] == 0x0d && indata [i+3] == 0x0a) { head = 0; i = i + 4; buffpos = len-i; if (buffpos > maxsize) buffpos = maxsize; memcpy (buffer, indata+i, buffpos); } else if (strncmp (indata+i, "Content-Length:", 15) == 0) { i = i + 16; } else if (strncmp (indata+i, "403 ", 4) == 0) breakup = 1; else i++; } } // read data while (!breakup && (len = ReadTimeout (indata, NET_BUFFERSIZE-1, 2000)) >= 0) { i = len; if (i > maxsize-buffpos) i = maxsize-buffpos; if (i > 0) { memcpy (buffer+buffpos, indata, i); buffpos += i; } } return buffpos; }; const string TCP::WebGetURLHost (string url) { string result; int posSServPort = 7; // begin server:port int posEServPort = 0; // end Server:Port int posPort = -1; // port Position posEServPort = url.find("/", 7); result = url.substr (posSServPort, posEServPort-posSServPort); posPort = result.find(":"); if (posPort > 0) result = url.substr (posSServPort, posPort); return result; }; const string TCP::WebGetURLPort (string url) { string result; int posSServPort = 7; // begin server:port int posEServPort = 0; // end Server:Port int posPort = -1; // port Position posEServPort = url.find("/", 7); result = url.substr (posSServPort, posEServPort-posSServPort); posPort = result.find(":"); if (posPort > 0) result = result.substr (posPort+1); else result = "80"; return result; }; const string TCP::WebGetURLFile (string url) { string result; int posEServPort = 0; // end Server:Port posEServPort = url.find("/", 7); result = url.substr (posEServPort); return result; }; const string TCP::GetRemoteAddr() { string ret = ""; socklen_t addrlen = sizeof (peeraddr); char host[256] = ""; char port[256] = ""; if (getnameinfo ((struct sockaddr*)&peeraddr, addrlen, host, 255, port, 255, NI_NUMERICHOST | NI_NUMERICSERV) != 0) { printf ("getnameinfo error: %s\n", strerror(errno)); } ret = (string)host + ":" + (string)port; return ret; }; const string TCP::GetLocalAddr() { string ret; return ret; };