winhttp: Store the server certificate context in the request.
[wine.git] / dlls / winhttp / net.c
blobd577a64783185a3932c63764d80cea574a48f1e2
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 "config.h"
21 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #include <assert.h>
28 #include <sys/types.h>
29 #ifdef HAVE_SYS_SOCKET_H
30 # include <sys/socket.h>
31 #endif
32 #ifdef HAVE_SYS_IOCTL_H
33 # include <sys/ioctl.h>
34 #endif
35 #ifdef HAVE_SYS_FILIO_H
36 # include <sys/filio.h>
37 #endif
38 #ifdef HAVE_POLL_H
39 # include <poll.h>
40 #endif
42 #define NONAMELESSUNION
44 #include "wine/debug.h"
45 #include "wine/library.h"
47 #include "windef.h"
48 #include "winbase.h"
49 #include "winhttp.h"
50 #include "schannel.h"
52 #include "winhttp_private.h"
54 /* to avoid conflicts with the Unix socket headers */
55 #define USE_WS_PREFIX
56 #include "winsock2.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
60 #ifndef HAVE_GETADDRINFO
62 /* critical section to protect non-reentrant gethostbyname() */
63 static CRITICAL_SECTION cs_gethostbyname;
64 static CRITICAL_SECTION_DEBUG critsect_debug =
66 0, 0, &cs_gethostbyname,
67 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
68 0, 0, { (DWORD_PTR)(__FILE__ ": cs_gethostbyname") }
70 static CRITICAL_SECTION cs_gethostbyname = { &critsect_debug, -1, 0, 0, 0, 0 };
72 #endif
74 /* translate a unix error code into a winsock error code */
75 static int sock_get_error( int err )
77 #if !defined(__MINGW32__) && !defined (_MSC_VER)
78 switch (err)
80 case EINTR: return WSAEINTR;
81 case EBADF: return WSAEBADF;
82 case EPERM:
83 case EACCES: return WSAEACCES;
84 case EFAULT: return WSAEFAULT;
85 case EINVAL: return WSAEINVAL;
86 case EMFILE: return WSAEMFILE;
87 case EWOULDBLOCK: return WSAEWOULDBLOCK;
88 case EINPROGRESS: return WSAEINPROGRESS;
89 case EALREADY: return WSAEALREADY;
90 case ENOTSOCK: return WSAENOTSOCK;
91 case EDESTADDRREQ: return WSAEDESTADDRREQ;
92 case EMSGSIZE: return WSAEMSGSIZE;
93 case EPROTOTYPE: return WSAEPROTOTYPE;
94 case ENOPROTOOPT: return WSAENOPROTOOPT;
95 case EPROTONOSUPPORT: return WSAEPROTONOSUPPORT;
96 case ESOCKTNOSUPPORT: return WSAESOCKTNOSUPPORT;
97 case EOPNOTSUPP: return WSAEOPNOTSUPP;
98 case EPFNOSUPPORT: return WSAEPFNOSUPPORT;
99 case EAFNOSUPPORT: return WSAEAFNOSUPPORT;
100 case EADDRINUSE: return WSAEADDRINUSE;
101 case EADDRNOTAVAIL: return WSAEADDRNOTAVAIL;
102 case ENETDOWN: return WSAENETDOWN;
103 case ENETUNREACH: return WSAENETUNREACH;
104 case ENETRESET: return WSAENETRESET;
105 case ECONNABORTED: return WSAECONNABORTED;
106 case EPIPE:
107 case ECONNRESET: return WSAECONNRESET;
108 case ENOBUFS: return WSAENOBUFS;
109 case EISCONN: return WSAEISCONN;
110 case ENOTCONN: return WSAENOTCONN;
111 case ESHUTDOWN: return WSAESHUTDOWN;
112 case ETOOMANYREFS: return WSAETOOMANYREFS;
113 case ETIMEDOUT: return WSAETIMEDOUT;
114 case ECONNREFUSED: return WSAECONNREFUSED;
115 case ELOOP: return WSAELOOP;
116 case ENAMETOOLONG: return WSAENAMETOOLONG;
117 case EHOSTDOWN: return WSAEHOSTDOWN;
118 case EHOSTUNREACH: return WSAEHOSTUNREACH;
119 case ENOTEMPTY: return WSAENOTEMPTY;
120 #ifdef EPROCLIM
121 case EPROCLIM: return WSAEPROCLIM;
122 #endif
123 #ifdef EUSERS
124 case EUSERS: return WSAEUSERS;
125 #endif
126 #ifdef EDQUOT
127 case EDQUOT: return WSAEDQUOT;
128 #endif
129 #ifdef ESTALE
130 case ESTALE: return WSAESTALE;
131 #endif
132 #ifdef EREMOTE
133 case EREMOTE: return WSAEREMOTE;
134 #endif
135 default: errno = err; perror( "sock_set_error" ); return WSAEFAULT;
137 #endif
138 return err;
141 static int sock_send(int fd, const void *msg, size_t len, int flags)
143 int ret;
146 if ((ret = send(fd, msg, len, flags)) == -1) WARN("send error %s\n", strerror(errno));
148 while(ret == -1 && errno == EINTR);
149 return ret;
152 static int sock_recv(int fd, void *msg, size_t len, int flags)
154 int ret;
157 if ((ret = recv(fd, msg, len, flags)) == -1) WARN("recv error %s\n", strerror(errno));
159 while(ret == -1 && errno == EINTR);
160 return ret;
163 static DWORD netconn_verify_cert( PCCERT_CONTEXT cert, WCHAR *server, DWORD security_flags )
165 HCERTSTORE store = cert->hCertStore;
166 BOOL ret;
167 CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
168 PCCERT_CHAIN_CONTEXT chain;
169 char oid_server_auth[] = szOID_PKIX_KP_SERVER_AUTH;
170 char *server_auth[] = { oid_server_auth };
171 DWORD err = ERROR_SUCCESS;
173 TRACE("verifying %s\n", debugstr_w( server ));
174 chainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
175 chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = server_auth;
176 if ((ret = CertGetCertificateChain( NULL, cert, NULL, store, &chainPara,
177 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
178 NULL, &chain )))
180 if (chain->TrustStatus.dwErrorStatus)
182 static const DWORD supportedErrors =
183 CERT_TRUST_IS_NOT_TIME_VALID |
184 CERT_TRUST_IS_UNTRUSTED_ROOT |
185 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
187 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID)
189 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID))
190 err = ERROR_WINHTTP_SECURE_CERT_DATE_INVALID;
192 else if (chain->TrustStatus.dwErrorStatus &
193 CERT_TRUST_IS_UNTRUSTED_ROOT)
195 if (!(security_flags & SECURITY_FLAG_IGNORE_UNKNOWN_CA))
196 err = ERROR_WINHTTP_SECURE_INVALID_CA;
198 else if ((chain->TrustStatus.dwErrorStatus &
199 CERT_TRUST_IS_OFFLINE_REVOCATION) ||
200 (chain->TrustStatus.dwErrorStatus &
201 CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
202 err = ERROR_WINHTTP_SECURE_CERT_REV_FAILED;
203 else if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)
204 err = ERROR_WINHTTP_SECURE_CERT_REVOKED;
205 else if (chain->TrustStatus.dwErrorStatus &
206 CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
208 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE))
209 err = ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE;
211 else if (chain->TrustStatus.dwErrorStatus & ~supportedErrors)
212 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
214 if (!err)
216 CERT_CHAIN_POLICY_PARA policyPara;
217 SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
218 CERT_CHAIN_POLICY_STATUS policyStatus;
219 CERT_CHAIN_CONTEXT chainCopy;
221 /* Clear chain->TrustStatus.dwErrorStatus so
222 * CertVerifyCertificateChainPolicy will verify additional checks
223 * rather than stopping with an existing, ignored error.
225 memcpy(&chainCopy, chain, sizeof(chainCopy));
226 chainCopy.TrustStatus.dwErrorStatus = 0;
227 sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara);
228 sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER;
229 sslExtraPolicyPara.pwszServerName = server;
230 sslExtraPolicyPara.fdwChecks = security_flags;
231 policyPara.cbSize = sizeof(policyPara);
232 policyPara.dwFlags = 0;
233 policyPara.pvExtraPolicyPara = &sslExtraPolicyPara;
234 ret = CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL,
235 &chainCopy, &policyPara,
236 &policyStatus );
237 /* Any error in the policy status indicates that the
238 * policy couldn't be verified.
240 if (ret && policyStatus.dwError)
242 if (policyStatus.dwError == CERT_E_CN_NO_MATCH)
243 err = ERROR_WINHTTP_SECURE_CERT_CN_INVALID;
244 else
245 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
248 CertFreeCertificateChain( chain );
250 else
251 err = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
252 TRACE("returning %08x\n", err);
253 return err;
256 void netconn_unload( void )
258 #ifndef HAVE_GETADDRINFO
259 DeleteCriticalSection(&cs_gethostbyname);
260 #endif
263 netconn_t *netconn_create( hostdata_t *host, const struct sockaddr_storage *sockaddr, int timeout )
265 netconn_t *conn;
266 unsigned int addr_len;
267 BOOL ret = FALSE;
268 int res;
269 ULONG state;
271 conn = heap_alloc_zero(sizeof(*conn));
272 if (!conn) return NULL;
273 conn->host = host;
274 conn->sockaddr = *sockaddr;
275 if ((conn->socket = socket( sockaddr->ss_family, SOCK_STREAM, 0 )) == -1)
277 WARN("unable to create socket (%s)\n", strerror(errno));
278 set_last_error( sock_get_error( errno ) );
279 heap_free(conn);
280 return NULL;
283 switch (conn->sockaddr.ss_family)
285 case AF_INET:
286 addr_len = sizeof(struct sockaddr_in);
287 break;
288 case AF_INET6:
289 addr_len = sizeof(struct sockaddr_in6);
290 break;
291 default:
292 assert(0);
295 if (timeout > 0)
297 state = 1;
298 ioctlsocket( conn->socket, FIONBIO, &state );
301 for (;;)
303 res = 0;
304 if (connect( conn->socket, (const struct sockaddr *)&conn->sockaddr, addr_len ) < 0)
306 res = sock_get_error( errno );
307 if (res == WSAEWOULDBLOCK || res == WSAEINPROGRESS)
309 struct pollfd pfd;
311 pfd.fd = conn->socket;
312 pfd.events = POLLOUT;
313 for (;;)
315 res = 0;
316 if (poll( &pfd, 1, timeout ) > 0)
318 ret = TRUE;
319 break;
321 else
323 res = sock_get_error( errno );
324 if (res != WSAEINTR) break;
328 if (res != WSAEINTR) break;
330 else
332 ret = TRUE;
333 break;
336 if (timeout > 0)
338 state = 0;
339 ioctlsocket( conn->socket, FIONBIO, &state );
341 if (!ret)
343 WARN("unable to connect to host (%d)\n", res);
344 set_last_error( res );
345 netconn_close( conn );
346 return NULL;
348 return conn;
351 BOOL netconn_close( netconn_t *conn )
353 int res;
355 if (conn->secure)
357 heap_free( conn->peek_msg_mem );
358 heap_free(conn->ssl_buf);
359 heap_free(conn->extra_buf);
360 DeleteSecurityContext(&conn->ssl_ctx);
362 res = closesocket( conn->socket );
363 release_host( conn->host );
364 heap_free(conn);
365 if (res == -1)
367 set_last_error( sock_get_error( errno ) );
368 return FALSE;
370 return TRUE;
373 BOOL netconn_secure_connect( netconn_t *conn, WCHAR *hostname, DWORD security_flags, CredHandle *cred_handle )
375 SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}};
376 SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs};
377 BYTE *read_buf;
378 SIZE_T read_buf_size = 2048;
379 ULONG attrs = 0;
380 CtxtHandle ctx;
381 SSIZE_T size;
382 const CERT_CONTEXT *cert;
383 SECURITY_STATUS status;
384 DWORD res = ERROR_SUCCESS;
386 const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY
387 |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION;
389 read_buf = heap_alloc(read_buf_size);
390 if(!read_buf)
391 return FALSE;
393 status = InitializeSecurityContextW(cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0,
394 &ctx, &out_desc, &attrs, NULL);
396 assert(status != SEC_E_OK);
398 while(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) {
399 if(out_buf.cbBuffer) {
400 assert(status == SEC_I_CONTINUE_NEEDED);
402 TRACE("sending %u bytes\n", out_buf.cbBuffer);
404 size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, 0);
405 if(size != out_buf.cbBuffer) {
406 ERR("send failed\n");
407 res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
408 break;
411 FreeContextBuffer(out_buf.pvBuffer);
412 out_buf.pvBuffer = NULL;
413 out_buf.cbBuffer = 0;
416 if(status == SEC_I_CONTINUE_NEEDED) {
417 assert(in_bufs[1].cbBuffer < read_buf_size);
419 memmove(read_buf, (BYTE*)in_bufs[0].pvBuffer+in_bufs[0].cbBuffer-in_bufs[1].cbBuffer, in_bufs[1].cbBuffer);
420 in_bufs[0].cbBuffer = in_bufs[1].cbBuffer;
422 in_bufs[1].BufferType = SECBUFFER_EMPTY;
423 in_bufs[1].cbBuffer = 0;
424 in_bufs[1].pvBuffer = NULL;
427 assert(in_bufs[0].BufferType == SECBUFFER_TOKEN);
428 assert(in_bufs[1].BufferType == SECBUFFER_EMPTY);
430 if(in_bufs[0].cbBuffer + 1024 > read_buf_size) {
431 BYTE *new_read_buf;
433 new_read_buf = heap_realloc(read_buf, read_buf_size + 1024);
434 if(!new_read_buf) {
435 status = E_OUTOFMEMORY;
436 break;
439 in_bufs[0].pvBuffer = read_buf = new_read_buf;
440 read_buf_size += 1024;
443 size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0);
444 if(size < 1) {
445 status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
446 break;
449 TRACE("recv %lu bytes\n", size);
451 in_bufs[0].cbBuffer += size;
452 in_bufs[0].pvBuffer = read_buf;
453 status = InitializeSecurityContextW(cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc,
454 0, NULL, &out_desc, &attrs, NULL);
455 TRACE("InitializeSecurityContext ret %08x\n", status);
457 if(status == SEC_E_OK) {
458 if(in_bufs[1].BufferType == SECBUFFER_EXTRA)
459 FIXME("SECBUFFER_EXTRA not supported\n");
461 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes);
462 if(status != SEC_E_OK) {
463 WARN("Could not get sizes\n");
464 break;
467 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert);
468 if(status == SEC_E_OK) {
469 res = netconn_verify_cert(cert, hostname, security_flags);
470 CertFreeCertificateContext(cert);
471 if(res != ERROR_SUCCESS) {
472 WARN("cert verify failed: %u\n", res);
473 break;
475 }else {
476 WARN("Could not get cert\n");
477 break;
480 conn->ssl_buf = heap_alloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
481 if(!conn->ssl_buf) {
482 res = GetLastError();
483 break;
488 heap_free(read_buf);
490 if(status != SEC_E_OK || res != ERROR_SUCCESS) {
491 WARN("Failed to initialize security context failed: %08x\n", status);
492 heap_free(conn->ssl_buf);
493 conn->ssl_buf = NULL;
494 DeleteSecurityContext(&ctx);
495 set_last_error(res ? res : ERROR_WINHTTP_SECURE_CHANNEL_ERROR);
496 return FALSE;
500 TRACE("established SSL connection\n");
501 conn->secure = TRUE;
502 conn->ssl_ctx = ctx;
503 return TRUE;
506 static BOOL send_ssl_chunk(netconn_t *conn, const void *msg, size_t size)
508 SecBuffer bufs[4] = {
509 {conn->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, conn->ssl_buf},
510 {size, SECBUFFER_DATA, conn->ssl_buf+conn->ssl_sizes.cbHeader},
511 {conn->ssl_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, conn->ssl_buf+conn->ssl_sizes.cbHeader+size},
512 {0, SECBUFFER_EMPTY, NULL}
514 SecBufferDesc buf_desc = {SECBUFFER_VERSION, sizeof(bufs)/sizeof(*bufs), bufs};
515 SECURITY_STATUS res;
517 memcpy(bufs[1].pvBuffer, msg, size);
518 res = EncryptMessage(&conn->ssl_ctx, 0, &buf_desc, 0);
519 if(res != SEC_E_OK) {
520 WARN("EncryptMessage failed\n");
521 return FALSE;
524 if(sock_send(conn->socket, conn->ssl_buf, bufs[0].cbBuffer+bufs[1].cbBuffer+bufs[2].cbBuffer, 0) < 1) {
525 WARN("send failed\n");
526 return FALSE;
529 return TRUE;
532 BOOL netconn_send( netconn_t *conn, const void *msg, size_t len, int *sent )
534 if (conn->secure)
536 const BYTE *ptr = msg;
537 size_t chunk_size;
539 *sent = 0;
541 while(len) {
542 chunk_size = min(len, conn->ssl_sizes.cbMaximumMessage);
543 if(!send_ssl_chunk(conn, ptr, chunk_size))
544 return FALSE;
546 *sent += chunk_size;
547 ptr += chunk_size;
548 len -= chunk_size;
551 return TRUE;
553 if ((*sent = sock_send( conn->socket, msg, len, 0 )) == -1)
555 set_last_error( sock_get_error( errno ) );
556 return FALSE;
558 return TRUE;
561 static BOOL read_ssl_chunk(netconn_t *conn, void *buf, SIZE_T buf_size, SIZE_T *ret_size, BOOL *eof)
563 const SIZE_T ssl_buf_size = conn->ssl_sizes.cbHeader+conn->ssl_sizes.cbMaximumMessage+conn->ssl_sizes.cbTrailer;
564 SecBuffer bufs[4];
565 SecBufferDesc buf_desc = {SECBUFFER_VERSION, sizeof(bufs)/sizeof(*bufs), bufs};
566 SSIZE_T size, buf_len;
567 unsigned int i;
568 SECURITY_STATUS res;
570 assert(conn->extra_len < ssl_buf_size);
572 if(conn->extra_len) {
573 memcpy(conn->ssl_buf, conn->extra_buf, conn->extra_len);
574 buf_len = conn->extra_len;
575 conn->extra_len = 0;
576 heap_free(conn->extra_buf);
577 conn->extra_buf = NULL;
578 }else {
579 buf_len = sock_recv(conn->socket, conn->ssl_buf+conn->extra_len, ssl_buf_size-conn->extra_len, 0);
580 if(buf_len < 0)
581 return FALSE;
583 if(!buf_len) {
584 *eof = TRUE;
585 return TRUE;
589 *ret_size = 0;
590 *eof = FALSE;
592 do {
593 memset(bufs, 0, sizeof(bufs));
594 bufs[0].BufferType = SECBUFFER_DATA;
595 bufs[0].cbBuffer = buf_len;
596 bufs[0].pvBuffer = conn->ssl_buf;
598 res = DecryptMessage(&conn->ssl_ctx, &buf_desc, 0, NULL);
599 switch(res) {
600 case SEC_E_OK:
601 break;
602 case SEC_I_CONTEXT_EXPIRED:
603 TRACE("context expired\n");
604 *eof = TRUE;
605 return TRUE;
606 case SEC_E_INCOMPLETE_MESSAGE:
607 assert(buf_len < ssl_buf_size);
609 size = sock_recv(conn->socket, conn->ssl_buf+buf_len, ssl_buf_size-buf_len, 0);
610 if(size < 1)
611 return FALSE;
613 buf_len += size;
614 continue;
615 default:
616 WARN("failed: %08x\n", res);
617 return FALSE;
619 } while(res != SEC_E_OK);
621 for(i=0; i < sizeof(bufs)/sizeof(*bufs); i++) {
622 if(bufs[i].BufferType == SECBUFFER_DATA) {
623 size = min(buf_size, bufs[i].cbBuffer);
624 memcpy(buf, bufs[i].pvBuffer, size);
625 if(size < bufs[i].cbBuffer) {
626 assert(!conn->peek_len);
627 conn->peek_msg_mem = conn->peek_msg = heap_alloc(bufs[i].cbBuffer - size);
628 if(!conn->peek_msg)
629 return FALSE;
630 conn->peek_len = bufs[i].cbBuffer-size;
631 memcpy(conn->peek_msg, (char*)bufs[i].pvBuffer+size, conn->peek_len);
634 *ret_size = size;
638 for(i=0; i < sizeof(bufs)/sizeof(*bufs); i++) {
639 if(bufs[i].BufferType == SECBUFFER_EXTRA) {
640 conn->extra_buf = heap_alloc(bufs[i].cbBuffer);
641 if(!conn->extra_buf)
642 return FALSE;
644 conn->extra_len = bufs[i].cbBuffer;
645 memcpy(conn->extra_buf, bufs[i].pvBuffer, conn->extra_len);
649 return TRUE;
652 BOOL netconn_recv( netconn_t *conn, void *buf, size_t len, int flags, int *recvd )
654 *recvd = 0;
655 if (!len) return TRUE;
657 if (conn->secure)
659 SIZE_T size, cread;
660 BOOL res, eof;
662 if (conn->peek_msg)
664 *recvd = min( len, conn->peek_len );
665 memcpy( buf, conn->peek_msg, *recvd );
666 conn->peek_len -= *recvd;
667 conn->peek_msg += *recvd;
669 if (conn->peek_len == 0)
671 heap_free( conn->peek_msg_mem );
672 conn->peek_msg_mem = NULL;
673 conn->peek_msg = NULL;
675 /* check if we have enough data from the peek buffer */
676 if (!(flags & MSG_WAITALL) || *recvd == len) return TRUE;
678 size = *recvd;
680 do {
681 res = read_ssl_chunk(conn, (BYTE*)buf+size, len-size, &cread, &eof);
682 if(!res) {
683 WARN("read_ssl_chunk failed\n");
684 if(!size)
685 return FALSE;
686 break;
689 if(eof) {
690 TRACE("EOF\n");
691 break;
694 size += cread;
695 }while(!size || ((flags & MSG_WAITALL) && size < len));
697 TRACE("received %ld bytes\n", size);
698 *recvd = size;
699 return TRUE;
701 if ((*recvd = sock_recv( conn->socket, buf, len, flags )) == -1)
703 set_last_error( sock_get_error( errno ) );
704 return FALSE;
706 return TRUE;
709 ULONG netconn_query_data_available( netconn_t *conn )
711 return conn->secure ? conn->peek_len : 0;
714 DWORD netconn_set_timeout( netconn_t *netconn, BOOL send, int value )
716 struct timeval tv;
718 /* value is in milliseconds, convert to struct timeval */
719 tv.tv_sec = value / 1000;
720 tv.tv_usec = (value % 1000) * 1000;
722 if (setsockopt( netconn->socket, SOL_SOCKET, send ? SO_SNDTIMEO : SO_RCVTIMEO, (void*)&tv, sizeof(tv) ) == -1)
724 WARN("setsockopt failed (%s)\n", strerror( errno ));
725 return sock_get_error( errno );
727 return ERROR_SUCCESS;
730 BOOL netconn_is_alive( netconn_t *netconn )
732 #ifdef MSG_DONTWAIT
733 ssize_t len;
734 BYTE b;
736 len = recv( netconn->socket, &b, 1, MSG_PEEK | MSG_DONTWAIT );
737 return len == 1 || (len == -1 && errno == EWOULDBLOCK);
738 #elif defined(__MINGW32__) || defined(_MSC_VER)
739 ULONG mode;
740 int len;
741 char b;
743 mode = 1;
744 if(!ioctlsocket(netconn->socket, FIONBIO, &mode))
745 return FALSE;
747 len = recv(netconn->socket, &b, 1, MSG_PEEK);
749 mode = 0;
750 if(!ioctlsocket(netconn->socket, FIONBIO, &mode))
751 return FALSE;
753 return len == 1 || (len == -1 && WSAGetLastError() == WSAEWOULDBLOCK);
754 #else
755 FIXME("not supported on this platform\n");
756 return TRUE;
757 #endif
760 static DWORD resolve_hostname( const WCHAR *hostnameW, INTERNET_PORT port, struct sockaddr_storage *sa )
762 char *hostname;
763 #ifdef HAVE_GETADDRINFO
764 struct addrinfo *res, hints;
765 int ret;
766 #else
767 struct hostent *he;
768 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
769 #endif
771 if (!(hostname = strdupWA( hostnameW ))) return ERROR_OUTOFMEMORY;
773 #ifdef HAVE_GETADDRINFO
774 memset( &hints, 0, sizeof(struct addrinfo) );
775 /* Prefer IPv4 to IPv6 addresses, since some web servers do not listen on
776 * their IPv6 addresses even though they have IPv6 addresses in the DNS.
778 hints.ai_family = AF_INET;
780 ret = getaddrinfo( hostname, NULL, &hints, &res );
781 if (ret != 0)
783 TRACE("failed to get IPv4 address of %s (%s), retrying with IPv6\n", debugstr_w(hostnameW), gai_strerror(ret));
784 hints.ai_family = AF_INET6;
785 ret = getaddrinfo( hostname, NULL, &hints, &res );
786 if (ret != 0)
788 TRACE("failed to get address of %s (%s)\n", debugstr_w(hostnameW), gai_strerror(ret));
789 heap_free( hostname );
790 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
793 heap_free( hostname );
794 memcpy( sa, res->ai_addr, res->ai_addrlen );
795 /* Copy port */
796 switch (res->ai_family)
798 case AF_INET:
799 ((struct sockaddr_in *)sa)->sin_port = htons( port );
800 break;
801 case AF_INET6:
802 ((struct sockaddr_in6 *)sa)->sin6_port = htons( port );
803 break;
806 freeaddrinfo( res );
807 return ERROR_SUCCESS;
808 #else
809 EnterCriticalSection( &cs_gethostbyname );
811 he = gethostbyname( hostname );
812 heap_free( hostname );
813 if (!he)
815 TRACE("failed to get address of %s (%d)\n", debugstr_w(hostnameW), h_errno);
816 LeaveCriticalSection( &cs_gethostbyname );
817 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
819 memset( sa, 0, sizeof(struct sockaddr_in) );
820 memcpy( &sin->sin_addr, he->h_addr, he->h_length );
821 sin->sin_family = he->h_addrtype;
822 sin->sin_port = htons( port );
824 LeaveCriticalSection( &cs_gethostbyname );
825 return ERROR_SUCCESS;
826 #endif
829 struct resolve_args
831 const WCHAR *hostname;
832 INTERNET_PORT port;
833 struct sockaddr_storage *sa;
836 static DWORD CALLBACK resolve_proc( LPVOID arg )
838 struct resolve_args *ra = arg;
839 return resolve_hostname( ra->hostname, ra->port, ra->sa );
842 BOOL netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr_storage *sa, int timeout )
844 DWORD ret;
846 if (timeout)
848 DWORD status;
849 HANDLE thread;
850 struct resolve_args ra;
852 ra.hostname = hostname;
853 ra.port = port;
854 ra.sa = sa;
856 thread = CreateThread( NULL, 0, resolve_proc, &ra, 0, NULL );
857 if (!thread) return FALSE;
859 status = WaitForSingleObject( thread, timeout );
860 if (status == WAIT_OBJECT_0) GetExitCodeThread( thread, &ret );
861 else ret = ERROR_WINHTTP_TIMEOUT;
862 CloseHandle( thread );
864 else ret = resolve_hostname( hostname, port, sa );
866 if (ret)
868 set_last_error( ret );
869 return FALSE;
871 return TRUE;
874 const void *netconn_get_certificate( netconn_t *conn )
876 const CERT_CONTEXT *ret;
877 SECURITY_STATUS res;
879 if (!conn->secure) return NULL;
880 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&ret);
881 return res == SEC_E_OK ? ret : NULL;
884 int netconn_get_cipher_strength( netconn_t *conn )
886 SecPkgContext_ConnectionInfo conn_info;
887 SECURITY_STATUS res;
889 if (!conn->secure) return 0;
890 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_CONNECTION_INFO, (void*)&conn_info);
891 if(res != SEC_E_OK)
892 WARN("QueryContextAttributesW failed: %08x\n", res);
893 return res == SEC_E_OK ? conn_info.dwCipherStrength : 0;