OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / ada / socket.c
index 6f5067f..ee1f760 100644 (file)
  *                                                                          *
  *                         GNAT COMPILER COMPONENTS                         *
  *                                                                          *
- *                              S O C K E T                                 *
+ *                               S O C K E T                                *
  *                                                                          *
  *                          C Implementation File                           *
  *                                                                          *
- *            Copyright (C) 2003, Free Software Foundation, Inc.            *
+ *          Copyright (C) 2003-2010, Free Software Foundation, Inc.         *
  *                                                                          *
  * GNAT is free software;  you can  redistribute it  and/or modify it under *
  * terms of the  GNU General Public License as published  by the Free Soft- *
- * ware  Foundation;  either version 2,  or (at your option) any later ver- *
+ * ware  Foundation;  either version 3,  or (at your option) any later ver- *
  * sion.  GNAT is distributed in the hope that it will be useful, but WITH- *
  * OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY *
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License *
- * for  more details.  You should have  received  a copy of the GNU General *
- * Public License  distributed with GNAT;  see file COPYING.  If not, write *
- * to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, *
- * MA 02111-1307, USA.                                                      *
+ * or FITNESS FOR A PARTICULAR PURPOSE.                                     *
  *                                                                          *
- * As a  special  exception,  if you  link  this file  with other  files to *
- * produce an executable,  this file does not by itself cause the resulting *
- * executable to be covered by the GNU General Public License. This except- *
- * ion does not  however invalidate  any other reasons  why the  executable *
- * file might be covered by the  GNU Public License.                        *
+ * As a special exception under Section 7 of GPL version 3, you are granted *
+ * additional permissions described in the GCC Runtime Library Exception,   *
+ * version 3.1, as published by the Free Software Foundation.               *
+ *                                                                          *
+ * You should have received a copy of the GNU General Public License and    *
+ * a copy of the GCC Runtime Library Exception along with this program;     *
+ * see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    *
+ * <http://www.gnu.org/licenses/>.                                          *
  *                                                                          *
  * GNAT was originally developed  by the GNAT team at  New York University. *
  * Extensive contributions were provided by Ada Core Technologies Inc.      *
  *                                                                          *
  ****************************************************************************/
 
-/*  This file provides a portable binding to the fd set functions           */
+/*  This file provides a portable binding to the sockets API                */
 
-#ifdef __vxworks
-#include "vxWorks.h"
-#endif
+#include "gsocket.h"
 
-#ifdef IN_RTS
-#include "tconfig.h"
-#include "tsystem.h"
+#ifdef VMS
+/*
+ * For VMS, gsocket.h can't include sockets-related DEC C header files
+ * when building the runtime (because these files are in a DEC C text library
+ * (DECC$RTLDEF.TLB) not accessible to GCC). So, we generate a separate header
+ * file along with s-oscons.ads and include it here.
+ */
+# include "s-oscons.h"
 
-#if defined (WINNT)
-#define FD_SETSIZE 1024
-#include <windows.h>
+/*
+ * We also need the declaration of struct hostent/servent, which s-oscons
+ * can't provide, so we copy it manually here. This needs to be kept in synch
+ * with the definition of that structure in the DEC C headers, which
+ * hopefully won't change frequently.
+ */
+typedef char *__netdb_char_ptr __attribute__ (( mode (SI) ));
+typedef __netdb_char_ptr *__netdb_char_ptr_ptr __attribute__ (( mode (SI) ));
 
-#ifdef __MINGW32__
-#include "mingw32.h"
-#if STD_MINGW
-#include <winsock.h>
-#else
-#include <windows32/sockets.h>
-#endif
-#endif
-#endif
+struct hostent {
+  __netdb_char_ptr     h_name;
+  __netdb_char_ptr_ptr h_aliases;
+  int                  h_addrtype;
+  int                  h_length;
+  __netdb_char_ptr_ptr h_addr_list;
+};
 
-#if defined (VMS)
-#define FD_SETSIZE 4096
-#include <sys/time.h>
+struct servent {
+  __netdb_char_ptr     s_name;
+  __netdb_char_ptr_ptr s_aliases;
+  int                  s_port;
+  __netdb_char_ptr     s_proto;
+};
 #endif
 
-#else
-#include "config.h"
-#include "system.h"
-#endif
+#if defined(HAVE_SOCKETS)
 
-#if !(defined (VMS) || defined (__MINGW32__))
-# include <sys/socket.h>
+/* Include all the necessary system-specific headers and define the
+ * necessary macros (shared with gen-oscons).
+ */
+
+#if !defined(SO_NOSIGPIPE) && !defined (MSG_NOSIGNAL)
+#include <signal.h>
 #endif
