wined3d: Destroy the Vulkan command pool after cleaning up resources.
[wine.git] / dlls / winhttp / net.c
blobab94b5bfd1d8960ba8f5754b6cfba1ae36cc3577
1 /*
2 * Copyright 2008 Hans Leidekker for CodeWeavers
3 * Copyright 2013 Jacek Caban for CodeWeavers
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.
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.
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
20 #include <assert.h>
21 #include <stdarg.h>
23 #define NONAMELESSUNION
24 #include "windef.h"
25 #include "winbase.h"
26 #include "ws2tcpip.h"
27 #include "winhttp.h"
28 #include "schannel.h"
30 #include "wine/debug.h"
31 #include "winhttp_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
35 static int sock_send(int fd, const void *msg, size_t len, WSAOVERLAPPED *ovr)
37 WSABUF wsabuf;
38 DWORD size;
39 int err;
41 wsabuf.len = len;
42 wsabuf.buf = (void *)msg;
44 if (!WSASend( (SOCKET)fd, &wsabuf, 1, &size, 0, ovr, NULL ))
46 assert( size == len );
47 return size;
49 err = WSAGetLastError();
50 if (!(ovr && err == WSA_IO_PENDING)) WARN( "send error %d\n", err );
51 return -1;
54 BOOL netconn_wait_overlapped_result( struct netconn *conn, WSAOVERLAPPED *ovr, DWORD *len )
56 OVERLAPPED *completion_ovr;
57 ULONG_PTR key;
59 if (!GetQueuedCompletionStatus( conn->port, len, &key, &completion_ovr, INFINITE ))
61 WARN( "GetQueuedCompletionStatus failed, err %lu.\n", GetLastError() );
62 return FALSE;
64 if ((key != conn->socket && conn->socket != -1) || completion_ovr != (OVERLAPPED *)ovr)
66 ERR( "Unexpected completion key %Ix, overlapped %p.\n", key, completion_ovr );
67 return FALSE;
69 return TRUE;
72 static int sock_recv(int fd, void *msg, size_t len, int flags)
74 int ret;
77 if ((ret = recv(fd, msg, len, flags)) == -1) WARN( "recv error %d\n", WSAGetLastError() );
79 while(ret == -1 && WSAGetLastError() == WSAEINTR);
80 return ret;
83 static DWORD netconn_verify_cert( PCCERT_CONTEXT cert, WCHAR *server, DWORD security_flags, BOOL check_revocation )
85 HCERTSTORE store = cert->hCertStore;
86 BOOL ret;
87 CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
88 PCCERT_CHAIN_CONTEXT chain;
89 char oid_server_auth[] = szOID_PKIX_KP_SERVER_AUTH;
90 char *server_auth[] = { oid_server_auth };
91 DWORD err = ERROR_SUCCESS;
93 TRACE("verifying %s\n", debugstr_w( server ));
94 chainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
95 chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = server_auth;
96 ret = CertGetCertificateChain( NULL, cert, NULL, store, &chainPara,
97 check_revocation ? CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT : 0,
98 NULL, &chain );
99 if (ret)
101 if (chain->TrustStatus.dwErrorStatus)
103 static const DWORD supportedErrors =
104 CERT_TRUST_IS_NOT_TIME_VALID |
105 CERT_TRUST_IS_UNTRUSTED_ROOT |
106 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
108 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID)
110 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID))
111 err = ERROR_WINHTTP_SECURE_CERT_DATE_INVALID;
113 else if ((chain->TrustStatus.dwErrorStatus &
114 CERT_TRUST_IS_UNTRUSTED_ROOT) ||
115 (chain->TrustStatus.dwErrorStatus &
116 CERT_TRUST_IS_PARTIAL_CHAIN))
118 if (!(security_flags & SECURITY_FLAG_IGNORE_UNKNOWN_CA))
119 err = ERROR_WINHTTP_SECURE_INVALID_CA;
121 else if ((chain->TrustStatus.dwErrorStatus &
122 CERT_TRUST_IS_OFFLINE_REVOCATION) ||
123 (chain->TrustStatus.dwErrorStatus &
124 CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
125 err = ERROR_WINHTTP_SECURE_CERT_REV_FAILED;
126 else if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)
127 err = ERROR_WINHTTP_SECURE_CERT_REVOKED;
128 else if (chain->TrustStatus.dwErrorStatus &
129 CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
131 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE))
132 err = ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE;
134 else if (chain->TrustStatus.dwErrorStatus & ~supportedErrors)
135 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
137 if (!err)
139 CERT_CHAIN_POLICY_PARA policyPara;
140 SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
141 CERT_CHAIN_POLICY_STATUS policyStatus;
142 CERT_CHAIN_CONTEXT chainCopy;
144 /* Clear chain->TrustStatus.dwErrorStatus so
145 * CertVerifyCertificateChainPolicy will verify additional checks
146 * rather than stopping with an existing, ignored error.
148 memcpy(&chainCopy, chain, sizeof(chainCopy));
149 chainCopy.TrustStatus.dwErrorStatus = 0;
150 sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara);
151 sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER;
152 sslExtraPolicyPara.pwszServerName = server;
153 sslExtraPolicyPara.fdwChecks = security_flags;
154 policyPara.cbSize = sizeof(policyPara);
155 policyPara.dwFlags = 0;
156 policyPara.pvExtraPolicyPara = &sslExtraPolicyPara;
157 ret = CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL,
158 &chainCopy, &policyPara,
159 &policyStatus );
160 /* Any error in the policy status indicates that the
161 * policy couldn't be verified.
163 if (ret && policyStatus.dwError)
165 if (policyStatus.dwError == CERT_E_CN_NO_MATCH)
166 err = ERROR_WINHTTP_SECURE_CERT_CN_INVALID;
167 else
168 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
171 CertFreeCertificateChain( chain );
173 else
174 err = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
175 TRACE( "returning %#lx\n", err );
176 return err;
179 static BOOL winsock_loaded;
181 void netconn_unload( void )
183 if (winsock_loaded) WSACleanup();
186 static BOOL WINAPI winsock_startup( INIT_ONCE *once, void *param, void **ctx )
188 int ret;
189 WSADATA data;
190 if (!(ret = WSAStartup( MAKEWORD(1,1), &data ))) winsock_loaded = TRUE;
191 else ERR( "WSAStartup failed: %d\n", ret );
192 return TRUE;
195 static void winsock_init(void)
197 static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
198 InitOnceExecuteOnce( &once, winsock_startup, NULL, NULL );
201 static void set_blocking( struct netconn *conn, BOOL blocking )
203 ULONG state = !blocking;
204 ioctlsocket( conn->socket, FIONBIO, &state );
207 DWORD netconn_create( struct hostdata *host, const struct sockaddr_storage *sockaddr, int timeout,
208 struct netconn **ret_conn )
210 struct netconn *conn;
211 unsigned int addr_len;
212 DWORD ret;
214 winsock_init();
216 if (!(conn = calloc( 1, sizeof(*conn) ))) return ERROR_OUTOFMEMORY;
217 conn->host = host;
218 conn->sockaddr = *sockaddr;
219 if ((conn->socket = WSASocketW( sockaddr->ss_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED )) == -1)
221 ret = WSAGetLastError();
222 WARN( "unable to create socket (%lu)\n", ret );
223 free( conn );
224 return ret;
227 switch (conn->sockaddr.ss_family)
229 case AF_INET:
230 addr_len = sizeof(struct sockaddr_in);
231 break;
232 case AF_INET6:
233 addr_len = sizeof(struct sockaddr_in6);
234 break;
235 default:
236 ERR( "unhandled family %u\n", conn->sockaddr.ss_family );
237 free( conn );
238 return ERROR_INVALID_PARAMETER;
241 if (timeout > 0) set_blocking( conn, FALSE );
243 if (!connect( conn->socket, (const struct sockaddr *)&conn->sockaddr, addr_len )) ret = ERROR_SUCCESS;
244 else
246 ret = WSAGetLastError();
247 if (ret == WSAEWOULDBLOCK || ret == WSAEINPROGRESS)
249 TIMEVAL timeval = { timeout / 1000, (timeout % 1000) * 1000 };
250 FD_SET set_read, set_error;
251 int res;
253 FD_ZERO( &set_read );
254 FD_SET( conn->socket, &set_read );
255 FD_ZERO( &set_error );
256 FD_SET( conn->socket, &set_error );
257 if ((res = select( conn->socket + 1, NULL, &set_read, &set_error, &timeval )) > 0)
259 if (FD_ISSET(conn->socket, &set_read)) ret = ERROR_SUCCESS;
260 else assert( FD_ISSET(conn->socket, &set_error) );
262 else if (!res) ret = ERROR_WINHTTP_TIMEOUT;
266 if (timeout > 0) set_blocking( conn, TRUE );
268 if (ret)
270 WARN( "unable to connect to host (%lu)\n", ret );
271 closesocket( conn->socket );
272 free( conn );
273 return ret == ERROR_WINHTTP_TIMEOUT ? ERROR_WINHTTP_TIMEOUT : ERROR_WINHTTP_CANNOT_CONNECT;
276 *ret_conn = conn;
277 return ERROR_SUCCESS;
280 void netconn_close( struct netconn *conn )
282 if (conn->secure)
284 free( conn->peek_msg_mem );
285 free(conn->ssl_read_buf);
286 free(conn->ssl_write_buf);
287 free(conn->extra_buf);
288 DeleteSecurityContext(&conn->ssl_ctx);
290 if (conn->socket != -1)
291 closesocket( conn->socket );
292 release_host( conn->host );
293 if (conn->port)
294 CloseHandle( conn->port );
295 free(conn);
298 DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD security_flags, CredHandle *cred_handle,
299 BOOL check_revocation )
301 SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}};
302 SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs};
303 BYTE *read_buf;
304 SIZE_T read_buf_size = 2048;
305 ULONG attrs = 0;
306 CtxtHandle ctx;
307 SSIZE_T size;
308 const CERT_CONTEXT *cert;
309 SECURITY_STATUS status;
310 DWORD res = ERROR_SUCCESS;
312 const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY
313 |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION;
315 if (!(read_buf = malloc( read_buf_size ))) return ERROR_OUTOFMEMORY;
317 memset( &ctx, 0, sizeof(ctx) );
318 status = InitializeSecurityContextW(cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0,
319 &ctx, &out_desc, &attrs, NULL);
321 assert(status != SEC_E_OK);
323 while(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) {
324 if(out_buf.cbBuffer) {
325 assert(status == SEC_I_CONTINUE_NEEDED);
327 TRACE( "sending %lu bytes\n", out_buf.cbBuffer );
329 size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, NULL);
330 if(size != out_buf.cbBuffer) {
331 ERR("send failed\n");
332 res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
333 break;
336 FreeContextBuffer(out_buf.pvBuffer);
337 out_buf.pvBuffer = NULL;
338 out_buf.cbBuffer = 0;
341 if(status == SEC_I_CONTINUE_NEEDED) {
342 assert(in_bufs[1].cbBuffer < read_buf_size);
344 memmove(read_buf, (BYTE*)in_bufs[0].pvBuffer+in_bufs[0].cbBuffer-in_bufs[1].cbBuffer, in_bufs[1].cbBuffer);
345 in_bufs[0].cbBuffer = in_bufs[1].cbBuffer;
348 assert(in_bufs[0].BufferType == SECBUFFER_TOKEN);
349 in_bufs[1].BufferType = SECBUFFER_EMPTY;
350 in_bufs[1].cbBuffer = 0;
351 in_bufs[1].pvBuffer = NULL;
353 if(in_bufs[0].cbBuffer + 1024 > read_buf_size) {
354 BYTE *new_read_buf;
356 new_read_buf = realloc(read_buf, read_buf_size + 1024);
357 if(!new_read_buf) {
358 status = E_OUTOFMEMORY;
359 break;
362 in_bufs[0].pvBuffer = read_buf = new_read_buf;
363 read_buf_size += 1024;
366 size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0);
367 if(size < 1) {
368 status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
369 break;
372 TRACE( "recv %Iu bytes\n", size );
374 in_bufs[0].cbBuffer += size;
375 in_bufs[0].pvBuffer = read_buf;
376 status = InitializeSecurityContextW(cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc,
377 0, NULL, &out_desc, &attrs, NULL);
378 TRACE( "InitializeSecurityContext ret %#lx\n", status );
380 if(status == SEC_E_OK) {
381 if(in_bufs[1].BufferType == SECBUFFER_EXTRA)
382 FIXME("SECBUFFER_EXTRA not supported\n");
384 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes);
385 if(status != SEC_E_OK) {
386 WARN("Could not get sizes\n");
387 break;
390 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert);
391 if(status == SEC_E_OK) {
392 res = netconn_verify_cert(cert, hostname, security_flags, check_revocation);
393 CertFreeCertificateContext(cert);
394 if(res != ERROR_SUCCESS) {
395 WARN( "cert verify failed: %lu\n", res );
396 break;
398 }else {
399 WARN("Could not get cert\n");
400 break;
403 conn->ssl_read_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
404 if(!conn->ssl_read_buf) {
405 res = ERROR_OUTOFMEMORY;
406 break;
408 conn->ssl_write_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
409 if(!conn->ssl_write_buf) {
410 res = ERROR_OUTOFMEMORY;
411 break;
416 free(read_buf);
418 if(status != SEC_E_OK || res != ERROR_SUCCESS) {
419 WARN( "Failed to initialize security context: %#lx\n", status );
420 free(conn->ssl_read_buf);
421 conn->ssl_read_buf = NULL;
422 free(conn->ssl_write_buf);
423 conn->ssl_write_buf = NULL;
424 DeleteSecurityContext(&ctx);
425 return ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
429 TRACE("established SSL connection\n");
430 conn->secure = TRUE;
431 conn->ssl_ctx = ctx;
432 return ERROR_SUCCESS;
435 static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size, WSAOVERLAPPED *ovr )
437 SecBuffer bufs[4] = {
438 {conn->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, conn->ssl_write_buf},
439 {size, SECBUFFER_DATA, conn->ssl_write_buf+conn->ssl_sizes.cbHeader},
440 {conn->ssl_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, conn->ssl_write_buf+conn->ssl_sizes.cbHeader+size},
441 {0, SECBUFFER_EMPTY, NULL}
443 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
444 SECURITY_STATUS res;
446 memcpy( bufs[1].pvBuffer, msg, size );
447 if ((res = EncryptMessage(&conn->ssl_ctx, 0, &buf_desc, 0)) != SEC_E_OK)
449 WARN( "EncryptMessage failed: %#lx\n", res );
450 return res;
453 if (sock_send( conn->socket, conn->ssl_write_buf, bufs[0].cbBuffer + bufs[1].cbBuffer + bufs[2].cbBuffer, ovr ) < 1)
455 WARN("send failed\n");
456 return WSAGetLastError();
459 return ERROR_SUCCESS;
462 DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent, WSAOVERLAPPED *ovr )
464 DWORD err;
466 if (ovr && !conn->port)
468 if (!(conn->port = CreateIoCompletionPort( (HANDLE)(SOCKET)conn->socket, NULL, (ULONG_PTR)conn->socket, 0 )))
469 ERR( "Failed to create port.\n" );
472 if (conn->secure)
474 const BYTE *ptr = msg;
475 size_t chunk_size;
476 DWORD res;
478 *sent = 0;
479 while (len)
481 chunk_size = min( len, conn->ssl_sizes.cbMaximumMessage );
482 if ((res = send_ssl_chunk( conn, ptr, chunk_size, ovr )))
484 if (res == WSA_IO_PENDING) *sent += chunk_size;
485 return res;
487 *sent += chunk_size;
488 ptr += chunk_size;
489 len -= chunk_size;
492 return ERROR_SUCCESS;
495 if ((*sent = sock_send( conn->socket, msg, len, ovr )) < 0)
497 err = WSAGetLastError();
498 *sent = (err == WSA_IO_PENDING) ? len : 0;
499 return err;
501 return ERROR_SUCCESS;
504 static DWORD read_ssl_chunk( struct netconn *conn, void *buf, SIZE_T buf_size, SIZE_T *ret_size, BOOL *eof )
506 const SIZE_T ssl_buf_size = conn->ssl_sizes.cbHeader+conn->ssl_sizes.cbMaximumMessage+conn->ssl_sizes.cbTrailer;
507 SecBuffer bufs[4];
508 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
509 SSIZE_T size, buf_len;
510 unsigned int i;
511 SECURITY_STATUS res;
513 assert(conn->extra_len < ssl_buf_size);
515 if(conn->extra_len) {
516 memcpy(conn->ssl_read_buf, conn->extra_buf, conn->extra_len);
517 buf_len = conn->extra_len;
518 conn->extra_len = 0;
519 free(conn->extra_buf);
520 conn->extra_buf = NULL;
521 }else {
522 if ((buf_len = sock_recv( conn->socket, conn->ssl_read_buf + conn->extra_len, ssl_buf_size - conn->extra_len, 0)) < 0)
523 return WSAGetLastError();
525 if (!buf_len)
527 *eof = TRUE;
528 return ERROR_SUCCESS;
532 *ret_size = 0;
533 *eof = FALSE;
535 do {
536 memset(bufs, 0, sizeof(bufs));
537 bufs[0].BufferType = SECBUFFER_DATA;
538 bufs[0].cbBuffer = buf_len;
539 bufs[0].pvBuffer = conn->ssl_read_buf;
541 switch ((res = DecryptMessage( &conn->ssl_ctx, &buf_desc, 0, NULL )))
543 case SEC_E_OK:
544 break;
546 case SEC_I_RENEGOTIATE:
547 TRACE("renegotiate\n");
548 return ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED;
550 case SEC_I_CONTEXT_EXPIRED:
551 TRACE("context expired\n");
552 *eof = TRUE;
553 return ERROR_SUCCESS;
555 case SEC_E_INCOMPLETE_MESSAGE:
556 assert(buf_len < ssl_buf_size);
558 if ((size = sock_recv( conn->socket, conn->ssl_read_buf + buf_len, ssl_buf_size - buf_len, 0 )) < 1)
559 return SEC_E_INCOMPLETE_MESSAGE;
561 buf_len += size;
562 continue;
564 default:
565 WARN( "failed: %#lx\n", res );
566 return res;
568 } while (res != SEC_E_OK);
570 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
571 if(bufs[i].BufferType == SECBUFFER_DATA) {
572 size = min(buf_size, bufs[i].cbBuffer);
573 memcpy(buf, bufs[i].pvBuffer, size);
574 if(size < bufs[i].cbBuffer) {
575 assert(!conn->peek_len);
576 conn->peek_msg_mem = conn->peek_msg = malloc(bufs[i].cbBuffer - size);
577 if(!conn->peek_msg)
578 return ERROR_OUTOFMEMORY;
579 conn->peek_len = bufs[i].cbBuffer-size;
580 memcpy(conn->peek_msg, (char*)bufs[i].pvBuffer+size, conn->peek_len);
583 *ret_size = size;
587 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
588 if(bufs[i].BufferType == SECBUFFER_EXTRA) {
589 conn->extra_buf = malloc(bufs[i].cbBuffer);
590 if(!conn->extra_buf)
591 return ERROR_OUTOFMEMORY;
593 conn->extra_len = bufs[i].cbBuffer;
594 memcpy(conn->extra_buf, bufs[i].pvBuffer, conn->extra_len);
598 return ERROR_SUCCESS;
601 DWORD netconn_recv( struct netconn *conn, void *buf, size_t len, int flags, int *recvd )
603 *recvd = 0;
604 if (!len) return ERROR_SUCCESS;
606 if (conn->secure)
608 SIZE_T size;
609 DWORD res;
610 BOOL eof;
612 if (conn->peek_msg)
614 *recvd = min( len, conn->peek_len );
615 memcpy( buf, conn->peek_msg, *recvd );
616 conn->peek_len -= *recvd;
617 conn->peek_msg += *recvd;
619 if (conn->peek_len == 0)
621 free( conn->peek_msg_mem );
622 conn->peek_msg_mem = NULL;
623 conn->peek_msg = NULL;
625 /* check if we have enough data from the peek buffer */
626 if (!(flags & MSG_WAITALL) || *recvd == len) return ERROR_SUCCESS;
628 size = *recvd;
632 SIZE_T cread = 0;
633 if ((res = read_ssl_chunk( conn, (BYTE *)buf + size, len - size, &cread, &eof )))
635 WARN( "read_ssl_chunk failed: %lu\n", res );
636 if (!size) return res;
637 break;
639 if (eof)
641 TRACE("EOF\n");
642 break;
644 size += cread;
646 } while (!size || ((flags & MSG_WAITALL) && size < len));
648 TRACE( "received %Iu bytes\n", size );
649 *recvd = size;
650 return ERROR_SUCCESS;
653 if ((*recvd = sock_recv( conn->socket, buf, len, flags )) < 0) return WSAGetLastError();
654 return ERROR_SUCCESS;
657 void netconn_cancel_io( struct netconn *conn )
659 SOCKET socket = InterlockedExchange( (LONG *)&conn->socket, -1 );
661 closesocket( socket );
664 ULONG netconn_query_data_available( struct netconn *conn )
666 return conn->secure ? conn->peek_len : 0;
669 DWORD netconn_set_timeout( struct netconn *netconn, BOOL send, int value )
671 int opt = send ? SO_SNDTIMEO : SO_RCVTIMEO;
672 if (setsockopt( netconn->socket, SOL_SOCKET, opt, (void *)&value, sizeof(value) ) == -1)
674 DWORD err = WSAGetLastError();
675 WARN( "setsockopt failed (%lu)\n", err );
676 return err;
678 return ERROR_SUCCESS;
681 BOOL netconn_is_alive( struct netconn *netconn )
683 SIZE_T size;
684 int len;
685 char b;
686 DWORD err;
687 BOOL eof;
689 set_blocking( netconn, FALSE );
690 if (netconn->secure)
692 while (!netconn->peek_msg && !(err = read_ssl_chunk( netconn, NULL, 0, &size, &eof )) && !eof)
695 TRACE( "checking secure connection, err %lu\n", err );
697 if (netconn->peek_msg || err == WSAEWOULDBLOCK)
699 set_blocking( netconn, TRUE );
700 return TRUE;
702 if (err != SEC_E_OK && err != SEC_E_INCOMPLETE_MESSAGE)
704 set_blocking( netconn, TRUE );
705 return FALSE;
708 len = sock_recv( netconn->socket, &b, 1, MSG_PEEK );
709 err = WSAGetLastError();
710 set_blocking( netconn, TRUE );
712 return len == 1 || (len == -1 && err == WSAEWOULDBLOCK);
715 static DWORD resolve_hostname( const WCHAR *name, INTERNET_PORT port, struct sockaddr_storage *sa )
717 ADDRINFOW *res, hints;
718 int ret;
720 memset( &hints, 0, sizeof(hints) );
721 /* Prefer IPv4 to IPv6 addresses, since some web servers do not listen on
722 * their IPv6 addresses even though they have IPv6 addresses in the DNS.
724 hints.ai_family = AF_INET;
726 ret = GetAddrInfoW( name, NULL, &hints, &res );
727 if (ret != 0)
729 TRACE("failed to get IPv4 address of %s, retrying with IPv6\n", debugstr_w(name));
730 hints.ai_family = AF_INET6;
731 ret = GetAddrInfoW( name, NULL, &hints, &res );
732 if (ret != 0)
734 TRACE("failed to get address of %s\n", debugstr_w(name));
735 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
738 memcpy( sa, res->ai_addr, res->ai_addrlen );
739 switch (res->ai_family)
741 case AF_INET:
742 ((struct sockaddr_in *)sa)->sin_port = htons( port );
743 break;
744 case AF_INET6:
745 ((struct sockaddr_in6 *)sa)->sin6_port = htons( port );
746 break;
749 FreeAddrInfoW( res );
750 return ERROR_SUCCESS;
753 struct async_resolve
755 LONG ref;
756 WCHAR *hostname;
757 INTERNET_PORT port;
758 struct sockaddr_storage addr;
759 DWORD result;
760 HANDLE done;
763 static struct async_resolve *create_async_resolve( const WCHAR *hostname, INTERNET_PORT port )
765 struct async_resolve *ret;
767 if (!(ret = malloc(sizeof(*ret))))
769 ERR( "No memory.\n" );
770 return NULL;
772 ret->ref = 1;
773 ret->hostname = strdupW( hostname );
774 ret->port = port;
775 if (!(ret->done = CreateEventW( NULL, FALSE, FALSE, NULL )))
777 free( ret->hostname );
778 free( ret );
779 return NULL;
781 return ret;
784 static void async_resolve_release( struct async_resolve *async )
786 if (InterlockedDecrement( &async->ref )) return;
788 free( async->hostname );
789 CloseHandle( async->done );
790 free( async );
793 static void CALLBACK resolve_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
795 struct async_resolve *async = ctx;
797 async->result = resolve_hostname( async->hostname, async->port, &async->addr );
798 SetEvent( async->done );
799 async_resolve_release( async );
802 DWORD netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr_storage *addr, int timeout )
804 DWORD ret;
806 if (!timeout) ret = resolve_hostname( hostname, port, addr );
807 else
809 struct async_resolve *async;
811 if (!(async = create_async_resolve( hostname, port )))
812 return ERROR_OUTOFMEMORY;
814 InterlockedIncrement( &async->ref );
815 if (!TrySubmitThreadpoolCallback( resolve_proc, async, NULL ))
817 InterlockedDecrement( &async->ref );
818 async_resolve_release( async );
819 return GetLastError();
821 if (WaitForSingleObject( async->done, timeout ) != WAIT_OBJECT_0) ret = ERROR_WINHTTP_TIMEOUT;
822 else
824 *addr = async->addr;
825 ret = async->result;
827 async_resolve_release( async );
830 return ret;
833 const void *netconn_get_certificate( struct netconn *conn )
835 const CERT_CONTEXT *ret;
836 SECURITY_STATUS res;
838 if (!conn->secure) return NULL;
839 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&ret);
840 return res == SEC_E_OK ? ret : NULL;
843 int netconn_get_cipher_strength( struct netconn *conn )
845 SecPkgContext_ConnectionInfo conn_info;
846 SECURITY_STATUS res;
848 if (!conn->secure) return 0;
849 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_CONNECTION_INFO, (void*)&conn_info);
850 if(res != SEC_E_OK)
851 WARN( "QueryContextAttributesW failed: %#lx\n", res );
852 return res == SEC_E_OK ? conn_info.dwCipherStrength : 0;