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 + -
显示快捷键?