Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2022 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (net_socket.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
7 | * Permission is hereby granted, free of charge, | |
8 | * to any person obtaining a copy of this software and associated documentation files (the "Software"), | |
9 | * to deal in the Software without restriction, including without limitation the rights to | |
10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
11 | * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
16 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
21 | */ | |
22 | ||
23 | #include <stdlib.h> | |
24 | #include <stdio.h> | |
25 | #include <string.h> | |
26 | ||
27 | #ifdef _MSC_VER | |
28 | #include <compat/msvc.h> | |
29 | #endif | |
30 | ||
31 | #include <features/features_cpu.h> | |
32 | ||
33 | #include <net/net_socket.h> | |
34 | ||
35 | int socket_init(void **address, uint16_t port, const char *server, | |
36 | enum socket_type type, int family) | |
37 | { | |
38 | char port_buf[6]; | |
39 | struct addrinfo hints = {0}; | |
40 | struct addrinfo **addrinfo = (struct addrinfo**)address; | |
41 | struct addrinfo *addr = NULL; | |
42 | ||
43 | if (!family) | |
44 | #if defined(HAVE_SOCKET_LEGACY) || defined(WIIU) | |
45 | family = AF_INET; | |
46 | #else | |
47 | family = AF_UNSPEC; | |
48 | #endif | |
49 | ||
50 | hints.ai_family = family; | |
51 | ||
52 | switch (type) | |
53 | { | |
54 | case SOCKET_TYPE_DATAGRAM: | |
55 | hints.ai_socktype = SOCK_DGRAM; | |
56 | break; | |
57 | case SOCKET_TYPE_STREAM: | |
58 | hints.ai_socktype = SOCK_STREAM; | |
59 | break; | |
60 | default: | |
61 | return -1; | |
62 | } | |
63 | ||
64 | if (!server) | |
65 | hints.ai_flags = AI_PASSIVE; | |
66 | ||
67 | if (!network_init()) | |
68 | return -1; | |
69 | ||
70 | snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port); | |
71 | hints.ai_flags |= AI_NUMERICSERV; | |
72 | ||
73 | if (getaddrinfo_retro(server, port_buf, &hints, addrinfo)) | |
74 | return -1; | |
75 | ||
76 | addr = *addrinfo; | |
77 | if (!addr) | |
78 | return -1; | |
79 | ||
80 | return socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); | |
81 | } | |
82 | ||
83 | int socket_next(void **address) | |
84 | { | |
85 | struct addrinfo **addrinfo = (struct addrinfo**)address; | |
86 | struct addrinfo *addr = *addrinfo; | |
87 | ||
88 | if ((*addrinfo = addr = addr->ai_next)) | |
89 | return socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); | |
90 | ||
91 | return -1; | |
92 | } | |
93 | ||
94 | ssize_t socket_receive_all_nonblocking(int fd, bool *error, | |
95 | void *data_, size_t size) | |
96 | { | |
97 | ssize_t ret = recv(fd, (char*)data_, size, 0); | |
98 | ||
99 | if (ret > 0) | |
100 | return ret; | |
101 | ||
102 | if (ret < 0 && isagain((int)ret)) | |
103 | return 0; | |
104 | ||
105 | *error = true; | |
106 | return -1; | |
107 | } | |
108 | ||
109 | bool socket_receive_all_blocking(int fd, void *data_, size_t size) | |
110 | { | |
111 | const uint8_t *data = (const uint8_t*)data_; | |
112 | ||
113 | while (size) | |
114 | { | |
115 | ssize_t ret = recv(fd, (char*)data, size, 0); | |
116 | ||
117 | if (!ret) | |
118 | return false; | |
119 | ||
120 | if (ret < 0) | |
121 | { | |
122 | if (!isagain((int)ret)) | |
123 | return false; | |
124 | } | |
125 | else | |
126 | { | |
127 | data += ret; | |
128 | size -= ret; | |
129 | } | |
130 | } | |
131 | ||
132 | return true; | |
133 | } | |
134 | ||
135 | bool socket_receive_all_blocking_with_timeout(int fd, | |
136 | void *data_, size_t size, | |
137 | int timeout) | |
138 | { | |
139 | const uint8_t *data = (const uint8_t*)data_; | |
140 | retro_time_t deadline = cpu_features_get_time_usec(); | |
141 | ||
142 | if (timeout > 0) | |
143 | deadline += (retro_time_t)timeout * 1000; | |
144 | else | |
145 | deadline += 5000000; | |
146 | ||
147 | while (size) | |
148 | { | |
149 | ssize_t ret = recv(fd, (char*)data, size, 0); | |
150 | ||
151 | if (!ret) | |
152 | return false; | |
153 | ||
154 | if (ret < 0) | |
155 | { | |
156 | int _timeout; | |
157 | bool ready = true; | |
158 | ||
159 | if (!isagain((int)ret)) | |
160 | return false; | |
161 | ||
162 | _timeout = (int)((deadline - cpu_features_get_time_usec()) / 1000); | |
163 | if (_timeout <= 0) | |
164 | return false; | |
165 | ||
166 | if (!socket_wait(fd, &ready, NULL, _timeout) || !ready) | |
167 | return false; | |
168 | } | |
169 | else | |
170 | { | |
171 | data += ret; | |
172 | size -= ret; | |
173 | } | |
174 | } | |
175 | ||
176 | return true; | |
177 | } | |
178 | ||
179 | bool socket_set_block(int fd, bool block) | |
180 | { | |
181 | #if defined(_WIN32) | |
182 | u_long i = !block; | |
183 | ||
184 | return !ioctlsocket(fd, FIONBIO, &i); | |
185 | #elif defined(__PS3__) || defined(VITA) || defined(WIIU) | |
186 | int i = !block; | |
187 | ||
188 | return !setsockopt(fd, SOL_SOCKET, SO_NBIO, &i, sizeof(i)); | |
189 | #elif defined(GEKKO) | |
190 | u32 i = !block; | |
191 | ||
192 | return !net_ioctl(fd, FIONBIO, &i); | |
193 | #else | |
194 | int flags = fcntl(fd, F_GETFL); | |
195 | ||
196 | if (block) | |
197 | flags &= ~O_NONBLOCK; | |
198 | else | |
199 | flags |= O_NONBLOCK; | |
200 | ||
201 | return !fcntl(fd, F_SETFL, flags); | |
202 | #endif | |
203 | } | |
204 | ||
205 | bool socket_nonblock(int fd) | |
206 | { | |
207 | return socket_set_block(fd, false); | |
208 | } | |
209 | ||
210 | int socket_close(int fd) | |
211 | { | |
212 | #if defined(_WIN32) && !defined(_XBOX360) | |
213 | /* WinSock has headers from the stone age. */ | |
214 | return closesocket(fd); | |
215 | #elif defined(__PS3__) || defined(WIIU) | |
216 | return socketclose(fd); | |
217 | #elif defined(VITA) | |
218 | return sceNetSocketClose(fd); | |
219 | #else | |
220 | return close(fd); | |
221 | #endif | |
222 | } | |
223 | ||
224 | int socket_select(int nfds, fd_set *readfds, fd_set *writefds, | |
225 | fd_set *errorfds, struct timeval *timeout) | |
226 | { | |
227 | #if defined(__PS3__) | |
228 | return socketselect(nfds, readfds, writefds, errorfds, timeout); | |
229 | #elif defined(VITA) | |
230 | int i, j; | |
231 | fd_set rfds, wfds, efds; | |
232 | int epoll_fd; | |
233 | SceNetEpollEvent *events = NULL; | |
234 | int event_count = 0; | |
235 | int timeout_us = -1; | |
236 | int ret = -1; | |
237 | ||
238 | if (nfds < 0 || nfds > 1024) | |
239 | return SCE_NET_ERROR_EINVAL; | |
240 | if (timeout && (timeout->tv_sec < 0 || timeout->tv_usec < 0)) | |
241 | return SCE_NET_ERROR_EINVAL; | |
242 | ||
243 | epoll_fd = sceNetEpollCreate("socket_select", 0); | |
244 | if (epoll_fd < 0) | |
245 | return SCE_NET_ERROR_ENOMEM; | |
246 | ||
247 | FD_ZERO(&rfds); | |
248 | FD_ZERO(&wfds); | |
249 | FD_ZERO(&efds); | |
250 | ||
251 | for (i = 0; i < nfds; i++) | |
252 | { | |
253 | if (readfds && FD_ISSET(i, readfds)) | |
254 | event_count++; | |
255 | else if (writefds && FD_ISSET(i, writefds)) | |
256 | event_count++; | |
257 | else if (errorfds && FD_ISSET(i, errorfds)) | |
258 | event_count++; | |
259 | } | |
260 | ||
261 | #define ALLOC_EVENTS(count) \ | |
262 | events = (SceNetEpollEvent*)calloc((count), sizeof(*events)); \ | |
263 | if (!events) \ | |
264 | { \ | |
265 | ret = SCE_NET_ERROR_ENOMEM; \ | |
266 | goto done; \ | |
267 | } | |
268 | ||
269 | if (event_count) | |
270 | { | |
271 | ALLOC_EVENTS(event_count) | |
272 | ||
273 | for (i = 0, j = 0; i < nfds && j < event_count; i++) | |
274 | { | |
275 | SceNetEpollEvent *event = &events[j]; | |
276 | ||
277 | if (readfds && FD_ISSET(i, readfds)) | |
278 | event->events |= SCE_NET_EPOLLIN; | |
279 | if (writefds && FD_ISSET(i, writefds)) | |
280 | event->events |= SCE_NET_EPOLLOUT; | |
281 | ||
282 | if (event->events || (errorfds && FD_ISSET(i, errorfds))) | |
283 | { | |
284 | event->data.fd = i; | |
285 | ||
286 | ret = sceNetEpollControl(epoll_fd, SCE_NET_EPOLL_CTL_ADD, | |
287 | i, event); | |
288 | if (ret < 0) | |
289 | { | |
290 | switch (ret) | |
291 | { | |
292 | case SCE_NET_ERROR_EBADF: | |
293 | case SCE_NET_ERROR_ENOMEM: | |
294 | break; | |
295 | default: | |
296 | ret = SCE_NET_ERROR_EBADF; | |
297 | break; | |
298 | } | |
299 | goto done; | |
300 | } | |
301 | ||
302 | j++; | |
303 | } | |
304 | } | |
305 | ||
306 | memset(events, 0, event_count * sizeof(*events)); | |
307 | ||
308 | /* Keep a copy of the original sets for lookup later. */ | |
309 | if (readfds) | |
310 | memcpy(&rfds, readfds, sizeof(rfds)); | |
311 | if (writefds) | |
312 | memcpy(&wfds, writefds, sizeof(wfds)); | |
313 | if (errorfds) | |
314 | memcpy(&efds, errorfds, sizeof(efds)); | |
315 | } | |
316 | else | |
317 | { | |
318 | /* Necessary to work with epoll wait. */ | |
319 | event_count = 1; | |
320 | ALLOC_EVENTS(1) | |
321 | } | |
322 | ||
323 | #undef ALLOC_EVENTS | |
324 | ||
325 | if (readfds) | |
326 | FD_ZERO(readfds); | |
327 | if (writefds) | |
328 | FD_ZERO(writefds); | |
329 | if (errorfds) | |
330 | FD_ZERO(errorfds); | |
331 | ||
332 | /* Vita's epoll takes a microsecond timeout parameter. */ | |
333 | if (timeout) | |
334 | timeout_us = (int)(timeout->tv_usec + (timeout->tv_sec * 1000000)); | |
335 | ||
336 | ret = sceNetEpollWait(epoll_fd, events, event_count, timeout_us); | |
337 | if (ret <= 0) | |
338 | goto done; | |
339 | ||
340 | #define EPOLL_FD_SET(op, in_set, out_set) \ | |
341 | if ((event->events & (op)) && FD_ISSET(event->data.fd, (in_set))) \ | |
342 | { \ | |
343 | FD_SET(event->data.fd, (out_set)); \ | |
344 | j++; \ | |
345 | } | |
346 | ||
347 | for (i = 0, j = 0; i < ret; i++) | |
348 | { | |
349 | SceNetEpollEvent *event = &events[i]; | |
350 | ||
351 | /* Sanity check */ | |
352 | if (event->data.fd < 0 || event->data.fd >= nfds) | |
353 | continue; | |
354 | ||
355 | EPOLL_FD_SET(SCE_NET_EPOLLIN, &rfds, readfds) | |
356 | EPOLL_FD_SET(SCE_NET_EPOLLOUT, &wfds, writefds) | |
357 | EPOLL_FD_SET(SCE_NET_EPOLLERR, &efds, errorfds) | |
358 | } | |
359 | ||
360 | ret = j; | |
361 | ||
362 | #undef EPOLL_FD_SET | |
363 | ||
364 | done: | |
365 | free(events); | |
366 | sceNetEpollDestroy(epoll_fd); | |
367 | ||
368 | return ret; | |
369 | #else | |
370 | return select(nfds, readfds, writefds, errorfds, timeout); | |
371 | #endif | |
372 | } | |
373 | ||
374 | #ifdef NETWORK_HAVE_POLL | |
375 | int socket_poll(struct pollfd *fds, unsigned nfds, int timeout) | |
376 | { | |
377 | #if defined(_WIN32) | |
378 | return WSAPoll(fds, nfds, timeout); | |
379 | #elif defined(VITA) | |
380 | int i, j; | |
381 | int epoll_fd; | |
382 | SceNetEpollEvent *events = NULL; | |
383 | int event_count = (int)nfds; | |
384 | int ret = -1; | |
385 | ||
386 | if (event_count < 0) | |
387 | return SCE_NET_ERROR_EINVAL; | |
388 | ||
389 | epoll_fd = sceNetEpollCreate("socket_poll", 0); | |
390 | if (epoll_fd < 0) | |
391 | return SCE_NET_ERROR_ENOMEM; | |
392 | ||
393 | #define ALLOC_EVENTS(count) \ | |
394 | events = (SceNetEpollEvent*)calloc((count), sizeof(*events)); \ | |
395 | if (!events) \ | |
396 | { \ | |
397 | ret = SCE_NET_ERROR_ENOMEM; \ | |
398 | goto done; \ | |
399 | } | |
400 | ||
401 | if (event_count) | |
402 | { | |
403 | ALLOC_EVENTS(event_count) | |
404 | ||
405 | for (i = 0; i < event_count; i++) | |
406 | { | |
407 | struct pollfd *fd = &fds[i]; | |
408 | SceNetEpollEvent *event = &events[i]; | |
409 | ||
410 | fd->revents = 0; | |
411 | ||
412 | if (fd->fd < 0) | |
413 | continue; | |
414 | ||
415 | event->events = fd->events; | |
416 | event->data.fd = fd->fd; | |
417 | ||
418 | ret = sceNetEpollControl(epoll_fd, SCE_NET_EPOLL_CTL_ADD, | |
419 | fd->fd, event); | |
420 | if (ret < 0) | |
421 | goto done; | |
422 | } | |
423 | ||
424 | memset(events, 0, event_count * sizeof(*events)); | |
425 | } | |
426 | else | |
427 | { | |
428 | /* Necessary to work with epoll wait. */ | |
429 | event_count = 1; | |
430 | ALLOC_EVENTS(1) | |
431 | } | |
432 | ||
433 | #undef ALLOC_EVENTS | |
434 | ||
435 | /* Vita's epoll takes a microsecond timeout parameter. */ | |
436 | if (timeout > 0) | |
437 | timeout *= 1000; | |
438 | ||
439 | ret = sceNetEpollWait(epoll_fd, events, event_count, timeout); | |
440 | if (ret <= 0) | |
441 | goto done; | |
442 | ||
443 | for (i = 0, j = 0; i < ret; i++) | |
444 | { | |
445 | unsigned k; | |
446 | SceNetEpollEvent *event = &events[i]; | |
447 | ||
448 | /* Sanity check */ | |
449 | if (event->data.fd < 0) | |
450 | continue; | |
451 | ||
452 | for (k = 0; k < nfds; k++) | |
453 | { | |
454 | struct pollfd *fd = &fds[k]; | |
455 | ||
456 | if (fd->fd == event->data.fd) | |
457 | { | |
458 | fd->revents = event->events; | |
459 | j++; | |
460 | break; | |
461 | } | |
462 | } | |
463 | } | |
464 | ||
465 | ret = j; | |
466 | ||
467 | done: | |
468 | free(events); | |
469 | sceNetEpollDestroy(epoll_fd); | |
470 | ||
471 | return ret; | |
472 | #elif defined(_3DS) | |
473 | int i; | |
474 | int timeout_quotient; | |
475 | int timeout_remainder; | |
476 | int ret = -1; | |
477 | ||
478 | #define TIMEOUT_DIVISOR 100 | |
479 | if (timeout <= TIMEOUT_DIVISOR) | |
480 | return poll(fds, nfds, timeout); | |
481 | ||
482 | timeout_quotient = timeout / TIMEOUT_DIVISOR; | |
483 | for (i = 0; i < timeout_quotient; i++) | |
484 | { | |
485 | ret = poll(fds, nfds, TIMEOUT_DIVISOR); | |
486 | ||
487 | /* Success or error. */ | |
488 | if (ret) | |
489 | return ret; | |
490 | } | |
491 | ||
492 | timeout_remainder = timeout % TIMEOUT_DIVISOR; | |
493 | if (timeout_remainder) | |
494 | ret = poll(fds, nfds, timeout_remainder); | |
495 | ||
496 | return ret; | |
497 | #undef TIMEOUT_DIVISOR | |
498 | ||
499 | #elif defined(GEKKO) | |
500 | return net_poll(fds, nfds, timeout); | |
501 | #else | |
502 | return poll(fds, nfds, timeout); | |
503 | #endif | |
504 | } | |
505 | #endif | |
506 | ||
507 | bool socket_wait(int fd, bool *rd, bool *wr, int timeout) | |
508 | { | |
509 | #ifdef NETWORK_HAVE_POLL | |
510 | struct pollfd fds = {0}; | |
511 | ||
512 | NET_POLL_FD(fd, &fds); | |
513 | ||
514 | if (rd && *rd) | |
515 | { | |
516 | NET_POLL_EVENT(POLLIN, &fds); | |
517 | *rd = false; | |
518 | } | |
519 | if (wr && *wr) | |
520 | { | |
521 | NET_POLL_EVENT(POLLOUT, &fds); | |
522 | *wr = false; | |
523 | } | |
524 | ||
525 | if (socket_poll(&fds, 1, timeout) < 0) | |
526 | return false; | |
527 | ||
528 | if (rd && NET_POLL_HAS_EVENT(POLLIN, &fds)) | |
529 | *rd = true; | |
530 | if (wr && NET_POLL_HAS_EVENT(POLLOUT, &fds)) | |
531 | *wr = true; | |
532 | ||
533 | return !NET_POLL_HAS_EVENT((POLLERR | POLLNVAL), &fds); | |
534 | #else | |
535 | fd_set rfd, wfd, efd; | |
536 | struct timeval tv, *ptv = NULL; | |
537 | ||
538 | FD_ZERO(&rfd); | |
539 | FD_ZERO(&wfd); | |
540 | FD_ZERO(&efd); | |
541 | ||
542 | if (rd && *rd) | |
543 | { | |
544 | FD_SET(fd, &rfd); | |
545 | *rd = false; | |
546 | } | |
547 | if (wr && *wr) | |
548 | { | |
549 | FD_SET(fd, &wfd); | |
550 | *wr = false; | |
551 | } | |
552 | FD_SET(fd, &efd); | |
553 | ||
554 | if (timeout >= 0) | |
555 | { | |
556 | tv.tv_sec = (unsigned)timeout / 1000; | |
557 | tv.tv_usec = ((unsigned)timeout % 1000) * 1000; | |
558 | ptv = &tv; | |
559 | } | |
560 | ||
561 | if (socket_select(fd + 1, &rfd, &wfd, &efd, ptv) < 0) | |
562 | return false; | |
563 | ||
564 | if (rd && FD_ISSET(fd, &rfd)) | |
565 | *rd = true; | |
566 | if (wr && FD_ISSET(fd, &wfd)) | |
567 | *wr = true; | |
568 | ||
569 | return !FD_ISSET(fd, &efd); | |
570 | #endif | |
571 | } | |
572 | ||
573 | bool socket_send_all_blocking(int fd, const void *data_, size_t size, | |
574 | bool no_signal) | |
575 | { | |
576 | const uint8_t *data = (const uint8_t*)data_; | |
577 | int flags = no_signal ? MSG_NOSIGNAL : 0; | |
578 | ||
579 | while (size) | |
580 | { | |
581 | ssize_t ret = send(fd, (const char*)data, size, flags); | |
582 | ||
583 | if (!ret) | |
584 | continue; | |
585 | ||
586 | if (ret < 0) | |
587 | { | |
588 | if (!isagain((int)ret)) | |
589 | return false; | |
590 | } | |
591 | else | |
592 | { | |
593 | data += ret; | |
594 | size -= ret; | |
595 | } | |
596 | } | |
597 | ||
598 | return true; | |
599 | } | |
600 | ||
601 | bool socket_send_all_blocking_with_timeout(int fd, | |
602 | const void *data_, size_t size, | |
603 | int timeout, bool no_signal) | |
604 | { | |
605 | const uint8_t *data = (const uint8_t*)data_; | |
606 | int flags = no_signal ? MSG_NOSIGNAL : 0; | |
607 | retro_time_t deadline = cpu_features_get_time_usec(); | |
608 | ||
609 | if (timeout > 0) | |
610 | deadline += (retro_time_t)timeout * 1000; | |
611 | else | |
612 | deadline += 5000000; | |
613 | ||
614 | while (size) | |
615 | { | |
616 | ssize_t ret = send(fd, (const char*)data, size, flags); | |
617 | ||
618 | if (!ret) | |
619 | continue; | |
620 | ||
621 | if (ret < 0) | |
622 | { | |
623 | int _timeout; | |
624 | bool ready = true; | |
625 | ||
626 | if (!isagain((int)ret)) | |
627 | return false; | |
628 | ||
629 | _timeout = (int)((deadline - cpu_features_get_time_usec()) / 1000); | |
630 | if (_timeout <= 0) | |
631 | return false; | |
632 | ||
633 | if (!socket_wait(fd, NULL, &ready, _timeout) || !ready) | |
634 | return false; | |
635 | } | |
636 | else | |
637 | { | |
638 | data += ret; | |
639 | size -= ret; | |
640 | } | |
641 | } | |
642 | ||
643 | return true; | |
644 | } | |
645 | ||
646 | ssize_t socket_send_all_nonblocking(int fd, const void *data_, size_t size, | |
647 | bool no_signal) | |
648 | { | |
649 | const uint8_t *data = (const uint8_t*)data_; | |
650 | int flags = no_signal ? MSG_NOSIGNAL : 0; | |
651 | ||
652 | while (size) | |
653 | { | |
654 | ssize_t ret = send(fd, (const char*)data, size, flags); | |
655 | ||
656 | if (!ret) | |
657 | break; | |
658 | ||
659 | if (ret < 0) | |
660 | { | |
661 | if (isagain((int)ret)) | |
662 | break; | |
663 | ||
664 | return -1; | |
665 | } | |
666 | else | |
667 | { | |
668 | data += ret; | |
669 | size -= ret; | |
670 | } | |
671 | } | |
672 | ||
673 | return (ssize_t)((size_t)data - (size_t)data_); | |
674 | } | |
675 | ||
676 | bool socket_bind(int fd, void *data) | |
677 | { | |
678 | struct addrinfo *addr = (struct addrinfo*)data; | |
679 | ||
680 | { | |
681 | int on = 1; | |
682 | ||
683 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, | |
684 | (char*)&on, sizeof(on)); | |
685 | } | |
686 | ||
687 | return !bind(fd, addr->ai_addr, addr->ai_addrlen); | |
688 | } | |
689 | ||
690 | int socket_connect(int fd, void *data) | |
691 | { | |
692 | struct addrinfo *addr = (struct addrinfo*)data; | |
693 | ||
694 | #ifdef WIIU | |
695 | { | |
696 | int op = 1; | |
697 | ||
698 | setsockopt(fd, SOL_SOCKET, SO_WINSCALE, &op, sizeof(op)); | |
699 | ||
700 | if (addr->ai_socktype == SOCK_STREAM) | |
701 | { | |
702 | int recvsz = WIIU_RCVBUF; | |
703 | int sendsz = WIIU_SNDBUF; | |
704 | ||
705 | setsockopt(fd, SOL_SOCKET, SO_TCPSACK, &op, sizeof(op)); | |
706 | setsockopt(fd, SOL_SOCKET, SO_RUSRBUF, &op, sizeof(op)); | |
707 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvsz, sizeof(recvsz)); | |
708 | setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendsz, sizeof(sendsz)); | |
709 | } | |
710 | } | |
711 | #endif | |
712 | ||
713 | return connect(fd, addr->ai_addr, addr->ai_addrlen); | |
714 | } | |
715 | ||
716 | bool socket_connect_with_timeout(int fd, void *data, int timeout) | |
717 | { | |
718 | int res; | |
719 | struct addrinfo *addr = (struct addrinfo*)data; | |
720 | ||
721 | if (!socket_nonblock(fd)) | |
722 | return false; | |
723 | ||
724 | #ifdef WIIU | |
725 | { | |
726 | int op = 1; | |
727 | ||
728 | setsockopt(fd, SOL_SOCKET, SO_WINSCALE, &op, sizeof(op)); | |
729 | ||
730 | if (addr->ai_socktype == SOCK_STREAM) | |
731 | { | |
732 | int recvsz = WIIU_RCVBUF; | |
733 | int sendsz = WIIU_SNDBUF; | |
734 | ||
735 | setsockopt(fd, SOL_SOCKET, SO_TCPSACK, &op, sizeof(op)); | |
736 | setsockopt(fd, SOL_SOCKET, SO_RUSRBUF, &op, sizeof(op)); | |
737 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvsz, sizeof(recvsz)); | |
738 | setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendsz, sizeof(sendsz)); | |
739 | } | |
740 | } | |
741 | #endif | |
742 | ||
743 | res = connect(fd, addr->ai_addr, addr->ai_addrlen); | |
744 | if (res) | |
745 | { | |
746 | bool ready = true; | |
747 | ||
748 | if (!isinprogress(res) && !isagain(res)) | |
749 | return false; | |
750 | ||
751 | if (timeout <= 0) | |
752 | timeout = 5000; | |
753 | ||
754 | if (!socket_wait(fd, NULL, &ready, timeout) || !ready) | |
755 | return false; | |
756 | } | |
757 | ||
758 | #if defined(GEKKO) | |
759 | /* libogc does not have getsockopt implemented */ | |
760 | res = connect(fd, addr->ai_addr, addr->ai_addrlen); | |
761 | if (res < 0 && -res != EISCONN) | |
762 | return false; | |
763 | #elif defined(_3DS) | |
764 | /* libctru getsockopt does not return expected value */ | |
765 | if ((connect(fd, addr->ai_addr, addr->ai_addrlen) < 0) && errno != EISCONN) | |
766 | return false; | |
767 | #elif defined(WIIU) | |
768 | /* On WiiU, getsockopt() returns -1 and sets lastsocketerr() (Wii's | |
769 | * equivalent to errno) to 16. */ | |
770 | if ((connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) | |
771 | && socketlasterr() != SO_EISCONN) | |
772 | return false; | |
773 | #else | |
774 | { | |
775 | int error = -1; | |
776 | socklen_t errsz = sizeof(error); | |
777 | ||
778 | getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&error, &errsz); | |
779 | if (error) | |
780 | return false; | |
781 | } | |
782 | #endif | |
783 | ||
784 | return true; | |
785 | } | |
786 | ||
787 | int socket_create( | |
788 | const char *name, | |
789 | enum socket_domain domain_type, | |
790 | enum socket_type socket_type, | |
791 | enum socket_protocol protocol_type) | |
792 | { | |
793 | int domain = 0; | |
794 | int type = 0; | |
795 | int protocol = 0; | |
796 | ||
797 | switch (domain_type) | |
798 | { | |
799 | case SOCKET_DOMAIN_INET: | |
800 | domain = AF_INET; | |
801 | break; | |
802 | default: | |
803 | break; | |
804 | } | |
805 | ||
806 | switch (socket_type) | |
807 | { | |
808 | case SOCKET_TYPE_DATAGRAM: | |
809 | type = SOCK_DGRAM; | |
810 | break; | |
811 | case SOCKET_TYPE_STREAM: | |
812 | type = SOCK_STREAM; | |
813 | break; | |
814 | case SOCKET_TYPE_SEQPACKET: | |
815 | default: | |
816 | /* TODO/FIXME - implement */ | |
817 | break; | |
818 | } | |
819 | ||
820 | switch (protocol_type) | |
821 | { | |
822 | case SOCKET_PROTOCOL_TCP: | |
823 | protocol = IPPROTO_TCP; | |
824 | break; | |
825 | case SOCKET_PROTOCOL_UDP: | |
826 | protocol = IPPROTO_UDP; | |
827 | break; | |
828 | case SOCKET_PROTOCOL_NONE: | |
829 | default: | |
830 | break; | |
831 | } | |
832 | ||
833 | #ifdef VITA | |
834 | return sceNetSocket(name, domain, type, protocol); | |
835 | #else | |
836 | return socket(domain, type, protocol); | |
837 | #endif | |
838 | } | |
839 | ||
840 | void socket_set_target(void *data, socket_target_t *in_addr) | |
841 | { | |
842 | struct sockaddr_in *out_target = (struct sockaddr_in*)data; | |
843 | ||
844 | #ifdef GEKKO | |
845 | out_target->sin_len = 8; | |
846 | #endif | |
847 | switch (in_addr->domain) | |
848 | { | |
849 | case SOCKET_DOMAIN_INET: | |
850 | out_target->sin_family = AF_INET; | |
851 | break; | |
852 | default: | |
853 | out_target->sin_family = 0; | |
854 | break; | |
855 | } | |
856 | out_target->sin_port = htons(in_addr->port); | |
857 | inet_pton(AF_INET, in_addr->server, &out_target->sin_addr); | |
858 | } |