d3dcompiler: Allow D3DCompile2() to succeed with null output shader blob pointer.
[wine.git] / dlls / winhttp / net.c
blob1be78d126d4279660421faa3cee424f0a9030e0c
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->refs = 1;
218 conn->host = host;
219 conn->sockaddr = *sockaddr;
220 if ((conn->socket = WSASocketW( sockaddr->ss_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED )) == -1)
222 ret = WSAGetLastError();
223 WARN( "unable to create socket (%lu)\n", ret );
224 free( conn );
225 return ret;
228 switch (conn->sockaddr.ss_family)
230 case AF_INET:
231 addr_len = sizeof(struct sockaddr_in);
232 break;
233 case AF_INET6:
234 addr_len = sizeof(struct sockaddr_in6);
235 break;
236 default:
237 ERR( "unhandled family %u\n", conn->sockaddr.ss_family );
238 free( conn );
239 return ERROR_INVALID_PARAMETER;
242 if (timeout > 0) set_blocking( conn, FALSE );
244 if (!connect( conn->socket, (const struct sockaddr *)&conn->sockaddr, addr_len )) ret = ERROR_SUCCESS;
245 else
247 ret = WSAGetLastError();
248 if (ret == WSAEWOULDBLOCK || ret == WSAEINPROGRESS)
250 TIMEVAL timeval = { timeout / 1000, (timeout % 1000) * 1000 };
251 FD_SET set_read, set_error;
252 int res;
254 FD_ZERO( &set_read );
255 FD_SET( conn->socket, &set_read );
256 FD_ZERO( &set_error );
257 FD_SET( conn->socket, &set_error );
258 if ((res = select( conn->socket + 1, NULL, &set_read, &set_error, &timeval )) > 0)
260 if (FD_ISSET(conn->socket, &set_read)) ret = ERROR_SUCCESS;
261 else assert( FD_ISSET(conn->socket, &set_error) );
263 else if (!res) ret = ERROR_WINHTTP_TIMEOUT;
267 if (timeout > 0) set_blocking( conn, TRUE );
269 if (ret)
271 WARN( "unable to connect to host (%lu)\n", ret );
272 closesocket( conn->socket );
273 free( conn );
274 return ret == ERROR_WINHTTP_TIMEOUT ? ERROR_WINHTTP_TIMEOUT : ERROR_WINHTTP_CANNOT_CONNECT;
277 *ret_conn = conn;
278 return ERROR_SUCCESS;
281 void netconn_addref( struct netconn *conn )
283 InterlockedIncrement( &conn->refs );
286 void netconn_release( struct netconn *conn )
288 if (InterlockedDecrement( &conn->refs )) return;
289 TRACE( "Closing connection %p.\n", conn );
290 if (conn->secure)
292 free( conn->peek_msg_mem );
293 free(conn->ssl_read_buf);
294 free(conn->ssl_write_buf);
295 free(conn->extra_buf);
296 DeleteSecurityContext(&conn->ssl_ctx);
298 if (conn->socket != -1)
299 closesocket( conn->socket );
300 release_host( conn->host );
301 if (conn->port)
302 CloseHandle( conn->port );
303 free(conn);
306 DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD security_flags, CredHandle *cred_handle,
307 BOOL check_revocation )
309 SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}};
310 SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs};
311 BYTE *read_buf;
312 SIZE_T read_buf_size = 2048;
313 ULONG attrs = 0;
314 CtxtHandle ctx;
315 SSIZE_T size;
316 const CERT_CONTEXT *cert;
317 SECURITY_STATUS status;
318 DWORD res = ERROR_SUCCESS;
320 const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY
321 |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION;
323 if (!(read_buf = malloc( read_buf_size ))) return ERROR_OUTOFMEMORY;
325 memset( &ctx, 0, sizeof(ctx) );
326 status = InitializeSecurityContextW(cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0,
327 &ctx, &out_desc, &attrs, NULL);
329 assert(status != SEC_E_OK);
331 while(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) {
332 if(out_buf.cbBuffer) {
333 assert(status == SEC_I_CONTINUE_NEEDED);
335 TRACE( "sending %lu bytes\n", out_buf.cbBuffer );
337 size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, NULL);
338 if(size != out_buf.cbBuffer) {
339 ERR("send failed\n");
340 res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
341 break;
344 FreeContextBuffer(out_buf.pvBuffer);
345 out_buf.pvBuffer = NULL;
346 out_buf.cbBuffer = 0;
349 if(status == SEC_I_CONTINUE_NEEDED) {
350 assert(in_bufs[1].cbBuffer < read_buf_size);
352 memmove(read_buf, (BYTE*)in_bufs[0].pvBuffer+in_bufs[0].cbBuffer-in_bufs[1].cbBuffer, in_bufs[1].cbBuffer);
353 in_bufs[0].cbBuffer = in_bufs[1].cbBuffer;
356 assert(in_bufs[0].BufferType == SECBUFFER_TOKEN);
357 in_bufs[1].BufferType = SECBUFFER_EMPTY;
358 in_bufs[1].cbBuffer = 0;
359 in_bufs[1].pvBuffer = NULL;
361 if(in_bufs[0].cbBuffer + 1024 > read_buf_size) {
362 BYTE *new_read_buf;
364 new_read_buf = realloc(read_buf, read_buf_size + 1024);
365 if(!new_read_buf) {
366 status = E_OUTOFMEMORY;
367 break;
370 in_bufs[0].pvBuffer = read_buf = new_read_buf;
371 read_buf_size += 1024;
374 size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0);
375 if(size < 1) {
376 status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
377 break;
380 TRACE( "recv %Iu bytes\n", size );
382 in_bufs[0].cbBuffer += size;
383 in_bufs[0].pvBuffer = read_buf;
384 status = InitializeSecurityContextW(cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc,
385 0, NULL, &out_desc, &attrs, NULL);
386 TRACE( "InitializeSecurityContext ret %#lx\n", status );
388 if(status == SEC_E_OK) {
389 if(in_bufs[1].BufferType == SECBUFFER_EXTRA)
390 FIXME("SECBUFFER_EXTRA not supported\n");
392 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes);
393 if(status != SEC_E_OK) {
394 WARN("Could not get sizes\n");
395 break;
398 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert);
399 if(status == SEC_E_OK) {
400 res = netconn_verify_cert(cert, hostname, security_flags, check_revocation);
401 CertFreeCertificateContext(cert);
402 if(res != ERROR_SUCCESS) {
403 WARN( "cert verify failed: %lu\n", res );
404 break;
406 }else {
407 WARN("Could not get cert\n");
408 break;
411 conn->ssl_read_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
412 if(!conn->ssl_read_buf) {
413 res = ERROR_OUTOFMEMORY;
414 break;
416 conn->ssl_write_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
417 if(!conn->ssl_write_buf) {
418 res = ERROR_OUTOFMEMORY;
419 break;
424 free(read_buf);
426 if(status != SEC_E_OK || res != ERROR_SUCCESS) {
427 WARN( "Failed to initialize security context: %#lx\n", status );
428 free(conn->ssl_read_buf);
429 conn->ssl_read_buf = NULL;
430 free(conn->ssl_write_buf);
431 conn->ssl_write_buf = NULL;
432 DeleteSecurityContext(&ctx);
433 return ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
437 TRACE("established SSL connection\n");
438 conn->secure = TRUE;
439 conn->ssl_ctx = ctx;
440 return ERROR_SUCCESS;
443 static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size, WSAOVERLAPPED *ovr )
445 SecBuffer bufs[4] = {
446 {conn->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, conn->ssl_write_buf},
447 {size, SECBUFFER_DATA, conn->ssl_write_buf+conn->ssl_sizes.cbHeader},
448 {conn->ssl_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, conn->ssl_write_buf+conn->ssl_sizes.cbHeader+size},
449 {0, SECBUFFER_EMPTY, NULL}
451 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
452 SECURITY_STATUS res;
454 memcpy( bufs[1].pvBuffer, msg, size );
455 if ((res = EncryptMessage(&conn->ssl_ctx, 0, &buf_desc, 0)) != SEC_E_OK)
457 WARN( "EncryptMessage failed: %#lx\n", res );
458 return res;
461 if (sock_send( conn->socket, conn->ssl_write_buf, bufs[0].cbBuffer + bufs[1].cbBuffer + bufs[2].cbBuffer, ovr ) < 1)
463 WARN("send failed\n");
464 return WSAGetLastError();
467 return ERROR_SUCCESS;
470 DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent, WSAOVERLAPPED *ovr )
472 DWORD err;
474 if (ovr && !conn->port)
476 if (!(conn->port = CreateIoCompletionPort( (HANDLE)(SOCKET)conn->socket, NULL, (ULONG_PTR)conn->socket, 0 )))
477 ERR( "Failed to create port.\n" );
480 if (conn->secure)
482 const BYTE *ptr = msg;
483 size_t chunk_size;
484 DWORD res;
486 *sent = 0;
487 while (len)
489 chunk_size = min( len, conn->ssl_sizes.cbMaximumMessage );
490 if ((res = send_ssl_chunk( conn, ptr, chunk_size, ovr )))
492 if (res == WSA_IO_PENDING) *sent += chunk_size;
493 return res;
495 *sent += chunk_size;
496 ptr += chunk_size;
497 len -= chunk_size;
500 return ERROR_SUCCESS;
503 if ((*sent = sock_send( conn->socket, msg, len, ovr )) < 0)
505 err = WSAGetLastError();
506 *sent = (err == WSA_IO_PENDING) ? len : 0;
507 return err;
509 return ERROR_SUCCESS;
512 static DWORD read_ssl_chunk( struct netconn *conn, void *buf, SIZE_T buf_size, SIZE_T *ret_size, BOOL *eof )
514 const SIZE_T ssl_buf_size = conn->ssl_sizes.cbHeader+conn->ssl_sizes.cbMaximumMessage+conn->ssl_sizes.cbTrailer;
515 SecBuffer bufs[4];
516 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
517 SSIZE_T size, buf_len;
518 unsigned int i;
519 SECURITY_STATUS res;
521 assert(conn->extra_len < ssl_buf_size);
523 if(conn->extra_len) {
524 memcpy(conn->ssl_read_buf, conn->extra_buf, conn->extra_len);
525 buf_len = conn->extra_len;
526 conn->extra_len = 0;
527 free(conn->extra_buf);
528 conn->extra_buf = NULL;
529 }else {
530 if ((buf_len = sock_recv( conn->socket, conn->ssl_read_buf + conn->extra_len, ssl_buf_size - conn->extra_len, 0)) < 0)
531 return WSAGetLastError();
533 if (!buf_len)
535 *eof = TRUE;
536 return ERROR_SUCCESS;
540 *ret_size = 0;
541 *eof = FALSE;
543 do {
544 memset(bufs, 0, sizeof(bufs));
545 bufs[0].BufferType = SECBUFFER_DATA;
546 bufs[0].cbBuffer = buf_len;
547 bufs[0].pvBuffer = conn->ssl_read_buf;
549 switch ((res = DecryptMessage( &conn->ssl_ctx, &buf_desc, 0, NULL )))
551 case SEC_E_OK:
552 break;
554 case SEC_I_RENEGOTIATE:
555 TRACE("renegotiate\n");
556 return ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED;
558 case SEC_I_CONTEXT_EXPIRED:
559 TRACE("context expired\n");
560 *eof = TRUE;
561 return ERROR_SUCCESS;
563 case SEC_E_INCOMPLETE_MESSAGE:
564 assert(buf_len < ssl_buf_size);
566 if ((size = sock_recv( conn->socket, conn->ssl_read_buf + buf_len, ssl_buf_size - buf_len, 0 )) < 1)
567 return SEC_E_INCOMPLETE_MESSAGE;
569 buf_len += size;
570 continue;
572 default:
573 WARN( "failed: %#lx\n", res );
574 return res;
576 } while (res != SEC_E_OK);
578 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
579 if(bufs[i].BufferType == SECBUFFER_DATA) {
580 size = min(buf_size, bufs[i].cbBuffer);
581 memcpy(buf, bufs[i].pvBuffer, size);
582 if(size < bufs[i].cbBuffer) {
583 assert(!conn->peek_len);
584 conn->peek_msg_mem = conn->peek_msg = malloc(bufs[i].cbBuffer - size);
585 if(!conn->peek_msg)
586 return ERROR_OUTOFMEMORY;
587 conn->peek_len = bufs[i].cbBuffer-size;
588 memcpy(conn->peek_msg, (char*)bufs[i].pvBuffer+size, conn->peek_len);
591 *ret_size = size;
595 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
596 if(bufs[i].BufferType == SECBUFFER_EXTRA) {
597 conn->extra_buf = malloc(bufs[i].cbBuffer);
598 if(!conn->extra_buf)
599 return ERROR_OUTOFMEMORY;
601 conn->extra_len = bufs[i].cbBuffer;
602 memcpy(conn->extra_buf, bufs[i].pvBuffer, conn->extra_len);
606 return ERROR_SUCCESS;
609 DWORD netconn_recv( struct netconn *conn, void *buf, size_t len, int flags, int *recvd )
611 *recvd = 0;
612 if (!len) return ERROR_SUCCESS;
614 if (conn->secure)
616 SIZE_T size;
617 DWORD res;
618 BOOL eof;
620 if (conn->peek_msg)
622 *recvd = min( len, conn->peek_len );
623 memcpy( buf, conn->peek_msg, *recvd );
624 conn->peek_len -= *recvd;
625 conn->peek_msg += *recvd;
627 if (conn->peek_len == 0)
629 free( conn->peek_msg_mem );
630 conn->peek_msg_mem = NULL;
631 conn->peek_msg = NULL;
633 /* check if we have enough data from the peek buffer */
634 if (!(flags & MSG_WAITALL) || *recvd == len) return ERROR_SUCCESS;
636 size = *recvd;
640 SIZE_T cread = 0;
641 if ((res = read_ssl_chunk( conn, (BYTE *)buf + size, len - size, &cread, &eof )))
643 WARN( "read_ssl_chunk failed: %lu\n", res );
644 if (!size) return res;
645 break;
647 if (eof)
649 TRACE("EOF\n");
650 break;
652 size += cread;
654 } while (!size || ((flags & MSG_WAITALL) && size < len));
656 TRACE( "received %Iu bytes\n", size );
657 *recvd = size;
658 return ERROR_SUCCESS;
661 if ((*recvd = sock_recv( conn->socket, buf, len, flags )) < 0) return WSAGetLastError();
662 return ERROR_SUCCESS;
665 void netconn_cancel_io( struct netconn *conn )
667 SOCKET socket = InterlockedExchange( (LONG *)&conn->socket, -1 );
669 closesocket( socket );
672 ULONG netconn_query_data_available( struct netconn *conn )
674 return conn->secure ? conn->peek_len : 0;
677 DWORD netconn_set_timeout( struct netconn *netconn, BOOL send, int value )
679 int opt = send ? SO_SNDTIMEO : SO_RCVTIMEO;
680 if (setsockopt( netconn->socket, SOL_SOCKET, opt, (void *)&value, sizeof(value) ) == -1)
682 DWORD err = WSAGetLastError();
683 WARN( "setsockopt failed (%lu)\n", err );
684 return err;
686 return ERROR_SUCCESS;
689 BOOL netconn_is_alive( struct netconn *netconn )
691 SIZE_T size;
692 int len;
693 char b;
694 DWORD err;
695 BOOL eof;
697 set_blocking( netconn, FALSE );
698 if (netconn->secure)
700 while (!netconn->peek_msg && !(err = read_ssl_chunk( netconn, NULL, 0, &size, &eof )) && !eof)
703 TRACE( "checking secure connection, err %lu\n", err );
705 if (netconn->peek_msg || err == WSAEWOULDBLOCK)
707 set_blocking( netconn, TRUE );
708 return TRUE;
710 if (err != SEC_E_OK && err != SEC_E_INCOMPLETE_MESSAGE)
712 set_blocking( netconn, TRUE );
713 return FALSE;
716 len = sock_recv( netconn->socket, &b, 1, MSG_PEEK );
717 err = WSAGetLastError();
718 set_blocking( netconn, TRUE );
720 return len == 1 || (len == -1 && err == WSAEWOULDBLOCK);
723 static DWORD resolve_hostname( const WCHAR *name, INTERNET_PORT port, struct sockaddr_storage *sa )
725 ADDRINFOW *res, hints;
726 int ret;
728 memset( &hints, 0, sizeof(hints) );
729 /* Prefer IPv4 to IPv6 addresses, since some web servers do not listen on
730 * their IPv6 addresses even though they have IPv6 addresses in the DNS.
732 hints.ai_family = AF_INET;
734 ret = GetAddrInfoW( name, NULL, &hints, &res );
735 if (ret != 0)
737 TRACE("failed to get IPv4 address of %s, retrying with IPv6\n", debugstr_w(name));
738 hints.ai_family = AF_INET6;
739 ret = GetAddrInfoW( name, NULL, &hints, &res );
740 if (ret != 0)
742 TRACE("failed to get address of %s\n", debugstr_w(name));
743 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
746 memcpy( sa, res->ai_addr, res->ai_addrlen );
747 switch (res->ai_family)
749 case AF_INET:
750 ((struct sockaddr_in *)sa)->sin_port = htons( port );
751 break;
752 case AF_INET6:
753 ((struct sockaddr_in6 *)sa)->sin6_port = htons( port );
754 break;
757 FreeAddrInfoW( res );
758 return ERROR_SUCCESS;
761 struct async_resolve
763 LONG ref;
764 WCHAR *hostname;
765 INTERNET_PORT port;
766 struct sockaddr_storage addr;
767 DWORD result;
768 HANDLE done;
771 static struct async_resolve *create_async_resolve( const WCHAR *hostname, INTERNET_PORT port )
773 struct async_resolve *ret;
775 if (!(ret = malloc(sizeof(*ret))))
777 ERR( "No memory.\n" );
778 return NULL;
780 ret->ref = 1;
781 ret->hostname = wcsdup( hostname );
782 ret->port = port;
783 if (!(ret->done = CreateEventW( NULL, FALSE, FALSE, NULL )))
785 free( ret->hostname );
786 free( ret );
787 return NULL;
789 return ret;
792 static void async_resolve_release( struct async_resolve *async )
794 if (InterlockedDecrement( &async->ref )) return;
796 free( async->hostname );
797 CloseHandle( async->done );
798 free( async );
801 static void CALLBACK resolve_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
803 struct async_resolve *async = ctx;
805 async->result = resolve_hostname( async->hostname, async->port, &async->addr );
806 SetEvent( async->done );
807 async_resolve_release( async );
810 DWORD netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr_storage *addr, int timeout )
812 DWORD ret;
814 if (!timeout) ret = resolve_hostname( hostname, port, addr );
815 else
817 struct async_resolve *async;
819 if (!(async = create_async_resolve( hostname, port )))
820 return ERROR_OUTOFMEMORY;
822 InterlockedIncrement( &async->ref );
823 if (!TrySubmitThreadpoolCallback( resolve_proc, async, NULL ))
825 InterlockedDecrement( &async->ref );
826 async_resolve_release( async );
827 return GetLastError();
829 if (WaitForSingleObject( async->done, timeout ) != WAIT_OBJECT_0) ret = ERROR_WINHTTP_TIMEOUT;
830 else
832 *addr = async->addr;
833 ret = async->result;
835 async_resolve_release( async );
838 return ret;
841 const void *netconn_get_certificate( struct netconn *conn )
843 const CERT_CONTEXT *ret;
844 SECURITY_STATUS res;
846 if (!conn->secure) return NULL;
847 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&ret);
848 return res == SEC_E_OK ? ret : NULL;
851 int netconn_get_cipher_strength( struct netconn *conn )
853 SecPkgContext_ConnectionInfo conn_info;
854 SECURITY_STATUS res;
856 if (!conn->secure) return 0;
857 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_CONNECTION_INFO, (void*)&conn_info);
858 if(res != SEC_E_OK)
859 WARN( "QueryContextAttributesW failed: %#lx\n", res );
860 return res == SEC_E_OK ? conn_info.dwCipherStrength : 0;