bdb778716c91938d9331c3740e1a4bf8fd29ab5e
[reactos.git] / reactos / dll / win32 / winhttp / net.c
1 /*
2 * Copyright 2008 Hans Leidekker for CodeWeavers
3 * Copyright 2013 Jacek Caban for CodeWeavers
4 *
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.
9 *
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.
14 *
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
18 */
19
20 #include "winhttp_private.h"
21
22 #include <assert.h>
23 #include <schannel.h>
24
25 #ifdef HAVE_SYS_SOCKET_H
26 # include <sys/socket.h>
27 #endif
28 #ifdef HAVE_SYS_IOCTL_H
29 # include <sys/ioctl.h>
30 #endif
31 #ifdef HAVE_SYS_FILIO_H
32 # include <sys/filio.h>
33 #endif
34 #ifdef HAVE_POLL_H
35 # include <poll.h>
36 #endif
37
38 #ifndef HAVE_GETADDRINFO
39
40 /* critical section to protect non-reentrant gethostbyname() */
41 static CRITICAL_SECTION cs_gethostbyname;
42 static CRITICAL_SECTION_DEBUG critsect_debug =
43 {
44 0, 0, &cs_gethostbyname,
45 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
46 0, 0, { (DWORD_PTR)(__FILE__ ": cs_gethostbyname") }
47 };
48 static CRITICAL_SECTION cs_gethostbyname = { &critsect_debug, -1, 0, 0, 0, 0 };
49
50 #endif
51
52 /* translate a unix error code into a winsock error code */
53 #ifndef __REACTOS__
54 static int sock_get_error( int err )
55 {
56 #if !defined(__MINGW32__) && !defined (_MSC_VER)
57 switch (err)
58 {
59 case EINTR: return WSAEINTR;
60 case EBADF: return WSAEBADF;
61 case EPERM:
62 case EACCES: return WSAEACCES;
63 case EFAULT: return WSAEFAULT;
64 case EINVAL: return WSAEINVAL;
65 case EMFILE: return WSAEMFILE;
66 case EWOULDBLOCK: return WSAEWOULDBLOCK;
67 case EINPROGRESS: return WSAEINPROGRESS;
68 case EALREADY: return WSAEALREADY;
69 case ENOTSOCK: return WSAENOTSOCK;
70 case EDESTADDRREQ: return WSAEDESTADDRREQ;
71 case EMSGSIZE: return WSAEMSGSIZE;
72 case EPROTOTYPE: return WSAEPROTOTYPE;
73 case ENOPROTOOPT: return WSAENOPROTOOPT;
74 case EPROTONOSUPPORT: return WSAEPROTONOSUPPORT;
75 case ESOCKTNOSUPPORT: return WSAESOCKTNOSUPPORT;
76 case EOPNOTSUPP: return WSAEOPNOTSUPP;
77 case EPFNOSUPPORT: return WSAEPFNOSUPPORT;
78 case EAFNOSUPPORT: return WSAEAFNOSUPPORT;
79 case EADDRINUSE: return WSAEADDRINUSE;
80 case EADDRNOTAVAIL: return WSAEADDRNOTAVAIL;
81 case ENETDOWN: return WSAENETDOWN;
82 case ENETUNREACH: return WSAENETUNREACH;
83 case ENETRESET: return WSAENETRESET;
84 case ECONNABORTED: return WSAECONNABORTED;
85 case EPIPE:
86 case ECONNRESET: return WSAECONNRESET;
87 case ENOBUFS: return WSAENOBUFS;
88 case EISCONN: return WSAEISCONN;
89 case ENOTCONN: return WSAENOTCONN;
90 case ESHUTDOWN: return WSAESHUTDOWN;
91 case ETOOMANYREFS: return WSAETOOMANYREFS;
92 case ETIMEDOUT: return WSAETIMEDOUT;
93 case ECONNREFUSED: return WSAECONNREFUSED;
94 case ELOOP: return WSAELOOP;
95 case ENAMETOOLONG: return WSAENAMETOOLONG;
96 case EHOSTDOWN: return WSAEHOSTDOWN;
97 case EHOSTUNREACH: return WSAEHOSTUNREACH;
98 case ENOTEMPTY: return WSAENOTEMPTY;
99 #ifdef EPROCLIM
100 case EPROCLIM: return WSAEPROCLIM;
101 #endif
102 #ifdef EUSERS
103 case EUSERS: return WSAEUSERS;
104 #endif
105 #ifdef EDQUOT
106 case EDQUOT: return WSAEDQUOT;
107 #endif
108 #ifdef ESTALE
109 case ESTALE: return WSAESTALE;
110 #endif
111 #ifdef EREMOTE
112 case EREMOTE: return WSAEREMOTE;
113 #endif
114 default: errno = err; perror( "sock_set_error" ); return WSAEFAULT;
115 }
116 #endif
117 return err;
118 }
119 #else
120 #define sock_get_error(x) WSAGetLastError()
121
122 static inline int unix_ioctl(int filedes, long request, void *arg)
123 {
124 return ioctlsocket(filedes, request, arg);
125 }
126 #define ioctlsocket unix_ioctl
127 #endif
128
129 static int sock_send(int fd, const void *msg, size_t len, int flags)
130 {
131 int ret;
132 do
133 {
134 ret = send(fd, msg, len, flags);
135 }
136 while(ret == -1 && errno == EINTR);
137 return ret;
138 }
139
140 static int sock_recv(int fd, void *msg, size_t len, int flags)
141 {
142 int ret;
143 do
144 {
145 ret = recv(fd, msg, len, flags);
146 }
147 while(ret == -1 && errno == EINTR);
148 return ret;
149 }
150
151 static DWORD netconn_verify_cert( PCCERT_CONTEXT cert, WCHAR *server, DWORD security_flags )
152 {
153 HCERTSTORE store = cert->hCertStore;
154 BOOL ret;
155 CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
156 PCCERT_CHAIN_CONTEXT chain;
157 char oid_server_auth[] = szOID_PKIX_KP_SERVER_AUTH;
158 char *server_auth[] = { oid_server_auth };
159 DWORD err = ERROR_SUCCESS;
160
161 TRACE("verifying %s\n", debugstr_w( server ));
162 chainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
163 chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = server_auth;
164 if ((ret = CertGetCertificateChain( NULL, cert, NULL, store, &chainPara,
165 CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
166 NULL, &chain )))
167 {
168 if (chain->TrustStatus.dwErrorStatus)
169 {
170 static const DWORD supportedErrors =
171 CERT_TRUST_IS_NOT_TIME_VALID |
172 CERT_TRUST_IS_UNTRUSTED_ROOT |
173 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
174
175 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID)
176 {
177 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID))
178 err = ERROR_WINHTTP_SECURE_CERT_DATE_INVALID;
179 }
180 else if (chain->TrustStatus.dwErrorStatus &
181 CERT_TRUST_IS_UNTRUSTED_ROOT)
182 {
183 if (!(security_flags & SECURITY_FLAG_IGNORE_UNKNOWN_CA))
184 err = ERROR_WINHTTP_SECURE_INVALID_CA;
185 }
186 else if ((chain->TrustStatus.dwErrorStatus &
187 CERT_TRUST_IS_OFFLINE_REVOCATION) ||
188 (chain->TrustStatus.dwErrorStatus &
189 CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
190 err = ERROR_WINHTTP_SECURE_CERT_REV_FAILED;
191 else if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)
192 err = ERROR_WINHTTP_SECURE_CERT_REVOKED;
193 else if (chain->TrustStatus.dwErrorStatus &
194 CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
195 {
196 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE))
197 err = ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE;
198 }
199 else if (chain->TrustStatus.dwErrorStatus & ~supportedErrors)
200 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
201 }
202 if (!err)
203 {
204 CERT_CHAIN_POLICY_PARA policyPara;
205 SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
206 CERT_CHAIN_POLICY_STATUS policyStatus;
207 CERT_CHAIN_CONTEXT chainCopy;
208
209 /* Clear chain->TrustStatus.dwErrorStatus so
210 * CertVerifyCertificateChainPolicy will verify additional checks
211 * rather than stopping with an existing, ignored error.
212 */
213 memcpy(&chainCopy, chain, sizeof(chainCopy));
214 chainCopy.TrustStatus.dwErrorStatus = 0;
215 sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara);
216 sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER;
217 sslExtraPolicyPara.pwszServerName = server;
218 sslExtraPolicyPara.fdwChecks = security_flags;
219 policyPara.cbSize = sizeof(policyPara);
220 policyPara.dwFlags = 0;
221 policyPara.pvExtraPolicyPara = &sslExtraPolicyPara;
222 ret = CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL,
223 &chainCopy, &policyPara,
224 &policyStatus );
225 /* Any error in the policy status indicates that the
226 * policy couldn't be verified.
227 */
228 if (ret && policyStatus.dwError)
229 {
230 if (policyStatus.dwError == CERT_E_CN_NO_MATCH)
231 err = ERROR_WINHTTP_SECURE_CERT_CN_INVALID;
232 else
233 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
234 }
235 }
236 CertFreeCertificateChain( chain );
237 }
238 else
239 err = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
240 TRACE("returning %08x\n", err);
241 return err;
242 }
243
244 static SecHandle cred_handle;
245 static BOOL cred_handle_initialized;
246
247 static CRITICAL_SECTION init_sechandle_cs;
248 static CRITICAL_SECTION_DEBUG init_sechandle_cs_debug = {
249 0, 0, &init_sechandle_cs,
250 { &init_sechandle_cs_debug.ProcessLocksList,
251 &init_sechandle_cs_debug.ProcessLocksList },
252 0, 0, { (DWORD_PTR)(__FILE__ ": init_sechandle_cs") }
253 };
254 static CRITICAL_SECTION init_sechandle_cs = { &init_sechandle_cs_debug, -1, 0, 0, 0, 0 };
255
256 static BOOL ensure_cred_handle(void)
257 {
258 BOOL ret = TRUE;
259
260 EnterCriticalSection(&init_sechandle_cs);
261
262 if(!cred_handle_initialized) {
263 SECURITY_STATUS res;
264
265 res = AcquireCredentialsHandleW(NULL, (WCHAR*)UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, NULL,
266 NULL, NULL, &cred_handle, NULL);
267 if(res == SEC_E_OK) {
268 cred_handle_initialized = TRUE;
269 }else {
270 WARN("AcquireCredentialsHandleW failed: %u\n", res);
271 ret = FALSE;
272 }
273 }
274
275 LeaveCriticalSection(&init_sechandle_cs);
276 return ret;
277 }
278
279 BOOL netconn_init( netconn_t *conn )
280 {
281 memset(conn, 0, sizeof(*conn));
282 conn->socket = -1;
283 return TRUE;
284 }
285
286 void netconn_unload( void )
287 {
288 if(cred_handle_initialized)
289 FreeCredentialsHandle(&cred_handle);
290 DeleteCriticalSection(&init_sechandle_cs);
291 #ifndef HAVE_GETADDRINFO
292 DeleteCriticalSection(&cs_gethostbyname);
293 #endif
294 }
295
296 BOOL netconn_connected( netconn_t *conn )
297 {
298 return (conn->socket != -1);
299 }
300
301 BOOL netconn_create( netconn_t *conn, int domain, int type, int protocol )
302 {
303 if ((conn->socket = socket( domain, type, protocol )) == -1)
304 {
305 WARN("unable to create socket (%s)\n", strerror(errno));
306 set_last_error( sock_get_error( errno ) );
307 return FALSE;
308 }
309 return TRUE;
310 }
311
312 BOOL netconn_close( netconn_t *conn )
313 {
314 int res;
315
316 if (conn->secure)
317 {
318 heap_free( conn->peek_msg_mem );
319 conn->peek_msg_mem = NULL;
320 conn->peek_msg = NULL;
321 conn->peek_len = 0;
322 heap_free(conn->ssl_buf);
323 conn->ssl_buf = NULL;
324 heap_free(conn->extra_buf);
325 conn->extra_buf = NULL;
326 conn->extra_len = 0;
327 DeleteSecurityContext(&conn->ssl_ctx);
328 conn->secure = FALSE;
329 }
330 res = closesocket( conn->socket );
331 conn->socket = -1;
332 if (res == -1)
333 {
334 set_last_error( sock_get_error( errno ) );
335 return FALSE;
336 }
337 return TRUE;
338 }
339
340 BOOL netconn_connect( netconn_t *conn, const struct sockaddr *sockaddr, unsigned int addr_len, int timeout )
341 {
342 BOOL ret = FALSE;
343 int res = 0;
344 ULONG state;
345
346 if (timeout > 0)
347 {
348 state = 1;
349 ioctlsocket( conn->socket, FIONBIO, &state );
350 }
351 if (connect( conn->socket, sockaddr, addr_len ) < 0)
352 {
353 res = sock_get_error( errno );
354 if (res == WSAEWOULDBLOCK || res == WSAEINPROGRESS)
355 {
356 #ifdef __REACTOS__
357 /* ReactOS: use select instead of poll */
358 fd_set outfd;
359 struct timeval tv;
360
361 FD_ZERO(&outfd);
362 FD_SET(conn->socket, &outfd);
363
364 tv.tv_sec = 0;
365 tv.tv_usec = timeout * 1000;
366
367 if (select( 0, NULL, &outfd, NULL, &tv ) > 0)
368 #else
369 struct pollfd pfd;
370
371 pfd.fd = conn->socket;
372 pfd.events = POLLOUT;
373 if (poll( &pfd, 1, timeout ) > 0)
374 #endif
375 ret = TRUE;
376 else
377 res = sock_get_error( errno );
378 }
379 }
380 else
381 ret = TRUE;
382 if (timeout > 0)
383 {
384 state = 0;
385 ioctlsocket( conn->socket, FIONBIO, &state );
386 }
387 if (!ret)
388 {
389 WARN("unable to connect to host (%d)\n", res);
390 set_last_error( res );
391 }
392 return ret;
393 }
394
395 BOOL netconn_secure_connect( netconn_t *conn, WCHAR *hostname )
396 {
397 SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}};
398 SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs};
399 BYTE *read_buf;
400 SIZE_T read_buf_size = 2048;
401 ULONG attrs = 0;
402 CtxtHandle ctx;
403 SSIZE_T size;
404 const CERT_CONTEXT *cert;
405 SECURITY_STATUS status;
406 DWORD res = ERROR_SUCCESS;
407
408 const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY
409 |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION;
410
411 if(!ensure_cred_handle())
412 return FALSE;
413
414 read_buf = heap_alloc(read_buf_size);
415 if(!read_buf)
416 return FALSE;
417
418 status = InitializeSecurityContextW(&cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0,
419 &ctx, &out_desc, &attrs, NULL);
420
421 assert(status != SEC_E_OK);
422
423 while(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) {
424 if(out_buf.cbBuffer) {
425 assert(status == SEC_I_CONTINUE_NEEDED);
426
427 TRACE("sending %u bytes\n", out_buf.cbBuffer);
428
429 size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, 0);
430 if(size != out_buf.cbBuffer) {
431 ERR("send failed\n");
432 res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
433 break;
434 }
435
436 FreeContextBuffer(out_buf.pvBuffer);
437 out_buf.pvBuffer = NULL;
438 out_buf.cbBuffer = 0;
439 }
440
441 if(status == SEC_I_CONTINUE_NEEDED) {
442 assert(in_bufs[1].cbBuffer < read_buf_size);
443
444 memmove(read_buf, (BYTE*)in_bufs[0].pvBuffer+in_bufs[0].cbBuffer-in_bufs[1].cbBuffer, in_bufs[1].cbBuffer);
445 in_bufs[0].cbBuffer = in_bufs[1].cbBuffer;
446
447 in_bufs[1].BufferType = SECBUFFER_EMPTY;
448 in_bufs[1].cbBuffer = 0;
449 in_bufs[1].pvBuffer = NULL;
450 }
451
452 assert(in_bufs[0].BufferType == SECBUFFER_TOKEN);
453 assert(in_bufs[1].BufferType == SECBUFFER_EMPTY);
454
455 if(in_bufs[0].cbBuffer + 1024 > read_buf_size) {
456 BYTE *new_read_buf;
457
458 new_read_buf = heap_realloc(read_buf, read_buf_size + 1024);
459 if(!new_read_buf) {
460 status = E_OUTOFMEMORY;
461 break;
462 }
463
464 in_bufs[0].pvBuffer = read_buf = new_read_buf;
465 read_buf_size += 1024;
466 }
467
468 size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0);
469 if(size < 1) {
470 WARN("recv error\n");
471 status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
472 break;
473 }
474
475 TRACE("recv %lu bytes\n", size);
476
477 in_bufs[0].cbBuffer += size;
478 in_bufs[0].pvBuffer = read_buf;
479 status = InitializeSecurityContextW(&cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc,
480 0, NULL, &out_desc, &attrs, NULL);
481 TRACE("InitializeSecurityContext ret %08x\n", status);
482
483 if(status == SEC_E_OK) {
484 if(in_bufs[1].BufferType == SECBUFFER_EXTRA)
485 FIXME("SECBUFFER_EXTRA not supported\n");
486
487 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes);
488 if(status != SEC_E_OK) {
489 WARN("Could not get sizes\n");
490 break;
491 }
492
493 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert);
494 if(status == SEC_E_OK) {
495 res = netconn_verify_cert(cert, hostname, conn->security_flags);
496 CertFreeCertificateContext(cert);
497 if(res != ERROR_SUCCESS) {
498 WARN("cert verify failed: %u\n", res);
499 break;
500 }
501 }else {
502 WARN("Could not get cert\n");
503 break;
504 }
505
506 conn->ssl_buf = heap_alloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
507 if(!conn->ssl_buf) {
508 res = GetLastError();
509 break;
510 }
511 }
512 }
513
514 heap_free(read_buf);
515
516 if(status != SEC_E_OK || res != ERROR_SUCCESS) {
517 WARN("Failed to initialize security context failed: %08x\n", status);
518 heap_free(conn->ssl_buf);
519 conn->ssl_buf = NULL;
520 DeleteSecurityContext(&ctx);
521 set_last_error(res ? res : ERROR_WINHTTP_SECURE_CHANNEL_ERROR);
522 return FALSE;
523 }
524
525
526 TRACE("established SSL connection\n");
527 conn->secure = TRUE;
528 conn->ssl_ctx = ctx;
529 return TRUE;
530 }
531
532 static BOOL send_ssl_chunk(netconn_t *conn, const void *msg, size_t size)
533 {
534 SecBuffer bufs[4] = {
535 {conn->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, conn->ssl_buf},
536 {size, SECBUFFER_DATA, conn->ssl_buf+conn->ssl_sizes.cbHeader},
537 {conn->ssl_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, conn->ssl_buf+conn->ssl_sizes.cbHeader+size},
538 {0, SECBUFFER_EMPTY, NULL}
539 };
540 SecBufferDesc buf_desc = {SECBUFFER_VERSION, sizeof(bufs)/sizeof(*bufs), bufs};
541 SECURITY_STATUS res;
542
543 memcpy(bufs[1].pvBuffer, msg, size);
544 res = EncryptMessage(&conn->ssl_ctx, 0, &buf_desc, 0);
545 if(res != SEC_E_OK) {
546 WARN("EncryptMessage failed\n");
547 return FALSE;
548 }
549
550 if(sock_send(conn->socket, conn->ssl_buf, bufs[0].cbBuffer+bufs[1].cbBuffer+bufs[2].cbBuffer, 0) < 1) {
551 WARN("send failed\n");
552 return FALSE;
553 }
554
555 return TRUE;
556 }
557
558 BOOL netconn_send( netconn_t *conn, const void *msg, size_t len, int *sent )
559 {
560 if (!netconn_connected( conn )) return FALSE;
561 if (conn->secure)
562 {
563 const BYTE *ptr = msg;
564 size_t chunk_size;
565
566 *sent = 0;
567
568 while(len) {
569 chunk_size = min(len, conn->ssl_sizes.cbMaximumMessage);
570 if(!send_ssl_chunk(conn, ptr, chunk_size))
571 return FALSE;
572
573 *sent += chunk_size;
574 ptr += chunk_size;
575 len -= chunk_size;
576 }
577
578 return TRUE;
579 }
580 if ((*sent = sock_send( conn->socket, msg, len, 0 )) == -1)
581 {
582 set_last_error( sock_get_error( errno ) );
583 return FALSE;
584 }
585 return TRUE;
586 }
587
588 static BOOL read_ssl_chunk(netconn_t *conn, void *buf, SIZE_T buf_size, SIZE_T *ret_size, BOOL *eof)
589 {
590 const SIZE_T ssl_buf_size = conn->ssl_sizes.cbHeader+conn->ssl_sizes.cbMaximumMessage+conn->ssl_sizes.cbTrailer;
591 SecBuffer bufs[4];
592 SecBufferDesc buf_desc = {SECBUFFER_VERSION, sizeof(bufs)/sizeof(*bufs), bufs};
593 SSIZE_T size, buf_len;
594 unsigned int i;
595 SECURITY_STATUS res;
596
597 assert(conn->extra_len < ssl_buf_size);
598
599 if(conn->extra_len) {
600 memcpy(conn->ssl_buf, conn->extra_buf, conn->extra_len);
601 buf_len = conn->extra_len;
602 conn->extra_len = 0;
603 heap_free(conn->extra_buf);
604 conn->extra_buf = NULL;
605 }else {
606 buf_len = sock_recv(conn->socket, conn->ssl_buf+conn->extra_len, ssl_buf_size-conn->extra_len, 0);
607 if(buf_len < 0) {
608 WARN("recv failed\n");
609 return FALSE;
610 }
611
612 if(!buf_len) {
613 *eof = TRUE;
614 return TRUE;
615 }
616 }
617
618 *ret_size = 0;
619 *eof = FALSE;
620
621 do {
622 memset(bufs, 0, sizeof(bufs));
623 bufs[0].BufferType = SECBUFFER_DATA;
624 bufs[0].cbBuffer = buf_len;
625 bufs[0].pvBuffer = conn->ssl_buf;
626
627 res = DecryptMessage(&conn->ssl_ctx, &buf_desc, 0, NULL);
628 switch(res) {
629 case SEC_E_OK:
630 break;
631 case SEC_I_CONTEXT_EXPIRED:
632 TRACE("context expired\n");
633 *eof = TRUE;
634 return TRUE;
635 case SEC_E_INCOMPLETE_MESSAGE:
636 assert(buf_len < ssl_buf_size);
637
638 size = sock_recv(conn->socket, conn->ssl_buf+buf_len, ssl_buf_size-buf_len, 0);
639 if(size < 1)
640 return FALSE;
641
642 buf_len += size;
643 continue;
644 default:
645 WARN("failed: %08x\n", res);
646 return FALSE;
647 }
648 } while(res != SEC_E_OK);
649
650 for(i=0; i < sizeof(bufs)/sizeof(*bufs); i++) {
651 if(bufs[i].BufferType == SECBUFFER_DATA) {
652 size = min(buf_size, bufs[i].cbBuffer);
653 memcpy(buf, bufs[i].pvBuffer, size);
654 if(size < bufs[i].cbBuffer) {
655 assert(!conn->peek_len);
656 conn->peek_msg_mem = conn->peek_msg = heap_alloc(bufs[i].cbBuffer - size);
657 if(!conn->peek_msg)
658 return FALSE;
659 conn->peek_len = bufs[i].cbBuffer-size;
660 memcpy(conn->peek_msg, (char*)bufs[i].pvBuffer+size, conn->peek_len);
661 }
662
663 *ret_size = size;
664 }
665 }
666
667 for(i=0; i < sizeof(bufs)/sizeof(*bufs); i++) {
668 if(bufs[i].BufferType == SECBUFFER_EXTRA) {
669 conn->extra_buf = heap_alloc(bufs[i].cbBuffer);
670 if(!conn->extra_buf)
671 return FALSE;
672
673 conn->extra_len = bufs[i].cbBuffer;
674 memcpy(conn->extra_buf, bufs[i].pvBuffer, conn->extra_len);
675 }
676 }
677
678 return TRUE;
679 }
680
681 BOOL netconn_recv( netconn_t *conn, void *buf, size_t len, int flags, int *recvd )
682 {
683 *recvd = 0;
684 if (!netconn_connected( conn )) return FALSE;
685 if (!len) return TRUE;
686
687 if (conn->secure)
688 {
689 SIZE_T size, cread;
690 BOOL res, eof;
691
692 if (conn->peek_msg)
693 {
694 *recvd = min( len, conn->peek_len );
695 memcpy( buf, conn->peek_msg, *recvd );
696 conn->peek_len -= *recvd;
697 conn->peek_msg += *recvd;
698
699 if (conn->peek_len == 0)
700 {
701 heap_free( conn->peek_msg_mem );
702 conn->peek_msg_mem = NULL;
703 conn->peek_msg = NULL;
704 }
705 /* check if we have enough data from the peek buffer */
706 if (!(flags & MSG_WAITALL) || *recvd == len) return TRUE;
707 }
708 size = *recvd;
709
710 do {
711 res = read_ssl_chunk(conn, (BYTE*)buf+size, len-size, &cread, &eof);
712 if(!res) {
713 WARN("read_ssl_chunk failed\n");
714 if(!size)
715 return FALSE;
716 break;
717 }
718
719 if(eof) {
720 TRACE("EOF\n");
721 break;
722 }
723
724 size += cread;
725 }while(!size || ((flags & MSG_WAITALL) && size < len));
726
727 TRACE("received %ld bytes\n", size);
728 *recvd = size;
729 return TRUE;
730 }
731 if ((*recvd = sock_recv( conn->socket, buf, len, flags )) == -1)
732 {
733 set_last_error( sock_get_error( errno ) );
734 return FALSE;
735 }
736 return TRUE;
737 }
738
739 ULONG netconn_query_data_available( netconn_t *conn )
740 {
741 if(!netconn_connected(conn))
742 return 0;
743
744 if(conn->secure)
745 return conn->peek_len;
746
747 return 0;
748 }
749
750 DWORD netconn_set_timeout( netconn_t *netconn, BOOL send, int value )
751 {
752 struct timeval tv;
753
754 /* value is in milliseconds, convert to struct timeval */
755 tv.tv_sec = value / 1000;
756 tv.tv_usec = (value % 1000) * 1000;
757
758 if (setsockopt( netconn->socket, SOL_SOCKET, send ? SO_SNDTIMEO : SO_RCVTIMEO, (void*)&tv, sizeof(tv) ) == -1)
759 {
760 WARN("setsockopt failed (%s)\n", strerror( errno ));
761 return sock_get_error( errno );
762 }
763 return ERROR_SUCCESS;
764 }
765
766 static DWORD resolve_hostname( const WCHAR *hostnameW, INTERNET_PORT port, struct sockaddr *sa, socklen_t *sa_len )
767 {
768 char *hostname;
769 #ifdef HAVE_GETADDRINFO
770 struct addrinfo *res, hints;
771 int ret;
772 #else
773 struct hostent *he;
774 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
775 #endif
776
777 if (!(hostname = strdupWA( hostnameW ))) return ERROR_OUTOFMEMORY;
778
779 #ifdef HAVE_GETADDRINFO
780 memset( &hints, 0, sizeof(struct addrinfo) );
781 /* Prefer IPv4 to IPv6 addresses, since some web servers do not listen on
782 * their IPv6 addresses even though they have IPv6 addresses in the DNS.
783 */
784 hints.ai_family = AF_INET;
785
786 ret = getaddrinfo( hostname, NULL, &hints, &res );
787 if (ret != 0)
788 {
789 TRACE("failed to get IPv4 address of %s (%s), retrying with IPv6\n", debugstr_w(hostnameW), gai_strerror(ret));
790 hints.ai_family = AF_INET6;
791 ret = getaddrinfo( hostname, NULL, &hints, &res );
792 if (ret != 0)
793 {
794 TRACE("failed to get address of %s (%s)\n", debugstr_w(hostnameW), gai_strerror(ret));
795 heap_free( hostname );
796 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
797 }
798 }
799 heap_free( hostname );
800 if (*sa_len < res->ai_addrlen)
801 {
802 WARN("address too small\n");
803 freeaddrinfo( res );
804 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
805 }
806 *sa_len = res->ai_addrlen;
807 memcpy( sa, res->ai_addr, res->ai_addrlen );
808 /* Copy port */
809 switch (res->ai_family)
810 {
811 case AF_INET:
812 ((struct sockaddr_in *)sa)->sin_port = htons( port );
813 break;
814 case AF_INET6:
815 ((struct sockaddr_in6 *)sa)->sin6_port = htons( port );
816 break;
817 }
818
819 freeaddrinfo( res );
820 return ERROR_SUCCESS;
821 #else
822 EnterCriticalSection( &cs_gethostbyname );
823
824 he = gethostbyname( hostname );
825 heap_free( hostname );
826 if (!he)
827 {
828 TRACE("failed to get address of %s (%d)\n", debugstr_w(hostnameW), h_errno);
829 LeaveCriticalSection( &cs_gethostbyname );
830 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
831 }
832 if (*sa_len < sizeof(struct sockaddr_in))
833 {
834 WARN("address too small\n");
835 LeaveCriticalSection( &cs_gethostbyname );
836 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
837 }
838 *sa_len = sizeof(struct sockaddr_in);
839 memset( sa, 0, sizeof(struct sockaddr_in) );
840 memcpy( &sin->sin_addr, he->h_addr, he->h_length );
841 sin->sin_family = he->h_addrtype;
842 sin->sin_port = htons( port );
843
844 LeaveCriticalSection( &cs_gethostbyname );
845 return ERROR_SUCCESS;
846 #endif
847 }
848
849 struct resolve_args
850 {
851 const WCHAR *hostname;
852 INTERNET_PORT port;
853 struct sockaddr *sa;
854 socklen_t *sa_len;
855 };
856
857 static DWORD CALLBACK resolve_proc( LPVOID arg )
858 {
859 struct resolve_args *ra = arg;
860 return resolve_hostname( ra->hostname, ra->port, ra->sa, ra->sa_len );
861 }
862
863 BOOL netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr *sa, socklen_t *sa_len, int timeout )
864 {
865 DWORD ret;
866
867 if (timeout)
868 {
869 DWORD status;
870 HANDLE thread;
871 struct resolve_args ra;
872
873 ra.hostname = hostname;
874 ra.port = port;
875 ra.sa = sa;
876 ra.sa_len = sa_len;
877
878 thread = CreateThread( NULL, 0, resolve_proc, &ra, 0, NULL );
879 if (!thread) return FALSE;
880
881 status = WaitForSingleObject( thread, timeout );
882 if (status == WAIT_OBJECT_0) GetExitCodeThread( thread, &ret );
883 else ret = ERROR_WINHTTP_TIMEOUT;
884 CloseHandle( thread );
885 }
886 else ret = resolve_hostname( hostname, port, sa, sa_len );
887
888 if (ret)
889 {
890 set_last_error( ret );
891 return FALSE;
892 }
893 return TRUE;
894 }
895
896 const void *netconn_get_certificate( netconn_t *conn )
897 {
898 const CERT_CONTEXT *ret;
899 SECURITY_STATUS res;
900
901 if (!conn->secure) return NULL;
902 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&ret);
903 return res == SEC_E_OK ? ret : NULL;
904 }
905
906 int netconn_get_cipher_strength( netconn_t *conn )
907 {
908 SecPkgContext_ConnectionInfo conn_info;
909 SECURITY_STATUS res;
910
911 if (!conn->secure) return 0;
912 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_CONNECTION_INFO, (void*)&conn_info);
913 if(res != SEC_E_OK)
914 WARN("QueryContextAttributesW failed: %08x\n", res);
915 return res == SEC_E_OK ? conn_info.dwCipherStrength : 0;
916 }