diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2006-07-25 23:23:23 +0400 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2006-07-25 23:23:23 +0400 |
commit | 70e476d27be8e49146c49e8d6e1319100b84d5eb (patch) | |
tree | 1827e4d9dec3e1eb523db1df6a53e64c1c234518 /winsup/cygwin/net.cc | |
parent | e9d500b6a04e842ffaf04fd1cb6048b44fb99e67 (diff) |
2006-07-25 Corinna Vinschen <corinna@vinschen.de>
* include/cygwin/version.h: Bump DLL version to 1.7.0.
2006-07-25 Corinna Vinschen <corinna@vinschen.de>
* select.h: Remove.
* fhandler_socket.cc: Don't include select.h.
* select.cc: Ditto.
2006-07-25 Corinna Vinschen <corinna@vinschen.de>
* cygtls.h: Drop socket related includes.
(struct _local_storage): Remove exitsock and exitsock_sin. Add
select_sockevt.
* cygtls.cc: Accomodate above change throughout.
* fhandler.h (class fhandler_socket): Make wsock_evt public.
* fhandler_socket.cc (fhandler_socket::fhandler_socket): Accomodate
reordering members.
(fhandler_socket::evaluate_events): Drop FD_CONNECT event as soon as
it gets read once. Never remove FD_WRITE event here.
(fhandler_socket::wait_for_events): Wait 50 ms instead of INFINITE for
socket events.
(fhandler_socket::accept): Fix conditional. Set wsock_events members
of accepted socket to useful start values.
(fhandler_socket::recv_internal): Always drop FD_READ/FD_OOB events from
wsock_events after the call to WSARecvFrom.
(fhandler_socket::send_internal): Drop FD_WRITE event from wsock_events
if the call to WSASendTo fails with WSAEWOULDBLOCK. Fix return value
condition.
* select.cc (struct socketinf): Change to accomodate using socket event
handling.
(peek_socket): Use event handling for peeking socket.
(thread_socket): Ditto.
(start_thread_socket): Ditto.
(socket_cleanup): Same here.
* tlsoffsets.h: Regenerate.
2006-07-20 Corinna Vinschen <corinna@vinschen.de>
* fhandler.h (class fhandler_socket): Rearrange slightly to keep
event handling methods and members together. Drop owner status flag.
Split wait method. Rename event handling methods for readability.
* fhandler_socket.cc (struct wsa_event): Add owner field.
(LOCK_EVENTS): New macro.
(UNLOCK_EVENTS): Ditto.
(fhandler_socket::init_events): rename from prepare.
(fhandler_socket::evaluate_events): First half of former wait method.
Do everything but wait. Allow specifiying whether or not events from
event_mask should be erased from wsock_events->events. Simplify
OOB handling. Allow sending SIGURG to any process (group).
(fhandler_socket::wait_for_events): Second half of former wait method.
Call evaluate_events and wait in a loop if socket is blocking.
(fhandler_socket::release_events): Rename from release.
(fhandler_socket::connect): Accomodate above name changes.
(fhandler_socket::accept): Ditto.
(fhandler_socket::recv_internal): Ditto.
(fhandler_socket::send_internal): Ditto.
(fhandler_socket::close): Ditto.
(fhandler_socket::fcntl): Always set owner to given input value on
F_SETOWN. Handle F_GETOWN.
* net.cc (fdsock): Accomodate above name changes.
2006-07-20 Corinna Vinschen <corinna@vinschen.de>
* fhandler_socket.cc (fhandler_socket::wait): Set Winsock errno to
WSAEWOULDBLOCK instead of WSAEINPROGRESS.
2006-07-18 Brian Ford <Brian.Ford@FlightSafety.com>
Corinna Vinschen <corinna@vinschen.de>
* winsup.h (mmap_region_status): New enum.
(mmap_is_attached_or_noreserve_page): Adjust prototype and rename
as below.
* mmap.cc (mmap_is_attached_or_noreserve_page): Rename
mmap_is_attached_or_noreserve. Add region length parameter.
Return enum above.
* exceptions.cc (_cygtls::handle_exceptions): Accomodate above.
* fhandler.cc (fhandler_base::raw_read): Call above for NOACCESS
errors and retry on success to allow reads into untouched
MAP_NORESERVE buffers.
2006-07-18 Corinna Vinschen <corinna@vinschen.de>
* cygwin.din (posix_openpt): Export.
* tty.cc (posix_openpt): New function.
* include/cygwin/stdlib.h (posix_openpt): Declare.
* include/cygwin/version.h: Bump API minor number.
2006-07-14 Corinna Vinschen <corinna@vinschen.de>
* security.cc (get_token_group_sidlist): Always add the interactive
group to the token. Add comment. Create logon_id group SID by
copying it from incoming group list.
(create_token): Add subauth_token parameter. Use information in
subauth_token if present. Tweak SourceIdentifier if subauth_token
is present for debugging purposes.
* security.h (create_token): Add subauth_token parameter in declaration.
* syscalls.cc (seteuid32): Call subauth first. Call create_token
regardless. Use subauth token in call to create_token if subauth
succeeded.
2006-07-13 Corinna Vinschen <corinna@vinschen.de>
* include/netinet/in.h: Update copyright.
2006-07-13 Corinna Vinschen <corinna@vinschen.de>
* fhandler_socket.cc (fhandler_socket::wait): Rework function so that
WaitForMultipleObjects is really only called when necessary.
2006-07-12 Corinna Vinschen <corinna@vinschen.de>
* include/netdb.h: Declare rcmd, rcmd_af, rexec, rresvport,
rresvport_af, iruserok, iruserok_sa, ruserok.
2006-07-12 Corinna Vinschen <corinna@vinschen.de>
* Makefile.in (DLL_OFILES): Drop iruserok.o. Add rcmd.o.
* autoload.cc (rcmd): Drop definition.
* cygwin.din: Export bindresvport, bindresvport_sa, iruserok_sa,
rcmd_af, rresvport_af.
* net.cc (cygwin_rcmd): Remove.
(last_used_bindresvport): Rename from last_used_rrecvport.
(cygwin_bindresvport_sa): New function implementing bindresvport_sa.
(cygwin_bindresvport): New function implementing bindresvport.
(cygwin_rresvport): Remove.
* include/cygwin/version.h: Bump API minor number.
* include/netinet/in.h: Declare bindresvport and bindresvport_sa.
* libc/iruserok.c: Remove file.
* libc/rcmd.cc: New file implementing rcmd, rcmd_af, rresvport,
rresvport_af, iruserok_sa, iruserok and ruserok.
2006-07-12 Corinna Vinschen <corinna@vinschen.de>
* fhandler_socket.cc (fhandler_socket::getsockname): Return valid
result for unbound sockets.
2006-07-11 Corinna Vinschen <corinna@vinschen.de>
* fhandler_socket.cc (fhandler_socket::fixup_after_fork): Handle
wsock_mtx and wsock_evt on fork, thus handling close_on_exec correctly.
(fhandler_socket::fixup_after_exec): Drop misguided attempt to handle
close_on_exec here.
(fhandler_socket::dup): Call fixup_after_fork with NULL parent.
Add comment.
(fhandler_socket::set_close_on_exec): Handle wsock_mtx and wsock_evt.
2006-07-10 Corinna Vinschen <corinna@vinschen.de>
* fhandler.h (class fhandler_socket): Add wsock_mtx, wsock_evt
and wsock_events members. Remove closed status flag, add listener
status flag. Accomodate new implementation of socket event handling
methods. Declare recv* and send* functions ssize_t as the POSIX
equivalents.
(fhandler_socket::recv_internal): Declare.
(fhandler_socket::send_internal): Ditto.
* fhandler_socket.cc (EVENT_MASK): Define mask of selected events.
(fhandler_socket::fhandler_socket): Initialize new members.
(fhandler_socket::af_local_setblocking): Don't actually set the
socket to blocking mode. Keep sane event selection.
(fhandler_socket::af_local_unsetblocking): Don't actually set the
socket to previous blocking setting, just remember it.
(struct wsa_event): New structure to keep event data per shared
socket.
(NUM_SOCKS): Define number of shared sockets concurrently handled by
all active Cygwin processes.
(wsa_events): New shared datastructure keeping all wsa_event records.
(socket_serial_number): New shared variable to identify shared sockets.
(wsa_slot_mtx): Global mutex to serialize wsa_events access.
(search_wsa_event_slot): New static function to select a new wsa_event
slot for a new socket.
(fhandler_socket::prepare): Rewrite. Prepare event selection
per new socket.
(fhandler_socket::wait): Rewrite. Wait for socket events in thread
safe and multiple process safe.
(fhandler_socket::release): Rewrite. Close per-socket descriptor
mutex handle and event handle.
(fhandler_socket::dup): Duplicate wsock_mtx and wsock_evt. Fix
copy-paste error in debug output.
(fhandler_socket::connect): Accomodate new event handling.
(fhandler_socket::listen): Set listener flag on successful listen.
(fhandler_socket::accept): Accomodate new event handling.
(fhandler_socket::recv_internal): New inline method centralizing
common recv code.
(fhandler_socket::recvfrom): Call recv_internal now.
(fhandler_socket::recvmsg): Ditto. Streamline copying from iovec
to WSABUF.
(fhandler_socket::send_internal): New inline method centralizing
common send code.
(fhandler_socket::sendto): Call send_internal now.
(fhandler_socket::sendmsg): Ditto. Streamline copying from iovec
to WSABUF.
(fhandler_socket::close): Call release now.
(fhandler_socket::ioctl): Never actually switch to blocking mode.
Just keep track of the setting.
* net.cc (fdsock): Call prepare now.
(cygwin_connect): Revert again to event driven technique.
(cygwin_accept): Ditto.
* poll.cc (poll): Don't call recvfrom on a listening socket.
Remove special case for failing recvfrom.
* include/sys/socket.h: Declare recv* and send* functions ssize_t as
requested by POSIX.
2006-07-07 Corinna Vinschen <corinna@vinschen.de>
* net.cc (cygwin_inet_ntop): Fix data type of forth parameter.
2006-07-06 Corinna Vinschen <corinna@vinschen.de>
* include/cygwin/in6.h (struct in6_addr): Fix typo.
2006-07-06 Corinna Vinschen <corinna@vinschen.de>
* cygwin.din: Export in6addr_any, in6addr_loopback, freeaddrinfo,
gai_strerror, getaddrinfo, getnameinfo.
* fhandler_socket.cc: Include cygwin/in6.h.
(get_inet_addr): Accomodate AF_INET6 usage.
(fhandler_socket::connect): Ditto.
(fhandler_socket::listen): Ditto.
(fhandler_socket::sendto): Ditto.
* net.cc: Include cygwin/in6.h.
(in6addr_any): Define.
(in6addr_loopback): Define.
(cygwin_socket): Accomodate AF_INET6 usage.
(socketpair): Bind socketpairs only to loopback for security.
(inet_pton4): New static function.
(inet_pton6): Ditto.
(cygwin_inet_pton): New AF_INET6 aware inet_pton implementation.
(inet_ntop4): New static function.
(inet_ntop6): Ditto.
(cygwin_inet_ntop): New AF_INET6 aware inet_ntop implementation.
(ga_aistruct): New static function.
(ga_clone): Ditto.
(ga_echeck): Ditto.
(ga_nsearch): Ditto.
(ga_port): Ditto.
(ga_serv): Ditto.
(ga_unix): Ditto.
(gn_ipv46): Ditto.
(ipv4_freeaddrinfo): Ditto.
(ipv4_getaddrinfo): Ditto.
(ipv4_getnameinfo): Ditto.
(gai_errmap_t): New structure holding error code - error string mapping.
(cygwin_gai_strerror): New function implementing gai_strerror.
(w32_to_gai_err): New static function.
(get_ipv6_funcs): Ditto.
(load_ipv6_funcs): Ditto.
(cygwin_freeaddrinfo): New function implementing freeaddrinfo.
(cygwin_getaddrinfo): New function implementing getaddrinfo.
(cygwin_getnameinfo): New function implementing getnameinfo.
* include/netdb.h: Include stdint.h and cygwin/socket.h. Define
data types and macros used by getaddrinfo and friends. Declare
freeaddrinfo, gai_strerror, getaddrinfo and getnameinfo.
* include/cygwin/in.h: Add IPv6 related IPPROTOs. Remove definition
of struct sockaddr_in6. Include cygwin/in6.h instead.
* include/cygwin/in6.h: New header file defining IPv6 releated
data types and macros.
* include/cygwin/socket.h: Enable AF_INET6 and PF_INET6. Add
IPv6 related socket options.
* include/cygwin/version.h: Bump API minor number.
2006-07-06 Corinna Vinschen <corinna@vinschen.de>
* autoload.cc (DsGetDcNameA): Define.
(NetGetAnyDCName): Define.
* security.cc: Include dsgetdc.h.
(DsGetDcNameA): Declare.
(DS_FORCE_REDISCOVERY): Define.
(get_logon_server): Add bool parameter to control rediscovery of DC.
Use DsGetDcNameA function if supported, NetGetDCName/NetGetAnyDCName
otherwise.
(get_server_groups): Rediscover DC if get_user_groups fails and
try again.
(get_reg_security): Use correct error code macro when testing
RegGetKeySecurity return value.
* security.h (get_logon_server): Remove default vaue from wserver
parameter. Add rediscovery parameter.
* uinfo.cc (cygheap_user::env_logsrv): Accomodate rediscovery parameter
in call to get_logon_server.
Diffstat (limited to 'winsup/cygwin/net.cc')
-rw-r--r-- | winsup/cygwin/net.cc | 1652 |
1 files changed, 1438 insertions, 214 deletions
diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc index 7db6c1abb..80f34a886 100644 --- a/winsup/cygwin/net.cc +++ b/winsup/cygwin/net.cc @@ -40,6 +40,7 @@ details. */ #include "pinfo.h" #include "registry.h" #include "cygtls.h" +#include "cygwin/in6.h" extern "C" { @@ -52,6 +53,9 @@ extern "C" const char *cygwin_inet_ntop (int, const void *, char *, socklen_t); } /* 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) { @@ -509,6 +513,8 @@ fdsock (cygheap_fdmanip& fd, const device *dev, SOCKET soc) if (!fd.isopen ()) return false; fd->set_io_handle ((HANDLE) soc); + if (!((fhandler_socket *) fd)->init_events ()) + return false; fd->set_flags (O_RDWR | O_BINARY); fd->uninterruptible_io (true); cygheap->fdtab.inc_need_fixup_before (); @@ -545,7 +551,8 @@ cygwin_socket (int af, int type, int protocol) debug_printf ("socket (%d, %d, %d)", af, type, protocol); - soc = socket (AF_INET, type, af == AF_LOCAL ? 0 : protocol); + soc = socket (af == AF_LOCAL ? AF_INET : af, type, + af == AF_LOCAL ? 0 : protocol); if (soc == INVALID_SOCKET) { @@ -555,10 +562,10 @@ cygwin_socket (int af, int type, int protocol) const device *dev; - if (af == AF_INET) - dev = type == SOCK_STREAM ? tcp_dev : udp_dev; - else + if (af == AF_LOCAL) dev = type == SOCK_STREAM ? stream_dev : dgram_dev; + else + dev = type == SOCK_STREAM ? tcp_dev : udp_dev; { cygheap_fdnew fd; @@ -771,56 +778,7 @@ cygwin_connect (int fd, const struct sockaddr *name, socklen_t namelen) if (efault.faulted (EFAULT) || !fh) res = -1; else - { - bool was_blocking = false; - if (!fh->is_nonblocking ()) - { - int nonblocking = 1; - fh->ioctl (FIONBIO, &nonblocking); - was_blocking = true; - } - res = fh->connect (name, namelen); - if (was_blocking) - { - if (res == -1 && get_errno () == EINPROGRESS) - { - size_t fds_size = howmany (fd + 1, NFDBITS) * sizeof (fd_mask); - fd_set *write_fds = (fd_set *) alloca (fds_size); - fd_set *except_fds = (fd_set *) alloca (fds_size); - memset (write_fds, 0, fds_size); - memset (except_fds, 0, fds_size); - FD_SET (fd, write_fds); - FD_SET (fd, except_fds); - res = cygwin_select (fd + 1, NULL, write_fds, except_fds, NULL); - if (res > 0 && FD_ISSET (fd, except_fds)) - { - res = -1; - for (;;) - { - int err; - int len = sizeof err; - cygwin_getsockopt (fd, SOL_SOCKET, SO_ERROR, - (void *) &err, &len); - if (err) - { - set_errno (err); - break; - } - low_priority_sleep (0); - } - } - else if (res > 0) - res = 0; - else - { - WSASetLastError (WSAEINPROGRESS); - set_winsock_errno (); - } - } - int nonblocking = 0; - fh->ioctl (FIONBIO, &nonblocking); - } - } + res = fh->connect (name, namelen); syscall_printf ("%d = connect (%d, %p, %d)", res, fd, name, namelen); @@ -954,19 +912,7 @@ cygwin_accept (int fd, struct sockaddr *peer, socklen_t *len) if (efault.faulted (EFAULT) || !fh) res = -1; else - { - if (!fh->is_nonblocking ()) - { - size_t fds_size = howmany (fd + 1, NFDBITS) * sizeof (fd_mask); - fd_set *read_fds = (fd_set *) alloca (fds_size); - memset (read_fds, 0, fds_size); - FD_SET (fd, read_fds); - res = cygwin_select (fd + 1, read_fds, NULL, NULL, NULL); - if (res == -1) - return -1; - } - res = fh->accept (peer, len); - } + res = fh->accept (peer, len); syscall_printf ("%d = accept (%d, %p, %p)", res, fd, peer, len); return res; @@ -1816,146 +1762,92 @@ get_ifconf (struct ifconf *ifc, int what) return 0; } -/* exported as rcmd: standards? */ -extern "C" int -cygwin_rcmd (char **ahost, unsigned short inport, char *locuser, - char *remuser, char *cmd, int *fd2p) -{ - int res = -1; - SOCKET fd2s; - - sig_dispatch_pending (); - - myfault efault; - if (efault.faulted (EFAULT)) - return (int) INVALID_SOCKET; - if (!*locuser) - { - set_errno (EINVAL); - return (int) INVALID_SOCKET; - } - - res = rcmd (ahost, inport, locuser, remuser, cmd, fd2p ? &fd2s : NULL); - if (res != (int) INVALID_SOCKET) - { - cygheap_fdnew res_fd; - - if (res_fd >= 0 && fdsock (res_fd, tcp_dev, res)) - { - ((fhandler_socket *) res_fd)->connect_state (connected); - res = res_fd; - } - else - { - closesocket (res); - res = -1; - } - - if (res >= 0 && fd2p) - { - cygheap_fdnew newfd (res_fd, false); - cygheap_fdget fd (*fd2p); - - if (newfd >= 0 && fdsock (newfd, tcp_dev, fd2s)) - { - *fd2p = newfd; - ((fhandler_socket *) fd2p)->connect_state (connected); - } - else - { - closesocket (res); - closesocket (fd2s); - res = -1; - } - } - } - - syscall_printf ("%d = rcmd (...)", res); - return res; -} - -/* The below implementation of rresvport looks pretty ugly, but there's - a problem in Winsock. The bind(2) call does not fail if a local - address is still in TIME_WAIT state, and there's no way to get this - behaviour. Unfortunately the first time when this is detected is when - the calling application tries to connect. - - One (also not really foolproof) way around this problem would be to use - the iphlpapi function GetTcpTable and to check if the port in question is - in TIME_WAIT state and if so, choose another port number. But this method - is as prone to races as the below one, or any other method using random - port numbers, etc. The below method at least tries to avoid races between - multiple applications using rrecvport. - - As for the question "why don't you just use the Winsock rresvport?"... - For some reason I do NOT understand, the call to WinSocks rresvport - corrupts the stack when Cygwin is built using -fomit-frame-pointers. - And then again, the Winsock rresvport function has the exact same - problem with reusing ports in the TIME_WAIT state as the socket/bind - method has. So there's no gain in using that function. */ - #define PORT_LOW (IPPORT_EFSSERVER + 1) #define PORT_HIGH (IPPORT_RESERVED - 1) #define NUM_PORTS (PORT_HIGH - PORT_LOW + 1) -LONG last_used_rrecvport __attribute__((section (".cygwin_dll_common"), shared)) = IPPORT_RESERVED; +LONG last_used_bindresvport __attribute__((section (".cygwin_dll_common"), shared)) = IPPORT_RESERVED; -/* exported as rresvport: standards? */ extern "C" int -cygwin_rresvport (int *port) +cygwin_bindresvport_sa (int fd, struct sockaddr *sa) { - int res; + struct sockaddr_storage sst; + struct sockaddr_in *sin = NULL; + struct sockaddr_in6 *sin6 = NULL; + in_port_t port; + socklen_t salen; + int ret; + sig_dispatch_pending (); myfault efault; if (efault.faulted (EFAULT)) return -1; - res = socket (AF_INET, SOCK_STREAM, 0); - if (res != (int) INVALID_SOCKET) + fhandler_socket *fh = get (fd); + if (!fh) + return -1; + + if (!sa) { - LONG myport; - int ret = SOCKET_ERROR; - struct sockaddr_in sin; - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; + sa = (struct sockaddr *) &sst; + memset (&sst, 0, sizeof sst); + sa->sa_family = fh->get_addr_family (); + } - for (int i = 0; i < NUM_PORTS; i++) - { - while ((myport = InterlockedExchange (&last_used_rrecvport, 0)) == 0) - low_priority_sleep (0); - if (--myport < PORT_LOW) - myport = PORT_HIGH; - InterlockedExchange (&last_used_rrecvport, myport); - - sin.sin_port = htons (myport); - if (!(ret = bind (res, (struct sockaddr *) &sin, sizeof sin))) - break; - int err = WSAGetLastError (); - if (err != WSAEADDRINUSE && err != WSAEINVAL) - break; - } - if (ret == SOCKET_ERROR) - { - closesocket (res); - res = (int) INVALID_SOCKET; - } - else if (port) - *port = myport; + 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); + return -1; } - if (res != (int) INVALID_SOCKET) + /* If a non-zero port number is given, try this first. If that succeeds, + or if the error message is serious, return. */ + if (port) { - cygheap_fdnew res_fd; + ret = fh->bind (sa, salen); + if (!ret || (get_errno () != EADDRINUSE && get_errno () != EINVAL)) + return ret; + } - if (res_fd >= 0 && fdsock (res_fd, tcp_dev, res)) - res = res_fd; + LONG myport; + + for (int i = 0; i < NUM_PORTS; i++) + { + while ((myport = InterlockedExchange (&last_used_bindresvport, 0)) == 0) + low_priority_sleep (0); + if (--myport < PORT_LOW) + myport = PORT_HIGH; + InterlockedExchange (&last_used_bindresvport, myport); + + if (sa->sa_family == AF_INET6) + sin6->sin6_port = htons (myport); else - res = -1; + sin->sin_port = htons (myport); + if (!(ret = fh->bind (sa, salen))) + break; + if (get_errno () != EADDRINUSE && get_errno () != EINVAL) + break; } - syscall_printf ("%d = rresvport (%d)", res, port ? *port : 0); - return res; + return ret; +} + +extern "C" int +cygwin_bindresvport (int fd, struct sockaddr_in *sin) +{ + return cygwin_bindresvport_sa (fd, (struct sockaddr *) sin); } /* socketpair: standards? */ @@ -2038,7 +1930,7 @@ socketpair (int family, int type, int protocol, int *sb) { sock_out.sin_family = AF_INET; sock_out.sin_port = 0; - sock_out.sin_addr.s_addr = INADDR_ANY; + sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK); if (bind (outsock, (struct sockaddr *) &sock_out, sizeof (sock_out)) < 0) { debug_printf ("bind failed"); @@ -2207,49 +2099,1381 @@ cygwin_sendmsg (int fd, const struct msghdr *msg, int flags) return res; } -/* See "UNIX Network Programming, Networing APIs: Sockets and XTI", - W. Richard Stevens, Prentice Hall PTR, 1998. */ +/* 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. + */ + +#define IN6ADDRSZ 16 +#define INADDRSZ 4 +#define INT16SZ 2 + +/* 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_char *dst) +{ + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + u_char 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_int 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_char *dst) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit; + u_int 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_char) (val >> 8) & 0xff; + *tp++ = (u_char) 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_char) (val >> 8) & 0xff; + *tp++ = (u_char) 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 family, const char *strptr, void *addrptr) +cygwin_inet_pton (int af, const char *src, void *dst) { - if (family == AF_INET) + switch (af) { - struct in_addr in_val; + case AF_INET: + return (inet_pton4(src, (u_char *) dst)); + case AF_INET6: + return (inet_pton6(src, (u_char *) 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_char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4 (const u_char *src, char *dst, size_t size) +{ + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; - if (cygwin_inet_aton (strptr, &in_val)) + __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_char *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_int 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; + 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))) { - memcpy (addrptr, &in_val, sizeof (struct in_addr)); - return 1; + if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; } - return 0; + __small_sprintf(tp, "%x", words[i]); + tp += strlen(tp); } - set_errno (EAFNOSUPPORT); - return -1; + /* 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); } -/* See "UNIX Network Programming, Networing APIs: Sockets and XTI", - W. Richard Stevens, Prentice Hall PTR, 1998. */ +/* 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 family, const void *addrptr, char *strptr, socklen_t len) +cygwin_inet_ntop (int af, const void *src, char *dst, socklen_t size) { - const u_char *p = (const u_char *) addrptr; + switch (af) + { + case AF_INET: + return (inet_ntop4((const u_char *) src, dst, size)); + case AF_INET6: + return (inet_ntop6((const u_char *) src, dst, size)); + default: + errno = EAFNOSUPPORT; + return (NULL); + } + /* NOTREACHED */ +} - myfault efault; - if (efault.faulted (EFAULT)) - return NULL; - if (family == AF_INET) +/* W. Richard STEVENS libgai implementation, slightly tweaked for inclusion + into Cygwin as pure IPv4 replacement. Please note that the code is + kept intact as much as possible. Especially the IPv6 and AF_UNIX code + is kept in, even though we can support neither of them. Please don't + activate them, they won't work correctly. */ + +#define IPv4 +#undef IPv6 +#undef UNIXdomain + +#undef HAVE_SOCKADDR_SA_LEN +#define gethostbyname2(host,family) cygwin_gethostbyname((host)) + +#define AI_CLONE 0x8000 /* Avoid collision with AI_ values in netdb.h */ + +/* + * Create and fill in an addrinfo{}. + */ + +/* include ga_aistruct1 */ +static int +ga_aistruct (struct addrinfo ***paipnext, const struct addrinfo *hintsp, + const void *addr, int family) +{ + struct addrinfo *ai; + + if ((ai = (struct addrinfo *) calloc (1, sizeof (struct addrinfo))) == NULL) + return (EAI_MEMORY); + ai->ai_next = NULL; + ai->ai_canonname = NULL; + **paipnext = ai; + *paipnext = &ai->ai_next; + + if ((ai->ai_socktype = hintsp->ai_socktype) == 0) + ai->ai_flags |= AI_CLONE; + + ai->ai_protocol = hintsp->ai_protocol; +/* end ga_aistruct1 */ + +/* include ga_aistruct2 */ + switch ((ai->ai_family = family)) { - char temp[64]; /* Big enough for 4 ints ... */ +#ifdef IPv4 + case AF_INET: + { + struct sockaddr_in *sinptr; + + /* 4allocate sockaddr_in{} and fill in all but port */ + if ((sinptr = (struct sockaddr_in *) + calloc (1, sizeof (struct sockaddr_in))) == NULL) + return (EAI_MEMORY); +#ifdef HAVE_SOCKADDR_SA_LEN + sinptr->sin_len = sizeof (struct sockaddr_in); +#endif + sinptr->sin_family = AF_INET; + memcpy (&sinptr->sin_addr, addr, sizeof (struct in_addr)); + ai->ai_addr = (struct sockaddr *) sinptr; + ai->ai_addrlen = sizeof (struct sockaddr_in); + break; + } +#endif /* IPV4 */ +#ifdef IPv6 + case AF_INET6: + { + struct sockaddr_in6 *sin6ptr; - __small_sprintf (temp, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); - if (strlen (temp) >= (size_t) len) + /* 4allocate sockaddr_in6{} and fill in all but port */ + if ((sin6ptr = calloc (1, sizeof (struct sockaddr_in6))) == NULL) + return (EAI_MEMORY); +#ifdef HAVE_SOCKADDR_SA_LEN + sin6ptr->sin6_len = sizeof (struct sockaddr_in6); +#endif + sin6ptr->sin6_family = AF_INET6; + memcpy (&sin6ptr->sin6_addr, addr, sizeof (struct in6_addr)); + ai->ai_addr = (struct sockaddr *) sin6ptr; + ai->ai_addrlen = sizeof (struct sockaddr_in6); + break; + } +#endif /* IPV6 */ +#ifdef UNIXdomain + case AF_LOCAL: + { + struct sockaddr_un *unp; + + /* 4allocate sockaddr_un{} and fill in */ +/* *INDENT-OFF* */ + if (strlen(addr) >= sizeof(unp->sun_path)) + return(EAI_SERVICE); + if ( (unp = calloc(1, sizeof(struct sockaddr_un))) == NULL) + return(EAI_MEMORY); +/* *INDENT-ON* */ + unp->sun_family = AF_LOCAL; + strcpy (unp->sun_path, addr); +#ifdef HAVE_SOCKADDR_SA_LEN + unp->sun_len = SUN_LEN (unp); +#endif + ai->ai_addr = (struct sockaddr *) unp; + ai->ai_addrlen = sizeof (struct sockaddr_un); + if (hintsp->ai_flags & AI_PASSIVE) + unlink (unp->sun_path); /* OK if this fails */ + break; + } +#endif /* UNIXDOMAIN */ + } + return (0); +} + +/* end ga_aistruct2 */ + +/* + * Clone a new addrinfo structure from an existing one. + */ + +/* include ga_clone */ +static struct addrinfo * +ga_clone (struct addrinfo *ai) +{ + struct addrinfo *nai; + + if ((nai = (struct addrinfo *) calloc (1, sizeof (struct addrinfo))) == NULL) + return (NULL); + + nai->ai_next = ai->ai_next; + ai->ai_next = nai; + + nai->ai_flags = 0; /* make sure AI_CLONE is off */ + nai->ai_family = ai->ai_family; + nai->ai_socktype = ai->ai_socktype; + nai->ai_protocol = ai->ai_protocol; + nai->ai_canonname = NULL; + nai->ai_addrlen = ai->ai_addrlen; + if ((nai->ai_addr = (struct sockaddr *) malloc (ai->ai_addrlen)) == NULL) + return (NULL); + memcpy (nai->ai_addr, ai->ai_addr, ai->ai_addrlen); + + return (nai); +} + +/* end ga_clone */ + +/* + * Basic error checking of flags, family, socket type, and protocol. + */ + +/* include ga_echeck */ +static int +ga_echeck (const char *hostname, const char *servname, + int flags, int family, int socktype, int protocol) +{ + if (flags & ~(AI_PASSIVE | AI_CANONNAME)) + return (EAI_BADFLAGS); /* unknown flag bits */ + + if (hostname == NULL || hostname[0] == '\0') + { + if (servname == NULL || servname[0] == '\0') + return (EAI_NONAME); /* host or service must be specified */ + } + + switch (family) + { + case AF_UNSPEC: + break; +#ifdef IPv4 + case AF_INET: + if (socktype != 0 && + (socktype != SOCK_STREAM && + socktype != SOCK_DGRAM && socktype != SOCK_RAW)) + return (EAI_SOCKTYPE); /* invalid socket type */ + break; +#endif +#ifdef IPv6 + case AF_INET6: + if (socktype != 0 && + (socktype != SOCK_STREAM && + socktype != SOCK_DGRAM && socktype != SOCK_RAW)) + return (EAI_SOCKTYPE); /* invalid socket type */ + break; +#endif +#ifdef UNIXdomain + case AF_LOCAL: + if (socktype != 0 && + (socktype != SOCK_STREAM && socktype != SOCK_DGRAM)) + return (EAI_SOCKTYPE); /* invalid socket type */ + break; +#endif + default: + return (EAI_FAMILY); /* unknown protocol family */ + } + return (0); +} + +/* end ga_echeck */ + +struct search { + const char *host; /* hostname or address string */ + int family; /* AF_xxx */ +}; + +/* + * Set up the search[] array with the hostnames and address families + * that we are to look up. + */ + +/* include ga_nsearch1 */ +static int +ga_nsearch (const char *hostname, const struct addrinfo *hintsp, + struct search *search) +{ + int nsearch = 0; + + if (hostname == NULL || hostname[0] == '\0') + { + if (hintsp->ai_flags & AI_PASSIVE) { - set_errno (ENOSPC); - return NULL; + /* 4no hostname and AI_PASSIVE: implies wildcard bind */ + switch (hintsp->ai_family) + { +#ifdef IPv4 + case AF_INET: + search[nsearch].host = "0.0.0.0"; + search[nsearch].family = AF_INET; + nsearch++; + break; +#endif +#ifdef IPv6 + case AF_INET6: + search[nsearch].host = "0::0"; + search[nsearch].family = AF_INET6; + nsearch++; + break; +#endif + case AF_UNSPEC: +#ifdef IPv6 + search[nsearch].host = "0::0"; /* IPv6 first, then IPv4 */ + search[nsearch].family = AF_INET6; + nsearch++; +#endif +#ifdef IPv4 + search[nsearch].host = "0.0.0.0"; + search[nsearch].family = AF_INET; + nsearch++; +#endif + break; + } +/* end ga_nsearch1 */ +/* include ga_nsearch2 */ + } + else + { + /* 4no host and not AI_PASSIVE: connect to local host */ + switch (hintsp->ai_family) + { +#ifdef IPv4 + case AF_INET: + search[nsearch].host = "localhost"; /* 127.0.0.1 */ + search[nsearch].family = AF_INET; + nsearch++; + break; +#endif +#ifdef IPv6 + case AF_INET6: + search[nsearch].host = "0::1"; + search[nsearch].family = AF_INET6; + nsearch++; + break; +#endif + case AF_UNSPEC: +#ifdef IPv6 + search[nsearch].host = "0::1"; /* IPv6 first, then IPv4 */ + search[nsearch].family = AF_INET6; + nsearch++; +#endif +#ifdef IPv4 + search[nsearch].host = "localhost"; + search[nsearch].family = AF_INET; + nsearch++; +#endif + break; + } } - strcpy (strptr, temp); - return strptr; +/* end ga_nsearch2 */ +/* include ga_nsearch3 */ } - set_errno (EAFNOSUPPORT); - return NULL; + else + { /* host is specified */ + switch (hintsp->ai_family) + { +#ifdef IPv4 + case AF_INET: + search[nsearch].host = hostname; + search[nsearch].family = AF_INET; + nsearch++; + break; +#endif +#ifdef IPv6 + case AF_INET6: + search[nsearch].host = hostname; + search[nsearch].family = AF_INET6; + nsearch++; + break; +#endif + case AF_UNSPEC: +#ifdef IPv6 + search[nsearch].host = hostname; + search[nsearch].family = AF_INET6; /* IPv6 first */ + nsearch++; +#endif +#ifdef IPv4 + search[nsearch].host = hostname; + search[nsearch].family = AF_INET; /* then IPv4 */ + nsearch++; +#endif + break; + } + } + if (nsearch < 1 || nsearch > 2) + return -1; + return (nsearch); +} + +/* end ga_nsearch3 */ + +/* + * Go through all the addrinfo structures, checking for a match of the + * socket type and filling in the socket type, and then the port number + * in the corresponding socket address structures. + * + * The AI_CLONE flag works as follows. Consider a multihomed host with + * two IP addresses and no socket type specified by the caller. After + * the "host" search there are two addrinfo structures, one per IP address. + * Assuming a service supported by both TCP and UDP (say the daytime + * service) we need to return *four* addrinfo structures: + * IP#1, SOCK_STREAM, TCP port, + * IP#1, SOCK_DGRAM, UDP port, + * IP#2, SOCK_STREAM, TCP port, + * IP#2, SOCK_DGRAM, UDP port. + * To do this, when the "host" loop creates an addrinfo structure, if the + * caller has not specified a socket type (hintsp->ai_socktype == 0), the + * AI_CLONE flag is set. When the following function finds an entry like + * this it is handled as follows: If the entry's ai_socktype is still 0, + * this is the first use of the structure, and the ai_socktype field is set. + * But, if the entry's ai_socktype is nonzero, then we clone a new addrinfo + * structure and set it's ai_socktype to the new value. Although we only + * need two socket types today (SOCK_STREAM and SOCK_DGRAM) this algorithm + * will handle any number. Also notice that Posix.1g requires all socket + * types to be nonzero. + */ + +/* include ga_port */ +static int +ga_port (struct addrinfo *aihead, int port, int socktype) + /* port must be in network byte order */ +{ + int nfound = 0; + struct addrinfo *ai; + + for (ai = aihead; ai != NULL; ai = ai->ai_next) + { + if (ai->ai_flags & AI_CLONE) + { + if (ai->ai_socktype != 0) + { + if ((ai = ga_clone (ai)) == NULL) + return (-1); /* memory allocation error */ + /* ai points to newly cloned entry, which is what we want */ + } + } + else if (ai->ai_socktype != socktype) + continue; /* ignore if mismatch on socket type */ + + ai->ai_socktype = socktype; + + switch (ai->ai_family) + { +#ifdef IPv4 + case AF_INET: + ((struct sockaddr_in *) ai->ai_addr)->sin_port = port; + nfound++; + break; +#endif +#ifdef IPv6 + case AF_INET6: + ((struct sockaddr_in6 *) ai->ai_addr)->sin6_port = port; + nfound++; + break; +#endif + } + } + return (nfound); +} + +/* end ga_port */ + +/* + * This function handles the service string. + */ + +/* include ga_serv */ +static int +ga_serv (struct addrinfo *aihead, const struct addrinfo *hintsp, + const char *serv) +{ + int port, rc, nfound; + struct servent *sptr; + + nfound = 0; + if (isdigit (serv[0])) + { /* check for port number string first */ + port = htons (atoi (serv)); + if (hintsp->ai_socktype) + { + /* 4caller specifies socket type */ + if ((rc = ga_port (aihead, port, hintsp->ai_socktype)) < 0) + return (EAI_MEMORY); + nfound += rc; + } + else + { + /* 4caller does not specify socket type */ + if ((rc = ga_port (aihead, port, SOCK_STREAM)) < 0) + return (EAI_MEMORY); + nfound += rc; + if ((rc = ga_port (aihead, port, SOCK_DGRAM)) < 0) + return (EAI_MEMORY); + nfound += rc; + } + } + else + { + /* 4try service name, TCP then UDP */ + if (hintsp->ai_socktype == 0 || hintsp->ai_socktype == SOCK_STREAM) + { + if ((sptr = cygwin_getservbyname (serv, "tcp")) != NULL) + { + if ((rc = ga_port (aihead, sptr->s_port, SOCK_STREAM)) < 0) + return (EAI_MEMORY); + nfound += rc; + } + } + if (hintsp->ai_socktype == 0 || hintsp->ai_socktype == SOCK_DGRAM) + { + if ((sptr = cygwin_getservbyname (serv, "udp")) != NULL) + { + if ((rc = ga_port (aihead, sptr->s_port, SOCK_DGRAM)) < 0) + return (EAI_MEMORY); + nfound += rc; + } + } + } + + if (nfound == 0) + { + if (hintsp->ai_socktype == 0) + return (EAI_NONAME); /* all calls to getservbyname() failed */ + else + return (EAI_SERVICE); /* service not supported for socket type */ + } + return (0); +} + +/* end ga_serv */ + +#ifdef UNIXdomain +/* include ga_unix */ +static int +ga_unix (const char *path, struct addrinfo *hintsp, struct addrinfo **result) +{ + int rc; + struct addrinfo *aihead, **aipnext; + + aihead = NULL; + aipnext = &aihead; + + if (hintsp->ai_family != AF_UNSPEC && hintsp->ai_family != AF_LOCAL) + return (EAI_ADDRFAMILY); + + if (hintsp->ai_socktype == 0) + { + /* 4no socket type specified: return stream then dgram */ + hintsp->ai_socktype = SOCK_STREAM; + if ((rc = ga_aistruct (&aipnext, hintsp, path, AF_LOCAL)) != 0) + return (rc); + hintsp->ai_socktype = SOCK_DGRAM; + } + + if ((rc = ga_aistruct (&aipnext, hintsp, path, AF_LOCAL)) != 0) + return (rc); + + if (hintsp->ai_flags & AI_CANONNAME) + { + struct utsname myname; + + if (uname (&myname) < 0) + return (EAI_SYSTEM); + if ((aihead->ai_canonname = strdup (myname.nodename)) == NULL) + return (EAI_MEMORY); + } + + *result = aihead; /* pointer to first structure in linked list */ + return (0); +} + +/* end ga_unix */ +#endif /* UNIXdomain */ + +/* include gn_ipv46 */ +static int +gn_ipv46 (char *host, size_t hostlen, char *serv, size_t servlen, + void *aptr, size_t alen, int family, int port, int flags) +{ + char *ptr; + struct hostent *hptr; + struct servent *sptr; + + if (host && hostlen > 0) + { + if (flags & NI_NUMERICHOST) + { + if (cygwin_inet_ntop (family, aptr, host, hostlen) == NULL) + return (1); + } + else + { + hptr = cygwin_gethostbyaddr ((const char *) aptr, alen, family); + if (hptr != NULL && hptr->h_name != NULL) + { + if (flags & NI_NOFQDN) + { + if ((ptr = strchr (hptr->h_name, '.')) != NULL) + *ptr = 0; /* overwrite first dot */ + } + //snprintf (host, hostlen, "%s", hptr->h_name); + *host = '\0'; + strncat (host, hptr->h_name, hostlen - 1); + } + else + { + if (flags & NI_NAMEREQD) + return (1); + if (cygwin_inet_ntop (family, aptr, host, hostlen) == NULL) + return (1); + } + } + } + + if (serv && servlen > 0) + { + if (flags & NI_NUMERICSERV) + { + //snprintf (serv, servlen, "%d", ntohs (port)); + char buf[32]; + __small_sprintf (buf, "%d", ntohs (port)); + *serv = '\0'; + strncat (serv, buf, servlen - 1); + } + else + { + sptr = cygwin_getservbyport (port, (flags & NI_DGRAM) ? "udp" : NULL); + if (sptr != NULL && sptr->s_name != NULL) + { + //snprintf (serv, servlen, "%s", sptr->s_name); + *serv = '\0'; + strncat (serv, sptr->s_name, servlen - 1); + } + else + { + //snprintf (serv, servlen, "%d", ntohs (port)); + char buf[32]; + __small_sprintf (buf, "%d", ntohs (port)); + *serv = '\0'; + strncat (serv, buf, servlen - 1); + } + } + } + return (0); +} + +/* end gn_ipv46 */ + +/* include freeaddrinfo */ +void +ipv4_freeaddrinfo (struct addrinfo *aihead) +{ + struct addrinfo *ai, *ainext; + + for (ai = aihead; 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 */ + } +} + +/* end freeaddrinfo */ + +/* include ga1 */ + +int +ipv4_getaddrinfo (const char *hostname, const char *servname, + const struct addrinfo *hintsp, struct addrinfo **result) +{ + int rc, error, nsearch; + char **ap, *canon; + struct hostent *hptr; + struct search search[3], *sptr; + struct addrinfo hints, *aihead, **aipnext; + + /* + * If we encounter an error we want to free() any dynamic memory + * that we've allocated. This is our hack to simplify the code. + */ +#define error(e) { error = (e); goto bad; } + + aihead = NULL; /* initialize automatic variables */ + aipnext = &aihead; + canon = NULL; + + if (hintsp == NULL) + { + bzero (&hints, sizeof (hints)); + hints.ai_family = AF_UNSPEC; + } + else + hints = *hintsp; /* struct copy */ + + /* 4first some basic error checking */ + if ((rc = ga_echeck (hostname, servname, hints.ai_flags, hints.ai_family, + hints.ai_socktype, hints.ai_protocol)) != 0) + error (rc); + +#ifdef UNIXdomain + /* 4special case Unix domain first */ + if (hostname != NULL && + (strcmp (hostname, "/local") == 0 || strcmp (hostname, "/unix") == 0) && + (servname != NULL && servname[0] == '/')) + return (ga_unix (servname, &hints, result)); +#endif +/* end ga1 */ + +/* include ga3 */ + /* 4remainder of function for IPv4/IPv6 */ + nsearch = ga_nsearch (hostname, &hints, &search[0]); + if (nsearch == -1) + error (EAI_FAMILY); + for (sptr = &search[0]; sptr < &search[nsearch]; sptr++) + { +#ifdef IPv4 + /* 4check for an IPv4 dotted-decimal string */ + if (isdigit (sptr->host[0])) + { + struct in_addr inaddr; + + if (inet_pton4 (sptr->host, (u_char *) &inaddr) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET) + error (EAI_ADDRFAMILY); + if (sptr->family != AF_INET) + continue; /* ignore */ + rc = ga_aistruct (&aipnext, &hints, &inaddr, AF_INET); + if (rc != 0) + error (rc); + continue; + } + } +#endif + +#ifdef IPv6 + /* 4check for an IPv6 hex string */ + if ((isxdigit (sptr->host[0]) || sptr->host[0] == ':') && + (strchr (sptr->host, ':') != NULL)) + { + struct in6_addr in6addr; + + if (inet_pton6 (sptr->host, &in6addr) == 1) + { + if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET6) + error (EAI_ADDRFAMILY); + if (sptr->family != AF_INET6) + continue; /* ignore */ + rc = ga_aistruct (&aipnext, &hints, &in6addr, AF_INET6); + if (rc != 0) + error (rc); + continue; + } + } +#endif +/* end ga3 */ +/* include ga4 */ +#ifdef IPv6 + /* 4remainder of for() to look up hostname */ + if ((_res.options & RES_INIT) == 0) + res_init (); /* need this to set _res.options */ +#endif + + if (nsearch == 2) + { +#ifdef IPv6 + _res.options &= ~RES_USE_INET6; +#endif + hptr = gethostbyname2 (sptr->host, sptr->family); + } + else + { +#ifdef IPv6 + if (sptr->family == AF_INET6) + _res.options |= RES_USE_INET6; + else + _res.options &= ~RES_USE_INET6; +#endif + hptr = gethostbyname (sptr->host); + } + if (hptr == NULL) + { + if (nsearch == 2) + continue; /* failure OK if multiple searches */ + + switch (h_errno) + { + case HOST_NOT_FOUND: + error (EAI_NONAME); + case TRY_AGAIN: + error (EAI_AGAIN); + case NO_RECOVERY: + error (EAI_FAIL); + case NO_DATA: + error (EAI_NODATA); + default: + error (EAI_NONAME); + } + } + + /* 4check for address family mismatch if one specified */ + if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype) + error (EAI_ADDRFAMILY); + + /* 4save canonical name first time */ + if (hostname != NULL && hostname[0] != '\0' && + (hints.ai_flags & AI_CANONNAME) && canon == NULL) + { + if ((canon = strdup (hptr->h_name)) == NULL) + error (EAI_MEMORY); + } + + /* 4create one addrinfo{} for each returned address */ + for (ap = hptr->h_addr_list; *ap != NULL; ap++) + { + rc = ga_aistruct (&aipnext, &hints, *ap, hptr->h_addrtype); + if (rc != 0) + error (rc); + } + } + if (aihead == NULL) + error (EAI_NONAME); /* nothing found */ +/* end ga4 */ + +/* include ga5 */ + /* 4return canonical name */ + if (hostname != NULL && hostname[0] != '\0' && + hints.ai_flags & AI_CANONNAME) + { + if (canon != NULL) + aihead->ai_canonname = canon; /* strdup'ed earlier */ + else + { + if ((aihead->ai_canonname = strdup (search[0].host)) == NULL) + error (EAI_MEMORY); + } + } + + /* 4now process the service name */ + if (servname != NULL && servname[0] != '\0') + { + if ((rc = ga_serv (aihead, &hints, servname)) != 0) + error (rc); + } + + *result = aihead; /* pointer to first structure in linked list */ + return (0); + +bad: + ipv4_freeaddrinfo (aihead); /* free any alloc'ed memory */ + return (error); } + +/* end ga5 */ + +/* include getnameinfo */ +int +ipv4_getnameinfo (const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int flags) +{ + + switch (sa->sa_family) + { +#ifdef IPv4 + case AF_INET: + { + struct sockaddr_in *sain = (struct sockaddr_in *) sa; + + return (gn_ipv46 (host, hostlen, serv, servlen, + &sain->sin_addr, sizeof (struct in_addr), + AF_INET, sain->sin_port, flags)); + } +#endif + +#ifdef IPv6 + case AF_INET6: + { + struct sockaddr_in6 *sain = (struct sockaddr_in6 *) sa; + + return (gn_ipv46 (host, hostlen, serv, servlen, + &sain->sin6_addr, sizeof (struct in6_addr), + AF_INET6, sain->sin6_port, flags)); + } +#endif + +#ifdef UNIXdomain + case AF_LOCAL: + { + struct sockaddr_un *un = (struct sockaddr_un *) sa; + + if (hostlen > 0) + snprintf (host, hostlen, "%s", "/local"); + if (servlen > 0) + snprintf (serv, servlen, "%s", un->sun_path); + return (0); + } +#endif + + default: + return (1); + } +} + +/* end getnameinfo */ + +/* Start of cygwin specific wrappers around the gai functions. */ + +struct gai_errmap_t +{ + int w32_errval; + const char *errtxt; +}; + +static gai_errmap_t gai_errmap[] = +{ + {0, "Success"}, + {0, "Address family for hostname not supported"}, + {WSATRY_AGAIN, "Temporary failure in name resolution"}, + {WSAEINVAL, "Invalid value for ai_flags"}, + {WSANO_RECOVERY, "Non-recoverable failure in name resolution"}, + {WSAEAFNOSUPPORT, "ai_family not supported"}, + {WSA_NOT_ENOUGH_MEMORY, "Memory allocation failure"}, + {WSANO_DATA, "No address associated with hostname"}, + {WSAHOST_NOT_FOUND, "hostname nor servname provided, or not known"}, + {WSATYPE_NOT_FOUND, "servname not supported for ai_socktype"}, + {WSAESOCKTNOSUPPORT, "ai_socktype not supported"}, + {0, "System error returned in errno"}, + {0, "Invalid value for hints"}, + {0, "Resolved protocol is unknown"} +}; + +extern "C" const char * +cygwin_gai_strerror (int err) +{ + if (err >= 0 && err < EAI_MAX) + return gai_errmap[err].errtxt; + return "Unknown error"; +} + +static int +w32_to_gai_err (int w32_err) +{ + if (w32_err >= WSABASEERR) + for (int i = 0; i < EAI_MAX; ++i) + if (gai_errmap[i].w32_errval == w32_err) + return i; + return w32_err; +} + +/* We can't use autoload here because we don't know where the functions + are loaded from. On Win2K, the functions are available in the + ipv6 technology preview lib called wship6.dll, in XP and above they + are implemented in ws2_32.dll. For older systems we use the ipv4-only + version above. */ + +static void (WINAPI *freeaddrinfo)(const struct addrinfo *); +static int (WINAPI *getaddrinfo)(const char *, const char *, + const struct addrinfo *, + struct addrinfo **); +static int (WINAPI *getnameinfo)(const struct sockaddr *, socklen_t, + char *, size_t, char *, size_t, int); +static bool +get_ipv6_funcs (HMODULE lib) +{ + return ((freeaddrinfo = (void (WINAPI *)(const struct addrinfo *)) + GetProcAddress(lib, "freeaddrinfo")) + && (getaddrinfo = (int (WINAPI *)(const char *, const char *, + const struct addrinfo *, + struct addrinfo **)) + GetProcAddress(lib, "getaddrinfo")) + && (getnameinfo = (int (WINAPI *)(const struct sockaddr *, + socklen_t, char *, size_t, + char *, size_t, int)) + GetProcAddress(lib, "getnameinfo"))); +} + +static NO_COPY muto load_ipv6_guard; +static bool ipv6_inited = false; +#define load_ipv6() if (!ipv6_inited) load_ipv6_funcs (); + +static void +load_ipv6_funcs () +{ + + char lib_name[CYG_MAX_PATH]; + size_t len; + HMODULE lib; + + load_ipv6_guard.init ("klog_guard")->acquire (); + if (ipv6_inited) + goto out; + WSAGetLastError (); /* Kludge. Enforce WSAStartup call. */ + if (GetSystemDirectory (lib_name, CYG_MAX_PATH)) + { + len = strlen (lib_name); + strcpy (lib_name + len, "\\ws2_32.dll"); + if ((lib = LoadLibrary (lib_name))) + { + if (get_ipv6_funcs (lib)) + goto out; + FreeLibrary (lib); + } + strcpy (lib_name + len, "\\wship6.dll"); + if ((lib = LoadLibrary (lib_name))) + { + if (get_ipv6_funcs (lib)) + goto out; + FreeLibrary (lib); + } + freeaddrinfo = NULL; + getaddrinfo = NULL; + getnameinfo = NULL; + } +out: + ipv6_inited = true; + load_ipv6_guard.release (); +} + +extern "C" void +cygwin_freeaddrinfo (struct addrinfo *addr) +{ + myfault efault; + if (efault.faulted (EFAULT)) + return; + load_ipv6 (); + if (freeaddrinfo) + freeaddrinfo (addr); + else + ipv4_freeaddrinfo (addr); +} + +extern "C" int +cygwin_getaddrinfo (const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + myfault efault; + if (efault.faulted (EFAULT)) + return EAI_SYSTEM; + load_ipv6 (); + if (getaddrinfo) + return w32_to_gai_err (getaddrinfo (hostname, servname, hints, res)); + return ipv4_getaddrinfo (hostname, servname, hints, res); +} + +extern "C" int +cygwin_getnameinfo (const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, char *serv, + size_t servlen, int flags) +{ + myfault efault; + if (efault.faulted (EFAULT)) + return EAI_SYSTEM; + load_ipv6 (); + if (getnameinfo) + { + /* When the incoming port number is set to 0, Winsock's getnameinfo + returns with error WSAENO_DATA instead of simply ignoring the port. + To avoid this strange behaviour, we check manually, if the port number + is 0. If so, set the NI_NUMERICSERV flag to avoid this problem. */ + switch (sa->sa_family) + { + case AF_INET: + if (((struct sockaddr_in *) sa)->sin_port == 0) + flags |= NI_NUMERICSERV; + break; + case AF_INET6: + if (((struct sockaddr_in6 *) sa)->sin6_port == 0) + flags |= NI_NUMERICSERV; + break; + } + int ret = w32_to_gai_err (getnameinfo (sa, salen, host, hostlen, serv, + servlen, flags)); + if (ret) + set_winsock_errno (); + return ret; + } + return ipv4_getnameinfo (sa, salen, host, hostlen, serv, servlen, flags); +} + |