+/* Required if we will be calling signal() in __gnat_disable_all_sigpipes() */
 
 #include "raise.h"
+/* Required for __gnat_malloc() */
+
+#include <string.h>
+/* Required for memcpy() */
 
 extern void __gnat_disable_sigpipe (int fd);
-extern void __gnat_free_socket_set (fd_set *);
+extern void __gnat_disable_all_sigpipes (void);
+extern int  __gnat_create_signalling_fds (int *fds);
+extern int  __gnat_read_signalling_fd (int rsig);
+extern int  __gnat_write_signalling_fd (int wsig);
+extern void  __gnat_close_signalling_fd (int sig);
 extern void __gnat_last_socket_in_set (fd_set *, int *);
 extern void __gnat_get_socket_from_set (fd_set *, int *, int *);
 extern void __gnat_insert_socket_in_set (fd_set *, int);
 extern int __gnat_is_socket_in_set (fd_set *, int);
 extern fd_set *__gnat_new_socket_set (fd_set *);
 extern void __gnat_remove_socket_from_set (fd_set *, int);
+extern void __gnat_reset_socket_set (fd_set *);
+extern int  __gnat_get_h_errno (void);
+extern int  __gnat_socket_ioctl (int, int, int *);
+
+extern char * __gnat_servent_s_name (struct servent *);
+extern char * __gnat_servent_s_alias (struct servent *, int index);
+extern unsigned short __gnat_servent_s_port (struct servent *);
+extern char * __gnat_servent_s_proto (struct servent *);
+
+extern char * __gnat_hostent_h_name (struct hostent *);
+extern char * __gnat_hostent_h_alias (struct hostent *, int);
+extern int __gnat_hostent_h_addrtype (struct hostent *);
+extern int __gnat_hostent_h_length (struct hostent *);
+extern char * __gnat_hostent_h_addr (struct hostent *, int);
+
+#ifndef HAVE_INET_PTON
+extern int  __gnat_inet_pton (int, const char *, void *);
+#endif
 \f
 /* Disable the sending of SIGPIPE for writes on a broken stream */
+
 void
 __gnat_disable_sigpipe (int fd)
 {
@@ -89,14 +126,271 @@ __gnat_disable_sigpipe (int fd)
 #endif
 }
 
-/* Free socket set. */
-
 void
