xref: /reactos/dll/win32/winhttp/net.c (revision 4bf76a65)
1 /*
2  * Copyright 2008 Hans Leidekker for CodeWeavers
3  * Copyright 2013 Jacek Caban for CodeWeavers
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include "config.h"
21 #define NONAMELESSUNION
22 #include "ws2tcpip.h"
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26 
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winhttp.h"
30 #include "schannel.h"
31 
32 #include "wine/debug.h"
33 #include "wine/library.h"
34 #include "winhttp_private.h"
35 
36 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
37 
sock_send(int fd,const void * msg,size_t len,int flags)38 static int sock_send(int fd, const void *msg, size_t len, int flags)
39 {
40     int ret;
41     do
42     {
43         if ((ret = send(fd, msg, len, flags)) == -1) WARN("send error %u\n", WSAGetLastError());
44     }
45     while(ret == -1 && WSAGetLastError() == WSAEINTR);
46     return ret;
47 }
48 
sock_recv(int fd,void * msg,size_t len,int flags)49 static int sock_recv(int fd, void *msg, size_t len, int flags)
50 {
51     int ret;
52     do
53     {
54         if ((ret = recv(fd, msg, len, flags)) == -1) WARN("recv error %u\n", WSAGetLastError());
55     }
56     while(ret == -1 && WSAGetLastError() == WSAEINTR);
57     return ret;
58 }
59 
netconn_verify_cert(PCCERT_CONTEXT cert,WCHAR * server,DWORD security_flags,BOOL check_revocation)60 static DWORD netconn_verify_cert( PCCERT_CONTEXT cert, WCHAR *server, DWORD security_flags, BOOL check_revocation )
61 {
62     HCERTSTORE store = cert->hCertStore;
63     BOOL ret;
64     CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
65     PCCERT_CHAIN_CONTEXT chain;
66     char oid_server_auth[] = szOID_PKIX_KP_SERVER_AUTH;
67     char *server_auth[] = { oid_server_auth };
68     DWORD err = ERROR_SUCCESS;
69 
70     TRACE("verifying %s\n", debugstr_w( server ));
71     chainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
72     chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = server_auth;
73     ret = CertGetCertificateChain( NULL, cert, NULL, store, &chainPara,
74                                    check_revocation ? CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT : 0,
75                                    NULL, &chain );
76     if (ret)
77     {
78         if (chain->TrustStatus.dwErrorStatus)
79         {
80             static const DWORD supportedErrors =
81                 CERT_TRUST_IS_NOT_TIME_VALID |
82                 CERT_TRUST_IS_UNTRUSTED_ROOT |
83                 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
84 
85             if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID)
86             {
87                 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID))
88                     err = ERROR_WINHTTP_SECURE_CERT_DATE_INVALID;
89             }
90             else if (chain->TrustStatus.dwErrorStatus &
91                      CERT_TRUST_IS_UNTRUSTED_ROOT)
92             {
93                 if (!(security_flags & SECURITY_FLAG_IGNORE_UNKNOWN_CA))
94                     err = ERROR_WINHTTP_SECURE_INVALID_CA;
95             }
96             else if ((chain->TrustStatus.dwErrorStatus &
97                       CERT_TRUST_IS_OFFLINE_REVOCATION) ||
98                      (chain->TrustStatus.dwErrorStatus &
99                       CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
100                 err = ERROR_WINHTTP_SECURE_CERT_REV_FAILED;
101             else if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)
102                 err = ERROR_WINHTTP_SECURE_CERT_REVOKED;
103             else if (chain->TrustStatus.dwErrorStatus &
104                 CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
105             {
106                 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE))
107                     err = ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE;
108             }
109             else if (chain->TrustStatus.dwErrorStatus & ~supportedErrors)
110                 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
111         }
112         if (!err)
113         {
114             CERT_CHAIN_POLICY_PARA policyPara;
115             SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
116             CERT_CHAIN_POLICY_STATUS policyStatus;
117             CERT_CHAIN_CONTEXT chainCopy;
118 
119             /* Clear chain->TrustStatus.dwErrorStatus so
120              * CertVerifyCertificateChainPolicy will verify additional checks
121              * rather than stopping with an existing, ignored error.
122              */
123             memcpy(&chainCopy, chain, sizeof(chainCopy));
124             chainCopy.TrustStatus.dwErrorStatus = 0;
125             sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara);
126             sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER;
127             sslExtraPolicyPara.pwszServerName = server;
128             sslExtraPolicyPara.fdwChecks = security_flags;
129             policyPara.cbSize = sizeof(policyPara);
130             policyPara.dwFlags = 0;
131             policyPara.pvExtraPolicyPara = &sslExtraPolicyPara;
132             ret = CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL,
133                                                     &chainCopy, &policyPara,
134                                                     &policyStatus );
135             /* Any error in the policy status indicates that the
136              * policy couldn't be verified.
137              */
138             if (ret && policyStatus.dwError)
139             {
140                 if (policyStatus.dwError == CERT_E_CN_NO_MATCH)
141                     err = ERROR_WINHTTP_SECURE_CERT_CN_INVALID;
142                 else
143                     err = ERROR_WINHTTP_SECURE_INVALID_CERT;
144             }
145         }
146         CertFreeCertificateChain( chain );
147     }
148     else
149         err = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
150     TRACE("returning %08x\n", err);
151     return err;
152 }
153 
154 static BOOL winsock_loaded;
155 
netconn_unload(void)156 void netconn_unload( void )
157 {
158     if (winsock_loaded) WSACleanup();
159 }
160 
winsock_startup(INIT_ONCE * once,void * param,void ** ctx)161 static BOOL WINAPI winsock_startup( INIT_ONCE *once, void *param, void **ctx )
162 {
163     int ret;
164     WSADATA data;
165     if (!(ret = WSAStartup( MAKEWORD(1,1), &data ))) winsock_loaded = TRUE;
166     else ERR( "WSAStartup failed: %d\n", ret );
167     return TRUE;
168 }
169 
170 #ifdef __REACTOS__
winsock_init(void)171 void winsock_init(void)
172 #else
173 static void winsock_init(void)
174 #endif
175 {
176     static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
177     InitOnceExecuteOnce( &once, winsock_startup, NULL, NULL );
178 }
179 
set_blocking(struct netconn * conn,BOOL blocking)180 static void set_blocking( struct netconn *conn, BOOL blocking )
181 {
182     ULONG state = !blocking;
183     ioctlsocket( conn->socket, FIONBIO, &state );
184 }
185 
netconn_create(struct hostdata * host,const struct sockaddr_storage * sockaddr,int timeout)186 struct netconn *netconn_create( struct hostdata *host, const struct sockaddr_storage *sockaddr, int timeout )
187 {
188     struct netconn *conn;
189     unsigned int addr_len;
190     BOOL ret = FALSE;
191 
192 #ifndef __REACTOS__
193     winsock_init();
194 #endif
195 
196     conn = heap_alloc_zero(sizeof(*conn));
197     if (!conn) return NULL;
198     conn->host = host;
199     conn->sockaddr = *sockaddr;
200     if ((conn->socket = socket( sockaddr->ss_family, SOCK_STREAM, 0 )) == -1)
201     {
202         WARN("unable to create socket (%u)\n", WSAGetLastError());
203         heap_free(conn);
204         return NULL;
205     }
206 
207     switch (conn->sockaddr.ss_family)
208     {
209     case AF_INET:
210         addr_len = sizeof(struct sockaddr_in);
211         break;
212     case AF_INET6:
213         addr_len = sizeof(struct sockaddr_in6);
214         break;
215     default:
216         assert(0);
217     }
218 
219     if (timeout > 0) set_blocking( conn, FALSE );
220 
221     if (!connect( conn->socket, (const struct sockaddr *)&conn->sockaddr, addr_len )) ret = TRUE;
222     else
223     {
224         DWORD err = WSAGetLastError();
225         if (err == WSAEWOULDBLOCK || err == WSAEINPROGRESS)
226         {
227             FD_SET set;
228             TIMEVAL timeval = { 0, timeout * 1000 };
229             int res;
230 
231             FD_ZERO( &set );
232             FD_SET( conn->socket, &set );
233             if ((res = select( conn->socket + 1, NULL, &set, NULL, &timeval )) > 0) ret = TRUE;
234             else if (!res) SetLastError( ERROR_WINHTTP_TIMEOUT );
235         }
236     }
237 
238     if (timeout > 0) set_blocking( conn, TRUE );
239 
240     if (!ret)
241     {
242         WARN("unable to connect to host (%u)\n", GetLastError());
243         closesocket( conn->socket );
244         heap_free( conn );
245         return NULL;
246     }
247     return conn;
248 }
249 
netconn_close(struct netconn * conn)250 void netconn_close( struct netconn *conn )
251 {
252     if (conn->secure)
253     {
254         heap_free( conn->peek_msg_mem );
255         heap_free(conn->ssl_buf);
256         heap_free(conn->extra_buf);
257         DeleteSecurityContext(&conn->ssl_ctx);
258     }
259     closesocket( conn->socket );
260     release_host( conn->host );
261     heap_free(conn);
262 }
263 
netconn_secure_connect(struct netconn * conn,WCHAR * hostname,DWORD security_flags,CredHandle * cred_handle,BOOL check_revocation)264 BOOL netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD security_flags, CredHandle *cred_handle,
265                              BOOL check_revocation)
266 {
267     SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}};
268     SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs};
269     BYTE *read_buf;
270     SIZE_T read_buf_size = 2048;
271     ULONG attrs = 0;
272     CtxtHandle ctx;
273     SSIZE_T size;
274     const CERT_CONTEXT *cert;
275     SECURITY_STATUS status;
276     DWORD res = ERROR_SUCCESS;
277 
278     const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY
279         |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION;
280 
281     read_buf = heap_alloc(read_buf_size);
282     if(!read_buf)
283         return FALSE;
284 
285     status = InitializeSecurityContextW(cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0,
286             &ctx, &out_desc, &attrs, NULL);
287 
288     assert(status != SEC_E_OK);
289 
290     while(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) {
291         if(out_buf.cbBuffer) {
292             assert(status == SEC_I_CONTINUE_NEEDED);
293 
294             TRACE("sending %u bytes\n", out_buf.cbBuffer);
295 
296             size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, 0);
297             if(size != out_buf.cbBuffer) {
298                 ERR("send failed\n");
299                 res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
300                 break;
301             }
302 
303             FreeContextBuffer(out_buf.pvBuffer);
304             out_buf.pvBuffer = NULL;
305             out_buf.cbBuffer = 0;
306         }
307 
308         if(status == SEC_I_CONTINUE_NEEDED) {
309             assert(in_bufs[1].cbBuffer < read_buf_size);
310 
311             memmove(read_buf, (BYTE*)in_bufs[0].pvBuffer+in_bufs[0].cbBuffer-in_bufs[1].cbBuffer, in_bufs[1].cbBuffer);
312             in_bufs[0].cbBuffer = in_bufs[1].cbBuffer;
313 
314             in_bufs[1].BufferType = SECBUFFER_EMPTY;
315             in_bufs[1].cbBuffer = 0;
316             in_bufs[1].pvBuffer = NULL;
317         }
318 
319         assert(in_bufs[0].BufferType == SECBUFFER_TOKEN);
320         assert(in_bufs[1].BufferType == SECBUFFER_EMPTY);
321 
322         if(in_bufs[0].cbBuffer + 1024 > read_buf_size) {
323             BYTE *new_read_buf;
324 
325             new_read_buf = heap_realloc(read_buf, read_buf_size + 1024);
326             if(!new_read_buf) {
327                 status = E_OUTOFMEMORY;
328                 break;
329             }
330 
331             in_bufs[0].pvBuffer = read_buf = new_read_buf;
332             read_buf_size += 1024;
333         }
334 
335         size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0);
336         if(size < 1) {
337             status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
338             break;
339         }
340 
341         TRACE("recv %lu bytes\n", size);
342 
343         in_bufs[0].cbBuffer += size;
344         in_bufs[0].pvBuffer = read_buf;
345         status = InitializeSecurityContextW(cred_handle, &ctx, hostname,  isc_req_flags, 0, 0, &in_desc,
346                 0, NULL, &out_desc, &attrs, NULL);
347         TRACE("InitializeSecurityContext ret %08x\n", status);
348 
349         if(status == SEC_E_OK) {
350             if(in_bufs[1].BufferType == SECBUFFER_EXTRA)
351                 FIXME("SECBUFFER_EXTRA not supported\n");
352 
353             status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes);
354             if(status != SEC_E_OK) {
355                 WARN("Could not get sizes\n");
356                 break;
357             }
358 
359             status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert);
360             if(status == SEC_E_OK) {
361                 res = netconn_verify_cert(cert, hostname, security_flags, check_revocation);
362                 CertFreeCertificateContext(cert);
363                 if(res != ERROR_SUCCESS) {
364                     WARN("cert verify failed: %u\n", res);
365                     break;
366                 }
367             }else {
368                 WARN("Could not get cert\n");
369                 break;
370             }
371 
372             conn->ssl_buf = heap_alloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
373             if(!conn->ssl_buf) {
374                 res = GetLastError();
375                 break;
376             }
377         }
378     }
379 
380     heap_free(read_buf);
381 
382     if(status != SEC_E_OK || res != ERROR_SUCCESS) {
383         WARN("Failed to initialize security context failed: %08x\n", status);
384         heap_free(conn->ssl_buf);
385         conn->ssl_buf = NULL;
386         DeleteSecurityContext(&ctx);
387         SetLastError(res ? res : ERROR_WINHTTP_SECURE_CHANNEL_ERROR);
388         return FALSE;
389     }
390 
391 
392     TRACE("established SSL connection\n");
393     conn->secure = TRUE;
394     conn->ssl_ctx = ctx;
395     return TRUE;
396 }
397 
send_ssl_chunk(struct netconn * conn,const void * msg,size_t size)398 static BOOL send_ssl_chunk(struct netconn *conn, const void *msg, size_t size)
399 {
400     SecBuffer bufs[4] = {
401         {conn->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, conn->ssl_buf},
402         {size,  SECBUFFER_DATA, conn->ssl_buf+conn->ssl_sizes.cbHeader},
403         {conn->ssl_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, conn->ssl_buf+conn->ssl_sizes.cbHeader+size},
404         {0, SECBUFFER_EMPTY, NULL}
405     };
406     SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
407     SECURITY_STATUS res;
408 
409     memcpy(bufs[1].pvBuffer, msg, size);
410     res = EncryptMessage(&conn->ssl_ctx, 0, &buf_desc, 0);
411     if(res != SEC_E_OK) {
412         WARN("EncryptMessage failed\n");
413         return FALSE;
414     }
415 
416     if(sock_send(conn->socket, conn->ssl_buf, bufs[0].cbBuffer+bufs[1].cbBuffer+bufs[2].cbBuffer, 0) < 1) {
417         WARN("send failed\n");
418         return FALSE;
419     }
420 
421     return TRUE;
422 }
423 
netconn_send(struct netconn * conn,const void * msg,size_t len,int * sent)424 BOOL netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent )
425 {
426     if (conn->secure)
427     {
428         const BYTE *ptr = msg;
429         size_t chunk_size;
430 
431         *sent = 0;
432 
433         while(len) {
434             chunk_size = min(len, conn->ssl_sizes.cbMaximumMessage);
435             if(!send_ssl_chunk(conn, ptr, chunk_size))
436                 return FALSE;
437 
438             *sent += chunk_size;
439             ptr += chunk_size;
440             len -= chunk_size;
441         }
442 
443         return TRUE;
444     }
445     return ((*sent = sock_send( conn->socket, msg, len, 0 )) != -1);
446 }
447 
read_ssl_chunk(struct netconn * conn,void * buf,SIZE_T buf_size,SIZE_T * ret_size,BOOL * eof)448 static BOOL read_ssl_chunk(struct netconn *conn, void *buf, SIZE_T buf_size, SIZE_T *ret_size, BOOL *eof)
449 {
450     const SIZE_T ssl_buf_size = conn->ssl_sizes.cbHeader+conn->ssl_sizes.cbMaximumMessage+conn->ssl_sizes.cbTrailer;
451     SecBuffer bufs[4];
452     SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
453     SSIZE_T size, buf_len;
454     unsigned int i;
455     SECURITY_STATUS res;
456 
457     assert(conn->extra_len < ssl_buf_size);
458 
459     if(conn->extra_len) {
460         memcpy(conn->ssl_buf, conn->extra_buf, conn->extra_len);
461         buf_len = conn->extra_len;
462         conn->extra_len = 0;
463         heap_free(conn->extra_buf);
464         conn->extra_buf = NULL;
465     }else {
466         buf_len = sock_recv(conn->socket, conn->ssl_buf+conn->extra_len, ssl_buf_size-conn->extra_len, 0);
467         if(buf_len < 0)
468             return FALSE;
469 
470         if(!buf_len) {
471             *eof = TRUE;
472             return TRUE;
473         }
474     }
475 
476     *ret_size = 0;
477     *eof = FALSE;
478 
479     do {
480         memset(bufs, 0, sizeof(bufs));
481         bufs[0].BufferType = SECBUFFER_DATA;
482         bufs[0].cbBuffer = buf_len;
483         bufs[0].pvBuffer = conn->ssl_buf;
484 
485         res = DecryptMessage(&conn->ssl_ctx, &buf_desc, 0, NULL);
486         switch(res) {
487         case SEC_E_OK:
488             break;
489         case SEC_I_CONTEXT_EXPIRED:
490             TRACE("context expired\n");
491             *eof = TRUE;
492             return TRUE;
493         case SEC_E_INCOMPLETE_MESSAGE:
494             assert(buf_len < ssl_buf_size);
495 
496             size = sock_recv(conn->socket, conn->ssl_buf+buf_len, ssl_buf_size-buf_len, 0);
497             if(size < 1)
498                 return FALSE;
499 
500             buf_len += size;
501             continue;
502         default:
503             WARN("failed: %08x\n", res);
504             return FALSE;
505         }
506     } while(res != SEC_E_OK);
507 
508     for(i = 0; i < ARRAY_SIZE(bufs); i++) {
509         if(bufs[i].BufferType == SECBUFFER_DATA) {
510             size = min(buf_size, bufs[i].cbBuffer);
511             memcpy(buf, bufs[i].pvBuffer, size);
512             if(size < bufs[i].cbBuffer) {
513                 assert(!conn->peek_len);
514                 conn->peek_msg_mem = conn->peek_msg = heap_alloc(bufs[i].cbBuffer - size);
515                 if(!conn->peek_msg)
516                     return FALSE;
517                 conn->peek_len = bufs[i].cbBuffer-size;
518                 memcpy(conn->peek_msg, (char*)bufs[i].pvBuffer+size, conn->peek_len);
519             }
520 
521             *ret_size = size;
522         }
523     }
524 
525     for(i = 0; i < ARRAY_SIZE(bufs); i++) {
526         if(bufs[i].BufferType == SECBUFFER_EXTRA) {
527             conn->extra_buf = heap_alloc(bufs[i].cbBuffer);
528             if(!conn->extra_buf)
529                 return FALSE;
530 
531             conn->extra_len = bufs[i].cbBuffer;
532             memcpy(conn->extra_buf, bufs[i].pvBuffer, conn->extra_len);
533         }
534     }
535 
536     return TRUE;
537 }
538 
netconn_recv(struct netconn * conn,void * buf,size_t len,int flags,int * recvd)539 BOOL netconn_recv( struct netconn *conn, void *buf, size_t len, int flags, int *recvd )
540 {
541     *recvd = 0;
542     if (!len) return TRUE;
543 
544     if (conn->secure)
545     {
546         SIZE_T size, cread;
547         BOOL res, eof;
548 
549         if (conn->peek_msg)
550         {
551             *recvd = min( len, conn->peek_len );
552             memcpy( buf, conn->peek_msg, *recvd );
553             conn->peek_len -= *recvd;
554             conn->peek_msg += *recvd;
555 
556             if (conn->peek_len == 0)
557             {
558                 heap_free( conn->peek_msg_mem );
559                 conn->peek_msg_mem = NULL;
560                 conn->peek_msg = NULL;
561             }
562             /* check if we have enough data from the peek buffer */
563             if (!(flags & MSG_WAITALL) || *recvd == len) return TRUE;
564         }
565         size = *recvd;
566 
567         do {
568             res = read_ssl_chunk(conn, (BYTE*)buf+size, len-size, &cread, &eof);
569             if(!res) {
570                 WARN("read_ssl_chunk failed\n");
571                 if(!size)
572                     return FALSE;
573                 break;
574             }
575 
576             if(eof) {
577                 TRACE("EOF\n");
578                 break;
579             }
580 
581             size += cread;
582         }while(!size || ((flags & MSG_WAITALL) && size < len));
583 
584         TRACE("received %ld bytes\n", size);
585         *recvd = size;
586         return TRUE;
587     }
588     return ((*recvd = sock_recv( conn->socket, buf, len, flags )) != -1);
589 }
590 
netconn_query_data_available(struct netconn * conn)591 ULONG netconn_query_data_available( struct netconn *conn )
592 {
593     return conn->secure ? conn->peek_len : 0;
594 }
595 
netconn_set_timeout(struct netconn * netconn,BOOL send,int value)596 DWORD netconn_set_timeout( struct netconn *netconn, BOOL send, int value )
597 {
598     int opt = send ? SO_SNDTIMEO : SO_RCVTIMEO;
599     if (setsockopt( netconn->socket, SOL_SOCKET, opt, (void *)&value, sizeof(value) ) == -1)
600     {
601         DWORD err = WSAGetLastError();
602         WARN("setsockopt failed (%u)\n", err );
603         return err;
604     }
605     return ERROR_SUCCESS;
606 }
607 
netconn_is_alive(struct netconn * netconn)608 BOOL netconn_is_alive( struct netconn *netconn )
609 {
610     int len;
611     char b;
612     DWORD err;
613 
614     set_blocking( netconn, FALSE );
615     len = sock_recv( netconn->socket, &b, 1, MSG_PEEK );
616     err = WSAGetLastError();
617     set_blocking( netconn, TRUE );
618 
619     return len == 1 || (len == -1 && err == WSAEWOULDBLOCK);
620 }
621 
resolve_hostname(const WCHAR * name,INTERNET_PORT port,struct sockaddr_storage * sa)622 static DWORD resolve_hostname( const WCHAR *name, INTERNET_PORT port, struct sockaddr_storage *sa )
623 {
624     ADDRINFOW *res, hints;
625     int ret;
626 
627     memset( &hints, 0, sizeof(hints) );
628     /* Prefer IPv4 to IPv6 addresses, since some web servers do not listen on
629      * their IPv6 addresses even though they have IPv6 addresses in the DNS.
630      */
631     hints.ai_family = AF_INET;
632 
633     ret = GetAddrInfoW( name, NULL, &hints, &res );
634     if (ret != 0)
635     {
636         TRACE("failed to get IPv4 address of %s, retrying with IPv6\n", debugstr_w(name));
637         hints.ai_family = AF_INET6;
638         ret = GetAddrInfoW( name, NULL, &hints, &res );
639         if (ret != 0)
640         {
641             TRACE("failed to get address of %s\n", debugstr_w(name));
642             return ERROR_WINHTTP_NAME_NOT_RESOLVED;
643         }
644     }
645     memcpy( sa, res->ai_addr, res->ai_addrlen );
646     switch (res->ai_family)
647     {
648     case AF_INET:
649         ((struct sockaddr_in *)sa)->sin_port = htons( port );
650         break;
651     case AF_INET6:
652         ((struct sockaddr_in6 *)sa)->sin6_port = htons( port );
653         break;
654     }
655 
656     FreeAddrInfoW( res );
657     return ERROR_SUCCESS;
658 }
659 
660 #ifdef __REACTOS__
661 
662 struct resolve_args
663 {
664     const WCHAR             *hostname;
665     INTERNET_PORT            port;
666     struct sockaddr_storage *sa;
667 };
668 
resolve_proc(LPVOID arg)669 static DWORD CALLBACK resolve_proc( LPVOID arg )
670 {
671     struct resolve_args *ra = arg;
672     return resolve_hostname( ra->hostname, ra->port, ra->sa );
673 }
674 
netconn_resolve(WCHAR * hostname,INTERNET_PORT port,struct sockaddr_storage * sa,int timeout)675 BOOL netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr_storage *sa, int timeout )
676 {
677     DWORD ret;
678 
679     if (timeout)
680     {
681         DWORD status;
682         HANDLE thread;
683         struct resolve_args ra;
684 
685         ra.hostname = hostname;
686         ra.port     = port;
687         ra.sa       = sa;
688 
689         thread = CreateThread( NULL, 0, resolve_proc, &ra, 0, NULL );
690         if (!thread) return FALSE;
691 
692         status = WaitForSingleObject( thread, timeout );
693         if (status == WAIT_OBJECT_0) GetExitCodeThread( thread, &ret );
694         else ret = ERROR_WINHTTP_TIMEOUT;
695         CloseHandle( thread );
696     }
697     else ret = resolve_hostname( hostname, port, sa );
698 
699     if (ret)
700     {
701         SetLastError( ret );
702         return FALSE;
703     }
704     return TRUE;
705 }
706 
707 #else /* __REACTOS__ */
708 
709 struct async_resolve
710 {
711     const WCHAR             *hostname;
712     INTERNET_PORT            port;
713     struct sockaddr_storage *addr;
714     DWORD                    result;
715     HANDLE                   done;
716 };
717 
resolve_proc(TP_CALLBACK_INSTANCE * instance,void * ctx)718 static void CALLBACK resolve_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
719 {
720     struct async_resolve *async = ctx;
721     async->result = resolve_hostname( async->hostname, async->port, async->addr );
722     SetEvent( async->done );
723 }
724 
netconn_resolve(WCHAR * hostname,INTERNET_PORT port,struct sockaddr_storage * addr,int timeout)725 BOOL netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr_storage *addr, int timeout )
726 {
727     DWORD ret;
728 
729     if (!timeout) ret = resolve_hostname( hostname, port, addr );
730     else
731     {
732         struct async_resolve async;
733 
734         async.hostname = hostname;
735         async.port     = port;
736         async.addr     = addr;
737         if (!(async.done = CreateEventW( NULL, FALSE, FALSE, NULL ))) return FALSE;
738         if (!TrySubmitThreadpoolCallback( resolve_proc, &async, NULL ))
739         {
740             CloseHandle( async.done );
741             return FALSE;
742         }
743         if (WaitForSingleObject( async.done, timeout ) != WAIT_OBJECT_0) ret = ERROR_WINHTTP_TIMEOUT;
744         else ret = async.result;
745         CloseHandle( async.done );
746     }
747 
748     if (ret)
749     {
750         SetLastError( ret );
751         return FALSE;
752     }
753     return TRUE;
754 }
755 
756 #endif /* __REACTOS__ */
757 
netconn_get_certificate(struct netconn * conn)758 const void *netconn_get_certificate( struct netconn *conn )
759 {
760     const CERT_CONTEXT *ret;
761     SECURITY_STATUS res;
762 
763     if (!conn->secure) return NULL;
764     res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&ret);
765     return res == SEC_E_OK ? ret : NULL;
766 }
767 
netconn_get_cipher_strength(struct netconn * conn)768 int netconn_get_cipher_strength( struct netconn *conn )
769 {
770     SecPkgContext_ConnectionInfo conn_info;
771     SECURITY_STATUS res;
772 
773     if (!conn->secure) return 0;
774     res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_CONNECTION_INFO, (void*)&conn_info);
775     if(res != SEC_E_OK)
776         WARN("QueryContextAttributesW failed: %08x\n", res);
777     return res == SEC_E_OK ? conn_info.dwCipherStrength : 0;
778 }
779