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