]> git.vomp.tv Git - vompclient.git/blob - tcp.cc
EventDispatcher: Switch to std::mutex/cond
[vompclient.git] / tcp.cc
1 /*
2     Copyright 2004-2019 Chris Tallon
3     Copyright 2003-2004 University Of Bradford
4
5     This file is part of VOMP.
6
7     VOMP is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     VOMP is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with VOMP; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21
22 #ifdef WIN32
23 #include <WinSock2.h>
24 #include <Iphlpapi.h>
25 #else
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netdb.h>
29 #endif
30
31 #include "log.h"
32
33 #include "tcp.h"
34
35 #ifndef WIN32
36 #define MUTEX_LOCK(mutex) pthread_mutex_lock(mutex)
37 #define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(mutex)
38 #else
39 #define MUTEX_LOCK(mutex) WaitForSingleObject(*(mutex), INFINITE )
40 #define MUTEX_UNLOCK(mutex) ReleaseMutex(*(mutex))
41 #endif
42
43 TCP::TCP()
44 {
45   sock = 0;
46   connected = 0;
47   timeoutEnabled = 1;
48
49 #ifndef WIN32
50   pthread_mutex_init(&mutex, NULL);
51 #else
52   mutex=CreateMutex(NULL,FALSE,NULL);
53 #endif
54 }
55
56 TCP::~TCP()
57 {
58   if (connected)
59   {
60     CLOSESOCKET(sock);
61     Log::getInstance()->log("TCP", Log::DEBUG, "Have closed");
62   }
63
64 #ifdef WIN32
65   CloseHandle(mutex);
66 #endif  
67 }
68
69 void TCP::disableTimeout()
70 {
71   timeoutEnabled = 0;
72 }
73
74 void TCP::getMAC(char* dest)
75 {
76 #ifndef WIN32
77   struct ifreq ifr;
78   strcpy(ifr.ifr_name, "eth0");
79   ioctl(sock, SIOCGIFHWADDR, &ifr);
80   memcpy(dest, ifr.ifr_hwaddr.sa_data, 6);
81 #else
82   //TODO: Get MAC Address for windows
83   PIP_ADAPTER_INFO daptinfo=NULL;
84   DWORD size=0;
85   GetAdaptersInfo(daptinfo,&size);
86   daptinfo=(PIP_ADAPTER_INFO)new char[size+1];
87   memcpy(dest,"ABCDEF", 6);//Dummy Address
88   sockaddr_in sock_address;
89   int sockname_len=sizeof(sock_address);
90   getsockname(sock,(sockaddr*)&sock_address,&sockname_len);
91   ULONG sockip=sock_address.sin_addr.s_addr;
92   if (GetAdaptersInfo(daptinfo,&size)==ERROR_SUCCESS)
93   {
94     PIP_ADAPTER_INFO daptinfo_it=daptinfo;
95     while (daptinfo_it!=NULL)
96     {
97       ULONG ipaddress=inet_addr(daptinfo_it->IpAddressList.IpAddress.String);
98       if (ipaddress==sockip)
99       { //Is it our MAC?
100         memcpy(dest,daptinfo_it->Address, 6);
101         break;
102       }
103       daptinfo_it=daptinfo_it->Next;
104       if (daptinfo_it==daptinfo) break;
105     }
106   }
107   else
108   {
109     // Marten?
110   }
111
112   delete [] daptinfo;
113 #endif
114 }
115
116 int TCP::connectTo(char* host, unsigned short port)
117 {
118 #ifdef VOMP_PLATFORM_RASPBERRY
119 #define IPV 6
120 #else
121 #define IPV 4
122 #endif
123
124 #if IPV == 4
125
126   sock = socket(PF_INET, SOCK_STREAM, 0);
127   if (sock == -1) return 0;
128   struct sockaddr_in dest_addr;
129   dest_addr.sin_family = AF_INET;
130   dest_addr.sin_port = htons(port);
131
132 #ifndef WIN32
133   if (!inet_aton(host, &dest_addr.sin_addr))
134 #else
135   dest_addr.sin_addr.s_addr = inet_addr(host);
136   if (dest_addr.sin_addr.s_addr == INADDR_NONE)
137 #endif
138   {
139     CLOSESOCKET(sock);
140     return 0;
141   }
142
143   memset(&(dest_addr.sin_zero), '\0', 8);
144
145 #elif IPV == 6
146
147   char portstring[10];
148   snprintf(portstring, 10, "%u", port);
149
150   struct addrinfo hints;
151   struct addrinfo* res;
152   memset(&hints, 0, sizeof(hints));
153   hints.ai_family = AF_UNSPEC;
154   hints.ai_socktype = SOCK_STREAM;
155   if (getaddrinfo(host, portstring, &hints, &res))
156   {
157     //printf("[%s] [%s]\n", host, portstring);
158     return 0;
159   }
160
161   sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
162   if (sock == -1) return 0;
163
164 #endif
165
166
167
168   // set non blocking
169 #ifndef WIN32
170   int oldflags = fcntl (sock, F_GETFL, 0);
171   oldflags |= O_NONBLOCK;
172   fcntl(sock, F_SETFL, oldflags);
173 #else
174   unsigned long flag=1;
175   ioctlsocket(sock,FIONBIO,&flag);
176
177 #endif
178
179 //  setReceiveWindow(2048);
180
181   // ok, how to open a connection in non blocking mode (and therefore have a self set timeout!!)
182
183 #if IPV == 4
184   int success = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
185 #elif IPV == 6
186   int success = connect(sock, res->ai_addr, res->ai_addrlen);
187   freeaddrinfo(res);
188 #endif
189
190
191   if (success == 0)  // if by some miracle the connection succeeded in no time flat, just return!
192   {
193     connected = 1;
194     return 1;
195   }
196
197   // first check errno for EINPROGRESS, otherwise it's a fail
198   // this doesn't work?
199 #ifndef WIN32
200   if (errno != EINPROGRESS)
201 #else
202   int wsalasterr = WSAGetLastError();
203   if ((wsalasterr != WSAEWOULDBLOCK) && (wsalasterr != WSAEINPROGRESS))
204 #endif
205   {
206     CLOSESOCKET(sock);
207
208     return 0;
209   }
210
211   // now do a timeout wait on writability on the socket
212
213   fd_set connectSet;
214   struct timeval timeout;
215   FD_ZERO(&connectSet);
216   FD_SET(sock, &connectSet);
217   timeout.tv_sec = 10;
218   timeout.tv_usec = 0;
219   success = select(sock + 1, NULL, &connectSet, NULL, &timeout);
220   if (success < 1)
221   {
222     // timeout or error
223     CLOSESOCKET(sock);
224     return 0;
225   }
226
227   // so the socket became available for writing. Contrary to expectation, this doesn't actually
228   // mean it connected...
229
230   int soError; // SO_ERROR optval is int
231   socklen_t soErrorSize = sizeof(soError);
232
233 #ifdef WIN32
234   int gso = getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&soError), &soErrorSize);
235 #else
236   int gso = getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&soError), &soErrorSize);
237 #endif
238
239   if ((gso == 0) && (soError == 0))
240   {
241     // success!
242     connected = 1;
243     return 1;
244   }
245   else
246   {
247     CLOSESOCKET(sock);
248     return 0;
249   }
250
251 /*
252 The full documentation:
253
254        EINPROGRESS
255               The  socket is non-blocking and the connection canĀ­
256               not be completed immediately.  It  is  possible  to
257               select(2)  or  poll(2)  for completion by selecting
258               the socket  for  writing.  After  select  indicates
259               writability, use getsockopt(2) to read the SO_ERROR
260               option at level  SOL_SOCKET  to  determine  whether
261               connect  completed  successfully (SO_ERROR is zero)
262               or unsuccessfully (SO_ERROR is  one  of  the  usual
263               error  codes listed here, explaining the reason for
264               the failure).
265 */
266 }
267
268 void TCP::setReceiveWindow(size_t rxBufferSize)
269 {
270   // Set receive window
271   // According to docs, optval in setsockopt is a pointer to int unless otherwise noted
272   int rxSize = rxBufferSize;
273 #ifdef WIN32
274   int r = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char*>(&rxSize), sizeof(size_t));
275 #else
276   int r = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<void*>(&rxSize), sizeof(size_t));
277 #endif
278   Log::getInstance()->log("TCP", Log::DEBUG, "Set receive window to %i, success(=0): %i", rxBufferSize, r);
279 }
280
281 void TCP::assignSocket(int tsocket)
282 {
283   sock = tsocket;
284 }
285
286 int TCP::isConnected()
287 {
288   return connected;
289 }
290
291 int TCP::sendData(void* bufR, size_t count)
292 {
293   size_t bytes_sent = 0;
294   int this_write;
295
296   unsigned char* buf = static_cast<unsigned char*>(bufR);
297
298   MUTEX_LOCK(&mutex);
299
300   while (bytes_sent < count)
301   {
302     do
303     {
304 #ifndef WIN32
305       this_write = write(sock, buf, count - bytes_sent);
306 //      Log::getInstance()->log("TCP", Log::DEBUG, "TCP has written %i bytes", this_write);
307     } while ( (this_write < 0) && (errno == EINTR) );
308 #else
309       this_write = send(sock,(char*) buf, count- bytes_sent,0);
310     } while ( (this_write == SOCKET_ERROR) && (WSAGetLastError() == WSAEINTR) );
311 #endif
312     if (this_write <= 0)
313     {
314       MUTEX_UNLOCK(&mutex);
315       return(this_write);
316     }
317     bytes_sent += this_write;
318     buf += this_write;
319   }
320   
321   MUTEX_UNLOCK(&mutex);
322   
323   return(count);
324 }
325
326 int TCP::readData(void* targetBuffer, int totalBytes)
327 {
328   UCHAR* buffer = reinterpret_cast<UCHAR*>(targetBuffer);
329   int bytesRead = 0;
330   int thisRead;
331   int readTries = 0;
332   int success;
333   fd_set readSet;
334   struct timeval timeout;
335   struct timeval* passToSelect;
336
337   if (timeoutEnabled) passToSelect = &timeout;
338   else passToSelect = NULL;
339
340   while(1)
341   {
342     FD_ZERO(&readSet);
343     FD_SET(sock, &readSet);
344     timeout.tv_sec = 2;
345     timeout.tv_usec = 0;
346   //  Log::getInstance()->log("TCP", Log::DEBUG, "Going to select");
347     success = select(sock + 1, &readSet, NULL, NULL, passToSelect);
348    // Log::getInstance()->log("TCP", Log::DEBUG, "Back from select with success = %i", success);
349     if (success < 1)
350     {
351       return 0;  // error, or timeout
352     }
353 #ifndef WIN32
354     thisRead = read(sock, &buffer[bytesRead], totalBytes - bytesRead);
355 #else
356     thisRead = recv(sock, (char*)&buffer[bytesRead], totalBytes - bytesRead, 0);
357 #endif
358     //Log::getInstance()->log("TCP", Log::DEBUG, "Read %i", thisRead);
359     if (!thisRead)
360     {
361       // if read returns 0 then connection is closed
362       // in non-blocking mode if read is called with no data available, it returns -1
363       // and sets errno to EGAGAIN. but we use select so it wouldn't do that anyway.
364       Log::getInstance()->log("TCP", Log::ERR, "Detected connection closed");
365       CLOSESOCKET(sock);
366       connected = 0;
367       return 0;
368     }
369     bytesRead += thisRead;
370     if (bytesRead == totalBytes)
371     {
372       return 1;
373     }
374     else
375     {
376       if (++readTries == 1000)
377       {
378         Log::getInstance()->log("TCP", Log::ERR, "Too many reads");
379         return 0;
380       }
381     }
382   }
383 }
384
385 void TCP::dump(unsigned char* data, ULONG size)
386 {
387   printf("Size = %lu\n", size);
388
389   ULONG c = 0;
390   while(c < size)
391   {
392     if ((size - c) > 15)
393     {
394       printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
395         data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
396         data[c+8], data[c+9], data[c+10], data[c+11], data[c+12], data[c+13], data[c+14], data[c+15],
397         dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
398         dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]), dcc(data[c+12]), dcc(data[c+13]), dcc(data[c+14]), dcc(data[c+15]));
399       c += 16;
400     }
401     else
402     {
403       switch (size - c)
404       {
405         case 15:
406           printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X     %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
407             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
408             data[c+8], data[c+9], data[c+10], data[c+11], data[c+12], data[c+13], data[c+14],
409             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
410             dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]), dcc(data[c+12]), dcc(data[c+13]), dcc(data[c+14]));
411           c += 15;
412           break;
413         case 14:
414           printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X        %c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
415             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
416             data[c+8], data[c+9], data[c+10], data[c+11], data[c+12], data[c+13],
417             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
418             dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]), dcc(data[c+12]), dcc(data[c+13]));
419           c += 14;
420           break;
421         case 13:
422           printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X           %c%c%c%c%c%c%c%c%c%c%c%c%c\n",
423             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
424             data[c+8], data[c+9], data[c+10], data[c+11], data[c+12],
425             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
426             dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]), dcc(data[c+12]));
427           c += 13;
428           break;
429         case 12:
430           printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X               %c%c%c%c%c%c%c%c%c%c%c%c\n",
431             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
432             data[c+8], data[c+9], data[c+10], data[c+11],
433             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
434             dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]), dcc(data[c+11]));
435           c += 12;
436           break;
437         case 11:
438           printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X                  %c%c%c%c%c%c%c%c%c%c%c\n",
439             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
440             data[c+8], data[c+9], data[c+10],
441             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
442             dcc(data[c+8]), dcc(data[c+9]), dcc(data[c+10]));
443           c += 11;
444           break;
445         case 10:
446           printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X                     %c%c%c%c%c%c%c%c%c%c\n",
447             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
448             data[c+8], data[c+9],
449             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
450             dcc(data[c+8]), dcc(data[c+9]));
451           c += 10;
452           break;
453         case 9:
454           printf(" %02X %02X %02X %02X  %02X %02X %02X %02X  %02X                        %c%c%c%c%c%c%c%c%c\n",
455             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
456             data[c+8],
457             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]),
458             dcc(data[c+8]));
459           c += 9;
460           break;
461         case 8:
462           printf(" %02X %02X %02X %02X  %02X %02X %02X %02X                            %c%c%c%c%c%c%c%c\n",
463             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6], data[c+7],
464             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]), dcc(data[c+7]));
465           c += 8;
466           break;
467         case 7:
468           printf(" %02X %02X %02X %02X  %02X %02X %02X                               %c%c%c%c%c%c%c\n",
469             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5], data[c+6],
470             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]), dcc(data[c+6]));
471           c += 7;
472           break;
473         case 6:
474           printf(" %02X %02X %02X %02X  %02X %02X                                  %c%c%c%c%c%c\n",
475             data[c], data[c+1], data[c+2], data[c+3], data[c+4], data[c+5],
476             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]), dcc(data[c+5]));
477           c += 6;
478           break;
479         case 5:
480           printf(" %02X %02X %02X %02X  %02X                                     %c%c%c%c%c\n",
481             data[c], data[c+1], data[c+2], data[c+3], data[c+4],
482             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]), dcc(data[c+4]));
483           c += 5;
484           break;
485         case 4:
486           printf(" %02X %02X %02X %02X                                         %c%c%c%c\n",
487             data[c], data[c+1], data[c+2], data[c+3],
488             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]), dcc(data[c+3]));
489           c += 4;
490           break;
491         case 3:
492           printf(" %02X %02X %02X                                            %c%c%c\n",
493             data[c], data[c+1], data[c+2],
494             dcc(data[c]), dcc(data[c+1]), dcc(data[c+2]));
495           c += 3;
496           break;
497         case 2:
498           printf(" %02X %02X                                               %c%c\n",
499             data[c], data[c+1],
500             dcc(data[c]), dcc(data[c+1]));
501           c += 2;
502           break;
503         case 1:
504           printf(" %02X                                                  %c\n",
505             data[c],
506             dcc(data[c]));
507           c += 1;
508           break;
509       }
510     }
511   }
512 }
513
514 UCHAR TCP::dcc(UCHAR c)
515 {
516   if (isspace(c)) return ' ';
517   if (isprint(c)) return c;
518   return '.';
519 }