From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- lib/erl_interface/src/connect/ei_connect.c | 1738 ++++++++++++++++++++++++ lib/erl_interface/src/connect/ei_connect_int.h | 114 ++ lib/erl_interface/src/connect/ei_resolve.c | 645 +++++++++ lib/erl_interface/src/connect/ei_resolve.h | 24 + lib/erl_interface/src/connect/eirecv.c | 280 ++++ lib/erl_interface/src/connect/eirecv.h | 26 + lib/erl_interface/src/connect/eisend.h | 41 + lib/erl_interface/src/connect/send.c | 125 ++ lib/erl_interface/src/connect/send_exit.c | 101 ++ lib/erl_interface/src/connect/send_reg.c | 122 ++ 10 files changed, 3216 insertions(+) create mode 100644 lib/erl_interface/src/connect/ei_connect.c create mode 100644 lib/erl_interface/src/connect/ei_connect_int.h create mode 100644 lib/erl_interface/src/connect/ei_resolve.c create mode 100644 lib/erl_interface/src/connect/ei_resolve.h create mode 100644 lib/erl_interface/src/connect/eirecv.c create mode 100644 lib/erl_interface/src/connect/eirecv.h create mode 100644 lib/erl_interface/src/connect/eisend.h create mode 100644 lib/erl_interface/src/connect/send.c create mode 100644 lib/erl_interface/src/connect/send_exit.c create mode 100644 lib/erl_interface/src/connect/send_reg.c (limited to 'lib/erl_interface/src/connect') 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 +#include +#include + +#ifdef __WIN32__ +#include +#include +#include + +#elif VXWORKS +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define getpid() taskIdSelf() + +#else /* some other unix */ +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include +#include /* for gen_challenge (NEED FIX?) */ +#include +#endif + +/* common includes */ +#include +#include +#include +#include +#include + +#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," get_cookie: too long path to home"); + return 0; + } + + strcat(fname, COOKIE_FILE); + if ((fd = open(fname, O_RDONLY, 0777)) < 0) { + fprintf(stderr," get_cookie: can't open cookie file"); + return 0; + } + + if ((len = read(fd, buf, bufsize-1)) < 0) { + fprintf(stderr," 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," 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 +#include +#include + +#ifdef __WIN32__ +#include +#include +#include + +#elif VXWORKS +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define getpid() taskIdSelf() +extern int h_errno; + +#else /* some other unix */ +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include +#include /* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#elif __WIN32__ +#include +#include +#include + +#else /* unix of some kind */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +# include +# include +#else /* Unix/VxWorks */ +# include +#endif + +/* common */ +#include +#include +#include + +#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 + +#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 +# include +# include + +#elif VXWORKS + +# include +# include +# include +# include + +#else /* unix */ + +# include +# include +# include + +#endif + +#include + +#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 +# include +# include +#else /* Unix/VxWorks */ +#include +#endif + +#include +#include +#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 +#include +#include + +#elif VXWORKS +#include +#include + +#else /* unix */ +#include +#include +#include +#endif + +#include + +#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); +} + -- cgit v1.2.3