winhttp: Remove assertion that is no longer valid in Windows 10.
[wine.git] / dlls / winhttp / net.c
blobbe776ae2971009ee9f7039524c9747fe77b8e5c0
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;
347 in_bufs[1].BufferType = SECBUFFER_EMPTY;
348 in_bufs[1].cbBuffer = 0;
349 in_bufs[1].pvBuffer = NULL;
352 assert(in_bufs[0].BufferType == SECBUFFER_TOKEN);
353 in_bufs[1].BufferType = SECBUFFER_EMPTY;
355 if(in_bufs[0].cbBuffer + 1024 > read_buf_size) {
356 BYTE *new_read_buf;
358 new_read_buf = realloc(read_buf, read_buf_size + 1024);
359 if(!new_read_buf) {
360 status = E_OUTOFMEMORY;
361 break;
364 in_bufs[0].pvBuffer = read_buf = new_read_buf;
365 read_buf_size += 1024;
368 size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0);
369 if(size < 1) {
370 status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
371 break;
374 TRACE( "recv %Iu bytes\n", size );
376 in_bufs[0].cbBuffer += size;
377 in_bufs[0].pvBuffer = read_buf;
378 status = InitializeSecurityContextW(cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc,
379 0, NULL, &out_desc, &attrs, NULL);
380 TRACE( "InitializeSecurityContext ret %#lx\n", status );
382 if(status == SEC_E_OK) {
383 if(in_bufs[1].BufferType == SECBUFFER_EXTRA)
384 FIXME("SECBUFFER_EXTRA not supported\n");
386 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes);
387 if(status != SEC_E_OK) {
388 WARN("Could not get sizes\n");
389 break;
392 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert);
393 if(status == SEC_E_OK) {
394 res = netconn_verify_cert(cert, hostname, security_flags, check_revocation);
395 CertFreeCertificateContext(cert);
396 if(res != ERROR_SUCCESS) {
397 WARN( "cert verify failed: %lu\n", res );
398 break;
400 }else {
401 WARN("Could not get cert\n");
402 break;
405 conn->ssl_read_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
406 if(!conn->ssl_read_buf) {
407 res = ERROR_OUTOFMEMORY;
408 break;
410 conn->ssl_write_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
411 if(!conn->ssl_write_buf) {
412 res = ERROR_OUTOFMEMORY;
413 break;
418 free(read_buf);
420 if(status != SEC_E_OK || res != ERROR_SUCCESS) {
421 WARN( "Failed to initialize security context: %#lx\n", status );
422 free(conn->ssl_read_buf);
423 conn->ssl_read_buf = NULL;
424 free(conn->ssl_write_buf);
425 conn->ssl_write_buf = NULL;
426 DeleteSecurityContext(&ctx);
427 return ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
431 TRACE("established SSL connection\n");
432 conn->secure = TRUE;
433 conn->ssl_ctx = ctx;
434 return ERROR_SUCCESS;
437 static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size, WSAOVERLAPPED *ovr )
439 SecBuffer bufs[4] = {
440 {conn->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, conn->ssl_write_buf},
441 {size, SECBUFFER_DATA, conn->ssl_write_buf+conn->ssl_sizes.cbHeader},
442 {conn->ssl_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, conn->ssl_write_buf+conn->ssl_sizes.cbHeader+size},
443 {0, SECBUFFER_EMPTY, NULL}
445 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
446 SECURITY_STATUS res;
448 memcpy( bufs[1].pvBuffer, msg, size );
449 if ((res = EncryptMessage(&conn->ssl_ctx, 0, &buf_desc, 0)) != SEC_E_OK)
451 WARN( "EncryptMessage failed: %#lx\n", res );
452 return res;
455 if (sock_send( conn->socket, conn->ssl_write_buf, bufs[0].cbBuffer + bufs[1].cbBuffer + bufs[2].cbBuffer, ovr ) < 1)
457 WARN("send failed\n");
458 return WSAGetLastError();
461 return ERROR_SUCCESS;
464 DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent, WSAOVERLAPPED *ovr )
466 DWORD err;
468 if (ovr && !conn->port)
470 if (!(conn->port = CreateIoCompletionPort( (HANDLE)(SOCKET)conn->socket, NULL, (ULONG_PTR)conn->socket, 0 )))
471 ERR( "Failed to create port.\n" );
474 if (conn->secure)
476 const BYTE *ptr = msg;
477 size_t chunk_size;
478 DWORD res;
480 *sent = 0;
481 while (len)
483 chunk_size = min( len, conn->ssl_sizes.cbMaximumMessage );
484 if ((res = send_ssl_chunk( conn, ptr, chunk_size, ovr )))
486 if (res == WSA_IO_PENDING) *sent += chunk_size;
487 return res;
489 *sent += chunk_size;
490 ptr += chunk_size;
491 len -= chunk_size;
494 return ERROR_SUCCESS;
497 if ((*sent = sock_send( conn->socket, msg, len, ovr )) < 0)
499 err = WSAGetLastError();
500 *sent = (err == WSA_IO_PENDING) ? len : 0;
501 return err;
503 return ERROR_SUCCESS;
506 static DWORD read_ssl_chunk( struct netconn *conn, void *buf, SIZE_T buf_size, SIZE_T *ret_size, BOOL *eof )
508 const SIZE_T ssl_buf_size = conn->ssl_sizes.cbHeader+conn->ssl_sizes.cbMaximumMessage+conn->ssl_sizes.cbTrailer;
509 SecBuffer bufs[4];
510 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
511 SSIZE_T size, buf_len;
512 unsigned int i;
513 SECURITY_STATUS res;
515 assert(conn->extra_len < ssl_buf_size);
517 if(conn->extra_len) {
518 memcpy(conn->ssl_read_buf, conn->extra_buf, conn->extra_len);
519 buf_len = conn->extra_len;
520 conn->extra_len = 0;
521 free(conn->extra_buf);
522 conn->extra_buf = NULL;
523 }else {
524 if ((buf_len = sock_recv( conn->socket, conn->ssl_read_buf + conn->extra_len, ssl_buf_size - conn->extra_len, 0)) < 0)
525 return WSAGetLastError();
527 if (!buf_len)
529 *eof = TRUE;
530 return ERROR_SUCCESS;
534 *ret_size = 0;
535 *eof = FALSE;
537 do {
538 memset(bufs, 0, sizeof(bufs));
539 bufs[0].BufferType = SECBUFFER_DATA;
540 bufs[0].cbBuffer = buf_len;
541 bufs[0].pvBuffer = conn->ssl_read_buf;
543 switch ((res = DecryptMessage( &conn->ssl_ctx, &buf_desc, 0, NULL )))
545 case SEC_E_OK:
546 break;
548 case SEC_I_RENEGOTIATE:
549 TRACE("renegotiate\n");
550 return ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED;
552 case SEC_I_CONTEXT_EXPIRED:
553 TRACE("context expired\n");
554 *eof = TRUE;
555 return ERROR_SUCCESS;
557 case SEC_E_INCOMPLETE_MESSAGE:
558 assert(buf_len < ssl_buf_size);
560 if ((size = sock_recv( conn->socket, conn->ssl_read_buf + buf_len, ssl_buf_size - buf_len, 0 )) < 1)
561 return SEC_E_INCOMPLETE_MESSAGE;
563 buf_len += size;
564 continue;
566 default:
567 WARN( "failed: %#lx\n", res );
568 return res;
570 } while (res != SEC_E_OK);
572 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
573 if(bufs[i].BufferType == SECBUFFER_DATA) {
574 size = min(buf_size, bufs[i].cbBuffer);
575 memcpy(buf, bufs[i].pvBuffer, size);
576 if(size < bufs[i].cbBuffer) {
577 assert(!conn->peek_len);
578 conn->peek_msg_mem = conn->peek_msg = malloc(bufs[i].cbBuffer - size);
579 if(!conn->peek_msg)
580 return ERROR_OUTOFMEMORY;
581 conn->peek_len = bufs[i].cbBuffer-size;
582 memcpy(conn->peek_msg, (char*)bufs[i].pvBuffer+size, conn->peek_len);
585 *ret_size = size;
589 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
590 if(bufs[i].BufferType == SECBUFFER_EXTRA) {
591 conn->extra_buf = malloc(bufs[i].cbBuffer);
592 if(!conn->extra_buf)
593 return ERROR_OUTOFMEMORY;
595 conn->extra_len = bufs[i].cbBuffer;
596 memcpy(conn->extra_buf, bufs[i].pvBuffer, conn->extra_len);
600 return ERROR_SUCCESS;
603 DWORD netconn_recv( struct netconn *conn, void *buf, size_t len, int flags, int *recvd )
605 *recvd = 0;
606 if (!len) return ERROR_SUCCESS;
608 if (conn->secure)
610 SIZE_T size;
611 DWORD res;
612 BOOL eof;
614 if (conn->peek_msg)
616 *recvd = min( len, conn->peek_len );
617 memcpy( buf, conn->peek_msg, *recvd );
618 conn->peek_len -= *recvd;
619 conn->peek_msg += *recvd;
621 if (conn->peek_len == 0)
623 free( conn->peek_msg_mem );
624 conn->peek_msg_mem = NULL;
625 conn->peek_msg = NULL;
627 /* check if we have enough data from the peek buffer */
628 if (!(flags & MSG_WAITALL) || *recvd == len) return ERROR_SUCCESS;
630 size = *recvd;
634 SIZE_T cread = 0;
635 if ((res = read_ssl_chunk( conn, (BYTE *)buf + size, len - size, &cread, &eof )))
637 WARN( "read_ssl_chunk failed: %lu\n", res );
638 if (!size) return res;
639 break;
641 if (eof)
643 TRACE("EOF\n");
644 break;
646 size += cread;
648 } while (!size || ((flags & MSG_WAITALL) && size < len));
650 TRACE( "received %Iu bytes\n", size );
651 *recvd = size;
652 return ERROR_SUCCESS;
655 if ((*recvd = sock_recv( conn->socket, buf, len, flags )) < 0) return WSAGetLastError();
656 return ERROR_SUCCESS;
659 void netconn_cancel_io( struct netconn *conn )
661 SOCKET socket = InterlockedExchange( (LONG *)&conn->socket, -1 );
663 closesocket( socket );
666 ULONG netconn_query_data_available( struct netconn *conn )
668 return conn->secure ? conn->peek_len : 0;
671 DWORD netconn_set_timeout( struct netconn *netconn, BOOL send, int value )
673 int opt = send ? SO_SNDTIMEO : SO_RCVTIMEO;
674 if (setsockopt( netconn->socket, SOL_SOCKET, opt, (void *)&value, sizeof(value) ) == -1)
676 DWORD err = WSAGetLastError();
677 WARN( "setsockopt failed (%lu)\n", err );
678 return err;
680 return ERROR_SUCCESS;
683 BOOL netconn_is_alive( struct netconn *netconn )
685 SIZE_T size;
686 int len;
687 char b;
688 DWORD err;
689 BOOL eof;
691 set_blocking( netconn, FALSE );
692 if (netconn->secure)
694 while (!netconn->peek_msg && !(err = read_ssl_chunk( netconn, NULL, 0, &size, &eof )) && !eof)
697 TRACE( "checking secure connection, err %lu\n", err );
699 if (netconn->peek_msg || err == WSAEWOULDBLOCK)
701 set_blocking( netconn, TRUE );
702 return TRUE;
704 if (err != SEC_E_OK && err != SEC_E_INCOMPLETE_MESSAGE)
706 set_blocking( netconn, TRUE );
707 return FALSE;
710 len = sock_recv( netconn->socket, &b, 1, MSG_PEEK );
711 err = WSAGetLastError();
712 set_blocking( netconn, TRUE );
714 return len == 1 || (len == -1 && err == WSAEWOULDBLOCK);
717 static DWORD resolve_hostname( const WCHAR *name, INTERNET_PORT port, struct sockaddr_storage *sa )
719 ADDRINFOW *res, hints;
720 int ret;
722 memset( &hints, 0, sizeof(hints) );
723 /* Prefer IPv4 to IPv6 addresses, since some web servers do not listen on
724 * their IPv6 addresses even though they have IPv6 addresses in the DNS.
726 hints.ai_family = AF_INET;
728 ret = GetAddrInfoW( name, NULL, &hints, &res );
729 if (ret != 0)
731 TRACE("failed to get IPv4 address of %s, retrying with IPv6\n", debugstr_w(name));
732 hints.ai_family = AF_INET6;
733 ret = GetAddrInfoW( name, NULL, &hints, &res );
734 if (ret != 0)
736 TRACE("failed to get address of %s\n", debugstr_w(name));
737 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
740 memcpy( sa, res->ai_addr, res->ai_addrlen );
741 switch (res->ai_family)
743 case AF_INET:
744 ((struct sockaddr_in *)sa)->sin_port = htons( port );
745 break;
746 case AF_INET6:
747 ((struct sockaddr_in6 *)sa)->sin6_port = htons( port );
748 break;
751 FreeAddrInfoW( res );
752 return ERROR_SUCCESS;
755 struct async_resolve
757 const WCHAR *hostname;
758 INTERNET_PORT port;
759 struct sockaddr_storage *addr;
760 DWORD result;
761 HANDLE done;
764 static void CALLBACK resolve_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
766 struct async_resolve *async = ctx;
767 async->result = resolve_hostname( async->hostname, async->port, async->addr );
768 SetEvent( async->done );
771 DWORD netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr_storage *addr, int timeout )
773 DWORD ret;
775 if (!timeout) ret = resolve_hostname( hostname, port, addr );
776 else
778 struct async_resolve async;
780 async.hostname = hostname;
781 async.port = port;
782 async.addr = addr;
783 if (!(async.done = CreateEventW( NULL, FALSE, FALSE, NULL ))) return GetLastError();
784 if (!TrySubmitThreadpoolCallback( resolve_proc, &async, NULL ))
786 CloseHandle( async.done );
787 return GetLastError();
789 if (WaitForSingleObject( async.done, timeout ) != WAIT_OBJECT_0) ret = ERROR_WINHTTP_TIMEOUT;
790 else ret = async.result;
791 CloseHandle( async.done );
794 return ret;
797 const void *netconn_get_certificate( struct netconn *conn )
799 const CERT_CONTEXT *ret;
800 SECURITY_STATUS res;
802 if (!conn->secure) return NULL;
803 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&ret);
804 return res == SEC_E_OK ? ret : NULL;
807 int netconn_get_cipher_strength( struct netconn *conn )
809 SecPkgContext_ConnectionInfo conn_info;
810 SECURITY_STATUS res;
812 if (!conn->secure) return 0;
813 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_CONNECTION_INFO, (void*)&conn_info);
814 if(res != SEC_E_OK)
815 WARN( "QueryContextAttributesW failed: %#lx\n", res );
816 return res == SEC_E_OK ? conn_info.dwCipherStrength : 0;