explorerframe/tests: Use CRT memory allocation functions.
[wine.git] / dlls / winhttp / net.c
blob07a65c5465f77885a6283e6df42f82e6808b27bb
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 static int sock_recv(int fd, void *msg, size_t len, int flags)
56 int ret;
59 if ((ret = recv(fd, msg, len, flags)) == -1) WARN( "recv error %d\n", WSAGetLastError() );
61 while(ret == -1 && WSAGetLastError() == WSAEINTR);
62 return ret;
65 static DWORD netconn_verify_cert( PCCERT_CONTEXT cert, WCHAR *server, DWORD security_flags, BOOL check_revocation )
67 HCERTSTORE store = cert->hCertStore;
68 BOOL ret;
69 CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
70 PCCERT_CHAIN_CONTEXT chain;
71 char oid_server_auth[] = szOID_PKIX_KP_SERVER_AUTH;
72 char *server_auth[] = { oid_server_auth };
73 DWORD err = ERROR_SUCCESS;
75 TRACE("verifying %s\n", debugstr_w( server ));
76 chainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
77 chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = server_auth;
78 ret = CertGetCertificateChain( NULL, cert, NULL, store, &chainPara,
79 check_revocation ? CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT : 0,
80 NULL, &chain );
81 if (ret)
83 if (chain->TrustStatus.dwErrorStatus)
85 static const DWORD supportedErrors =
86 CERT_TRUST_IS_NOT_TIME_VALID |
87 CERT_TRUST_IS_UNTRUSTED_ROOT |
88 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
90 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID)
92 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID))
93 err = ERROR_WINHTTP_SECURE_CERT_DATE_INVALID;
95 else if ((chain->TrustStatus.dwErrorStatus &
96 CERT_TRUST_IS_UNTRUSTED_ROOT) ||
97 (chain->TrustStatus.dwErrorStatus &
98 CERT_TRUST_IS_PARTIAL_CHAIN))
100 if (!(security_flags & SECURITY_FLAG_IGNORE_UNKNOWN_CA))
101 err = ERROR_WINHTTP_SECURE_INVALID_CA;
103 else if ((chain->TrustStatus.dwErrorStatus &
104 CERT_TRUST_IS_OFFLINE_REVOCATION) ||
105 (chain->TrustStatus.dwErrorStatus &
106 CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
107 err = ERROR_WINHTTP_SECURE_CERT_REV_FAILED;
108 else if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)
109 err = ERROR_WINHTTP_SECURE_CERT_REVOKED;
110 else if (chain->TrustStatus.dwErrorStatus &
111 CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
113 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE))
114 err = ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE;
116 else if (chain->TrustStatus.dwErrorStatus & ~supportedErrors)
117 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
119 if (!err)
121 CERT_CHAIN_POLICY_PARA policyPara;
122 SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
123 CERT_CHAIN_POLICY_STATUS policyStatus;
124 CERT_CHAIN_CONTEXT chainCopy;
126 /* Clear chain->TrustStatus.dwErrorStatus so
127 * CertVerifyCertificateChainPolicy will verify additional checks
128 * rather than stopping with an existing, ignored error.
130 memcpy(&chainCopy, chain, sizeof(chainCopy));
131 chainCopy.TrustStatus.dwErrorStatus = 0;
132 sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara);
133 sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER;
134 sslExtraPolicyPara.pwszServerName = server;
135 sslExtraPolicyPara.fdwChecks = security_flags;
136 policyPara.cbSize = sizeof(policyPara);
137 policyPara.dwFlags = 0;
138 policyPara.pvExtraPolicyPara = &sslExtraPolicyPara;
139 ret = CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL,
140 &chainCopy, &policyPara,
141 &policyStatus );
142 /* Any error in the policy status indicates that the
143 * policy couldn't be verified.
145 if (ret && policyStatus.dwError)
147 if (policyStatus.dwError == CERT_E_CN_NO_MATCH)
148 err = ERROR_WINHTTP_SECURE_CERT_CN_INVALID;
149 else
150 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
153 CertFreeCertificateChain( chain );
155 else
156 err = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
157 TRACE( "returning %#lx\n", err );
158 return err;
161 static BOOL winsock_loaded;
163 void netconn_unload( void )
165 if (winsock_loaded) WSACleanup();
168 static BOOL WINAPI winsock_startup( INIT_ONCE *once, void *param, void **ctx )
170 int ret;
171 WSADATA data;
172 if (!(ret = WSAStartup( MAKEWORD(1,1), &data ))) winsock_loaded = TRUE;
173 else ERR( "WSAStartup failed: %d\n", ret );
174 return TRUE;
177 static void winsock_init(void)
179 static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
180 InitOnceExecuteOnce( &once, winsock_startup, NULL, NULL );
183 static void set_blocking( struct netconn *conn, BOOL blocking )
185 ULONG state = !blocking;
186 ioctlsocket( conn->socket, FIONBIO, &state );
189 DWORD netconn_create( struct hostdata *host, const struct sockaddr_storage *sockaddr, int timeout,
190 struct netconn **ret_conn )
192 struct netconn *conn;
193 unsigned int addr_len;
194 DWORD ret;
196 winsock_init();
198 if (!(conn = calloc( 1, sizeof(*conn) ))) return ERROR_OUTOFMEMORY;
199 conn->host = host;
200 conn->sockaddr = *sockaddr;
201 if ((conn->socket = WSASocketW( sockaddr->ss_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED )) == -1)
203 ret = WSAGetLastError();
204 WARN( "unable to create socket (%lu)\n", ret );
205 free( conn );
206 return ret;
209 switch (conn->sockaddr.ss_family)
211 case AF_INET:
212 addr_len = sizeof(struct sockaddr_in);
213 break;
214 case AF_INET6:
215 addr_len = sizeof(struct sockaddr_in6);
216 break;
217 default:
218 ERR( "unhandled family %u\n", conn->sockaddr.ss_family );
219 free( conn );
220 return ERROR_INVALID_PARAMETER;
223 if (timeout > 0) set_blocking( conn, FALSE );
225 if (!connect( conn->socket, (const struct sockaddr *)&conn->sockaddr, addr_len )) ret = ERROR_SUCCESS;
226 else
228 ret = WSAGetLastError();
229 if (ret == WSAEWOULDBLOCK || ret == WSAEINPROGRESS)
231 TIMEVAL timeval = { timeout / 1000, (timeout % 1000) * 1000 };
232 FD_SET set_read, set_error;
233 int res;
235 FD_ZERO( &set_read );
236 FD_SET( conn->socket, &set_read );
237 FD_ZERO( &set_error );
238 FD_SET( conn->socket, &set_error );
239 if ((res = select( conn->socket + 1, NULL, &set_read, &set_error, &timeval )) > 0)
241 if (FD_ISSET(conn->socket, &set_read)) ret = ERROR_SUCCESS;
242 else assert( FD_ISSET(conn->socket, &set_error) );
244 else if (!res) ret = ERROR_WINHTTP_TIMEOUT;
248 if (timeout > 0) set_blocking( conn, TRUE );
250 if (ret)
252 WARN( "unable to connect to host (%lu)\n", ret );
253 closesocket( conn->socket );
254 free( conn );
255 return ret == ERROR_WINHTTP_TIMEOUT ? ERROR_WINHTTP_TIMEOUT : ERROR_WINHTTP_CANNOT_CONNECT;
258 *ret_conn = conn;
259 return ERROR_SUCCESS;
262 void netconn_close( struct netconn *conn )
264 if (conn->secure)
266 free( conn->peek_msg_mem );
267 free(conn->ssl_read_buf);
268 free(conn->ssl_write_buf);
269 free(conn->extra_buf);
270 DeleteSecurityContext(&conn->ssl_ctx);
272 closesocket( conn->socket );
273 release_host( conn->host );
274 free(conn);
277 DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD security_flags, CredHandle *cred_handle,
278 BOOL check_revocation )
280 SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}};
281 SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs};
282 BYTE *read_buf;
283 SIZE_T read_buf_size = 2048;
284 ULONG attrs = 0;
285 CtxtHandle ctx;
286 SSIZE_T size;
287 const CERT_CONTEXT *cert;
288 SECURITY_STATUS status;
289 DWORD res = ERROR_SUCCESS;
291 const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY
292 |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION;
294 if (!(read_buf = malloc( read_buf_size ))) return ERROR_OUTOFMEMORY;
296 memset( &ctx, 0, sizeof(ctx) );
297 status = InitializeSecurityContextW(cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0,
298 &ctx, &out_desc, &attrs, NULL);
300 assert(status != SEC_E_OK);
302 while(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) {
303 if(out_buf.cbBuffer) {
304 assert(status == SEC_I_CONTINUE_NEEDED);
306 TRACE( "sending %lu bytes\n", out_buf.cbBuffer );
308 size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, NULL);
309 if(size != out_buf.cbBuffer) {
310 ERR("send failed\n");
311 res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
312 break;
315 FreeContextBuffer(out_buf.pvBuffer);
316 out_buf.pvBuffer = NULL;
317 out_buf.cbBuffer = 0;
320 if(status == SEC_I_CONTINUE_NEEDED) {
321 assert(in_bufs[1].cbBuffer < read_buf_size);
323 memmove(read_buf, (BYTE*)in_bufs[0].pvBuffer+in_bufs[0].cbBuffer-in_bufs[1].cbBuffer, in_bufs[1].cbBuffer);
324 in_bufs[0].cbBuffer = in_bufs[1].cbBuffer;
326 in_bufs[1].BufferType = SECBUFFER_EMPTY;
327 in_bufs[1].cbBuffer = 0;
328 in_bufs[1].pvBuffer = NULL;
331 assert(in_bufs[0].BufferType == SECBUFFER_TOKEN);
332 assert(in_bufs[1].BufferType == SECBUFFER_EMPTY);
334 if(in_bufs[0].cbBuffer + 1024 > read_buf_size) {
335 BYTE *new_read_buf;
337 new_read_buf = realloc(read_buf, read_buf_size + 1024);
338 if(!new_read_buf) {
339 status = E_OUTOFMEMORY;
340 break;
343 in_bufs[0].pvBuffer = read_buf = new_read_buf;
344 read_buf_size += 1024;
347 size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0);
348 if(size < 1) {
349 status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
350 break;
353 TRACE( "recv %Iu bytes\n", size );
355 in_bufs[0].cbBuffer += size;
356 in_bufs[0].pvBuffer = read_buf;
357 status = InitializeSecurityContextW(cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc,
358 0, NULL, &out_desc, &attrs, NULL);
359 TRACE( "InitializeSecurityContext ret %#lx\n", status );
361 if(status == SEC_E_OK) {
362 if(in_bufs[1].BufferType == SECBUFFER_EXTRA)
363 FIXME("SECBUFFER_EXTRA not supported\n");
365 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes);
366 if(status != SEC_E_OK) {
367 WARN("Could not get sizes\n");
368 break;
371 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert);
372 if(status == SEC_E_OK) {
373 res = netconn_verify_cert(cert, hostname, security_flags, check_revocation);
374 CertFreeCertificateContext(cert);
375 if(res != ERROR_SUCCESS) {
376 WARN( "cert verify failed: %lu\n", res );
377 break;
379 }else {
380 WARN("Could not get cert\n");
381 break;
384 conn->ssl_read_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
385 if(!conn->ssl_read_buf) {
386 res = ERROR_OUTOFMEMORY;
387 break;
389 conn->ssl_write_buf = malloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
390 if(!conn->ssl_write_buf) {
391 res = ERROR_OUTOFMEMORY;
392 break;
397 free(read_buf);
399 if(status != SEC_E_OK || res != ERROR_SUCCESS) {
400 WARN( "Failed to initialize security context: %#lx\n", status );
401 free(conn->ssl_read_buf);
402 conn->ssl_read_buf = NULL;
403 free(conn->ssl_write_buf);
404 conn->ssl_write_buf = NULL;
405 DeleteSecurityContext(&ctx);
406 return ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
410 TRACE("established SSL connection\n");
411 conn->secure = TRUE;
412 conn->ssl_ctx = ctx;
413 return ERROR_SUCCESS;
416 static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size, WSAOVERLAPPED *ovr )
418 SecBuffer bufs[4] = {
419 {conn->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, conn->ssl_write_buf},
420 {size, SECBUFFER_DATA, conn->ssl_write_buf+conn->ssl_sizes.cbHeader},
421 {conn->ssl_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, conn->ssl_write_buf+conn->ssl_sizes.cbHeader+size},
422 {0, SECBUFFER_EMPTY, NULL}
424 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
425 SECURITY_STATUS res;
427 memcpy( bufs[1].pvBuffer, msg, size );
428 if ((res = EncryptMessage(&conn->ssl_ctx, 0, &buf_desc, 0)) != SEC_E_OK)
430 WARN( "EncryptMessage failed: %#lx\n", res );
431 return res;
434 if (sock_send( conn->socket, conn->ssl_write_buf, bufs[0].cbBuffer + bufs[1].cbBuffer + bufs[2].cbBuffer, ovr ) < 1)
436 WARN("send failed\n");
437 return WSAGetLastError();
440 return ERROR_SUCCESS;
443 DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent, WSAOVERLAPPED *ovr )
445 DWORD err;
447 if (conn->secure)
449 const BYTE *ptr = msg;
450 size_t chunk_size;
451 DWORD res;
453 *sent = 0;
454 while (len)
456 chunk_size = min( len, conn->ssl_sizes.cbMaximumMessage );
457 if ((res = send_ssl_chunk( conn, ptr, chunk_size, ovr )))
459 if (res == WSA_IO_PENDING) *sent += chunk_size;
460 return res;
462 *sent += chunk_size;
463 ptr += chunk_size;
464 len -= chunk_size;
467 return ERROR_SUCCESS;
470 if ((*sent = sock_send( conn->socket, msg, len, ovr )) < 0)
472 err = WSAGetLastError();
473 *sent = (err == WSA_IO_PENDING) ? len : 0;
474 return err;
476 return ERROR_SUCCESS;
479 static DWORD read_ssl_chunk( struct netconn *conn, void *buf, SIZE_T buf_size, SIZE_T *ret_size, BOOL *eof )
481 const SIZE_T ssl_buf_size = conn->ssl_sizes.cbHeader+conn->ssl_sizes.cbMaximumMessage+conn->ssl_sizes.cbTrailer;
482 SecBuffer bufs[4];
483 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
484 SSIZE_T size, buf_len;
485 unsigned int i;
486 SECURITY_STATUS res;
488 assert(conn->extra_len < ssl_buf_size);
490 if(conn->extra_len) {
491 memcpy(conn->ssl_read_buf, conn->extra_buf, conn->extra_len);
492 buf_len = conn->extra_len;
493 conn->extra_len = 0;
494 free(conn->extra_buf);
495 conn->extra_buf = NULL;
496 }else {
497 if ((buf_len = sock_recv( conn->socket, conn->ssl_read_buf + conn->extra_len, ssl_buf_size - conn->extra_len, 0)) < 0)
498 return WSAGetLastError();
500 if (!buf_len)
502 *eof = TRUE;
503 return ERROR_SUCCESS;
507 *ret_size = 0;
508 *eof = FALSE;
510 do {
511 memset(bufs, 0, sizeof(bufs));
512 bufs[0].BufferType = SECBUFFER_DATA;
513 bufs[0].cbBuffer = buf_len;
514 bufs[0].pvBuffer = conn->ssl_read_buf;
516 switch ((res = DecryptMessage( &conn->ssl_ctx, &buf_desc, 0, NULL )))
518 case SEC_E_OK:
519 break;
521 case SEC_I_RENEGOTIATE:
522 TRACE("renegotiate\n");
523 return ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED;
525 case SEC_I_CONTEXT_EXPIRED:
526 TRACE("context expired\n");
527 *eof = TRUE;
528 return ERROR_SUCCESS;
530 case SEC_E_INCOMPLETE_MESSAGE:
531 assert(buf_len < ssl_buf_size);
533 if ((size = sock_recv( conn->socket, conn->ssl_read_buf + buf_len, ssl_buf_size - buf_len, 0 )) < 1)
534 return SEC_E_INCOMPLETE_MESSAGE;
536 buf_len += size;
537 continue;
539 default:
540 WARN( "failed: %#lx\n", res );
541 return res;
543 } while (res != SEC_E_OK);
545 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
546 if(bufs[i].BufferType == SECBUFFER_DATA) {
547 size = min(buf_size, bufs[i].cbBuffer);
548 memcpy(buf, bufs[i].pvBuffer, size);
549 if(size < bufs[i].cbBuffer) {
550 assert(!conn->peek_len);
551 conn->peek_msg_mem = conn->peek_msg = malloc(bufs[i].cbBuffer - size);
552 if(!conn->peek_msg)
553 return ERROR_OUTOFMEMORY;
554 conn->peek_len = bufs[i].cbBuffer-size;
555 memcpy(conn->peek_msg, (char*)bufs[i].pvBuffer+size, conn->peek_len);
558 *ret_size = size;
562 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
563 if(bufs[i].BufferType == SECBUFFER_EXTRA) {
564 conn->extra_buf = malloc(bufs[i].cbBuffer);
565 if(!conn->extra_buf)
566 return ERROR_OUTOFMEMORY;
568 conn->extra_len = bufs[i].cbBuffer;
569 memcpy(conn->extra_buf, bufs[i].pvBuffer, conn->extra_len);
573 return ERROR_SUCCESS;
576 DWORD netconn_recv( struct netconn *conn, void *buf, size_t len, int flags, int *recvd )
578 *recvd = 0;
579 if (!len) return ERROR_SUCCESS;
581 if (conn->secure)
583 SIZE_T size;
584 DWORD res;
585 BOOL eof;
587 if (conn->peek_msg)
589 *recvd = min( len, conn->peek_len );
590 memcpy( buf, conn->peek_msg, *recvd );
591 conn->peek_len -= *recvd;
592 conn->peek_msg += *recvd;
594 if (conn->peek_len == 0)
596 free( conn->peek_msg_mem );
597 conn->peek_msg_mem = NULL;
598 conn->peek_msg = NULL;
600 /* check if we have enough data from the peek buffer */
601 if (!(flags & MSG_WAITALL) || *recvd == len) return ERROR_SUCCESS;
603 size = *recvd;
607 SIZE_T cread = 0;
608 if ((res = read_ssl_chunk( conn, (BYTE *)buf + size, len - size, &cread, &eof )))
610 WARN( "read_ssl_chunk failed: %lu\n", res );
611 if (!size) return res;
612 break;
614 if (eof)
616 TRACE("EOF\n");
617 break;
619 size += cread;
621 } while (!size || ((flags & MSG_WAITALL) && size < len));
623 TRACE( "received %Iu bytes\n", size );
624 *recvd = size;
625 return ERROR_SUCCESS;
628 if ((*recvd = sock_recv( conn->socket, buf, len, flags )) < 0) return WSAGetLastError();
629 return ERROR_SUCCESS;
632 ULONG netconn_query_data_available( struct netconn *conn )
634 return conn->secure ? conn->peek_len : 0;
637 DWORD netconn_set_timeout( struct netconn *netconn, BOOL send, int value )
639 int opt = send ? SO_SNDTIMEO : SO_RCVTIMEO;
640 if (setsockopt( netconn->socket, SOL_SOCKET, opt, (void *)&value, sizeof(value) ) == -1)
642 DWORD err = WSAGetLastError();
643 WARN( "setsockopt failed (%lu)\n", err );
644 return err;
646 return ERROR_SUCCESS;
649 BOOL netconn_is_alive( struct netconn *netconn )
651 SIZE_T size;
652 int len;
653 char b;
654 DWORD err;
655 BOOL eof;
657 set_blocking( netconn, FALSE );
658 if (netconn->secure)
660 while (!netconn->peek_msg && !(err = read_ssl_chunk( netconn, NULL, 0, &size, &eof )) && !eof)
663 TRACE( "checking secure connection, err %lu\n", err );
665 if (netconn->peek_msg || err == WSAEWOULDBLOCK)
667 set_blocking( netconn, TRUE );
668 return TRUE;
670 if (err != SEC_E_OK && err != SEC_E_INCOMPLETE_MESSAGE)
672 set_blocking( netconn, TRUE );
673 return FALSE;
676 len = sock_recv( netconn->socket, &b, 1, MSG_PEEK );
677 err = WSAGetLastError();
678 set_blocking( netconn, TRUE );
680 return len == 1 || (len == -1 && err == WSAEWOULDBLOCK);
683 static DWORD resolve_hostname( const WCHAR *name, INTERNET_PORT port, struct sockaddr_storage *sa )
685 ADDRINFOW *res, hints;
686 int ret;
688 memset( &hints, 0, sizeof(hints) );
689 /* Prefer IPv4 to IPv6 addresses, since some web servers do not listen on
690 * their IPv6 addresses even though they have IPv6 addresses in the DNS.
692 hints.ai_family = AF_INET;
694 ret = GetAddrInfoW( name, NULL, &hints, &res );
695 if (ret != 0)
697 TRACE("failed to get IPv4 address of %s, retrying with IPv6\n", debugstr_w(name));
698 hints.ai_family = AF_INET6;
699 ret = GetAddrInfoW( name, NULL, &hints, &res );
700 if (ret != 0)
702 TRACE("failed to get address of %s\n", debugstr_w(name));
703 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
706 memcpy( sa, res->ai_addr, res->ai_addrlen );
707 switch (res->ai_family)
709 case AF_INET:
710 ((struct sockaddr_in *)sa)->sin_port = htons( port );
711 break;
712 case AF_INET6:
713 ((struct sockaddr_in6 *)sa)->sin6_port = htons( port );
714 break;
717 FreeAddrInfoW( res );
718 return ERROR_SUCCESS;
721 struct async_resolve
723 const WCHAR *hostname;
724 INTERNET_PORT port;
725 struct sockaddr_storage *addr;
726 DWORD result;
727 HANDLE done;
730 static void CALLBACK resolve_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
732 struct async_resolve *async = ctx;
733 async->result = resolve_hostname( async->hostname, async->port, async->addr );
734 SetEvent( async->done );
737 DWORD netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr_storage *addr, int timeout )
739 DWORD ret;
741 if (!timeout) ret = resolve_hostname( hostname, port, addr );
742 else
744 struct async_resolve async;
746 async.hostname = hostname;
747 async.port = port;
748 async.addr = addr;
749 if (!(async.done = CreateEventW( NULL, FALSE, FALSE, NULL ))) return GetLastError();
750 if (!TrySubmitThreadpoolCallback( resolve_proc, &async, NULL ))
752 CloseHandle( async.done );
753 return GetLastError();
755 if (WaitForSingleObject( async.done, timeout ) != WAIT_OBJECT_0) ret = ERROR_WINHTTP_TIMEOUT;
756 else ret = async.result;
757 CloseHandle( async.done );
760 return ret;
763 const void *netconn_get_certificate( struct netconn *conn )
765 const CERT_CONTEXT *ret;
766 SECURITY_STATUS res;
768 if (!conn->secure) return NULL;
769 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&ret);
770 return res == SEC_E_OK ? ret : NULL;
773 int netconn_get_cipher_strength( struct netconn *conn )
775 SecPkgContext_ConnectionInfo conn_info;
776 SECURITY_STATUS res;
778 if (!conn->secure) return 0;
779 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_CONNECTION_INFO, (void*)&conn_info);
780 if(res != SEC_E_OK)
781 WARN( "QueryContextAttributesW failed: %#lx\n", res );
782 return res == SEC_E_OK ? conn_info.dwCipherStrength : 0;