ei_connect.c

来自「OTP是开放电信平台的简称」· C语言 代码 · 共 1,724 行 · 第 1/3 页

C
1,724
字号
/* ``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 via the world wide web 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. *  * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' *  *     $Id$ *//* * 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 _REENTRANTei_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);}#endifstatic 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 0static 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);}#endifstatic 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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?