1 /* sim_sock.c: OS-dependent socket routines
2
3 Copyright (c) 2001-2019, Robert M Supnik
4
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
25
26 22-Nov-19 MP Latest 4.X changes
27 15-Oct-12 MP Added definitions needed to detect possible tcp
28 connect failures
29 25-Sep-12 MP Reworked for RFC3493 interfaces supporting IPv6 and IPv4
30 22-Jun-10 RMS Fixed types in sim_accept_conn (from Mark Pizzolato)
31 19-Nov-05 RMS Added conditional for OpenBSD (from Federico G. Schwindt)
32 16-Aug-05 RMS Fixed spurious SIGPIPE signal error in Unix
33 14-Apr-05 RMS Added WSAEINPROGRESS test (from Tim Riker)
34 09-Jan-04 RMS Fixed typing problem in Alpha Unix (found by Tim Chapman)
35 17-Apr-03 RMS Fixed non-implemented version of sim_close_sock
36 (found by Mark Pizzolato)
37 17-Dec-02 RMS Added sim_connect_socket, sim_create_socket
38 08-Oct-02 RMS Revised for .NET compatibility
39 22-Aug-02 RMS Changed calling sequence for sim_accept_conn
40 22-May-02 RMS Added OS2 EMX support from Holger Veit
41 06-Feb-02 RMS Added VMS support from Robert Alan Byer
42 16-Sep-01 RMS Added Macintosh support from Peter Schorn
43 02-Sep-01 RMS Fixed UNIX bugs found by Mirian Lennox and Tom Markson
44 */
45
46 #ifdef __cplusplus
47 extern "C" {
48 #endif
49
50 #include "sim_sock.h"
51 #include <signal.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54
55 #if defined(AF_INET6) && defined(_WIN32)
56 #include <ws2tcpip.h>
57 #endif
58
59 #ifdef HAVE_DLOPEN
60 #include <dlfcn.h>
61 #endif
62
63 #ifndef WSAAPI
64 #define WSAAPI
65 #endif
66
67 #if defined(SHUT_RDWR) && !defined(SD_BOTH)
68 #define SD_BOTH SHUT_RDWR
69 #endif
70
71 #ifndef NI_MAXHOST
72 #define NI_MAXHOST 1025
73 #endif
74
75 /* OS dependent routines
76
77 sim_master_sock create master socket
78 sim_connect_sock connect a socket to a remote destination
79 sim_connect_sock_ex connect a socket to a remote destination
80 sim_accept_conn accept connection
81 sim_read_sock read from socket
82 sim_write_sock write from socket
83 sim_close_sock close socket
84 sim_setnonblock set socket non-blocking
85 */
86
87 /* First, all the non-implemented versions */
88
89 #if defined (__OS2__) && !defined (__EMX__)
90
sim_init_sock(void)91 void sim_init_sock (void)
92 {
93 }
94
sim_cleanup_sock(void)95 void sim_cleanup_sock (void)
96 {
97 }
98
sim_master_sock_ex(const char * hostport,int * parse_status,int opt_flags)99 SOCKET sim_master_sock_ex (const char *hostport, int *parse_status, int opt_flags)
100 {
101 return INVALID_SOCKET;
102 }
103
sim_connect_sock_ex(const char * sourcehostport,const char * hostport,const char * default_host,const char * default_port,int opt_flags)104 SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, int opt_flags)
105 {
106 return INVALID_SOCKET;
107 }
108
109 SOCKET sim_accept_conn (SOCKET master, char **connectaddr);
110 {
111 return INVALID_SOCKET;
112 }
113
sim_read_sock(SOCKET sock,char * buf,int nbytes)114 int sim_read_sock (SOCKET sock, char *buf, int nbytes)
115 {
116 return -1;
117 }
118
sim_write_sock(SOCKET sock,char * msg,int nbytes)119 int sim_write_sock (SOCKET sock, char *msg, int nbytes)
120 {
121 return 0;
122 }
123
sim_close_sock(SOCKET sock)124 void sim_close_sock (SOCKET sock)
125 {
126 return;
127 }
128
129 #else /* endif unimpl */
130
131 /* UNIX, Win32, Macintosh, VMS, OS2 (Berkeley socket) routines */
132
133 static struct sock_errors {
134 int value;
135 const char *text;
136 } sock_errors[] = {
137 {WSAEWOULDBLOCK, "Operation would block"},
138 {WSAENAMETOOLONG, "File name too long"},
139 {WSAEINPROGRESS, "Operation now in progress "},
140 {WSAETIMEDOUT, "Connection timed out"},
141 {WSAEISCONN, "Transport endpoint is already connected"},
142 {WSAECONNRESET, "Connection reset by peer"},
143 {WSAECONNREFUSED, "Connection refused"},
144 {WSAECONNABORTED, "Connection aborted"},
145 {WSAEHOSTUNREACH, "No route to host"},
146 {WSAEADDRINUSE, "Address already in use"},
147 #if defined (WSAEAFNOSUPPORT)
148 {WSAEAFNOSUPPORT, "Address family not supported by protocol"},
149 #endif
150 {WSAEACCES, "Permission denied"},
151 {0, NULL}
152 };
153
154
155 const char *sim_get_err_sock (const char *emsg)
156 {
157 int err = WSAGetLastError ();
158 int i;
159 static char err_buf[512];
160
161 for (i=0; (sock_errors[i].text) && (sock_errors[i].value != err); i++)
162 ;
163 if (sock_errors[i].value == err)
164 sprintf (err_buf, "Sockets: %s error %d - %s\n", emsg, err, sock_errors[i].text);
165 else
166 #if defined(_WIN32)
167 sprintf (err_buf, "Sockets: %s error %d\n", emsg, err);
168 #else
169 sprintf (err_buf, "Sockets: %s error %d - %s\n", emsg, err, strerror(err));
170 #endif
171 return err_buf;
172 }
173
174 SOCKET sim_err_sock (SOCKET s, const char *emsg)
175 {
176 sim_printf ("%s", sim_get_err_sock (emsg));
177 if (s != INVALID_SOCKET) {
178 int err = WSAGetLastError ();
179 sim_close_sock (s);
180 WSASetLastError (err); /* Retain Original socket error value */
181 }
182 return INVALID_SOCKET;
183 }
184
185 typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo *ai);
186 static freeaddrinfo_func p_freeaddrinfo;
187
188 typedef int (WSAAPI *getaddrinfo_func) (const char *hostname,
189 const char *service,
190 const struct addrinfo *hints,
191 struct addrinfo **res);
192 static getaddrinfo_func p_getaddrinfo;
193
194 #if defined(VMS)
195 typedef size_t socklen_t;
196 #if !defined(EAI_OVERFLOW)
197 #define EAI_OVERFLOW EAI_FAIL
198 #endif
199 #endif
200
201 #if defined(__hpux)
202 #if !defined(EAI_OVERFLOW)
203 #define EAI_OVERFLOW EAI_FAIL
204 #endif
205 #endif
206
207 typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags);
208 static getnameinfo_func p_getnameinfo;
209
210 static void WSAAPI s_freeaddrinfo (struct addrinfo *ai)
211 {
212 struct addrinfo *a, *an;
213
214 for (a=ai; a != NULL; a=an) {
215 an = a->ai_next;
216 free (a->ai_canonname);
217 free (a->ai_addr);
218 free (a);
219 }
220 }
221
222 static int WSAAPI s_getaddrinfo (const char *hostname,
223 const char *service,
224 const struct addrinfo *hints,
225 struct addrinfo **res)
226 {
227 struct hostent *he;
228 struct servent *se = NULL;
229 struct sockaddr_in *sin;
230 struct addrinfo *result = NULL;
231 struct addrinfo *ai, *lai = NULL;
232 struct addrinfo dhints;
233 struct in_addr ipaddr;
234 struct in_addr *fixed[2];
235 struct in_addr **ips = NULL;
236 struct in_addr **ip;
237 const char *cname = NULL;
238 int port = 0;
239
240 // Validate parameters
241 if ((hostname == NULL) && (service == NULL))
242 return EAI_NONAME;
243
244 if (hints) {
245 if ((hints->ai_family != PF_INET) && (hints->ai_family != PF_UNSPEC))
246 return EAI_FAMILY;
247 switch (hints->ai_socktype)
248 {
249 default:
250 return EAI_SOCKTYPE;
251 case SOCK_DGRAM:
252 case SOCK_STREAM:
253 case 0:
254 break;
255 }
256 }
257 else {
258 hints = &dhints;
259 memset(&dhints, 0, sizeof(dhints));
260 dhints.ai_family = PF_UNSPEC;
261 }
262 if (service) {
263 char *c;
264
265 port = strtoul(service, &c, 10);
266 if ((port == 0) || (*c != '\0')) {
267 switch (hints->ai_socktype)
268 {
269 case SOCK_DGRAM:
270 se = getservbyname(service, "udp");
271 break;
272 case SOCK_STREAM:
273 case 0:
274 se = getservbyname(service, "tcp");
275 break;
276 }
277 if (NULL == se)
278 return EAI_SERVICE;
279 port = se->s_port;
280 }
281 }
282
283 if (hostname) {
284 if ((0xffffffff != (ipaddr.s_addr = inet_addr(hostname))) ||
285 (0 == strcmp("255.255.255.255", hostname))) {
286 fixed[0] = &ipaddr;
287 fixed[1] = NULL;
288 if ((hints->ai_flags & AI_CANONNAME) && !(hints->ai_flags & AI_NUMERICHOST)) {
289 he = gethostbyaddr((char *)&ipaddr, 4, AF_INET);
290 if (NULL != he)
291 cname = he->h_name;
292 else
293 cname = hostname;
294 }
295 ips = fixed;
296 }
297 else {
298 if (hints->ai_flags & AI_NUMERICHOST)
299 return EAI_NONAME;
300 he = gethostbyname(hostname);
301 if (he) {
302 ips = (struct in_addr **)he->h_addr_list;
303 if (hints->ai_flags & AI_CANONNAME)
304 cname = he->h_name;
305 }
306 else {
307 switch (h_errno)
308 {
309 case HOST_NOT_FOUND:
310 case NO_DATA:
311 return EAI_NONAME;
312 case TRY_AGAIN:
313 return EAI_AGAIN;
314 default:
315 return EAI_FAIL;
316 }
317 }
318 }
319 }
320 else {
321 if (hints->ai_flags & AI_PASSIVE)
322 ipaddr.s_addr = htonl(INADDR_ANY);
323 else
324 ipaddr.s_addr = htonl(INADDR_LOOPBACK);
325 fixed[0] = &ipaddr;
326 fixed[1] = NULL;
327 ips = fixed;
328 }
329 for (ip=ips; (ip != NULL) && (*ip != NULL); ++ip) {
330 ai = (struct addrinfo *)calloc(1, sizeof(*ai));
331 if (NULL == ai) {
332 s_freeaddrinfo(result);
333 return EAI_MEMORY;
334 }
335 ai->ai_family = PF_INET;
336 ai->ai_socktype = hints->ai_socktype;
337 ai->ai_protocol = hints->ai_protocol;
338 ai->ai_addr = NULL;
339 ai->ai_addrlen = sizeof(struct sockaddr_in);
340 ai->ai_canonname = NULL;
341 ai->ai_next = NULL;
342 ai->ai_addr = (struct sockaddr *)calloc(1, sizeof(struct sockaddr_in));
343 if (NULL == ai->ai_addr) {
344 free(ai);
345 s_freeaddrinfo(result);
346 return EAI_MEMORY;
347 }
348 sin = (struct sockaddr_in *)ai->ai_addr;
349 sin->sin_family = PF_INET;
350 sin->sin_port = (unsigned short)port;
351 memcpy(&sin->sin_addr, *ip, sizeof(sin->sin_addr));
352 if (NULL == result)
353 result = ai;
354 else
355 lai->ai_next = ai;
356 lai = ai;
357 }
358 if (cname) {
359 result->ai_canonname = (char *)calloc(1, strlen(cname)+1);
360 if (NULL == result->ai_canonname) {
361 s_freeaddrinfo(result);
362 return EAI_MEMORY;
363 }
364 strcpy(result->ai_canonname, cname);
365 }
366 *res = result;
367 return 0;
368 }
369
370 #ifndef EAI_OVERFLOW
371 #define EAI_OVERFLOW WSAENAMETOOLONG
372 #endif
373
374 static int WSAAPI s_getnameinfo (const struct sockaddr *sa, socklen_t salen,
375 char *host, size_t hostlen,
376 char *serv, size_t servlen,
377 int flags)
378 {
379 struct hostent *he;
380 struct servent *se = NULL;
381 const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
382
383 if (sin->sin_family != PF_INET)
384 return EAI_FAMILY;
385 if ((NULL == host) && (NULL == serv))
386 return EAI_NONAME;
387 if ((serv) && (servlen > 0)) {
388 if (flags & NI_NUMERICSERV)
389 se = NULL;
390 else
391 if (flags & NI_DGRAM)
392 se = getservbyport(sin->sin_port, "udp");
393 else
394 se = getservbyport(sin->sin_port, "tcp");
395 if (se) {
396 if (servlen <= strlen(se->s_name))
397 return EAI_OVERFLOW;
398 strcpy(serv, se->s_name);
399 }
400 else {
401 char buf[16];
402
403 sprintf(buf, "%d", ntohs(sin->sin_port));
404 if (servlen <= strlen(buf))
405 return EAI_OVERFLOW;
406 strcpy(serv, buf);
407 }
408 }
409 if ((host) && (hostlen > 0)) {
410 if (flags & NI_NUMERICHOST)
411 he = NULL;
412 else
413 he = gethostbyaddr((const char *)&sin->sin_addr, 4, AF_INET);
414 if (he) {
415 if (hostlen < strlen(he->h_name)+1)
416 return EAI_OVERFLOW;
417 strcpy(host, he->h_name);
418 }
419 else {
420 if (flags & NI_NAMEREQD)
421 return EAI_NONAME;
422 if (hostlen < strlen(inet_ntoa(sin->sin_addr))+1)
423 return EAI_OVERFLOW;
424 strcpy(host, inet_ntoa(sin->sin_addr));
425 }
426 }
427 return 0;
428 }
429
430 #if defined(_WIN32) || defined(__CYGWIN__)
431
432 #if !defined(IPV6_V6ONLY) /* Older XP environments may not define IPV6_V6ONLY */
433 #define IPV6_V6ONLY 27 /* Treat wildcard bind as AF_INET6-only. */
434 #endif
435 /* Dynamic DLL load variables */
436 #ifdef _WIN32
437 static HINSTANCE hLib = 0; /* handle to DLL */
438 #else
439 static void *hLib = NULL; /* handle to Library */
440 #endif
441 static int lib_loaded = 0; /* 0=not loaded, 1=loaded, 2=library load failed, 3=Func load failed */
442 static const char* lib_name = "Ws2_32.dll";
443
444 /* load function pointer from DLL */
445 typedef int (*_func)();
446
447 static void load_function(const char* function, _func* func_ptr) {
448 #ifdef _WIN32
449 *func_ptr = (_func)GetProcAddress(hLib, function);
450 #else
451 *func_ptr = (_func)dlsym(hLib, function);
452 #endif
453 if (*func_ptr == 0) {
454 sim_printf ("Sockets: Failed to find function '%s' in %s\r\n", function, lib_name);
455 lib_loaded = 3;
456 }
457 }
458
459 /* load Ws2_32.dll as required */
460 int load_ws2(void) {
461 switch(lib_loaded) {
462 case 0: /* not loaded */
463 /* attempt to load DLL */
464 #ifdef _WIN32
465 hLib = LoadLibraryA(lib_name);
466 #else
467 hLib = dlopen(lib_name, RTLD_NOW);
468 #endif
469 if (hLib == 0) {
470 /* failed to load DLL */
471 sim_printf ("Sockets: Failed to load %s\r\n", lib_name);
472 lib_loaded = 2;
473 break;
474 } else {
475 /* library loaded OK */
476 lib_loaded = 1;
477 }
478
479 /* load required functions; sets dll_load=3 on error */
480 load_function("getaddrinfo", (_func *) &p_getaddrinfo);
481 load_function("getnameinfo", (_func *) &p_getnameinfo);
482 load_function("freeaddrinfo", (_func *) &p_freeaddrinfo);
483
484 if (lib_loaded != 1) {
485 /* unsuccessful load, connect stubs */
486 p_getaddrinfo = (getaddrinfo_func)s_getaddrinfo;
487 p_getnameinfo = (getnameinfo_func)s_getnameinfo;
488 p_freeaddrinfo = (freeaddrinfo_func)s_freeaddrinfo;
489 }
490 break;
491 default: /* loaded or failed */
492 break;
493 }
494 return (lib_loaded == 1) ? 1 : 0;
495 }
496 #endif
497
498 /* OS independent routines
499
500 sim_parse_addr parse a hostname/ipaddress from port and apply defaults and
501 optionally validate an address match
502 */
503
504 /* sim_parse_addr host:port
505
506 Presumption is that the input, if it doesn't contain a ':' character is a port specifier.
507 If the host field contains one or more colon characters (i.e. it is an IPv6 address),
508 the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format)
509
510 Inputs:
511 cptr = pointer to input string
512 default_host
513 = optional pointer to default host if none specified
514 host_len = length of host buffer
515 default_port
516 = optional pointer to default port if none specified
517 port_len = length of port buffer
518 validate_addr = optional name/addr which is checked to be equivalent
519 to the host result of parsing the other input. This
520 address would usually be returned by sim_accept_conn.
521 Outputs:
522 host = pointer to buffer for IP address (may be NULL), 0 = none
523 port = pointer to buffer for IP port (may be NULL), 0 = none
524 result = status (0 on complete success or -1 if
525 parsing can't happen due to bad syntax, a value is
526 out of range, a result can't fit into a result buffer,
527 a service name doesn't exist, or a validation name
528 doesn't match the parsed host)
529 */
530
531 int sim_parse_addr (const char *cptr, char *host, size_t host_len, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr)
532 {
533 char gbuf[CBUFSIZE], default_pbuf[CBUFSIZE];
534 const char *hostp;
535 char *portp;
536 char *endc;
537 unsigned long portval;
538
539 if ((host != NULL) && (host_len != 0))
540 memset (host, 0, host_len);
541 if ((port != NULL) && (port_len != 0))
542 memset (port, 0, port_len);
543 if ((cptr == NULL) || (*cptr == 0)) {
544 if (((default_host == NULL) || (*default_host == 0)) || ((default_port == NULL) || (*default_port == 0)))
545 return -1;
546 if ((host == NULL) || (port == NULL))
547 return -1; /* no place */
548 if ((strlen(default_host) >= host_len) || (strlen(default_port) >= port_len))
549 return -1; /* no room */
550 strcpy (host, default_host);
551 strcpy (port, default_port);
552 return 0;
553 }
554 memset (default_pbuf, 0, sizeof(default_pbuf));
555 if (default_port)
556 strncpy (default_pbuf, default_port, sizeof(default_pbuf)-1);
557 gbuf[sizeof(gbuf)-1] = '\0';
558 strncpy (gbuf, cptr, sizeof(gbuf)-1);
559 hostp = gbuf; /* default addr */
560 portp = NULL;
561 if ((portp = strrchr (gbuf, ':')) && /* x:y? split */
562 (NULL == strchr (portp, ']'))) {
563 *portp++ = 0;
564 if (*portp == '\0')
565 portp = default_pbuf;
566 }
567 else { /* No colon in input */
568 portp = gbuf; /* Input is the port specifier */
569 hostp = (const char *)default_host; /* host is defaulted if provided */
570 }
571 if (portp != NULL) {
572 portval = strtoul(portp, &endc, 10);
573 if ((*endc == '\0') && ((portval == 0) || (portval > 65535)))
574 return -1; /* numeric value too big */
575 if (*endc != '\0') {
576 struct servent *se = getservbyname(portp, "tcp");
577
578 if (se == NULL)
579 return -1; /* invalid service name */
580 }
581 }
582 if (port) /* port wanted? */
583 if (portp != NULL) {
584 if (strlen(portp) >= port_len)
585 return -1; /* no room */
586 else
587 strcpy (port, portp);
588 }
589 if (hostp != NULL) {
590 if (']' == hostp[strlen(hostp)-1]) {
591 if ('[' != hostp[0])
592 return -1; /* invalid domain literal */
593 /* host may be the const default_host so move to temp buffer before modifying */
594 strncpy(gbuf, hostp+1, sizeof(gbuf)-1); /* remove brackets from domain literal host */
595 gbuf[strlen(gbuf)-1] = '\0';
596 hostp = gbuf;
597 }
598 }
599 if (host) { /* host wanted? */
600 if (hostp != NULL) {
601 if (strlen(hostp) >= host_len)
602 return -1; /* no room */
603 else
604 if (('\0' != hostp[0]) || (default_host == NULL))
605 strcpy (host, hostp);
606 else
607 if (strlen(default_host) >= host_len)
608 return -1; /* no room */
609 else
610 strcpy (host, default_host);
611 }
612 else {
613 if (default_host) {
614 if (strlen(default_host) >= host_len)
615 return -1; /* no room */
616 else
617 strcpy (host, default_host);
618 }
619 }
620 }
621 if (validate_addr) {
622 struct addrinfo *ai_host, *ai_validate, *ai, *aiv;
623 int status;
624
625 if (hostp == NULL)
626 return -1;
627 if (p_getaddrinfo(hostp, NULL, NULL, &ai_host))
628 return -1;
629 if (p_getaddrinfo(validate_addr, NULL, NULL, &ai_validate)) {
630 p_freeaddrinfo (ai_host);
631 return -1;
632 }
633 status = -1;
634 for (ai = ai_host; ai != NULL; ai = ai->ai_next) {
635 for (aiv = ai_validate; aiv != NULL; aiv = aiv->ai_next) {
636 if ((ai->ai_addrlen == aiv->ai_addrlen) &&
637 (ai->ai_family == aiv->ai_family) &&
638 (0 == memcmp (ai->ai_addr, aiv->ai_addr, ai->ai_addrlen))) {
639 status = 0;
640 break;
641 }
642 }
643 }
644 if (status != 0) {
645 /* be generous and allow successful validations against variations of localhost addresses */
646 if (((0 == strcmp("127.0.0.1", hostp)) &&
647 (0 == strcmp("::1", validate_addr))) ||
648 ((0 == strcmp("127.0.0.1", validate_addr)) &&
649 (0 == strcmp("::1", hostp))))
650 status = 0;
651 }
652 p_freeaddrinfo (ai_host);
653 p_freeaddrinfo (ai_validate);
654 return status;
655 }
656 return 0;
657 }
658
659 /* sim_parse_addr_ex localport:host:port
660
661 Presumption is that the input, if it doesn't contain a ':' character is a port specifier.
662 If the host field contains one or more colon characters (i.e. it is an IPv6 address),
663 the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format)
664
665 llll:w.x.y.z:rrrr
666 llll:name.domain.com:rrrr
667 llll::rrrr
668 rrrr
669 w.x.y.z:rrrr
670 [w.x.y.z]:rrrr
671 name.domain.com:rrrr
672
673 Inputs:
674 cptr = pointer to input string
675 default_host
676 = optional pointer to default host if none specified
677 host_len = length of host buffer
678 default_port
679 = optional pointer to default port if none specified
680 port_len = length of port buffer
681
682 Outputs:
683 host = pointer to buffer for IP address (may be NULL), 0 = none
684 port = pointer to buffer for IP port (may be NULL), 0 = none
685 localport
686 = pointer to buffer for local IP port (may be NULL), 0 = none
687 result = status (SCPE_OK on complete success or SCPE_ARG if
688 parsing can't happen due to bad syntax, a value is
689 out of range, a result can't fit into a result buffer,
690 a service name doesn't exist, or a validation name
691 doesn't match the parsed host)
692 */
693 int sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, char *localport, size_t localport_len, const char *default_port)
694 {
695 const char *hostp;
696
697 if ((localport != NULL) && (localport_len != 0))
698 memset (localport, 0, localport_len);
699 hostp = strchr (cptr, ':');
700 if ((hostp != NULL) && ((hostp[1] == '[') || (NULL != strchr (hostp+1, ':')))) {
701 if ((localport != NULL) && (localport_len != 0)) {
702 localport_len -= 1;
703 if (localport_len > (size_t)(hostp-cptr))
704 localport_len = (size_t)(hostp-cptr);
705 memcpy (localport, cptr, localport_len);
706 }
707 return sim_parse_addr (hostp+1, host, hostlen, default_host, port, port_len, default_port, NULL);
708 }
709 return sim_parse_addr (cptr, host, hostlen, default_host, port, port_len, default_port, NULL);
710 }
711
712
713 void sim_init_sock (void)
714 {
715 #if defined (_WIN32)
716 int err;
717 WORD wVersionRequested;
718 WSADATA wsaData;
719 wVersionRequested = MAKEWORD (2, 2);
720
721 err = WSAStartup (wVersionRequested, &wsaData); /* start Winsock */
722 if (err != 0)
723 sim_printf ("Winsock: startup error %d\n", err);
724 #if defined(AF_INET6)
725 load_ws2 ();
726 #endif /* endif AF_INET6 */
727 #else /* Use native addrinfo APIs */
728 #if defined(AF_INET6)
729 p_getaddrinfo = (getaddrinfo_func)getaddrinfo;
730 p_getnameinfo = (getnameinfo_func)getnameinfo;
731 p_freeaddrinfo = (freeaddrinfo_func)freeaddrinfo;
732 #else
733 /* Native APIs not available, connect stubs */
734 p_getaddrinfo = (getaddrinfo_func)s_getaddrinfo;
735 p_getnameinfo = (getnameinfo_func)s_getnameinfo;
736 p_freeaddrinfo = (freeaddrinfo_func)s_freeaddrinfo;
737 #endif /* endif AF_INET6 */
738 #endif /* endif _WIN32 */
739 #if defined (SIGPIPE)
740 signal (SIGPIPE, SIG_IGN); /* no pipe signals */
741 #endif
742 }
743
744 void sim_cleanup_sock (void)
745 {
746 #if defined (_WIN32)
747 WSACleanup ();
748 #endif
749 }
750
751 #if defined (_WIN32) /* Windows */
752 static int sim_setnonblock (SOCKET sock)
753 {
754 unsigned long non_block = 1;
755
756 return ioctlsocket (sock, FIONBIO, &non_block); /* set nonblocking */
757 }
758
759 #elif defined (VMS) /* VMS */
760 static int sim_setnonblock (SOCKET sock)
761 {
762 int non_block = 1;
763
764 return ioctl (sock, FIONBIO, &non_block); /* set nonblocking */
765 }
766
767 #else /* Mac, Unix, OS/2 */
768 static int sim_setnonblock (SOCKET sock)
769 {
770 int fl, sta;
771
772 fl = fcntl (sock, F_GETFL,0); /* get flags */
773 if (fl == -1)
774 return SOCKET_ERROR;
775 sta = fcntl (sock, F_SETFL, fl | O_NONBLOCK); /* set nonblock */
776 if (sta == -1)
777 return SOCKET_ERROR;
778 #if !defined (macintosh) && !defined (__EMX__) && \
779 !defined (__HAIKU__) /* Unix only */
780 sta = fcntl (sock, F_SETOWN, getpid()); /* set ownership */
781 if (sta == -1)
782 return SOCKET_ERROR;
783 #endif
784 return 0;
785 }
786
787 #endif /* endif !Win32 && !VMS */
788
789 static int sim_setnodelay (SOCKET sock)
790 {
791 int nodelay = 1;
792 int sta;
793
794 /* disable Nagle algorithm */
795 sta = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay));
796 if (sta == -1)
797 return SOCKET_ERROR;
798
799 #if defined(TCP_NODELAYACK)
800 /* disable delayed ack algorithm */
801 sta = setsockopt (sock, IPPROTO_TCP, TCP_NODELAYACK, (char *)&nodelay, sizeof(nodelay));
802 if (sta == -1)
803 return SOCKET_ERROR;
804 #endif
805
806 #if defined(TCP_QUICKACK)
807 /* disable delayed ack algorithm */
808 sta = setsockopt (sock, IPPROTO_TCP, TCP_QUICKACK, (char *)&nodelay, sizeof(nodelay));
809 if (sta == -1)
810 return SOCKET_ERROR;
811 #endif
812
813 return sta;
814 }
815
816 static SOCKET sim_create_sock (int af, int opt_flags)
817 {
818 SOCKET newsock;
819 int err;
820
821 newsock = socket (af, ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? SOCK_DGRAM : SOCK_STREAM), 0);/* create socket */
822 if (newsock == INVALID_SOCKET) { /* socket error? */
823 err = WSAGetLastError ();
824 #if defined(WSAEAFNOSUPPORT)
825 if (err == WSAEAFNOSUPPORT) /* expected error, just return */
826 return newsock;
827 #endif
828 return sim_err_sock (newsock, "socket"); /* report error and return */
829 }
830 return newsock;
831 }
832
833 /*
834 Some platforms and/or network stacks have varying support for listening on
835 an IPv6 socket and receiving connections from both IPv4 and IPv6 client
836 connections. This is known as IPv4-Mapped. Some platforms claim such
837 support (i.e. some Windows versions), but it doesn't work in all cases.
838 */
839
840 SOCKET sim_master_sock_ex (const char *hostport, int *parse_status, int opt_flags)
841 {
842 SOCKET newsock = INVALID_SOCKET;
843 int sta;
844 char host[CBUFSIZE], port[CBUFSIZE];
845 int r;
846 struct addrinfo hints;
847 struct addrinfo *result = NULL, *preferred;
848
849 r = sim_parse_addr (hostport, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL);
850 if (parse_status)
851 *parse_status = r;
852 if (r)
853 return newsock;
854
855 memset(&hints, 0, sizeof(hints));
856 hints.ai_flags = AI_PASSIVE;
857 hints.ai_family = AF_UNSPEC;
858 hints.ai_protocol = IPPROTO_TCP;
859 hints.ai_socktype = SOCK_STREAM;
860 if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &result)) {
861 if (parse_status)
862 *parse_status = -1;
863 return newsock;
864 }
865 preferred = result;
866 #ifdef IPV6_V6ONLY
867 /*
868 When we can create a dual stack socket, be sure to find the IPv6 addrinfo
869 to bind to.
870 */
871 for (; preferred != NULL; preferred = preferred->ai_next) {
872 if (preferred->ai_family == AF_INET6)
873 break;
874 }
875 if (preferred == NULL)
876 preferred = result;
877 #endif
878 retry:
879 newsock = sim_create_sock (preferred->ai_family, 0); /* create socket */
880 if (newsock == INVALID_SOCKET) { /* socket error? */
881 #ifndef IPV6_V6ONLY
882 if (preferred->ai_next) {
883 preferred = preferred->ai_next;
884 goto retry;
885 }
886 #else
887 if ((preferred->ai_family == AF_INET6) &&
888 (preferred != result)) {
889 preferred = result;
890 goto retry;
891 }
892 #endif
893 p_freeaddrinfo(result);
894 return newsock;
895 }
896 #ifdef IPV6_V6ONLY
897 if (preferred->ai_family == AF_INET6) {
898 int off = 0;
899 sta = setsockopt (newsock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&off, sizeof(off));
900 }
901 #endif
902 if (opt_flags & SIM_SOCK_OPT_REUSEADDR) {
903 int on = 1;
904
905 sta = setsockopt (newsock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
906 }
907 #if defined (SO_EXCLUSIVEADDRUSE)
908 else {
909 int on = 1;
910
911 sta = setsockopt (newsock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&on, sizeof(on));
912 }
913 #endif
914 sta = bind (newsock, preferred->ai_addr, preferred->ai_addrlen);
915 p_freeaddrinfo(result);
916 if (sta == SOCKET_ERROR) /* bind error? */
917 return sim_err_sock (newsock, "bind");
918 if (!(opt_flags & SIM_SOCK_OPT_BLOCKING)) {
919 sta = sim_setnonblock (newsock); /* set nonblocking */
920 if (sta == SOCKET_ERROR) /* fcntl error? */
921 return sim_err_sock (newsock, "fcntl");
922 }
923 sta = listen (newsock, 1); /* listen on socket */
924 if (sta == SOCKET_ERROR) /* listen error? */
925 return sim_err_sock (newsock, "listen");
926 return newsock; /* got it! */
927 }
928
929 SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, int opt_flags)
930 {
931 SOCKET newsock = INVALID_SOCKET;
932 int sta;
933 char host[CBUFSIZE], port[CBUFSIZE];
934 struct addrinfo hints;
935 struct addrinfo *result = NULL, *source = NULL;
936
937 if (sim_parse_addr (hostport, host, sizeof(host), default_host, port, sizeof(port), default_port, NULL))
938 return INVALID_SOCKET;
939
940 memset(&hints, 0, sizeof(hints));
941 hints.ai_family = AF_UNSPEC;
942 hints.ai_protocol = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? IPPROTO_UDP : IPPROTO_TCP);
943 hints.ai_socktype = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? SOCK_DGRAM : SOCK_STREAM);
944 if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &result))
945 return INVALID_SOCKET;
946
947 if (sourcehostport) {
948
949 /* Validate the local/source side address which we'll bind to */
950 if (sim_parse_addr (sourcehostport, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL)) {
951 p_freeaddrinfo (result);
952 return INVALID_SOCKET;
953 }
954
955 memset(&hints, 0, sizeof(hints));
956 hints.ai_flags = AI_PASSIVE;
957 hints.ai_family = result->ai_family; /* Same family as connect destination */
958 hints.ai_protocol = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? IPPROTO_UDP : IPPROTO_TCP);
959 hints.ai_socktype = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? SOCK_DGRAM : SOCK_STREAM);
960 if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &source)) {
961 p_freeaddrinfo (result);
962 return INVALID_SOCKET;
963 }
964
965 newsock = sim_create_sock (result->ai_family, opt_flags & SIM_SOCK_OPT_DATAGRAM);/* create socket */
966 if (newsock == INVALID_SOCKET) { /* socket error? */
967 p_freeaddrinfo (result);
968 p_freeaddrinfo (source);
969 return newsock;
970 }
971
972 sta = bind (newsock, source->ai_addr, source->ai_addrlen);
973 p_freeaddrinfo(source);
974 source = NULL;
975 if (sta == SOCKET_ERROR) { /* bind error? */
976 p_freeaddrinfo (result);
977 return sim_err_sock (newsock, "bind");
978 }
979 }
980
981 if (newsock == INVALID_SOCKET) { /* socket error? */
982 newsock = sim_create_sock (result->ai_family, opt_flags & SIM_SOCK_OPT_DATAGRAM);/* create socket */
983 if (newsock == INVALID_SOCKET) { /* socket error? */
984 p_freeaddrinfo (result);
985 return newsock;
986 }
987 }
988
989 if (!(opt_flags & SIM_SOCK_OPT_BLOCKING)) {
990 sta = sim_setnonblock (newsock); /* set nonblocking */
991 if (sta == SOCKET_ERROR) { /* fcntl error? */
992 p_freeaddrinfo (result);
993 return sim_err_sock (newsock, "fcntl");
994 }
995 }
996 if ((!(opt_flags & SIM_SOCK_OPT_DATAGRAM)) && (opt_flags & SIM_SOCK_OPT_NODELAY)) {
997 sta = sim_setnodelay (newsock); /* set nodelay */
998 if (sta == SOCKET_ERROR) { /* setsock error? */
999 p_freeaddrinfo (result);
1000 return sim_err_sock (newsock, "setnodelay");
1001 }
1002 }
1003 if (!(opt_flags & SIM_SOCK_OPT_DATAGRAM)) {
1004 int keepalive = 1;
1005
1006 /* enable TCP Keep Alives */
1007 sta = setsockopt (newsock, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(keepalive));
1008 if (sta == -1)
1009 return sim_err_sock (newsock, "setsockopt KEEPALIVE");
1010 }
1011 sta = connect (newsock, result->ai_addr, result->ai_addrlen);
1012 p_freeaddrinfo (result);
1013 if (sta == SOCKET_ERROR) {
1014 if (opt_flags & SIM_SOCK_OPT_BLOCKING) {
1015 if ((WSAGetLastError () == WSAETIMEDOUT) || /* expected errors after a connect failure */
1016 (WSAGetLastError () == WSAEHOSTUNREACH) ||
1017 (WSAGetLastError () == WSAECONNREFUSED) ||
1018 (WSAGetLastError () == WSAECONNABORTED) ||
1019 (WSAGetLastError () == WSAECONNRESET)) {
1020 sim_close_sock (newsock);
1021 newsock = INVALID_SOCKET;
1022 }
1023 else
1024 return sim_err_sock (newsock, "connect");
1025 }
1026 else /* Non Blocking case won't return errors until some future read */
1027 if ((WSAGetLastError () != WSAEWOULDBLOCK) &&
1028 (WSAGetLastError () != WSAEINPROGRESS))
1029 return sim_err_sock (newsock, "connect");
1030 }
1031 return newsock; /* got it! */
1032 }
1033
1034 SOCKET sim_accept_conn_ex (SOCKET master, char **connectaddr, int opt_flags)
1035 {
1036 int sta = 0, err;
1037 int keepalive = 1;
1038 #if defined (macintosh) || defined (__linux) || defined (__linux__) || \
1039 defined (__APPLE__) || defined (__OpenBSD__) || \
1040 defined(__NetBSD__) || defined(__FreeBSD__) || \
1041 (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \
1042 defined (__HAIKU__) || defined(__CYGWIN__)
1043 socklen_t size;
1044 #elif defined (_WIN32) || defined (__EMX__) || \
1045 (defined (__ALPHA) && defined (__unix__)) || \
1046 defined (__hpux)
1047 int size;
1048 #else
1049 size_t size;
1050 #endif
1051 SOCKET newsock;
1052 struct sockaddr_storage clientname;
1053
1054 if (master == 0) /* not attached? */
1055 return INVALID_SOCKET;
1056 size = sizeof (clientname);
1057 memset (&clientname, 0, sizeof(clientname));
1058 newsock = accept (master, (struct sockaddr *) &clientname, &size);
1059 if (newsock == INVALID_SOCKET) { /* error? */
1060 err = WSAGetLastError ();
1061 if (err != WSAEWOULDBLOCK)
1062 sim_err_sock(newsock, "accept");
1063 return INVALID_SOCKET;
1064 }
1065 if (connectaddr != NULL) {
1066 *connectaddr = (char *)calloc(1, NI_MAXHOST+1);
1067 #ifdef AF_INET6
1068 p_getnameinfo((struct sockaddr *)&clientname, size, *connectaddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1069 if (0 == memcmp("::ffff:", *connectaddr, 7)) /* is this a IPv4-mapped IPv6 address? */
1070 memmove(*connectaddr, 7+*connectaddr, /* prefer bare IPv4 address */
1071 strlen(*connectaddr) - 7 + 1); /* length to include terminating \0 */
1072 #else
1073 strcpy(*connectaddr, inet_ntoa(((struct sockaddr_in *)&connectaddr)->s_addr));
1074 #endif
1075 }
1076
1077 if (!(opt_flags & SIM_SOCK_OPT_BLOCKING)) {
1078 sta = sim_setnonblock (newsock); /* set nonblocking */
1079 if (sta == SOCKET_ERROR) /* fcntl error? */
1080 return sim_err_sock (newsock, "fcntl");
1081 }
1082
1083 if ((opt_flags & SIM_SOCK_OPT_NODELAY)) {
1084 sta = sim_setnodelay (newsock); /* set nonblocking */
1085 if (sta == SOCKET_ERROR) /* setsockopt error? */
1086 return sim_err_sock (newsock, "setnodelay");
1087 }
1088
1089 /* enable TCP Keep Alives */
1090 sta = setsockopt (newsock, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(keepalive));
1091 if (sta == -1)
1092 return sim_err_sock (newsock, "setsockopt KEEPALIVE");
1093
1094 return newsock;
1095 }
1096
1097 int sim_check_conn (SOCKET sock, int rd)
1098 {
1099 fd_set rw_set, er_set;
1100 fd_set *rw_p = &rw_set;
1101 fd_set *er_p = &er_set;
1102 struct timeval zero;
1103 struct sockaddr_storage peername;
1104 #if defined (macintosh) || defined (__linux) || defined (__linux__) || \
1105 defined (__APPLE__) || defined (__OpenBSD__) || \
1106 defined(__NetBSD__) || defined(__FreeBSD__) || \
1107 (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \
1108 defined (__HAIKU__) || defined(__CYGWIN__)
1109 socklen_t peernamesize = (socklen_t)sizeof(peername);
1110 #elif defined (_WIN32) || defined (__EMX__) || \
1111 (defined (__ALPHA) && defined (__unix__)) || \
1112 defined (__hpux)
1113 int peernamesize = (int)sizeof(peername);
1114 #else
1115 size_t peernamesize = sizeof(peername);
1116 #endif
1117
1118 memset (&zero, 0, sizeof(zero));
1119 FD_ZERO (rw_p);
1120 FD_ZERO (er_p);
1121 FD_SET (sock, rw_p);
1122 FD_SET (sock, er_p);
1123 if (rd)
1124 (void)select ((int) sock + 1, rw_p, NULL, er_p, &zero);
1125 else
1126 (void)select ((int) sock + 1, NULL, rw_p, er_p, &zero);
1127 if (FD_ISSET (sock, er_p))
1128 return -1;
1129 if (FD_ISSET (sock, rw_p)) {
1130 if (0 == getpeername (sock, (struct sockaddr *)&peername, &peernamesize))
1131 return 1;
1132 else
1133 return -1;
1134 }
1135 return 0;
1136 }
1137
1138 static int _sim_getaddrname (struct sockaddr *addr, size_t addrsize, char *hostnamebuf, char *portnamebuf)
1139 {
1140 #if defined (macintosh) || defined (__linux) || defined (__linux__) || \
1141 defined (__APPLE__) || defined (__OpenBSD__) || \
1142 defined(__NetBSD__) || defined(__FreeBSD__) || \
1143 (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \
1144 defined (__HAIKU__) || defined(__CYGWIN__)
1145 socklen_t size = (socklen_t)addrsize;
1146 #elif defined (_WIN32) || defined (__EMX__) || \
1147 (defined (__ALPHA) && defined (__unix__)) || \
1148 defined (__hpux)
1149 int size = (int)addrsize;
1150 #else
1151 size_t size = addrsize;
1152 #endif
1153 int ret = 0;
1154
1155 #ifdef AF_INET6
1156 *hostnamebuf = '\0';
1157 *portnamebuf = '\0';
1158 ret = p_getnameinfo(addr, size, hostnamebuf, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
1159 if (0 == memcmp("::ffff:", hostnamebuf, 7)) /* is this a IPv4-mapped IPv6 address? */
1160 memmove(hostnamebuf, 7+hostnamebuf, /* prefer bare IPv4 address */
1161 strlen(hostnamebuf) + 7 - 1); /* length to include terminating \0 */
1162 if (!ret)
1163 ret = p_getnameinfo(addr, size, NULL, 0, portnamebuf, NI_MAXSERV, NI_NUMERICSERV);
1164 #else
1165 strcpy(hostnamebuf, inet_ntoa(((struct sockaddr_in *)addr)->s_addr));
1166 sprintf(portnamebuf, "%d", (int)ntohs(((struct sockaddr_in *)addr)->s_port)));
1167 #endif
1168 return ret;
1169 }
1170
1171 int sim_getnames_sock (SOCKET sock, char **socknamebuf, char **peernamebuf)
1172 {
1173 struct sockaddr_storage sockname, peername;
1174 #if defined (macintosh) || defined (__linux) || defined (__linux__) || \
1175 defined (__APPLE__) || defined (__OpenBSD__) || \
1176 defined(__NetBSD__) || defined(__FreeBSD__) || \
1177 (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \
1178 defined (__HAIKU__) || defined(__CYGWIN__)
1179 socklen_t socknamesize = (socklen_t)sizeof(sockname);
1180 socklen_t peernamesize = (socklen_t)sizeof(peername);
1181 #elif defined (_WIN32) || defined (__EMX__) || \
1182 (defined (__ALPHA) && defined (__unix__)) || \
1183 defined (__hpux)
1184 int socknamesize = (int)sizeof(sockname);
1185 int peernamesize = (int)sizeof(peername);
1186 #else
1187 size_t socknamesize = sizeof(sockname);
1188 size_t peernamesize = sizeof(peername);
1189 #endif
1190 char hostbuf[NI_MAXHOST+1];
1191 char portbuf[NI_MAXSERV+1];
1192
1193 if (socknamebuf)
1194 *socknamebuf = (char *)calloc(1, NI_MAXHOST+NI_MAXSERV+4);
1195 if (peernamebuf)
1196 *peernamebuf = (char *)calloc(1, NI_MAXHOST+NI_MAXSERV+4);
1197 (void)getsockname (sock, (struct sockaddr *)&sockname, &socknamesize);
1198 (void)getpeername (sock, (struct sockaddr *)&peername, &peernamesize);
1199 if (socknamebuf != NULL) {
1200 _sim_getaddrname ((struct sockaddr *)&sockname, (size_t)socknamesize, hostbuf, portbuf);
1201 sprintf(*socknamebuf, "[%s]:%s", hostbuf, portbuf);
1202 }
1203 if (peernamebuf != NULL) {
1204 _sim_getaddrname ((struct sockaddr *)&peername, (size_t)peernamesize, hostbuf, portbuf);
1205 sprintf(*peernamebuf, "[%s]:%s", hostbuf, portbuf);
1206 }
1207 return 0;
1208 }
1209
1210
1211 int sim_read_sock (SOCKET sock, char *buf, int nbytes)
1212 {
1213 int rbytes, err;
1214
1215 rbytes = recv (sock, buf, nbytes, 0);
1216 if (rbytes == 0) /* disconnect */
1217 return -1;
1218 if (rbytes == SOCKET_ERROR) {
1219 err = WSAGetLastError ();
1220 if (err == WSAEWOULDBLOCK) /* no data */
1221 return 0;
1222 #if defined(EAGAIN)
1223 if (err == EAGAIN) /* no data */
1224 return 0;
1225 #endif
1226 if ((err != WSAETIMEDOUT) && /* expected errors after a connect failure */
1227 (err != WSAEHOSTUNREACH) &&
1228 (err != WSAECONNREFUSED) &&
1229 (err != WSAECONNABORTED) &&
1230 (err != WSAECONNRESET) &&
1231 (err != WSAEINTR)) /* or a close of a blocking read */
1232 sim_err_sock (INVALID_SOCKET, "read");
1233 return -1;
1234 }
1235 return rbytes;
1236 }
1237
1238 int sim_write_sock (SOCKET sock, const char *msg, int nbytes)
1239 {
1240 int err, sbytes = send (sock, msg, nbytes, 0);
1241
1242 if (sbytes == SOCKET_ERROR) {
1243 err = WSAGetLastError ();
1244 if (err == WSAEWOULDBLOCK) /* no data */
1245 return 0;
1246 #if defined(EAGAIN)
1247 if (err == EAGAIN) /* no data */
1248 return 0;
1249 #endif
1250 }
1251 return sbytes;
1252 }
1253
1254 void sim_close_sock (SOCKET sock)
1255 {
1256 shutdown(sock, SD_BOTH);
1257 closesocket (sock);
1258 }
1259
1260 #endif /* end else !implemented */
1261
1262 #ifdef __cplusplus
1263 }
1264 #endif
1265