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 "config.h"
21 #define NONAMELESSUNION
22 #include "ws2tcpip.h"
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <assert.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winhttp.h"
30 #include "schannel.h"
31
32 #include "wine/debug.h"
33 #include "wine/library.h"
34 #include "winhttp_private.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
37
sock_send(int fd,const void * msg,size_t len,int flags)38 static int sock_send(int fd, const void *msg, size_t len, int flags)
39 {
40 int ret;
41 do
42 {
43 if ((ret = send(fd, msg, len, flags)) == -1) WARN("send error %u\n", WSAGetLastError());
44 }
45 while(ret == -1 && WSAGetLastError() == WSAEINTR);
46 return ret;
47 }
48
sock_recv(int fd,void * msg,size_t len,int flags)49 static int sock_recv(int fd, void *msg, size_t len, int flags)
50 {
51 int ret;
52 do
53 {
54 if ((ret = recv(fd, msg, len, flags)) == -1) WARN("recv error %u\n", WSAGetLastError());
55 }
56 while(ret == -1 && WSAGetLastError() == WSAEINTR);
57 return ret;
58 }
59
netconn_verify_cert(PCCERT_CONTEXT cert,WCHAR * server,DWORD security_flags,BOOL check_revocation)60 static DWORD netconn_verify_cert( PCCERT_CONTEXT cert, WCHAR *server, DWORD security_flags, BOOL check_revocation )
61 {
62 HCERTSTORE store = cert->hCertStore;
63 BOOL ret;
64 CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
65 PCCERT_CHAIN_CONTEXT chain;
66 char oid_server_auth[] = szOID_PKIX_KP_SERVER_AUTH;
67 char *server_auth[] = { oid_server_auth };
68 DWORD err = ERROR_SUCCESS;
69
70 TRACE("verifying %s\n", debugstr_w( server ));
71 chainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
72 chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = server_auth;
73 ret = CertGetCertificateChain( NULL, cert, NULL, store, &chainPara,
74 check_revocation ? CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT : 0,
75 NULL, &chain );
76 if (ret)
77 {
78 if (chain->TrustStatus.dwErrorStatus)
79 {
80 static const DWORD supportedErrors =
81 CERT_TRUST_IS_NOT_TIME_VALID |
82 CERT_TRUST_IS_UNTRUSTED_ROOT |
83 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
84
85 if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID)
86 {
87 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID))
88 err = ERROR_WINHTTP_SECURE_CERT_DATE_INVALID;
89 }
90 else if (chain->TrustStatus.dwErrorStatus &
91 CERT_TRUST_IS_UNTRUSTED_ROOT)
92 {
93 if (!(security_flags & SECURITY_FLAG_IGNORE_UNKNOWN_CA))
94 err = ERROR_WINHTTP_SECURE_INVALID_CA;
95 }
96 else if ((chain->TrustStatus.dwErrorStatus &
97 CERT_TRUST_IS_OFFLINE_REVOCATION) ||
98 (chain->TrustStatus.dwErrorStatus &
99 CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
100 err = ERROR_WINHTTP_SECURE_CERT_REV_FAILED;
101 else if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)
102 err = ERROR_WINHTTP_SECURE_CERT_REVOKED;
103 else if (chain->TrustStatus.dwErrorStatus &
104 CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
105 {
106 if (!(security_flags & SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE))
107 err = ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE;
108 }
109 else if (chain->TrustStatus.dwErrorStatus & ~supportedErrors)
110 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
111 }
112 if (!err)
113 {
114 CERT_CHAIN_POLICY_PARA policyPara;
115 SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
116 CERT_CHAIN_POLICY_STATUS policyStatus;
117 CERT_CHAIN_CONTEXT chainCopy;
118
119 /* Clear chain->TrustStatus.dwErrorStatus so
120 * CertVerifyCertificateChainPolicy will verify additional checks
121 * rather than stopping with an existing, ignored error.
122 */
123 memcpy(&chainCopy, chain, sizeof(chainCopy));
124 chainCopy.TrustStatus.dwErrorStatus = 0;
125 sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara);
126 sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER;
127 sslExtraPolicyPara.pwszServerName = server;
128 sslExtraPolicyPara.fdwChecks = security_flags;
129 policyPara.cbSize = sizeof(policyPara);
130 policyPara.dwFlags = 0;
131 policyPara.pvExtraPolicyPara = &sslExtraPolicyPara;
132 ret = CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL,
133 &chainCopy, &policyPara,
134 &policyStatus );
135 /* Any error in the policy status indicates that the
136 * policy couldn't be verified.
137 */
138 if (ret && policyStatus.dwError)
139 {
140 if (policyStatus.dwError == CERT_E_CN_NO_MATCH)
141 err = ERROR_WINHTTP_SECURE_CERT_CN_INVALID;
142 else
143 err = ERROR_WINHTTP_SECURE_INVALID_CERT;
144 }
145 }
146 CertFreeCertificateChain( chain );
147 }
148 else
149 err = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
150 TRACE("returning %08x\n", err);
151 return err;
152 }
153
154 static BOOL winsock_loaded;
155
netconn_unload(void)156 void netconn_unload( void )
157 {
158 if (winsock_loaded) WSACleanup();
159 }
160
winsock_startup(INIT_ONCE * once,void * param,void ** ctx)161 static BOOL WINAPI winsock_startup( INIT_ONCE *once, void *param, void **ctx )
162 {
163 int ret;
164 WSADATA data;
165 if (!(ret = WSAStartup( MAKEWORD(1,1), &data ))) winsock_loaded = TRUE;
166 else ERR( "WSAStartup failed: %d\n", ret );
167 return TRUE;
168 }
169
170 #ifdef __REACTOS__
winsock_init(void)171 void winsock_init(void)
172 #else
173 static void winsock_init(void)
174 #endif
175 {
176 static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
177 InitOnceExecuteOnce( &once, winsock_startup, NULL, NULL );
178 }
179
set_blocking(struct netconn * conn,BOOL blocking)180 static void set_blocking( struct netconn *conn, BOOL blocking )
181 {
182 ULONG state = !blocking;
183 ioctlsocket( conn->socket, FIONBIO, &state );
184 }
185
netconn_create(struct hostdata * host,const struct sockaddr_storage * sockaddr,int timeout)186 struct netconn *netconn_create( struct hostdata *host, const struct sockaddr_storage *sockaddr, int timeout )
187 {
188 struct netconn *conn;
189 unsigned int addr_len;
190 BOOL ret = FALSE;
191
192 #ifndef __REACTOS__
193 winsock_init();
194 #endif
195
196 conn = heap_alloc_zero(sizeof(*conn));
197 if (!conn) return NULL;
198 conn->host = host;
199 conn->sockaddr = *sockaddr;
200 if ((conn->socket = socket( sockaddr->ss_family, SOCK_STREAM, 0 )) == -1)
201 {
202 WARN("unable to create socket (%u)\n", WSAGetLastError());
203 heap_free(conn);
204 return NULL;
205 }
206
207 switch (conn->sockaddr.ss_family)
208 {
209 case AF_INET:
210 addr_len = sizeof(struct sockaddr_in);
211 break;
212 case AF_INET6:
213 addr_len = sizeof(struct sockaddr_in6);
214 break;
215 default:
216 assert(0);
217 }
218
219 if (timeout > 0) set_blocking( conn, FALSE );
220
221 if (!connect( conn->socket, (const struct sockaddr *)&conn->sockaddr, addr_len )) ret = TRUE;
222 else
223 {
224 DWORD err = WSAGetLastError();
225 if (err == WSAEWOULDBLOCK || err == WSAEINPROGRESS)
226 {
227 FD_SET set;
228 TIMEVAL timeval = { 0, timeout * 1000 };
229 int res;
230
231 FD_ZERO( &set );
232 FD_SET( conn->socket, &set );
233 if ((res = select( conn->socket + 1, NULL, &set, NULL, &timeval )) > 0) ret = TRUE;
234 else if (!res) SetLastError( ERROR_WINHTTP_TIMEOUT );
235 }
236 }
237
238 if (timeout > 0) set_blocking( conn, TRUE );
239
240 if (!ret)
241 {
242 WARN("unable to connect to host (%u)\n", GetLastError());
243 closesocket( conn->socket );
244 heap_free( conn );
245 return NULL;
246 }
247 return conn;
248 }
249
netconn_close(struct netconn * conn)250 void netconn_close( struct netconn *conn )
251 {
252 if (conn->secure)
253 {
254 heap_free( conn->peek_msg_mem );
255 heap_free(conn->ssl_buf);
256 heap_free(conn->extra_buf);
257 DeleteSecurityContext(&conn->ssl_ctx);
258 }
259 closesocket( conn->socket );
260 release_host( conn->host );
261 heap_free(conn);
262 }
263
netconn_secure_connect(struct netconn * conn,WCHAR * hostname,DWORD security_flags,CredHandle * cred_handle,BOOL check_revocation)264 BOOL netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD security_flags, CredHandle *cred_handle,
265 BOOL check_revocation)
266 {
267 SecBuffer out_buf = {0, SECBUFFER_TOKEN, NULL}, in_bufs[2] = {{0, SECBUFFER_TOKEN}, {0, SECBUFFER_EMPTY}};
268 SecBufferDesc out_desc = {SECBUFFER_VERSION, 1, &out_buf}, in_desc = {SECBUFFER_VERSION, 2, in_bufs};
269 BYTE *read_buf;
270 SIZE_T read_buf_size = 2048;
271 ULONG attrs = 0;
272 CtxtHandle ctx;
273 SSIZE_T size;
274 const CERT_CONTEXT *cert;
275 SECURITY_STATUS status;
276 DWORD res = ERROR_SUCCESS;
277
278 const DWORD isc_req_flags = ISC_REQ_ALLOCATE_MEMORY|ISC_REQ_USE_SESSION_KEY|ISC_REQ_CONFIDENTIALITY
279 |ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_MANUAL_CRED_VALIDATION;
280
281 read_buf = heap_alloc(read_buf_size);
282 if(!read_buf)
283 return FALSE;
284
285 status = InitializeSecurityContextW(cred_handle, NULL, hostname, isc_req_flags, 0, 0, NULL, 0,
286 &ctx, &out_desc, &attrs, NULL);
287
288 assert(status != SEC_E_OK);
289
290 while(status == SEC_I_CONTINUE_NEEDED || status == SEC_E_INCOMPLETE_MESSAGE) {
291 if(out_buf.cbBuffer) {
292 assert(status == SEC_I_CONTINUE_NEEDED);
293
294 TRACE("sending %u bytes\n", out_buf.cbBuffer);
295
296 size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, 0);
297 if(size != out_buf.cbBuffer) {
298 ERR("send failed\n");
299 res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
300 break;
301 }
302
303 FreeContextBuffer(out_buf.pvBuffer);
304 out_buf.pvBuffer = NULL;
305 out_buf.cbBuffer = 0;
306 }
307
308 if(status == SEC_I_CONTINUE_NEEDED) {
309 assert(in_bufs[1].cbBuffer < read_buf_size);
310
311 memmove(read_buf, (BYTE*)in_bufs[0].pvBuffer+in_bufs[0].cbBuffer-in_bufs[1].cbBuffer, in_bufs[1].cbBuffer);
312 in_bufs[0].cbBuffer = in_bufs[1].cbBuffer;
313
314 in_bufs[1].BufferType = SECBUFFER_EMPTY;
315 in_bufs[1].cbBuffer = 0;
316 in_bufs[1].pvBuffer = NULL;
317 }
318
319 assert(in_bufs[0].BufferType == SECBUFFER_TOKEN);
320 assert(in_bufs[1].BufferType == SECBUFFER_EMPTY);
321
322 if(in_bufs[0].cbBuffer + 1024 > read_buf_size) {
323 BYTE *new_read_buf;
324
325 new_read_buf = heap_realloc(read_buf, read_buf_size + 1024);
326 if(!new_read_buf) {
327 status = E_OUTOFMEMORY;
328 break;
329 }
330
331 in_bufs[0].pvBuffer = read_buf = new_read_buf;
332 read_buf_size += 1024;
333 }
334
335 size = sock_recv(conn->socket, read_buf+in_bufs[0].cbBuffer, read_buf_size-in_bufs[0].cbBuffer, 0);
336 if(size < 1) {
337 status = ERROR_WINHTTP_SECURE_CHANNEL_ERROR;
338 break;
339 }
340
341 TRACE("recv %lu bytes\n", size);
342
343 in_bufs[0].cbBuffer += size;
344 in_bufs[0].pvBuffer = read_buf;
345 status = InitializeSecurityContextW(cred_handle, &ctx, hostname, isc_req_flags, 0, 0, &in_desc,
346 0, NULL, &out_desc, &attrs, NULL);
347 TRACE("InitializeSecurityContext ret %08x\n", status);
348
349 if(status == SEC_E_OK) {
350 if(in_bufs[1].BufferType == SECBUFFER_EXTRA)
351 FIXME("SECBUFFER_EXTRA not supported\n");
352
353 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_STREAM_SIZES, &conn->ssl_sizes);
354 if(status != SEC_E_OK) {
355 WARN("Could not get sizes\n");
356 break;
357 }
358
359 status = QueryContextAttributesW(&ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&cert);
360 if(status == SEC_E_OK) {
361 res = netconn_verify_cert(cert, hostname, security_flags, check_revocation);
362 CertFreeCertificateContext(cert);
363 if(res != ERROR_SUCCESS) {
364 WARN("cert verify failed: %u\n", res);
365 break;
366 }
367 }else {
368 WARN("Could not get cert\n");
369 break;
370 }
371
372 conn->ssl_buf = heap_alloc(conn->ssl_sizes.cbHeader + conn->ssl_sizes.cbMaximumMessage + conn->ssl_sizes.cbTrailer);
373 if(!conn->ssl_buf) {
374 res = GetLastError();
375 break;
376 }
377 }
378 }
379
380 heap_free(read_buf);
381
382 if(status != SEC_E_OK || res != ERROR_SUCCESS) {
383 WARN("Failed to initialize security context failed: %08x\n", status);
384 heap_free(conn->ssl_buf);
385 conn->ssl_buf = NULL;
386 DeleteSecurityContext(&ctx);
387 SetLastError(res ? res : ERROR_WINHTTP_SECURE_CHANNEL_ERROR);
388 return FALSE;
389 }
390
391
392 TRACE("established SSL connection\n");
393 conn->secure = TRUE;
394 conn->ssl_ctx = ctx;
395 return TRUE;
396 }
397
send_ssl_chunk(struct netconn * conn,const void * msg,size_t size)398 static BOOL send_ssl_chunk(struct netconn *conn, const void *msg, size_t size)
399 {
400 SecBuffer bufs[4] = {
401 {conn->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, conn->ssl_buf},
402 {size, SECBUFFER_DATA, conn->ssl_buf+conn->ssl_sizes.cbHeader},
403 {conn->ssl_sizes.cbTrailer, SECBUFFER_STREAM_TRAILER, conn->ssl_buf+conn->ssl_sizes.cbHeader+size},
404 {0, SECBUFFER_EMPTY, NULL}
405 };
406 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
407 SECURITY_STATUS res;
408
409 memcpy(bufs[1].pvBuffer, msg, size);
410 res = EncryptMessage(&conn->ssl_ctx, 0, &buf_desc, 0);
411 if(res != SEC_E_OK) {
412 WARN("EncryptMessage failed\n");
413 return FALSE;
414 }
415
416 if(sock_send(conn->socket, conn->ssl_buf, bufs[0].cbBuffer+bufs[1].cbBuffer+bufs[2].cbBuffer, 0) < 1) {
417 WARN("send failed\n");
418 return FALSE;
419 }
420
421 return TRUE;
422 }
423
netconn_send(struct netconn * conn,const void * msg,size_t len,int * sent)424 BOOL netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent )
425 {
426 if (conn->secure)
427 {
428 const BYTE *ptr = msg;
429 size_t chunk_size;
430
431 *sent = 0;
432
433 while(len) {
434 chunk_size = min(len, conn->ssl_sizes.cbMaximumMessage);
435 if(!send_ssl_chunk(conn, ptr, chunk_size))
436 return FALSE;
437
438 *sent += chunk_size;
439 ptr += chunk_size;
440 len -= chunk_size;
441 }
442
443 return TRUE;
444 }
445 return ((*sent = sock_send( conn->socket, msg, len, 0 )) != -1);
446 }
447
read_ssl_chunk(struct netconn * conn,void * buf,SIZE_T buf_size,SIZE_T * ret_size,BOOL * eof)448 static BOOL read_ssl_chunk(struct netconn *conn, void *buf, SIZE_T buf_size, SIZE_T *ret_size, BOOL *eof)
449 {
450 const SIZE_T ssl_buf_size = conn->ssl_sizes.cbHeader+conn->ssl_sizes.cbMaximumMessage+conn->ssl_sizes.cbTrailer;
451 SecBuffer bufs[4];
452 SecBufferDesc buf_desc = {SECBUFFER_VERSION, ARRAY_SIZE(bufs), bufs};
453 SSIZE_T size, buf_len;
454 unsigned int i;
455 SECURITY_STATUS res;
456
457 assert(conn->extra_len < ssl_buf_size);
458
459 if(conn->extra_len) {
460 memcpy(conn->ssl_buf, conn->extra_buf, conn->extra_len);
461 buf_len = conn->extra_len;
462 conn->extra_len = 0;
463 heap_free(conn->extra_buf);
464 conn->extra_buf = NULL;
465 }else {
466 buf_len = sock_recv(conn->socket, conn->ssl_buf+conn->extra_len, ssl_buf_size-conn->extra_len, 0);
467 if(buf_len < 0)
468 return FALSE;
469
470 if(!buf_len) {
471 *eof = TRUE;
472 return TRUE;
473 }
474 }
475
476 *ret_size = 0;
477 *eof = FALSE;
478
479 do {
480 memset(bufs, 0, sizeof(bufs));
481 bufs[0].BufferType = SECBUFFER_DATA;
482 bufs[0].cbBuffer = buf_len;
483 bufs[0].pvBuffer = conn->ssl_buf;
484
485 res = DecryptMessage(&conn->ssl_ctx, &buf_desc, 0, NULL);
486 switch(res) {
487 case SEC_E_OK:
488 break;
489 case SEC_I_CONTEXT_EXPIRED:
490 TRACE("context expired\n");
491 *eof = TRUE;
492 return TRUE;
493 case SEC_E_INCOMPLETE_MESSAGE:
494 assert(buf_len < ssl_buf_size);
495
496 size = sock_recv(conn->socket, conn->ssl_buf+buf_len, ssl_buf_size-buf_len, 0);
497 if(size < 1)
498 return FALSE;
499
500 buf_len += size;
501 continue;
502 default:
503 WARN("failed: %08x\n", res);
504 return FALSE;
505 }
506 } while(res != SEC_E_OK);
507
508 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
509 if(bufs[i].BufferType == SECBUFFER_DATA) {
510 size = min(buf_size, bufs[i].cbBuffer);
511 memcpy(buf, bufs[i].pvBuffer, size);
512 if(size < bufs[i].cbBuffer) {
513 assert(!conn->peek_len);
514 conn->peek_msg_mem = conn->peek_msg = heap_alloc(bufs[i].cbBuffer - size);
515 if(!conn->peek_msg)
516 return FALSE;
517 conn->peek_len = bufs[i].cbBuffer-size;
518 memcpy(conn->peek_msg, (char*)bufs[i].pvBuffer+size, conn->peek_len);
519 }
520
521 *ret_size = size;
522 }
523 }
524
525 for(i = 0; i < ARRAY_SIZE(bufs); i++) {
526 if(bufs[i].BufferType == SECBUFFER_EXTRA) {
527 conn->extra_buf = heap_alloc(bufs[i].cbBuffer);
528 if(!conn->extra_buf)
529 return FALSE;
530
531 conn->extra_len = bufs[i].cbBuffer;
532 memcpy(conn->extra_buf, bufs[i].pvBuffer, conn->extra_len);
533 }
534 }
535
536 return TRUE;
537 }
538
netconn_recv(struct netconn * conn,void * buf,size_t len,int flags,int * recvd)539 BOOL netconn_recv( struct netconn *conn, void *buf, size_t len, int flags, int *recvd )
540 {
541 *recvd = 0;
542 if (!len) return TRUE;
543
544 if (conn->secure)
545 {
546 SIZE_T size, cread;
547 BOOL res, eof;
548
549 if (conn->peek_msg)
550 {
551 *recvd = min( len, conn->peek_len );
552 memcpy( buf, conn->peek_msg, *recvd );
553 conn->peek_len -= *recvd;
554 conn->peek_msg += *recvd;
555
556 if (conn->peek_len == 0)
557 {
558 heap_free( conn->peek_msg_mem );
559 conn->peek_msg_mem = NULL;
560 conn->peek_msg = NULL;
561 }
562 /* check if we have enough data from the peek buffer */
563 if (!(flags & MSG_WAITALL) || *recvd == len) return TRUE;
564 }
565 size = *recvd;
566
567 do {
568 res = read_ssl_chunk(conn, (BYTE*)buf+size, len-size, &cread, &eof);
569 if(!res) {
570 WARN("read_ssl_chunk failed\n");
571 if(!size)
572 return FALSE;
573 break;
574 }
575
576 if(eof) {
577 TRACE("EOF\n");
578 break;
579 }
580
581 size += cread;
582 }while(!size || ((flags & MSG_WAITALL) && size < len));
583
584 TRACE("received %ld bytes\n", size);
585 *recvd = size;
586 return TRUE;
587 }
588 return ((*recvd = sock_recv( conn->socket, buf, len, flags )) != -1);
589 }
590
netconn_query_data_available(struct netconn * conn)591 ULONG netconn_query_data_available( struct netconn *conn )
592 {
593 return conn->secure ? conn->peek_len : 0;
594 }
595
netconn_set_timeout(struct netconn * netconn,BOOL send,int value)596 DWORD netconn_set_timeout( struct netconn *netconn, BOOL send, int value )
597 {
598 int opt = send ? SO_SNDTIMEO : SO_RCVTIMEO;
599 if (setsockopt( netconn->socket, SOL_SOCKET, opt, (void *)&value, sizeof(value) ) == -1)
600 {
601 DWORD err = WSAGetLastError();
602 WARN("setsockopt failed (%u)\n", err );
603 return err;
604 }
605 return ERROR_SUCCESS;
606 }
607
netconn_is_alive(struct netconn * netconn)608 BOOL netconn_is_alive( struct netconn *netconn )
609 {
610 int len;
611 char b;
612 DWORD err;
613
614 set_blocking( netconn, FALSE );
615 len = sock_recv( netconn->socket, &b, 1, MSG_PEEK );
616 err = WSAGetLastError();
617 set_blocking( netconn, TRUE );
618
619 return len == 1 || (len == -1 && err == WSAEWOULDBLOCK);
620 }
621
resolve_hostname(const WCHAR * name,INTERNET_PORT port,struct sockaddr_storage * sa)622 static DWORD resolve_hostname( const WCHAR *name, INTERNET_PORT port, struct sockaddr_storage *sa )
623 {
624 ADDRINFOW *res, hints;
625 int ret;
626
627 memset( &hints, 0, sizeof(hints) );
628 /* Prefer IPv4 to IPv6 addresses, since some web servers do not listen on
629 * their IPv6 addresses even though they have IPv6 addresses in the DNS.
630 */
631 hints.ai_family = AF_INET;
632
633 ret = GetAddrInfoW( name, NULL, &hints, &res );
634 if (ret != 0)
635 {
636 TRACE("failed to get IPv4 address of %s, retrying with IPv6\n", debugstr_w(name));
637 hints.ai_family = AF_INET6;
638 ret = GetAddrInfoW( name, NULL, &hints, &res );
639 if (ret != 0)
640 {
641 TRACE("failed to get address of %s\n", debugstr_w(name));
642 return ERROR_WINHTTP_NAME_NOT_RESOLVED;
643 }
644 }
645 memcpy( sa, res->ai_addr, res->ai_addrlen );
646 switch (res->ai_family)
647 {
648 case AF_INET:
649 ((struct sockaddr_in *)sa)->sin_port = htons( port );
650 break;
651 case AF_INET6:
652 ((struct sockaddr_in6 *)sa)->sin6_port = htons( port );
653 break;
654 }
655
656 FreeAddrInfoW( res );
657 return ERROR_SUCCESS;
658 }
659
660 #ifdef __REACTOS__
661
662 struct resolve_args
663 {
664 const WCHAR *hostname;
665 INTERNET_PORT port;
666 struct sockaddr_storage *sa;
667 };
668
resolve_proc(LPVOID arg)669 static DWORD CALLBACK resolve_proc( LPVOID arg )
670 {
671 struct resolve_args *ra = arg;
672 return resolve_hostname( ra->hostname, ra->port, ra->sa );
673 }
674
netconn_resolve(WCHAR * hostname,INTERNET_PORT port,struct sockaddr_storage * sa,int timeout)675 BOOL netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr_storage *sa, int timeout )
676 {
677 DWORD ret;
678
679 if (timeout)
680 {
681 DWORD status;
682 HANDLE thread;
683 struct resolve_args ra;
684
685 ra.hostname = hostname;
686 ra.port = port;
687 ra.sa = sa;
688
689 thread = CreateThread( NULL, 0, resolve_proc, &ra, 0, NULL );
690 if (!thread) return FALSE;
691
692 status = WaitForSingleObject( thread, timeout );
693 if (status == WAIT_OBJECT_0) GetExitCodeThread( thread, &ret );
694 else ret = ERROR_WINHTTP_TIMEOUT;
695 CloseHandle( thread );
696 }
697 else ret = resolve_hostname( hostname, port, sa );
698
699 if (ret)
700 {
701 SetLastError( ret );
702 return FALSE;
703 }
704 return TRUE;
705 }
706
707 #else /* __REACTOS__ */
708
709 struct async_resolve
710 {
711 const WCHAR *hostname;
712 INTERNET_PORT port;
713 struct sockaddr_storage *addr;
714 DWORD result;
715 HANDLE done;
716 };
717
resolve_proc(TP_CALLBACK_INSTANCE * instance,void * ctx)718 static void CALLBACK resolve_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
719 {
720 struct async_resolve *async = ctx;
721 async->result = resolve_hostname( async->hostname, async->port, async->addr );
722 SetEvent( async->done );
723 }
724
netconn_resolve(WCHAR * hostname,INTERNET_PORT port,struct sockaddr_storage * addr,int timeout)725 BOOL netconn_resolve( WCHAR *hostname, INTERNET_PORT port, struct sockaddr_storage *addr, int timeout )
726 {
727 DWORD ret;
728
729 if (!timeout) ret = resolve_hostname( hostname, port, addr );
730 else
731 {
732 struct async_resolve async;
733
734 async.hostname = hostname;
735 async.port = port;
736 async.addr = addr;
737 if (!(async.done = CreateEventW( NULL, FALSE, FALSE, NULL ))) return FALSE;
738 if (!TrySubmitThreadpoolCallback( resolve_proc, &async, NULL ))
739 {
740 CloseHandle( async.done );
741 return FALSE;
742 }
743 if (WaitForSingleObject( async.done, timeout ) != WAIT_OBJECT_0) ret = ERROR_WINHTTP_TIMEOUT;
744 else ret = async.result;
745 CloseHandle( async.done );
746 }
747
748 if (ret)
749 {
750 SetLastError( ret );
751 return FALSE;
752 }
753 return TRUE;
754 }
755
756 #endif /* __REACTOS__ */
757
netconn_get_certificate(struct netconn * conn)758 const void *netconn_get_certificate( struct netconn *conn )
759 {
760 const CERT_CONTEXT *ret;
761 SECURITY_STATUS res;
762
763 if (!conn->secure) return NULL;
764 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (void*)&ret);
765 return res == SEC_E_OK ? ret : NULL;
766 }
767
netconn_get_cipher_strength(struct netconn * conn)768 int netconn_get_cipher_strength( struct netconn *conn )
769 {
770 SecPkgContext_ConnectionInfo conn_info;
771 SECURITY_STATUS res;
772
773 if (!conn->secure) return 0;
774 res = QueryContextAttributesW(&conn->ssl_ctx, SECPKG_ATTR_CONNECTION_INFO, (void*)&conn_info);
775 if(res != SEC_E_OK)
776 WARN("QueryContextAttributesW failed: %08x\n", res);
777 return res == SEC_E_OK ? conn_info.dwCipherStrength : 0;
778 }
779