-__gnat_free_socket_set (fd_set *set)
+__gnat_disable_all_sigpipes (void)
+{
+#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL) && defined(SIGPIPE)
+  (void) signal (SIGPIPE, SIG_IGN);
+#endif
+}
+\f
+#if defined (_WIN32) || defined (__vxworks) || defined (VMS)
+/*
+ * Signalling FDs operations are implemented in Ada for these platforms
+ * (see subunit GNAT.Sockets.Thin.Signalling_Fds).
+ */
+#else
+/*
+ * Create a pair of connected file descriptors fds[0] and fds[1] used for
+ * signalling by a Selector object. fds[0] is the read end, and fds[1] the
+ * write end.
+ */
+int
+__gnat_create_signalling_fds (int *fds) {
+  return pipe (fds);
+}
+\f
+/*
+ * Read one byte of data from rsig, the read end of a pair of signalling fds
+ * created by __gnat_create_signalling_fds.
+ */
+int
+__gnat_read_signalling_fd (int rsig) {
+  char c;
+  return read (rsig, &c, 1);
+}
+\f
+/*
+ * Write one byte of data to wsig, the write end of a pair of signalling fds
+ * created by __gnat_create_signalling_fds.
+ */
+int
+__gnat_write_signalling_fd (int wsig) {
+  char c = 0;
+  return write (wsig, &c, 1);
+}
+\f
+/*
+ * Close one end of a pair of signalling fds
+ */
+void
+__gnat_close_signalling_fd (int sig) {
+  (void) close (sig);
+}
+#endif
+\f
+/*
+ * Handling of gethostbyname, gethostbyaddr, getservbyname and getservbyport
+ * =========================================================================
+ *
+ * This module exposes __gnat_getXXXbyYYY operations with the same signature
+ * as the reentrant variant getXXXbyYYY_r.
+ *
+ * On platforms where getXXXbyYYY is intrinsically reentrant, the provided user
+ * buffer argument is ignored.
+ *
+ * When getXXXbyYYY is not reentrant but getXXXbyYYY_r exists, the latter is
+ * used, and the provided buffer argument must point to a valid, thread-local
+ * buffer (usually on the caller's stack).
+ *
+ * When getXXXbyYYY is not reentrant and no reentrant getXXXbyYYY_r variant
+ * is available, the non-reentrant getXXXbyYYY is called, the provided user
+ * buffer is ignored, and the caller is expected to take care of mutual
+ * exclusion.
+ */
+
+#ifdef HAVE_GETxxxBYyyy_R
+int
+__gnat_gethostbyname (const char *name,
+  struct hostent *ret, char *buf, size_t buflen,
+  int *h_errnop)
+{
+  struct hostent *rh;
+  int ri;
+
+#if defined(__linux__) || defined(__GLIBC__)
+  (void) gethostbyname_r (name, ret, buf, buflen, &rh, h_errnop);
+#else
+  rh = gethostbyname_r (name, ret, buf, buflen, h_errnop);
+#endif
+  ri = (rh == NULL) ? -1 : 0;
+  return ri;
+}
+
+int
+__gnat_gethostbyaddr (const char *addr, int len, int type,
+  struct hostent *ret, char *buf, size_t buflen,
+  int *h_errnop)
+{
+  struct hostent *rh;
+  int ri;
+
+#if defined(__linux__) || defined(__GLIBC__)
+  (void) gethostbyaddr_r (addr, len, type, ret, buf, buflen, &rh, h_errnop);
+#else
+  rh = gethostbyaddr_r (addr, len, type, ret, buf, buflen, h_errnop);
+#endif
+  ri = (rh == NULL) ? -1 : 0;
+  return ri;
+}
+
+int
+__gnat_getservbyname (const char *name, const char *proto,
+  struct servent *ret, char *buf, size_t buflen)
+{
+  struct servent *rh;
+  int ri;
+
+#if defined(__linux__) || defined(__GLIBC__) || defined(__rtems__)
+  (void) getservbyname_r (name, proto, ret, buf, buflen, &rh);
+#else
+  rh = getservbyname_r (name, proto, ret, buf, buflen);
+#endif
+  ri = (rh == NULL) ? -1 : 0;
+  return ri;
+}
+
+int
+__gnat_getservbyport (int port, const char *proto,
+  struct servent *ret, char *buf, size_t buflen)
+{
+  struct servent *rh;
+  int ri;
+
+#if defined(__linux__) || defined(__GLIBC__) || defined(__rtems__)
+  (void) getservbyport_r (port, proto, ret, buf, buflen, &rh);
+#else
+  rh = getservbyport_r (port, proto, ret, buf, buflen);
+#endif
+  ri = (rh == NULL) ? -1 : 0;
+  return ri;
+}
+#elif defined (__vxworks)
+static char vxw_h_name[MAXHOSTNAMELEN + 1];
+static char *vxw_h_aliases[1] = { NULL };
+static int vxw_h_addr;
+static char *vxw_h_addr_list[2] = { (char*) &vxw_h_addr, NULL };
+
+int
+__gnat_gethostbyname (const char *name,
+  struct hostent *ret, char *buf, size_t buflen,
+  int *h_errnop)
 {
-  __gnat_free (set);
+  vxw_h_addr = hostGetByName (name);
+  if (vxw_h_addr == ERROR) {
+    *h_errnop = __gnat_get_h_errno ();
+    return -1;
+  }
+  ret->h_name      = name;
+  ret->h_aliases   = &vxw_h_aliases;
+  ret->h_addrtype  = AF_INET;
+  ret->h_length    = 4;
+  ret->h_addr_list = &vxw_h_addr_list;
+  return 0;
 }
 
