]> icculus.org git repositories - taylor/freespace2.git/blob - src/inetfile/chttpget.cpp
take the inetfile stuff out of non-Windows builds since it doesn't compile properly
[taylor/freespace2.git] / src / inetfile / chttpget.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10 * $Logfile: /Freespace2/code/Inetfile/Chttpget.cpp $
11 * $Revision$
12 * $Date$
13 * $Author$
14 *
15 * HTTP Client class (get only)
16 *
17 * $Log$
18 * Revision 1.8  2002/06/21 03:04:12  relnev
19 * nothing important
20 *
21 * Revision 1.7  2002/06/17 06:33:09  relnev
22 * ryan's struct patch for gcc 2.95
23 *
24 * Revision 1.6  2002/06/09 04:41:21  relnev
25 * added copyright header
26 *
27 * Revision 1.5  2002/06/02 04:26:34  relnev
28 * warning cleanup
29 *
30 * Revision 1.4  2002/05/26 20:32:24  theoddone33
31 * Fix some minor stuff
32 *
33 * Revision 1.3  2002/05/26 20:20:54  relnev
34 * unix.h: updated
35 *
36 * inetfile/: complete
37 *
38 * Revision 1.2  2002/05/07 03:16:45  theoddone33
39 * The Great Newline Fix
40 *
41 * Revision 1.1.1.1  2002/05/03 03:28:09  root
42 * Initial import.
43 *
44  * 
45  * 5     8/24/99 1:49a Dave
46  * Fixed client-side afterburner stuttering. Added checkbox for no version
47  * checking on PXO join. Made button info passing more friendly between
48  * client and server.
49  * 
50  * 4     8/22/99 1:19p Dave
51  * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
52  * which d3d cards are detected.
53  * 
54  * 21    8/21/99 6:33p Kevin
55  * Fixed Proxy Stuff
56  * 
57  * 20    8/21/99 6:48a Jeff
58  * Linux port
59  * 
60  * 19    8/20/99 3:01p Kevin
61  * Added support for Proxies (I hope!)
62  * 
63  * 18    8/15/99 6:38p Jeff
64  * fixed compile error
65  * 
66  * 17    8/15/99 6:26p Kevin
67  * 
68  * 16    4/14/99 1:20a Jeff
69  * fixed case mismatched #includes
70  * 
71  * 15    3/03/99 12:28a Nate
72  * sped up something or other when the connection is done
73  * 
74  * 14    2/03/99 4:20p Kevin
75  * Got multiplayer working with .mn3 files, and setup autodownloading
76  * 
77  * 13    1/27/99 5:49p Kevin
78  * 
79  * 12    1/27/99 5:38p Kevin
80  * 
81  * 11    12/30/98 12:15p Kevin
82  * Auto Mission Download system
83  * 
84  * 10    10/12/98 4:59p Kevin
85  * Added delay to thread when cancelled...
86  * 
87  * 9     10/12/98 4:49p Nate
88  * More fixes
89  * 
90  * 8     10/12/98 1:54p Nate
91  * Fixed bug
92  * 
93  * 7     10/12/98 11:30a Kevin
94  * More memory stuff
95  * 
96  * 6     10/08/98 12:59p Nate
97  * fixed cancel
98  * 
99  * 5     10/08/98 9:57a Kevin
100  * made transfer cancellable
101  * 
102  * 4     7/31/98 12:19p Nate
103  * Fixed http abort problem.
104  * 
105  * 3     7/31/98 11:57a Kevin
106  * Added new functions for getting state
107  * 
108  * 2     6/01/98 10:10a Kevin
109  * Added DLL connection interface and auto update DLL
110  * 
111  * 1     5/27/98 9:52a Kevin
112  * 
113  * 1     5/25/98 5:31p Kevin
114  * Initial version
115 *
116 * $NoKeywords: $
117 */
118
119 #ifndef PLAT_UNIX       // this isn't working yet (really only needed by PXO anyway)
120
121 #ifndef PLAT_UNIX
122 // #define WIN32
123
124 // #ifdef WIN32
125 #include <windows.h>
126 #include <process.h>
127 // #endif
128 #else
129 #include <sys/types.h>
130 #include <sys/socket.h>
131 #include <netinet/in.h>
132 #include <arpa/inet.h>
133 #include <netdb.h>
134 #include <sys/ioctl.h>
135 #include <errno.h>
136 #include <sys/time.h>
137 #include <unistd.h>
138
139 #include "unix.h"
140
141 #endif
142
143 #include <string.h>
144 #include <stdio.h>
145 #include <stdlib.h>
146 #include <ctype.h>
147
148 #include "inetgetfile.h"
149 #include "chttpget.h"
150
151 #ifdef __LINUX__
152
153 inline void Sleep(int millis)
154 {
155         struct timeval tv;
156         tv.tv_sec = 0;
157         tv.tv_usec = millis*1000;
158         select(0,NULL,NULL,NULL,&tv);
159 }
160 #endif
161
162 #define NW_AGHBN_CANCEL         1
163 #define NW_AGHBN_LOOKUP         2
164 #define NW_AGHBN_READ           3
165
166 #ifndef __LINUX__
167 void __cdecl http_gethostbynameworker(void *parm);
168 #else
169 void *http_gethostbynameworker(void *parm);
170 #endif
171
172 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname);
173
174 #ifndef __LINUX__
175 void HTTPObjThread( void * obj )
176 #else
177 void *HTTPObjThread( void * obj )
178 #endif
179 {
180         ((ChttpGet *)obj)->WorkerThread();
181         ((ChttpGet *)obj)->m_Aborted = true;
182         //OutputDebugString("http transfer exiting....\n");
183
184         #ifdef __LINUX__
185         return NULL;
186         #endif
187 }
188
189 void ChttpGet::AbortGet()
190 {
191 // #ifdef WIN32
192         OutputDebugString("Aborting....\n");
193 // #endif
194         m_Aborting = true;
195         while(!m_Aborted) Sleep(50); //Wait for the thread to end
196 // #ifdef WIN32
197         OutputDebugString("Aborted....\n");
198 // #endif
199 }
200
201 ChttpGet::ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport)
202 {
203         m_ProxyEnabled = true;
204         m_ProxyIP = proxyip;
205         m_ProxyPort = proxyport;
206         GetFile(URL,localfile);
207 }
208
209 ChttpGet::ChttpGet(char *URL,char *localfile)
210 {
211         m_ProxyEnabled = false;
212         GetFile(URL,localfile);
213 }
214
215
216 void ChttpGet::GetFile(char *URL,char *localfile)
217 {
218         m_DataSock = INVALID_SOCKET;
219         m_iBytesIn = 0;
220         m_iBytesTotal = 0;
221         m_State = HTTP_STATE_STARTUP;;
222         m_Aborting = false;
223         m_Aborted = false;
224
225         strncpy(m_URL,URL,MAX_URL_LEN-1);
226         m_URL[MAX_URL_LEN-1] = 0;
227
228         LOCALFILE = fopen(localfile,"wb");
229         if(NULL == LOCALFILE)
230         {
231                 m_State = HTTP_STATE_CANT_WRITE_FILE;
232                 return;
233         }
234         m_DataSock = socket(AF_INET, SOCK_STREAM, 0);
235         if(INVALID_SOCKET == m_DataSock)
236         {
237                 m_State = HTTP_STATE_SOCKET_ERROR;
238                 return;
239         }
240         unsigned long arg;
241
242         arg = true;
243 //#ifndef __LINUX__
244 #ifndef PLAT_UNIX
245         ioctlsocket( m_DataSock, FIONBIO, &arg );
246 #else
247         ioctl( m_DataSock, FIONBIO, &arg );
248 #endif
249
250         char *pURL = URL;
251         if(strnicmp(URL,"http:",5)==0)
252         {
253                 pURL +=5;
254                 while(*pURL == '/')
255                 {
256                         pURL++;
257                 }
258         }
259         //There shouldn't be any : in this string
260         if(strchr(pURL,':'))
261         {
262                 m_State = HTTP_STATE_URL_PARSING_ERROR;
263                 return;
264         }
265         //read the filename by searching backwards for a /
266         //then keep reading until you find the first /
267         //when you found it, you have the host and dir
268         char *filestart = NULL;
269         char *dirstart = NULL;
270         for(int i = strlen(pURL);i>=0;i--)
271         {
272                 if(pURL[i]== '/')
273                 {
274                         if(!filestart)
275                         {
276                                 filestart = pURL+i+1;
277                                 dirstart = pURL+i+1;
278                                 strcpy(m_szFilename,filestart);
279                         }
280                         else
281                         {
282                                 dirstart = pURL+i+1;
283                         }
284                 }
285         }
286         if((dirstart==NULL) || (filestart==NULL))
287         {
288                 m_State = HTTP_STATE_URL_PARSING_ERROR;
289                 return;
290         }
291         else
292         {
293                 strcpy(m_szDir,dirstart);//,(filestart-dirstart));
294                 //m_szDir[(filestart-dirstart)] = NULL;
295                 strncpy(m_szHost,pURL,(dirstart-pURL));
296                 m_szHost[(dirstart-pURL)-1] = '\0';
297         }
298 // #ifdef WIN32
299         if(0==_beginthread(HTTPObjThread,0,this))
300         {
301                 m_State = HTTP_STATE_INTERNAL_ERROR;
302                 return;
303         }
304         /*
305 #elif defined(__LINUX__)
306         pthread_t thread;
307         if(!inet_LoadThreadLib())
308         {
309                 m_State = HTTP_STATE_INTERNAL_ERROR;
310                 return;
311         }
312         if(df_pthread_create(&thread,NULL,HTTPObjThread,this)!=0)
313         {
314                 m_State = HTTP_STATE_INTERNAL_ERROR;
315                 return;
316         }
317 #endif
318         */
319 }
320
321
322 ChttpGet::~ChttpGet()
323 {
324         if(m_DataSock != INVALID_SOCKET)
325         {
326                 shutdown(m_DataSock,2);
327 #ifndef __LINUX__
328                 closesocket(m_DataSock);
329 #else
330                 close(m_DataSock);
331 #endif
332         }
333 }
334
335 int ChttpGet::GetStatus()
336 {
337         return m_State;
338 }
339
340 unsigned int ChttpGet::GetBytesIn()
341 {
342         return m_iBytesIn;
343 }
344
345 unsigned int ChttpGet::GetTotalBytes()
346 {
347         return m_iBytesTotal;
348 }
349
350
351 void ChttpGet::WorkerThread()
352 {
353         char szCommand[1000];
354         char *p;
355         int irsp = 0;
356         ConnectSocket();
357         if(m_Aborting)
358         {
359                 fclose(LOCALFILE);
360                 return;
361         }
362         if(m_State != HTTP_STATE_CONNECTED)
363         {
364                 fclose(LOCALFILE);
365                 return;
366         }
367         sprintf(szCommand,"GET %s%s HTTP/1.1\nAccept: */*\nAccept-Encoding: deflate\nHost: %s\n\n\n",m_ProxyEnabled?"":"/",m_ProxyEnabled?m_URL:m_szDir,m_szHost);
368         send(m_DataSock,szCommand,strlen(szCommand),0);
369         p = GetHTTPLine();
370         if(strnicmp("HTTP/",p,5)==0)
371         {
372                 char *pcode;
373                 pcode = strchr(p,' ')+1;
374                 if(!pcode)
375                 {
376                         m_State = HTTP_STATE_UNKNOWN_ERROR;     
377                         fclose(LOCALFILE);
378                         return;
379
380                 }
381                 pcode[3] = '\0';
382                 irsp = atoi(pcode);
383
384                 if(irsp == 0)
385                 {
386                         m_State = HTTP_STATE_UNKNOWN_ERROR;     
387                         fclose(LOCALFILE);
388                         return;
389                 }
390                 if(irsp==200)
391                 {
392                         int idataready=0;
393                         do
394                         {
395                                 p = GetHTTPLine();
396                                 if(p==NULL)
397                                 {
398                                         m_State = HTTP_STATE_UNKNOWN_ERROR;     
399                                         fclose(LOCALFILE);
400                                         return;
401                                 }
402                                 if(*p=='\0')
403                                 {
404                                         idataready = 1;
405                                         break;
406                                 }
407                                 if(strnicmp(p,"Content-Length:",strlen("Content-Length:"))==0)
408                                 {
409                                         char *s = strchr(p,' ')+1;
410                                         p = s;
411                                         if(s)
412                                         {
413                                                 while(*s)
414                                                 {
415                                                         if(!isdigit(*s))
416                                                         {
417                                                                 *s='\0';
418                                                         }
419                                                         s++;
420                                                 };
421                                                 m_iBytesTotal = atoi(p);
422                                         }
423
424                                 }
425
426                                 Sleep(1);
427                         }while(!idataready);
428                 ReadDataChannel();
429                 return;
430                 }
431                 m_State = HTTP_STATE_FILE_NOT_FOUND;
432                 fclose(LOCALFILE);
433                 return;
434         }
435         else
436         {
437                 m_State = HTTP_STATE_UNKNOWN_ERROR;
438                 fclose(LOCALFILE);
439                 return;
440         }
441 }
442
443 int ChttpGet::ConnectSocket()
444 {
445         //HOSTENT *he;
446         unsigned int ip;
447         SERVENT *se;
448         SOCKADDR_IN hostaddr;
449         if(m_Aborting){
450                 return 0;
451         }
452         
453         ip = inet_addr((const char *)m_szHost);
454
455         int rcode = 0;
456         if(ip==INADDR_NONE)
457         {
458                 http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_szHost);          
459                 do
460                 {       
461                         if(m_Aborting)
462                         {
463                                 http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_szHost);
464                                 return 0;
465                         }
466                         rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_szHost);
467
468                         Sleep(1);
469                 }while(rcode==0);
470         }
471         
472         if(rcode == -1)
473         {
474                 m_State = HTTP_STATE_HOST_NOT_FOUND;
475                 return 0;
476         }
477         //m_ControlSock
478         if(m_Aborting)
479                 return 0;
480         se = getservbyname("http", NULL);
481         if(m_Aborting)
482                 return 0;
483         if(se == NULL)
484         {
485                 hostaddr.sin_port = htons(80);
486         }
487         else
488         {
489                 hostaddr.sin_port = se->s_port;
490         }
491         hostaddr.sin_family = AF_INET;          
492         //ip = htonl(ip);
493         memcpy(&hostaddr.sin_addr,&ip,4);
494
495         if(m_ProxyEnabled)
496         {
497                 //This is on a proxy, so we need to make sure to connect to the proxy machine
498                 ip = inet_addr((const char *)m_ProxyIP);
499                                 
500                 if(ip==INADDR_NONE)
501                 {
502                         http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_ProxyIP);
503                         rcode = 0;
504                         do
505                         {       
506                                 if(m_Aborting)
507                                 {
508                                         http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_ProxyIP);
509                                         return 0;
510                                 }
511                                 rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_ProxyIP);
512
513                                 Sleep(1);
514                         }while(rcode==0);
515                         
516                         
517                         if(rcode == -1)
518                         {
519                                 m_State = HTTP_STATE_HOST_NOT_FOUND;
520                                 return 0;
521                         }
522
523                 }
524                 //Use either the proxy port or 80 if none specified
525                 hostaddr.sin_port = htons((ushort)(m_ProxyPort ? m_ProxyPort : 80));
526                 //Copy the proxy address...
527                 memcpy(&hostaddr.sin_addr,&ip,4);
528
529         }
530         //Now we will connect to the host                                       
531         fd_set  wfds;
532
533         timeval timeout;
534         timeout.tv_sec = 0;
535         timeout.tv_usec = 0;
536         int serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
537         int cerr = WSAGetLastError();
538         if(serr)
539         {
540                 while((cerr==WSAEALREADY)||(cerr==WSAEINVAL)||(cerr==WSAEWOULDBLOCK))
541                 {
542                         FD_ZERO(&wfds);
543                         FD_SET( m_DataSock, &wfds );
544                         if(select(0,NULL,&wfds,NULL,&timeout))
545                         {
546                                 serr = 0;
547                                 break;
548                         }
549                         if(m_Aborting)
550                                 return 0;
551                         serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
552                         if(serr == 0)
553                                 break;
554                         cerr = WSAGetLastError();
555                         if(cerr==WSAEISCONN)
556                         {
557                                 serr = 0;
558                                 break;
559                         }
560
561                         Sleep(1);
562                 };
563         }
564         if(serr)
565         {
566                 m_State = HTTP_STATE_CANT_CONNECT;
567                 return 0;
568         }
569         m_State = HTTP_STATE_CONNECTED;
570         return 1;
571 }
572
573 char *ChttpGet::GetHTTPLine()
574 {
575         int iBytesRead;
576         char chunk[2];
577         unsigned int igotcrlf = 0;
578         memset(recv_buffer,0,1000);
579         do
580         {
581                 chunk[0]='\0';
582                 bool gotdata = false;
583                 do
584                 {
585                         iBytesRead = recv(m_DataSock,chunk,1,0);
586
587                         if(SOCKET_ERROR == iBytesRead)
588                         {       
589                                 int error = WSAGetLastError();
590                                 if(WSAEWOULDBLOCK==error)
591                                 {
592                                         gotdata = false;
593                                         continue;
594                                 }
595                                 else
596                                         return NULL;
597                         }
598                         else
599                         {
600                                 gotdata = true;
601                         }
602
603                         Sleep(1);
604                 }while(!gotdata);
605                 
606                 if(chunk[0]==0x0d)
607                 {
608                         //This should always read a 0x0a
609                         do
610                         {
611                                 iBytesRead = recv(m_DataSock,chunk,1,0);
612
613                                 if(SOCKET_ERROR == iBytesRead)
614                                 {       
615                                         int error = WSAGetLastError();
616                                         if(WSAEWOULDBLOCK==error)
617                                         {
618                                                 gotdata = false;
619                                                 continue;
620                                         }
621                                         else
622                                                 return NULL;
623                                 }
624                                 else
625                                 {
626                                         gotdata = true;
627                                 }
628
629                                 Sleep(1);
630                         }while(!gotdata);
631                         igotcrlf = 1;   
632                 }
633                 else
634                 {       chunk[1] = '\0';
635                         strcat(recv_buffer,chunk);
636                 }
637                 
638                 Sleep(1);
639         }while(igotcrlf==0);
640         return recv_buffer;     
641 }
642
643 unsigned int ChttpGet::ReadDataChannel()
644 {
645         char sDataBuffer[4096];         // Data-storage buffer for the data channel
646         int nBytesRecv = 0;                                             // Bytes received from the data channel
647
648         fd_set  wfds;
649
650         timeval timeout;
651         timeout.tv_sec = 0;
652         timeout.tv_usec = 500;
653
654         m_State = HTTP_STATE_RECEIVING;                 
655    do   
656    {
657                 FD_ZERO(&wfds);
658                 FD_SET( m_DataSock, &wfds );
659
660                 if((m_iBytesTotal)&&(m_iBytesIn==m_iBytesTotal))
661                 {
662                         break;
663                 }
664                 select(0,&wfds,NULL,NULL,&timeout);
665         if(m_Aborting)
666                 {
667                         fclose(LOCALFILE);
668                         return 0;               
669                 }
670                 nBytesRecv = recv(m_DataSock, (char *)&sDataBuffer,sizeof(sDataBuffer), 0);
671         if(m_Aborting)
672                 {
673                         fclose(LOCALFILE);
674                         return 0;
675                 }
676                 if(SOCKET_ERROR == nBytesRecv)
677                 {       
678                         int error = WSAGetLastError();
679                         if(WSAEWOULDBLOCK==error)
680                         {
681                                 nBytesRecv = 1;
682                                 continue;
683                         }
684                 }
685                 m_iBytesIn += nBytesRecv;
686                 if (nBytesRecv > 0 )
687                 {
688                         fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE);
689                         //Write sDataBuffer, nBytesRecv
690         }
691                 
692                 Sleep(1);
693         }while (nBytesRecv > 0);
694         fclose(LOCALFILE);                                                      
695         // Close the file and check for error returns.
696         if (nBytesRecv == SOCKET_ERROR)
697         { 
698                 //Ok, we got a socket error -- xfer aborted?
699                 m_State = HTTP_STATE_RECV_FAILED;
700                 return 0;
701         }
702         else
703         {
704                 //OutputDebugString("HTTP File complete!\n");
705                 //done!
706                 m_State = HTTP_STATE_FILE_RECEIVED;
707                 return 1;
708         }
709 }       
710
711
712 typedef struct _async_dns_lookup
713 {
714         unsigned int ip;        //resolved host. Write only to worker thread.
715         char * host;//host name to resolve. read only to worker thread
716         bool done;      //write only to the worker thread. Signals that the operation is complete
717         bool error; //write only to worker thread. Thread sets this if the name doesn't resolve
718         bool abort;     //read only to worker thread. If this is set, don't fill in the struct.
719 }async_dns_lookup;
720
721 async_dns_lookup httpaslu;
722 async_dns_lookup *http_lastaslu = NULL;
723
724 #ifndef __LINUX__
725 void __cdecl http_gethostbynameworker(void *parm);
726 #else
727 void *http_gethostbynameworker(void *parm);
728 #endif
729
730 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname)
731 {
732         
733         if(command==NW_AGHBN_LOOKUP)
734         {
735                 if(http_lastaslu)
736                         http_lastaslu->abort = true;
737
738                 async_dns_lookup *newaslu;
739                 newaslu = (async_dns_lookup *)malloc(sizeof(async_dns_lookup));
740                 memset(&newaslu->ip,0,sizeof(unsigned int));
741                 newaslu->host = hostname;
742                 newaslu->done = false;
743                 newaslu->error = false;
744                 newaslu->abort = false;
745                 http_lastaslu = newaslu;
746                 httpaslu.done = false;
747
748 // #ifdef WIN32
749                 _beginthread(http_gethostbynameworker,0,newaslu);
750         /*
751 #elif defined(__LINUX__)
752                 pthread_t thread;
753                 if(!inet_LoadThreadLib())
754                 {
755                         return 0;
756                 }
757
758                 df_pthread_create(&thread,NULL,http_gethostbynameworker,newaslu);
759 #endif
760                 */
761                 return 1;
762         }
763         else if(command==NW_AGHBN_CANCEL)
764         {
765                 if(http_lastaslu)
766                         http_lastaslu->abort = true;
767                 http_lastaslu = NULL;
768         }
769         else if(command==NW_AGHBN_READ)
770         {
771                 if(!http_lastaslu)
772                         return -1;
773                 if(httpaslu.done)
774                 {
775                         //free(http_lastaslu);
776                         http_lastaslu = NULL;
777                         memcpy(ip,&httpaslu.ip,sizeof(unsigned int));
778                         return 1;
779                 }
780                 else if(httpaslu.error)
781                 {
782                         free(http_lastaslu);
783                         http_lastaslu = NULL;
784                         return -1;
785                 }
786                 else return 0;
787         }
788         return -2;
789
790 }
791
792 // This is the worker thread which does the lookup.
793 #ifndef __LINUX__
794 void __cdecl http_gethostbynameworker(void *parm)
795 #else
796 void *http_gethostbynameworker(void *parm)
797 #endif
798 {
799 #ifdef __LINUX__
800         df_pthread_detach(df_pthread_self());
801 #endif
802         async_dns_lookup *lookup = (async_dns_lookup *)parm;
803         HOSTENT *he = gethostbyname(lookup->host);
804         if(he==NULL)
805         {
806                 lookup->error = true;
807                 #ifdef __LINUX__
808                 return NULL;
809                 #else
810                 return;
811                 #endif
812         }
813         else if(!lookup->abort)
814         {
815                 memcpy(&lookup->ip,he->h_addr_list[0],sizeof(unsigned int));
816                 lookup->done = true;
817                 memcpy(&httpaslu,lookup,sizeof(async_dns_lookup));
818         }
819         free(lookup);
820
821 #ifdef __LINUX__
822         return NULL;
823 #endif
824 }
825
826 #endif // !PLAT_UNIX