FreeRDP
Loading...
Searching...
No Matches
tcp.c
1
21#include <freerdp/config.h>
22
23#include "settings.h"
24
25#include <time.h>
26#include <errno.h>
27#include <fcntl.h>
28
29#include <winpr/crt.h>
30#include <winpr/platform.h>
31#include <winpr/winsock.h>
32
33#include "rdp.h"
34#include "utils.h"
35
36#if !defined(_WIN32)
37
38#include <netdb.h>
39#include <unistd.h>
40#include <sys/ioctl.h>
41#include <sys/socket.h>
42#include <netinet/in.h>
43#include <netinet/tcp.h>
44#include <net/if.h>
45#include <sys/types.h>
46#include <arpa/inet.h>
47
48#ifdef WINPR_HAVE_POLL_H
49#include <poll.h>
50#else
51#include <time.h>
52#include <sys/select.h>
53#endif
54
55#if defined(__FreeBSD__) || defined(__OpenBSD__)
56#ifndef SOL_TCP
57#define SOL_TCP IPPROTO_TCP
58#endif
59#endif
60
61#ifdef __APPLE__
62#ifndef SOL_TCP
63#define SOL_TCP IPPROTO_TCP
64#endif
65#ifndef TCP_KEEPIDLE
66#define TCP_KEEPIDLE TCP_KEEPALIVE
67#endif
68#endif
69
70#else
71
72#include <winpr/windows.h>
73
74#include <winpr/crt.h>
75
76#define SHUT_RDWR SD_BOTH
77#define close(_fd) closesocket(_fd)
78
79#endif
80
81#include <freerdp/log.h>
82
83#include <winpr/stream.h>
84
85#include "tcp.h"
86#include "../crypto/opensslcompat.h"
87
88#if defined(HAVE_AF_VSOCK_H)
89#include <ctype.h>
90#include <linux/vm_sockets.h>
91#endif
92
93#define TAG FREERDP_TAG("core")
94
95/* Simple Socket BIO */
96
97typedef struct
98{
99 SOCKET socket;
100 HANDLE hEvent;
101} WINPR_BIO_SIMPLE_SOCKET;
102
103static int transport_bio_simple_init(BIO* bio, SOCKET socket, int shutdown);
104static int transport_bio_simple_uninit(BIO* bio);
105
106static int transport_bio_simple_write(BIO* bio, const char* buf, int size)
107{
108 int error = 0;
109 int status = 0;
110 WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
111
112 if (!buf)
113 return 0;
114
115 BIO_clear_flags(bio, BIO_FLAGS_WRITE);
116 status = _send(ptr->socket, buf, size, 0);
117
118 if (status <= 0)
119 {
120 error = WSAGetLastError();
121
122 if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) || (error == WSAEINPROGRESS) ||
123 (error == WSAEALREADY))
124 {
125 BIO_set_flags(bio, (BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY));
126 }
127 else
128 {
129 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
130 }
131 }
132
133 return status;
134}
135
136static int transport_bio_simple_read(BIO* bio, char* buf, int size)
137{
138 int error = 0;
139 int status = 0;
140 WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
141
142 if (!buf)
143 return 0;
144
145 BIO_clear_flags(bio, BIO_FLAGS_READ);
146 (void)WSAResetEvent(ptr->hEvent);
147 status = _recv(ptr->socket, buf, size, 0);
148
149 if (status > 0)
150 {
151 return status;
152 }
153
154 if (status == 0)
155 {
156 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
157 return 0;
158 }
159
160 error = WSAGetLastError();
161
162 if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) || (error == WSAEINPROGRESS) ||
163 (error == WSAEALREADY))
164 {
165 BIO_set_flags(bio, (BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY));
166 }
167 else
168 {
169 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
170 }
171
172 return -1;
173}
174
175static int transport_bio_simple_puts(WINPR_ATTR_UNUSED BIO* bio, WINPR_ATTR_UNUSED const char* str)
176{
177 return -2;
178}
179
180static int transport_bio_simple_gets(WINPR_ATTR_UNUSED BIO* bio, WINPR_ATTR_UNUSED char* str,
181 WINPR_ATTR_UNUSED int size)
182{
183 return 1;
184}
185
186static long transport_bio_simple_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
187{
188 int status = -1;
189 WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
190
191 switch (cmd)
192 {
193 case BIO_C_SET_SOCKET:
194 transport_bio_simple_uninit(bio);
195 transport_bio_simple_init(bio, (SOCKET)arg2, (int)arg1);
196 return 1;
197 case BIO_C_GET_SOCKET:
198 if (!BIO_get_init(bio) || !arg2)
199 return 0;
200
201 *((SOCKET*)arg2) = ptr->socket;
202 return 1;
203 case BIO_C_GET_EVENT:
204 if (!BIO_get_init(bio) || !arg2)
205 return 0;
206
207 *((HANDLE*)arg2) = ptr->hEvent;
208 return 1;
209 case BIO_C_SET_NONBLOCK:
210 {
211#ifndef _WIN32
212 int flags = 0;
213 flags = fcntl((int)ptr->socket, F_GETFL);
214
215 if (flags == -1)
216 return 0;
217
218 if (arg1)
219 (void)fcntl((int)ptr->socket, F_SETFL, flags | O_NONBLOCK);
220 else
221 (void)fcntl((int)ptr->socket, F_SETFL, flags & ~(O_NONBLOCK));
222
223#else
224 /* the internal socket is always non-blocking */
225#endif
226 return 1;
227 }
228 case BIO_C_WAIT_READ:
229 {
230 int timeout = (int)arg1;
231 int sockfd = (int)ptr->socket;
232#ifdef WINPR_HAVE_POLL_H
233 struct pollfd pollset;
234 pollset.fd = sockfd;
235 pollset.events = POLLIN;
236 pollset.revents = 0;
237
238 do
239 {
240 status = poll(&pollset, 1, timeout);
241 } while ((status < 0) && (errno == EINTR));
242
243#else
244 fd_set rset = WINPR_C_ARRAY_INIT;
245 struct timeval tv = WINPR_C_ARRAY_INIT;
246 FD_ZERO(&rset);
247 FD_SET(sockfd, &rset);
248
249 if (timeout)
250 {
251 tv.tv_sec = timeout / 1000;
252 tv.tv_usec = (timeout % 1000) * 1000;
253 }
254
255 do
256 {
257 status = select(sockfd + 1, &rset, nullptr, nullptr, timeout ? &tv : nullptr);
258 } while ((status < 0) && (errno == EINTR));
259
260#endif
261 /* Convert timeout to error return */
262 if (status == 0)
263 errno = ETIMEDOUT;
264 }
265 break;
266
267 case BIO_C_WAIT_WRITE:
268 {
269 int timeout = (int)arg1;
270 int sockfd = (int)ptr->socket;
271#ifdef WINPR_HAVE_POLL_H
272 struct pollfd pollset;
273 pollset.fd = sockfd;
274 pollset.events = POLLOUT;
275 pollset.revents = 0;
276
277 do
278 {
279 status = poll(&pollset, 1, timeout);
280 } while ((status < 0) && (errno == EINTR));
281
282#else
283 fd_set rset = WINPR_C_ARRAY_INIT;
284 struct timeval tv = WINPR_C_ARRAY_INIT;
285 FD_ZERO(&rset);
286 FD_SET(sockfd, &rset);
287
288 if (timeout)
289 {
290 tv.tv_sec = timeout / 1000;
291 tv.tv_usec = (timeout % 1000) * 1000;
292 }
293
294 do
295 {
296 status = select(sockfd + 1, nullptr, &rset, nullptr, timeout ? &tv : nullptr);
297 } while ((status < 0) && (errno == EINTR));
298
299#endif
300 /* Convert timeout to error return */
301 if (status == 0)
302 errno = ETIMEDOUT;
303 }
304 break;
305
306 case BIO_C_SET_FD:
307 if (arg2)
308 {
309 transport_bio_simple_uninit(bio);
310 transport_bio_simple_init(bio, (SOCKET) * ((int*)arg2), (int)arg1);
311 status = 1;
312 }
313
314 break;
315
316 case BIO_C_GET_FD:
317 if (BIO_get_init(bio))
318 {
319 if (arg2)
320 *((int*)arg2) = (int)ptr->socket;
321
322 status = (int)ptr->socket;
323 }
324
325 break;
326
327 case BIO_CTRL_GET_CLOSE:
328 status = BIO_get_shutdown(bio);
329 break;
330
331 case BIO_CTRL_SET_CLOSE:
332 BIO_set_shutdown(bio, (int)arg1);
333 status = 1;
334 break;
335
336 case BIO_CTRL_FLUSH:
337 case BIO_CTRL_DUP:
338 status = 1;
339 break;
340
341 default:
342 status = 0;
343 break;
344 }
345
346 return status;
347}
348
349static int transport_bio_simple_init(BIO* bio, SOCKET socket, int shutdown)
350{
351 WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
352 ptr->socket = socket;
353 BIO_set_shutdown(bio, shutdown);
354 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
355 BIO_set_init(bio, 1);
356 ptr->hEvent = WSACreateEvent();
357
358 if (!ptr->hEvent)
359 return 0;
360
361 /* WSAEventSelect automatically sets the socket in non-blocking mode */
362 if (WSAEventSelect(ptr->socket, ptr->hEvent, FD_READ | FD_ACCEPT | FD_CLOSE))
363 {
364 WLog_ERR(TAG, "WSAEventSelect returned 0x%08x",
365 WINPR_CXX_COMPAT_CAST(unsigned, WSAGetLastError()));
366 return 0;
367 }
368
369 return 1;
370}
371
372static int transport_bio_simple_uninit(BIO* bio)
373{
374 WINPR_BIO_SIMPLE_SOCKET* ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
375
376 if (BIO_get_shutdown(bio))
377 {
378 if (BIO_get_init(bio) && ptr)
379 {
380 _shutdown(ptr->socket, SD_BOTH);
381 closesocket(ptr->socket);
382 ptr->socket = 0;
383 }
384 }
385
386 if (ptr && ptr->hEvent)
387 {
388 (void)CloseHandle(ptr->hEvent);
389 ptr->hEvent = nullptr;
390 }
391
392 BIO_set_init(bio, 0);
393 BIO_set_flags(bio, 0);
394 return 1;
395}
396
397static int transport_bio_simple_new(BIO* bio)
398{
399 WINPR_BIO_SIMPLE_SOCKET* ptr = nullptr;
400 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
401 ptr = (WINPR_BIO_SIMPLE_SOCKET*)calloc(1, sizeof(WINPR_BIO_SIMPLE_SOCKET));
402
403 if (!ptr)
404 return 0;
405
406 BIO_set_data(bio, ptr);
407 return 1;
408}
409
410static int transport_bio_simple_free(BIO* bio)
411{
412 WINPR_BIO_SIMPLE_SOCKET* ptr = nullptr;
413
414 if (!bio)
415 return 0;
416
417 transport_bio_simple_uninit(bio);
418 ptr = (WINPR_BIO_SIMPLE_SOCKET*)BIO_get_data(bio);
419
420 if (ptr)
421 {
422 BIO_set_data(bio, nullptr);
423 free(ptr);
424 }
425
426 return 1;
427}
428
429BIO_METHOD* BIO_s_simple_socket(void)
430{
431 static BIO_METHOD* bio_methods = nullptr;
432
433 if (bio_methods == nullptr)
434 {
435 if (!(bio_methods = BIO_meth_new(BIO_TYPE_SIMPLE, "SimpleSocket")))
436 return nullptr;
437
438 BIO_meth_set_write(bio_methods, transport_bio_simple_write);
439 BIO_meth_set_read(bio_methods, transport_bio_simple_read);
440 BIO_meth_set_puts(bio_methods, transport_bio_simple_puts);
441 BIO_meth_set_gets(bio_methods, transport_bio_simple_gets);
442 BIO_meth_set_ctrl(bio_methods, transport_bio_simple_ctrl);
443 BIO_meth_set_create(bio_methods, transport_bio_simple_new);
444 BIO_meth_set_destroy(bio_methods, transport_bio_simple_free);
445 }
446
447 return bio_methods;
448}
449
450/* Buffered Socket BIO */
451
452typedef struct
453{
454 BIO* bufferedBio;
455 BOOL readBlocked;
456 BOOL writeBlocked;
457 RingBuffer xmitBuffer;
458} WINPR_BIO_BUFFERED_SOCKET;
459
460static int transport_bio_buffered_write(BIO* bio, const char* buf, int num)
461{
462 int ret = num;
463 int nchunks = 0;
464 size_t committedBytes = 0;
465 DataChunk chunks[2] = WINPR_C_ARRAY_INIT;
466 WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*)BIO_get_data(bio);
467 BIO* next_bio = nullptr;
468
469 WINPR_ASSERT(bio);
470 WINPR_ASSERT(ptr);
471 if (num < 0)
472 return num;
473
474 ptr->writeBlocked = FALSE;
475 BIO_clear_flags(bio, BIO_FLAGS_WRITE);
476
477 /* we directly append extra bytes in the xmit buffer, this could be prevented
478 * but for now it makes the code more simple.
479 */
480 if (buf && (num > 0) && !ringbuffer_write(&ptr->xmitBuffer, (const BYTE*)buf, (size_t)num))
481 {
482 WLog_ERR(TAG, "an error occurred when writing (num: %d)", num);
483 return -1;
484 }
485
486 nchunks = ringbuffer_peek(&ptr->xmitBuffer, chunks, ringbuffer_used(&ptr->xmitBuffer));
487 next_bio = BIO_next(bio);
488
489 for (int i = 0; i < nchunks; i++)
490 {
491 while (chunks[i].size)
492 {
493 ERR_clear_error();
494
495 const size_t wr = MIN(INT32_MAX, chunks[i].size);
496 const int status = BIO_write(next_bio, chunks[i].data, (int)wr);
497
498 if (status <= 0)
499 {
500 if (!BIO_should_retry(next_bio))
501 {
502 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
503 ret = -1; /* fatal error */
504 goto out;
505 }
506
507 if (BIO_should_write(next_bio))
508 {
509 BIO_set_flags(bio, BIO_FLAGS_WRITE);
510 ptr->writeBlocked = TRUE;
511 goto out; /* EWOULDBLOCK */
512 }
513 }
514 else
515 {
516 committedBytes += (size_t)status;
517 chunks[i].size -= (size_t)status;
518 chunks[i].data += status;
519 }
520 }
521 }
522
523out:
524 ringbuffer_commit_read_bytes(&ptr->xmitBuffer, committedBytes);
525 return ret;
526}
527
528static int transport_bio_buffered_read(BIO* bio, char* buf, int size)
529{
530 int status = 0;
531 WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*)BIO_get_data(bio);
532 BIO* next_bio = BIO_next(bio);
533 ptr->readBlocked = FALSE;
534 BIO_clear_flags(bio, BIO_FLAGS_READ);
535 ERR_clear_error();
536 status = BIO_read(next_bio, buf, size);
537
538 if (status <= 0)
539 {
540 if (!BIO_should_retry(next_bio))
541 {
542 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
543 goto out;
544 }
545
546 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
547
548 if (BIO_should_read(next_bio))
549 {
550 BIO_set_flags(bio, BIO_FLAGS_READ);
551 ptr->readBlocked = TRUE;
552 goto out;
553 }
554 }
555
556out:
557 return status;
558}
559
560static int transport_bio_buffered_puts(WINPR_ATTR_UNUSED BIO* bio,
561 WINPR_ATTR_UNUSED const char* str)
562{
563 return 1;
564}
565
566static int transport_bio_buffered_gets(WINPR_ATTR_UNUSED BIO* bio, WINPR_ATTR_UNUSED char* str,
567 WINPR_ATTR_UNUSED int size)
568{
569 return 1;
570}
571
572static long transport_bio_buffered_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
573{
574 long status = -1;
575 WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*)BIO_get_data(bio);
576
577 switch (cmd)
578 {
579 case BIO_CTRL_FLUSH:
580 if (!ringbuffer_used(&ptr->xmitBuffer))
581 status = 1;
582 else
583 status = (transport_bio_buffered_write(bio, nullptr, 0) >= 0) ? 1 : -1;
584
585 break;
586
587 case BIO_CTRL_WPENDING:
588 status = WINPR_ASSERTING_INT_CAST(long, ringbuffer_used(&ptr->xmitBuffer));
589 break;
590
591 case BIO_CTRL_PENDING:
592 status = 0;
593 break;
594
595 case BIO_C_READ_BLOCKED:
596 status = (int)ptr->readBlocked;
597 break;
598
599 case BIO_C_WRITE_BLOCKED:
600 status = (int)ptr->writeBlocked;
601 break;
602
603 default:
604 status = BIO_ctrl(BIO_next(bio), cmd, arg1, arg2);
605 break;
606 }
607
608 return status;
609}
610
611static int transport_bio_buffered_new(BIO* bio)
612{
613 WINPR_BIO_BUFFERED_SOCKET* ptr = nullptr;
614 BIO_set_init(bio, 1);
615 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
616 ptr = (WINPR_BIO_BUFFERED_SOCKET*)calloc(1, sizeof(WINPR_BIO_BUFFERED_SOCKET));
617
618 if (!ptr)
619 return -1;
620
621 BIO_set_data(bio, (void*)ptr);
622
623 if (!ringbuffer_init(&ptr->xmitBuffer, 0x10000))
624 return -1;
625
626 return 1;
627}
628
629/* Free the buffered BIO.
630 * Do not free other elements in the BIO stack,
631 * let BIO_free_all handle that. */
632static int transport_bio_buffered_free(BIO* bio)
633{
634 WINPR_BIO_BUFFERED_SOCKET* ptr = (WINPR_BIO_BUFFERED_SOCKET*)BIO_get_data(bio);
635
636 if (!ptr)
637 return 0;
638
639 ringbuffer_destroy(&ptr->xmitBuffer);
640 free(ptr);
641 return 1;
642}
643
644BIO_METHOD* BIO_s_buffered_socket(void)
645{
646 static BIO_METHOD* bio_methods = nullptr;
647
648 if (bio_methods == nullptr)
649 {
650 if (!(bio_methods = BIO_meth_new(BIO_TYPE_BUFFERED, "BufferedSocket")))
651 return nullptr;
652
653 BIO_meth_set_write(bio_methods, transport_bio_buffered_write);
654 BIO_meth_set_read(bio_methods, transport_bio_buffered_read);
655 BIO_meth_set_puts(bio_methods, transport_bio_buffered_puts);
656 BIO_meth_set_gets(bio_methods, transport_bio_buffered_gets);
657 BIO_meth_set_ctrl(bio_methods, transport_bio_buffered_ctrl);
658 BIO_meth_set_create(bio_methods, transport_bio_buffered_new);
659 BIO_meth_set_destroy(bio_methods, transport_bio_buffered_free);
660 }
661
662 return bio_methods;
663}
664
665char* freerdp_tcp_address_to_string(const struct sockaddr_storage* addr, BOOL* pIPv6)
666{
667 char ipAddress[INET6_ADDRSTRLEN + 1] = WINPR_C_ARRAY_INIT;
668 const struct sockaddr_in6* sockaddr_ipv6 = (const struct sockaddr_in6*)addr;
669 const struct sockaddr_in* sockaddr_ipv4 = (const struct sockaddr_in*)addr;
670
671 if (addr == nullptr)
672 {
673 return nullptr;
674 }
675
676 switch (sockaddr_ipv4->sin_family)
677 {
678 case AF_INET:
679 if (!inet_ntop(sockaddr_ipv4->sin_family, &sockaddr_ipv4->sin_addr, ipAddress,
680 sizeof(ipAddress)))
681 return nullptr;
682
683 break;
684
685 case AF_INET6:
686 if (!inet_ntop(sockaddr_ipv6->sin6_family, &sockaddr_ipv6->sin6_addr, ipAddress,
687 sizeof(ipAddress)))
688 return nullptr;
689
690 break;
691
692 case AF_UNIX:
693 (void)sprintf_s(ipAddress, ARRAYSIZE(ipAddress), "127.0.0.1");
694 break;
695
696 default:
697 return nullptr;
698 }
699
700 if (pIPv6 != nullptr)
701 {
702 *pIPv6 = (sockaddr_ipv4->sin_family == AF_INET6);
703 }
704
705 return _strdup(ipAddress);
706}
707
708static bool freerdp_tcp_get_ip_address(rdpSettings* settings, int sockfd)
709{
710 WINPR_ASSERT(settings);
711
712 struct sockaddr_storage saddr = WINPR_C_ARRAY_INIT;
713 socklen_t length = sizeof(struct sockaddr_storage);
714
715 if (!freerdp_settings_set_string(settings, FreeRDP_ClientAddress, nullptr))
716 return false;
717 if (sockfd < 0)
718 return false;
719 if (getsockname(sockfd, (struct sockaddr*)&saddr, &length) != 0)
720 return false;
721 settings->ClientAddress = freerdp_tcp_address_to_string(&saddr, &settings->IPv6Enabled);
722 return settings->ClientAddress != nullptr;
723}
724
725char* freerdp_tcp_get_peer_address(SOCKET sockfd)
726{
727 struct sockaddr_storage saddr = WINPR_C_ARRAY_INIT;
728 socklen_t length = sizeof(struct sockaddr_storage);
729
730 if (getpeername((int)sockfd, (struct sockaddr*)&saddr, &length) != 0)
731 {
732 return nullptr;
733 }
734
735 return freerdp_tcp_address_to_string(&saddr, nullptr);
736}
737
738static int freerdp_uds_connect(const char* path)
739{
740#ifndef _WIN32
741 int status = 0;
742 int sockfd = 0;
743 struct sockaddr_un addr = WINPR_C_ARRAY_INIT;
744 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
745
746 if (sockfd == -1)
747 {
748 WLog_ERR(TAG, "socket");
749 return -1;
750 }
751
752 addr.sun_family = AF_UNIX;
753 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
754 status = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
755
756 if (status < 0)
757 {
758 WLog_ERR(TAG, "connect");
759 close(sockfd);
760 return -1;
761 }
762
763 return sockfd;
764#else /* ifndef _WIN32 */
765 return -1;
766#endif
767}
768
769struct addrinfo* freerdp_tcp_resolve_host(const char* hostname, int port, int ai_flags)
770{
771 char* service = nullptr;
772 char port_str[16] = WINPR_C_ARRAY_INIT;
773 int status = 0;
774
775 struct addrinfo* result = nullptr;
776 struct addrinfo hints = { .ai_family = AF_UNSPEC,
777 .ai_socktype = SOCK_STREAM,
778 .ai_flags = ai_flags };
779
780 if (port >= 0)
781 {
782 (void)sprintf_s(port_str, sizeof(port_str) - 1, "%d", port);
783 service = port_str;
784 }
785
786 status = getaddrinfo(hostname, service, &hints, &result);
787
788 if (status)
789 {
790 WLog_WARN(TAG, "getaddrinfo(%s) failed with %s", hostname, gai_strerror(status));
791 return nullptr;
792 }
793
794 return result;
795}
796
797static BOOL freerdp_tcp_is_hostname_resolvable(rdpContext* context, const char* hostname)
798{
799 struct addrinfo* result = freerdp_tcp_resolve_host(hostname, -1, 0);
800
801 if (!result)
802 {
803 freerdp_set_last_error_if_not(context, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
804
805 return FALSE;
806 }
807
808 freerdp_set_last_error_log(context, 0);
809 freeaddrinfo(result);
810 return TRUE;
811}
812
813static BOOL freerdp_tcp_connect_timeout(rdpContext* context, int sockfd, struct sockaddr* addr,
814 size_t addrlen, UINT32 timeout)
815{
816 BOOL rc = FALSE;
817 HANDLE handles[2] = WINPR_C_ARRAY_INIT;
818 DWORD count = 0;
819 u_long arg = 0;
820 DWORD tout = (timeout > 0) ? timeout : INFINITE;
821
822 handles[count] = CreateEvent(nullptr, TRUE, FALSE, nullptr);
823
824 if (!handles[count])
825 return FALSE;
826
827 const int wsastatus = WSAEventSelect((SOCKET)sockfd, handles[count++],
828 FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE);
829
830 if (wsastatus < 0)
831 {
832 WLog_ERR(TAG, "WSAEventSelect failed with %d", WSAGetLastError());
833 goto fail;
834 }
835
836 handles[count++] = utils_get_abort_event(context->rdp);
837
838 {
839 const int constatus =
840 _connect((SOCKET)sockfd, addr, WINPR_ASSERTING_INT_CAST(int, addrlen));
841 if (constatus < 0)
842 {
843 const int estatus = WSAGetLastError();
844
845 switch (estatus)
846 {
847 case WSAEINPROGRESS:
848 case WSAEWOULDBLOCK:
849 break;
850
851 default:
852 goto fail;
853 }
854 }
855 }
856
857 {
858 const DWORD wstatus = WaitForMultipleObjects(count, handles, FALSE, tout);
859 if (WAIT_OBJECT_0 != wstatus)
860 goto fail;
861 }
862
863 {
864 const int status = WSAEventSelect((SOCKET)sockfd, handles[0], 0);
865 if (status < 0)
866 {
867 WLog_ERR(TAG, "WSAEventSelect failed with %d", WSAGetLastError());
868 goto fail;
869 }
870 }
871
872 if (_ioctlsocket((SOCKET)sockfd, FIONBIO, &arg) != 0)
873 goto fail;
874
875 rc = TRUE;
876fail:
877{
878 INT32 optval = 0;
879 socklen_t optlen = sizeof(optval);
880 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&optval, &optlen) >= 0)
881 {
882 if (optval != 0)
883 {
884 char ebuffer[256] = WINPR_C_ARRAY_INIT;
885 char hostname[512] = WINPR_C_ARRAY_INIT;
886 char serv[512] = WINPR_C_ARRAY_INIT;
887
888 getnameinfo(addr, WINPR_ASSERTING_INT_CAST(socklen_t, addrlen), hostname,
889 WINPR_ASSERTING_INT_CAST(socklen_t, sizeof(hostname)), serv,
890 WINPR_ASSERTING_INT_CAST(socklen_t, sizeof(serv)),
891 NI_NUMERICHOST | NI_NUMERICSERV);
892 WLog_WARN(TAG, "connect to %s:%s failed with error: %s [%" PRId32 "]", hostname, serv,
893 winpr_strerror(optval, ebuffer, sizeof(ebuffer)), optval);
894 rc = FALSE;
895 }
896 }
897}
898
899 (void)CloseHandle(handles[0]);
900 return rc;
901}
902
903typedef struct
904{
905 SOCKET s;
906 struct addrinfo* addr;
907 struct addrinfo* result;
908} t_peer;
909
910static void peer_free(t_peer* peer)
911{
912 if (peer->s != INVALID_SOCKET)
913 closesocket(peer->s);
914
915 freeaddrinfo(peer->addr);
916 memset(peer, 0, sizeof(t_peer));
917 peer->s = INVALID_SOCKET;
918}
919
920static int freerdp_tcp_connect_multi(rdpContext* context, char** hostnames, const UINT32* ports,
921 UINT32 count, UINT16 port, WINPR_ATTR_UNUSED UINT32 timeout)
922{
923 UINT32 sindex = count;
924 SOCKET sockfd = INVALID_SOCKET;
925 struct addrinfo* addr = nullptr;
926 struct addrinfo* result = nullptr;
927
928 HANDLE* events = (HANDLE*)calloc(count + 1, sizeof(HANDLE));
929 t_peer* peers = (t_peer*)calloc(count, sizeof(t_peer));
930
931 if (!peers || !events || (count < 1))
932 {
933 free(peers);
934 free((void*)events);
935 return -1;
936 }
937
938 for (UINT32 index = 0; index < count; index++)
939 {
940 int curPort = port;
941
942 if (ports)
943 curPort = WINPR_ASSERTING_INT_CAST(int, ports[index]);
944
945 result = freerdp_tcp_resolve_host(hostnames[index], curPort, 0);
946
947 if (!result)
948 continue;
949
950 addr = result;
951
952 if ((addr->ai_family == AF_INET6) && (addr->ai_next != nullptr))
953 {
954 while ((addr = addr->ai_next))
955 {
956 if (addr->ai_family == AF_INET)
957 break;
958 }
959
960 if (!addr)
961 addr = result;
962 }
963
964 peers[index].s = _socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
965
966 if (peers[index].s == INVALID_SOCKET)
967 {
968 freeaddrinfo(result);
969 continue;
970 }
971
972 peers[index].addr = addr;
973 peers[index].result = result;
974 }
975
976 for (UINT32 index = 0; index < count; index++)
977 {
978 sockfd = peers[index].s;
979 addr = peers[index].addr;
980
981 if ((sockfd == INVALID_SOCKET) || (!addr))
982 continue;
983
984 /* blocking tcp connect */
985 const int rc =
986 _connect(sockfd, addr->ai_addr, WINPR_ASSERTING_INT_CAST(int, addr->ai_addrlen));
987
988 if (rc >= 0)
989 {
990 /* connection success */
991 sindex = index;
992 break;
993 }
994 }
995
996 if (sindex < count)
997 {
998 sockfd = peers[sindex].s;
999 peers[sindex].s = INVALID_SOCKET;
1000 }
1001 else
1002 freerdp_set_last_error_log(context, FREERDP_ERROR_CONNECT_CANCELLED);
1003
1004 for (UINT32 index = 0; index < count; index++)
1005 peer_free(&peers[index]);
1006
1007 free(peers);
1008 free((void*)events);
1009 return (int)sockfd;
1010}
1011
1012BOOL freerdp_tcp_set_keep_alive_mode(const rdpSettings* settings, int sockfd)
1013{
1014 const BOOL keepalive = (freerdp_settings_get_bool(settings, FreeRDP_TcpKeepAlive));
1015 UINT32 optval = 0;
1016 socklen_t optlen = 0;
1017 BOOL isTcp = FALSE;
1018
1019 int xoptval = 0;
1020 socklen_t xoptlen = sizeof(xoptval);
1021 const int rc = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void*)&xoptval, &xoptlen);
1022
1023 if (rc == 0)
1024 isTcp = xoptval == SOCK_STREAM;
1025
1026 optval = keepalive ? 1 : 0;
1027 optlen = sizeof(optval);
1028
1029 if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&optval, optlen) < 0)
1030 {
1031 WLog_WARN(TAG, "setsockopt() SOL_SOCKET, SO_KEEPALIVE");
1032 }
1033
1034 if (!isTcp)
1035 return TRUE;
1036
1037#ifndef _WIN32
1038#ifdef TCP_KEEPIDLE
1039 optval = keepalive ? freerdp_settings_get_uint32(settings, FreeRDP_TcpKeepAliveDelay) : 0;
1040 optlen = sizeof(optval);
1041
1042 if (setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&optval, optlen) < 0)
1043 {
1044 WLog_WARN(TAG, "setsockopt() IPPROTO_TCP, TCP_KEEPIDLE");
1045 }
1046
1047#endif
1048#ifndef SOL_TCP
1049 /* "tcp" from /etc/protocols as getprotobyname(3C) */
1050#define SOL_TCP 6
1051#endif
1052#ifdef TCP_KEEPCNT
1053 optval = keepalive ? freerdp_settings_get_uint32(settings, FreeRDP_TcpKeepAliveRetries) : 0;
1054 optlen = sizeof(optval);
1055
1056 if (setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void*)&optval, optlen) < 0)
1057 {
1058 WLog_WARN(TAG, "setsockopt() SOL_TCP, TCP_KEEPCNT");
1059 }
1060
1061#endif
1062#ifdef TCP_KEEPINTVL
1063 optval = keepalive ? freerdp_settings_get_uint32(settings, FreeRDP_TcpKeepAliveInterval) : 0;
1064 optlen = sizeof(optval);
1065
1066 if (setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void*)&optval, optlen) < 0)
1067 {
1068 WLog_WARN(TAG, "setsockopt() SOL_TCP, TCP_KEEPINTVL");
1069 }
1070
1071#endif
1072#endif
1073#if defined(__MACOSX__) || defined(__IOS__)
1074 optval = 1;
1075 optlen = sizeof(optval);
1076
1077 if (setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&optval, optlen) < 0)
1078 {
1079 WLog_WARN(TAG, "setsockopt() SOL_SOCKET, SO_NOSIGPIPE");
1080 }
1081
1082#endif
1083#ifdef TCP_USER_TIMEOUT
1084 optval = freerdp_settings_get_uint32(settings, FreeRDP_TcpAckTimeout);
1085 optlen = sizeof(optval);
1086
1087 if (setsockopt(sockfd, SOL_TCP, TCP_USER_TIMEOUT, (void*)&optval, optlen) < 0)
1088 {
1089 WLog_WARN(TAG, "setsockopt() SOL_TCP, TCP_USER_TIMEOUT");
1090 }
1091
1092#endif
1093 return TRUE;
1094}
1095
1096int freerdp_tcp_connect(rdpContext* context, const char* hostname, int port, DWORD timeout)
1097{
1098 rdpTransport* transport = nullptr;
1099 if (!context || !context->rdp)
1100 return -1;
1101 transport = context->rdp->transport;
1102 if (!transport)
1103 return -1;
1104 return transport_tcp_connect(context->rdp->transport, hostname, port, timeout);
1105}
1106
1107static struct addrinfo* reorder_addrinfo_by_preference(rdpContext* context, struct addrinfo* addr)
1108{
1109 WINPR_ASSERT(context);
1110 WINPR_ASSERT(addr);
1111
1112 const BOOL preferIPv6 =
1113 freerdp_settings_get_bool(context->settings, FreeRDP_PreferIPv6OverIPv4);
1114 if (!preferIPv6)
1115 return addr;
1116
1117 struct addrinfo* ipv6Head = nullptr;
1118 struct addrinfo* ipv6Tail = nullptr;
1119 struct addrinfo* otherHead = nullptr;
1120 struct addrinfo* otherTail = nullptr;
1121
1122 /* Partition the list into IPv6 and other addresses */
1123 while (addr)
1124 {
1125 struct addrinfo* next = addr->ai_next;
1126 addr->ai_next = nullptr;
1127
1128 if (addr->ai_family == AF_INET6)
1129 {
1130 if (!ipv6Head)
1131 ipv6Head = addr;
1132 else
1133 ipv6Tail->ai_next = addr;
1134 ipv6Tail = addr;
1135 }
1136 else
1137 {
1138 if (!otherHead)
1139 otherHead = addr;
1140 else
1141 otherTail->ai_next = addr;
1142 otherTail = addr;
1143 }
1144 addr = next;
1145 }
1146
1147 /* Concatenate the lists */
1148 if (ipv6Tail)
1149 ipv6Tail->ai_next = otherHead;
1150
1151 return ipv6Head ? ipv6Head : otherHead;
1152}
1153
1154static int get_next_addrinfo(rdpContext* context, struct addrinfo* input, struct addrinfo** result,
1155 UINT32 errorCode)
1156{
1157 WINPR_ASSERT(context);
1158 WINPR_ASSERT(result);
1159
1160 struct addrinfo* addr = input;
1161 if (!addr)
1162 goto fail;
1163
1164 /* We want to force IPvX, abort if not detected */
1165 {
1166 const UINT32 IPvX = freerdp_settings_get_uint32(context->settings, FreeRDP_ForceIPvX);
1167 switch (IPvX)
1168 {
1169 case 4:
1170 case 6:
1171 {
1172 const int family = (IPvX == 4) ? AF_INET : AF_INET6;
1173 while (addr && (addr->ai_family != family))
1174 addr = addr->ai_next;
1175 }
1176 break;
1177 default:
1178 break;
1179 }
1180 }
1181
1182 if (!addr)
1183 goto fail;
1184
1185 *result = addr;
1186 return 0;
1187
1188fail:
1189 freerdp_set_last_error_if_not(context, errorCode);
1190 *result = nullptr;
1191 return -1;
1192}
1193
1194static int freerdp_vsock_connect(rdpContext* context, const char* hostname, int port)
1195{
1196#if defined(HAVE_AF_VSOCK_H)
1197 int sockfd = socket(AF_VSOCK, SOCK_STREAM, 0);
1198 if (sockfd < 0)
1199 {
1200 char buffer[256] = WINPR_C_ARRAY_INIT;
1201 WLog_WARN(TAG, "socket(AF_VSOCK, SOCK_STREAM, 0) failed with %s",
1202 winpr_strerror(errno, buffer, sizeof(buffer)));
1203 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
1204 return -1;
1205 }
1206
1207 struct sockaddr_vm addr = WINPR_C_ARRAY_INIT;
1208
1209 addr.svm_family = AF_VSOCK;
1210 addr.svm_port = WINPR_ASSERTING_INT_CAST(typeof(addr.svm_port), port);
1211
1212 errno = 0;
1213 char* ptr = nullptr;
1214 unsigned long val = strtoul(hostname, &ptr, 10);
1215 if (errno || (val > UINT32_MAX))
1216 {
1217 char ebuffer[256] = WINPR_C_ARRAY_INIT;
1218 WLog_ERR(TAG, "could not extract port from '%s', value=%lu, error=%s", hostname, val,
1219 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
1220 close(sockfd);
1221 return -1;
1222 }
1223 addr.svm_cid = WINPR_ASSERTING_INT_CAST(typeof(addr.svm_cid), val);
1224 if (addr.svm_cid == 2)
1225 {
1226 addr.svm_flags = VMADDR_FLAG_TO_HOST;
1227 }
1228 if ((connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_vm))) == -1)
1229 {
1230 WLog_ERR(TAG, "failed to connect to %s", hostname);
1231 close(sockfd);
1232 return -1;
1233 }
1234 return sockfd;
1235#else
1236 WLog_ERR(TAG, "Compiled without AF_VSOCK, '%s' not supported", hostname);
1237 return -1;
1238#endif
1239}
1240
1241static void log_connection_address(const char* hostname, struct addrinfo* addr)
1242{
1243 WINPR_ASSERT(addr);
1244
1245 char* peerAddress = freerdp_tcp_address_to_string(
1246 WINPR_PACKED_ALIGN_CAST(const struct sockaddr_storage*, addr->ai_addr), nullptr);
1247 if (peerAddress)
1248 WLog_DBG(TAG, "resolved %s: try to connect to %s", hostname, peerAddress);
1249 free(peerAddress);
1250}
1251
1252static int freerdp_host_connect(rdpContext* context, const char* hostname, int port, DWORD timeout)
1253{
1254 int sockfd = -1;
1255 struct addrinfo* addr = nullptr;
1256 struct addrinfo* result = freerdp_tcp_resolve_host(hostname, port, 0);
1257
1258 if (!result)
1259 {
1260 freerdp_set_last_error_if_not(context, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
1261 return -1;
1262 }
1263 freerdp_set_last_error_log(context, 0);
1264
1265 /* By default we take the first returned entry.
1266 * If PreferIPv6OverIPv4 = TRUE we reorder addresses by preference:
1267 * IPv6 addresses come first, then other addresses.
1268 */
1269 result = reorder_addrinfo_by_preference(context, result);
1270
1271 const int rc = get_next_addrinfo(context, result, &addr, FREERDP_ERROR_DNS_NAME_NOT_FOUND);
1272 if (rc < 0)
1273 goto fail;
1274
1275 do
1276 {
1277 sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
1278 if (sockfd >= 0)
1279 {
1280 log_connection_address(hostname, addr);
1281
1282 if (!freerdp_tcp_connect_timeout(context, sockfd, addr->ai_addr, addr->ai_addrlen,
1283 timeout))
1284 {
1285 close(sockfd);
1286 sockfd = -1;
1287 }
1288 }
1289
1290 if (sockfd < 0)
1291 {
1292 const int lrc =
1293 get_next_addrinfo(context, addr->ai_next, &addr, FREERDP_ERROR_CONNECT_FAILED);
1294 if (lrc < 0)
1295 goto fail;
1296 }
1297 } while (sockfd < 0);
1298
1299fail:
1300 freeaddrinfo(result);
1301 return sockfd;
1302}
1303
1304int freerdp_tcp_default_connect(rdpContext* context, rdpSettings* settings, const char* hostname,
1305 int port, DWORD timeout)
1306{
1307 int sockfd = -1;
1308 BOOL ipcSocket = FALSE;
1309 BOOL useExternalDefinedSocket = FALSE;
1310
1311 if (!hostname)
1312 {
1313 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
1314
1315 return -1;
1316 }
1317
1318 if (hostname[0] == '/')
1319 ipcSocket = TRUE;
1320
1321 if (hostname[0] == '|')
1322 useExternalDefinedSocket = TRUE;
1323
1324 const char* vsock = utils_is_vsock(hostname);
1325 if (ipcSocket)
1326 {
1327 sockfd = freerdp_uds_connect(hostname);
1328
1329 if (sockfd < 0)
1330 {
1331 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
1332 return -1;
1333 }
1334 }
1335 else if (useExternalDefinedSocket)
1336 sockfd = port;
1337 else if (vsock)
1338 sockfd = freerdp_vsock_connect(context, vsock, port);
1339 else
1340 {
1341 if (!settings->GatewayEnabled)
1342 {
1343 if (!freerdp_tcp_is_hostname_resolvable(context, hostname) ||
1344 settings->RemoteAssistanceMode)
1345 {
1346 if (settings->TargetNetAddressCount > 0)
1347 {
1348 WINPR_ASSERT(port <= UINT16_MAX);
1349 sockfd = freerdp_tcp_connect_multi(
1350 context, settings->TargetNetAddresses, settings->TargetNetPorts,
1351 settings->TargetNetAddressCount, (UINT16)port, timeout);
1352 }
1353 }
1354 }
1355
1356 if (sockfd <= 0)
1357 sockfd = freerdp_host_connect(context, hostname, port, timeout);
1358 }
1359
1360 if (!vsock)
1361 {
1362 if (!freerdp_tcp_get_ip_address(settings, sockfd))
1363 {
1364 if (!useExternalDefinedSocket)
1365 close(sockfd);
1366
1367 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
1368
1369 WLog_ERR(TAG, "Couldn't get socket ip address");
1370 return -1;
1371 }
1372 }
1373
1374 if (!ipcSocket && !useExternalDefinedSocket)
1375 {
1376 (void)freerdp_tcp_set_nodelay(WLog_Get(TAG), WLOG_ERROR, sockfd);
1377 }
1378
1379 /* receive buffer must be a least 32 K */
1380 UINT32 optval = 1;
1381 socklen_t optlen = sizeof(optval);
1382 if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (void*)&optval, &optlen) == 0)
1383 {
1384 if (optval < (1024 * 32))
1385 {
1386 optval = 1024 * 32;
1387 optlen = sizeof(optval);
1388
1389 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (void*)&optval, optlen) < 0)
1390 {
1391 close(sockfd);
1392
1393 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
1394
1395 WLog_ERR(TAG, "unable to set receive buffer len");
1396 return -1;
1397 }
1398 }
1399 }
1400
1401 if (!ipcSocket && !useExternalDefinedSocket)
1402 {
1403 if (!freerdp_tcp_set_keep_alive_mode(settings, sockfd))
1404 {
1405 close(sockfd);
1406
1407 freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_FAILED);
1408
1409 WLog_ERR(TAG, "Couldn't set keep alive mode.");
1410 return -1;
1411 }
1412 }
1413
1414 if (WaitForSingleObject(utils_get_abort_event(context->rdp), 0) == WAIT_OBJECT_0)
1415 {
1416 close(sockfd);
1417 return -1;
1418 }
1419
1420 return sockfd;
1421}
1422
1423struct rdp_tcp_layer
1424{
1425 int sockfd;
1426 HANDLE hEvent;
1427};
1428typedef struct rdp_tcp_layer rdpTcpLayer;
1429
1430static int freerdp_tcp_layer_read(void* userContext, void* data, int bytes)
1431{
1432 if (!userContext)
1433 return -1;
1434 if (!data || !bytes)
1435 return 0;
1436
1437 rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
1438
1439 (void)WSAResetEvent(tcpLayer->hEvent);
1440 const int status = _recv((SOCKET)tcpLayer->sockfd, data, bytes, 0);
1441
1442 if (status > 0)
1443 return status;
1444
1445 const int error = WSAGetLastError();
1446
1447 if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) || (error == WSAEINPROGRESS) ||
1448 (error == WSAEALREADY))
1449 errno = EAGAIN;
1450
1451 return status;
1452}
1453
1454static int freerdp_tcp_layer_write(void* userContext, const void* data, int bytes)
1455{
1456 if (!userContext)
1457 return -1;
1458 if (!data || !bytes)
1459 return 0;
1460
1461 rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
1462
1463 const int status = _send((SOCKET)tcpLayer->sockfd, data, bytes, 0);
1464 if (status > 0)
1465 return status;
1466
1467 const int error = WSAGetLastError();
1468
1469 if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) || (error == WSAEINPROGRESS) ||
1470 (error == WSAEALREADY))
1471 errno = EAGAIN;
1472
1473 return status;
1474}
1475
1476static BOOL freerdp_tcp_layer_close(void* userContext)
1477{
1478 if (!userContext)
1479 return FALSE;
1480
1481 rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
1482
1483 if (tcpLayer->sockfd >= 0)
1484 closesocket((SOCKET)tcpLayer->sockfd);
1485 if (tcpLayer->hEvent)
1486 (void)CloseHandle(tcpLayer->hEvent);
1487
1488 return TRUE;
1489}
1490
1491static BOOL freerdp_tcp_layer_wait(void* userContext, BOOL waitWrite, DWORD timeout)
1492{
1493 if (!userContext)
1494 return FALSE;
1495
1496 rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
1497
1498 int status = -1;
1499 int sockfd = tcpLayer->sockfd;
1500#ifdef WINPR_HAVE_POLL_H
1501 struct pollfd pollset = WINPR_C_ARRAY_INIT;
1502 pollset.fd = sockfd;
1503 pollset.events = waitWrite ? POLLOUT : POLLIN;
1504
1505 do
1506 {
1507 status = poll(&pollset, 1, (int)timeout);
1508 } while ((status < 0) && (errno == EINTR));
1509
1510#else
1511 fd_set rset = WINPR_C_ARRAY_INIT;
1512 struct timeval tv = WINPR_C_ARRAY_INIT;
1513 FD_ZERO(&rset);
1514 FD_SET(sockfd, &rset);
1515
1516 if (timeout)
1517 {
1518 tv.tv_sec = timeout / 1000;
1519 tv.tv_usec = (timeout % 1000) * 1000;
1520 }
1521
1522 do
1523 {
1524 if (waitWrite)
1525 status = select(sockfd + 1, nullptr, &rset, nullptr, timeout ? &tv : nullptr);
1526 else
1527 status = select(sockfd + 1, &rset, nullptr, nullptr, timeout ? &tv : nullptr);
1528 } while ((status < 0) && (errno == EINTR));
1529
1530#endif
1531
1532 return status != 0;
1533}
1534
1535static HANDLE freerdp_tcp_layer_get_event(void* userContext)
1536{
1537 if (!userContext)
1538 return nullptr;
1539
1540 rdpTcpLayer* tcpLayer = (rdpTcpLayer*)userContext;
1541
1542 return tcpLayer->hEvent;
1543}
1544
1545rdpTransportLayer* freerdp_tcp_connect_layer(rdpContext* context, const char* hostname, int port,
1546 DWORD timeout)
1547{
1548 WINPR_ASSERT(context);
1549
1550 const rdpSettings* settings = context->settings;
1551 WINPR_ASSERT(settings);
1552
1553 rdpTransportLayer* layer = nullptr;
1554 rdpTcpLayer* tcpLayer = nullptr;
1555
1556 int sockfd = freerdp_tcp_connect(context, hostname, port, timeout);
1557 if (sockfd < 0)
1558 goto fail;
1559 if (!freerdp_tcp_set_keep_alive_mode(settings, sockfd))
1560 goto fail;
1561
1562 layer = transport_layer_new(freerdp_get_transport(context), sizeof(rdpTcpLayer));
1563 if (!layer)
1564 goto fail;
1565
1566 layer->Read = freerdp_tcp_layer_read;
1567 layer->Write = freerdp_tcp_layer_write;
1568 layer->Close = freerdp_tcp_layer_close;
1569 layer->Wait = freerdp_tcp_layer_wait;
1570 layer->GetEvent = freerdp_tcp_layer_get_event;
1571
1572 tcpLayer = (rdpTcpLayer*)layer->userContext;
1573 WINPR_ASSERT(tcpLayer);
1574
1575 tcpLayer->sockfd = -1;
1576 tcpLayer->hEvent = WSACreateEvent();
1577 if (!tcpLayer->hEvent)
1578 goto fail;
1579
1580 /* WSAEventSelect automatically sets the socket in non-blocking mode */
1581 if (WSAEventSelect((SOCKET)sockfd, tcpLayer->hEvent, FD_READ | FD_ACCEPT | FD_CLOSE))
1582 {
1583 WLog_ERR(TAG, "WSAEventSelect returned 0x%08x", (unsigned)WSAGetLastError());
1584 goto fail;
1585 }
1586
1587 tcpLayer->sockfd = sockfd;
1588
1589 return layer;
1590
1591fail:
1592 if (sockfd >= 0)
1593 closesocket((SOCKET)sockfd);
1594 transport_layer_free(layer);
1595 return nullptr;
1596}
1597
1598BOOL freerdp_tcp_set_nodelay(wLog* log, DWORD level, int sockfd)
1599{
1600 WINPR_ASSERT(log);
1601
1602 int type = -1;
1603 socklen_t typelen = sizeof(type);
1604 char* ptype = (char*)&type;
1605 const int rc = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, ptype, &typelen);
1606 if (rc < 0)
1607 {
1608 char buffer[128] = WINPR_C_ARRAY_INIT;
1609 WLog_Print(log, level, "can't get SOL_SOCKET|SO_TYPE (%s)",
1610 winpr_strerror(errno, buffer, sizeof(buffer)));
1611 return FALSE;
1612 }
1613 else if (type == SOCK_STREAM)
1614 {
1615 int option_value = -1;
1616 const socklen_t option_len = sizeof(option_value);
1617 const int sr =
1618 setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void*)&option_value, option_len);
1619 if (sr < 0)
1620 {
1621 /* local unix sockets don't have the TCP_NODELAY implemented, so don't make this
1622 * error fatal */
1623 char buffer[128] = WINPR_C_ARRAY_INIT;
1624 WLog_Print(log, level, "can't set TCP_NODELAY (%s)",
1625 winpr_strerror(errno, buffer, sizeof(buffer)));
1626 return FALSE;
1627 }
1628 }
1629 else
1630 {
1631 WLog_Print(log, level, "Socket SOL_SOCKET|SO_TYPE %d unsupported", type);
1632 return FALSE;
1633 }
1634 return TRUE;
1635}
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
a piece of data in the ring buffer, exactly like a glibc iovec
Definition ringbuffer.h:44
ring buffer meta data
Definition ringbuffer.h:33