+int
+__gnat_gethostbyaddr (const char *addr, int len, int type,
+  struct hostent *ret, char *buf, size_t buflen,
+  int *h_errnop)
+{
+  if (type != AF_INET) {
+    *h_errnop = EAFNOSUPPORT;
+    return -1;
+  }
+
+  if (addr == NULL || len != 4) {
+    *h_errnop = EINVAL;
+    return -1;
+  }
+
+  if (hostGetByAddr (*(int*)addr, &vxw_h_name) != OK) {
+    *h_errnop = __gnat_get_h_errno ();
+    return -1;
+  }
+
+  vxw_h_addr       = addr;
+
+  ret->h_name      = &vxw_h_name;
+  ret->h_aliases   = &vxw_h_aliases;
+  ret->h_addrtype  = AF_INET;
+  ret->h_length    = 4;
+  ret->h_addr_list = &vxw_h_addr_list;
+}
+
+int
+__gnat_getservbyname (const char *name, const char *proto,
+  struct servent *ret, char *buf, size_t buflen)
+{
+  /* Not available under VxWorks */
+  return -1;
+}
+
+int
+__gnat_getservbyport (int port, const char *proto,
+  struct servent *ret, char *buf, size_t buflen)
+{
+  /* Not available under VxWorks */
+  return -1;
+}
+#else
+int
+__gnat_gethostbyname (const char *name,
+  struct hostent *ret, char *buf, size_t buflen,
+  int *h_errnop)
+{
+  struct hostent *rh;
+  rh = gethostbyname (name);
+  if (rh == NULL) {
+    *h_errnop = __gnat_get_h_errno ();
+    return -1;
+  }
+  *ret = *rh;
+  *h_errnop = 0;
+  return 0;
+}
+
+int
+__gnat_gethostbyaddr (const char *addr, int len, int type,
+  struct hostent *ret, char *buf, size_t buflen,
+  int *h_errnop)
+{
+  struct hostent *rh;
+  rh = gethostbyaddr (addr, len, type);
+  if (rh == NULL) {
+    *h_errnop = __gnat_get_h_errno ();
+    return -1;
+  }
+  *ret = *rh;
+  *h_errnop = 0;
+  return 0;
+}
+
+int
+__gnat_getservbyname (const char *name, const char *proto,
+  struct servent *ret, char *buf, size_t buflen)
+{
+  struct servent *rh;
+  rh = getservbyname (name, proto);
+  if (rh == NULL)
+    return -1;
+  *ret = *rh;
+  return 0;
+}
+
+int
+__gnat_getservbyport (int port, const char *proto,
+  struct servent *ret, char *buf, size_t buflen)
+{
+  struct servent *rh;
+  rh = getservbyport (port, proto);
+  if (rh == NULL)
+    return -1;
+  *ret = *rh;
+  return 0;
+}
+#endif
+\f
 /* Find the largest socket in the socket set SET. This is needed for
    `select'.  LAST is the maximum value for the largest socket. This hint is
    used to avoid scanning very large socket sets.  On return, LAST is the
@@ -109,7 +403,7 @@ __gnat_last_socket_in_set (fd_set *set, int *last)
   int l;
   l = -1;
 
-#ifdef WINNT
+#ifdef _WIN32
   /* More efficient method for NT. */
   for (s = 0; s < set->fd_count; s++)
     if ((int) set->fd_array[s] > l)
@@ -157,27 +451,240 @@ __gnat_is_socket_in_set (fd_set *set, int socket)
   return FD_ISSET (socket, set);
 }
 
-/* Allocate a new socket set and set it as empty.  */
+/* Remove SOCKET from the socket set SET. */
 
-fd_set *
-__gnat_new_socket_set (fd_set *set)
+void
+__gnat_remove_socket_from_set (fd_set *set, int socket)
 {
-  fd_set *new;
+  FD_CLR (socket, set);
+}
 
-  new = (fd_set *) __gnat_malloc (sizeof (fd_set));
+/* Reset SET */
+void
+__gnat_reset_socket_set (fd_set *set)
+{
+  FD_ZERO (set);
+}
 
-  if (set)
-    memcpy (new, set, sizeof (fd_set));
-  else
-    FD_ZERO (new);
+/* Get the value of the last host error */
 
-  return new;
+int
+__gnat_get_h_errno (void) {
+#ifdef __vxworks
+  int vxw_errno = errno;
+
+  switch (vxw_errno) {
+    case 0:
+      return 0;
+
+#ifdef S_hostLib_HOST_NOT_FOUND
+    case S_hostLib_HOST_NOT_FOUND:
+#endif
+    case S_hostLib_UNKNOWN_HOST:
+      return HOST_NOT_FOUND;
+
+#ifdef S_hostLib_TRY_AGAIN
+    case S_hostLib_TRY_AGAIN:
+      return TRY_AGAIN;
+#endif
+
+#ifdef S_hostLib_NO_RECOVERY
+    case S_hostLib_NO_RECOVERY:
+#endif
+#ifdef S_hostLib_NETDB_INTERNAL
+    case S_hostLib_NETDB_INTERNAL:
+#endif
+    case S_hostLib_INVALID_PARAMETER:
+      return NO_RECOVERY;
+
+    default:
+      return -1;
+  }
+
+#elif defined (VMS)
+  /* h_errno is defined as follows in OpenVMS' version of <netdb.h>.
+   * However this header file is not available when building the GNAT
+   * runtime library using GCC, so we are hardcoding the definition
+   * directly. Note that the returned address is thread-specific.
+   */
+  extern int *decc$h_errno_get_addr ();
+  return *decc$h_errno_get_addr ();
+
+#elif defined (__rtems__)
+  /* At this stage in the tool build, no networking .h files are available.
+   * Newlib does not provide networking .h files and RTEMS is not built yet.
+   * So we need to explicitly extern h_errno to access it.
+   */
+  extern int h_errno;
+  return h_errno;
+
+#else
+  return h_errno;
+#endif
 }
 
