aboutsummaryrefslogtreecommitdiffstats
path: root/lib/erl_interface/src/connect
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/erl_interface/src/connect
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/erl_interface/src/connect')
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c1738
-rw-r--r--lib/erl_interface/src/connect/ei_connect_int.h114
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c645
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.h24
-rw-r--r--lib/erl_interface/src/connect/eirecv.c280
-rw-r--r--lib/erl_interface/src/connect/eirecv.h26
-rw-r--r--lib/erl_interface/src/connect/eisend.h41
-rw-r--r--lib/erl_interface/src/connect/send.c125
-rw-r--r--lib/erl_interface/src/connect/send_exit.c101
-rw-r--r--lib/erl_interface/src/connect/send_reg.c122
10 files changed, 3216 insertions, 0 deletions
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
new file mode 100644
index 0000000000..9ac5a93c5a
--- /dev/null
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -0,0 +1,1738 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-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%
+ */
+/*
+ * Purpose: Connect to any node at any host. (EI version)
+ */
+
+#include "eidef.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#ifdef __WIN32__
+#include <winsock2.h>
+#include <windows.h>
+#include <winbase.h>
+
+#elif VXWORKS
+#include <vxWorks.h>
+#include <hostLib.h>
+#include <selectLib.h>
+#include <ifLib.h>
+#include <sockLib.h>
+#include <taskLib.h>
+#include <inetLib.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <timers.h>
+
+#define getpid() taskIdSelf()
+
+#else /* some other unix */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/times.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/utsname.h> /* for gen_challenge (NEED FIX?) */
+#include <time.h>
+#endif
+
+/* common includes */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "eiext.h"
+#include "ei_portio.h"
+#include "ei_internal.h"
+#include "ei_connect_int.h"
+#include "ei_locking.h"
+#include "eisend.h"
+#include "eirecv.h"
+#include "eimd5.h"
+#include "putget.h"
+#include "ei_resolve.h"
+#include "ei_epmd.h"
+#include "ei_internal.h"
+
+int ei_tracelevel = 0;
+
+#define COOKIE_FILE "/.erlang.cookie"
+#define EI_MAX_HOME_PATH 1024
+
+/* FIXME why not macro? */
+static char *null_cookie = "";
+
+static int get_cookie(char *buf, int len);
+static int get_home(char *buf, int size);
+
+/* forwards */
+static unsigned gen_challenge(void);
+static void gen_digest(unsigned challenge, char cookie[],
+ unsigned char digest[16]);
+static int send_status(int fd, char *status, unsigned ms);
+static int recv_status(int fd, unsigned ms);
+static int send_challenge(int fd, char *nodename,
+ unsigned challenge, unsigned version, unsigned ms);
+static int recv_challenge(int fd, unsigned *challenge,
+ unsigned *version,
+ unsigned *flags, ErlConnect *namebuf, unsigned ms);
+static int send_challenge_reply(int fd, unsigned char digest[16],
+ unsigned challenge, unsigned ms);
+static int recv_challenge_reply(int fd,
+ unsigned our_challenge,
+ char cookie[],
+ unsigned *her_challenge, unsigned ms);
+static int send_challenge_ack(int fd, unsigned char digest[16], unsigned ms);
+static int recv_challenge_ack(int fd,
+ unsigned our_challenge,
+ char cookie[], unsigned ms);
+static int send_name(int fd, char *nodename,
+ unsigned version, unsigned ms);
+
+/* Common for both handshake types */
+static int recv_name(int fd,
+ unsigned *version,
+ unsigned *flags, ErlConnect *namebuf, unsigned ms);
+
+
+/***************************************************************************
+ *
+ * For each file descriptor returned from ei_connect() we save information
+ * about distribution protocol version, node information for this node
+ * and the cookie.
+ *
+ ***************************************************************************/
+
+typedef struct ei_socket_info_s {
+ int socket;
+ int dist_version;
+ ei_cnode cnode; /* A copy, not a pointer. We don't know when freed */
+ char cookie[EI_MAX_COOKIE_SIZE+1];
+} ei_socket_info;
+
+int ei_n_sockets = 0, ei_sz_sockets = 0;
+ei_socket_info *ei_sockets = NULL;
+#ifdef _REENTRANT
+ei_mutex_t* ei_sockets_lock = NULL;
+#endif /* _REENTRANT */
+
+
+/***************************************************************************
+ *
+ * XXX
+ *
+ ***************************************************************************/
+
+static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode *ec)
+{
+ int i;
+
+#ifdef _REENTRANT
+ ei_mutex_lock(ei_sockets_lock, 0);
+#endif /* _REENTRANT */
+ for (i = 0; i < ei_n_sockets; ++i) {
+ if (ei_sockets[i].socket == fd) {
+ if (dist_version == -1) {
+ memmove(&ei_sockets[i], &ei_sockets[i+1],
+ sizeof(ei_sockets[0])*(ei_n_sockets-i-1));
+ } else {
+ ei_sockets[i].dist_version = dist_version;
+ /* Copy the content, see ei_socket_info */
+ ei_sockets[i].cnode = *ec;
+ strcpy(ei_sockets[i].cookie, cookie);
+ }
+#ifdef _REENTRANT
+ ei_mutex_unlock(ei_sockets_lock);
+#endif /* _REENTRANT */
+ return 0;
+ }
+ }
+ if (ei_n_sockets == ei_sz_sockets) {
+ ei_sz_sockets += 5;
+ ei_sockets = realloc(ei_sockets,
+ sizeof(ei_sockets[0])*ei_sz_sockets);
+ if (ei_sockets == NULL) {
+ ei_sz_sockets = ei_n_sockets = 0;
+#ifdef _REENTRANT
+ ei_mutex_unlock(ei_sockets_lock);
+#endif /* _REENTRANT */
+ return -1;
+ }
+ ei_sockets[ei_n_sockets].socket = fd;
+ ei_sockets[ei_n_sockets].dist_version = dist_version;
+ ei_sockets[i].cnode = *ec;
+ strcpy(ei_sockets[ei_n_sockets].cookie, cookie);
+ ++ei_n_sockets;
+ }
+#ifdef _REENTRANT
+ ei_mutex_unlock(ei_sockets_lock);
+#endif /* _REENTRANT */
+ return 0;
+}
+
+#if 0
+/* FIXME not used ?! */
+static int remove_ei_socket_info(int fd, int dist_version, char* cookie)
+{
+ return put_ei_socket_info(fd, -1, NULL);
+}
+#endif
+
+static ei_socket_info* get_ei_socket_info(int fd)
+{
+ int i;
+#ifdef _REENTRANT
+ ei_mutex_lock(ei_sockets_lock, 0);
+#endif /* _REENTRANT */
+ for (i = 0; i < ei_n_sockets; ++i)
+ if (ei_sockets[i].socket == fd) {
+ /*fprintf("get_ei_socket_info %d %d \"%s\"\n",
+ fd, ei_sockets[i].dist_version, ei_sockets[i].cookie);*/
+#ifdef _REENTRANT
+ ei_mutex_unlock(ei_sockets_lock);
+#endif /* _REENTRANT */
+ return &ei_sockets[i];
+ }
+#ifdef _REENTRANT
+ ei_mutex_unlock(ei_sockets_lock);
+#endif /* _REENTRANT */
+ return NULL;
+}
+
+ei_cnode *ei_fd_to_cnode(int fd)
+{
+ ei_socket_info *sockinfo = get_ei_socket_info(fd);
+ if (sockinfo == NULL) return NULL;
+ return &sockinfo->cnode;
+}
+
+/***************************************************************************
+ * XXXX
+ ***************************************************************************/
+
+int ei_distversion(int fd)
+{
+ ei_socket_info* e = get_ei_socket_info(fd);
+ if (e == NULL)
+ return -1;
+ else
+ return e->dist_version;
+}
+
+static const char* ei_cookie(int fd)
+{
+ ei_socket_info* e = get_ei_socket_info(fd);
+ if (e == NULL)
+ return NULL;
+ else
+ return e->cookie;
+}
+
+const char *ei_thisnodename(const ei_cnode* ec)
+{
+ return ec->thisnodename;
+}
+
+const char *ei_thishostname(const ei_cnode* ec)
+{
+ return ec->thishostname;
+}
+
+const char *ei_thisalivename(const ei_cnode* ec)
+{
+ return ec->thisalivename;
+}
+
+short ei_thiscreation(const ei_cnode* ec)
+{
+ return ec->creation;
+}
+
+/* FIXME: this function is not an api, why not? */
+const char *ei_thiscookie(const ei_cnode* ec)
+{
+ return (const char *)ec->ei_connect_cookie;
+}
+
+erlang_pid *ei_self(ei_cnode* ec)
+{
+ return &ec->self;
+}
+
+/* two internal functions that will let us support different cookies
+* (to be able to connect to other nodes that don't have the same
+* cookie as each other or us)
+*/
+const char *ei_getfdcookie(int fd)
+{
+ const char* r = ei_cookie(fd);
+ if (r == NULL) r = "";
+ return r;
+}
+
+/* call with cookie to set value to use on descriptor fd,
+* or specify NULL to use default
+*/
+/* FIXME why defined but not used? */
+#if 0
+static int ei_setfdcookie(ei_cnode* ec, int fd, char *cookie)
+{
+ int dist_version = ei_distversion(fd);
+
+ if (cookie == NULL)
+ cookie = ec->ei_connect_cookie;
+ return put_ei_socket_info(fd, dist_version, cookie);
+}
+#endif
+
+static int get_int32(unsigned char *s)
+{
+ return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] ));
+}
+
+
+#ifdef __WIN32__
+void win32_error(char *buf, int buflen)
+{
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0, /* n/a */
+ WSAGetLastError(), /* error code */
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* language */
+ buf,
+ buflen,
+ NULL);
+ return;
+}
+
+static int initWinSock(void)
+{
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int i;
+ /* FIXME problem for threaded ? */
+ static int initialized = 0;
+
+ wVersionRequested = MAKEWORD(1, 1);
+ if (!initialized) {
+ initialized = 1;
+ /* FIXME not terminate, just a message?! */
+ if ((i = WSAStartup(wVersionRequested, &wsaData))) {
+ EI_TRACE_ERR1("ei_connect_init",
+ "ERROR: can't initialize windows sockets: %d",i);
+ return 0;
+ }
+
+ if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
+ EI_TRACE_ERR0("initWinSock","ERROR: this version of windows "
+ "sockets not supported");
+ WSACleanup();
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+/*
+* Perhaps run this routine instead of ei_connect_init/2 ?
+* Initailize by setting:
+* thishostname, thisalivename, thisnodename and thisipaddr
+*/
+int ei_connect_xinit(ei_cnode* ec, const char *thishostname,
+ const char *thisalivename, const char *thisnodename,
+ Erl_IpAddr thisipaddr, const char *cookie,
+ const short creation)
+{
+ char *dbglevel;
+
+/* FIXME this code was enabled for 'erl'_connect_xinit(), why not here? */
+#if 0
+#ifdef __WIN32__
+ if (!initWinSock()) {
+ EI_TRACE_ERR0("ei_connect_xinit","can't initiate winsock");
+ return ERL_ERROR;
+ }
+#endif
+#endif
+
+#ifdef _REENTRANT
+ if (ei_sockets_lock == NULL) {
+ ei_sockets_lock = ei_mutex_create();
+ }
+#endif /* _REENTRANT */
+
+ ec->creation = creation;
+
+ if (cookie) {
+ if (strlen(cookie) >= sizeof(ec->ei_connect_cookie)) {
+ EI_TRACE_ERR0("ei_connect_xinit",
+ "ERROR: Cookie size too large");
+ return ERL_ERROR;
+ } else {
+ strcpy(ec->ei_connect_cookie, cookie);
+ }
+ } else if (!get_cookie(ec->ei_connect_cookie, sizeof(ec->ei_connect_cookie))) {
+ return ERL_ERROR;
+ }
+
+ if (strlen(thishostname) >= sizeof(ec->thishostname)) {
+ EI_TRACE_ERR0("ei_connect_xinit","ERROR: Thishostname too long");
+ return ERL_ERROR;
+ }
+ strcpy(ec->thishostname, thishostname);
+
+ if (strlen(thisalivename) >= sizeof(ec->thisalivename)) {
+ EI_TRACE_ERR0("ei_connect_init","Thisalivename too long");
+ return ERL_ERROR;
+ }
+
+ strcpy(ec->thisalivename, thisalivename);
+
+ if (strlen(thisnodename) >= sizeof(ec->thisnodename)) {
+ EI_TRACE_ERR0("ei_connect_init","Thisnodename too long");
+ return ERL_ERROR;
+ }
+ strcpy(ec->thisnodename, thisnodename);
+
+/* FIXME right now this_ipaddr is never used */
+/* memmove(&ec->this_ipaddr, thisipaddr, sizeof(ec->this_ipaddr)); */
+
+ strcpy(ec->self.node,thisnodename);
+ ec->self.num = 0;
+ ec->self.serial = 0;
+ ec->self.creation = creation;
+
+ if ((dbglevel = getenv("EI_TRACELEVEL")) != NULL ||
+ (dbglevel = getenv("ERL_DEBUG_DIST")) != NULL)
+ ei_tracelevel = atoi(dbglevel);
+
+ return 0;
+}
+
+
+/*
+* Initialize by set: thishostname, thisalivename,
+* thisnodename and thisipaddr. At success return 0,
+* otherwise return -1.
+*/
+int ei_connect_init(ei_cnode* ec, const char* this_node_name,
+ const char *cookie, short creation)
+{
+ struct hostent *hp;
+ char thishostname[EI_MAXHOSTNAMELEN+1];
+ char thisnodename[MAXNODELEN+1];
+ char thisalivename[EI_MAXALIVELEN+1];
+
+#ifdef __WIN32__
+ if (!initWinSock()) {
+ EI_TRACE_ERR0("ei_connect_xinit","can't initiate winsock");
+ return ERL_ERROR;
+ }
+#endif /* win32 */
+#ifdef _REENTRANT
+ if (ei_sockets_lock == NULL) {
+ ei_sockets_lock = ei_mutex_create();
+ }
+#endif /* _REENTRANT */
+
+ if (gethostname(thishostname, EI_MAXHOSTNAMELEN) == -1) {
+#ifdef __WIN32__
+ EI_TRACE_ERR1("ei_connect_init","Failed to get host name: %d",
+ WSAGetLastError());
+#else
+ EI_TRACE_ERR1("ei_connect_init","Failed to get host name: %d", errno);
+#endif /* win32 */
+ return ERL_ERROR;
+ }
+
+ if (this_node_name == NULL)
+ sprintf(thisalivename, "c%d", (int) getpid());
+ else
+ strcpy(thisalivename, this_node_name);
+
+ if ((hp = ei_gethostbyname(thishostname)) == 0) {
+ /* Looking up IP given hostname fails. We must be on a standalone
+ host so let's use loopback for communication instead. */
+ if ((hp = ei_gethostbyname("localhost")) == 0) {
+#ifdef __WIN32__
+ char reason[1024];
+
+ win32_error(reason,sizeof(reason));
+ EI_TRACE_ERR2("ei_connect_init",
+ "Can't get ip address for host %s: %s",
+ thishostname, reason);
+#else
+ EI_TRACE_ERR2("ei_connect_init",
+ "Can't get ip address for host %s: %d",
+ thishostname, h_errno);
+#endif /* win32 */
+ return ERL_ERROR;
+ }
+ }
+ {
+ char* ct;
+ if (strcmp(hp->h_name, "localhost") == 0) {
+ /* We use a short node name */
+ if ((ct = strchr(thishostname, '.')) != NULL) *ct = '\0';
+ sprintf(thisnodename, "%s@%s", this_node_name, thishostname);
+ } else {
+ /* We use a short node name */
+ if ((ct = strchr(hp->h_name, '.')) != NULL) *ct = '\0';
+ strcpy(thishostname, hp->h_name);
+ sprintf(thisnodename, "%s@%s", this_node_name, hp->h_name);
+ }
+ }
+ return ei_connect_xinit(ec, thishostname, thisalivename, thisnodename,
+ (struct in_addr *)*hp->h_addr_list, cookie, creation);
+}
+
+
+/* connects to port at ip-address ip_addr
+* and returns fd to socket
+* port has to be in host byte order
+*/
+static int cnct(uint16 port, struct in_addr *ip_addr, int addr_len, unsigned ms)
+{
+ int s, res;
+ struct sockaddr_in iserv_addr;
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ erl_errno = errno;
+ return ERL_ERROR;
+ }
+
+ memset((char*)&iserv_addr, 0, sizeof(struct sockaddr_in));
+ memcpy((char*)&iserv_addr.sin_addr, (char*)ip_addr, addr_len);
+ iserv_addr.sin_family = AF_INET;
+ iserv_addr.sin_port = htons(port);
+
+ if ((res = ei_connect_t(s, (struct sockaddr*)&iserv_addr,
+ sizeof(iserv_addr),ms)) < 0) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ closesocket(s);
+ return ERL_ERROR;
+ }
+
+ return s;
+} /* cnct */
+
+ /*
+ * Set up a connection to a given Node, and
+ * interchange hand shake messages with it.
+ * Returns a valid file descriptor at success,
+ * otherwise a negative error code.
+*/
+int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned ms)
+{
+ char *hostname, alivename[BUFSIZ];
+ struct hostent *hp;
+#if !defined (__WIN32__)
+ /* these are needed for the call to gethostbyname_r */
+ struct hostent host;
+ char buffer[1024];
+ int ei_h_errno;
+#endif /* !win32 */
+
+ /* extract the host and alive parts from nodename */
+ if (!(hostname = strchr(nodename,'@'))) {
+ EI_TRACE_ERR0("ei_connect","Node name has no @ in name");
+ return ERL_ERROR;
+ } else {
+ strncpy(alivename, nodename, hostname - nodename);
+ alivename[hostname - nodename] = 0x0;
+ hostname++;
+ }
+
+#ifndef __WIN32__
+ hp = ei_gethostbyname_r(hostname,&host,buffer,1024,&ei_h_errno);
+ if (hp == NULL) {
+ char thishostname[EI_MAXHOSTNAMELEN+1];
+ if (gethostname(thishostname,EI_MAXHOSTNAMELEN) < 0) {
+ EI_TRACE_ERR0("ei_connect_tmo",
+ "Failed to get name of this host");
+ erl_errno = EHOSTUNREACH;
+ return ERL_ERROR;
+ } else {
+ char *ct;
+ /* We use a short node name */
+ if ((ct = strchr(thishostname, '.')) != NULL) *ct = '\0';
+ }
+ if (strcmp(hostname,thishostname) == 0)
+ /* Both nodes on same standalone host, use loopback */
+ hp = ei_gethostbyname_r("localhost",&host,buffer,1024,&ei_h_errno);
+ if (hp == NULL) {
+ EI_TRACE_ERR2("ei_connect",
+ "Can't find host for %s: %d\n",nodename,ei_h_errno);
+ erl_errno = EHOSTUNREACH;
+ return ERL_ERROR;
+ }
+ }
+#else /* __WIN32__ */
+ if ((hp = ei_gethostbyname(hostname)) == NULL) {
+ char thishostname[EI_MAXHOSTNAMELEN+1];
+ if (gethostname(thishostname,EI_MAXHOSTNAMELEN) < 0) {
+ EI_TRACE_ERR1("ei_connect_tmo",
+ "Failed to get name of this host: %d",
+ WSAGetLastError());
+ erl_errno = EHOSTUNREACH;
+ return ERL_ERROR;
+ } else {
+ char *ct;
+ /* We use a short node name */
+ if ((ct = strchr(thishostname, '.')) != NULL) *ct = '\0';
+ }
+ if (strcmp(hostname,thishostname) == 0)
+ /* Both nodes on same standalone host, use loopback */
+ hp = ei_gethostbyname("localhost");
+ if (hp == NULL) {
+ char reason[1024];
+ win32_error(reason,sizeof(reason));
+ EI_TRACE_ERR2("ei_connect",
+ "Can't find host for %s: %s",nodename,reason);
+ erl_errno = EHOSTUNREACH;
+ return ERL_ERROR;
+ }
+ }
+#endif /* win32 */
+ return ei_xconnect_tmo(ec, (Erl_IpAddr) *hp->h_addr_list, alivename, ms);
+} /* ei_connect */
+
+int ei_connect(ei_cnode* ec, char *nodename)
+{
+ return ei_connect_tmo(ec, nodename, 0);
+}
+
+
+ /* ip_addr is now in network byte order
+ *
+ * first we have to get hold of the portnumber to
+ * the node through epmd at that host
+ *
+*/
+int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr adr, char *alivename, unsigned ms)
+{
+ struct in_addr *ip_addr=(struct in_addr *) adr;
+ int rport = 0; /*uint16 rport = 0;*/
+ int sockd;
+ int one = 1;
+ int dist = 0;
+ ErlConnect her_name;
+ unsigned her_flags, her_version;
+
+ erl_errno = EIO; /* Default error code */
+
+ EI_TRACE_CONN1("ei_xconnect","-> CONNECT attempt to connect to %s",
+ alivename);
+
+ if ((rport = ei_epmd_port_tmo(ip_addr,alivename,&dist, ms)) < 0) {
+ EI_TRACE_ERR0("ei_xconnect","-> CONNECT can't get remote port");
+ /* ei_epmd_port_tmo() has set erl_errno */
+ return ERL_NO_PORT;
+ }
+
+ /* we now have port number to enode, try to connect */
+ if((sockd = cnct((uint16)rport, ip_addr, sizeof(struct in_addr),ms)) < 0) {
+ EI_TRACE_ERR0("ei_xconnect","-> CONNECT socket connect failed");
+ /* cnct() has set erl_errno */
+ return ERL_CONNECT_FAIL;
+ }
+
+ EI_TRACE_CONN0("ei_xconnect","-> CONNECT connected to remote");
+
+ /* FIXME why connect before checking 'dist' output from ei_epmd_port() ?! */
+ if (dist <= 4) {
+ EI_TRACE_ERR0("ei_xconnect","-> CONNECT remote version not compatible");
+ goto error;
+ }
+ else {
+ unsigned our_challenge, her_challenge;
+ unsigned char our_digest[16];
+
+ if (send_name(sockd, ec->thisnodename, (unsigned) dist, ms))
+ goto error;
+ if (recv_status(sockd, ms))
+ goto error;
+ if (recv_challenge(sockd, &her_challenge, &her_version,
+ &her_flags, &her_name, ms))
+ goto error;
+ our_challenge = gen_challenge();
+ gen_digest(her_challenge, ec->ei_connect_cookie, our_digest);
+ if (send_challenge_reply(sockd, our_digest, our_challenge, ms))
+ goto error;
+ if (recv_challenge_ack(sockd, our_challenge,
+ ec->ei_connect_cookie, ms))
+ goto error;
+ put_ei_socket_info(sockd, dist, null_cookie, ec); /* FIXME check == 0 */
+ }
+
+ setsockopt(sockd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one));
+ setsockopt(sockd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one));
+
+ EI_TRACE_CONN1("ei_xconnect","-> CONNECT (ok) remote = %s",alivename);
+
+ erl_errno = 0;
+ return sockd;
+
+error:
+ EI_TRACE_ERR0("ei_xconnect","-> CONNECT failed");
+ closesocket(sockd);
+ return ERL_ERROR;
+} /* ei_xconnect */
+
+int ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename)
+{
+ return ei_xconnect_tmo(ec, adr, alivename, 0);
+}
+
+
+ /*
+ * For symmetry reasons
+*/
+#if 0
+int ei_close_connection(int fd)
+{
+ return closesocket(fd);
+} /* ei_close_connection */
+#endif
+
+ /*
+ * Accept and initiate a connection from an other
+ * Erlang node. Return a file descriptor at success,
+ * otherwise -1;
+*/
+int ei_accept(ei_cnode* ec, int lfd, ErlConnect *conp)
+{
+ return ei_accept_tmo(ec, lfd, conp, 0);
+}
+
+int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
+{
+ int fd;
+ struct sockaddr_in cli_addr;
+ int cli_addr_len=sizeof(struct sockaddr_in);
+ unsigned her_version, her_flags;
+ ErlConnect her_name;
+
+ erl_errno = EIO; /* Default error code */
+
+ EI_TRACE_CONN0("ei_accept","<- ACCEPT waiting for connection");
+
+ if ((fd = ei_accept_t(lfd, (struct sockaddr*) &cli_addr,
+ &cli_addr_len, ms )) < 0) {
+ EI_TRACE_ERR0("ei_accept","<- ACCEPT socket accept failed");
+ erl_errno = (fd == -2) ? ETIMEDOUT : EIO;
+ goto error;
+ }
+
+ EI_TRACE_CONN0("ei_accept","<- ACCEPT connected to remote");
+
+ if (recv_name(fd, &her_version, &her_flags, &her_name, ms)) {
+ EI_TRACE_ERR0("ei_accept","<- ACCEPT initial ident failed");
+ goto error;
+ }
+
+ if (her_version <= 4) {
+ EI_TRACE_ERR0("ei_accept","<- ACCEPT remote version not compatible");
+ goto error;
+ }
+ else {
+ unsigned our_challenge;
+ unsigned her_challenge;
+ unsigned char our_digest[16];
+
+ if (send_status(fd,"ok", ms))
+ goto error;
+ our_challenge = gen_challenge();
+ if (send_challenge(fd, ec->thisnodename,
+ our_challenge, her_version, ms))
+ goto error;
+ if (recv_challenge_reply(fd, our_challenge,
+ ec->ei_connect_cookie,
+ &her_challenge, ms))
+ goto error;
+ gen_digest(her_challenge, ec->ei_connect_cookie, our_digest);
+ if (send_challenge_ack(fd, our_digest, ms))
+ goto error;
+ put_ei_socket_info(fd, her_version, null_cookie, ec);
+ }
+ if (conp)
+ *conp = her_name;
+
+ EI_TRACE_CONN1("ei_accept","<- ACCEPT (ok) remote = %s",her_name.nodename);
+
+ erl_errno = 0; /* No error */
+ return fd;
+
+error:
+ EI_TRACE_ERR0("ei_accept","<- ACCEPT failed");
+ closesocket(fd);
+ return ERL_ERROR;
+} /* ei_accept */
+
+
+/* Receives a message from an Erlang socket.
+ * If the message was a TICK it is immediately
+ * answered. Returns: ERL_ERROR, ERL_TICK or
+ * the number of bytes read.
+ */
+int ei_receive_tmo(int fd, unsigned char *bufp, int bufsize, unsigned ms)
+{
+ int len;
+ unsigned char fourbyte[4]={0,0,0,0};
+ int res;
+
+ if ((res = ei_read_fill_t(fd, (char *) bufp, 4, ms)) != 4) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return ERL_ERROR;
+ }
+
+ /* Tick handling */
+ if ((len = get_int32(bufp)) == ERL_TICK)
+ {
+ ei_write_fill_t(fd, (char *) fourbyte, 4, ms);
+ /* FIXME ok to ignore error or timeout? */
+ erl_errno = EAGAIN;
+ return ERL_TICK;
+ }
+ else if (len > bufsize)
+ {
+ /* FIXME: We should drain the message. */
+ erl_errno = EMSGSIZE;
+ return ERL_ERROR;
+ }
+ else if ((res = ei_read_fill_t(fd, (char *) bufp, len, ms)) != len)
+ {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return ERL_ERROR;
+ }
+
+ return len;
+
+}
+
+int ei_receive(int fd, unsigned char *bufp, int bufsize)
+{
+ return ei_receive_tmo(fd, bufp, bufsize, 0);
+}
+
+int ei_reg_send_tmo(ei_cnode* ec, int fd, char *server_name,
+ char* buf, int len, unsigned ms)
+{
+ erlang_pid *self = ei_self(ec);
+ self->num = fd;
+
+ /* erl_errno and return code is set by ei_reg_send_encoded_tmo() */
+ return ei_send_reg_encoded_tmo(fd, self, server_name, buf, len, ms);
+}
+
+
+int ei_reg_send(ei_cnode* ec, int fd, char *server_name, char* buf, int len)
+{
+ return ei_reg_send_tmo(ec,fd,server_name,buf,len,0);
+}
+
+/*
+* Sends an Erlang message to a process at an Erlang node
+*/
+int ei_send_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned ms)
+{
+ /* erl_errno and return code is set by ei_reg_send_encoded_tmo() */
+ return ei_send_encoded_tmo(fd, to, buf, len, ms);
+}
+
+int ei_send(int fd, erlang_pid* to, char* buf, int len)
+{
+ return ei_send_tmo(fd, to, buf, len, 0);
+}
+
+
+/*
+* Try to receive an Erlang message on a given socket. Returns
+* ERL_TICK, ERL_MSG, or ERL_ERROR. Sets `erl_errno' on ERL_ERROR and
+* ERL_TICK (to EAGAIN in the latter case).
+*/
+
+int ei_do_receive_msg(int fd, int staticbuffer_p,
+ erlang_msg* msg, ei_x_buff* x, unsigned ms)
+{
+ int msglen;
+ int i;
+
+ if (!(i=ei_recv_internal(fd, &x->buff, &x->buffsz, msg, &msglen,
+ staticbuffer_p, ms))) {
+ erl_errno = EAGAIN;
+ return ERL_TICK;
+ }
+ if (i<0) {
+ /* erl_errno set by ei_recv_internal() */
+ return ERL_ERROR;
+ }
+ if (staticbuffer_p && msglen > x->buffsz)
+ {
+ erl_errno = EMSGSIZE;
+ return ERL_ERROR;
+ }
+ x->index = x->buffsz;
+ switch (msg->msgtype) { /* FIXME are these all? */
+ case ERL_SEND:
+ case ERL_REG_SEND:
+ case ERL_LINK:
+ case ERL_UNLINK:
+ case ERL_GROUP_LEADER:
+ case ERL_EXIT:
+ case ERL_EXIT2:
+ case ERL_NODE_LINK:
+ return ERL_MSG;
+
+ default:
+ /*if (emsg->to) 'erl'_free_term(emsg->to);
+ if (emsg->from) 'erl'_free_term(emsg->from);
+ if (emsg->msg) 'erl'_free_term(emsg->msg);
+ emsg->to = NULL;
+ emsg->from = NULL;
+ emsg->msg = NULL;*/
+
+ erl_errno = EIO;
+ return ERL_ERROR;
+ }
+} /* do_receive_msg */
+
+
+int ei_receive_msg(int fd, erlang_msg* msg, ei_x_buff* x)
+{
+ return ei_do_receive_msg(fd, 1, msg, x, 0);
+}
+
+int ei_xreceive_msg(int fd, erlang_msg *msg, ei_x_buff *x)
+{
+ return ei_do_receive_msg(fd, 0, msg, x, 0);
+}
+
+int ei_receive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned ms)
+{
+ return ei_do_receive_msg(fd, 1, msg, x, ms);
+}
+
+int ei_xreceive_msg_tmo(int fd, erlang_msg *msg, ei_x_buff *x, unsigned ms)
+{
+ return ei_do_receive_msg(fd, 0, msg, x, ms);
+}
+
+/*
+* The RPC consists of two parts, send and receive.
+* Here is the send part !
+* { PidFrom, { call, Mod, Fun, Args, user }}
+*/
+/*
+* Now returns non-negative number for success, negative for failure.
+*/
+int ei_rpc_to(ei_cnode *ec, int fd, char *mod, char *fun,
+ const char *buf, int len)
+{
+
+ ei_x_buff x;
+ erlang_pid *self = ei_self(ec);
+ self->num = fd;
+
+ /* encode header */
+ ei_x_new_with_version(&x);
+ ei_x_encode_tuple_header(&x, 2); /* A */
+
+ self->num = fd;
+ ei_x_encode_pid(&x, self); /* A 1 */
+
+ ei_x_encode_tuple_header(&x, 5); /* B A 2 */
+ ei_x_encode_atom(&x, "call"); /* B 1 */
+ ei_x_encode_atom(&x, mod); /* B 2 */
+ ei_x_encode_atom(&x, fun); /* B 3 */
+ ei_x_append_buf(&x, buf, len); /* B 4 */
+ ei_x_encode_atom(&x, "user"); /* B 5 */
+
+ /* ei_x_encode_atom(&x,"user"); */
+ ei_send_reg_encoded(fd, self, "rex", x.buff, x.index);
+ ei_x_free(&x);
+
+ return 0;
+} /* rpc_to */
+
+ /*
+ * And here is the rpc receiving part. A negative
+ * timeout means 'infinity'. Returns either of: ERL_MSG,
+ * ERL_TICK, ERL_ERROR or ERL_TIMEOUT.
+*/
+int ei_rpc_from(ei_cnode *ec, int fd, int timeout, erlang_msg *msg,
+ ei_x_buff *x)
+{
+ fd_set readmask;
+ struct timeval tv;
+ struct timeval *t = NULL;
+
+ if (timeout >= 0) {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ t = &tv;
+ }
+
+ FD_ZERO(&readmask);
+ FD_SET(fd,&readmask);
+
+ switch (select(fd+1, &readmask, NULL, NULL, t)) {
+ case -1:
+ erl_errno = EIO;
+ return ERL_ERROR;
+
+ case 0:
+ erl_errno = ETIMEDOUT;
+ return ERL_TIMEOUT;
+
+ default:
+ if (FD_ISSET(fd, &readmask)) {
+ return ei_xreceive_msg(fd, msg, x);
+ } else {
+ erl_errno = EIO;
+ return ERL_ERROR;
+ }
+ }
+} /* rpc_from */
+
+ /*
+ * A true RPC. It return a NULL pointer
+ * in case of failure, otherwise a valid
+ * (ETERM *) pointer containing the reply
+ */
+int ei_rpc(ei_cnode* ec, int fd, char *mod, char *fun,
+ const char* inbuf, int inbuflen, ei_x_buff* x)
+{
+ int i, index;
+ ei_term t;
+ erlang_msg msg;
+ char rex[MAXATOMLEN+1];
+
+ if (ei_rpc_to(ec, fd, mod, fun, inbuf, inbuflen) < 0) {
+ return -1;
+ }
+ /* FIXME are we not to reply to the tick? */
+ while ((i = ei_rpc_from(ec, fd, ERL_NO_TIMEOUT, &msg, x)) == ERL_TICK)
+ ;
+
+ if (i == ERL_ERROR) return -1;
+ /*ep = 'erl'_element(2,emsg.msg);*/ /* {RPC_Tag, RPC_Reply} */
+ index = 0;
+ if (ei_decode_version(x->buff, &index, &i) < 0
+ || ei_decode_ei_term(x->buff, &index, &t) < 0)
+ return -1; /* FIXME ei_decode_version don't set erl_errno as before */
+ /* FIXME this is strange, we don't check correct "rex" atom
+ and we let it pass if not ERL_SMALL_TUPLE_EXT and arity == 2 */
+ if (t.ei_type == ERL_SMALL_TUPLE_EXT && t.arity == 2)
+ if (ei_decode_atom(x->buff, &index, rex) < 0)
+ return -1;
+ /* remove header */
+ x->index -= index;
+ memmove(x->buff, &x->buff[index], x->index);
+ return 0;
+}
+
+
+ /*
+ ** Handshake
+*/
+
+
+/* FROM RTP RFC 1889 (except that we use all bits, bug in RFC?) */
+static unsigned int md_32(char* string, int length)
+{
+ MD5_CTX ctx;
+ union {
+ char c[16];
+ unsigned x[4];
+ } digest;
+ ei_MD5Init(&ctx);
+ ei_MD5Update(&ctx, (unsigned char *) string,
+ (unsigned) length);
+ ei_MD5Final((unsigned char *) digest.c, &ctx);
+ return (digest.x[0] ^ digest.x[1] ^ digest.x[2] ^ digest.x[3]);
+}
+
+#if defined(__WIN32__)
+unsigned int gen_challenge(void)
+{
+ struct {
+ SYSTEMTIME tv;
+ DWORD cpu;
+ int pid;
+ } s;
+ GetSystemTime(&s.tv);
+ s.cpu = GetTickCount();
+ s.pid = getpid();
+ return md_32((char*) &s, sizeof(s));
+}
+
+#elif defined(VXWORKS)
+
+static unsigned int gen_challenge(void)
+{
+ struct {
+ struct timespec tv;
+ clock_t cpu;
+ int pid;
+ } s;
+ s.cpu = clock();
+ clock_gettime(CLOCK_REALTIME, &s.tv);
+ s.pid = getpid();
+ return md_32((char*) &s, sizeof(s));
+}
+
+#else /* some unix */
+
+static unsigned int gen_challenge(void)
+{
+ struct {
+ struct timeval tv;
+ clock_t cpu;
+ pid_t pid;
+ u_long hid;
+ uid_t uid;
+ gid_t gid;
+ struct utsname name;
+ } s;
+
+ gettimeofday(&s.tv, 0);
+ uname(&s.name);
+ s.cpu = clock();
+ s.pid = getpid();
+ s.hid = gethostid();
+ s.uid = getuid();
+ s.gid = getgid();
+
+ return md_32((char*) &s, sizeof(s));
+}
+#endif
+
+static void gen_digest(unsigned challenge, char cookie[],
+ unsigned char digest[16])
+{
+ MD5_CTX c;
+
+ char chbuf[21];
+
+ sprintf(chbuf,"%u", challenge);
+ ei_MD5Init(&c);
+ ei_MD5Update(&c, (unsigned char *) cookie,
+ (unsigned) strlen(cookie));
+ ei_MD5Update(&c, (unsigned char *) chbuf,
+ (unsigned) strlen(chbuf));
+ ei_MD5Final(digest, &c);
+}
+
+
+static char *hex(char digest[16], char buff[33])
+{
+ static char tab[] = "0123456789abcdef";
+ unsigned char *d = (unsigned char *) digest;
+ //static char buff[sizeof(digest)*2 + 1];
+ char *p = buff;
+ int i;
+
+ for (i = 0; i < sizeof(digest); ++i) {
+ *p++ = tab[(int)((*d) >> 4)];
+ *p++ = tab[(int)((*d++) & 0xF)];
+ }
+ *p = '\0';
+ return buff;
+}
+
+static int read_2byte_package(int fd, char **buf, int *buflen,
+ int *is_static, unsigned ms)
+{
+ unsigned char nbuf[2];
+ unsigned char *x = nbuf;
+ unsigned len;
+ int res;
+
+ if((res = ei_read_fill_t(fd, (char *)nbuf, 2, ms)) != 2) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+ len = get16be(x);
+
+ if (len > *buflen) {
+ if (*is_static) {
+ char *tmp = malloc(len);
+ if (!tmp) {
+ erl_errno = ENOMEM;
+ return -1;
+ }
+ *buf = tmp;
+ *is_static = 0;
+ *buflen = len;
+ } else {
+ char *tmp = realloc(*buf, len);
+ if (!tmp) {
+ erl_errno = ENOMEM;
+ return -1;
+ }
+ *buf = tmp;
+ *buflen = len;
+ }
+ }
+ if ((res = ei_read_fill_t(fd, *buf, len, ms)) != len) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+ return len;
+}
+
+
+static int send_status(int fd, char *status, unsigned ms)
+{
+ char *buf, *s;
+ char dbuf[DEFBUF_SIZ];
+ int siz = strlen(status) + 1 + 2;
+ int res;
+
+ buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
+ if (!buf) {
+ erl_errno = ENOMEM;
+ return -1;
+ }
+ s = buf;
+ put16be(s,siz - 2);
+ put8(s, 's');
+ memcpy(s, status, strlen(status));
+ if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) {
+ EI_TRACE_ERR0("send_status","-> SEND_STATUS socket write failed");
+ if (buf != dbuf)
+ free(buf);
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+ EI_TRACE_CONN1("send_status","-> SEND_STATUS (%s)",status);
+
+ if (buf != dbuf)
+ free(buf);
+ return 0;
+}
+
+static int recv_status(int fd, unsigned ms)
+{
+ char dbuf[DEFBUF_SIZ];
+ char *buf = dbuf;
+ int is_static = 1;
+ int buflen = DEFBUF_SIZ;
+ int rlen;
+
+ if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) <= 0) {
+ EI_TRACE_ERR1("recv_status",
+ "<- RECV_STATUS socket read failed (%d)", rlen);
+ goto error;
+ }
+ if (rlen == 3 && buf[0] == 's' && buf[1] == 'o' &&
+ buf[2] == 'k') {
+ if (!is_static)
+ free(buf);
+ EI_TRACE_CONN0("recv_status","<- RECV_STATUS (ok)");
+ return 0;
+ }
+error:
+ if (!is_static)
+ free(buf);
+ return -1;
+}
+
+/* FIXME fix the signed/unsigned mess..... */
+
+static int send_name_or_challenge(int fd, char *nodename,
+ int f_chall,
+ unsigned challenge,
+ unsigned version,
+ unsigned ms)
+{
+ char *buf;
+ unsigned char *s;
+ char dbuf[DEFBUF_SIZ];
+ int siz = 2 + 1 + 2 + 4 + strlen(nodename);
+ const char* function[] = {"SEND_NAME", "SEND_CHALLENGE"};
+ int res;
+
+ if (f_chall)
+ siz += 4;
+ buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
+ if (!buf) {
+ erl_errno = ENOMEM;
+ return -1;
+ }
+ s = (unsigned char *)buf;
+ put16be(s,siz - 2);
+ put8(s, 'n');
+ put16be(s, version);
+ put32be(s, (DFLAG_EXTENDED_REFERENCES
+ | DFLAG_EXTENDED_PIDS_PORTS
+ | DFLAG_FUN_TAGS
+ | DFLAG_NEW_FUN_TAGS));
+ if (f_chall)
+ put32be(s, challenge);
+ memcpy(s, nodename, strlen(nodename));
+
+ if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) {
+ EI_TRACE_ERR1("send_name_or_challenge",
+ "-> %s socket write failed", function[f_chall]);
+ if (buf != dbuf)
+ free(buf);
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+
+ if (buf != dbuf)
+ free(buf);
+ return 0;
+}
+
+static int recv_challenge(int fd, unsigned *challenge,
+ unsigned *version,
+ unsigned *flags, ErlConnect *namebuf, unsigned ms)
+{
+ char dbuf[DEFBUF_SIZ];
+ char *buf = dbuf;
+ int is_static = 1;
+ int buflen = DEFBUF_SIZ;
+ int rlen;
+ char *s;
+ struct sockaddr_in sin;
+ socklen_t sin_len = sizeof(sin);
+ char tag;
+
+ erl_errno = EIO; /* Default */
+
+ if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) <= 0) {
+ EI_TRACE_ERR1("recv_challenge",
+ "<- RECV_CHALLENGE socket read failed (%d)",rlen);
+ goto error;
+ }
+ if ((rlen - 11) > MAXNODELEN) {
+ EI_TRACE_ERR1("recv_challenge",
+ "<- RECV_CHALLENGE nodename too long (%d)",rlen - 11);
+ goto error;
+ }
+ s = buf;
+ if ((tag = get8(s)) != 'n') {
+ EI_TRACE_ERR2("recv_challenge",
+ "<- RECV_CHALLENGE incorrect tag, "
+ "expected 'n' got '%c' (%u)",tag,tag);
+ goto error;
+ }
+ *version = get16be(s);
+ *flags = get32be(s);
+ *challenge = get32be(s);
+
+ if (!(*flags & DFLAG_EXTENDED_REFERENCES)) {
+ EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE peer cannot "
+ "handle extended references");
+ goto error;
+ }
+
+ if (!(*flags & DFLAG_EXTENDED_PIDS_PORTS)
+ && !ei_internal_use_r9_pids_ports()) {
+ EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE peer cannot "
+ "handle extended pids and ports");
+ erl_errno = EIO;
+ goto error;
+ }
+
+
+ if (getpeername(fd, (struct sockaddr *) &sin, &sin_len) < 0) {
+ EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE can't get peername");
+ erl_errno = errno;
+ goto error;
+ }
+ memcpy(namebuf->ipadr, &(sin.sin_addr.s_addr),
+ sizeof(sin.sin_addr.s_addr));
+ memcpy(namebuf->nodename, s, rlen - 11);
+ namebuf->nodename[rlen - 11] = '\0';
+ if (!is_static)
+ free(buf);
+ EI_TRACE_CONN4("recv_challenge","<- RECV_CHALLENGE (ok) node = %s, "
+ "version = %u, "
+ "flags = %u, "
+ "challenge = %d",
+ namebuf->nodename,
+ *version,
+ *flags,
+ *challenge
+ );
+ erl_errno = 0;
+ return 0;
+error:
+ if (!is_static)
+ free(buf);
+ return -1;
+}
+
+static int send_challenge_reply(int fd, unsigned char digest[16],
+ unsigned challenge, unsigned ms)
+{
+ char *s;
+ char buf[DEFBUF_SIZ];
+ int siz = 2 + 1 + 4 + 16;
+ int res;
+
+ s = buf;
+ put16be(s,siz - 2);
+ put8(s, 'r');
+ put32be(s, challenge);
+ memcpy(s, digest, 16);
+
+ if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) {
+ EI_TRACE_ERR0("send_challenge_reply",
+ "-> SEND_CHALLENGE_REPLY socket write failed");
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+
+ if (ei_tracelevel >= 3) {
+ char buffer[33];
+ EI_TRACE_CONN2("send_challenge_reply",
+ "-> SEND_CHALLENGE_REPLY (ok) challenge = %d, digest = %s",
+ challenge,hex((char*)digest, buffer));
+ }
+ return 0;
+}
+
+static int recv_challenge_reply (int fd,
+ unsigned our_challenge,
+ char cookie[],
+ unsigned *her_challenge,
+ unsigned ms)
+{
+ char dbuf[DEFBUF_SIZ];
+ char *buf = dbuf;
+ int is_static = 1;
+ int buflen = DEFBUF_SIZ;
+ int rlen;
+ char *s;
+ char tag;
+ char her_digest[16], expected_digest[16];
+
+ erl_errno = EIO; /* Default */
+
+ if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) != 21) {
+ EI_TRACE_ERR1("recv_challenge_reply",
+ "<- RECV_CHALLENGE_REPLY socket read failed (%d)",rlen);
+ goto error;
+ }
+
+ s = buf;
+ if ((tag = get8(s)) != 'r') {
+ EI_TRACE_ERR2("recv_challenge_reply",
+ "<- RECV_CHALLENGE_REPLY incorrect tag, "
+ "expected 'r' got '%c' (%u)",tag,tag);
+ goto error;
+ }
+ *her_challenge = get32be(s);
+ memcpy(her_digest, s, 16);
+ gen_digest(our_challenge, cookie, (unsigned char*)expected_digest);
+ if (memcmp(her_digest, expected_digest, 16)) {
+ EI_TRACE_ERR0("recv_challenge_reply",
+ "<- RECV_CHALLENGE_REPLY authorization failure");
+ goto error;
+ }
+ if (!is_static)
+ free(buf);
+
+
+ if (ei_tracelevel >= 3) {
+ char buffer[33];
+ EI_TRACE_CONN2("recv_challenge_reply",
+ "<- RECV_CHALLENGE_REPLY (ok) challenge = %u, digest = %s",
+ *her_challenge,hex(her_digest,buffer));
+ }
+ erl_errno = 0;
+ return 0;
+
+error:
+ if (!is_static)
+ free(buf);
+ return -1;
+}
+
+static int send_challenge_ack(int fd, unsigned char digest[16], unsigned ms)
+{
+ char *s;
+ char buf[DEFBUF_SIZ];
+ int siz = 2 + 1 + 16;
+ int res;
+
+ s = buf;
+
+ put16be(s,siz - 2);
+ put8(s, 'a');
+ memcpy(s, digest, 16);
+
+ if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) {
+ EI_TRACE_ERR0("recv_challenge_reply",
+ "-> SEND_CHALLENGE_ACK socket write failed");
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+
+ if (ei_tracelevel >= 3) {
+ char buffer[33];
+ EI_TRACE_CONN1("recv_challenge_reply",
+ "-> SEND_CHALLENGE_ACK (ok) digest = %s",hex((char *)digest,buffer));
+ }
+
+ return 0;
+}
+
+static int recv_challenge_ack(int fd,
+ unsigned our_challenge,
+ char cookie[], unsigned ms)
+{
+ char dbuf[DEFBUF_SIZ];
+ char *buf = dbuf;
+ int is_static = 1;
+ int buflen = DEFBUF_SIZ;
+ int rlen;
+ char *s;
+ char tag;
+ char her_digest[16], expected_digest[16];
+
+ erl_errno = EIO; /* Default */
+
+ if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) != 17) {
+ EI_TRACE_ERR1("recv_challenge_ack",
+ "<- RECV_CHALLENGE_ACK socket read failed (%d)",rlen);
+ goto error;
+ }
+
+ s = buf;
+ if ((tag = get8(s)) != 'a') {
+ EI_TRACE_ERR2("recv_challenge_ack",
+ "<- RECV_CHALLENGE_ACK incorrect tag, "
+ "expected 'a' got '%c' (%u)",tag,tag);
+ goto error;
+ }
+ memcpy(her_digest, s, 16);
+ gen_digest(our_challenge, cookie, (unsigned char *)expected_digest);
+ if (memcmp(her_digest, expected_digest, 16)) {
+ EI_TRACE_ERR0("recv_challenge_ack",
+ "<- RECV_CHALLENGE_ACK authorization failure");
+ goto error;
+ }
+ if (!is_static)
+ free(buf);
+
+ if (ei_tracelevel >= 3) {
+ char buffer[33];
+ EI_TRACE_CONN1("recv_challenge_ack",
+ "<- RECV_CHALLENGE_ACK (ok) digest = %s",hex(her_digest,buffer));
+ }
+ erl_errno = 0;
+ return 0;
+
+error:
+ if (!is_static)
+ free(buf);
+ return -1;
+}
+
+static int send_name(int fd, char *nodename, unsigned version, unsigned ms)
+{
+ return send_name_or_challenge(fd, nodename, 0, 0, version, ms);
+}
+
+static int send_challenge(int fd, char *nodename,
+ unsigned challenge, unsigned version, unsigned ms)
+{
+ return send_name_or_challenge(fd, nodename, 1, challenge, version, ms);
+}
+
+static int recv_name(int fd,
+ unsigned *version,
+ unsigned *flags, ErlConnect *namebuf, unsigned ms)
+{
+ char dbuf[DEFBUF_SIZ];
+ char *buf = dbuf;
+ int is_static = 1;
+ int buflen = DEFBUF_SIZ;
+ int rlen;
+ char *s;
+ struct sockaddr_in sin;
+ socklen_t sin_len = sizeof(sin);
+ char tag;
+
+ erl_errno = EIO; /* Default */
+
+ if ((rlen = read_2byte_package(fd, &buf, &buflen, &is_static, ms)) <= 0) {
+ EI_TRACE_ERR1("recv_name","<- RECV_NAME socket read failed (%d)",rlen);
+ goto error;
+ }
+ if ((rlen - 7) > MAXNODELEN) {
+ EI_TRACE_ERR1("recv_name","<- RECV_NAME nodename too long (%d)",rlen-7);
+ goto error;
+ }
+ s = buf;
+ tag = get8(s);
+ if (tag != 'n') {
+ EI_TRACE_ERR2("recv_name","<- RECV_NAME incorrect tag, "
+ "expected 'n' got '%c' (%u)",tag,tag);
+ goto error;
+ }
+ *version = get16be(s);
+ *flags = get32be(s);
+
+ if (!(*flags & DFLAG_EXTENDED_REFERENCES)) {
+ EI_TRACE_ERR0("recv_name","<- RECV_NAME peer cannot handle"
+ "extended references");
+ goto error;
+ }
+
+ if (!(*flags & DFLAG_EXTENDED_PIDS_PORTS)
+ && !ei_internal_use_r9_pids_ports()) {
+ EI_TRACE_ERR0("recv_name","<- RECV_NAME peer cannot "
+ "handle extended pids and ports");
+ erl_errno = EIO;
+ goto error;
+ }
+
+ if (getpeername(fd, (struct sockaddr *) &sin, &sin_len) < 0) {
+ EI_TRACE_ERR0("recv_name","<- RECV_NAME can't get peername");
+ erl_errno = errno;
+ goto error;
+ }
+ memcpy(namebuf->ipadr, &(sin.sin_addr.s_addr),
+ sizeof(sin.sin_addr.s_addr));
+ memcpy(namebuf->nodename, s, rlen - 7);
+ namebuf->nodename[rlen - 7] = '\0';
+ if (!is_static)
+ free(buf);
+ EI_TRACE_CONN3("recv_name",
+ "<- RECV_NAME (ok) node = %s, version = %u, flags = %u",
+ namebuf->nodename,*version,*flags);
+ erl_errno = 0;
+ return 0;
+
+error:
+ if (!is_static)
+ free(buf);
+ return -1;
+}
+
+/***************************************************************************
+ *
+ * Returns 1 on success and 0 on failure.
+ *
+ ***************************************************************************/
+
+
+/* size is the buffer size, e.i. string length + 1 */
+
+static int get_home(char *buf, int size)
+{
+ char* homedrive;
+ char* homepath;
+
+#ifdef __WIN32__
+ homedrive = getenv("HOMEDRIVE");
+ homepath = getenv("HOMEPATH");
+#else
+ homedrive = "";
+ homepath = getenv("HOME");
+#endif
+
+ if (!homedrive || !homepath) {
+ buf[0] = '.';
+ buf[1] = '\0';
+ return 1;
+ } else if (strlen(homedrive)+strlen(homepath) < size-1) {
+ strcpy(buf, homedrive);
+ strcat(buf, homepath);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int get_cookie(char *buf, int bufsize)
+{
+ char fname[EI_MAX_HOME_PATH + sizeof(COOKIE_FILE) + 1];
+ int fd;
+ int len;
+ unsigned char next_c;
+
+ if (!get_home(fname, EI_MAX_HOME_PATH+1)) {
+ fprintf(stderr,"<ERROR> get_cookie: too long path to home");
+ return 0;
+ }
+
+ strcat(fname, COOKIE_FILE);
+ if ((fd = open(fname, O_RDONLY, 0777)) < 0) {
+ fprintf(stderr,"<ERROR> get_cookie: can't open cookie file");
+ return 0;
+ }
+
+ if ((len = read(fd, buf, bufsize-1)) < 0) {
+ fprintf(stderr,"<ERROR> get_cookie: reading cookie file");
+ close(fd);
+ return 0;
+ }
+
+ /* If more to read it is too long. Not 100% correct test but will do. */
+ if (read(fd, &next_c, 1) > 0 && !isspace(next_c)) {
+ fprintf(stderr,"<ERROR> get_cookie: cookie in %s is too long",fname);
+ close(fd);
+ return 0;
+ }
+
+ close(fd);
+
+ /* Remove all newlines after the first newline */
+ buf[len] = '\0'; /* Terminate string */
+ len = strcspn(buf,"\r\n");
+ buf[len] = '\0'; /* Terminate string again */
+
+ return 1; /* Success! */
+}
diff --git a/lib/erl_interface/src/connect/ei_connect_int.h b/lib/erl_interface/src/connect/ei_connect_int.h
new file mode 100644
index 0000000000..9926f799df
--- /dev/null
+++ b/lib/erl_interface/src/connect/ei_connect_int.h
@@ -0,0 +1,114 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2001-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%
+ */
+/*
+* Purpose: Connect to any node at any host. (EI version)
+*/
+
+/* FIXME far to many includes here... */
+
+/* some send() functions use buffer on heap for "small" messages */
+/* messages larger than this require call to malloc() */
+
+#ifndef _EI_CONNECT_H
+#define _EI_CONNECT_H
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#ifdef __WIN32__
+#include <winsock2.h>
+#include <windows.h>
+#include <winbase.h>
+
+#elif VXWORKS
+#include <vxWorks.h>
+#include <hostLib.h>
+#include <selectLib.h>
+#include <ifLib.h>
+#include <sockLib.h>
+#include <taskLib.h>
+#include <inetLib.h>
+#include <ioLib.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <timers.h>
+
+#define getpid() taskIdSelf()
+extern int h_errno;
+
+#else /* some other unix */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/times.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/utsname.h> /* for gen_challenge (NEED FIX?) */
+#endif
+
+/* FIXME remove duplicate defintions */
+
+#define DEFBUF_SIZ 100
+
+/* rpc_from() uses a buffer this size */
+#ifndef MAX_RECEIVE_BUF
+# define MAX_RECEIVE_BUF 32*1024
+#endif
+
+/* Distribution capability flags */
+#define DFLAG_PUBLISHED 1
+#define DFLAG_ATOM_CACHE 2
+#define DFLAG_EXTENDED_REFERENCES 4
+#define DFLAG_DIST_MONITOR 8
+#define DFLAG_FUN_TAGS 16
+#define DFLAG_NEW_FUN_TAGS 0x80
+#define DFLAG_EXTENDED_PIDS_PORTS 0x100
+
+ei_cnode *ei_fd_to_cnode(int fd);
+int ei_distversion(int fd);
+const char* ei_getfdcookie(int fd);
+short ei_thiscreation(const ei_cnode* ec);
+const char *ei_thiscookie(const ei_cnode* ec);
+
+int ei_do_receive_msg(int fd, int staticbuffer_p,
+ erlang_msg* msg, ei_x_buff* x, unsigned ms);
+
+#endif /* _EI_CONNECT_H */
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 */
+
diff --git a/lib/erl_interface/src/connect/ei_resolve.h b/lib/erl_interface/src/connect/ei_resolve.h
new file mode 100644
index 0000000000..4cb8aff338
--- /dev/null
+++ b/lib/erl_interface/src/connect/ei_resolve.h
@@ -0,0 +1,24 @@
+/*
+ * %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%
+ */
+#ifndef _EI_RESOLVE_H
+#define _EI_RESOLVE_H
+
+void ei_init_resolve(void);
+
+#endif /* _EI_RESOLVE_H */
diff --git a/lib/erl_interface/src/connect/eirecv.c b/lib/erl_interface/src/connect/eirecv.c
new file mode 100644
index 0000000000..51fc32d65c
--- /dev/null
+++ b/lib/erl_interface/src/connect/eirecv.c
@@ -0,0 +1,280 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-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%
+ */
+#ifdef __WIN32__
+# include <winsock2.h>
+# include <windows.h>
+# include <winbase.h>
+#else /* Unix/VxWorks */
+# include <unistd.h>
+#endif
+
+/* common */
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "eidef.h"
+#include "eiext.h"
+#include "eirecv.h"
+#include "ei_portio.h"
+#include "ei_internal.h"
+#include "putget.h"
+#include "ei_trace.h"
+#include "show_msg.h"
+
+#include <errno.h>
+
+#define EIRECVBUF 2048 /* largest possible header is approx 1300 bytes */
+
+/* length (4), PASS_THOUGH (1), header, message */
+int
+ei_recv_internal (int fd,
+ char **mbufp, int *bufsz,
+ erlang_msg *msg, int *msglenp,
+ int staticbufp, unsigned ms)
+{
+ char header[EIRECVBUF];
+ char *s=header;
+ char *mbuf=*mbufp;
+ int len = 0;
+ int msglen = 0;
+ int bytesread = 0;
+ int remain;
+ int arity;
+ int version;
+ int index = 0;
+ int i = 0;
+ int res;
+ int show_this_msg = 0;
+
+ /* get length field */
+ if ((res = ei_read_fill_t(fd, header, 4, ms)) != 4)
+ {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+ len = get32be(s);
+
+ /* got tick - respond and return */
+ if (!len) {
+ char tock[] = {0,0,0,0};
+ ei_write_fill_t(fd, tock, sizeof(tock), ms); /* Failure no problem */
+ *msglenp = 0;
+ return 0; /* maybe flag ERL_EAGAIN [sverkerw] */
+ }
+
+ /* turn off tracing on each receive. it will be turned back on if
+ * we receive a trace token.
+ */
+ ei_trace(-1,NULL);
+
+ /* read enough to get at least entire header */
+ bytesread = (len > EIRECVBUF ? EIRECVBUF : len);
+ if ((i = ei_read_fill_t(fd,header,bytesread,ms)) != bytesread) {
+ erl_errno = (i == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+
+ /* now decode header */
+ /* pass-through, version, control tuple header, control message type */
+ s = header;
+ index = 1;
+ if ((get8(s) != ERL_PASS_THROUGH)
+ || ei_decode_version(header,&index,&version)
+ || (version != ERL_VERSION_MAGIC)
+ || ei_decode_tuple_header(header,&index,&arity)
+ || ei_decode_long(header,&index,&msg->msgtype))
+ {
+ erl_errno = EIO; /* Maybe another code for decoding errors */
+ return -1;
+ }
+
+ switch (msg->msgtype) {
+ case ERL_SEND: /* { SEND, Cookie, ToPid } */
+ if (ei_tracelevel > 0) show_this_msg = 1;
+ if (ei_decode_atom(header,&index,msg->cookie)
+ || ei_decode_pid(header,&index,&msg->to))
+ {
+ erl_errno = EIO;
+ return -1;
+ }
+
+ break;
+
+ case ERL_REG_SEND: /* { REG_SEND, From, Cookie, ToName } */
+ if (ei_tracelevel > 0) show_this_msg = 1;
+ if (ei_decode_pid(header,&index,&msg->from)
+ || ei_decode_atom(header,&index,msg->cookie)
+ || ei_decode_atom(header,&index,msg->toname))
+ {
+ erl_errno = EIO;
+ return -1;
+ }
+
+ /* actual message is remaining part of headerbuf, plus any unread bytes */
+ break;
+
+ case ERL_LINK: /* { LINK, From, To } */
+ case ERL_UNLINK: /* { UNLINK, From, To } */
+ case ERL_GROUP_LEADER: /* { GROUP_LEADER, From, To } */
+ if (ei_tracelevel > 1) show_this_msg = 1;
+ if (ei_decode_pid(header,&index,&msg->from)
+ || ei_decode_pid(header,&index,&msg->to))
+ {
+ erl_errno = EIO;
+ return -1;
+ }
+
+ break;
+
+ case ERL_EXIT: /* { EXIT, From, To, Reason } */
+ case ERL_EXIT2: /* { EXIT2, From, To, Reason } */
+ if (ei_tracelevel > 1) show_this_msg = 1;
+ if (ei_decode_pid(header,&index,&msg->from)
+ || ei_decode_pid(header,&index,&msg->to))
+ {
+ erl_errno = EIO;
+ return -1;
+ }
+
+ break;
+
+ case ERL_SEND_TT: /* { SEND_TT, Cookie, ToPid, TraceToken } */
+ if (ei_tracelevel > 0) show_this_msg = 1;
+ if (ei_decode_atom(header,&index,msg->cookie)
+ || ei_decode_pid(header,&index,&msg->to)
+ || ei_decode_trace(header,&index,&msg->token))
+ {
+ erl_errno = EIO;
+ return -1;
+ }
+
+ ei_trace(1,&msg->token); /* turn on tracing */
+ break;
+
+ case ERL_REG_SEND_TT: /* { REG_SEND_TT, From, Cookie, ToName, TraceToken } */
+ if (ei_tracelevel > 0) show_this_msg = 1;
+ if (ei_decode_pid(header,&index,&msg->from)
+ || ei_decode_atom(header,&index,msg->cookie)
+ || ei_decode_atom(header,&index,msg->toname)
+ || ei_decode_trace(header,&index,&msg->token))
+ {
+ erl_errno = EIO;
+ return -1;
+ }
+
+ ei_trace(1,&msg->token); /* turn on tracing */
+ break;
+
+ case ERL_EXIT_TT: /* { EXIT_TT, From, To, TraceToken, Reason } */
+ case ERL_EXIT2_TT: /* { EXIT2_TT, From, To, TraceToken, Reason } */
+ if (ei_tracelevel > 1) show_this_msg = 1;
+ if (ei_decode_pid(header,&index,&msg->from)
+ || ei_decode_pid(header,&index,&msg->to)
+ || ei_decode_trace(header,&index,&msg->token))
+ {
+ erl_errno = EIO;
+ return -1;
+ }
+
+ ei_trace(1,&msg->token); /* turn on tracing */
+ break;
+
+ case ERL_NODE_LINK: /* { NODE_LINK } */
+ if (ei_tracelevel > 1) show_this_msg = 1;
+ break;
+
+ default:
+ /* unknown type, just put any remaining bytes into buffer */
+ break;
+ }
+
+ /* actual message is remaining part of headerbuf, plus any unread bytes */
+ msglen = len - index; /* message size (payload) */
+ remain = len - bytesread; /* bytes left to read */
+
+ /* if callers buffer is too small, we flush in the rest of the
+ * message and discard it, unless we know that we can reallocate
+ * the buffer in which case we do that and read the message.
+ */
+ if (msglen > *bufsz) {
+ if (staticbufp) {
+ int sz = EIRECVBUF;
+ /* flush in rest of packet */
+ while (remain > 0) {
+ if (remain < sz) sz = remain;
+ if ((i=ei_read_fill_t(fd,header,sz,ms)) <= 0) break;
+ remain -= i;
+ }
+ erl_errno = EMSGSIZE;
+ return -1;
+ }
+ else {
+ /* Dynamic buffer --- grow it. */
+#ifdef DEBUG
+ fprintf(stderr, "Growing buffer from %d bytes to %d bytes\n",
+ *bufsz, msglen);
+#endif
+ if ((mbuf = realloc(*mbufp, msglen)) == NULL)
+ {
+ erl_errno = ENOMEM;
+ return -1;
+ }
+
+ *mbufp = mbuf;
+ *bufsz = msglen;
+ }
+ }
+
+ /* move remaining bytes to callers buffer */
+ memmove(mbuf,header+index,bytesread-index);
+
+ /* let the caller know how big the message is in his buffer */
+ *msglenp = msglen;
+
+ /* read the rest of the message into callers buffer */
+ if (remain > 0) {
+ if ((i = ei_read_fill_t(fd,mbuf+bytesread-index,remain,ms)) != remain) {
+ *msglenp = bytesread-index+1; /* actual bytes in users buffer */
+ erl_errno = (i == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+ }
+
+ if (show_this_msg)
+ ei_show_recmsg(stderr,msg,mbuf);
+
+ /* the caller only sees "untraced" message types */
+ /* the trace token is buried in the message struct */
+ if (msg->msgtype > 10) msg->msgtype -= 10;
+
+ return msg->msgtype;
+}
+
+int ei_receive_encoded(int fd, char **mbufp, int *bufsz,
+ erlang_msg *msg, int *msglen)
+{
+ return ei_recv_internal(fd, mbufp, bufsz, msg, msglen, 0, 0);
+}
+
+int ei_receive_encoded_tmo(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen, unsigned ms)
+{
+ return ei_recv_internal(fd, mbufp, bufsz, msg, msglen, 0, ms);
+}
+
diff --git a/lib/erl_interface/src/connect/eirecv.h b/lib/erl_interface/src/connect/eirecv.h
new file mode 100644
index 0000000000..b66eaeeb56
--- /dev/null
+++ b/lib/erl_interface/src/connect/eirecv.h
@@ -0,0 +1,26 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-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%
+ */
+#ifndef _EIRECV_H
+#define _EIRECV_H
+
+/* Internal interface */
+int ei_recv_internal(int fd, char **mbufp, int *bufsz, erlang_msg *msg,
+ int *msglenp, int staticbufp, unsigned ms);
+
+#endif /* _EIRECV_H */
diff --git a/lib/erl_interface/src/connect/eisend.h b/lib/erl_interface/src/connect/eisend.h
new file mode 100644
index 0000000000..d83caf1352
--- /dev/null
+++ b/lib/erl_interface/src/connect/eisend.h
@@ -0,0 +1,41 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-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%
+ */
+#ifndef _EISEND_H
+#define _EISEND_H
+
+/* FIXME strange, is this for debugging?! */
+#define EI_HAVE_TIMEOUT 1
+
+int ei_send_exit(int fd, const erlang_pid *from, const erlang_pid *to,
+ const char *msg);
+int ei_send_exit_tmo(int fd, const erlang_pid *from,
+ const erlang_pid *to,
+ const char *msg, unsigned ms);
+
+/* FIXME ei_send_*() functions not used */
+#if 0
+int ei_send_link(int fd, const erlang_pid *from, const erlang_pid *to);
+int ei_send_unlink(int fd, const erlang_pid *from, const erlang_pid *to);
+int ei_send_link_tmo(int fd, const erlang_pid *from,
+ const erlang_pid *to, unsigned ms);
+int ei_send_unlink_tmo(int fd, const erlang_pid *from,
+ const erlang_pid *to, unsigned ms);
+#endif /* Not used */
+
+#endif /* _EISEND_H */
diff --git a/lib/erl_interface/src/connect/send.c b/lib/erl_interface/src/connect/send.c
new file mode 100644
index 0000000000..cd832db4ea
--- /dev/null
+++ b/lib/erl_interface/src/connect/send.c
@@ -0,0 +1,125 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-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%
+ */
+
+#ifdef __WIN32__
+
+# include <winsock2.h>
+# include <windows.h>
+# include <winbase.h>
+
+#elif VXWORKS
+
+# include <sys/types.h>
+# include <unistd.h>
+# include <sysLib.h>
+# include <tickLib.h>
+
+#else /* unix */
+
+# include <sys/types.h>
+# include <unistd.h>
+# include <sys/uio.h>
+
+#endif
+
+#include <string.h>
+
+#include "eidef.h"
+#include "eiext.h"
+#include "eisend.h"
+#include "putget.h"
+#include "ei_connect_int.h"
+#include "ei_internal.h"
+#include "ei_trace.h"
+#include "ei_portio.h"
+#include "show_msg.h"
+
+
+int ei_send_encoded_tmo(int fd, const erlang_pid *to,
+ char *msg, int msglen, unsigned ms)
+{
+ char *s, header[1200]; /* see size calculation below */
+ erlang_trace *token = NULL;
+ int index = 5; /* reserve 5 bytes for control message */
+ int res;
+#ifdef HAVE_WRITEV
+ struct iovec v[2];
+#endif
+
+ /* are we tracing? */
+ /* check that he can receive trace tokens first */
+ if (ei_distversion(fd) > 0) token = ei_trace(0,NULL);
+
+ /* header = SEND, cookie, to max sizes: */
+ ei_encode_version(header,&index); /* 1 */
+ if (token) {
+ ei_encode_tuple_header(header,&index,4); /* 2 */
+ ei_encode_long(header,&index,ERL_SEND_TT); /* 2 */
+ } else {
+ ei_encode_tuple_header(header,&index,3);
+ ei_encode_long(header,&index,ERL_SEND);
+ }
+ ei_encode_atom(header,&index,ei_getfdcookie(fd)); /* 258 */
+ ei_encode_pid(header,&index,to); /* 268 */
+
+ if (token) ei_encode_trace(header,&index,token); /* 534 */
+
+ /* control message (precedes header actually) */
+ /* length = 1 ('p') + header len + message len */
+ s = header;
+ put32be(s, index + msglen - 4); /* 4 */
+ put8(s, ERL_PASS_THROUGH); /* 1 */
+ /*** sum: 1070 */
+
+ /* FIXME incorrect level */
+ if (ei_tracelevel > 0)
+ ei_show_sendmsg(stderr,header,msg);
+
+#ifdef HAVE_WRITEV
+
+ v[0].iov_base = (char *)header;
+ v[0].iov_len = index;
+ v[1].iov_base = (char *)msg;
+ v[1].iov_len = msglen;
+
+ if ((res = ei_writev_fill_t(fd,v,2,ms)) != index+msglen) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+
+#else /* !HAVE_WRITEV */
+
+ if ((res = ei_write_fill_t(fd,header,index,ms)) != index) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+ if ((res = ei_write_fill_t(fd,msg,msglen,ms)) != msglen) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+
+#endif /* !HAVE_WRITEV */
+
+ return 0;
+}
+
+int ei_send_encoded(int fd, const erlang_pid *to, char *msg, int msglen)
+{
+ return ei_send_encoded_tmo(fd, to, msg, msglen, 0);
+}
diff --git a/lib/erl_interface/src/connect/send_exit.c b/lib/erl_interface/src/connect/send_exit.c
new file mode 100644
index 0000000000..098797c96d
--- /dev/null
+++ b/lib/erl_interface/src/connect/send_exit.c
@@ -0,0 +1,101 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-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%
+ */
+#ifdef __WIN32__
+# include <winsock2.h>
+# include <windows.h>
+# include <winbase.h>
+#else /* Unix/VxWorks */
+#include <unistd.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include "eidef.h"
+#include "eiext.h"
+#include "eisend.h"
+#include "ei_connect_int.h"
+#include "ei_trace.h"
+#include "ei_internal.h"
+#include "putget.h"
+#include "ei_portio.h"
+#include "show_msg.h"
+
+/* use this to break a link */
+int ei_send_exit(int fd, const erlang_pid *from,
+ const erlang_pid *to, const char *reason)
+{
+ return ei_send_exit_tmo(fd,from,to,reason,0);
+}
+
+
+int ei_send_exit_tmo(int fd, const erlang_pid *from, const erlang_pid *to,
+ const char *reason, unsigned ms)
+{
+ char sbuf[EISMALLBUF];
+ erlang_trace *token = NULL;
+ char *dbuf = NULL;
+ char *msgbuf;
+ char *s;
+ int index = 0;
+ int len = strlen(reason) + 1080; /* see below */
+
+ if (len > EISMALLBUF)
+ if (!(dbuf = malloc(len)))
+ return -1;
+ msgbuf = (dbuf ? dbuf : sbuf);
+
+
+ /* are we tracing? */
+ /* check that he can receive trace tokens first */
+ if (ei_distversion(fd) > 0) token = ei_trace(0,NULL);
+
+ index = 5; /* max sizes: */
+ ei_encode_version(msgbuf,&index); /* 1 */
+ if (token) {
+ ei_encode_tuple_header(msgbuf,&index,5); /* 2 */
+ ei_encode_long(msgbuf,&index,ERL_EXIT_TT); /* 2 */
+ }
+ else {
+ ei_encode_tuple_header(msgbuf,&index,4);
+ ei_encode_long(msgbuf,&index,ERL_EXIT);
+ }
+ ei_encode_pid(msgbuf,&index,from); /* 268 */
+ ei_encode_pid(msgbuf,&index,to); /* 268 */
+
+ if (token) ei_encode_trace(msgbuf,&index,token); /* 534 */
+
+ /* Reason */
+ ei_encode_string(msgbuf,&index,reason); /* len */
+
+ /* 5 byte header missing */
+ s = msgbuf;
+ put32be(s, index - 4); /* 4 */
+ put8(s, ERL_PASS_THROUGH); /* 1 */
+ /*** sum: len + 1080 */
+ /* FIXME incorrect level */
+ if (ei_tracelevel > 1)
+ ei_show_sendmsg(stderr,msgbuf,NULL);
+
+ ei_write_fill_t(fd,msgbuf,index,ms);
+ /* FIXME ignore timeout etc? erl_errno?! */
+
+ if (dbuf) free(dbuf);
+ return 0;
+}
+
diff --git a/lib/erl_interface/src/connect/send_reg.c b/lib/erl_interface/src/connect/send_reg.c
new file mode 100644
index 0000000000..8f0e40309c
--- /dev/null
+++ b/lib/erl_interface/src/connect/send_reg.c
@@ -0,0 +1,122 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-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%
+ */
+#ifdef __WIN32__
+#include <winsock2.h>
+#include <windows.h>
+#include <winbase.h>
+
+#elif VXWORKS
+#include <sys/types.h>
+#include <unistd.h>
+
+#else /* unix */
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#endif
+
+#include <stdio.h>
+
+#include "eidef.h"
+#include "eiext.h"
+#include "eisend.h"
+#include "putget.h"
+#include "ei_connect_int.h"
+#include "ei_internal.h"
+#include "ei_trace.h"
+#include "ei_portio.h"
+#include "show_msg.h"
+
+int ei_send_reg_encoded_tmo(int fd, const erlang_pid *from,
+ const char *to, char *msg, int msglen,
+ unsigned ms)
+{
+ char *s, header[1400]; /* see size calculation below */
+ erlang_trace *token = NULL;
+ int index = 5; /* reserve 5 bytes for control message */
+ int res;
+
+#ifdef HAVE_WRITEV
+ struct iovec v[2];
+#endif
+
+ /* are we tracing? */
+ /* check that he can receive trace tokens first */
+ if (ei_distversion(fd) > 0)
+ token = ei_trace(0,NULL);
+
+ /* header = REG_SEND, from, cookie, toname max sizes: */
+ ei_encode_version(header,&index); /* 1 */
+ if (token) {
+ ei_encode_tuple_header(header,&index,5); /* 2 */
+ ei_encode_long(header,&index,ERL_REG_SEND_TT); /* 2 */
+ } else {
+ ei_encode_tuple_header(header,&index,4);
+ ei_encode_long(header,&index,ERL_REG_SEND);
+ }
+ ei_encode_pid(header, &index, from); /* 268 */
+ ei_encode_atom(header, &index, ei_getfdcookie(fd)); /* 258 */
+ ei_encode_atom(header, &index, to); /* 268 */
+
+ if (token) ei_encode_trace(header,&index,token); /* 534 */
+
+ /* control message (precedes header actually) */
+ /* length = 1 ('p') + header len + message len */
+ s = header;
+ put32be(s, index + msglen - 4); /* 4 */
+ put8(s, ERL_PASS_THROUGH); /* 1 */
+ /*** sum: 1336 */
+ /* FIXME incorrect level.... */
+ if (ei_tracelevel > 0)
+ ei_show_sendmsg(stderr,header,msg);
+
+#ifdef HAVE_WRITEV
+
+ v[0].iov_base = (char *)header;
+ v[0].iov_len = index;
+ v[1].iov_base = (char *)msg;
+ v[1].iov_len = msglen;
+
+ if ((res = ei_writev_fill_t(fd,v,2,ms)) != index+msglen) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+#else
+
+ /* no writev() */
+ if ((res = ei_write_fill_t(fd,header,index,ms)) != index) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+ if ((res = ei_write_fill_t(fd,msg,msglen,ms)) != msglen) {
+ erl_errno = (res == -2) ? ETIMEDOUT : EIO;
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+
+int ei_send_reg_encoded(int fd, const erlang_pid *from, const char *to,
+ char *msg, int msglen)
+{
+ return ei_send_reg_encoded_tmo(fd, from, to, msg, msglen, 0);
+}
+