| /* net.cc: network-related routines. |
| |
| This file is part of Cygwin. |
| |
| This software is a copyrighted work licensed under the terms of the |
| Cygwin license. Please consult the file "CYGWIN_LICENSE" for |
| details. */ |
| |
| /* #define DEBUG_NEST_ON 1 */ |
| |
| #define __INSIDE_CYGWIN_NET__ |
| #define USE_SYS_TYPES_FD_SET |
| #define __WSA_ERR_MACROS_DEFINED |
| #include "winsup.h" |
| #ifdef __x86_64__ |
| /* 2014-04-24: Current Mingw headers define sockaddr_in6 using u_long (8 byte) |
| because a redefinition for LP64 systems is missing. This leads to a wrong |
| definition and size of sockaddr_in6 when building with winsock headers. */ |
| #undef u_long |
| #define u_long __ms_u_long |
| #endif |
| #include <ws2tcpip.h> |
| #include <mswsock.h> |
| #include <iphlpapi.h> |
| #include "miscfuncs.h" |
| #include <ctype.h> |
| #include <wchar.h> |
| #include <stdlib.h> |
| #define gethostname cygwin_gethostname |
| #include <unistd.h> |
| #undef gethostname |
| #include <netdb.h> |
| #include <cygwin/in.h> |
| #include <asm/byteorder.h> |
| #include <assert.h> |
| #include "cygerrno.h" |
| #include "security.h" |
| #include "cygwin/version.h" |
| #include "shared_info.h" |
| #include "perprocess.h" |
| #include "path.h" |
| #include "fhandler.h" |
| #include "dtable.h" |
| #include "cygheap.h" |
| #include "sigproc.h" |
| #include "registry.h" |
| #include "cygtls.h" |
| #include "ifaddrs.h" |
| #include "tls_pbuf.h" |
| #include "ntdll.h" |
| |
| /* Unfortunately defined in Windows header files and arpa/nameser_compat.h. */ |
| #undef NOERROR |
| #undef DELETE |
| #define _CYGWIN_IN_H |
| #include <resolv.h> |
| |
| extern "C" |
| { |
| int h_errno; |
| |
| int __stdcall rcmd (char **ahost, unsigned short inport, char *locuser, |
| char *remuser, char *cmd, SOCKET * fd2p); |
| int sscanf (const char *, const char *, ...); |
| int cygwin_inet_pton(int, const char *, void *); |
| int cygwin_inet_aton(const char *, struct in_addr *); |
| const char *cygwin_inet_ntop (int, const void *, char *, socklen_t); |
| int dn_length1(const unsigned char *, const unsigned char *, |
| const unsigned char *); |
| } /* End of "C" section */ |
| |
| const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; |
| const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; |
| |
| static fhandler_socket * |
| get (const int fd) |
| { |
| cygheap_fdget cfd (fd); |
| |
| if (cfd < 0) |
| return 0; |
| |
| fhandler_socket *const fh = cfd->is_socket (); |
| |
| if (!fh) |
| set_errno (ENOTSOCK); |
| |
| return fh; |
| } |
| |
| /* exported as inet_ntoa: BSD 4.3 */ |
| extern "C" char * |
| cygwin_inet_ntoa (struct in_addr in) |
| { |
| char buf[20]; |
| const char *res = cygwin_inet_ntop (AF_INET, &in, buf, sizeof buf); |
| |
| if (_my_tls.locals.ntoa_buf) |
| { |
| free (_my_tls.locals.ntoa_buf); |
| _my_tls.locals.ntoa_buf = NULL; |
| } |
| if (res) |
| _my_tls.locals.ntoa_buf = strdup (res); |
| return _my_tls.locals.ntoa_buf; |
| } |
| |
| /* inet_netof is in the standard BSD sockets library. It is useless |
| for modern networks, since it assumes network values which are no |
| longer meaningful, but some existing code calls it. */ |
| |
| extern "C" unsigned long |
| inet_netof (struct in_addr in) |
| { |
| unsigned long i, res; |
| |
| i = ntohl (in.s_addr); |
| if (IN_CLASSA (i)) |
| res = (i & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT; |
| else if (IN_CLASSB (i)) |
| res = (i & IN_CLASSB_NET) >> IN_CLASSB_NSHIFT; |
| else |
| res = (i & IN_CLASSC_NET) >> IN_CLASSC_NSHIFT; |
| |
| return res; |
| } |
| |
| /* inet_makeaddr is in the standard BSD sockets library. It is |
| useless for modern networks, since it assumes network values which |
| are no longer meaningful, but some existing code calls it. */ |
| |
| extern "C" struct in_addr |
| inet_makeaddr (int net, int lna) |
| { |
| unsigned long i; |
| struct in_addr in; |
| |
| if (net < IN_CLASSA_MAX) |
| i = (net << IN_CLASSA_NSHIFT) | (lna & IN_CLASSA_HOST); |
| else if (net < IN_CLASSB_MAX) |
| i = (net << IN_CLASSB_NSHIFT) | (lna & IN_CLASSB_HOST); |
| else if (net < 0x1000000) |
| i = (net << IN_CLASSC_NSHIFT) | (lna & IN_CLASSC_HOST); |
| else |
| i = net | lna; |
| |
| in.s_addr = htonl (i); |
| |
| |
| return in; |
| } |
| |
| struct tl |
| { |
| int w; |
| const char *s; |
| int e; |
| }; |
| |
| static const struct tl errmap[] = { |
| {WSA_INVALID_HANDLE, "WSA_INVALID_HANDLE", EBADF}, |
| {WSA_NOT_ENOUGH_MEMORY, "WSA_NOT_ENOUGH_MEMORY", ENOMEM}, |
| {WSA_INVALID_PARAMETER, "WSA_INVALID_PARAMETER", EINVAL}, |
| {WSAEINTR, "WSAEINTR", EINTR}, |
| {WSAEWOULDBLOCK, "WSAEWOULDBLOCK", EWOULDBLOCK}, |
| {WSAEINPROGRESS, "WSAEINPROGRESS", EINPROGRESS}, |
| {WSAEALREADY, "WSAEALREADY", EALREADY}, |
| {WSAENOTSOCK, "WSAENOTSOCK", ENOTSOCK}, |
| {WSAEDESTADDRREQ, "WSAEDESTADDRREQ", EDESTADDRREQ}, |
| {WSAEMSGSIZE, "WSAEMSGSIZE", EMSGSIZE}, |
| {WSAEPROTOTYPE, "WSAEPROTOTYPE", EPROTOTYPE}, |
| {WSAENOPROTOOPT, "WSAENOPROTOOPT", ENOPROTOOPT}, |
| {WSAEPROTONOSUPPORT, "WSAEPROTONOSUPPORT", EPROTONOSUPPORT}, |
| {WSAESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT", ESOCKTNOSUPPORT}, |
| {WSAEOPNOTSUPP, "WSAEOPNOTSUPP", EOPNOTSUPP}, |
| {WSAEPFNOSUPPORT, "WSAEPFNOSUPPORT", EPFNOSUPPORT}, |
| {WSAEAFNOSUPPORT, "WSAEAFNOSUPPORT", EAFNOSUPPORT}, |
| {WSAEADDRINUSE, "WSAEADDRINUSE", EADDRINUSE}, |
| {WSAEADDRNOTAVAIL, "WSAEADDRNOTAVAIL", EADDRNOTAVAIL}, |
| {WSAENETDOWN, "WSAENETDOWN", ENETDOWN}, |
| {WSAENETUNREACH, "WSAENETUNREACH", ENETUNREACH}, |
| {WSAENETRESET, "WSAENETRESET", ENETRESET}, |
| {WSAECONNABORTED, "WSAECONNABORTED", ECONNABORTED}, |
| {WSAECONNRESET, "WSAECONNRESET", ECONNRESET}, |
| {WSAENOBUFS, "WSAENOBUFS", ENOBUFS}, |
| {WSAEISCONN, "WSAEISCONN", EISCONN}, |
| {WSAENOTCONN, "WSAENOTCONN", ENOTCONN}, |
| {WSAESHUTDOWN, "WSAESHUTDOWN", ESHUTDOWN}, |
| {WSAETOOMANYREFS, "WSAETOOMANYREFS", ETOOMANYREFS}, |
| {WSAETIMEDOUT, "WSAETIMEDOUT", ETIMEDOUT}, |
| {WSAECONNREFUSED, "WSAECONNREFUSED", ECONNREFUSED}, |
| {WSAELOOP, "WSAELOOP", ELOOP}, |
| {WSAENAMETOOLONG, "WSAENAMETOOLONG", ENAMETOOLONG}, |
| {WSAEHOSTDOWN, "WSAEHOSTDOWN", EHOSTDOWN}, |
| {WSAEHOSTUNREACH, "WSAEHOSTUNREACH", EHOSTUNREACH}, |
| {WSAENOTEMPTY, "WSAENOTEMPTY", ENOTEMPTY}, |
| {WSAEPROCLIM, "WSAEPROCLIM", EPROCLIM}, |
| {WSAEUSERS, "WSAEUSERS", EUSERS}, |
| {WSAEDQUOT, "WSAEDQUOT", EDQUOT}, |
| {WSAESTALE, "WSAESTALE", ESTALE}, |
| {WSAEREMOTE, "WSAEREMOTE", EREMOTE}, |
| {WSAEINVAL, "WSAEINVAL", EINVAL}, |
| {WSAEFAULT, "WSAEFAULT", EFAULT}, |
| {0, "NOERROR", 0}, |
| {0, NULL, 0} |
| }; |
| |
| static int |
| find_winsock_errno (int why) |
| { |
| for (int i = 0; errmap[i].s != NULL; ++i) |
| if (why == errmap[i].w) |
| return errmap[i].e; |
| |
| return EPERM; |
| } |
| |
| void |
| __set_winsock_errno (const char *fn, int ln) |
| { |
| DWORD werr = WSAGetLastError (); |
| int err = find_winsock_errno (werr); |
| |
| set_errno (err); |
| syscall_printf ("%s:%d - winsock error %u -> errno %d", fn, ln, werr, err); |
| } |
| |
| /* |
| * Since the member `s' isn't used for debug output we can use it |
| * for the error text returned by herror and hstrerror. |
| */ |
| static const struct tl host_errmap[] = { |
| {WSAHOST_NOT_FOUND, "Unknown host", HOST_NOT_FOUND}, |
| {WSATRY_AGAIN, "Host name lookup failure", TRY_AGAIN}, |
| {WSANO_RECOVERY, "Unknown server error", NO_RECOVERY}, |
| {WSANO_DATA, "No address associated with name", NO_DATA}, |
| {0, NULL, 0} |
| }; |
| |
| static void |
| set_host_errno () |
| { |
| int i; |
| |
| int why = WSAGetLastError (); |
| |
| for (i = 0; host_errmap[i].w != 0; ++i) |
| if (why == host_errmap[i].w) |
| break; |
| |
| if (host_errmap[i].w != 0) |
| h_errno = host_errmap[i].e; |
| else |
| h_errno = NETDB_INTERNAL; |
| } |
| |
| inline int |
| DWORD_round (int n) |
| { |
| return sizeof (DWORD) * (((n + sizeof (DWORD) - 1)) / sizeof (DWORD)); |
| } |
| |
| inline int |
| strlen_round (const char *s) |
| { |
| if (!s) |
| return 0; |
| return DWORD_round (strlen (s) + 1); |
| } |
| |
| #pragma pack(push,2) |
| struct pservent |
| { |
| char *s_name; |
| char **s_aliases; |
| short s_port; |
| char *s_proto; |
| }; |
| #pragma pack(pop) |
| |
| static const char *entnames[] = {"host", "proto", "serv"}; |
| |
| static unionent * |
| realloc_ent (unionent *&dst, int sz) |
| { |
| /* Allocate the storage needed. Allocate a rounded size to attempt to force |
| reuse of this buffer so that a poorly-written caller will not be using |
| a freed buffer. */ |
| unsigned rsz = 256 * ((sz + 255) / 256); |
| unionent * ptr; |
| if ((ptr = (unionent *) realloc (dst, rsz))) |
| dst = ptr; |
| return ptr; |
| } |
| |
| static inline hostent * |
| realloc_ent (int sz, hostent *) |
| { |
| return (hostent *) realloc_ent (_my_tls.locals.hostent_buf, sz); |
| } |
| |
| /* Generic "dup a {host,proto,serv}ent structure" function. |
| This is complicated because we need to be able to free the |
| structure at any point and we can't rely on the pointer contents |
| being untouched by callers. So, we allocate a chunk of memory |
| large enough to hold the structure and all of the stuff it points |
| to then we copy the source into this new block of memory. |
| The 'unionent' struct is a union of all of the currently used |
| *ent structure. */ |
| |
| #ifdef __x86_64__ |
| /* For some baffling reason, somebody at Microsoft decided that it would be |
| a good idea to exchange the s_port and s_proto members in the servent |
| structure. */ |
| struct win64_servent |
| { |
| char *s_name; |
| char **s_aliases; |
| char *s_proto; |
| short s_port; |
| }; |
| #define WIN_SERVENT(x) ((win64_servent *)(x)) |
| #else |
| #define WIN_SERVENT(x) ((servent *)(x)) |
| #endif |
| |
| #ifdef DEBUGGING |
| static void * |
| #else |
| static inline void * |
| #endif |
| dup_ent (unionent *&dst, unionent *src, unionent::struct_type type) |
| { |
| if (dst) |
| debug_printf ("old %sent structure \"%s\" %p\n", entnames[type], |
| dst->name, dst); |
| |
| if (!src) |
| { |
| set_winsock_errno (); |
| return NULL; |
| } |
| |
| debug_printf ("duping %sent \"%s\", %p", entnames[type], src->name, src); |
| |
| /* Find the size of the raw structure minus any character strings, etc. */ |
| int sz, struct_sz; |
| switch (type) |
| { |
| case unionent::t_protoent: |
| struct_sz = sizeof (protoent); |
| break; |
| case unionent::t_servent: |
| struct_sz = sizeof (servent); |
| break; |
| case unionent::t_hostent: |
| struct_sz = sizeof (hostent); |
| break; |
| default: |
| api_fatal ("called with invalid value %d", type); |
| break; |
| } |
| |
| /* Every *ent begins with a name. Calculate its length. */ |
| int namelen = strlen_round (src->name); |
| sz = struct_sz + namelen; |
| |
| char **av; |
| /* The next field in every *ent is an argv list of "something". |
| Calculate the number of components and how much space the |
| character strings will take. */ |
| int list_len = 0; |
| for (av = src->list; av && *av; av++) |
| { |
| list_len++; |
| sz += sizeof (char **) + strlen_round (*av); |
| } |
| |
| /* NULL terminate if there actually was a list */ |
| if (av) |
| { |
| sz += sizeof (char **); |
| list_len++; |
| } |
| |
| /* Do servent/hostent specific processing */ |
| int protolen = 0; |
| int addr_list_len = 0; |
| if (type == unionent::t_servent) |
| { |
| if (WIN_SERVENT (src)->s_proto) |
| sz += (protolen = strlen_round (WIN_SERVENT (src)->s_proto)); |
| } |
| else if (type == unionent::t_hostent) |
| { |
| /* Calculate the length and storage used for h_addr_list */ |
| for (av = src->h_addr_list; av && *av; av++) |
| { |
| addr_list_len++; |
| sz += sizeof (char **) + DWORD_round (src->h_len); |
| } |
| if (av) |
| { |
| sz += sizeof (char **); |
| addr_list_len++; |
| } |
| } |
| |
| /* Allocate the storage needed. */ |
| if (realloc_ent (dst, sz)) |
| { |
| memset (dst, 0, sz); |
| /* This field is common to all *ent structures but named differently |
| in each, of course. Also, take 64 bit Windows servent weirdness |
| into account. */ |
| if (type == unionent::t_servent) |
| dst->port_proto_addrtype = WIN_SERVENT (src)->s_port; |
| else |
| dst->port_proto_addrtype = src->port_proto_addrtype; |
| |
| char *dp = ((char *) dst) + struct_sz; |
| if (namelen) |
| { |
| /* Copy the name field to dst, using space just beyond the end of |
| the dst structure. */ |
| strcpy (dst->name = dp, src->name); |
| dp += namelen; |
| } |
| |
| /* Copy the 'list' type to dst, using space beyond end of structure |
| + storage for name. */ |
| if (src->list) |
| { |
| char **dav = dst->list = (char **) dp; |
| dp += sizeof (char **) * list_len; |
| for (av = src->list; av && *av; av++) |
| { |
| int len = strlen (*av) + 1; |
| memcpy (*dav++ = dp, *av, len); |
| dp += DWORD_round (len); |
| } |
| } |
| |
| /* Do servent/protoent/hostent specific processing. */ |
| if (type == unionent::t_protoent) |
| debug_printf ("protoent %s %p %y", dst->name, dst->list, dst->port_proto_addrtype); |
| else if (type == unionent::t_servent) |
| { |
| if (WIN_SERVENT (src)->s_proto) |
| { |
| strcpy (dst->s_proto = dp, WIN_SERVENT (src)->s_proto); |
| dp += protolen; |
| } |
| } |
| else if (type == unionent::t_hostent) |
| { |
| /* Transfer h_len and duplicate contents of h_addr_list, using |
| memory after 'list' allocation. */ |
| dst->h_len = src->h_len; |
| char **dav = dst->h_addr_list = (char **) dp; |
| dp += sizeof (char **) * addr_list_len; |
| for (av = src->h_addr_list; av && *av; av++) |
| { |
| memcpy (*dav++ = dp, *av, src->h_len); |
| dp += DWORD_round (src->h_len); |
| } |
| } |
| /* Sanity check that we did our bookkeeping correctly. */ |
| assert ((dp - (char *) dst) == sz); |
| } |
| debug_printf ("duped %sent \"%s\", %p", entnames[type], dst ? dst->name : "<null!>", dst); |
| return dst; |
| } |
| |
| static inline hostent * |
| dup_ent (hostent *src) |
| { |
| return (hostent *) dup_ent (_my_tls.locals.hostent_buf, (unionent *) src, unionent::t_hostent); |
| } |
| |
| static inline protoent * |
| dup_ent (protoent *src) |
| { |
| return (protoent *) dup_ent (_my_tls.locals.protoent_buf, (unionent *) src, unionent::t_protoent); |
| } |
| |
| static inline servent * |
| dup_ent (servent *src) |
| { |
| return (servent *) dup_ent (_my_tls.locals.servent_buf, (unionent *) src, unionent::t_servent); |
| } |
| |
| /* exported as getprotobyname: standards? */ |
| extern "C" struct protoent * |
| cygwin_getprotobyname (const char *p) |
| { |
| __try |
| { |
| return dup_ent (getprotobyname (p)); |
| } |
| __except (EFAULT) {} |
| __endtry |
| return NULL; |
| } |
| |
| /* exported as getprotobynumber: standards? */ |
| extern "C" struct protoent * |
| cygwin_getprotobynumber (int number) |
| { |
| return dup_ent (getprotobynumber (number)); |
| } |
| |
| #ifndef SIO_BASE_HANDLE |
| #define SIO_BASE_HANDLE _WSAIOR(IOC_WS2,34) |
| #endif |
| |
| bool |
| fdsock (cygheap_fdmanip& fd, const device *dev, SOCKET soc) |
| { |
| fd = build_fh_dev (*dev); |
| if (!fd.isopen ()) |
| return false; |
| |
| /* Usually sockets are inheritable IFS objects. Unfortunately some virus |
| scanners or other network-oriented software replace normal sockets |
| with their own kind, which is running through a filter driver called |
| "layered service provider" (LSP). |
| |
| LSP sockets are not kernel objects. They are typically not marked as |
| inheritable, nor are they IFS handles. They are in fact not inheritable |
| to child processes, and it does not help to mark them inheritable via |
| SetHandleInformation. Subsequent socket calls in the child process fail |
| with error 10038, WSAENOTSOCK. |
| |
| There's a neat way to workaround these annoying LSP sockets. WSAIoctl |
| allows to fetch the underlying base socket, which is a normal, inheritable |
| IFS handle. So we fetch the base socket, duplicate it, and close the |
| original socket. Now we have a standard IFS socket which (hopefully) |
| works as expected. |
| |
| If that doesn't work for some reason, mark the sockets for duplication |
| via WSADuplicateSocket/WSASocket. This requires to start the child |
| process in SUSPENDED state so we only do this if really necessary. */ |
| DWORD flags; |
| bool fixup = false; |
| if (!GetHandleInformation ((HANDLE) soc, &flags) |
| || !(flags & HANDLE_FLAG_INHERIT)) |
| { |
| int ret; |
| SOCKET base_soc; |
| DWORD bret; |
| |
| fixup = true; |
| debug_printf ("LSP handle: %p", soc); |
| ret = WSAIoctl (soc, SIO_BASE_HANDLE, NULL, 0, (void *) &base_soc, |
| sizeof (base_soc), &bret, NULL, NULL); |
| if (ret) |
| debug_printf ("WSAIoctl: %u", WSAGetLastError ()); |
| else if (base_soc != soc) |
| { |
| if (GetHandleInformation ((HANDLE) base_soc, &flags) |
| && (flags & HANDLE_FLAG_INHERIT)) |
| { |
| if (!DuplicateHandle (GetCurrentProcess (), (HANDLE) base_soc, |
| GetCurrentProcess (), (PHANDLE) &base_soc, |
| 0, TRUE, DUPLICATE_SAME_ACCESS)) |
| debug_printf ("DuplicateHandle failed, %E"); |
| else |
| { |
| closesocket (soc); |
| soc = base_soc; |
| fixup = false; |
| } |
| } |
| } |
| } |
| fd->set_io_handle ((HANDLE) soc); |
| if (!((fhandler_socket *) fd)->init_events ()) |
| return false; |
| if (fixup) |
| ((fhandler_socket *) fd)->init_fixup_before (); |
| fd->set_flags (O_RDWR | O_BINARY); |
| debug_printf ("fd %d, name '%s', soc %p", (int) fd, dev->name (), soc); |
| |
| /* Raise default buffer sizes (instead of WinSock default 8K). |
| |
| 64K appear to have the best size/performance ratio for a default |
| value. Tested with ssh/scp on Vista over Gigabit LAN. |
| |
| NOTE. If the SO_RCVBUF size exceeds 65535(*), and if the socket is |
| connected to a remote machine, then calling WSADuplicateSocket on |
| fork/exec fails with WinSock error 10022, WSAEINVAL. Fortunately |
| we don't use WSADuplicateSocket anymore, rather we just utilize |
| handle inheritance. An explanation for this weird behaviour would |
| be nice, though. |
| |
| NOTE 2. Testing on x86_64 (Vista, 2008 R2, W8) indicates that |
| this is no problem on 64 bit. So we set the default buffer size to |
| the default values in current 3.x Linux versions. |
| |
| NOTE 3. Setting the window size to 65535 results in extremely bad |
| performance for apps that send data in multiples of Kb, as they |
| eventually end up sending 1 byte on the network and naggle + delay |
| ack kicks in. For example, iperf on a 10Gb network gives only 10 |
| Mbits/sec with a 65535 send buffer. We want this to be a multiple |
| of 1k, but since 64k breaks WSADuplicateSocket we use 63Kb. |
| |
| NOTE 4. Tests with iperf uncover a problem in setting the SO_RCVBUF |
| and SO_SNDBUF sizes. Windows is using autotuning since Windows Vista. |
| Manually setting SO_RCVBUF/SO_SNDBUF disables autotuning and leads to |
| inferior send/recv performance in scenarios with larger RTTs, as is |
| basically standard when accessing the internet. For a discussion, |
| see https://cygwin.com/ml/cygwin-patches/2017-q1/msg00010.html. |
| |
| (*) Maximum normal TCP window size. Coincidence? */ |
| #ifdef __x86_64__ |
| ((fhandler_socket *) fd)->rmem () = 212992; |
| ((fhandler_socket *) fd)->wmem () = 212992; |
| #else |
| ((fhandler_socket *) fd)->rmem () = 64512; |
| ((fhandler_socket *) fd)->wmem () = 64512; |
| #endif |
| #if 0 /* See NOTE 4 above. */ |
| int size; |
| |
| if (::setsockopt (soc, SOL_SOCKET, SO_RCVBUF, |
| (char *) &((fhandler_socket *) fd)->rmem (), sizeof (int))) |
| { |
| debug_printf ("setsockopt(SO_RCVBUF) failed, %u", WSAGetLastError ()); |
| if (::getsockopt (soc, SOL_SOCKET, SO_RCVBUF, |
| (char *) &((fhandler_socket *) fd)->rmem (), |
| (size = sizeof (int), &size))) |
| system_printf ("getsockopt(SO_RCVBUF) failed, %u", WSAGetLastError ()); |
| } |
| if (::setsockopt (soc, SOL_SOCKET, SO_SNDBUF, |
| (char *) &((fhandler_socket *) fd)->wmem (), sizeof (int))) |
| { |
| debug_printf ("setsockopt(SO_SNDBUF) failed, %u", WSAGetLastError ()); |
| if (::getsockopt (soc, SOL_SOCKET, SO_SNDBUF, |
| (char *) &((fhandler_socket *) fd)->wmem (), |
| (size = sizeof (int), &size))) |
| system_printf ("getsockopt(SO_SNDBUF) failed, %u", WSAGetLastError ()); |
| } |
| #endif |
| /* A unique ID is necessary to recognize fhandler entries which are |
| duplicated by dup(2) or fork(2). This is used in BSD flock calls |
| to identify the descriptor. */ |
| ((fhandler_socket *) fd)->set_unique_id (); |
| |
| return true; |
| } |
| |
| /* exported as socket: standards? */ |
| extern "C" int |
| cygwin_socket (int af, int type, int protocol) |
| { |
| int res = -1; |
| SOCKET soc = 0; |
| |
| int flags = type & _SOCK_FLAG_MASK; |
| type &= ~_SOCK_FLAG_MASK; |
| |
| debug_printf ("socket (%d, %d (flags %y), %d)", af, type, flags, protocol); |
| |
| if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0) |
| { |
| set_errno (EINVAL); |
| goto done; |
| } |
| |
| soc = socket (af == AF_LOCAL ? AF_INET : af, type, |
| af == AF_LOCAL ? 0 : protocol); |
| |
| if (soc == INVALID_SOCKET) |
| { |
| set_winsock_errno (); |
| goto done; |
| } |
| |
| const device *dev; |
| |
| if (af == AF_LOCAL) |
| dev = type == SOCK_STREAM ? stream_dev : dgram_dev; |
| else |
| dev = type == SOCK_STREAM ? tcp_dev : udp_dev; |
| |
| { |
| cygheap_fdnew fd; |
| if (fd < 0 || !fdsock (fd, dev, soc)) |
| closesocket (soc); |
| else |
| { |
| ((fhandler_socket *) fd)->set_addr_family (af); |
| ((fhandler_socket *) fd)->set_socket_type (type); |
| if (flags & SOCK_NONBLOCK) |
| ((fhandler_socket *) fd)->set_nonblocking (true); |
| if (flags & SOCK_CLOEXEC) |
| ((fhandler_socket *) fd)->set_close_on_exec (true); |
| if (type == SOCK_DGRAM) |
| { |
| /* Workaround the problem that a missing listener on a UDP socket |
| in a call to sendto will result in select/WSAEnumNetworkEvents |
| reporting that the socket has pending data and a subsequent call |
| to recvfrom will return -1 with error set to WSAECONNRESET. |
| |
| This problem is a regression introduced in Windows 2000. |
| Instead of fixing the problem, a new socket IOCTL code has |
| been added, see http://support.microsoft.com/kb/263823 */ |
| BOOL cr = FALSE; |
| DWORD blen; |
| if (WSAIoctl (soc, SIO_UDP_CONNRESET, &cr, sizeof cr, NULL, 0, |
| &blen, NULL, NULL) == SOCKET_ERROR) |
| debug_printf ("Reset SIO_UDP_CONNRESET: WinSock error %u", |
| WSAGetLastError ()); |
| } |
| res = fd; |
| } |
| } |
| |
| done: |
| syscall_printf ("%R = socket(%d, %d (flags %y), %d)", |
| res, af, type, flags, protocol); |
| return res; |
| } |
| |
| /* exported as sendto: standards? */ |
| extern "C" ssize_t |
| cygwin_sendto (int fd, const void *buf, size_t len, int flags, |
| const struct sockaddr *to, socklen_t tolen) |
| { |
| ssize_t res = -1; |
| |
| pthread_testcancel (); |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| res = fh->sendto (buf, len, flags, to, tolen); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%lR = sendto(%d, %p, %ld, %y, %p, %d)", |
| res, fd, buf, len, flags, to, tolen); |
| return res; |
| } |
| |
| /* exported as recvfrom: standards? */ |
| extern "C" ssize_t |
| cygwin_recvfrom (int fd, void *buf, size_t len, int flags, |
| struct sockaddr *from, socklen_t *fromlen) |
| { |
| ssize_t res = -1; |
| |
| pthread_testcancel (); |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| /* Originally we shortcircuited here if res == 0. |
| Allow 0 bytes buffer. This is valid in POSIX and handled in |
| fhandler_socket::recv_internal. If we shortcircuit, we fail |
| to deliver valid error conditions and peer address. */ |
| res = fh->recvfrom (buf, len, flags, from, fromlen); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%lR = recvfrom(%d, %p, %ld, %y, %p, %p)", |
| res, fd, buf, len, flags, from, fromlen); |
| return res; |
| } |
| |
| static int |
| convert_ws1_ip_optname (int optname) |
| { |
| static int ws2_optname[] = |
| { |
| 0, |
| IP_OPTIONS, |
| IP_MULTICAST_IF, |
| IP_MULTICAST_TTL, |
| IP_MULTICAST_LOOP, |
| IP_ADD_MEMBERSHIP, |
| IP_DROP_MEMBERSHIP, |
| IP_TTL, |
| IP_TOS, |
| IP_DONTFRAGMENT |
| }; |
| return (optname < 1 || optname > _WS1_IP_DONTFRAGMENT) |
| ? optname |
| : ws2_optname[optname]; |
| } |
| |
| /* exported as setsockopt: standards? */ |
| extern "C" int |
| cygwin_setsockopt (int fd, int level, int optname, const void *optval, |
| socklen_t optlen) |
| { |
| int res = -1; |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (!fh) |
| __leave; |
| |
| /* Switch off the AF_LOCAL handshake and thus SO_PEERCRED handling |
| for AF_LOCAL/SOCK_STREAM sockets. This allows to handle special |
| situations in which connect is called before a listening socket |
| accepts connections. |
| FIXME: In the long run we should find a more generic solution which |
| doesn't require a blocking handshake in accept/connect to exchange |
| SO_PEERCRED credentials. */ |
| if (level == SOL_SOCKET && optname == SO_PEERCRED) |
| { |
| if (optval || optlen) |
| set_errno (EINVAL); |
| else |
| res = fh->af_local_set_no_getpeereid (); |
| __leave; |
| } |
| |
| /* Old applications still use the old WinSock1 IPPROTO_IP values. */ |
| if (level == IPPROTO_IP && CYGWIN_VERSION_CHECK_FOR_USING_WINSOCK1_VALUES) |
| optname = convert_ws1_ip_optname (optname); |
| |
| /* Per POSIX we must not be able to reuse a complete duplicate of a |
| local TCP address (same IP, same port), even if SO_REUSEADDR has been |
| set. That's unfortunately possible in WinSock, and this has never |
| been changed to maintain backward compatibility. Instead, the |
| SO_EXCLUSIVEADDRUSE option has been added to allow an application to |
| request POSIX standard behaviour in the non-SO_REUSEADDR case. |
| |
| However, the WinSock standard behaviour of stream socket binding |
| is equivalent to the POSIX behaviour as if SO_REUSEADDR has been set. |
| So what we do here is to note that SO_REUSEADDR has been set, but not |
| actually hand over the request to WinSock. This is tested in |
| fhandler_socket::bind(), so that SO_EXCLUSIVEADDRUSE can be set if |
| the application did not set SO_REUSEADDR. This should reflect the |
| POSIX socket binding behaviour as close as possible with WinSock. */ |
| if (level == SOL_SOCKET && optname == SO_REUSEADDR |
| && fh->get_socket_type () == SOCK_STREAM) |
| res = 0; |
| else |
| res = setsockopt (fh->get_socket (), level, optname, |
| (const char *) optval, optlen); |
| |
| if (optlen == sizeof (int)) |
| syscall_printf ("setsockopt optval=%x", *(int *) optval); |
| |
| if (res) |
| { |
| /* KB 248611: |
| |
| Windows 2000 and above don't support setting the IP_TOS field |
| with setsockopt. Additionally, TOS was always (also under 9x |
| and NT) only implemented for UDP and ICMP, never for TCP. |
| |
| The difference is that beginning with Windows 2000 the |
| setsockopt call returns WinSock error 10022, WSAEINVAL when |
| trying to set the IP_TOS field, instead of just ignoring the |
| call. This is *not* explained in KB 248611, but only in KB |
| 258978. |
| |
| Either case, the official workaround is to add a new registry |
| DWORD value HKLM/System/CurrentControlSet/Services/Tcpip/... |
| ... Parameters/DisableUserTOSSetting, set to 0, and reboot. |
| |
| Sidenote: The reasoning for dropping ToS in Win2K is that ToS |
| per RFC 1349 is incompatible with DiffServ per RFC 2474/2475. |
| |
| We just ignore the return value of setting IP_TOS entirely. |
| |
| CV 2014-04-16: Same for IPV6_TCLASS |
| FIXME: Same for IPV6_RECVTCLASS? */ |
| if (level == IPPROTO_IP && optname == IP_TOS |
| && WSAGetLastError () == WSAEINVAL) |
| { |
| debug_printf ("Faked IP_TOS success"); |
| res = 0; |
| } |
| else if (level == IPPROTO_IPV6 && optname == IPV6_TCLASS |
| && WSAGetLastError () == WSAENOPROTOOPT) |
| { |
| debug_printf ("Faked IPV6_TCLASS success"); |
| res = 0; |
| } |
| else |
| set_winsock_errno (); |
| } |
| else if (level == SOL_SOCKET) |
| switch (optname) |
| { |
| case SO_REUSEADDR: |
| fh->saw_reuseaddr (*(int *) optval); |
| break; |
| case SO_RCVBUF: |
| fh->rmem (*(int *) optval); |
| break; |
| case SO_SNDBUF: |
| fh->wmem (*(int *) optval); |
| break; |
| default: |
| break; |
| } |
| } |
| __except (EFAULT) |
| { |
| res = -1; |
| } |
| __endtry |
| syscall_printf ("%R = setsockopt(%d, %d, %y, %p, %d)", |
| res, fd, level, optname, optval, optlen); |
| return res; |
| } |
| |
| /* exported as getsockopt: standards? */ |
| extern "C" int |
| cygwin_getsockopt (int fd, int level, int optname, void *optval, |
| socklen_t *optlen) |
| { |
| int res = -1; |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (!fh) |
| __leave; |
| if (optname == SO_PEERCRED && level == SOL_SOCKET) |
| { |
| struct ucred *cred = (struct ucred *) optval; |
| res = fh->getpeereid (&cred->pid, &cred->uid, &cred->gid); |
| __leave; |
| } |
| else if (optname == SO_REUSEADDR && level == SOL_SOCKET) |
| { |
| unsigned int *reuseaddr = (unsigned int *) optval; |
| *reuseaddr = fh->saw_reuseaddr(); |
| *optlen = sizeof *reuseaddr; |
| res = 0; |
| __leave; |
| } |
| /* Old applications still use the old WinSock1 IPPROTO_IP values. */ |
| if (level == IPPROTO_IP && CYGWIN_VERSION_CHECK_FOR_USING_WINSOCK1_VALUES) |
| optname = convert_ws1_ip_optname (optname); |
| res = getsockopt (fh->get_socket (), level, optname, (char *) optval, |
| (int *) optlen); |
| if (res == SOCKET_ERROR) |
| set_winsock_errno (); |
| else if (level == SOL_SOCKET && optname == SO_ERROR) |
| { |
| int *e = (int *) optval; |
| debug_printf ("WinSock SO_ERROR = %d", *e); |
| *e = find_winsock_errno (*e); |
| } |
| else if (*optlen == 1) |
| { |
| /* Regression in Vista and later: instead of a 4 byte BOOL value, |
| a 1 byte BOOLEAN value is returned, in contrast to older systems |
| and the documentation. Since an int type is expected by the |
| calling application, we convert the result here. For some reason |
| only three BSD-compatible socket options seem to be affected. */ |
| if ((level == SOL_SOCKET |
| && (optname == SO_KEEPALIVE || optname == SO_DONTROUTE)) |
| || (level == IPPROTO_TCP && optname == TCP_NODELAY)) |
| { |
| BOOLEAN *in = (BOOLEAN *) optval; |
| int *out = (int *) optval; |
| *out = *in; |
| *optlen = 4; |
| } |
| } |
| } |
| __except (EFAULT) |
| { |
| res = -1; |
| } |
| __endtry |
| syscall_printf ("%R = getsockopt(%d, %d, %y, %p, %p)", |
| res, fd, level, optname, optval, optlen); |
| return res; |
| } |
| |
| /* POSIX.1-2001 */ |
| extern "C" int |
| sockatmark (int fd) |
| { |
| int ret; |
| |
| fhandler_socket *fh = get (fd); |
| if (fh && fh->ioctl (SIOCATMARK, &ret) != -1) |
| return ret; |
| return -1; |
| } |
| |
| /* BSD */ |
| extern "C" int |
| getpeereid (int fd, uid_t *euid, gid_t *egid) |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| return fh->getpeereid (NULL, euid, egid); |
| return -1; |
| } |
| |
| /* exported as connect: standards? */ |
| extern "C" int |
| cygwin_connect (int fd, const struct sockaddr *name, socklen_t namelen) |
| { |
| int res = -1; |
| |
| pthread_testcancel (); |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| res = fh->connect (name, namelen); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%R = connect(%d, %p, %d)", res, fd, name, namelen); |
| return res; |
| } |
| |
| /* exported as getservbyname: standards? */ |
| extern "C" struct servent * |
| cygwin_getservbyname (const char *name, const char *proto) |
| { |
| servent *res = NULL; |
| |
| __try |
| { |
| res = dup_ent (getservbyname (name, proto)); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%p = getservbyname (%s, %s)", res, name, proto); |
| return res; |
| } |
| |
| /* exported as getservbyport: standards? */ |
| extern "C" struct servent * |
| cygwin_getservbyport (int port, const char *proto) |
| { |
| servent *res = NULL; |
| |
| __try |
| { |
| res = dup_ent (getservbyport (port, proto)); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%p = getservbyport (%d, %s)", res, port, proto); |
| return res; |
| } |
| |
| extern "C" int |
| cygwin_gethostname (char *name, size_t len) |
| { |
| int res = -1; |
| |
| __try |
| { |
| if (gethostname (name, len)) |
| { |
| DWORD local_len = len; |
| |
| if (!GetComputerNameExA (ComputerNameDnsFullyQualified, name, |
| &local_len)) |
| { |
| if (GetLastError () == ERROR_MORE_DATA) |
| set_errno (ENAMETOOLONG); |
| else |
| set_winsock_errno (); |
| __leave; |
| } |
| } |
| debug_printf ("name %s", name); |
| res = 0; |
| } |
| __except (EFAULT) {} |
| __endtry |
| return res; |
| } |
| |
| extern "C" int |
| sethostname (const char *name, size_t len) |
| { |
| WCHAR wname[MAX_COMPUTERNAME_LENGTH + 1]; |
| |
| sys_mbstowcs (wname, MAX_COMPUTERNAME_LENGTH + 1, name, len); |
| if (!SetComputerNameExW (ComputerNamePhysicalDnsHostname, wname)) |
| { |
| __seterrno (); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* exported as gethostbyname: standards? */ |
| extern "C" struct hostent * |
| cygwin_gethostbyname (const char *name) |
| { |
| unsigned char tmp_addr[4]; |
| struct hostent tmp, *h; |
| char *tmp_aliases[1] = {0}; |
| char *tmp_addr_list[2] = {0,0}; |
| unsigned int a, b, c, d; |
| char dummy; |
| hostent *res = NULL; |
| |
| __try |
| { |
| if (sscanf (name, "%u.%u.%u.%u%c", &a, &b, &c, &d, &dummy) != 4 |
| || a >= 256 || b >= 256 || c >= 256 || d >= 256) |
| h = gethostbyname (name); |
| else |
| { |
| /* In case you don't have DNS, at least x.x.x.x still works */ |
| memset (&tmp, 0, sizeof (tmp)); |
| tmp_addr[0] = a; |
| tmp_addr[1] = b; |
| tmp_addr[2] = c; |
| tmp_addr[3] = d; |
| tmp_addr_list[0] = (char *) tmp_addr; |
| tmp.h_name = name; |
| tmp.h_aliases = tmp_aliases; |
| tmp.h_addrtype = 2; |
| tmp.h_length = 4; |
| tmp.h_addr_list = tmp_addr_list; |
| h = &tmp; |
| } |
| |
| res = dup_ent (h); |
| if (res) |
| debug_printf ("h_name %s", res->h_name); |
| else |
| { |
| debug_printf ("dup_ent returned NULL for name %s, h %p", name, h); |
| set_host_errno (); |
| } |
| } |
| __except (EFAULT) |
| { |
| res = NULL; |
| } |
| __endtry |
| return res; |
| } |
| |
| /* exported as gethostbyaddr: standards? */ |
| extern "C" struct hostent * |
| cygwin_gethostbyaddr (const void *addr, socklen_t len, int type) |
| { |
| hostent *res = NULL; |
| |
| __try |
| { |
| res = dup_ent (gethostbyaddr ((const char *) addr, len, type)); |
| if (res) |
| debug_printf ("h_name %s", res->h_name); |
| else |
| set_host_errno (); |
| } |
| __except (EFAULT) |
| { |
| res = NULL; |
| } |
| __endtry |
| return res; |
| } |
| |
| static void |
| memcpy4to6 (char *dst, const u_int8_t *src) |
| { |
| const unsigned int h[] = {0, 0, htonl (0xFFFF)}; |
| memcpy (dst, h, 12); |
| memcpy (dst + 12, src, NS_INADDRSZ); |
| } |
| |
| /* gethostby_specials: RFC 6761 |
| Handles numerical addresses and special names for gethostbyname2 */ |
| static hostent * |
| gethostby_specials (const char *name, const int af, |
| const int addrsize_in, const int addrsize_out) |
| { |
| int namelen = strlen (name); |
| /* Ignore a final '.' */ |
| if ((namelen == 0) || ((namelen -= (name[namelen - 1] == '.')) == 0)) |
| { |
| set_errno (EINVAL); |
| h_errno = NETDB_INTERNAL; |
| return NULL; |
| } |
| |
| int res; |
| u_int8_t address[NS_IN6ADDRSZ]; |
| /* Test for numerical addresses */ |
| res = cygwin_inet_pton(af, name, address); |
| /* Test for special domain names */ |
| if (res != 1) |
| { |
| { |
| char const match[] = "invalid"; |
| int const matchlen = sizeof(match) - 1; |
| int start = namelen - matchlen; |
| if ((start >= 0) && ((start == 0) || (name[start-1] == '.')) |
| && (strncasecmp (&name[start], match , matchlen) == 0)) |
| { |
| h_errno = HOST_NOT_FOUND; |
| return NULL; |
| } |
| } |
| { |
| char const match[] = "localhost"; |
| int const matchlen = sizeof(match) - 1; |
| int start = namelen - matchlen; |
| if ((start >= 0) && ((start == 0) || (name[start-1] == '.')) |
| && (strncasecmp (&name[start], match , matchlen) == 0)) |
| { |
| res = 1; |
| if (af == AF_INET) |
| { |
| address[0] = 127; |
| address[1] = address[2] = 0; |
| address[3] = 1; |
| } |
| else |
| { |
| memset (address, 0, NS_IN6ADDRSZ); |
| address[NS_IN6ADDRSZ-1] = 1; |
| } |
| } |
| } |
| } |
| if (res != 1) |
| return NULL; |
| |
| int const alias_count = 0, address_count = 1; |
| char * string_ptr; |
| int sz = DWORD_round (sizeof(hostent)) |
| + sizeof (char *) * (alias_count + address_count + 2) |
| + namelen + 1 |
| + address_count * addrsize_out; |
| hostent *ret = realloc_ent (sz, (hostent *) NULL); |
| if (!ret) |
| { |
| /* errno is already set */ |
| h_errno = NETDB_INTERNAL; |
| return NULL; |
| } |
| |
| ret->h_addrtype = af; |
| ret->h_length = addrsize_out; |
| ret->h_aliases = (char **) (((char *) ret) + DWORD_round (sizeof(hostent))); |
| ret->h_addr_list = ret->h_aliases + alias_count + 1; |
| string_ptr = (char *) (ret->h_addr_list + address_count + 1); |
| ret->h_name = string_ptr; |
| |
| memcpy (string_ptr, name, namelen); |
| string_ptr[namelen] = 0; |
| string_ptr += namelen + 1; |
| |
| ret->h_addr_list[0] = string_ptr; |
| if (addrsize_in != addrsize_out) |
| { |
| memcpy4to6 (string_ptr, address); |
| ret->h_addrtype = AF_INET6; |
| } |
| else |
| memcpy (string_ptr, address, addrsize_out); |
| |
| ret->h_aliases[alias_count] = NULL; |
| ret->h_addr_list[address_count] = NULL; |
| |
| return ret; |
| } |
| |
| static hostent * |
| gethostby_helper (const char *name, const int af, const int type, |
| const int addrsize_in, const int addrsize_out) |
| { |
| /* Get the data from the name server */ |
| const int maxcount = 3; |
| int old_errno, ancount = 0, anlen = 1024, msgsize = 0; |
| unsigned char *ptr, *msg = NULL; |
| int sz; |
| hostent *ret; |
| char *string_ptr; |
| |
| while ((anlen > msgsize) && (ancount++ < maxcount)) |
| { |
| msgsize = anlen; |
| ptr = (unsigned char *) realloc (msg, msgsize); |
| if (ptr == NULL ) |
| { |
| old_errno = errno; |
| free (msg); |
| set_errno (old_errno); |
| h_errno = NETDB_INTERNAL; |
| return NULL; |
| } |
| msg = ptr; |
| anlen = res_search (name, ns_c_in, type, msg, msgsize); |
| } |
| |
| if (ancount >= maxcount) |
| { |
| free (msg); |
| h_errno = NO_RECOVERY; |
| return NULL; |
| } |
| if (anlen < 0) /* errno and h_errno are set */ |
| { |
| old_errno = errno; |
| free (msg); |
| set_errno (old_errno); |
| return NULL; |
| } |
| unsigned char *eomsg = msg + anlen - 1; |
| |
| /* We scan the answer records to determine the required memory size. |
| They can be corrupted and we don't fully trust that the message |
| follows the standard exactly. glibc applies some checks that |
| we emulate. |
| The answers are copied in the hostent structure in a second scan. |
| To simplify the second scan we store information as follows: |
| - "class" is replaced by the compressed name size |
| - the first 16 bits of the "ttl" store the expanded name size + 1 |
| - the last 16 bits of the "ttl" store the offset to the next valid record. |
| Note that "type" is rewritten in host byte order. */ |
| |
| class record { |
| public: |
| unsigned type: 16; // type |
| unsigned complen: 16; // class or compressed length |
| unsigned namelen1: 16; // expanded length (with final 0) |
| unsigned next_o: 16; // offset to next valid |
| unsigned size: 16; // data size |
| unsigned char data[]; // data |
| record * next () { return (record *) (((char *) this) + next_o); } |
| void set_next ( record * nxt) { next_o = ((char *) nxt) - ((char *) this); } |
| unsigned char *name () { return (unsigned char *) |
| (((char *) this) - complen); } |
| }; |
| |
| record * anptr = NULL, * prevptr = NULL, * curptr; |
| int i, alias_count = 0, string_size = 0, address_count = 0; |
| int namelen1 = 0, address_len = 0, antype, anclass, ansize; |
| unsigned complen; |
| |
| /* Get the count of answers */ |
| ancount = ntohs (((HEADER *) msg)->ancount); |
| |
| /* Skip the question, it was verified by res_send */ |
| ptr = msg + sizeof (HEADER); |
| if ((complen = dn_skipname (ptr, eomsg)) < 0) |
| goto corrupted; |
| /* Point to the beginning of the answer section */ |
| ptr += complen + NS_QFIXEDSZ; |
| |
| /* Scan the answer records to determine the sizes */ |
| for (i = 0; i < ancount; i++, ptr = curptr->data + ansize) |
| { |
| if ((complen = dn_skipname (ptr, eomsg)) < 0) |
| goto corrupted; |
| |
| curptr = (record *) (ptr + complen); |
| antype = ntohs (curptr->type); |
| anclass = ntohs (curptr->complen); |
| ansize = ntohs (curptr->size); |
| /* Class must be internet */ |
| if (anclass != ns_c_in) |
| continue; |
| |
| curptr->complen = complen; |
| if ((namelen1 = dn_length1 (msg, eomsg, curptr-> name())) <= 0) |
| goto corrupted; |
| |
| if (antype == ns_t_cname) |
| { |
| alias_count++; |
| string_size += namelen1; |
| } |
| else if (antype == type) |
| { |
| ansize = ntohs (curptr->size); |
| if (ansize != addrsize_in) |
| continue; |
| if (address_count == 0) |
| { |
| address_len = namelen1; |
| string_size += namelen1; |
| } |
| else if (address_len != namelen1) |
| continue; |
| address_count++; |
| } |
| /* Update the records */ |
| curptr->type = antype; /* Host byte order */ |
| curptr->namelen1 = namelen1; |
| if (! anptr) |
| anptr = prevptr = curptr; |
| else |
| { |
| prevptr->set_next (curptr); |
| prevptr = curptr; |
| } |
| } |
| |
| /* If there is no address, quit */ |
| if (address_count == 0) |
| { |
| free (msg); |
| h_errno = NO_DATA; |
| return NULL; |
| } |
| |
| /* Determine the total size */ |
| sz = DWORD_round (sizeof(hostent)) |
| + sizeof (char *) * (alias_count + address_count + 2) |
| + string_size |
| + address_count * addrsize_out; |
| |
| ret = realloc_ent (sz, (hostent *) NULL); |
| if (! ret) |
| { |
| old_errno = errno; |
| free (msg); |
| set_errno (old_errno); |
| h_errno = NETDB_INTERNAL; |
| return NULL; |
| } |
| |
| ret->h_addrtype = af; |
| ret->h_length = addrsize_out; |
| ret->h_aliases = (char **) (((char *) ret) + DWORD_round (sizeof(hostent))); |
| ret->h_addr_list = ret->h_aliases + alias_count + 1; |
| string_ptr = (char *) (ret->h_addr_list + address_count + 1); |
| |
| /* Rescan the answers */ |
| alias_count = address_count = 0; |
| prevptr->set_next (prevptr + 1); |
| |
| for (curptr = anptr; curptr <= prevptr; curptr = curptr->next ()) |
| { |
| antype = curptr->type; |
| if (antype == ns_t_cname) |
| { |
| dn_expand (msg, eomsg, curptr->name (), string_ptr, curptr->namelen1); |
| ret->h_aliases[alias_count++] = string_ptr; |
| string_ptr += curptr->namelen1; |
| } |
| else |
| { |
| if (address_count == 0) |
| { |
| dn_expand (msg, eomsg, curptr->name (), string_ptr, |
| curptr->namelen1); |
| ret->h_name = string_ptr; |
| string_ptr += curptr->namelen1; |
| } |
| ret->h_addr_list[address_count++] = string_ptr; |
| if (addrsize_in != addrsize_out) |
| { |
| memcpy4to6 (string_ptr, curptr->data); |
| ret->h_addrtype = AF_INET6; |
| } |
| else |
| memcpy (string_ptr, curptr->data, addrsize_in); |
| string_ptr += addrsize_out; |
| } |
| } |
| |
| free (msg); |
| |
| ret->h_aliases[alias_count] = NULL; |
| ret->h_addr_list[address_count] = NULL; |
| |
| return ret; |
| |
| corrupted: |
| free (msg); |
| /* Hopefully message corruption errors are temporary. |
| Should it be NO_RECOVERY ? */ |
| h_errno = TRY_AGAIN; |
| return NULL; |
| } |
| |
| /* gethostbyname2: standards? */ |
| extern "C" struct hostent * |
| gethostbyname2 (const char *name, int af) |
| { |
| hostent *res = NULL; |
| |
| __try |
| { |
| if (!(_res.options & RES_INIT)) |
| res_init(); |
| |
| bool v4to6 = _res.options & RES_USE_INET6; |
| int type, addrsize_in, addrsize_out; |
| |
| switch (af) |
| { |
| case AF_INET: |
| addrsize_in = NS_INADDRSZ; |
| addrsize_out = (v4to6) ? NS_IN6ADDRSZ : NS_INADDRSZ; |
| type = ns_t_a; |
| break; |
| case AF_INET6: |
| addrsize_in = addrsize_out = NS_IN6ADDRSZ; |
| type = ns_t_aaaa; |
| break; |
| default: |
| set_errno (EAFNOSUPPORT); |
| h_errno = NETDB_INTERNAL; |
| __leave; |
| } |
| |
| h_errno = NETDB_SUCCESS; |
| res = gethostby_specials (name, af, addrsize_in, addrsize_out); |
| if ((res == NULL) && (h_errno == NETDB_SUCCESS)) |
| res = gethostby_helper (name, af, type, addrsize_in, addrsize_out); |
| } |
| __except (EFAULT) {} |
| __endtry |
| return res; |
| } |
| |
| /* exported as accept: standards? */ |
| extern "C" int |
| cygwin_accept (int fd, struct sockaddr *peer, socklen_t *len) |
| { |
| int res = -1; |
| |
| pthread_testcancel (); |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| res = fh->accept4 (peer, len, |
| fh->is_nonblocking () ? SOCK_NONBLOCK : 0); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%R = accept(%d, %p, %p)", res, fd, peer, len); |
| return res; |
| } |
| |
| extern "C" int |
| accept4 (int fd, struct sockaddr *peer, socklen_t *len, int flags) |
| { |
| int res = -1; |
| |
| pthread_testcancel (); |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (!fh) |
| __leave; |
| if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0) |
| set_errno (EINVAL); |
| else |
| res = fh->accept4 (peer, len, flags); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%R = accept4(%d, %p, %p, %y)", res, fd, peer, len, flags); |
| return res; |
| } |
| |
| /* exported as bind: standards? */ |
| extern "C" int |
| cygwin_bind (int fd, const struct sockaddr *my_addr, socklen_t addrlen) |
| { |
| int res = -1; |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| res = fh->bind (my_addr, addrlen); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%R = bind(%d, %p, %d)", res, fd, my_addr, addrlen); |
| return res; |
| } |
| |
| /* exported as getsockname: standards? */ |
| extern "C" int |
| cygwin_getsockname (int fd, struct sockaddr *addr, socklen_t *namelen) |
| { |
| int res = -1; |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| res = fh->getsockname (addr, namelen); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%R =getsockname (%d, %p, %p)", res, fd, addr, namelen); |
| return res; |
| } |
| |
| /* exported as listen: standards? */ |
| extern "C" int |
| cygwin_listen (int fd, int backlog) |
| { |
| int res = -1; |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| res = fh->listen (backlog); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%R = listen(%d, %d)", res, fd, backlog); |
| return res; |
| } |
| |
| /* exported as shutdown: standards? */ |
| extern "C" int |
| cygwin_shutdown (int fd, int how) |
| { |
| int res = -1; |
| |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| res = fh->shutdown (how); |
| syscall_printf ("%R = shutdown(%d, %d)", res, fd, how); |
| return res; |
| } |
| |
| /* exported as hstrerror: BSD 4.3 */ |
| extern "C" const char * |
| cygwin_hstrerror (int err) |
| { |
| int i; |
| |
| for (i = 0; host_errmap[i].e != 0; ++i) |
| if (err == host_errmap[i].e) |
| break; |
| |
| return host_errmap[i].s; |
| } |
| |
| /* exported as herror: BSD 4.3 */ |
| extern "C" void |
| cygwin_herror (const char *s) |
| { |
| __try |
| { |
| if (cygheap->fdtab.not_open (2)) |
| return; |
| |
| if (s) |
| { |
| write (2, s, strlen (s)); |
| write (2, ": ", 2); |
| } |
| |
| const char *h_errstr = cygwin_hstrerror (h_errno); |
| |
| if (!h_errstr) |
| switch (h_errno) |
| { |
| case NETDB_INTERNAL: |
| h_errstr = "Resolver internal error"; |
| break; |
| case NETDB_SUCCESS: |
| h_errstr = "Resolver error 0 (no error)"; |
| break; |
| default: |
| h_errstr = "Unknown resolver error"; |
| break; |
| } |
| write (2, h_errstr, strlen (h_errstr)); |
| write (2, "\n", 1); |
| } |
| __except (NO_ERROR) {} |
| __endtry |
| } |
| |
| /* exported as getpeername: standards? */ |
| extern "C" int |
| cygwin_getpeername (int fd, struct sockaddr *name, socklen_t *len) |
| { |
| int res = -1; |
| fhandler_socket *fh = NULL; |
| |
| __try |
| { |
| fh = get (fd); |
| if (fh) |
| res = fh->getpeername (name, len); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%R = getpeername(%d) %p", res, fd, |
| (fh ? fh->get_socket () : (SOCKET) -1)); |
| return res; |
| } |
| |
| /* exported as recv: standards? */ |
| extern "C" ssize_t |
| cygwin_recv (int fd, void *buf, size_t len, int flags) |
| { |
| ssize_t res = -1; |
| |
| pthread_testcancel (); |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| /* Originally we shortcircuited here if res == 0. |
| Allow 0 bytes buffer. This is valid in POSIX and handled in |
| fhandler_socket::recv_internal. If we shortcircuit, we fail |
| to deliver valid error conditions. */ |
| res = fh->recvfrom (buf, len, flags, NULL, NULL); |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%lR = recv(%d, %p, %ld, %y)", res, fd, buf, len, flags); |
| return res; |
| } |
| |
| /* exported as send: standards? */ |
| extern "C" ssize_t |
| cygwin_send (int fd, const void *buf, size_t len, int flags) |
| { |
| ssize_t res = -1; |
| |
| pthread_testcancel (); |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| res = fh->sendto (buf, len, flags, NULL, 0); |
| } |
| __except (EFAULT) |
| __endtry |
| syscall_printf ("%lR = send(%d, %p, %ld, %y)", res, fd, buf, len, flags); |
| return res; |
| } |
| |
| /* getdomainname: standards? */ |
| extern "C" int |
| getdomainname (char *domain, size_t len) |
| { |
| __try |
| { |
| PFIXED_INFO info = NULL; |
| ULONG size = 0; |
| |
| if (GetNetworkParams(info, &size) == ERROR_BUFFER_OVERFLOW |
| && (info = (PFIXED_INFO) alloca(size)) |
| && GetNetworkParams(info, &size) == ERROR_SUCCESS) |
| { |
| strncpy(domain, info->DomainName, len); |
| return 0; |
| } |
| __seterrno (); |
| } |
| __except (EFAULT) |
| __endtry |
| return -1; |
| } |
| |
| /* Fill out an ifconf struct. */ |
| |
| struct gaa_wa { |
| ULONG family; |
| PIP_ADAPTER_ADDRESSES *pa_ret; |
| }; |
| |
| DWORD WINAPI |
| call_gaa (LPVOID param) |
| { |
| DWORD ret, size = 0; |
| gaa_wa *p = (gaa_wa *) param; |
| PIP_ADAPTER_ADDRESSES pa0 = NULL; |
| |
| if (!p->pa_ret) |
| return GetAdaptersAddresses (p->family, GAA_FLAG_INCLUDE_PREFIX |
| | GAA_FLAG_INCLUDE_ALL_INTERFACES, |
| NULL, NULL, &size); |
| do |
| { |
| ret = GetAdaptersAddresses (p->family, GAA_FLAG_INCLUDE_PREFIX |
| | GAA_FLAG_INCLUDE_ALL_INTERFACES, |
| NULL, pa0, &size); |
| if (ret == ERROR_BUFFER_OVERFLOW |
| && !(pa0 = (PIP_ADAPTER_ADDRESSES) realloc (pa0, size))) |
| break; |
| } |
| while (ret == ERROR_BUFFER_OVERFLOW); |
| if (pa0) |
| { |
| if (ret != ERROR_SUCCESS) |
| { |
| free (pa0); |
| *p->pa_ret = NULL; |
| } |
| else |
| *p->pa_ret = pa0; |
| } |
| return ret; |
| } |
| |
| bool |
| get_adapters_addresses (PIP_ADAPTER_ADDRESSES *pa_ret, ULONG family) |
| { |
| DWORD ret; |
| gaa_wa param = { family, pa_ret }; |
| |
| if (wincap.has_gaa_largeaddress_bug () |
| && (uintptr_t) ¶m >= (uintptr_t) 0x80000000L) |
| { |
| /* In Windows Vista and Windows 7 under WOW64, GetAdaptersAddresses fails |
| if it's running in a thread with a stack located in the large address |
| area. So, if we're running in a pthread with such a stack, we call |
| GetAdaptersAddresses in a child thread with an OS-allocated stack. |
| The OS allocates stacks bottom up, so chances are good that the new |
| stack will be located in the lower address area. */ |
| HANDLE thr = CreateThread (NULL, 0, call_gaa, ¶m, 0, NULL); |
| SetThreadName (GetThreadId (thr), "__call_gaa"); |
| if (!thr) |
| { |
| debug_printf ("CreateThread: %E"); |
| return false; |
| } |
| WaitForSingleObject (thr, INFINITE); |
| GetExitCodeThread (thr, &ret); |
| CloseHandle (thr); |
| } |
| else |
| ret = call_gaa (¶m); |
| return ret == ERROR_SUCCESS || (!pa_ret && ret == ERROR_BUFFER_OVERFLOW); |
| } |
| |
| static in_addr_t |
| get_routedst (DWORD if_index) |
| { |
| PMIB_IPFORWARDTABLE pift; |
| ULONG size = 0; |
| if (GetIpForwardTable (NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER |
| && (pift = (PMIB_IPFORWARDTABLE) alloca (size)) |
| && GetIpForwardTable (pift, &size, FALSE) == NO_ERROR) |
| for (DWORD i = 0; i < pift->dwNumEntries; ++i) |
| { |
| if (pift->table[i].dwForwardIfIndex == if_index |
| && pift->table[i].dwForwardMask == INADDR_BROADCAST) |
| return pift->table[i].dwForwardDest; |
| } |
| return INADDR_ANY; |
| } |
| |
| struct ifall { |
| struct ifaddrs ifa_ifa; |
| char ifa_name[IFNAMSIZ]; |
| struct sockaddr_storage ifa_addr; |
| struct sockaddr_storage ifa_brddstaddr; |
| struct sockaddr_storage ifa_netmask; |
| struct ifaddrs_hwdata ifa_hwdata; |
| }; |
| |
| static unsigned int |
| get_flags (PIP_ADAPTER_ADDRESSES pap) |
| { |
| unsigned int flags = IFF_UP; |
| if (pap->IfType == IF_TYPE_SOFTWARE_LOOPBACK) |
| flags |= IFF_LOOPBACK; |
| else if (pap->IfType == IF_TYPE_PPP |
| || pap->IfType == IF_TYPE_SLIP) |
| flags |= IFF_POINTOPOINT | IFF_NOARP; |
| if (!(pap->Flags & IP_ADAPTER_NO_MULTICAST)) |
| flags |= IFF_MULTICAST; |
| if (pap->OperStatus == IfOperStatusUp |
| || pap->OperStatus == IfOperStatusUnknown) |
| flags |= IFF_RUNNING; |
| if (pap->OperStatus != IfOperStatusLowerLayerDown) |
| flags |= IFF_LOWER_UP; |
| if (pap->OperStatus == IfOperStatusDormant) |
| flags |= IFF_DORMANT; |
| return flags; |
| } |
| |
| static ULONG |
| get_ipv4fromreg_ipcnt (const char *name) |
| { |
| WCHAR regkey[256], *c; |
| |
| c = wcpcpy (regkey, L"Tcpip\\Parameters\\Interfaces\\"); |
| sys_mbstowcs (c, 220, name); |
| if (!NT_SUCCESS (RtlCheckRegistryKey (RTL_REGISTRY_SERVICES, regkey))) |
| return 0; |
| |
| ULONG ifs = 1; |
| DWORD dhcp = 0; |
| UNICODE_STRING uipa = { 0, 0, NULL }; |
| RTL_QUERY_REGISTRY_TABLE tab[3] = { |
| { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOSTRING, |
| L"EnableDHCP", &dhcp, REG_NONE, NULL, 0 }, |
| { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, |
| L"IPAddress", &uipa, REG_NONE, NULL, 0 }, |
| { NULL, 0, NULL, NULL, 0, NULL, 0 } |
| }; |
| |
| /* If DHCP is used, we have only one address. */ |
| if (NT_SUCCESS (RtlQueryRegistryValues (RTL_REGISTRY_SERVICES, regkey, tab, |
| NULL, NULL)) |
| && uipa.Buffer) |
| { |
| if (dhcp == 0) |
| for (ifs = 0, c = uipa.Buffer; *c; c += wcslen (c) + 1) |
| ifs++; |
| RtlFreeUnicodeString (&uipa); |
| } |
| return ifs; |
| } |
| |
| static void |
| get_ipv4fromreg (struct ifall *ifp, const char *name, DWORD idx) |
| { |
| WCHAR regkey[256], *c; |
| |
| c = wcpcpy (regkey, L"Tcpip\\Parameters\\Interfaces\\"); |
| sys_mbstowcs (c, 220, name); |
| if (!NT_SUCCESS (RtlCheckRegistryKey (RTL_REGISTRY_SERVICES, regkey))) |
| return; |
| |
| ULONG ifs; |
| DWORD dhcp = 0; |
| UNICODE_STRING udipa = { 0, 0, NULL }; |
| UNICODE_STRING udsub = { 0, 0, NULL }; |
| UNICODE_STRING uipa = { 0, 0, NULL }; |
| UNICODE_STRING usub = { 0, 0, NULL }; |
| RTL_QUERY_REGISTRY_TABLE tab[6] = { |
| { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOSTRING, |
| L"EnableDHCP", &dhcp, REG_NONE, NULL, 0 }, |
| { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, |
| L"DhcpIPAddress", &udipa, REG_NONE, NULL, 0 }, |
| { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, |
| L"DhcpSubnetMask", &udsub, REG_NONE, NULL, 0 }, |
| { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, |
| L"IPAddress", &uipa, REG_NONE, NULL, 0 }, |
| { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND, |
| L"SubnetMask", &usub, REG_NONE, NULL, 0 }, |
| { NULL, 0, NULL, NULL, 0, NULL, 0 } |
| }; |
| |
| if (NT_SUCCESS (RtlQueryRegistryValues (RTL_REGISTRY_SERVICES, regkey, tab, |
| NULL, NULL))) |
| { |
| # define addr ((struct sockaddr_in *) &ifp->ifa_addr) |
| # define mask ((struct sockaddr_in *) &ifp->ifa_netmask) |
| # define brdc ((struct sockaddr_in *) &ifp->ifa_brddstaddr) |
| # define inet_uton(u, a) \ |
| { \ |
| char t[64]; \ |
| sys_wcstombs (t, 64, (u)); \ |
| cygwin_inet_aton (t, (a)); \ |
| } |
| /* If DHCP is used, we have only one address. */ |
| if (dhcp) |
| { |
| if (udipa.Buffer) |
| inet_uton (udipa.Buffer, &addr->sin_addr); |
| if (udsub.Buffer) |
| inet_uton (udsub.Buffer, &mask->sin_addr); |
| } |
| else |
| { |
| if (uipa.Buffer) |
| { |
| for (ifs = 0, c = uipa.Buffer; *c && ifs < idx; |
| c += wcslen (c) + 1) |
| ifs++; |
| if (*c) |
| inet_uton (c, &addr->sin_addr); |
| } |
| if (usub.Buffer) |
| { |
| for (ifs = 0, c = usub.Buffer; *c && ifs < idx; |
| c += wcslen (c) + 1) |
| ifs++; |
| if (*c) |
| inet_uton (c, &mask->sin_addr); |
| } |
| } |
| if (ifp->ifa_ifa.ifa_flags & IFF_BROADCAST) |
| brdc->sin_addr.s_addr = (addr->sin_addr.s_addr |
| & mask->sin_addr.s_addr) |
| | ~mask->sin_addr.s_addr; |
| #undef addr |
| #undef mask |
| #undef brdc |
| #undef inet_uton |
| if (udipa.Buffer) |
| RtlFreeUnicodeString (&udipa); |
| if (udsub.Buffer) |
| RtlFreeUnicodeString (&udsub); |
| if (uipa.Buffer) |
| RtlFreeUnicodeString (&uipa); |
| if (usub.Buffer) |
| RtlFreeUnicodeString (&usub); |
| } |
| } |
| |
| static void |
| get_friendlyname (struct ifall *ifp, PIP_ADAPTER_ADDRESSES pap) |
| { |
| struct ifreq_frndlyname *iff = (struct ifreq_frndlyname *) |
| &ifp->ifa_hwdata.ifa_frndlyname; |
| iff->ifrf_len = sys_wcstombs (iff->ifrf_friendlyname, |
| IFRF_FRIENDLYNAMESIZ, |
| pap->FriendlyName) + 1; |
| } |
| |
| static void |
| get_hwaddr (struct ifall *ifp, PIP_ADAPTER_ADDRESSES pap) |
| { |
| for (UINT i = 0; i < IFHWADDRLEN; ++i) |
| if (i >= pap->PhysicalAddressLength) |
| ifp->ifa_hwdata.ifa_hwaddr.sa_data[i] = '\0'; |
| else |
| ifp->ifa_hwdata.ifa_hwaddr.sa_data[i] = pap->PhysicalAddress[i]; |
| } |
| |
| /* |
| * Generate short, unique interface name for usage with aged |
| * applications still using the old pre-1.7 ifreq structure. |
| */ |
| static void |
| gen_old_if_name (char *name, PIP_ADAPTER_ADDRESSES pap, DWORD idx) |
| { |
| /* Note: The returned name must be < 16 chars. */ |
| const char *prefix; |
| |
| switch (pap->IfType) |
| { |
| case IF_TYPE_ISO88025_TOKENRING: |
| prefix = "tok"; |
| break; |
| case IF_TYPE_PPP: |
| prefix = "ppp"; |
| break; |
| case IF_TYPE_SOFTWARE_LOOPBACK: |
| prefix = "lo"; |
| break; |
| case IF_TYPE_ATM: |
| prefix = "atm"; |
| break; |
| case IF_TYPE_IEEE80211: |
| prefix = "wlan"; |
| break; |
| case IF_TYPE_SLIP: |
| case IF_TYPE_RS232: |
| case IF_TYPE_MODEM: |
| prefix = "slp"; |
| break; |
| case IF_TYPE_TUNNEL: |
| prefix = "tun"; |
| break; |
| default: |
| prefix = "eth"; |
| break; |
| } |
| if (idx) |
| __small_sprintf (name, "%s%u:%u", prefix, pap->IfIndex, idx); |
| else |
| __small_sprintf (name, "%s%u", prefix, pap->IfIndex, idx); |
| } |
| |
| /* |
| * Get network interfaces. Use IP Helper function GetAdaptersAddresses. |
| */ |
| static struct ifall * |
| get_ifs (ULONG family) |
| { |
| PIP_ADAPTER_ADDRESSES pa0 = NULL, pap; |
| PIP_ADAPTER_UNICAST_ADDRESS pua; |
| int cnt = 0; |
| struct ifall *ifret = NULL, *ifp; |
| struct sockaddr_in *if_sin; |
| struct sockaddr_in6 *if_sin6; |
| |
| if (!get_adapters_addresses (&pa0, family)) |
| goto done; |
| |
| for (pap = pa0; pap; pap = pap->Next) |
| if (!pap->FirstUnicastAddress) |
| { |
| /* FirstUnicastAddress is NULL for interfaces which are disconnected. |
| Fetch number of configured IPv4 addresses from registry and |
| store in an unused member of the adapter addresses structure. */ |
| pap->Ipv6IfIndex = get_ipv4fromreg_ipcnt (pap->AdapterName); |
| cnt += pap->Ipv6IfIndex; |
| } |
| else for (pua = pap->FirstUnicastAddress; pua; pua = pua->Next) |
| ++cnt; |
| |
| if (!(ifret = (struct ifall *) calloc (cnt, sizeof (struct ifall)))) |
| goto done; |
| ifp = ifret; |
| |
| for (pap = pa0; pap; pap = pap->Next) |
| { |
| DWORD idx = 0; |
| if (!pap->FirstUnicastAddress) |
| for (idx = 0; idx < pap->Ipv6IfIndex; ++idx) |
| { |
| /* Next in chain */ |
| ifp->ifa_ifa.ifa_next = (struct ifaddrs *) &ifp[1].ifa_ifa; |
| /* Interface name */ |
| |
| if (CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ) |
| gen_old_if_name (ifp->ifa_name, pap, idx); |
| else if (idx) |
| __small_sprintf (ifp->ifa_name, "%s:%u", pap->AdapterName, idx); |
| else |
| strcpy (ifp->ifa_name, pap->AdapterName); |
| ifp->ifa_ifa.ifa_name = ifp->ifa_name; |
| /* Flags */ |
| ifp->ifa_ifa.ifa_flags = get_flags (pap); |
| if (pap->IfType != IF_TYPE_PPP |
| && pap->IfType != IF_TYPE_SOFTWARE_LOOPBACK) |
| ifp->ifa_ifa.ifa_flags |= IFF_BROADCAST; |
| /* Address */ |
| ifp->ifa_addr.ss_family = AF_INET; |
| ifp->ifa_ifa.ifa_addr = (struct sockaddr *) &ifp->ifa_addr; |
| /* Broadcast/Destination address */ |
| ifp->ifa_brddstaddr.ss_family = AF_INET; |
| ifp->ifa_ifa.ifa_dstaddr = NULL; |
| /* Netmask */ |
| ifp->ifa_netmask.ss_family = AF_INET; |
| ifp->ifa_ifa.ifa_netmask = (struct sockaddr *) &ifp->ifa_netmask; |
| /* Try to fetch real IPv4 address information from registry. */ |
| get_ipv4fromreg (ifp, pap->AdapterName, idx); |
| /* Hardware address */ |
| get_hwaddr (ifp, pap); |
| /* Metric */ |
| ifp->ifa_hwdata.ifa_metric = 1; |
| /* MTU */ |
| ifp->ifa_hwdata.ifa_mtu = pap->Mtu; |
| /* Interface index */ |
| ifp->ifa_hwdata.ifa_ifindex = pap->IfIndex; |
| /* Friendly name */ |
| get_friendlyname (ifp, pap); |
| /* Let ifa_data member point to "ifaddrs_hwdata" data. */ |
| ifp->ifa_ifa.ifa_data = &ifp->ifa_hwdata; |
| ++ifp; |
| } |
| else |
| for (idx = 0, pua = pap->FirstUnicastAddress; pua; pua = pua->Next) |
| { |
| struct sockaddr *sa = (struct sockaddr *) pua->Address.lpSockaddr; |
| # define sin ((struct sockaddr_in *) sa) |
| # define sin6 ((struct sockaddr_in6 *) sa) |
| size_t sa_size = (sa->sa_family == AF_INET6 |
| ? sizeof *sin6 : sizeof *sin); |
| /* Next in chain */ |
| ifp->ifa_ifa.ifa_next = (struct ifaddrs *) &ifp[1].ifa_ifa; |
| /* Interface name */ |
| if (CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ) |
| gen_old_if_name (ifp->ifa_name, pap, idx); |
| else if (sa->sa_family == AF_INET && idx) |
| __small_sprintf (ifp->ifa_name, "%s:%u", pap->AdapterName, idx); |
| else |
| strcpy (ifp->ifa_name, pap->AdapterName); |
| if (sa->sa_family == AF_INET) |
| ++idx; |
| ifp->ifa_ifa.ifa_name = ifp->ifa_name; |
| /* Flags */ |
| ifp->ifa_ifa.ifa_flags = get_flags (pap); |
| if (sa->sa_family == AF_INET |
| && pap->IfType != IF_TYPE_SOFTWARE_LOOPBACK |
| && pap->IfType != IF_TYPE_PPP) |
| ifp->ifa_ifa.ifa_flags |= IFF_BROADCAST; |
| /* Address */ |
| memcpy (&ifp->ifa_addr, sa, sa_size); |
| ifp->ifa_ifa.ifa_addr = (struct sockaddr *) &ifp->ifa_addr; |
| /* Netmask */ |
| int prefix = pua->OnLinkPrefixLength; |
| switch (sa->sa_family) |
| { |
| case AF_INET: |
| if_sin = (struct sockaddr_in *) &ifp->ifa_netmask; |
| if_sin->sin_addr.s_addr = htonl (UINT32_MAX << (32 - prefix)); |
| if_sin->sin_family = AF_INET; |
| break; |
| case AF_INET6: |
| if_sin6 = (struct sockaddr_in6 *) &ifp->ifa_netmask; |
| for (cnt = 0; cnt < 4 && prefix > 0; ++cnt, prefix -= 32) |
| { |
| if_sin6->sin6_addr.s6_addr32[cnt] = UINT32_MAX; |
| if (prefix < 32) |
| if_sin6->sin6_addr.s6_addr32[cnt] <<= 32 - prefix; |
| } |
| break; |
| } |
| ifp->ifa_ifa.ifa_netmask = (struct sockaddr *) &ifp->ifa_netmask; |
| if (pap->IfType == IF_TYPE_PPP) |
| { |
| /* Destination address */ |
| if (sa->sa_family == AF_INET) |
| { |
| if_sin = (struct sockaddr_in *) &ifp->ifa_brddstaddr; |
| if_sin->sin_addr.s_addr = get_routedst (pap->IfIndex); |
| if_sin->sin_family = AF_INET; |
| } |
| else |
| /* FIXME: No official way to get the dstaddr for ipv6? */ |
| memcpy (&ifp->ifa_addr, sa, sa_size); |
| ifp->ifa_ifa.ifa_dstaddr = (struct sockaddr *) |
| &ifp->ifa_brddstaddr; |
| } |
| else |
| { |
| /* Broadcast address */ |
| if (sa->sa_family == AF_INET) |
| { |
| if_sin = (struct sockaddr_in *) &ifp->ifa_brddstaddr; |
| uint32_t mask = |
| ((struct sockaddr_in *) &ifp->ifa_netmask)->sin_addr.s_addr; |
| if_sin->sin_addr.s_addr = (sin->sin_addr.s_addr & mask) |
| | ~mask; |
| if_sin->sin_family = AF_INET; |
| ifp->ifa_ifa.ifa_broadaddr = (struct sockaddr *) |
| &ifp->ifa_brddstaddr; |
| } |
| else /* No IPv6 broadcast */ |
| ifp->ifa_ifa.ifa_broadaddr = NULL; |
| } |
| /* Hardware address */ |
| get_hwaddr (ifp, pap); |
| /* Metric */ |
| ifp->ifa_hwdata.ifa_metric = (sa->sa_family == AF_INET |
| ? pap->Ipv4Metric : pap->Ipv6Metric); |
| /* MTU */ |
| ifp->ifa_hwdata.ifa_mtu = pap->Mtu; |
| /* Interface index */ |
| ifp->ifa_hwdata.ifa_ifindex = pap->IfIndex; |
| /* Friendly name */ |
| get_friendlyname (ifp, pap); |
| /* Let ifa_data member point to "ifaddrs_hwdata" data. */ |
| ifp->ifa_ifa.ifa_data = &ifp->ifa_hwdata; |
| ++ifp; |
| # undef sin |
| # undef sin6 |
| } |
| } |
| /* Since every entry is set to the next entry, the last entry points to an |
| invalid next entry now. Fix it retroactively. */ |
| if (ifp > ifret) |
| ifp[-1].ifa_ifa.ifa_next = NULL; |
| |
| done: |
| if (pa0) |
| free (pa0); |
| return ifret; |
| } |
| |
| extern "C" int |
| getifaddrs (struct ifaddrs **ifap) |
| { |
| if (!ifap) |
| { |
| set_errno (EINVAL); |
| return -1; |
| } |
| struct ifall *ifp; |
| ifp = get_ifs (AF_UNSPEC); |
| *ifap = &ifp->ifa_ifa; |
| return ifp ? 0 : -1; |
| } |
| |
| extern "C" void |
| freeifaddrs (struct ifaddrs *ifp) |
| { |
| if (ifp) |
| free (ifp); |
| } |
| |
| int |
| get_ifconf (struct ifconf *ifc, int what) |
| { |
| __try |
| { |
| /* Ensure we have space for at least one struct ifreqs, fail if not. */ |
| if (ifc->ifc_len < (int) sizeof (struct ifreq)) |
| { |
| set_errno (EINVAL); |
| __leave; |
| } |
| |
| struct ifall *ifret, *ifp; |
| ifret = get_ifs (AF_INET); |
| if (!ifret) |
| __leave; |
| |
| struct sockaddr_in *sin; |
| struct ifreq *ifr = ifc->ifc_req; |
| int cnt = 0; |
| for (ifp = ifret; ifp; ifp = (struct ifall *) ifp->ifa_ifa.ifa_next) |
| { |
| ++cnt; |
| strcpy (ifr->ifr_name, ifp->ifa_name); |
| switch (what) |
| { |
| case SIOCGIFFLAGS: |
| ifr->ifr_flags = ifp->ifa_ifa.ifa_flags; |
| break; |
| case SIOCGIFCONF: |
| case SIOCGIFADDR: |
| sin = (struct sockaddr_in *) &ifr->ifr_addr; |
| memcpy (sin, &ifp->ifa_addr, sizeof *sin); |
| break; |
| case SIOCGIFNETMASK: |
| sin = (struct sockaddr_in *) &ifr->ifr_netmask; |
| memcpy (sin, &ifp->ifa_netmask, sizeof *sin); |
| break; |
| case SIOCGIFDSTADDR: |
| sin = (struct sockaddr_in *) &ifr->ifr_dstaddr; |
| if (ifp->ifa_ifa.ifa_flags & IFF_POINTOPOINT) |
| memcpy (sin, &ifp->ifa_brddstaddr, sizeof *sin); |
| else /* Return addr as on Linux. */ |
| memcpy (sin, &ifp->ifa_addr, sizeof *sin); |
| break; |
| case SIOCGIFBRDADDR: |
| sin = (struct sockaddr_in *) &ifr->ifr_broadaddr; |
| if (!(ifp->ifa_ifa.ifa_flags & IFF_POINTOPOINT)) |
| memcpy (sin, &ifp->ifa_brddstaddr, sizeof *sin); |
| else |
| { |
| sin->sin_addr.s_addr = INADDR_ANY; |
| sin->sin_family = AF_INET; |
| sin->sin_port = 0; |
| } |
| break; |
| case SIOCGIFHWADDR: |
| memcpy (&ifr->ifr_hwaddr, &ifp->ifa_hwdata.ifa_hwaddr, |
| sizeof ifr->ifr_hwaddr); |
| break; |
| case SIOCGIFMETRIC: |
| ifr->ifr_metric = ifp->ifa_hwdata.ifa_metric; |
| break; |
| case SIOCGIFMTU: |
| ifr->ifr_mtu = ifp->ifa_hwdata.ifa_mtu; |
| break; |
| case SIOCGIFINDEX: |
| ifr->ifr_ifindex = ifp->ifa_hwdata.ifa_ifindex; |
| break; |
| case SIOCGIFFRNDLYNAM: |
| memcpy (ifr->ifr_frndlyname, &ifp->ifa_hwdata.ifa_frndlyname, |
| sizeof (struct ifreq_frndlyname)); |
| } |
| if ((caddr_t) ++ifr > |
| ifc->ifc_buf + ifc->ifc_len - sizeof (struct ifreq)) |
| break; |
| } |
| /* Set the correct length */ |
| ifc->ifc_len = cnt * sizeof (struct ifreq); |
| free (ifret); |
| return 0; |
| } |
| __except (EFAULT) {} |
| __endtry |
| return -1; |
| } |
| |
| extern "C" unsigned |
| cygwin_if_nametoindex (const char *name) |
| { |
| return (unsigned) ::if_nametoindex (name); |
| } |
| |
| extern "C" char * |
| cygwin_if_indextoname (unsigned ifindex, char *ifname) |
| { |
| return ::if_indextoname (ifindex, ifname); |
| } |
| |
| extern "C" struct if_nameindex * |
| if_nameindex (void) |
| { |
| PIP_ADAPTER_ADDRESSES pa0 = NULL, pap; |
| struct if_nameindex *iflist = NULL; |
| char (*ifnamelist)[IF_NAMESIZE]; |
| |
| __try |
| { |
| if (get_adapters_addresses (&pa0, AF_UNSPEC)) |
| { |
| int cnt = 0; |
| for (pap = pa0; pap; pap = pap->Next) |
| ++cnt; |
| iflist = (struct if_nameindex *) |
| malloc ((cnt + 1) * sizeof (struct if_nameindex) |
| + cnt * IF_NAMESIZE); |
| if (!iflist) |
| set_errno (ENOBUFS); |
| else |
| { |
| ifnamelist = (char (*)[IF_NAMESIZE]) (iflist + cnt + 1); |
| for (pap = pa0, cnt = 0; pap; pap = pap->Next) |
| { |
| for (int i = 0; i < cnt; ++i) |
| if (iflist[i].if_index |
| == (pap->Ipv6IfIndex ?: pap->IfIndex)) |
| goto outer_loop; |
| iflist[cnt].if_index = pap->Ipv6IfIndex ?: pap->IfIndex; |
| strcpy (iflist[cnt].if_name = ifnamelist[cnt], |
| pap->AdapterName); |
| /* See comment in if_indextoname. */ |
| if (pap->IfIndex == 1 && pap->Ipv6IfIndex == 0) |
| for (PIP_ADAPTER_ADDRESSES pap2 = pa0; |
| pap2; |
| pap2 = pap2->Next) |
| if (pap2->Ipv6IfIndex == 1) |
| { |
| strcpy (ifnamelist[cnt], pap2->AdapterName); |
| break; |
| } |
| ++cnt; |
| outer_loop: |
| ; |
| } |
| iflist[cnt].if_index = 0; |
| iflist[cnt].if_name = NULL; |
| } |
| free (pa0); |
| } |
| else |
| set_errno (ENXIO); |
| } |
| __except (EFAULT) {} |
| __endtry |
| return iflist; |
| } |
| |
| extern "C" void |
| if_freenameindex (struct if_nameindex *ptr) |
| { |
| free (ptr); |
| } |
| |
| #define PORT_LOW (IPPORT_EFSSERVER + 1) |
| #define PORT_HIGH (IPPORT_RESERVED - 1) |
| #define NUM_PORTS (PORT_HIGH - PORT_LOW + 1) |
| |
| extern "C" int |
| cygwin_bindresvport_sa (int fd, struct sockaddr *sa) |
| { |
| struct sockaddr_storage sst; |
| struct sockaddr_in *sin = NULL; |
| struct sockaddr_in6 *sin6 = NULL; |
| in_port_t port; |
| socklen_t salen; |
| int ret = -1; |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (!fh) |
| __leave; |
| |
| if (!sa) |
| { |
| sa = (struct sockaddr *) &sst; |
| memset (&sst, 0, sizeof sst); |
| sa->sa_family = fh->get_addr_family (); |
| } |
| |
| switch (sa->sa_family) |
| { |
| case AF_INET: |
| salen = sizeof (struct sockaddr_in); |
| sin = (struct sockaddr_in *) sa; |
| port = sin->sin_port; |
| break; |
| case AF_INET6: |
| salen = sizeof (struct sockaddr_in6); |
| sin6 = (struct sockaddr_in6 *) sa; |
| port = sin6->sin6_port; |
| break; |
| default: |
| set_errno (EPFNOSUPPORT); |
| __leave; |
| } |
| |
| /* If a non-zero port number is given, try this first. If that succeeds, |
| or if the error message is serious, return. */ |
| if (port) |
| { |
| ret = fh->bind (sa, salen); |
| if (!ret || (get_errno () != EADDRINUSE && get_errno () != EINVAL)) |
| __leave; |
| } |
| |
| LONG myport; |
| |
| for (int i = 0; i < NUM_PORTS; i++) |
| { |
| while ((myport = InterlockedExchange ( |
| &cygwin_shared->last_used_bindresvport, -1)) == -1) |
| yield (); |
| if (myport == 0 || --myport < PORT_LOW) |
| myport = PORT_HIGH; |
| InterlockedExchange (&cygwin_shared->last_used_bindresvport, myport); |
| |
| if (sa->sa_family == AF_INET6) |
| sin6->sin6_port = htons (myport); |
| else |
| sin->sin_port = htons (myport); |
| if (!(ret = fh->bind (sa, salen))) |
| break; |
| if (get_errno () != EADDRINUSE && get_errno () != EINVAL) |
| break; |
| } |
| |
| } |
| __except (EFAULT) {} |
| __endtry |
| return ret; |
| } |
| |
| |
| extern "C" int |
| cygwin_bindresvport (int fd, struct sockaddr_in *sin) |
| { |
| return cygwin_bindresvport_sa (fd, (struct sockaddr *) sin); |
| } |
| |
| /* socketpair: standards? */ |
| /* Win32 supports AF_INET only, so ignore domain and protocol arguments */ |
| extern "C" int |
| socketpair (int family, int type, int protocol, int *sb) |
| { |
| int res = -1; |
| SOCKET insock = INVALID_SOCKET; |
| SOCKET outsock = INVALID_SOCKET; |
| SOCKET newsock = INVALID_SOCKET; |
| struct sockaddr_in sock_in, sock_out; |
| int len; |
| |
| __try |
| { |
| int flags = type & _SOCK_FLAG_MASK; |
| type &= ~_SOCK_FLAG_MASK; |
| |
| if (family != AF_LOCAL && family != AF_INET) |
| { |
| set_errno (EAFNOSUPPORT); |
| __leave; |
| } |
| if (type != SOCK_STREAM && type != SOCK_DGRAM) |
| { |
| set_errno (EPROTOTYPE); |
| __leave; |
| } |
| if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0) |
| { |
| set_errno (EINVAL); |
| __leave; |
| } |
| if ((family == AF_LOCAL && protocol != PF_UNSPEC && protocol != PF_LOCAL) |
| || (family == AF_INET && protocol != PF_UNSPEC && protocol != PF_INET)) |
| { |
| set_errno (EPROTONOSUPPORT); |
| __leave; |
| } |
| |
| /* create the first socket */ |
| newsock = socket (AF_INET, type, 0); |
| if (newsock == INVALID_SOCKET) |
| { |
| debug_printf ("first socket call failed"); |
| set_winsock_errno (); |
| __leave; |
| } |
| |
| /* bind the socket to any unused port */ |
| sock_in.sin_family = AF_INET; |
| sock_in.sin_port = 0; |
| sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK); |
| if (bind (newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0) |
| { |
| debug_printf ("bind failed"); |
| set_winsock_errno (); |
| __leave; |
| } |
| len = sizeof (sock_in); |
| if (getsockname (newsock, (struct sockaddr *) &sock_in, &len) < 0) |
| { |
| debug_printf ("getsockname error"); |
| set_winsock_errno (); |
| __leave; |
| } |
| |
| /* For stream sockets, create a listener */ |
| if (type == SOCK_STREAM) |
| listen (newsock, 2); |
| |
| /* create a connecting socket */ |
| outsock = socket (AF_INET, type, 0); |
| if (outsock == INVALID_SOCKET) |
| { |
| debug_printf ("second socket call failed"); |
| set_winsock_errno (); |
| __leave; |
| } |
| |
| /* For datagram sockets, bind the 2nd socket to an unused address, too */ |
| if (type == SOCK_DGRAM) |
| { |
| sock_out.sin_family = AF_INET; |
| sock_out.sin_port = 0; |
| sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK); |
| if (bind (outsock, (struct sockaddr *) &sock_out, sizeof (sock_out)) < 0) |
| { |
| debug_printf ("bind failed"); |
| set_winsock_errno (); |
| __leave; |
| } |
| len = sizeof (sock_out); |
| if (getsockname (outsock, (struct sockaddr *) &sock_out, &len) < 0) |
| { |
| debug_printf ("getsockname error"); |
| set_winsock_errno (); |
| __leave; |
| } |
| } |
| |
| /* Force IP address to loopback */ |
| sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK); |
| if (type == SOCK_DGRAM) |
| sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK); |
| |
| /* Do a connect */ |
| if (connect (outsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0) |
| { |
| debug_printf ("connect error"); |
| set_winsock_errno (); |
| __leave; |
| } |
| |
| if (type == SOCK_STREAM) |
| { |
| /* For stream sockets, accept the connection and close the listener */ |
| len = sizeof (sock_in); |
| insock = accept (newsock, (struct sockaddr *) &sock_in, &len); |
| if (insock == INVALID_SOCKET) |
| { |
| debug_printf ("accept error"); |
| set_winsock_errno (); |
| __leave; |
| } |
| closesocket (newsock); |
| newsock = INVALID_SOCKET; |
| } |
| else |
| { |
| /* For datagram sockets, connect the 2nd socket */ |
| if (connect (newsock, (struct sockaddr *) &sock_out, |
| sizeof (sock_out)) < 0) |
| { |
| debug_printf ("connect error"); |
| set_winsock_errno (); |
| __leave; |
| } |
| insock = newsock; |
| newsock = INVALID_SOCKET; |
| } |
| |
| cygheap_fdnew sb0; |
| const device *dev; |
| |
| if (family == AF_INET) |
| dev = (type == SOCK_STREAM ? tcp_dev : udp_dev); |
| else |
| dev = (type == SOCK_STREAM ? stream_dev : dgram_dev); |
| |
| if (sb0 >= 0 && fdsock (sb0, dev, insock)) |
| { |
| ((fhandler_socket *) sb0)->set_addr_family (family); |
| ((fhandler_socket *) sb0)->set_socket_type (type); |
| ((fhandler_socket *) sb0)->connect_state (connected); |
| if (flags & SOCK_NONBLOCK) |
| ((fhandler_socket *) sb0)->set_nonblocking (true); |
| if (flags & SOCK_CLOEXEC) |
| ((fhandler_socket *) sb0)->set_close_on_exec (true); |
| if (family == AF_LOCAL && type == SOCK_STREAM) |
| ((fhandler_socket *) sb0)->af_local_set_sockpair_cred (); |
| |
| cygheap_fdnew sb1 (sb0, false); |
| |
| if (sb1 >= 0 && fdsock (sb1, dev, outsock)) |
| { |
| ((fhandler_socket *) sb1)->set_addr_family (family); |
| ((fhandler_socket *) sb1)->set_socket_type (type); |
| ((fhandler_socket *) sb1)->connect_state (connected); |
| if (flags & SOCK_NONBLOCK) |
| ((fhandler_socket *) sb1)->set_nonblocking (true); |
| if (flags & SOCK_CLOEXEC) |
| ((fhandler_socket *) sb1)->set_close_on_exec (true); |
| if (family == AF_LOCAL && type == SOCK_STREAM) |
| ((fhandler_socket *) sb1)->af_local_set_sockpair_cred (); |
| |
| sb[0] = sb0; |
| sb[1] = sb1; |
| res = 0; |
| } |
| else |
| sb0.release (); |
| } |
| } |
| __except (EFAULT) {} |
| __endtry |
| syscall_printf ("%R = socketpair(...)", res); |
| if (res == -1) |
| { |
| if (insock != INVALID_SOCKET) |
| closesocket (insock); |
| if (outsock != INVALID_SOCKET) |
| closesocket (outsock); |
| if (newsock != INVALID_SOCKET) |
| closesocket (newsock); |
| } |
| return res; |
| } |
| |
| /* sethostent: standards? */ |
| extern "C" void |
| sethostent (int) |
| { |
| } |
| |
| /* endhostent: standards? */ |
| extern "C" void |
| endhostent (void) |
| { |
| } |
| |
| /* exported as recvmsg: standards? */ |
| extern "C" ssize_t |
| cygwin_recvmsg (int fd, struct msghdr *msg, int flags) |
| { |
| ssize_t res = -1; |
| |
| pthread_testcancel (); |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| { |
| res = check_iovec_for_read (msg->msg_iov, msg->msg_iovlen); |
| /* Originally we shortcircuited here if res == 0. |
| Allow 0 bytes buffer. This is valid in POSIX and handled in |
| fhandler_socket::recv_internal. If we shortcircuit, we fail |
| to deliver valid error conditions and peer address. */ |
| if (res >= 0) |
| res = fh->recvmsg (msg, flags); |
| } |
| } |
| __except (EFAULT) |
| { |
| res = -1; |
| } |
| __endtry |
| syscall_printf ("%lR = recvmsg(%d, %p, %y)", res, fd, msg, flags); |
| return res; |
| } |
| |
| /* exported as sendmsg: standards? */ |
| extern "C" ssize_t |
| cygwin_sendmsg (int fd, const struct msghdr *msg, int flags) |
| { |
| ssize_t res = -1; |
| |
| pthread_testcancel (); |
| |
| __try |
| { |
| fhandler_socket *fh = get (fd); |
| if (fh) |
| { |
| res = check_iovec_for_write (msg->msg_iov, msg->msg_iovlen); |
| if (res >= 0) |
| res = fh->sendmsg (msg, flags); |
| } |
| } |
| __except (EFAULT) |
| { |
| res = -1; |
| } |
| __endtry |
| syscall_printf ("%lR = sendmsg(%d, %p, %y)", res, fd, msg, flags); |
| return res; |
| } |
| |
| /* This is from the BIND 4.9.4 release, modified to compile by itself */ |
| |
| /* Copyright (c) 1996 by Internet Software Consortium. |
| * |
| * Permission to use, copy, modify, and distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS |
| * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE |
| * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS |
| * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| * SOFTWARE. |
| */ |
| |
| /* int |
| * inet_pton4(src, dst) |
| * like inet_aton() but without all the hexadecimal and shorthand. |
| * return: |
| * 1 if `src' is a valid dotted quad, else 0. |
| * notice: |
| * does not touch `dst' unless it's returning 1. |
| * author: |
| * Paul Vixie, 1996. |
| */ |
| static int |
| inet_pton4 (const char *src, u_int8_t *dst) |
| { |
| static const char digits[] = "0123456789"; |
| int saw_digit, octets, ch; |
| u_int8_t tmp[INADDRSZ], *tp; |
| |
| saw_digit = 0; |
| octets = 0; |
| *(tp = tmp) = 0; |
| while ((ch = *src++) != '\0') |
| { |
| const char *pch; |
| |
| if ((pch = strchr(digits, ch)) != NULL) |
| { |
| u_int32_t ret = *tp * 10 + (pch - digits); |
| |
| if (ret > 255) |
| return (0); |
| *tp = ret; |
| if (! saw_digit) |
| { |
| if (++octets > 4) |
| return (0); |
| saw_digit = 1; |
| } |
| } |
| else if (ch == '.' && saw_digit) |
| { |
| if (octets == 4) |
| return (0); |
| *++tp = 0; |
| saw_digit = 0; |
| } |
| else |
| return (0); |
| } |
| if (octets < 4) |
| return (0); |
| |
| memcpy(dst, tmp, INADDRSZ); |
| return (1); |
| } |
| |
| /* int |
| * inet_pton6(src, dst) |
| * convert presentation level address to network order binary form. |
| * return: |
| * 1 if `src' is a valid [RFC1884 2.2] address, else 0. |
| * notice: |
| * (1) does not touch `dst' unless it's returning 1. |
| * (2) :: in a full address is silently ignored. |
| * credit: |
| * inspired by Mark Andrews. |
| * author: |
| * Paul Vixie, 1996. |
| */ |
| static int |
| inet_pton6 (const char *src, u_int8_t *dst) |
| { |
| static const char xdigits_l[] = "0123456789abcdef", |
| xdigits_u[] = "0123456789ABCDEF"; |
| u_int8_t tmp[IN6ADDRSZ], *tp, *endp, *colonp; |
| const char *xdigits, *curtok; |
| int ch, saw_xdigit; |
| u_int32_t val; |
| |
| memset((tp = tmp), 0, IN6ADDRSZ); |
| endp = tp + IN6ADDRSZ; |
| colonp = NULL; |
| /* Leading :: requires some special handling. */ |
| if (*src == ':') |
| if (*++src != ':') |
| return (0); |
| curtok = src; |
| saw_xdigit = 0; |
| val = 0; |
| while ((ch = *src++) != '\0') |
| { |
| const char *pch; |
| |
| if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) |
| pch = strchr((xdigits = xdigits_u), ch); |
| if (pch != NULL) |
| { |
| val <<= 4; |
| val |= (pch - xdigits); |
| if (val > 0xffff) |
| return (0); |
| saw_xdigit = 1; |
| continue; |
| } |
| if (ch == ':') |
| { |
| curtok = src; |
| if (!saw_xdigit) |
| { |
| if (colonp) |
| return (0); |
| colonp = tp; |
| continue; |
| } |
| if (tp + INT16SZ > endp) |
| return (0); |
| *tp++ = (u_int8_t) (val >> 8) & 0xff; |
| *tp++ = (u_int8_t) val & 0xff; |
| saw_xdigit = 0; |
| val = 0; |
| continue; |
| } |
| if (ch == '.' && ((tp + INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) |
| { |
| tp += INADDRSZ; |
| saw_xdigit = 0; |
| break; /* '\0' was seen by inet_pton4(). */ |
| } |
| return (0); |
| } |
| if (saw_xdigit) |
| { |
| if (tp + INT16SZ > endp) |
| return (0); |
| *tp++ = (u_int8_t) (val >> 8) & 0xff; |
| *tp++ = (u_int8_t) val & 0xff; |
| } |
| if (colonp != NULL) |
| { |
| /* |
| * Since some memmove()'s erroneously fail to handle |
| * overlapping regions, we'll do the shift by hand. |
| */ |
| const int n = tp - colonp; |
| int i; |
| |
| for (i = 1; i <= n; i++) |
| { |
| endp[- i] = colonp[n - i]; |
| colonp[n - i] = 0; |
| } |
| tp = endp; |
| } |
| if (tp != endp) |
| return (0); |
| |
| memcpy(dst, tmp, IN6ADDRSZ); |
| return (1); |
| } |
| |
| /* int |
| * inet_pton(af, src, dst) |
| * convert from presentation format (which usually means ASCII printable) |
| * to network format (which is usually some kind of binary format). |
| * return: |
| * 1 if the address was valid for the specified address family |
| * 0 if the address wasn't valid (`dst' is untouched in this case) |
| * -1 if some other error occurred (`dst' is untouched in this case, too) |
| * author: |
| * Paul Vixie, 1996. |
| */ |
| extern "C" int |
| cygwin_inet_pton (int af, const char *src, void *dst) |
| { |
| switch (af) |
| { |
| case AF_INET: |
| return (inet_pton4(src, (u_int8_t *) dst)); |
| case AF_INET6: |
| return (inet_pton6(src, (u_int8_t *) dst)); |
| default: |
| errno = EAFNOSUPPORT; |
| return (-1); |
| } |
| /* NOTREACHED */ |
| } |
| |
| /* const char * |
| * inet_ntop4(src, dst, size) |
| * format an IPv4 address, more or less like inet_ntoa() |
| * return: |
| * `dst' (as a const) |
| * notes: |
| * (1) uses no statics |
| * (2) takes a u_int8_t* not an in_addr as input |
| * author: |
| * Paul Vixie, 1996. |
| */ |
| static const char * |
| inet_ntop4 (const u_int8_t *src, char *dst, size_t size) |
| { |
| static const char fmt[] = "%u.%u.%u.%u"; |
| char tmp[sizeof "255.255.255.255"]; |
| |
| __small_sprintf(tmp, fmt, src[0], src[1], src[2], src[3]); |
| if (strlen(tmp) > size) |
| { |
| errno = ENOSPC; |
| return (NULL); |
| } |
| strcpy(dst, tmp); |
| return (dst); |
| } |
| |
| /* const char * |
| * inet_ntop6(src, dst, size) |
| * convert IPv6 binary address into presentation (printable) format |
| * author: |
| * Paul Vixie, 1996. |
| */ |
| static const char * |
| inet_ntop6 (const u_int8_t *src, char *dst, size_t size) |
| { |
| /* |
| * Note that int32_t and int16_t need only be "at least" large enough |
| * to contain a value of the specified size. On some systems, like |
| * Crays, there is no such thing as an integer variable with 16 bits. |
| * Keep this in mind if you think this function should have been coded |
| * to use pointer overlays. All the world's not a VAX. |
| */ |
| char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; |
| struct { int base, len; } best, cur; |
| u_int32_t words[IN6ADDRSZ / INT16SZ]; |
| int i; |
| |
| /* |
| * Preprocess: |
| * Copy the input (bytewise) array into a wordwise array. |
| * Find the longest run of 0x00's in src[] for :: shorthanding. |
| */ |
| memset(words, 0, sizeof words); |
| for (i = 0; i < IN6ADDRSZ; i++) |
| words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); |
| best.base = -1; |
| cur.base = -1; |
| best.len = 0; |
| cur.len = 0; |
| for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) |
| { |
| if (words[i] == 0) |
| { |
| if (cur.base == -1) |
| cur.base = i, cur.len = 1; |
| else |
| cur.len++; |
| } |
| else |
| { |
| if (cur.base != -1) |
| { |
| if (best.base == -1 || cur.len > best.len) |
| best = cur; |
| cur.base = -1; |
| } |
| } |
| } |
| if (cur.base != -1) |
| { |
| if (best.base == -1 || cur.len > best.len) |
| best = cur; |
| } |
| if (best.base != -1 && best.len < 2) |
| best.base = -1; |
| |
| /* |
| * Format the result. |
| */ |
| tp = tmp; |
| for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) |
| { |
| /* Are we inside the best run of 0x00's? */ |
| if (best.base != -1 && i >= best.base && i < (best.base + best.len)) |
| { |
| if (i == best.base) |
| *tp++ = ':'; |
| continue; |
| } |
| /* Are we following an initial run of 0x00s or any real hex? */ |
| if (i != 0) |
| *tp++ = ':'; |
| /* Is this address an encapsulated IPv4? */ |
| if (i == 6 && best.base == 0 && |
| (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) |
| { |
| if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) |
| return (NULL); |
| tp += strlen(tp); |
| break; |
| } |
| __small_sprintf(tp, "%x", words[i]); |
| while (*tp) |
| { |
| if (isupper (*tp)) |
| *tp = _tolower (*tp); |
| ++tp; |
| } |
| } |
| /* Was it a trailing run of 0x00's? */ |
| if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) |
| *tp++ = ':'; |
| *tp++ = '\0'; |
| |
| /* |
| * Check for overflow, copy, and we're done. |
| */ |
| if ((size_t) (tp - tmp) > size) |
| { |
| errno = ENOSPC; |
| return (NULL); |
| } |
| strcpy(dst, tmp); |
| return (dst); |
| } |
| |
| /* char * |
| * inet_ntop(af, src, dst, size) |
| * convert a network format address to presentation format. |
| * return: |
| * pointer to presentation format address (`dst'), or NULL (see errno). |
| * author: |
| * Paul Vixie, 1996. |
| */ |
| extern "C" const char * |
| cygwin_inet_ntop (int af, const void *src, char *dst, socklen_t size) |
| { |
| switch (af) |
| { |
| case AF_INET: |
| return (inet_ntop4((const u_int8_t *) src, dst, size)); |
| case AF_INET6: |
| return (inet_ntop6((const u_int8_t *) src, dst, size)); |
| default: |
| errno = EAFNOSUPPORT; |
| return (NULL); |
| } |
| /* NOTREACHED */ |
| } |
| |
| extern "C" void |
| cygwin_freeaddrinfo (struct addrinfo *addr) |
| { |
| struct addrinfo *ai, *ainext; |
| |
| for (ai = addr; ai != NULL; ai = ainext) |
| { |
| if (ai->ai_addr != NULL) |
| free (ai->ai_addr); /* socket address structure */ |
| |
| if (ai->ai_canonname != NULL) |
| free (ai->ai_canonname); |
| |
| ainext = ai->ai_next; /* can't fetch ai_next after free() */ |
| free (ai); /* the addrinfo{} itself */ |
| } |
| } |
| |
| static struct addrinfo * |
| ga_dup (struct addrinfoW *ai, bool v4mapped, int idn_flags, int &err) |
| { |
| struct addrinfo *nai; |
| |
| if ((nai = (struct addrinfo *) calloc (1, sizeof (struct addrinfo))) == NULL) |
| { |
| err = EAI_MEMORY; |
| return NULL; |
| } |
| |
| nai->ai_family = v4mapped ? AF_INET6 : ai->ai_family; |
| nai->ai_socktype = ai->ai_socktype; |
| nai->ai_protocol = ai->ai_protocol; |
| if (ai->ai_canonname) |
| { |
| tmp_pathbuf tp; |
| wchar_t *canonname = ai->ai_canonname; |
| |
| if (idn_flags & AI_CANONIDN) |
| { |
| /* Map flags to equivalent IDN_* flags. */ |
| wchar_t *canonbuf = tp.w_get (); |
| if (IdnToUnicode (idn_flags >> 16, canonname, -1, |
| canonbuf, NT_MAX_PATH)) |
| canonname = canonbuf; |
| else if (GetLastError () != ERROR_PROC_NOT_FOUND) |
| { |
| free (nai); |
| err = EAI_IDN_ENCODE; |
| return NULL; |
| } |
| } |
| size_t len = wcstombs (NULL, canonname, 0); |
| if (len == (size_t) -1) |
| { |
| free (nai); |
| err = EAI_IDN_ENCODE; |
| return NULL; |
| } |
| nai->ai_canonname = (char *) malloc (len + 1); |
| if (!nai->ai_canonname) |
| { |
| free (nai); |
| err = EAI_MEMORY; |
| return NULL; |
| } |
| wcstombs (nai->ai_canonname, canonname, len + 1); |
| } |
| |
| nai->ai_addrlen = v4mapped ? sizeof (struct sockaddr_in6) : ai->ai_addrlen; |
| if ((nai->ai_addr = (struct sockaddr *) malloc (v4mapped |
| ? sizeof (struct sockaddr_in6) |
| : ai->ai_addrlen)) == NULL) |
| { |
| if (nai->ai_canonname) |
| free (nai->ai_canonname); |
| free (nai); |
| err = EAI_MEMORY; |
| return NULL; |
| } |
| if (v4mapped) |
| { |
| struct sockaddr_in6 *in = (struct sockaddr_in6 *) nai->ai_addr; |
| in->sin6_family = AF_INET6; |
| in->sin6_port = ((struct sockaddr_in *) ai->ai_addr)->sin_port; |
| in->sin6_flowinfo = 0; |
| in->sin6_addr.s6_addr32[0] = 0; |
| in->sin6_addr.s6_addr32[1] = 0; |
| in->sin6_addr.s6_addr32[2] = htonl (0xffff); |
| in->sin6_addr.s6_addr32[3] = ((struct sockaddr_in *) ai->ai_addr)->sin_addr.s_addr; |
| in->sin6_scope_id = 0; |
| } |
| else |
| memcpy (nai->ai_addr, ai->ai_addr, ai->ai_addrlen); |
| |
| return nai; |
| } |
| |
| static struct addrinfo * |
| ga_duplist (struct addrinfoW *ai, bool v4mapped, int idn_flags, int &err) |
| { |
| struct addrinfo *tmp, *nai = NULL, *nai0 = NULL; |
| |
| for (; ai; ai = ai->ai_next, nai = tmp) |
| { |
| if (!(tmp = ga_dup (ai, v4mapped, idn_flags, err))) |
| goto bad; |
| if (!nai0) |
| nai0 = tmp; |
| if (nai) |
| nai->ai_next = tmp; |
| } |
| return nai0; |
| |
| bad: |
| cygwin_freeaddrinfo (nai0); |
| return NULL; |
| } |
| |
| /* Cygwin specific wrappers around the gai functions. */ |
| static struct gai_errmap_t |
| { |
| int w32_errval; |
| const char *errtxt; |
| } gai_errmap[] = |
| { |
| {0, "Success"}, |
| /* EAI_ADDRFAMILY */ |
| {0, "Address family for hostname not supported"}, |
| /* EAI_AGAIN */ |
| {WSATRY_AGAIN, "Temporary failure in name resolution"}, |
| /* EAI_BADFLAGS */ |
| {WSAEINVAL, "Bad value for ai_flags"}, |
| /* EAI_FAIL */ |
| {WSANO_RECOVERY, "Non-recoverable failure in name resolution"}, |
| /* EAI_FAMILY */ |
| {WSAEAFNOSUPPORT, "ai_family not supported"}, |
| /* EAI_MEMORY */ |
| {WSA_NOT_ENOUGH_MEMORY, "Memory allocation failure"}, |
| /* EAI_NODATA */ |
| {WSANO_DATA, "No address associated with hostname"}, |
| /* EAI_NONAME */ |
| {WSAHOST_NOT_FOUND, "Name or service not known"}, |
| /* EAI_SERVICE */ |
| {WSATYPE_NOT_FOUND, "Servname not supported for ai_socktype"}, |
| /* EAI_SOCKTYPE */ |
| {WSAESOCKTNOSUPPORT, "ai_socktype not supported"}, |
| /* EAI_SYSTEM */ |
| {0, "System error"}, |
| /* EAI_BADHINTS */ |
| {0, "Invalid value for hints"}, |
| /* EAI_PROTOCOL */ |
| {0, "Resolved protocol is unknown"}, |
| /* EAI_OVERFLOW */ |
| {WSAEFAULT, "An argument buffer overflowed"}, |
| /* EAI_IDN_ENCODE */ |
| {0, "Parameter string not correctly encoded"} |
| }; |
| |
| extern "C" const char * |
| cygwin_gai_strerror (int err) |
| { |
| if (err >= 0 && err < (int) (sizeof gai_errmap / sizeof *gai_errmap)) |
| return gai_errmap[err].errtxt; |
| return "Unknown error"; |
| } |
| |
| static int |
| w32_to_gai_err (int w32_err) |
| { |
| if (w32_err >= WSABASEERR) |
| for (unsigned i = 0; i < sizeof gai_errmap / sizeof *gai_errmap; ++i) |
| if (gai_errmap[i].w32_errval == w32_err) |
| return i; |
| return w32_err; |
| } |
| |
| extern "C" int |
| cygwin_getaddrinfo (const char *hostname, const char *servname, |
| const struct addrinfo *hints, struct addrinfo **res) |
| { |
| int ret = 0; |
| |
| /* Windows' getaddrinfo implementations lets all possible values |
| in ai_flags slip through and just ignores unknown values. So we |
| check manually here. */ |
| #define AI_IDN_MASK (AI_IDN | \ |
| AI_CANONIDN | \ |
| AI_IDN_ALLOW_UNASSIGNED | \ |
| AI_IDN_USE_STD3_ASCII_RULES) |
| #ifndef AI_DISABLE_IDN_ENCODING |
| #define AI_DISABLE_IDN_ENCODING 0x80000 |
| #endif |
| __try |
| { |
| if (hints && (hints->ai_flags |
| & ~(AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_ALL |
| | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED |
| | AI_IDN_MASK))) |
| return EAI_BADFLAGS; |
| /* AI_NUMERICSERV is not supported "by Microsoft providers". We just |
| check the servname parameter by ourselves here. */ |
| if (hints && (hints->ai_flags & AI_NUMERICSERV)) |
| { |
| char *p; |
| if (servname && *servname && (strtoul (servname, &p, 10), *p)) |
| return EAI_NONAME; |
| } |
| |
| int idn_flags = hints ? (hints->ai_flags & AI_IDN_MASK) : 0; |
| const char *src; |
| mbstate_t ps; |
| tmp_pathbuf tp; |
| wchar_t *whost = NULL, *wserv = NULL; |
| struct addrinfoW whints, *wres; |
| |
| if (hostname) |
| { |
| memset (&ps, 0, sizeof ps); |
| src = hostname; |
| whost = tp.w_get (); |
| if (mbsrtowcs (whost, &src, NT_MAX_PATH, &ps) == (size_t) -1) |
| return EAI_IDN_ENCODE; |
| if (src) |
| return EAI_MEMORY; |
| if (idn_flags & AI_IDN) |
| { |
| /* Map flags to equivalent IDN_* flags. */ |
| wchar_t *ascbuf = tp.w_get (); |
| if (IdnToAscii (idn_flags >> 16, whost, -1, ascbuf, NT_MAX_PATH)) |
| whost = ascbuf; |
| else if (GetLastError () != ERROR_PROC_NOT_FOUND) |
| return EAI_IDN_ENCODE; |
| } |
| } |
| if (servname) |
| { |
| memset (&ps, 0, sizeof ps); |
| src = servname; |
| wserv = tp.w_get (); |
| if (mbsrtowcs (wserv, &src, NT_MAX_PATH, &ps) == (size_t) -1) |
| return EAI_IDN_ENCODE; |
| if (src) |
| return EAI_MEMORY; |
| } |
| |
| if (!hints) |
| { |
| /* Default settings per glibc man page. */ |
| memset (&whints, 0, sizeof whints); |
| whints.ai_family = PF_UNSPEC; |
| whints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; |
| } |
| else |
| { |
| /* sizeof addrinfo == sizeof addrinfoW */ |
| memcpy (&whints, hints, sizeof whints); |
| whints.ai_flags &= ~AI_IDN_MASK; |
| #ifdef __x86_64__ |
| /* ai_addrlen is socklen_t (4 bytes) in POSIX but size_t (8 bytes) in |
| Winsock. Sert upper 4 bytes explicitely to 0 to avoid EAI_FAIL. */ |
| whints.ai_addrlen &= UINT32_MAX; |
| #endif |
| /* On Windows, the default behaviour is as if AI_ADDRCONFIG is set, |
| apparently for performance reasons. To get the POSIX default |
| behaviour, the AI_ALL flag has to be set. */ |
| if (whints.ai_family == PF_UNSPEC |
| && !(whints.ai_flags & AI_ADDRCONFIG)) |
| whints.ai_flags |= AI_ALL; |
| } |
| /* Disable automatic IDN conversion on W8 and later. */ |
| whints.ai_flags |= AI_DISABLE_IDN_ENCODING; |
| ret = GetAddrInfoW (whost, wserv, &whints, &wres); |
| /* Try to workaround an apparent shortcoming in Winsock's getaddrinfo |
| implementation. See this link for details: |
| https://communities.vmware.com/message/2577858#2577858 */ |
| if (ret == WSANO_RECOVERY && (whints.ai_flags & AI_ALL)) |
| { |
| whints.ai_flags &= ~AI_ALL; |
| ret = GetAddrInfoW (whost, wserv, &whints, &wres); |
| } |
| ret = w32_to_gai_err (ret); |
| /* Always copy over to self-allocated memory. */ |
| if (!ret) |
| { |
| *res = ga_duplist (wres, false, idn_flags, ret); |
| FreeAddrInfoW (wres); |
| if (!*res) |
| __leave; |
| } |
| } |
| __except (EFAULT) |
| { |
| ret = EAI_SYSTEM; |
| } |
| __endtry |
| return ret; |
| } |
| |
| extern "C" int |
| cygwin_getnameinfo (const struct sockaddr *sa, socklen_t salen, |
| char *host, size_t hostlen, char *serv, |
| size_t servlen, int flags) |
| { |
| int ret = 0; |
| |
| __try |
| { |
| /* We call GetNameInfoW with local buffers and convert to locale |
| charset to allow RFC 3490 IDNs like glibc 2.3.4 and later. */ |
| #define NI_IDN_MASK (NI_IDN | \ |
| NI_IDN_ALLOW_UNASSIGNED | \ |
| NI_IDN_USE_STD3_ASCII_RULES) |
| int idn_flags = flags & NI_IDN_MASK; |
| flags &= ~NI_IDN_MASK; |
| tmp_pathbuf tp; |
| wchar_t *whost = NULL, *wserv = NULL; |
| DWORD whlen = 0, wslen = 0; |
| |
| if (host && hostlen) |
| { |
| whost = tp.w_get (); |
| whlen = NT_MAX_PATH; |
| } |
| if (serv && servlen) |
| { |
| wserv = tp.w_get (); |
| wslen = NT_MAX_PATH; |
| } |
| |
| ret = w32_to_gai_err (GetNameInfoW (sa, salen, whost, whlen, |
| wserv, wslen, flags)); |
| if (!ret) |
| { |
| const wchar_t *src; |
| |
| if (whost) |
| { |
| if (idn_flags & NI_IDN) |
| { |
| /* Map flags to equivalent IDN_* flags. */ |
| wchar_t *idnbuf = tp.w_get (); |
| if (IdnToUnicode (idn_flags >> 16, whost, -1, |
| idnbuf, NT_MAX_PATH)) |
| whost = idnbuf; |
| else if (GetLastError () != ERROR_PROC_NOT_FOUND) |
| return EAI_IDN_ENCODE; |
| } |
| src = whost; |
| if (wcsrtombs (host, &src, hostlen, NULL) == (size_t) -1) |
| return EAI_IDN_ENCODE; |
| if (src) |
| return EAI_OVERFLOW; |
| } |
| if (wserv) |
| { |
| src = wserv; |
| if (wcsrtombs (serv, &src, servlen, NULL) == (size_t) -1) |
| return EAI_IDN_ENCODE; |
| if (src) |
| return EAI_OVERFLOW; |
| } |
| } |
| else if (ret == EAI_SYSTEM) |
| set_winsock_errno (); |
| } |
| __except (EFAULT) |
| { |
| ret = EAI_SYSTEM; |
| } |
| __endtry |
| return ret; |
| } |
| |
| /* These functions are stick to the end of this file so that the |
| optimization in asm/byteorder.h can be used even here in net.cc. */ |
| |
| #undef htonl |
| #undef ntohl |
| #undef htons |
| #undef ntohs |
| |
| /* htonl: standards? */ |
| extern "C" uint32_t |
| htonl (uint32_t x) |
| { |
| return __htonl (x); |
| } |
| |
| /* ntohl: standards? */ |
| extern "C" uint32_t |
| ntohl (uint32_t x) |
| { |
| return __ntohl (x); |
| } |
| |
| /* htons: standards? */ |
| extern "C" uint16_t |
| htons (uint16_t x) |
| { |
| return __htons (x); |
| } |
| |
| /* ntohs: standards? */ |
| extern "C" uint16_t |
| ntohs (uint16_t x) |
| { |
| return __ntohs (x); |
| } |