-/* Remove SOCKET from the socket set SET. */
+/* Wrapper for ioctl(2), which is a variadic function */
 
-void
-__gnat_remove_socket_from_set (fd_set *set, int socket)
+int
+__gnat_socket_ioctl (int fd, int req, int *arg) {
+#if defined (_WIN32)
+  return ioctlsocket (fd, req, arg);
+#elif defined (__APPLE__)
+  /*
+   * On Darwin, req is an unsigned long, and we want to convert without sign
+   * extension to get the proper bit pattern in the case of a 64 bit kernel.
+   */
+  return ioctl (fd, (unsigned int) req, arg);
+#else
+  return ioctl (fd, req, arg);
+#endif
+}
+
+#ifndef HAVE_INET_PTON
+
+#ifdef VMS
+# define in_addr_t int
+# define inet_addr decc$inet_addr
+#endif
+
+int
+__gnat_inet_pton (int af, const char *src, void *dst) {
+  switch (af) {
+#if defined (_WIN32) && defined (AF_INET6)
+    case AF_INET6:
+#endif
+    case AF_INET:
+      break;
+    default:
+      errno = EAFNOSUPPORT;
+      return -1;
+  }
+
+#if defined (__vxworks)
+  return (inet_aton (src, dst) == OK);
+
+#elif defined (_WIN32)
+  struct sockaddr_storage ss;
+  int sslen = sizeof ss;
+  int rc;
+
+  ss.ss_family = af;
+  rc = WSAStringToAddressA (src, af, NULL, (struct sockaddr *)&ss, &sslen);
+  if (rc == 0) {
+    switch (af) {
+      case AF_INET:
+        *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
+        break;
+#ifdef AF_INET6
+      case AF_INET6:
+        *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
+        break;
+#endif
+    }
+  }
+  return (rc == 0);
+
+#elif defined (__hpux__) || defined (VMS)
+  in_addr_t addr;
+  int rc = -1;
+
+  if (src == NULL || dst == NULL) {
+    errno = EINVAL;
+
+  } else if (!strcmp (src, "255.255.255.255")) {
+    addr = 0xffffffff;
+    rc = 1;
+
+  } else {
+    addr = inet_addr (src);
+    rc = (addr != 0xffffffff);
+  }
+  if (rc == 1) {
+    *(in_addr_t *)dst = addr;
+  }
+  return rc;
+#endif
+}
+#endif
+
+/*
+ * Accessor functions for struct hostent.
+ */
+
+char * __gnat_hostent_h_name (struct hostent * h) {
+  return h->h_name;
+}
+
+char * __gnat_hostent_h_alias (struct hostent * h, int index) {
+  return h->h_aliases[index];
+}
+
+int __gnat_hostent_h_addrtype (struct hostent * h) {
+  return h->h_addrtype;
+}
+
+int __gnat_hostent_h_length (struct hostent * h) {
+  return h->h_length;
+}
+
+char * __gnat_hostent_h_addr (struct hostent * h, int index) {
+  return h->h_addr_list[index];
+}
+
+/*
+ * Accessor functions for struct servent.
+ *
+ * These are needed because servent has different representations on different
+ * platforms, and we don't want to deal with that on the Ada side. For example,
+ * on Linux, we have (see /usr/include netdb.h):
+ *
+ *   struct servent
+ *   {
+ *     char *s_name;
+ *     char **s_aliases;
+ *     int s_port;
+ *     char *s_proto;
+ *   };
+ *
+ * and on Windows (see mingw's socket.h):
+ *
+ *   struct servent {
+ *     char *s_name;
+ *     char **s_aliases;
+ *   #ifdef _WIN64
+ *     char *s_proto;
+ *     short s_port;
+ *   #else
+ *     short s_port;
+ *     char *s_proto;
+ *   #endif
+ *   };
+ */
+
+char *
+__gnat_servent_s_name (struct servent * s)
 {
-  FD_CLR (socket, set);
+  return s->s_name;
 }
+
+char *
+__gnat_servent_s_alias (struct servent * s, int index)
+{
+  return s->s_aliases[index];
+}
+
+unsigned short
+__gnat_servent_s_port (struct servent * s)
+{
+  return s->s_port;
+}
+
+char *
+__gnat_servent_s_proto (struct servent * s)
+{
+  return s->s_proto;
+}
+
+#else
+# warning Sockets are not supported on this platform
+#endif /* defined(HAVE_SOCKETS) */