]> git.cworth.org Git - vogl/blob - src/common/channel.cpp
Initial vogl checkin
[vogl] / src / common / channel.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <netinet/tcp.h>
38 #include <stdint.h>
39 #include <netdb.h>
40 #include <time.h>
41 #include <fcntl.h>
42
43 #include "channel.h"
44
45 int hostname_to_ip(char *hostname, char *ip);
46 void print_time(const char *szDesc, struct timespec *ptspec);
47
48 #if defined(_DEBUG) || defined(DEBUG)
49 #define DEBUG_PRINT(fmt, args...)    fprintf( stderr, fmt, ## args )
50 #else
51 #define DEBUG_PRINT(...) /* Don't do anything in release builds */
52 #endif
53
54 using namespace network;
55
56
57 /**********************************************************/
58 //
59 //  Class Channel implementation
60 //
61 //
62 channel::channel(void *pbFixedBuffer, size_t cbFixedBuffer)
63 {
64     Initialize(pbFixedBuffer, cbFixedBuffer);
65 }
66
67 channel::channel()
68 {
69     Initialize(NULL, 0);
70 }
71
72 void
73 channel::Initialize(void *pbFixedBuffer, size_t cbFixedBuffer)
74 {
75     //  Common
76     m_socket = 0;
77     m_fServer = false;
78     m_port = 0;
79     //  fixed buffer to use for packets
80     m_cbFixedBuffer = cbFixedBuffer;
81     m_pbFixedBuffer = pbFixedBuffer;
82
83     //  Client specific
84     memset(m_szServer, 0, sizeof(m_szServer));
85
86     //  Server specific
87     m_fLocal = false;
88     m_listenSocket = 0;
89     m_backlog = 0;
90 }
91
92 channel::~channel()
93 {
94     if (m_listenSocket)
95     {
96         shutdown(m_listenSocket, SHUT_RDWR);
97         close(m_listenSocket);
98     }
99
100     if (m_socket)
101     {
102         shutdown(m_socket, SHUT_RDWR);
103         close(m_socket);
104     }
105 }
106
107 CHEC
108 channel::Connect(char *szServer, int port, int nRetries, unsigned int waitMS)
109 {
110     CHEC ec = EC_NONE;
111
112     m_fServer = false;
113     m_port = port;
114     //  Copy szServer
115     if (szServer)
116     {
117         strncpy(m_szServer, szServer, sizeof(m_szServer));
118     }
119
120     for (int iTry = 0; iTry < nRetries; iTry++)
121     {
122         ec = channelConnect();
123         if (EC_TIMEOUT != ec)
124         {
125             break;
126         }
127
128         usleep(waitMS * 1000); // 1000 microseconds = 1 milisecond
129     }
130
131     return ec;
132 }
133
134 CHEC
135 channel::Connect(int port, int backlog, bool fLocal)
136 {
137     CHEC ec = EC_NONE;
138
139     m_fServer = true;
140     m_port = port;
141     m_backlog = backlog;
142     m_fLocal = fLocal;
143
144     ec = channelConnect();
145
146     return ec;
147 }
148
149 CHEC
150 channel::channelConnect()
151 {
152     CHEC ec = EC_NONE;
153
154     if (false == m_fServer)
155     {
156         ec = connectConnection();
157     }
158     else
159     {
160         ec = acceptConnection();
161     }
162
163     return ec;
164 }
165
166 CHEC
167 channel::acceptConnection()
168 {
169     CHEC ec = EC_NONE;
170     int rv = 0;
171     int noNagle = 1; //  Turn off nagle
172     int reuseaddress = 1;  // Do not reuse addresses
173     struct sockaddr_in serv_addr;
174     bool fFirstTime = false;
175
176     if (0 == m_listenSocket)
177     {
178
179         m_listenSocket = socket(AF_INET, SOCK_STREAM, 0);
180         if (-1 == m_listenSocket)
181         {
182             m_listenSocket = 0;
183             DEBUG_PRINT("%s %d: %s socket() failed with %d\n", __FILE__, __LINE__, __func__, errno);
184             ec = EC_NETWORK;
185             goto out;
186         }
187
188         // setsockopt(listenfd, IPPROTO_TCP, TCP_NODELAY, &noNagle, sizeof(noNagle));
189         rv = setsockopt(m_listenSocket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuseaddress, sizeof(reuseaddress));
190         if (-1 == rv)
191         {
192             close(m_listenSocket);
193             m_listenSocket = 0;
194             DEBUG_PRINT("%s %d: %s setsockopt() failed with %d\n", __FILE__, __LINE__, __func__, errno);
195             ec = EC_NETWORK;
196             goto out;
197         }
198
199         rv = setsockopt(m_listenSocket, IPPROTO_TCP, TCP_NODELAY, (void *)&noNagle, sizeof(noNagle));
200         if (-1 == rv)
201         {
202             close(m_listenSocket);
203             m_listenSocket = 0;
204             DEBUG_PRINT("%s %d: %s setsockopt() failed with %d\n", __FILE__, __LINE__, __func__, errno);
205             ec = EC_NETWORK;
206             goto out;
207         }
208
209         memset(&serv_addr, '0', sizeof(serv_addr));
210         serv_addr.sin_family = AF_INET;
211         if (true == m_fLocal)
212         {
213             serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
214         }
215         else
216         {
217             serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
218         }
219         serv_addr.sin_port = htons(m_port);
220
221         rv = bind(m_listenSocket, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
222         if (-1 == rv)
223         {
224             close(m_listenSocket);
225             m_listenSocket = 0;
226             DEBUG_PRINT("%s %d: %s bind() failed with %d\n", __FILE__, __LINE__, __func__, errno);
227             ec = EC_NETWORK;
228             goto out;
229         }
230
231         rv = listen(m_listenSocket, m_backlog);
232         if (-1 == rv)
233         {
234             close(m_listenSocket);
235             m_listenSocket = 0;
236             DEBUG_PRINT("%s %d: %s listen() failed with %d\n", __FILE__, __LINE__, __func__, errno);
237             ec = EC_NETWORK;
238             goto out;
239         }
240
241         fFirstTime = true;
242     }
243
244     if (0 == m_socket)
245     {
246 #ifdef NOTYET
247         int flags = 0;
248         int ret = 0;
249
250         if (true != fFirstTime)
251         {
252             //  Non-blocking see if there's any connections awaiting us
253             flags = fcntl(m_listenSocket, F_GETFL, 0);
254             if (-1 == flags) flags = 0;
255             flags |= O_NONBLOCK;
256             ret = fcntl(m_listenSocket, F_SETFL, flags);
257         }
258 #endif // NOTYET
259         DEBUG_PRINT("%s %d: %s accept() attempt on port %d\n", __FILE__, __LINE__, __func__, m_port);
260         m_socket = accept(m_listenSocket, (struct sockaddr *)NULL, NULL);
261
262         if (-1 == m_socket)
263         {
264             m_socket = 0;
265
266             DEBUG_PRINT("%s %d: %s accept() failed with %d\n", __FILE__, __LINE__, __func__, errno);
267             ec = EC_NETWORK;
268             //if (EAGAIN == errno || EWOULDBLOCK == errno)
269             //    ec = EC_TIMEOUT;
270
271             goto out;
272         }
273
274         // setsockopt(listenfd, IPPROTO_TCP, TCP_NODELAY, &noNagle, sizeof(noNagle));
275         rv = setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuseaddress, sizeof(reuseaddress));
276         if (-1 == rv)
277         {
278             close(m_socket);
279             m_socket = 0;
280             DEBUG_PRINT("%s %d: %s setsockopt() failed with %d\n", __FILE__, __LINE__, __func__, errno);
281             ec = EC_NETWORK;
282             goto out;
283         }
284
285         noNagle = 1;
286         rv = setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&noNagle, sizeof(noNagle));
287         if (-1 == rv)
288         {
289             close(m_socket);
290             m_socket = 0;
291             DEBUG_PRINT("%s %d: %s setsockopt() failed with %d\n", __FILE__, __LINE__, __func__, errno);
292             ec = EC_NETWORK;
293             goto out;
294         }
295
296         DEBUG_PRINT("%s %d: %s accept() succeeded to socket %d on port %d\n", __FILE__, __LINE__, __func__, m_socket, m_port);
297     }
298
299 out:
300     return ec;
301 }
302
303 CHEC
304 channel::connectConnection()
305 {
306     CHEC ec = EC_NONE;
307     int rv = 0;
308     int noNagle = 1; //  Turn off nagle
309     int reuseaddress = 1;  // Do not reuse addresses
310     struct sockaddr_in serv_addr;
311     char ip[1024] = { 0 };
312
313     m_socket = socket(AF_INET, SOCK_STREAM, 0);
314
315     if (m_socket < 0)
316     {
317         DEBUG_PRINT("\n %s %d: %s  Error : Could not create socket \n", __FILE__, __LINE__, __func__);
318         m_socket = 0;
319
320         ec = EC_NETWORK;
321         goto out;
322     }
323
324     // setsockopt(listenfd, IPPROTO_TCP, TCP_NODELAY, &noNagle, sizeof(noNagle));
325     rv = setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuseaddress, sizeof(reuseaddress));
326     if (-1 == rv)
327     {
328         close(m_socket);
329         m_socket = 0;
330         DEBUG_PRINT("%s %d: %s setsockopt() failed with %d\n", __FILE__, __LINE__, __func__, errno);
331         ec = EC_NETWORK;
332         goto out;
333     }
334
335     rv = setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&noNagle, sizeof(noNagle));
336     if (-1 == rv)
337     {
338         close(m_socket);
339         m_socket = 0;
340         DEBUG_PRINT("%s %d: %s setsockopt() failed with %d\n", __FILE__, __LINE__, __func__, errno);
341         ec = EC_NETWORK;
342         goto out;
343     }
344
345
346     hostname_to_ip(m_szServer, ip);
347
348     DEBUG_PRINT("%s has the ip of: %s\n", m_szServer, ip);
349
350     memset(&serv_addr, '0', sizeof(serv_addr));
351
352     serv_addr.sin_family = AF_INET;
353     serv_addr.sin_port = htons(m_port);
354
355     rv = inet_pton(AF_INET, ip, &serv_addr.sin_addr);
356     if (-1 == rv)
357     {
358         DEBUG_PRINT("%s %d: %s inet_pton() failed with %d\n", __FILE__, __LINE__, __func__, errno);
359         ec = EC_NETWORK;
360         Disconnect();
361         goto out;
362     }
363     if (0 == rv)
364     {
365         DEBUG_PRINT("%s %d: %s inet_pton() \"%s\" does not contain a character string representing a valid network address in the AF_INET address family.", __FILE__, __LINE__, __func__, ip);
366         ec = EC_NETWORK;
367         Disconnect();
368         goto out;
369     }
370
371     rv = connect(m_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
372     if (-1 == rv)
373     {
374         int tErrno = errno;
375         ec = EC_NETWORK;
376         DEBUG_PRINT("%s %d: %s connect() failed on port %d with %d\n", __FILE__, __LINE__, __func__, m_port, tErrno);
377         if (ECONNREFUSED == tErrno)
378         {
379             ec = EC_TIMEOUT;
380         }
381         Disconnect();
382         goto out;
383     }
384
385     DEBUG_PRINT("%s %d: %s connect() to socket %d on port %d\n", __FILE__, __LINE__, __func__, m_socket, m_port);
386
387 out:
388     return ec;
389 }
390
391 CHEC
392 channel::Disconnect()
393 {
394     if (0 != m_socket)
395     {
396         DEBUG_PRINT("%s %d: %s closing down socket %d on port %d\n", __FILE__, __LINE__, __func__, m_socket, m_port);
397         shutdown(m_socket, SHUT_RDWR);
398         close(m_socket);
399         m_socket = 0;
400     }
401
402     return EC_NONE;
403 }
404
405
406
407 //  Public
408 //  ReadMsg
409 //
410 //  Returns the size and data of a message read from the network socket.  Lifetime of the data returned is owned by the
411 //  caller and should be cleaned up by using free().
412 //
413 //  This method is the one that loops through the retries.
414 //
415 CHEC
416 channel::ReadMsg(unsigned int *pcbBufOut, char **ppBufOut, int nRetries, int timeoutMS)
417 {
418     CHEC ec = EC_NONE;
419
420     for (int iTry = 0; iTry < nRetries; iTry++)
421     {
422         ec = readMsgLoop(pcbBufOut, ppBufOut, timeoutMS);
423         if (EC_TIMEOUT == ec)
424         {
425             continue;
426         }
427         break;
428     }
429
430     //if (EC_TIMEOUT == ec)
431         //Disconnect();
432
433     return ec;
434 }
435
436 //  Private
437 //  readMsgLoop
438 //
439 //  The inner loop of the ReadMsg() that deals with reconnection and message size.
440 //
441 CHEC
442 channel::readMsgLoop(unsigned int *pcbBufOut, char **ppBufOut, int timeoutMS)
443 {
444     CHEC ec = EC_NONE;
445     char *pRecBuff = NULL;
446     //int bToRead = 1024;
447     //int bRead = 0;
448     u_int64_t cbSize64 = 0;
449
450     if (0 == m_socket)
451     {
452         ec = channelConnect();
453         if (EC_NONE != ec)
454             goto out;
455     }
456
457     //  A buffer is defined as having a size (int64_t) followed by the data of that size.
458     //
459     //  First, read the size of the buffer:
460
461     ec = readMsgTimeout(sizeof(int64_t), (char *)&cbSize64, timeoutMS);
462     if (EC_NONE != ec)
463     {
464         goto out;
465     }
466
467     //  malloc up some space
468     pRecBuff = (char *)my_malloc((size_t)cbSize64);
469     if (NULL == pRecBuff)
470     {
471         ec = EC_MEMORY;
472         goto out;
473     }
474
475     //  Now read the data buffer
476     ec = readMsgTimeout(cbSize64, pRecBuff, timeoutMS);
477     if (EC_NONE != ec)
478     {
479         my_free(pRecBuff);
480         DEBUG_PRINT("%s %d: %s  Error from read(data) = %d (%d).", __FILE__, __LINE__, __func__, ec, errno);
481
482         goto out;
483     }
484
485     DEBUG_PRINT("Debug: client buffer read %zd bytes\n", cbSize64);
486
487     *ppBufOut = pRecBuff;
488     *pcbBufOut = cbSize64; //  MIN(bToRead, bRead)?
489
490 out:
491
492     if (EC_NONE != ec && EC_TIMEOUT != ec)
493     {
494         DEBUG_PRINT("  Disconnecting.\n");
495         Disconnect();
496         //  Try to reconnect right away.
497         ec = channelConnect();
498         if (EC_NONE == ec)
499             ec = EC_TIMEOUT;
500     }
501
502     return ec;
503 }
504
505 //  Private
506 //  readMsgTimeout
507 //
508 //  The inner read of the readMsgLoop().  This one deals with timeouts explicitly.
509 //
510 CHEC
511 channel::readMsgTimeout(unsigned int cbSize, char *pBuff, int timeoutMS)
512 {
513     CHEC ec = EC_NONE;
514     double timeElapsedMS = 0;
515     unsigned int bRead = 0; // How many bytes read so far
516     char *pRecBuff = pBuff;
517     struct timespec tspecBefore, tspecNow;
518
519     // baseline time
520     clock_gettime(CLOCK_MONOTONIC, &tspecBefore);
521
522     //DEBUG_PRINT("channel::InternalRead:  Timeout specified as %dms.\n", timeoutMS);
523     while (cbSize > bRead)
524     {
525         int bReadT = 0;
526
527         //bReadT = read(m_socket, pRecBuff+bRead, cbSize - bRead );
528         bReadT = recv(m_socket, pRecBuff + bRead, cbSize - bRead, MSG_NOSIGNAL | (m_fServer ? 0: MSG_DONTWAIT));
529
530         if (bReadT <= 0)
531         {
532             //  Check to see if the error is actually just a timeout
533             //
534             if (EWOULDBLOCK != errno && EAGAIN != errno)
535             {
536                 ec = EC_NETWORK;
537                 DEBUG_PRINT("%s %d: %s Error from read() on port %d = %d. \n", __FILE__, __LINE__, __func__, m_port, errno);
538                 goto out;
539             }
540
541             //  See if we've gone past the timeout requested
542             clock_gettime(CLOCK_MONOTONIC, &tspecNow);
543
544             timeElapsedMS = ((double)(tspecNow.tv_sec - tspecBefore.tv_sec) * 1000) + ((double)(tspecNow.tv_nsec - tspecBefore.tv_nsec) / 1.0e6);
545
546             if (timeoutMS && (timeoutMS <= timeElapsedMS))
547             {
548                 //ec = EC_NETWORK;
549                 ec = EC_TIMEOUT;
550                 //DEBUG_PRINT("channel::InternalRead: Timing out %f\n", timeElapsedMS);
551                 break;
552             }
553
554             //  Still time left
555             //DEBUG_PRINT("channel::InternalRead: Still time left %f\n", timeElapsedMS);
556             continue;
557         }
558
559         bRead += (unsigned int) bReadT; // Add the bytes we've read up to this point.
560     }
561
562 out:
563     if (EC_NONE == ec)
564         DEBUG_PRINT("%s %d: %s recv'd(%d bytes) on port %d\n", __FILE__, __LINE__, __func__, bRead, m_port);
565
566     return ec;
567 }
568
569 //
570 //  writeMsg -
571 //
572 //    Writes a buffer out to a socket.  In order to do so, it needs to prepend the buffer with the
573 //      size in order for the readMsg to know how much data to get.
574 //
575
576 CHEC
577 channel::WriteMsg(unsigned int cbBufIn, const char *pbBufIn, int nRetries, int timeoutMS)
578 {
579     CHEC ec = EC_NONE;
580
581     for (int iTry = 0; iTry < nRetries; iTry++)
582     {
583         ec = writeMsgLoop(cbBufIn, pbBufIn, timeoutMS);
584         if (EC_TIMEOUT == ec)
585         {
586             continue;
587         }
588         break;
589     }
590
591     //if (EC_TIMEOUT == ec)
592         //Disconnect();
593
594     return ec;
595 }
596
597 CHEC
598 channel::writeMsgLoop(unsigned int cbBufIn, const char *pbBufIn, int timeoutMS)
599 {
600     CHEC ec = EC_NONE;
601
602     u_int64_t cbToWrite = cbBufIn; // Number of bytes to write if this write is to succeed
603
604     if (0 == m_socket)
605     {
606         ec = channelConnect();
607         if (EC_NONE != ec)
608             goto out;
609     }
610
611     ec = writeMsgTimeout(sizeof(u_int64_t), (char *)&cbToWrite, timeoutMS);
612     if (EC_NONE != ec)
613     {
614         DEBUG_PRINT("%s %d: %s  Error from write(buffersize) = %d (%d) on port %d.", __FILE__, __LINE__, __func__, ec, errno, m_port);
615         goto out;
616     }
617
618     ec = writeMsgTimeout(cbBufIn, pbBufIn, timeoutMS);
619     if (EC_NONE != ec)
620     {
621         DEBUG_PRINT("%s %d: %s  Error from write(data) = %d (%d) on port %d.", __FILE__, __LINE__, __func__, ec, errno, m_port);
622         goto out;
623     }
624
625     DEBUG_PRINT("Debug: client buffer wrote %zd bytes on port %d\n", cbToWrite, m_port);
626
627 out:
628     if (EC_NONE != ec && EC_TIMEOUT != ec)
629     {
630         DEBUG_PRINT("  Disconnecting.\n");
631         Disconnect();
632         //  Try to reconnect right away.
633         ec = channelConnect();
634         if (EC_NONE == ec)
635             ec = EC_TIMEOUT;
636     }
637
638     return ec;
639 }
640
641 CHEC
642 channel::writeMsgTimeout(unsigned int cbSize, const char *pBuff, int timeoutMS)
643 {
644     CHEC ec = EC_NONE;
645     double timeMS = 0;
646     unsigned int bWritten = 0; // How many bytes sent so far
647     struct timespec tspecBefore, tspecNow;
648
649     // baseline time
650     clock_gettime(CLOCK_MONOTONIC, &tspecBefore);
651
652     DEBUG_PRINT("%s %d: %s attempting to send (%d bytes) to socket %d on port %d\n", __FILE__, __LINE__, __func__, cbSize, m_socket, m_port);
653     while (bWritten < cbSize)
654     {
655         int bWrote = 0;
656
657         bWrote = send(m_socket, pBuff + bWritten, cbSize - bWritten, MSG_NOSIGNAL | (timeoutMS ? MSG_DONTWAIT : 0));
658         if (bWrote <= 0)
659         {
660             //  Check to see if the error is actually just a timeout
661             //
662             if (EWOULDBLOCK != errno && EAGAIN != errno)
663             {
664                 ec = EC_NETWORK;
665                 DEBUG_PRINT("%s %d: %s  Error %d from write() to socket %d on port %d.\n", __FILE__, __LINE__, __func__, errno, m_socket, m_port);
666                 goto out;
667             }
668
669             //  See if we've gone past the timeout requested
670             clock_gettime(CLOCK_MONOTONIC, &tspecNow);
671
672             timeMS = ((double)(tspecNow.tv_sec - tspecBefore.tv_sec) * 1.0e9 + (double)(tspecNow.tv_nsec - tspecBefore.tv_nsec)) / 1000;
673
674             if (timeoutMS && (timeoutMS <= timeMS))
675             {
676                 ec = EC_TIMEOUT;
677                 break;
678             }
679
680             //  Still time left
681
682             continue;
683         }
684
685         bWritten += (unsigned int) bWrote;
686     }
687
688 out:
689     if (EC_NONE == ec)
690         DEBUG_PRINT("%s %d: %s sent (%d bytes) to socket %d on port %d\n", __FILE__, __LINE__, __func__, bWritten, m_socket, m_port);
691     else
692         DEBUG_PRINT("%s %d: %s error (%d) sending (%d bytes) to socket %d on port %d\n", __FILE__, __LINE__, __func__, errno, cbSize, m_socket, m_port);
693
694     return ec;
695 }
696
697 void *
698 channel::my_malloc(size_t cbSize)
699 {
700     if (NULL == m_pbFixedBuffer)
701         return malloc(cbSize);
702
703     if (cbSize > m_cbFixedBuffer)
704         return NULL;
705
706     return m_pbFixedBuffer;
707 }
708
709 void
710 channel::my_free(void *pBuff)
711 {
712
713     if (pBuff == m_pbFixedBuffer)
714         return;
715
716     free(pBuff);
717
718     return;
719 }
720
721 //
722 //  Internal helper functions
723 //
724
725 int
726 hostname_to_ip(char *hostname, char *ip)
727 {
728     struct addrinfo hints, *servinfo, *p;
729     struct sockaddr_in *h;
730     int rv;
731
732     memset(&hints, 0, sizeof hints);
733     hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
734     hints.ai_socktype = SOCK_STREAM;
735
736     if ((rv = getaddrinfo(hostname, "http", &hints, &servinfo)) != 0)
737     {
738         fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
739         return 1;
740     }
741
742     // loop through all the results and connect to the first we can
743     for (p = servinfo; p != NULL; p = p->ai_next)
744     {
745         h = (struct sockaddr_in *)p->ai_addr;
746         strcpy(ip, inet_ntoa(h->sin_addr));
747     }
748
749     freeaddrinfo(servinfo); // all done with this structure
750     return 0;
751 }
752
753 void print_time(const char *szDesc, struct timespec *ptspec)
754 {
755     printf("\t%s time:\n", szDesc);
756     printf("\t\t%ld seconds\n", ptspec->tv_sec);
757     printf("\t\t%ld nanoseconds\n", ptspec->tv_nsec);
758
759     return;
760 }