aboutsummaryrefslogtreecommitdiffstats
path: root/lib/erl_interface/src/connect/ei_resolve.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/erl_interface/src/connect/ei_resolve.c')
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c645
1 files changed, 645 insertions, 0 deletions
diff --git a/lib/erl_interface/src/connect/ei_resolve.c b/lib/erl_interface/src/connect/ei_resolve.c
new file mode 100644
index 0000000000..42aeab22b1
--- /dev/null
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -0,0 +1,645 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Interface functions to different versions of gethostbyname
+ */
+
+#ifdef VXWORKS
+#include <vxWorks.h>
+#include <stdio.h>
+#include <semLib.h>
+#include <hostLib.h>
+#include <resolvLib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <symLib.h>
+#include <sysSymTbl.h>
+
+#elif __WIN32__
+#include <winsock2.h>
+#include <windows.h>
+#include <winbase.h>
+
+#else /* unix of some kind */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/param.h>
+#endif
+
+/* common to all platforms */
+#include "eidef.h"
+#include "ei_resolve.h"
+#include "ei_locking.h"
+
+#ifdef HAVE_GETHOSTBYNAME_R
+
+void ei_init_resolve(void)
+{
+ return; /* Do nothing */
+}
+
+#else /* !HAVE_GETHOSTBYNAME_R */
+
+/* we have our own in that case */
+
+/* Make sure this semaphore has been initialized somewhere first. This
+ * should probably be done from 'erl'_init() but we do it in the first
+ * call to gethostbyname_r() or gethostbyaddr_r().
+ */
+/* FIXME we don't want globals here, but maybe ok? */
+#ifdef _REENTRANT
+static ei_mutex_t *ei_gethost_sem = NULL;
+#endif /* _REENTRANT */
+static int ei_resolve_initialized = 0;
+#ifndef __WIN32__
+int h_errno;
+#endif
+
+#ifdef DEBUG
+#define DEBUGF(X) fprintf X
+#else
+#define DEBUGF(X) /* Nothing */
+#endif
+
+#ifdef VXWORKS
+/* FIXME problem for threaded ? */
+static struct hostent *(*sens_gethostbyname)(const char *name,
+ char *, int) = NULL;
+static struct hostent *(*sens_gethostbyaddr)(const char *addr,
+ char *, int) = NULL;
+#endif
+
+#ifdef VXWORKS
+static int verify_dns_configuration(void);
+#endif
+
+/*
+ * If we find SENS resolver, use the functions found there, i.e.
+ * resolvGetHostByName() and resolvGetHostByAddr(). Otherwise we use
+ * our own, which are just wrappers around hostGetByName() and
+ * hostGetByAddr(). Here we look up the functions.
+ */
+void ei_init_resolve(void)
+{
+
+#ifdef VXWORKS
+ void *sym;
+ SYM_TYPE symtype;
+
+ if (symFindByName(sysSymTbl,"resolvGetHostByName",
+ (char **)&sym,&symtype) == OK &&
+ verify_dns_configuration()) {
+ sens_gethostbyname = sym;
+ DEBUGF((stderr,"found SENS resolver - using it for gethostbyname()\n"));
+ if (symFindByName(sysSymTbl,"resolvGetHostByAddr",
+ (char **)&sym,&symtype) == OK) {
+ sens_gethostbyaddr = sym;
+ DEBUGF((stderr,"found SENS resolver - "
+ "using it for gethostbyaddr()\n"));
+ }
+ else {
+ DEBUGF((stderr,"SENS resolver not found - "
+ "using default gethostbyaddr()\n"));
+ }
+ }
+ else {
+ DEBUGF((stderr,"SENS resolver not found - "
+ "using default gethostbyname()\n"));
+ }
+#endif /* VXWORKS */
+
+#ifdef _REENTRANT
+ ei_gethost_sem = ei_mutex_create();
+#endif /* _REENTRANT */
+
+ ei_resolve_initialized = 1;
+}
+
+#ifdef VXWORKS
+/*
+** Function to verify the DNS configuration on VwXorks SENS.
+** Actually configures to a default value if unconfigured...
+*/
+static int verify_dns_configuration(void)
+{
+ /* FIXME problem for threaded ? */
+ static char resolv_params[sizeof(RESOLV_PARAMS_S)];
+ void (*rpg)(char *);
+ STATUS (*rps)(char *);
+ SYM_TYPE dummy;
+ int get_result, set_result;
+
+ get_result = symFindByName(sysSymTbl,"resolvParamsGet", (char **) &rpg, &dummy);
+ set_result = symFindByName(sysSymTbl,"resolvParamsSet", (char **) &rps, &dummy);
+
+ if (!(get_result == OK &&
+ set_result == OK))
+ return -1;
+ (*rpg)(resolv_params);
+ if (*resolv_params == '\0') {
+ /* It exists, but is not configured, ei_connect would fail
+ if we left it this way... The best we can do is to configure
+ it to use the local host database on the card, as a fallback */
+ *resolv_params = (char) 1;
+ fprintf(stderr,"Trying to fix up DNS configuration.\n");
+ if (((*rps)(resolv_params)) != OK)
+ return -1;
+ }
+ return 0;
+}
+
+#endif
+
+/*
+ * Copy the contents of one struct hostent to another, i.e. don't just
+ * copy the pointers, copy all the data and create new pointers, etc.
+ * User must provide a secondary buffer to which the host data can be copied.
+ *
+ * Returns 0 on success or -1 if buffer is too small for host data
+*/
+
+/* a couple of helpers
+ * align: increment buf until it is dword-aligned, reduce len by same amount.
+ * advance: increment buf by n bytes, reduce len by same amount .
+ */
+#define align_buf(buf,len) for (;(((unsigned)buf)&0x3); (buf)++, len--)
+#define advance_buf(buf,len,n) ((buf)+=(n),(len)-=(n))
+
+/* "and now the tricky part..." */
+static int copy_hostent(struct hostent *dest, const struct hostent *src, char *buffer, int buflen)
+{
+ char **pptr;
+ int len;
+ char **src_aliases = NULL;
+ char **src_addr_list = NULL;
+
+ /* fix initial buffer alignment */
+ align_buf(buffer, buflen);
+
+ /* copy the data into our buffer... */
+ /* first the easy ones! */
+ dest->h_length = src->h_length;
+ dest->h_addrtype = src->h_addrtype;
+
+ /* h_name */
+ dest->h_name = buffer;
+ len = strlen(src->h_name);
+ if (buflen < len+1) return -1;
+ memmove((char *)dest->h_name,src->h_name,len);
+ buffer[len] = (char)0;
+ advance_buf(buffer,buflen,len+1);
+
+ /* traverse alias list, collecting the pointers */
+ align_buf(buffer, buflen);
+ pptr = (char **)buffer;
+ dest->h_aliases = pptr; /* save head of pointer array */
+
+ src_aliases = src->h_aliases;
+
+ while(*(src_aliases)) {
+ if (buflen < sizeof(*pptr)) return -1;
+ *pptr = src_aliases;
+ advance_buf(buffer,buflen,sizeof(*pptr));
+ src_aliases++;
+ pptr++;
+ }
+ if (buflen < sizeof(*pptr)) return -1;
+ *pptr = NULL;
+ advance_buf(buffer,buflen,sizeof(*pptr));
+
+ /* go back to saved position & transfer the alias data */
+ pptr = dest->h_aliases;
+ while (*pptr) {
+ len = strlen(*pptr);
+ if (buflen < len+1) return -1;
+ memmove(buffer,*pptr,len); /* copy data to local buffer */
+ buffer[len] = (char)0;
+ *pptr = buffer; /* point to own copy now */
+ advance_buf(buffer,buflen,len+1);
+ pptr++;
+ }
+
+ /* traverse address list, collecting the pointers */
+ align_buf(buffer, buflen);
+ pptr = (char **)buffer;
+ dest->h_addr_list = pptr; /* save head of pointer array */
+
+ src_addr_list = src->h_addr_list;
+
+ while(*(src_addr_list)) {
+ if (buflen < sizeof(*pptr)) return -1;
+ *pptr = *src_addr_list;
+ advance_buf(buffer,buflen,sizeof(*pptr));
+ src_addr_list++;
+ pptr++;
+ }
+ if (buflen < sizeof(*pptr)) return -1;
+ *pptr = NULL;
+ advance_buf(buffer,buflen,sizeof(*pptr));
+
+ /* go back to saved position & transfer the addresses */
+ /* align_buf(buffer, buflen); */
+ pptr = dest->h_addr_list;
+ while (*pptr) {
+ len = src->h_length;
+ if (buflen < len+1) return -1;
+ memmove(buffer,*pptr,len); /* copy data to local buffer */
+ buffer[len]=(char)0; /* not sure if termination is necessary */
+ *pptr = buffer; /* point to own copy now */
+ advance_buf(buffer,buflen,len+1);
+ pptr++;
+ }
+
+ if (buflen < 0) return -1;
+ return 0;
+}
+
+/* This function is a pseudo-reentrant version of gethostbyname(). It
+ * uses locks to serialize the call to the regular (non-reentrant)
+ * gethostbyname() and then copies the data into the user-provided
+ * buffers. It's not pretty but it works.
+ *
+ * name - name of host to look up
+ * hostp - user-supplied structure for returning host entry
+ * buffer - user-supplied buffer: storage for the copied host data
+ * buflen - length of user-supplied buffer
+ * h_errnop - buffer for return status
+ *
+ * Returns values as for gethostbyname(). Additionally, sets
+ * errno=ERANGE and returns NULL if buffer is too small for host data.
+ */
+
+static struct hostent *my_gethostbyname_r(const char *name,
+ struct hostent *hostp,
+ char *buffer,
+ int buflen,
+ int *h_errnop)
+{
+ struct hostent dest;
+ struct hostent *src;
+ struct hostent *rval = NULL;
+
+ /* FIXME this should have been done in 'erl'_init()? */
+ if (!ei_resolve_initialized) ei_init_resolve();
+
+#ifdef _REENTRANT
+ /* === BEGIN critical section === */
+ if (ei_mutex_lock(ei_gethost_sem,0) != 0) {
+ *h_errnop = NO_RECOVERY;
+ return NULL;
+ }
+#endif /* _REENTRANT */
+
+ /* lookup the data */
+ if ((src = ei_gethostbyname(name))) {
+ /* copy to caller's buffer */
+ if (!copy_hostent(&dest,src,buffer,buflen)) {
+ /* success */
+ *hostp = dest;
+ *h_errnop = 0;
+ rval = hostp;
+ }
+
+ else {
+ /* failure - buffer size */
+#ifdef __WIN32__
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+#else
+ errno = ERANGE;
+#endif
+ *h_errnop = 0;
+ }
+ }
+
+ else {
+ /* failure - lookup */
+#ifdef __WIN32__
+ *h_errnop = WSAGetLastError();
+#else
+ *h_errnop = h_errno;
+#endif
+ }
+
+#ifdef _REENTRANT
+ /* === END critical section === */
+ ei_mutex_unlock(ei_gethost_sem);
+#endif /* _REENTRANT */
+ return rval;
+}
+
+static struct hostent *my_gethostbyaddr_r(const char *addr,
+ int length,
+ int type,
+ struct hostent *hostp,
+ char *buffer,
+ int buflen,
+ int *h_errnop)
+{
+ struct hostent dest;
+ struct hostent *src;
+ struct hostent *rval = NULL;
+
+ /* FIXME this should have been done in 'erl'_init()? */
+ if (!ei_resolve_initialized) ei_init_resolve();
+
+#ifdef _REENTRANT
+ /* === BEGIN critical section === */
+ if (ei_mutex_lock(ei_gethost_sem,0) != 0) {
+ *h_errnop = NO_RECOVERY;
+ return NULL;
+ }
+#endif /* _REENTRANT */
+
+ /* lookup the data */
+ if ((src = ei_gethostbyaddr(addr,length,type))) {
+ /* copy to caller's buffer */
+ if (!copy_hostent(&dest,src,buffer,buflen)) {
+ /* success */
+ *hostp = dest;
+ *h_errnop = 0;
+ rval = hostp;
+ }
+
+ else {
+ /* failure - buffer size */
+#ifdef __WIN32__
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+#else
+ errno = ERANGE;
+#endif
+ *h_errnop = 0;
+ }
+ }
+
+ else {
+ /* failure - lookup */
+#ifdef __WIN32__
+ *h_errnop = WSAGetLastError();
+#else
+ *h_errnop = h_errno;
+#endif
+ }
+
+
+#ifdef _REENTRANT
+ /* === END critical section === */
+ ei_mutex_unlock(ei_gethost_sem);
+#endif /* _REENTRANT */
+ return rval;
+}
+
+
+#endif /* !HAVE_GETHOSTBYNAME_R */
+
+
+#ifdef __WIN32__
+struct hostent *ei_gethostbyname(const char *name)
+{
+ return gethostbyname(name);
+}
+
+struct hostent *ei_gethostbyaddr(const char *addr, int len, int type)
+{
+ return gethostbyaddr(addr, len, type);
+}
+
+#elif VXWORKS
+
+
+/* these are a couple of substitutes for the real thing when we run on
+ * stock vxworks (i.e. no sens).
+ *
+ * len and type are ignored, but we make up some reasonable values and
+ * insert them
+ */
+static struct hostent *my_gethostbyname(const char *name)
+{
+ /* FIXME problem for threaded ? */
+ static struct hostent h;
+ static char hostname[EI_MAXHOSTNAMELEN+1];
+ static char *aliases[1] = {NULL};
+ static char *addrp[2] = {NULL,NULL};
+ static unsigned long addr = 0;
+
+ strcpy(hostname,name);
+ if ((addr = (unsigned long)hostGetByName(hostname)) == ERROR) {
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ h_errno = 0;
+ h.h_name = hostname;
+ h.h_aliases = aliases;
+ h.h_length = 4;
+ h.h_addrtype = AF_INET;
+ addrp[0] = (char *)&addr;
+ h.h_addr_list = addrp;
+
+ return &h;
+}
+
+static struct hostent *my_gethostbyaddr(const char *addr, int len, int type)
+{
+ /* FIXME problem for threaded ? */
+ static struct hostent h;
+ static char hostname[EI_MAXHOSTNAMELEN+1];
+ static char *aliases[1] = { NULL };
+ static unsigned long inaddr;
+ static char *addrp[2] = {(char *)&inaddr, NULL};
+
+ memmove(&inaddr,addr,sizeof(inaddr));
+
+ if ((hostGetByAddr(inaddr,hostname)) == ERROR) {
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ h_errno = 0;
+ h.h_name = hostname;
+ h.h_aliases = aliases;
+ h.h_length = 4;
+ h.h_addrtype = AF_INET;
+ h.h_addr_list = addrp;
+
+ return &h;
+}
+
+/* use sens functions for these, if found. */
+struct hostent *ei_gethostbyname(const char *name)
+{
+ struct hostent *h = NULL;
+
+ if (!sens_gethostbyname) {
+ h = my_gethostbyname(name);
+ }
+ else {
+ /* FIXME problem for threaded ? */
+ static char buf[1024];
+ h = sens_gethostbyname(name,buf,1024);
+ }
+
+ return h;
+}
+
+struct hostent *ei_gethostbyaddr(const char *addr, int len, int type)
+{
+ struct hostent *h = NULL;
+
+ if (!sens_gethostbyaddr) {
+ h = my_gethostbyaddr(addr,len,type);
+ }
+ else {
+ /* FIXME problem for threaded ? */
+ static char buf[1024];
+ h = sens_gethostbyaddr(addr,buf,1024);
+ }
+
+ return h;
+}
+
+struct hostent *ei_gethostbyaddr_r(const char *addr,
+ int length,
+ int type,
+ struct hostent *hostp,
+ char *buffer,
+ int buflen,
+ int *h_errnop)
+{
+ struct hostent *h = NULL;
+
+ /* use own func if sens function not available */
+ if (!sens_gethostbyaddr) {
+ h = my_gethostbyaddr_r(addr,length,type,hostp,buffer,buflen,h_errnop);
+ }
+ else {
+ if (!(h = sens_gethostbyaddr(addr,buffer,buflen))) {
+ /* sens returns status via errno */
+ *h_errnop = errno;
+ }
+ else {
+ *hostp = *h;
+ *h_errnop = 0;
+ }
+ }
+
+ return h;
+}
+
+struct hostent *ei_gethostbyname_r(const char *name,
+ struct hostent *hostp,
+ char *buffer,
+ int buflen,
+ int *h_errnop)
+{
+ struct hostent *h = NULL;
+
+ /* use own func if sens function not available */
+ if (!sens_gethostbyname) {
+ h = my_gethostbyname_r(name,hostp,buffer,buflen,h_errnop);
+ }
+ else {
+ if (!(h = sens_gethostbyname(name,buffer,buflen))) {
+ /* sens returns status via errno */
+ *h_errnop = errno;
+ }
+ else {
+ *hostp = *h;
+ *h_errnop = 0;
+ }
+ }
+
+ return h;
+}
+
+#else /* unix of some kind */
+
+struct hostent *ei_gethostbyname(const char *name)
+{
+ return gethostbyname(name);
+}
+
+struct hostent *ei_gethostbyaddr(const char *addr, int len, int type)
+{
+ return gethostbyaddr(addr, len, type);
+}
+
+struct hostent *ei_gethostbyaddr_r(const char *addr,
+ int length,
+ int type,
+ struct hostent *hostp,
+ char *buffer,
+ int buflen,
+ int *h_errnop)
+{
+#if (EI_THREADS == false)
+ /* threads disabled, no need to call reentrant function */
+ return gethostbyaddr(addr, length, type);
+#else
+#ifndef HAVE_GETHOSTBYNAME_R
+ return my_gethostbyaddr_r(addr,length,type,hostp,buffer,buflen,h_errnop);
+#else
+#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000))
+ struct hostent *result;
+
+ gethostbyaddr_r(addr, length, type, hostp, buffer, buflen, &result,
+ h_errnop);
+
+ return result;
+#else
+ return gethostbyaddr_r(addr,length,type,hostp,buffer,buflen,h_errnop);
+#endif
+#endif
+#endif
+}
+
+struct hostent *ei_gethostbyname_r(const char *name,
+ struct hostent *hostp,
+ char *buffer,
+ int buflen,
+ int *h_errnop)
+{
+#ifndef _REENTRANT
+ /* threads disabled, no need to call reentrant function */
+ return gethostbyname(name);
+#else
+#ifndef HAVE_GETHOSTBYNAME_R
+ return my_gethostbyname_r(name,hostp,buffer,buflen,h_errnop);
+#else
+#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000))
+ struct hostent *result;
+
+ gethostbyname_r(name, hostp, buffer, buflen, &result, h_errnop);
+
+ return result;
+#else
+ return gethostbyname_r(name,hostp,buffer,buflen,h_errnop);
+#endif
+#endif
+#endif
+}
+
+#endif /* vxworks, win, unix */
+