1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2000-2020. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 /*
21  * Purpose: Connect to any node at any host. (EI version)
22  */
23 
24 #include "eidef.h"
25 
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <fcntl.h>
29 
30 #ifdef __WIN32__
31 #include <winsock2.h>
32 #include <windows.h>
33 #include <winbase.h>
34 
35 #else /* some unix */
36 #include <unistd.h>
37 #include <sys/times.h>
38 
39 #if TIME_WITH_SYS_TIME
40 # include <sys/time.h>
41 # include <time.h>
42 #else
43 # if HAVE_SYS_TIME_H
44 #  include <sys/time.h>
45 # else
46 #  include <time.h>
47 # endif
48 #endif
49 
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <netinet/tcp.h>
53 #include <arpa/inet.h>
54 #include <netdb.h>
55 #include <sys/utsname.h>  /* for gen_challenge (NEED FIX?) */
56 #include <time.h>
57 #endif
58 
59 /* common includes */
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <errno.h>
64 #include <ctype.h>
65 #include <stddef.h>
66 
67 #include "eiext.h"
68 #include "ei_portio.h"
69 #include "ei_internal.h"
70 #include "ei_connect_int.h"
71 #include "ei_locking.h"
72 #include "eisend.h"
73 #include "eirecv.h"
74 #include "eimd5.h"
75 #include "putget.h"
76 #include "ei_resolve.h"
77 #include "ei_epmd.h"
78 #include "ei_internal.h"
79 
80 static int ei_connect_initialized = 0;
81 int ei_tracelevel = 0;
82 
83 #define COOKIE_FILE "/.erlang.cookie"
84 #define EI_MAX_HOME_PATH 1024
85 
86 #define EI_SOCKET_CALLBACKS_SZ_V1                       \
87     (offsetof(ei_socket_callbacks, get_fd)              \
88      + sizeof(int (*)(void *)))
89 
90 static char *null_cookie = "";
91 
92 static int get_cookie(char *buf, int len);
93 static int get_home(char *buf, int size);
94 
95 /* forwards */
96 static unsigned gen_challenge(void);
97 static void gen_digest(unsigned challenge, char cookie[],
98 		       unsigned char digest[16]);
99 static int send_status(ei_socket_callbacks *cbs, void *ctx,
100                        int pkt_sz, char *status, unsigned ms);
101 static int recv_status(ei_cnode*, void *ctx,
102                        int pkt_sz, unsigned ms);
103 static int send_challenge(ei_cnode *ec, void *ctx, int pkt_sz,
104                           unsigned challenge,
105                           DistFlags version, unsigned ms);
106 static int recv_challenge(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
107                           unsigned *challenge,
108 			  DistFlags *flags, char *namebuf, unsigned ms);
109 static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
110                                 int pkt_sz, unsigned char digest[16],
111 				unsigned challenge, unsigned ms);
112 static int recv_complement(ei_socket_callbacks *cbs, void *ctx,
113                            int pkt_sz, unsigned ms);
114 static int recv_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
115                                 int pkt_sz, unsigned our_challenge,
116 				char cookie[],
117 				unsigned *her_challenge, unsigned ms);
118 static int send_challenge_ack(ei_socket_callbacks *cbs, void *ctx,
119                               int pkt_sz, unsigned char digest[16],
120                               unsigned ms);
121 static int recv_challenge_ack(ei_socket_callbacks *cbs, void *ctx,
122 			      int pkt_sz, unsigned our_challenge,
123 			      char cookie[], unsigned ms);
124 static int send_name(ei_cnode *ec, void *ctx, int pkt_sz,
125                      unsigned version, unsigned ms);
126 static int send_complement(ei_cnode *ec, void *ctx, int pkt_sz,
127                             unsigned epmd_says_version, DistFlags her_flags,
128                             unsigned ms);
129 static int recv_name(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
130                      char* send_name_tag, DistFlags *flags,
131                      char *namebuf, unsigned ms);
132 static int ei_connect_helper(ei_cnode* ec,
133                              Erl_IpAddr ip_addr,
134                              char *alivename,
135                              unsigned ms,
136                              int rport,
137                              int epmd_says_version);
138 
139 static struct hostent*
140 dyn_gethostbyname_r(const char *name, struct hostent *hostp, char **buffer_p,
141                     int buflen, int *h_errnop);
142 
143 static void abort_connection(ei_socket_callbacks *cbs, void *ctx);
144 static int close_connection(ei_socket_callbacks *cbs, void *ctx, int fd);
145 
146 static const char *
estr(int e)147 estr(int e)
148 {
149     const char *str = strerror(e);
150     if (!str)
151         return "unknown error";
152     return str;
153 }
154 
155 
156 /***************************************************************************
157  *
158  *  For each file descriptor returned from ei_connect() we save information
159  *  about distribution protocol version, node information for this node
160  *  and the cookie.
161  *
162  ***************************************************************************/
163 
164 typedef struct ei_socket_info_s {
165     int socket;
166     ei_socket_callbacks *cbs;
167     void *ctx;
168     int dist_version;
169     ei_cnode cnode;	/* A copy, not a pointer. We don't know when freed */
170     char cookie[EI_MAX_COOKIE_SIZE+1];
171 } ei_socket_info;
172 
173 /***************************************************************************
174  *
175  *  XXX
176  *
177  ***************************************************************************/
178 
179 #ifndef ETHR_HAVE___atomic_compare_exchange_n
180 #  define ETHR_HAVE___atomic_compare_exchange_n 0
181 #endif
182 #ifndef ETHR_HAVE___atomic_load_n
183 #  define ETHR_HAVE___atomic_load_n 0
184 #endif
185 #ifndef ETHR_HAVE___atomic_store_n
186 #  define ETHR_HAVE___atomic_store_n 0
187 #endif
188 
189 #if defined(_REENTRANT)                                                 \
190     && (!(ETHR_HAVE___atomic_compare_exchange_n & SIZEOF_VOID_P)        \
191         || !(ETHR_HAVE___atomic_load_n & SIZEOF_VOID_P)                 \
192         || !(ETHR_HAVE___atomic_store_n & SIZEOF_VOID_P))
193 #  undef EI_DISABLE_SEQ_SOCKET_INFO
194 #  define EI_DISABLE_SEQ_SOCKET_INFO
195 #endif
196 
197 #ifdef __WIN32__
198 #  undef EI_DISABLE_SEQ_SOCKET_INFO
199 #  define EI_DISABLE_SEQ_SOCKET_INFO
200 #endif
201 
202 #ifndef EI_DISABLE_SEQ_SOCKET_INFO
203 
204 #ifdef _REENTRANT
205 
206 #define EI_ATOMIC_CMPXCHG_ACQ_REL(VARP, XCHGP, NEW) \
207     __atomic_compare_exchange_n((VARP), (XCHGP), (NEW), 0, \
208                                 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)
209 #define EI_ATOMIC_LOAD_ACQ(VARP) \
210     __atomic_load_n((VARP), __ATOMIC_ACQUIRE)
211 #define EI_ATOMIC_STORE_REL(VARP, NEW) \
212     __atomic_store_n((VARP), (NEW), __ATOMIC_RELEASE)
213 
214 #else /* ! _REENTRANT */
215 
216 #define EI_ATOMIC_CMPXCHG_ACQ_REL(VARP, XCHGP, NEW)    \
217     (*(VARP) == *(XCHGP)                               \
218      ? ((*(VARP) = (NEW)), !0)                         \
219      : ((*(XCHGP) = *(VARP)), 0))
220 #define EI_ATOMIC_LOAD_ACQ(VARP) (*(VARP))
221 #define EI_ATOMIC_STORE_REL(VARP, NEW) (*(VARP) = (NEW))
222 
223 #endif /* ! _REENTRANT */
224 
225 #define EI_SOCKET_INFO_SEG_BITS 5
226 #define EI_SOCKET_INFO_SEG_SIZE (1 << EI_SOCKET_INFO_SEG_BITS)
227 #define EI_SOCKET_INFO_SEG_MASK (EI_SOCKET_INFO_SEG_SIZE - 1)
228 
229 typedef struct {
230     int max_fds;
231     ei_socket_info *segments[1]; /* Larger in reality... */
232 } ei_socket_info_data__;
233 
234 static ei_socket_info_data__ *socket_info_data = NULL;
235 
init_socket_info(int late)236 static int init_socket_info(int late)
237 {
238     int max_fds;
239     int i;
240     size_t segments_len;
241     ei_socket_info_data__ *info_data, *xchg;
242 
243     if (EI_ATOMIC_LOAD_ACQ(&socket_info_data) != NULL)
244         return 0; /* Already initialized... */
245 
246 #if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
247     max_fds = sysconf(_SC_OPEN_MAX);
248 #else
249     max_fds = 1024;
250 #endif
251 
252     if (max_fds < 0)
253         return EIO;
254 
255     segments_len = ((max_fds-1)/EI_SOCKET_INFO_SEG_SIZE + 1);
256 
257     info_data = malloc(sizeof(ei_socket_info_data__)
258                        + (sizeof(ei_socket_info *)*(segments_len-1)));
259     if (!info_data)
260         return ENOMEM;
261 
262     info_data->max_fds = max_fds;
263     for (i = 0; i < segments_len; i++)
264         info_data->segments[i] = NULL;
265 
266     xchg = NULL;
267     if (!EI_ATOMIC_CMPXCHG_ACQ_REL(&socket_info_data, &xchg, info_data))
268         free(info_data); /* Already initialized... */
269 
270     return 0;
271 }
272 
put_ei_socket_info(int fd,int dist_version,char * cookie,ei_cnode * ec,ei_socket_callbacks * cbs,void * ctx)273 static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode *ec,
274                               ei_socket_callbacks *cbs, void *ctx)
275 {
276     int six;
277     ei_socket_info *seg, *si;
278     int socket;
279 
280     if (fd < 0 || socket_info_data->max_fds <= fd)
281         return -1;
282 
283     socket = fd;
284     six = fd >> EI_SOCKET_INFO_SEG_BITS;
285     seg = EI_ATOMIC_LOAD_ACQ(&socket_info_data->segments[six]);
286 
287     if (!seg) {
288         ei_socket_info *xchg;
289         int i;
290         seg = malloc(sizeof(ei_socket_info)*EI_SOCKET_INFO_SEG_SIZE);
291         if (!seg)
292             return -1;
293         for (i = 0; i < EI_SOCKET_INFO_SEG_SIZE; i++) {
294             seg[i].socket = -1;
295         }
296 
297         xchg = NULL;
298         if (!EI_ATOMIC_CMPXCHG_ACQ_REL(&socket_info_data->segments[six], &xchg, seg)) {
299             free(seg);
300             seg = xchg;
301         }
302     }
303 
304     si = &seg[fd & EI_SOCKET_INFO_SEG_MASK];
305 
306     if (dist_version < 0) {
307         socket = -1;
308         si->cbs = NULL;
309         si->ctx = NULL;
310     }
311     else {
312         si->dist_version = dist_version;
313         si->cnode = *ec;
314         si->cbs = cbs;
315         si->ctx = ctx;
316         strcpy(si->cookie, cookie);
317     }
318 
319     EI_ATOMIC_STORE_REL(&si->socket, socket);
320 
321     return 0;
322 }
323 
get_ei_socket_info(int fd)324 static ei_socket_info* get_ei_socket_info(int fd)
325 {
326     int six, socket;
327     ei_socket_info *seg, *si;
328 
329     if (fd < 0 || socket_info_data->max_fds <= fd)
330         return NULL;
331 
332     six = fd >> EI_SOCKET_INFO_SEG_BITS;
333     seg = EI_ATOMIC_LOAD_ACQ(&socket_info_data->segments[six]);
334 
335     if (!seg)
336         return NULL;
337 
338     si = &seg[fd & EI_SOCKET_INFO_SEG_MASK];
339     socket = EI_ATOMIC_LOAD_ACQ(&si->socket);
340     if (socket != fd)
341         return NULL;
342     return si;
343 }
344 
345 #else /* EI_DISABLE_SEQ_SOCKET_INFO */
346 
347 int ei_n_sockets = 0, ei_sz_sockets = 0;
348 ei_socket_info *ei_sockets = NULL;
349 
350 #ifdef _REENTRANT
351 ei_mutex_t* ei_sockets_lock = NULL;
352 #endif /* _REENTRANT */
353 
init_socket_info(int late)354 static int init_socket_info(int late)
355 {
356 #ifdef _REENTRANT
357     if (late)
358         return ENOTSUP; /* Refuse doing unsafe initialization... */
359     ei_sockets_lock = ei_mutex_create();
360     if (!ei_sockets_lock)
361         return ENOMEM;
362 #endif /* _REENTRANT */
363     return 0;
364 }
365 
put_ei_socket_info(int fd,int dist_version,char * cookie,ei_cnode * ec,ei_socket_callbacks * cbs,void * ctx)366 static int put_ei_socket_info(int fd, int dist_version, char* cookie, ei_cnode *ec,
367                               ei_socket_callbacks *cbs, void *ctx)
368 {
369     int i;
370 
371 #ifdef _REENTRANT
372     ei_mutex_lock(ei_sockets_lock, 0);
373 #endif /* _REENTRANT */
374     for (i = 0; i < ei_n_sockets; ++i) {
375 	if (ei_sockets[i].socket == fd) {
376 	    if (dist_version == -1) {
377                 memmove(&ei_sockets[i], &ei_sockets[i+1],
378 			sizeof(ei_sockets[0])*(ei_n_sockets-i-1));
379 	    } else {
380 		ei_sockets[i].dist_version = dist_version;
381 		/* Copy the content, see ei_socket_info */
382                 ei_sockets[i].cbs = cbs;
383                 ei_sockets[i].ctx = ctx;
384 		ei_sockets[i].cnode = *ec;
385 		strcpy(ei_sockets[i].cookie, cookie);
386 	    }
387 #ifdef _REENTRANT
388 	    ei_mutex_unlock(ei_sockets_lock);
389 #endif /* _REENTRANT */
390 	    return 0;
391 	}
392     }
393     if (ei_n_sockets == ei_sz_sockets) {
394 	ei_sz_sockets += 5;
395 	ei_sockets = realloc(ei_sockets,
396 			     sizeof(ei_sockets[0])*ei_sz_sockets);
397 	if (ei_sockets == NULL) {
398 	    ei_sz_sockets = ei_n_sockets = 0;
399 #ifdef _REENTRANT
400 	    ei_mutex_unlock(ei_sockets_lock);
401 #endif /* _REENTRANT */
402 	    return -1;
403 	}
404     }
405     ei_sockets[ei_n_sockets].socket = fd;
406     ei_sockets[ei_n_sockets].dist_version = dist_version;
407     ei_sockets[ei_n_sockets].cnode = *ec;
408     ei_sockets[ei_n_sockets].cbs = cbs;
409     ei_sockets[ei_n_sockets].ctx = ctx;
410     strcpy(ei_sockets[ei_n_sockets].cookie, cookie);
411     ++ei_n_sockets;
412 #ifdef _REENTRANT
413     ei_mutex_unlock(ei_sockets_lock);
414 #endif /* _REENTRANT */
415     return 0;
416 }
417 
get_ei_socket_info(int fd)418 static ei_socket_info* get_ei_socket_info(int fd)
419 {
420     int i;
421 #ifdef _REENTRANT
422     ei_mutex_lock(ei_sockets_lock, 0);
423 #endif /* _REENTRANT */
424     for (i = 0; i < ei_n_sockets; ++i)
425 	if (ei_sockets[i].socket == fd) {
426 	    /*fprintf("get_ei_socket_info %d  %d \"%s\"\n",
427 		    fd, ei_sockets[i].dist_version, ei_sockets[i].cookie);*/
428 #ifdef _REENTRANT
429 	    ei_mutex_unlock(ei_sockets_lock);
430 #endif /* _REENTRANT */
431 	    return &ei_sockets[i];
432 	}
433 #ifdef _REENTRANT
434     ei_mutex_unlock(ei_sockets_lock);
435 #endif /* _REENTRANT */
436     return NULL;
437 }
438 
439 #endif /* EI_DISABLE_SEQ_SOCKET_INFO */
440 
remove_ei_socket_info(int fd)441 static int remove_ei_socket_info(int fd)
442 {
443     return put_ei_socket_info(fd, -1, null_cookie, NULL, NULL, NULL);
444 }
445 
ei_fd_to_cnode(int fd)446 ei_cnode *ei_fd_to_cnode(int fd)
447 {
448     ei_socket_info *sockinfo = get_ei_socket_info(fd);
449     if (sockinfo == NULL) return NULL;
450     return &sockinfo->cnode;
451 }
452 
ei_get_cbs_ctx__(ei_socket_callbacks ** cbs,void ** ctx,int fd)453 int ei_get_cbs_ctx__(ei_socket_callbacks **cbs, void **ctx, int fd)
454 {
455     ei_socket_info *sockinfo = get_ei_socket_info(fd);
456     if (sockinfo) {
457         *cbs = sockinfo->cbs;
458         *ctx = sockinfo->ctx;
459         return 0;
460     }
461 
462     *cbs = NULL;
463     *ctx = NULL;
464     return EBADF;
465 }
466 
467 /***************************************************************************
468  *  Get/Set tracelevel
469  ***************************************************************************/
470 
ei_set_tracelevel(int level)471 void ei_set_tracelevel(int level) {
472     ei_tracelevel = level;
473 }
474 
ei_get_tracelevel(void)475 int ei_get_tracelevel(void) {
476     return ei_tracelevel;
477 }
478 
479 
480 /***************************************************************************
481  *  Distversion
482  ***************************************************************************/
483 
ei_distversion(int fd)484 int ei_distversion(int fd)
485 {
486     ei_socket_info* e = get_ei_socket_info(fd);
487     if (e == NULL)
488 	return -1;
489     else
490 	return e->dist_version;
491 }
492 
ei_cookie(int fd)493 static const char* ei_cookie(int fd)
494 {
495     ei_socket_info* e = get_ei_socket_info(fd);
496     if (e == NULL)
497 	return NULL;
498     else
499 	return e->cookie;
500 }
501 
ei_thisnodename(const ei_cnode * ec)502 const char *ei_thisnodename(const ei_cnode* ec)
503 {
504     return ec->thisnodename;
505 }
506 
ei_thishostname(const ei_cnode * ec)507 const char *ei_thishostname(const ei_cnode* ec)
508 {
509     return ec->thishostname;
510 }
511 
ei_thisalivename(const ei_cnode * ec)512 const char *ei_thisalivename(const ei_cnode* ec)
513 {
514     return ec->thisalivename;
515 }
516 
ei_thiscreation(const ei_cnode * ec)517 short ei_thiscreation(const ei_cnode* ec)
518 {
519     return ec->creation;
520 }
521 
522 /* FIXME: this function is not an api, why not? */
ei_thiscookie(const ei_cnode * ec)523 const char *ei_thiscookie(const ei_cnode* ec)
524 {
525     return (const char *)ec->ei_connect_cookie;
526 }
527 
528 static int
check_initialized_node(ei_cnode * ec)529 check_initialized_node(ei_cnode *ec)
530 {
531     /*
532      * Try to guard against returning garbage pids and refs
533      * by verifying that the node has got its name...
534      */
535     int i, at, end;
536     char *nodename = &ec->thisnodename[0];
537 
538     for (i = at = end = 0; i < sizeof(ec->thisnodename); i++) {
539         if (!nodename[i]) {
540             end = !0;
541             break;
542         }
543         if (nodename[i] == '@')
544             at = !0;
545     }
546 
547     if (!at || !end) {
548         erl_errno = EINVAL;
549         return ERL_ERROR;
550     }
551 
552     return 0;
553 }
554 
ei_self(ei_cnode * ec)555 erlang_pid *ei_self(ei_cnode* ec)
556 {
557     int err = check_initialized_node(ec);
558     if (err)
559         return NULL;
560     return &ec->self;
561 }
562 
563 /*
564  * ei_make_pid()
565  */
566 
567 #undef EI_MAKE_PID_ATOMIC__
568 #ifdef _REENTRANT
569 #  if (SIZEOF_INT == 4                                  \
570        && (ETHR_HAVE___atomic_compare_exchange_n & 4)   \
571        && (ETHR_HAVE___atomic_load_n & 4))
572 #    define EI_MAKE_PID_ATOMIC__
573 #  else /* !EI_MAKE_PID_ATOMIC__ */
574 static ei_mutex_t *pid_mtx = NULL;
575 #  endif /* !EI_MAKE_PID_ATOMIC__ */
576 #endif /* _REENTRANT */
577 
578 static int
init_make_pid(int late)579 init_make_pid(int late)
580 {
581 #if defined(_REENTRANT) && !defined(EI_MAKE_PID_ATOMIC__)
582 
583     if (late)
584         return ENOTSUP; /* Refuse doing unsafe initialization... */
585 
586     pid_mtx = ei_mutex_create();
587     if (!pid_mtx)
588         return ENOMEM;
589 
590 #endif /* _REENTRANT */
591 
592     return 0;
593 }
594 
ei_make_pid(ei_cnode * ec,erlang_pid * pid)595 int ei_make_pid(ei_cnode *ec, erlang_pid *pid)
596 {
597     unsigned int new;
598     int err;
599 
600     if (!ei_connect_initialized) {
601 	fprintf(stderr,"<ERROR> erl_interface not initialized\n");
602         exit(1);
603     }
604 
605     err = check_initialized_node(ec);
606     if (err) {
607         /*
608          * write invalid utf8 in nodename which will make
609          * ei_encode_pid() fail if used...
610          */
611         pid->node[0] = 0xff;
612         pid->node[1] = 0;
613         pid->serial = -1;
614         pid->num = -1;
615         return err;
616     }
617 
618     strcpy(pid->node, ec->thisnodename);
619     pid->creation = ec->creation;
620 
621     /*
622      * We avoid creating pids with serial set to 0 since the
623      * documentation previously gave some really bad advise
624      * of modifying the 'num' field in the pid returned by
625      * ei_self(). Since 'serial' field in pid returned by
626      * ei_self() is initialized to 0, pids created by
627      * ei_make_pid() wont clash with such badly created pids
628      * using ei_self() unless user also modified serial, but
629      * that has at least never been suggested by the
630      * documentation.
631      */
632 
633 #ifdef EI_MAKE_PID_ATOMIC__
634     {
635         unsigned int xchg = __atomic_load_n(&ec->pidsn, __ATOMIC_RELAXED);
636         do {
637             new = xchg + 1;
638             if ((new & 0x0fff8000) == 0)
639                 new = 0x8000; /* serial==0 -> serial=1 num=0 */
640         } while(!__atomic_compare_exchange_n(&ec->pidsn, &xchg, new, 0,
641                                              __ATOMIC_ACQ_REL,
642                                              __ATOMIC_RELAXED));
643     }
644 #else /* !EI_MAKE_PID_ATOMIC__ */
645 
646 #ifdef _REENTRANT
647     ei_mutex_lock(pid_mtx, 0);
648 #endif
649 
650     new = ec->pidsn + 1;
651     if ((new & 0x0fff8000) == 0)
652         new = 0x8000; /* serial==0 -> serial=1 num=0 */
653 
654     ec->pidsn = new;
655 
656 #ifdef _REENTRANT
657     ei_mutex_unlock(pid_mtx);
658 #endif
659 
660 #endif /* !EI_MAKE_PID_ATOMIC__ */
661 
662     pid->num = new & 0x7fff; /* 15-bits */
663     pid->serial = (new >> 15) & 0x1fff; /* 13-bits */
664 
665     return 0;
666 }
667 
668 /*
669  * ei_make_ref()
670  */
671 
672 #undef EI_MAKE_REF_ATOMIC__
673 #ifdef _REENTRANT
674 #  if ((SIZEOF_LONG == 8 || SIZEOF_LONGLONG == 8)         \
675        && (ETHR_HAVE___atomic_compare_exchange_n & 8)     \
676        && (ETHR_HAVE___atomic_load_n & 8))
677 #    define EI_MAKE_REF_ATOMIC__
678 #    if SIZEOF_LONG == 8
679 typedef unsigned long ei_atomic_ref__;
680 #    else
681 typedef unsigned long long ei_atomic_ref__;
682 #    endif
683 #  else /* !EI_MAKE_REF_ATOMIC__ */
684 static ei_mutex_t *ref_mtx = NULL;
685 #  endif /* !EI_MAKE_REF_ATOMIC__ */
686 #endif /* _REENTRANT */
687 
688 /*
689  * We use a global counter for all c-nodes in this process.
690  * We wont wrap anyway due to the enormous amount of values
691  * available.
692  */
693 #ifdef EI_MAKE_REF_ATOMIC__
694 static ei_atomic_ref__ ref_count;
695 #else
696 static unsigned int ref_count[3];
697 #endif
698 
699 static int
init_make_ref(int late)700 init_make_ref(int late)
701 {
702 
703 #ifdef EI_MAKE_REF_ATOMIC__
704     ref_count = 0;
705 #else /* !EI_MAKE_REF_ATOMIC__ */
706 
707 #ifdef _REENTRANT
708 
709     if (late)
710         return ENOTSUP; /* Refuse doing unsafe initialization... */
711 
712     ref_mtx = ei_mutex_create();
713     if (!ref_mtx)
714         return ENOMEM;
715 
716 #endif /* _REENTRANT */
717 
718     ref_count[0] = 0;
719     ref_count[1] = 0;
720     ref_count[2] = 0;
721 
722 #endif /* !EI_MAKE_REF_ATOMIC__ */
723 
724     return 0;
725 }
726 
ei_make_ref(ei_cnode * ec,erlang_ref * ref)727 int ei_make_ref(ei_cnode *ec, erlang_ref *ref)
728 {
729     int err;
730     if (!ei_connect_initialized) {
731 	fprintf(stderr,"<ERROR> erl_interface not initialized\n");
732         exit(1);
733     }
734 
735     err = check_initialized_node(ec);
736     if (err) {
737         /*
738          * write invalid utf8 in nodename which will make
739          * ei_encode_ref() fail if used...
740          */
741         ref->node[0] = 0xff;
742         ref->node[1] = 0;
743         ref->len = -1;
744         return err;
745     }
746 
747     strcpy(ref->node, ec->thisnodename);
748     ref->creation = ec->creation;
749     ref->len = 3;
750 
751 #ifdef EI_MAKE_REF_ATOMIC__
752     {
753         ei_atomic_ref__ xchg, new;
754         xchg = __atomic_load_n(&ref_count, __ATOMIC_RELAXED);
755         do {
756             new = xchg + 1;
757         } while(!__atomic_compare_exchange_n(&ref_count, &xchg, new, 0,
758                                              __ATOMIC_ACQ_REL,
759                                              __ATOMIC_RELAXED));
760         ref->n[0] = (unsigned int) (new & 0x3ffff);
761         ref->n[1] = (unsigned int) ((new >> 18) & 0xffffffff);
762         ref->n[2] = (unsigned int) ((new >> (18+32)) & 0xffffffff);
763     }
764 #else /* !EI_MAKE_REF_ATOMIC__ */
765 
766 #ifdef _REENTRANT
767     ei_mutex_lock(ref_mtx, 0);
768 #endif
769 
770     ref->n[0] = ref_count[0];
771     ref->n[1] = ref_count[1];
772     ref->n[2] = ref_count[2];
773     ref->n[3] = 0;
774     ref->n[4] = 0;
775 
776     ref_count[0]++;
777     ref_count[0] &= 0x3ffff;
778     if (ref_count[0] == 0) {
779         ref_count[1]++;
780         ref_count[1] &= 0xffffffff;
781         if (ref_count[1] == 0) {
782             ref_count[2]++;
783             ref_count[2] &= 0xffffffff;
784         }
785     }
786 
787 #ifdef _REENTRANT
788     ei_mutex_unlock(ref_mtx);
789 #endif
790 
791 #endif /* !EI_MAKE_REF_ATOMIC__ */
792 
793     return 0;
794 }
795 
796 /* two internal functions that will let us support different cookies
797 * (to be able to connect to other nodes that don't have the same
798 * cookie as each other or us)
799 */
ei_getfdcookie(int fd)800 const char *ei_getfdcookie(int fd)
801 {
802     const char* r = ei_cookie(fd);
803     if (r == NULL) r = "";
804     return r;
805 }
806 
get_int32(unsigned char * s)807 static int get_int32(unsigned char *s)
808 {
809     return ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] ));
810 }
811 
812 
813 #ifdef __WIN32__
win32_error(char * buf,int buflen)814 void win32_error(char *buf, int buflen)
815 {
816     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
817 	0,	/* n/a */
818 	WSAGetLastError(), /* error code */
819 	MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* language */
820 	buf,
821 	buflen,
822 	NULL);
823     return;
824 }
825 
initWinSock(void)826 static int initWinSock(void)
827 {
828     WORD wVersionRequested;
829     WSADATA wsaData;
830     int i;
831 
832     static LONG volatile initialized = 0;
833 
834     wVersionRequested = MAKEWORD(1, 1);
835     if (InterlockedCompareExchange((LPLONG) &initialized,1L,0L) == 0L) {
836 	/* FIXME not terminate, just a message?! */
837 	if ((i = WSAStartup(wVersionRequested, &wsaData))) {
838 	    EI_TRACE_ERR1("ei_connect_init",
839 			  "ERROR: can't initialize windows sockets: %d",i);
840 	    initialized = 2L;
841 	    return 0;
842 	}
843 
844 	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
845 	    EI_TRACE_ERR0("initWinSock","ERROR: this version of windows "
846 			  "sockets not supported");
847 	    WSACleanup();
848 	    initialized = 2L;
849 	    return 0;
850 	}
851 	initialized = 3L;
852     } else while (initialized < 2) {
853 	SwitchToThread();
854     }
855     return (int) (initialized - 2);
856 }
857 #endif
858 
init_connect(int late)859 static int init_connect(int late)
860 {
861     int error;
862 
863     /*
864      * 'late' is non-zero when not called via ei_init(). Such a
865      * call is not supported, but we for now save the day if
866      * it easy to do so; otherwise, return ENOTSUP.
867      */
868 
869 #ifdef __WIN32__
870     if (!initWinSock()) {
871 	EI_TRACE_ERR0("ei_init_connect","can't initiate winsock");
872 	return EIO;
873     }
874 #endif /* win32 */
875 
876     error = init_socket_info(late);
877     if (error) {
878         EI_TRACE_ERR0("ei_init_connect","can't initiate socket info");
879         return error;
880     }
881 
882     error = init_make_ref(late);
883     if (error) {
884         EI_TRACE_ERR0("ei_init_connect","can't initiate ei_make_ref()");
885         return error;
886     }
887 
888     error = init_make_pid(late);
889     if (error) {
890         EI_TRACE_ERR0("ei_init_connect","can't initiate ei_make_pid()");
891         return error;
892     }
893 
894     ei_connect_initialized = !0;
895     return 0;
896 }
897 
ei_init_connect(void)898 int ei_init_connect(void)
899 {
900     return init_connect(0);
901 }
902 
903 /*
904 * Perhaps run this routine instead of ei_connect_init/2 ?
905 * Initailize by setting:
906 * thishostname, thisalivename, thisnodename and thisipaddr
907 */
ei_connect_xinit_ussi(ei_cnode * ec,const char * thishostname,const char * thisalivename,const char * thisnodename,Erl_IpAddr thisipaddr,const char * cookie,const short creation,ei_socket_callbacks * cbs,int cbs_sz,void * setup_context)908 int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname,
909                           const char *thisalivename, const char *thisnodename,
910                           Erl_IpAddr thisipaddr, const char *cookie,
911                           const short creation, ei_socket_callbacks *cbs,
912                           int cbs_sz, void *setup_context)
913 {
914     char *dbglevel;
915 
916     if (!ei_connect_initialized)
917         init_connect(!0);
918 
919     if (cbs != &ei_default_socket_callbacks)
920         EI_SET_HAVE_PLUGIN_SOCKET_IMPL__;
921 
922     if (cbs_sz < EI_SOCKET_CALLBACKS_SZ_V1) {
923 	EI_TRACE_ERR0("ei_connect_xinit","invalid size of ei_socket_callbacks struct");
924         return ERL_ERROR;
925     }
926 
927     ec->creation = creation;
928     ec->pidsn = 0;
929 
930     if (cookie) {
931 	if (strlen(cookie) >= sizeof(ec->ei_connect_cookie)) {
932 	    EI_TRACE_ERR0("ei_connect_xinit",
933 			  "ERROR: Cookie size too large");
934 	    return ERL_ERROR;
935 	} else {
936 	    strcpy(ec->ei_connect_cookie, cookie);
937 	}
938     } else if (!get_cookie(ec->ei_connect_cookie, sizeof(ec->ei_connect_cookie))) {
939 	return ERL_ERROR;
940     }
941 
942     if (strlen(thishostname) >= sizeof(ec->thishostname)) {
943 	EI_TRACE_ERR0("ei_connect_xinit","ERROR: Thishostname too long");
944 	return ERL_ERROR;
945     }
946     strcpy(ec->thishostname, thishostname);
947 
948     if (thisalivename) {
949         if (strlen(thisalivename) >= sizeof(ec->thisalivename)) {
950             EI_TRACE_ERR0("ei_connect_init","Thisalivename too long");
951             return ERL_ERROR;
952         }
953 
954         strcpy(ec->thisalivename, thisalivename);
955 
956         if (strlen(thisnodename) >= sizeof(ec->thisnodename)) {
957             EI_TRACE_ERR0("ei_connect_init","Thisnodename too long");
958             return ERL_ERROR;
959         }
960         strcpy(ec->thisnodename, thisnodename);
961 
962         strcpy(ec->self.node, thisnodename);
963         ec->self.num = 0;
964         ec->self.serial = 0;
965         ec->self.creation = creation;
966     }
967     else {
968         /* dynamic name */
969         ec->thisalivename[0] = 0;
970         ec->thisnodename[0] = 0;
971     }
972 
973 /* FIXME right now this_ipaddr is never used */
974     /*    memmove(&ec->this_ipaddr, thisipaddr, sizeof(ec->this_ipaddr)); */
975 
976 
977     ec->cbs = cbs;
978     ec->setup_context = setup_context;
979 
980     if ((dbglevel = getenv("EI_TRACELEVEL")) != NULL ||
981 	(dbglevel = getenv("ERL_DEBUG_DIST")) != NULL)
982 	ei_tracelevel = atoi(dbglevel);
983 
984     return 0;
985 }
986 
ei_connect_xinit(ei_cnode * ec,const char * thishostname,const char * thisalivename,const char * thisnodename,Erl_IpAddr thisipaddr,const char * cookie,const short creation)987 int ei_connect_xinit(ei_cnode* ec, const char *thishostname,
988                      const char *thisalivename, const char *thisnodename,
989                      Erl_IpAddr thisipaddr, const char *cookie,
990                      const short creation)
991 {
992     return ei_connect_xinit_ussi(ec, thishostname, thisalivename, thisnodename,
993                                  thisipaddr, cookie, creation,
994                                  &ei_default_socket_callbacks,
995                                  sizeof(ei_default_socket_callbacks),
996                                  NULL);
997 }
998 
999 /*
1000 * Initialize by set: thishostname, thisalivename,
1001 * thisnodename and thisipaddr. At success return 0,
1002 * otherwise return -1.
1003 */
ei_connect_init_ussi(ei_cnode * ec,const char * this_node_name,const char * cookie,short creation,ei_socket_callbacks * cbs,int cbs_sz,void * setup_context)1004 int ei_connect_init_ussi(ei_cnode* ec, const char* this_node_name,
1005                          const char *cookie, short creation,
1006                          ei_socket_callbacks *cbs, int cbs_sz,
1007                          void *setup_context)
1008 {
1009     char thishostname[EI_MAXHOSTNAMELEN+1];
1010     char thisnodename[MAXNODELEN+1];
1011     char thisalivename[EI_MAXALIVELEN+1];
1012     struct hostent host, *hp;
1013     char buffer[1024];
1014     char *buf = buffer;
1015     int ei_h_errno;
1016     int res;
1017 
1018     if (!ei_connect_initialized)
1019         init_connect(!0);
1020 
1021     /* gethostname requires len to be max(hostname) + 1 */
1022     if (gethostname(thishostname, EI_MAXHOSTNAMELEN+1) == -1) {
1023 #ifdef __WIN32__
1024 	EI_TRACE_ERR1("ei_connect_init","Failed to get host name: %d",
1025 		      WSAGetLastError());
1026 #else
1027 	EI_TRACE_ERR1("ei_connect_init","Failed to get host name: %d", errno);
1028 #endif /* win32 */
1029 	return ERL_ERROR;
1030     }
1031 
1032     if (strlen(this_node_name) >= sizeof(thisalivename)) {
1033 	EI_TRACE_ERR0("ei_connect_init","ERROR: this_node_name too long");
1034 	return ERL_ERROR;
1035     } else {
1036 	strcpy(thisalivename, this_node_name);
1037     }
1038 
1039     hp = dyn_gethostbyname_r(thishostname,&host,&buf,sizeof(buffer),&ei_h_errno);
1040     if (hp == NULL) {
1041 	/* Looking up IP given hostname fails. We must be on a standalone
1042 	   host so let's use loopback for communication instead. */
1043 	hp = dyn_gethostbyname_r("localhost", &host, &buf, sizeof(buffer),
1044                                  &ei_h_errno);
1045         if (hp == NULL) {
1046 #ifdef __WIN32__
1047 	    char reason[1024];
1048 
1049 	    win32_error(reason,sizeof(reason));
1050 	    EI_TRACE_ERR2("ei_connect_init",
1051 			  "Can't get ip address for host %s: %s",
1052 			  thishostname, reason);
1053 #else
1054 	    EI_TRACE_ERR2("ei_connect_init",
1055 			  "Can't get ip address for host %s: %d",
1056 			  thishostname, h_errno);
1057 #endif /* win32 */
1058 	    return ERL_ERROR;
1059 	}
1060     }
1061     {
1062 	char* ct;
1063 	if (strcmp(hp->h_name, "localhost") == 0) {
1064 	    /* We use a short node name */
1065 	    if ((ct = strchr(thishostname, '.')) != NULL) *ct = '\0';
1066 	} else {
1067 	    /* We use a short node name */
1068 	    if ((ct = strchr(hp->h_name, '.')) != NULL) *ct = '\0';
1069 	    strcpy(thishostname, hp->h_name);
1070 	}
1071     }
1072     if (strlen(this_node_name) + 1 + strlen(thishostname) > MAXNODELEN) {
1073         EI_TRACE_ERR0("ei_connect_init_ussi","this node name is too long");
1074         return ERL_ERROR;
1075     }
1076     sprintf(thisnodename, "%s@%s", this_node_name, thishostname);
1077     res = ei_connect_xinit_ussi(ec, thishostname, thisalivename, thisnodename,
1078                                 (struct in_addr *)*hp->h_addr_list, cookie, creation,
1079                                 cbs, cbs_sz, setup_context);
1080     if (buf != buffer)
1081         free(buf);
1082     return res;
1083 }
1084 
ei_connect_init(ei_cnode * ec,const char * this_node_name,const char * cookie,short creation)1085 int ei_connect_init(ei_cnode* ec, const char* this_node_name,
1086                     const char *cookie, short creation)
1087 {
1088     return ei_connect_init_ussi(ec, this_node_name, cookie, creation,
1089                                 &ei_default_socket_callbacks,
1090                                 sizeof(ei_default_socket_callbacks),
1091                                 NULL);
1092 }
1093 
1094 /*
1095  * Same as ei_gethostbyname_r, but also handles ERANGE error
1096  * and may allocate larger buffer with malloc.
1097  */
1098 static
dyn_gethostbyname_r(const char * name,struct hostent * hostp,char ** buffer_p,int buflen,int * h_errnop)1099 struct hostent *dyn_gethostbyname_r(const char *name,
1100                                     struct hostent *hostp,
1101                                     char **buffer_p,
1102 				    int buflen,
1103 				    int *h_errnop)
1104 {
1105 #ifdef __WIN32__
1106     /*
1107      * Apparently ei_gethostbyname_r not implemented for Windows (?)
1108      * Fall back on ei_gethostbyname like before.
1109      */
1110     return ei_gethostbyname(name);
1111 #else
1112     char* buf = *buffer_p;
1113     struct hostent *hp;
1114 
1115     while (1) {
1116         hp = ei_gethostbyname_r(name, hostp, buf, buflen, h_errnop);
1117         if (hp) {
1118             *buffer_p = buf;
1119             break;
1120         }
1121 
1122         if (*h_errnop != ERANGE) {
1123             if (buf != *buffer_p)
1124                 free(buf);
1125             break;
1126         }
1127 
1128         buflen *= 2;
1129         if (buf == *buffer_p)
1130             buf = malloc(buflen);
1131         else {
1132             char* buf2 = realloc(buf, buflen);
1133             if (buf2)
1134                 buf = buf2;
1135             else {
1136                 free(buf);
1137                 buf = NULL;
1138             }
1139         }
1140         if (!buf) {
1141             *h_errnop = ENOMEM;
1142             break;
1143         }
1144     }
1145     return hp;
1146 #endif
1147 }
1148 
1149 /* Finds the the IP address for hostname and saves that IP address at
1150    the location that ip_wb points to. Returns a negative error code if
1151    the IP address cannot be found for the hostname. */
ip_address_from_hostname(char * hostname,char ** buffer_p,size_t buffer_size,Erl_IpAddr * ip_wb)1152 static int ip_address_from_hostname(char* hostname,
1153                                     char** buffer_p,
1154                                     size_t buffer_size,
1155                                     Erl_IpAddr* ip_wb)
1156 {
1157     struct hostent *hp;
1158 #ifndef __WIN32__
1159     /* these are needed for the call to gethostbyname_r */
1160     struct hostent host;
1161     int ei_h_errno;
1162     hp = dyn_gethostbyname_r(hostname,&host,buffer_p,buffer_size,&ei_h_errno);
1163     if (hp == NULL) {
1164 	char thishostname[EI_MAXHOSTNAMELEN+1];
1165         /* gethostname requies len to be max(hostname) + 1*/
1166 	if (gethostname(thishostname,EI_MAXHOSTNAMELEN+1) < 0) {
1167 	    EI_TRACE_ERR0("ip_address_from_hostname",
1168 			  "Failed to get name of this host");
1169 	    erl_errno = EHOSTUNREACH;
1170 	    return ERL_ERROR;
1171 	} else {
1172 	    char *ct;
1173 	    /* We use a short node name */
1174 	    if ((ct = strchr(thishostname, '.')) != NULL) *ct = '\0';
1175 	}
1176 	if (strcmp(hostname,thishostname) == 0)
1177 	    /* Both nodes on same standalone host, use loopback */
1178 	    hp = dyn_gethostbyname_r("localhost",&host,buffer_p,buffer_size,&ei_h_errno);
1179 	if (hp == NULL) {
1180 	    EI_TRACE_ERR2("ei_connect",
1181 			  "Can't find host for %s: %d\n",hostname,ei_h_errno);
1182 	    erl_errno = EHOSTUNREACH;
1183 	    return ERL_ERROR;
1184 	}
1185     }
1186     *ip_wb = (Erl_IpAddr) *hp->h_addr_list;
1187 #else /* __WIN32__ */
1188     if ((hp = ei_gethostbyname(hostname)) == NULL) {
1189 	char thishostname[EI_MAXHOSTNAMELEN+1];
1190         /* gethostname requires len to be max(hostname) + 1 */
1191 	if (gethostname(thishostname,EI_MAXHOSTNAMELEN+1) < 0) {
1192 	    EI_TRACE_ERR1("ip_address_from_hostname",
1193 			  "Failed to get name of this host: %d",
1194 			  WSAGetLastError());
1195 	    erl_errno = EHOSTUNREACH;
1196 	    return ERL_ERROR;
1197 	} else {
1198 	    char *ct;
1199 	    /* We use a short node name */
1200 	    if ((ct = strchr(thishostname, '.')) != NULL) *ct = '\0';
1201 	}
1202 	if (strcmp(hostname,thishostname) == 0)
1203 	    /* Both nodes on same standalone host, use loopback */
1204 	    hp = ei_gethostbyname("localhost");
1205 	if (hp == NULL) {
1206 	    char reason[1024];
1207 	    win32_error(reason,sizeof(reason));
1208 	    EI_TRACE_ERR2("ei_connect",
1209 			  "Can't find host for %s: %s",hostname,reason);
1210 	    erl_errno = EHOSTUNREACH;
1211 	    return ERL_ERROR;
1212 	}
1213     }
1214     *ip_wb = (Erl_IpAddr) *hp->h_addr_list;
1215 #endif /* win32 */
1216     return 0;
1217 }
1218 
1219 /* Helper function for ei_connect family of functions */
ei_connect_helper(ei_cnode * ec,Erl_IpAddr ip_addr,char * alivename,unsigned ms,int rport,int epmd_says_version)1220 static int ei_connect_helper(ei_cnode* ec,
1221                              Erl_IpAddr ip_addr,  /* network byte order */
1222                              char *alivename,
1223                              unsigned ms,
1224                              int rport,
1225                              int epmd_says_version)
1226 {
1227     ei_socket_callbacks *cbs = ec->cbs;
1228     void *ctx;
1229     int sockd;
1230     unsigned her_version;
1231     DistFlags her_flags;
1232     unsigned our_challenge, her_challenge;
1233     unsigned char our_digest[16];
1234     int err;
1235     int pkt_sz;
1236     struct sockaddr_in addr;
1237     unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
1238 
1239     erl_errno = EIO;		/* Default error code */
1240 
1241     if (alivename != NULL) {
1242         EI_TRACE_CONN1("ei_xconnect","-> CONNECT attempt to connect to %s",
1243                        alivename);
1244     } else {
1245         EI_TRACE_CONN1("ei_xconnect","-> CONNECT attempt to connect to port %d",
1246                        rport);
1247     }
1248 
1249     if (epmd_says_version < EI_DIST_LOW) {
1250 	EI_TRACE_ERR1("ei_xconnect","-> CONNECT remote version %d not compatible",
1251                       epmd_says_version);
1252 	return ERL_ERROR;
1253     }
1254 
1255     if (!ec->thisnodename[0] && epmd_says_version < EI_DIST_6) {
1256         /* This is a dynamic node name. We have to use at least vsn 6
1257            of the dist protocol for this to work. */
1258         epmd_says_version = EI_DIST_6;
1259     }
1260 
1261     err = ei_socket_ctx__(cbs, &ctx, ec->setup_context);
1262     if (err) {
1263         EI_TRACE_ERR2("ei_xconnect","-> SOCKET failed: %s (%d)",
1264                       estr(err), err);
1265         erl_errno = err;
1266         return ERL_CONNECT_FAIL;
1267     }
1268 
1269     memset((void *) &addr, 0, sizeof(struct sockaddr_in));
1270     memcpy((void *) &addr.sin_addr, (void *) ip_addr, sizeof(addr.sin_addr));
1271     addr.sin_family = AF_INET;
1272     addr.sin_port = htons(rport);
1273 
1274     err = ei_connect_ctx_t__(cbs, ctx, (void *) &addr, sizeof(addr), tmo);
1275     if (err) {
1276         EI_TRACE_ERR2("ei_xconnect","-> CONNECT socket connect failed: %s (%d)",
1277                       estr(err), err);
1278         abort_connection(cbs, ctx);
1279         erl_errno = err;
1280         return ERL_CONNECT_FAIL;
1281     }
1282 
1283     EI_TRACE_CONN0("ei_xconnect","-> CONNECT connected to remote");
1284 
1285     err = EI_GET_FD__(cbs, ctx, &sockd);
1286     if (err) {
1287         EI_CONN_SAVE_ERRNO__(err);
1288         goto error;
1289     }
1290 
1291     err = cbs->handshake_packet_header_size(ctx, &pkt_sz);
1292     if (err) {
1293         EI_CONN_SAVE_ERRNO__(err);
1294         goto error;
1295     }
1296 
1297     if (send_name(ec, ctx, pkt_sz, epmd_says_version, tmo))
1298         goto error;
1299     if (recv_status(ec, ctx, pkt_sz, tmo))
1300         goto error;
1301     if (recv_challenge(cbs, ctx, pkt_sz, &her_challenge,
1302                        &her_flags, NULL, tmo))
1303         goto error;
1304     her_version = (her_flags & DFLAG_HANDSHAKE_23) ? EI_DIST_6 : EI_DIST_5;
1305     our_challenge = gen_challenge();
1306     gen_digest(her_challenge, ec->ei_connect_cookie, our_digest);
1307     if (send_complement(ec, ctx, pkt_sz, epmd_says_version, her_flags, tmo))
1308         goto error;
1309     if (send_challenge_reply(cbs, ctx, pkt_sz, our_digest, our_challenge, tmo))
1310         goto error;
1311     if (recv_challenge_ack(cbs, ctx, pkt_sz, our_challenge,
1312                            ec->ei_connect_cookie, tmo))
1313         goto error;
1314     if (put_ei_socket_info(sockd, her_version, null_cookie, ec, cbs, ctx) != 0)
1315         goto error;
1316 
1317     if (cbs->connect_handshake_complete) {
1318         err = cbs->connect_handshake_complete(ctx);
1319         if (err) {
1320             EI_TRACE_ERR2("ei_xconnect","-> CONNECT failed: %s (%d)",
1321                           estr(err), err);
1322             close_connection(cbs, ctx, sockd);
1323             EI_CONN_SAVE_ERRNO__(err);
1324             return ERL_ERROR;
1325         }
1326     }
1327 
1328     if (alivename != NULL) {
1329         EI_TRACE_CONN1("ei_xconnect","-> CONNECT (ok) remote = %s",alivename);
1330     } else {
1331         EI_TRACE_CONN1("ei_xconnect","-> CONNECT (ok) remote port = %d",rport);
1332     }
1333 
1334     erl_errno = 0;
1335     return sockd;
1336 
1337 error:
1338     EI_TRACE_ERR0("ei_xconnect","-> CONNECT failed");
1339     abort_connection(cbs, ctx);
1340     return ERL_ERROR;
1341 } /* ei_xconnect */
1342 
1343   /*
1344   * Set up a connection to a given Node, and
1345   * interchange hand shake messages with it.
1346   * Returns a valid file descriptor at success,
1347   * otherwise a negative error code.
1348 */
ei_connect_tmo(ei_cnode * ec,char * nodename,unsigned ms)1349 int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned ms)
1350 {
1351     char *hostname, alivename[BUFSIZ];
1352     Erl_IpAddr ip;
1353     int res;
1354     char buffer[1024];
1355     char* buf = buffer;
1356 
1357     if (strlen(nodename) > MAXNODELEN) {
1358 	EI_TRACE_ERR0("ei_connect","Too long nodename");
1359 	return ERL_ERROR;
1360     }
1361 
1362     /* extract the host and alive parts from nodename */
1363     if (!(hostname = strchr(nodename,'@'))) {
1364 	EI_TRACE_ERR0("ei_connect","Node name has no @ in name");
1365 	return ERL_ERROR;
1366     } else {
1367 	strncpy(alivename, nodename, hostname - nodename);
1368 	alivename[hostname - nodename] = 0x0;
1369 	hostname++;
1370     }
1371 
1372     res = ip_address_from_hostname(hostname, &buf, sizeof(buffer), &ip);
1373 
1374     if (res < 0) {
1375       return res;
1376     }
1377 
1378     res = ei_xconnect_tmo(ec, ip, alivename, ms);
1379 
1380     if(buf != buffer) {
1381         free(buf);
1382     }
1383 
1384     return res;
1385 } /* ei_connect */
1386 
ei_connect(ei_cnode * ec,char * nodename)1387 int ei_connect(ei_cnode* ec, char *nodename)
1388 {
1389     return ei_connect_tmo(ec, nodename, 0);
1390 }
1391 
ei_connect_host_port_tmo(ei_cnode * ec,char * host,int port,unsigned ms)1392 int ei_connect_host_port_tmo(ei_cnode* ec, char *host, int port, unsigned ms)
1393 {
1394     Erl_IpAddr ip;
1395     char buffer[1024];
1396     char* buf = buffer;
1397     int res = ip_address_from_hostname(host, &buf, sizeof(buffer), &ip);
1398     if (res < 0) {
1399       return res;
1400     }
1401     if(buf != buffer) {
1402         free(buf);
1403     }
1404     return ei_xconnect_host_port_tmo(ec, ip, port, ms);
1405 }
1406 
ei_connect_host_port(ei_cnode * ec,char * host,int port)1407 int ei_connect_host_port(ei_cnode* ec, char *host, int port)
1408 {
1409     return ei_connect_host_port_tmo(ec, host, port, 0);
1410 }
1411 
ei_xconnect_tmo(ei_cnode * ec,Erl_IpAddr ip_addr,char * alivename,unsigned ms)1412 int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename, unsigned ms)
1413 {
1414     int epmd_says_version = 0;
1415     int port;
1416     unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
1417     if ((port = ei_epmd_port_tmo(ip_addr,alivename,&epmd_says_version, tmo)) < 0) {
1418 	EI_TRACE_ERR0("ei_xconnect","-> CONNECT can't get remote port");
1419 	/* ei_epmd_port_tmo() has set erl_errno */
1420 	return ERL_NO_PORT;
1421     }
1422     return ei_connect_helper(ec, ip_addr, alivename, ms, port, epmd_says_version);
1423 }
1424 
ei_xconnect(ei_cnode * ec,Erl_IpAddr ip_addr,char * alivename)1425 int ei_xconnect(ei_cnode* ec, Erl_IpAddr ip_addr, char *alivename)
1426 {
1427     return ei_xconnect_tmo(ec, ip_addr, alivename, 0);
1428 }
1429 
ei_xconnect_host_port_tmo(ei_cnode * ec,Erl_IpAddr ip_addr,int port,unsigned ms)1430 int ei_xconnect_host_port_tmo(ei_cnode* ec, Erl_IpAddr ip_addr, int port, unsigned ms)
1431 {
1432     return ei_connect_helper(ec, ip_addr, NULL, ms, port, EI_DIST_LOW);
1433 }
1434 
ei_xconnect_host_port(ei_cnode * ec,Erl_IpAddr ip_addr,int port)1435 int ei_xconnect_host_port(ei_cnode* ec, Erl_IpAddr ip_addr, int port)
1436 {
1437     return ei_xconnect_host_port_tmo(ec, ip_addr, port, 0);
1438 }
1439 
ei_listen(ei_cnode * ec,int * port,int backlog)1440 int ei_listen(ei_cnode *ec, int *port, int backlog)
1441 {
1442     struct in_addr ip_addr;
1443     ip_addr.s_addr = htonl(INADDR_ANY);
1444     return ei_xlisten(ec, &ip_addr, port, backlog);
1445 }
1446 
ei_xlisten(ei_cnode * ec,struct in_addr * ip_addr,int * port,int backlog)1447 int ei_xlisten(ei_cnode *ec, struct in_addr *ip_addr, int *port, int backlog)
1448 {
1449     ei_socket_callbacks *cbs = ec->cbs;
1450     struct sockaddr_in sock_addr;
1451     void *ctx;
1452     int fd, err, len;
1453 
1454     err = ei_socket_ctx__(cbs, &ctx, ec->setup_context);
1455     if (err) {
1456         EI_TRACE_ERR2("ei_xlisten","-> SOCKET failed: %s (%d)",
1457                       estr(err), err);
1458         erl_errno = err;
1459         return ERL_ERROR;
1460     }
1461 
1462     memset((void *) &sock_addr, 0, sizeof(struct sockaddr_in));
1463     memcpy((void *) &sock_addr.sin_addr, (void *) ip_addr, sizeof(*ip_addr));
1464     sock_addr.sin_family = AF_INET;
1465     sock_addr.sin_port = htons((short) *port);
1466 
1467     len = sizeof(sock_addr);
1468     err = ei_listen_ctx__(cbs, ctx, (void *) &sock_addr, &len, backlog);
1469     if (err) {
1470         EI_TRACE_ERR2("ei_xlisten","-> listen failed: %s (%d)",
1471                       estr(err), err);
1472         erl_errno = err;
1473         goto error;
1474     }
1475 
1476     if (len != sizeof(sock_addr)) {
1477         if (len < offsetof(struct sockaddr_in, sin_addr) + sizeof(sock_addr.sin_addr)
1478             || len < offsetof(struct sockaddr_in, sin_port) + sizeof(sock_addr.sin_port)) {
1479             erl_errno = EIO;
1480             EI_TRACE_ERR0("ei_xlisten","-> get info failed");
1481             goto error;
1482         }
1483     }
1484 
1485     memcpy((void *) ip_addr, (void *) &sock_addr.sin_addr, sizeof(*ip_addr));
1486     *port = (int) ntohs(sock_addr.sin_port);
1487 
1488     err = EI_GET_FD__(cbs, ctx, &fd);
1489     if (err) {
1490         erl_errno = err;
1491         goto error;
1492     }
1493 
1494     if (put_ei_socket_info(fd, 0, null_cookie, ec, cbs, ctx) != 0) {
1495         EI_TRACE_ERR0("ei_xlisten","-> save socket info failed");
1496         erl_errno = EIO;
1497         goto error;
1498     }
1499 
1500     erl_errno = 0;
1501 
1502     return fd;
1503 
1504 error:
1505     abort_connection(cbs, ctx);
1506     return ERL_ERROR;
1507 }
1508 
close_connection(ei_socket_callbacks * cbs,void * ctx,int fd)1509 static int close_connection(ei_socket_callbacks *cbs, void *ctx, int fd)
1510 {
1511     int err;
1512     remove_ei_socket_info(fd);
1513     err = ei_close_ctx__(cbs, ctx);
1514     if (err) {
1515         erl_errno = err;
1516         return ERL_ERROR;
1517     }
1518     return 0;
1519 }
1520 
ei_close_connection(int fd)1521 int ei_close_connection(int fd)
1522 {
1523     ei_socket_callbacks *cbs;
1524     void *ctx;
1525     int err = EI_GET_CBS_CTX__(&cbs, &ctx, fd);
1526     if (err)
1527         erl_errno = err;
1528     else {
1529         if (close_connection(cbs, ctx, fd) == 0)
1530             return 0;
1531     }
1532     EI_TRACE_ERR2("ei_close_connection","<- CLOSE socket close failed: %s (%d)",
1533                   estr(erl_errno), erl_errno);
1534     return ERL_ERROR;
1535 } /* ei_close_connection */
1536 
abort_connection(ei_socket_callbacks * cbs,void * ctx)1537 static void abort_connection(ei_socket_callbacks *cbs, void *ctx)
1538 {
1539     (void) ei_close_ctx__(cbs, ctx);
1540 }
1541 
1542   /*
1543   * Accept and initiate a connection from another
1544   * Erlang node. Return a file descriptor at success,
1545   * otherwise -1;
1546 */
ei_accept(ei_cnode * ec,int lfd,ErlConnect * conp)1547 int ei_accept(ei_cnode* ec, int lfd, ErlConnect *conp)
1548 {
1549     return ei_accept_tmo(ec, lfd, conp, 0);
1550 }
1551 
ei_accept_tmo(ei_cnode * ec,int lfd,ErlConnect * conp,unsigned ms)1552 int ei_accept_tmo(ei_cnode* ec, int lfd, ErlConnect *conp, unsigned ms)
1553 {
1554     int fd;
1555     DistFlags her_flags;
1556     char tmp_nodename[MAXNODELEN+1];
1557     char send_name_tag;
1558     char *her_name;
1559     int pkt_sz, err;
1560     struct sockaddr_in addr;
1561     int addr_len = sizeof(struct sockaddr_in);
1562     ei_socket_callbacks *cbs;
1563     void *ctx;
1564     unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
1565 
1566     erl_errno = EIO;		/* Default error code */
1567 
1568     err = EI_GET_CBS_CTX__(&cbs, &ctx, lfd);
1569     if (err) {
1570         if (lfd < 0) {
1571             EI_CONN_SAVE_ERRNO__(err);
1572             return ERL_ERROR;
1573         }
1574         /*
1575          * This can be a listen socket created without ei_listen or ei_xlisten,
1576          * so we must assume it is.
1577          */
1578         cbs = &ei_default_socket_callbacks;
1579         ctx = EI_FD_AS_CTX__(lfd);
1580     }
1581 
1582     if (ec->cbs != cbs) {
1583         EI_CONN_SAVE_ERRNO__(EINVAL);
1584         return ERL_ERROR;
1585     }
1586 
1587     EI_TRACE_CONN0("ei_accept","<- ACCEPT waiting for connection");
1588 
1589     if (conp) {
1590         her_name = &conp->nodename[0];
1591     }
1592     else {
1593         her_name = &tmp_nodename[0];
1594     }
1595 
1596     /*
1597      * ei_accept_ctx_t__() replaces the pointer to the listen context
1598      * with a pointer to the accepted connection context on success.
1599      */
1600     err = ei_accept_ctx_t__(cbs, &ctx, (void *) &addr, &addr_len, tmo);
1601     if (err) {
1602 	EI_TRACE_ERR2("ei_accept","<- ACCEPT socket accept failed: %s (%d)",
1603                       estr(err), err);
1604         EI_CONN_SAVE_ERRNO__(err);
1605         return ERL_ERROR;
1606     }
1607 
1608     err = EI_GET_FD__(cbs, ctx, &fd);
1609     if (err) {
1610 	EI_TRACE_ERR2("ei_accept","<- ACCEPT get fd failed: %s (%d)",
1611                       estr(err), err);
1612         EI_CONN_SAVE_ERRNO__(err);
1613     }
1614 
1615     if (addr_len != sizeof(struct sockaddr_in)) {
1616         if (addr_len < (offsetof(struct sockaddr_in, sin_addr)
1617                         + sizeof(addr.sin_addr))) {
1618             EI_TRACE_ERR0("ei_accept","<- ACCEPT get addr failed");
1619             goto error;
1620         }
1621     }
1622 
1623     err = cbs->handshake_packet_header_size(ctx, &pkt_sz);
1624     if (err) {
1625 	EI_TRACE_ERR2("ei_accept","<- ACCEPT get packet size failed: %s (%d)",
1626                       estr(err), err);
1627         EI_CONN_SAVE_ERRNO__(err);
1628     }
1629 
1630     EI_TRACE_CONN0("ei_accept","<- ACCEPT connected to remote");
1631 
1632     if (recv_name(cbs, ctx, pkt_sz, &send_name_tag, &her_flags,
1633                   her_name, tmo)) {
1634 	EI_TRACE_ERR0("ei_accept","<- ACCEPT initial ident failed");
1635 	goto error;
1636     }
1637 
1638     {
1639         unsigned her_version = (her_flags & DFLAG_HANDSHAKE_23) ? 6 : 5;
1640 	unsigned our_challenge;
1641 	unsigned her_challenge;
1642 	unsigned char our_digest[16];
1643 
1644 	if (send_status(cbs, ctx, pkt_sz, "ok", tmo))
1645 	    goto error;
1646 	our_challenge = gen_challenge();
1647 	if (send_challenge(ec, ctx, pkt_sz, our_challenge, her_flags, tmo))
1648 	    goto error;
1649         if (send_name_tag == 'n' && (her_flags & DFLAG_HANDSHAKE_23)) {
1650             if (recv_complement(cbs, ctx, pkt_sz, tmo))
1651                 goto error;
1652         }
1653 	if (recv_challenge_reply(cbs, ctx, pkt_sz, our_challenge,
1654                                  ec->ei_connect_cookie, &her_challenge, tmo))
1655 	    goto error;
1656 	gen_digest(her_challenge, ec->ei_connect_cookie, our_digest);
1657 	if (send_challenge_ack(cbs, ctx, pkt_sz, our_digest, tmo))
1658 	    goto error;
1659 	if (put_ei_socket_info(fd, her_version, null_cookie, ec, cbs, ctx) != 0)
1660             goto error;
1661     }
1662     if (conp) {
1663         memcpy((void *) conp->ipadr, (void *) &addr.sin_addr, sizeof(conp->ipadr));
1664     }
1665 
1666     if (cbs->accept_handshake_complete) {
1667         err = cbs->accept_handshake_complete(ctx);
1668         if (err) {
1669             EI_TRACE_ERR2("ei_xconnect","-> ACCEPT handshake failed: %s (%d)",
1670                           estr(err), err);
1671             close_connection(cbs, ctx, fd);
1672             EI_CONN_SAVE_ERRNO__(err);
1673             return ERL_ERROR;
1674         }
1675     }
1676 
1677     EI_TRACE_CONN1("ei_accept","<- ACCEPT (ok) remote = %s",her_name);
1678 
1679     erl_errno = 0;		/* No error */
1680     return fd;
1681 
1682 error:
1683     EI_TRACE_ERR0("ei_accept","<- ACCEPT failed");
1684     abort_connection(cbs, ctx);
1685     return ERL_ERROR;
1686 } /* ei_accept */
1687 
1688 
1689 /* Receives a message from an Erlang socket.
1690  * If the message was a TICK it is immediately
1691  * answered. Returns: ERL_ERROR, ERL_TICK or
1692  * the number of bytes read.
1693  */
ei_receive_tmo(int fd,unsigned char * bufp,int bufsize,unsigned ms)1694 int ei_receive_tmo(int fd, unsigned char *bufp, int bufsize, unsigned ms)
1695 {
1696     ssize_t len;
1697     unsigned char fourbyte[4]={0,0,0,0};
1698     int err;
1699     ei_socket_callbacks *cbs;
1700     void *ctx;
1701     unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
1702 
1703     err = EI_GET_CBS_CTX__(&cbs, &ctx, fd);
1704     if (err) {
1705         EI_CONN_SAVE_ERRNO__(err);
1706         return ERL_ERROR;
1707     }
1708 
1709     len = (ssize_t) 4;
1710     err = ei_read_fill_ctx_t__(cbs, ctx, (char *) bufp, &len, tmo);
1711     if (!err && len != (ssize_t) 4)
1712         err = EIO;
1713     if (err) {
1714         EI_CONN_SAVE_ERRNO__(err);
1715 	return ERL_ERROR;
1716     }
1717 
1718     /* Tick handling */
1719     len = get_int32(bufp);
1720     if (len == ERL_TICK) {
1721         len = 4;
1722 	ei_write_fill_ctx_t__(cbs, ctx, (char *) fourbyte, &len, tmo);
1723 	/* FIXME ok to ignore error or timeout? */
1724 	erl_errno = EAGAIN;
1725 	return ERL_TICK;
1726     }
1727 
1728     if (len > bufsize) {
1729 	/* FIXME: We should drain the message. */
1730 	erl_errno = EMSGSIZE;
1731 	return ERL_ERROR;
1732     }
1733     else {
1734         ssize_t need = len;
1735         err = ei_read_fill_ctx_t__(cbs, ctx, (char *) bufp, &len, tmo);
1736         if (err) {
1737             EI_CONN_SAVE_ERRNO__(err);
1738             return ERL_ERROR;
1739         }
1740         if (len != need) {
1741             erl_errno = EIO;
1742             return ERL_ERROR;
1743         }
1744     }
1745 
1746     return (int) len;
1747 
1748 }
1749 
ei_receive(int fd,unsigned char * bufp,int bufsize)1750 int ei_receive(int fd, unsigned char *bufp, int bufsize)
1751 {
1752     return ei_receive_tmo(fd, bufp, bufsize, 0);
1753 }
1754 
ei_reg_send_tmo(ei_cnode * ec,int fd,char * server_name,char * buf,int len,unsigned ms)1755 int ei_reg_send_tmo(ei_cnode* ec, int fd, char *server_name,
1756 		    char* buf, int len, unsigned ms)
1757 {
1758     /* erl_errno and return code is set by ei_reg_send_encoded_tmo() */
1759     return ei_send_reg_encoded_tmo(fd, ei_self(ec), server_name, buf, len, ms);
1760 }
1761 
1762 
ei_reg_send(ei_cnode * ec,int fd,char * server_name,char * buf,int len)1763 int ei_reg_send(ei_cnode* ec, int fd, char *server_name, char* buf, int len)
1764 {
1765     return ei_reg_send_tmo(ec,fd,server_name,buf,len,0);
1766 }
1767 
1768 /*
1769 * Sends an Erlang message to a process at an Erlang node
1770 */
ei_send_tmo(int fd,erlang_pid * to,char * buf,int len,unsigned ms)1771 int ei_send_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned ms)
1772 {
1773     /* erl_errno and return code is set by ei_reg_send_encoded_tmo() */
1774     return ei_send_encoded_tmo(fd, to, buf, len, ms);
1775 }
1776 
ei_send(int fd,erlang_pid * to,char * buf,int len)1777 int ei_send(int fd, erlang_pid* to, char* buf, int len)
1778 {
1779     return ei_send_tmo(fd, to, buf, len, 0);
1780 }
1781 
1782 
1783 /*
1784 * Try to receive an Erlang message on a given socket. Returns
1785 * ERL_TICK, ERL_MSG, or ERL_ERROR. Sets `erl_errno' on ERL_ERROR and
1786 * ERL_TICK (to EAGAIN in the latter case).
1787 */
1788 
ei_do_receive_msg(int fd,int staticbuffer_p,erlang_msg * msg,ei_x_buff * x,unsigned ms)1789 int ei_do_receive_msg(int fd, int staticbuffer_p,
1790 		      erlang_msg* msg, ei_x_buff* x, unsigned ms)
1791 {
1792     int msglen;
1793     int i;
1794 
1795     if (!(i=ei_recv_internal(fd, &x->buff, &x->buffsz, msg, &msglen,
1796 	staticbuffer_p, ms))) {
1797 	erl_errno = EAGAIN;
1798 	return ERL_TICK;
1799     }
1800     if (i<0) {
1801 	/* erl_errno set by ei_recv_internal() */
1802 	return ERL_ERROR;
1803     }
1804     if (staticbuffer_p && msglen > x->buffsz)
1805     {
1806 	erl_errno = EMSGSIZE;
1807 	return ERL_ERROR;
1808     }
1809     x->index = msglen;
1810     switch (msg->msgtype) {	/* FIXME does not handle trace tokens and monitors */
1811     case ERL_SEND:
1812     case ERL_REG_SEND:
1813     case ERL_LINK:
1814     case ERL_UNLINK:
1815     case ERL_GROUP_LEADER:
1816     case ERL_EXIT:
1817     case ERL_EXIT2:
1818 	return ERL_MSG;
1819 
1820     default:
1821 	/*if (emsg->to) 'erl'_free_term(emsg->to);
1822 	  if (emsg->from) 'erl'_free_term(emsg->from);
1823 	  if (emsg->msg) 'erl'_free_term(emsg->msg);
1824 	  emsg->to = NULL;
1825 	  emsg->from = NULL;
1826 	  emsg->msg = NULL;*/
1827 
1828 	erl_errno = EIO;
1829 	return ERL_ERROR;
1830     }
1831 } /* do_receive_msg */
1832 
1833 
ei_receive_msg(int fd,erlang_msg * msg,ei_x_buff * x)1834 int ei_receive_msg(int fd, erlang_msg* msg, ei_x_buff* x)
1835 {
1836     return ei_do_receive_msg(fd, 1, msg, x, 0);
1837 }
1838 
ei_xreceive_msg(int fd,erlang_msg * msg,ei_x_buff * x)1839 int ei_xreceive_msg(int fd, erlang_msg *msg, ei_x_buff *x)
1840 {
1841     return ei_do_receive_msg(fd, 0, msg, x, 0);
1842 }
1843 
ei_receive_msg_tmo(int fd,erlang_msg * msg,ei_x_buff * x,unsigned ms)1844 int ei_receive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned ms)
1845 {
1846     return ei_do_receive_msg(fd, 1, msg, x, ms);
1847 }
1848 
ei_xreceive_msg_tmo(int fd,erlang_msg * msg,ei_x_buff * x,unsigned ms)1849 int ei_xreceive_msg_tmo(int fd, erlang_msg *msg, ei_x_buff *x, unsigned ms)
1850 {
1851     return ei_do_receive_msg(fd, 0, msg, x, ms);
1852 }
1853 
1854 /*
1855 * A remote process call consists of two parts, sending a request and
1856 * receiving a response. This function sends the request and the
1857 * ei_rpc_from function receives the response.
1858 *
1859 * Here is the term that is sent when (flags & EI_RPC_FETCH_STDOUT) != 0:
1860 *
1861 * { PidFrom, { call, Mod, Fun, Args, send_stdout_to_caller }}
1862 *
1863 * Here is the term that is sent otherwise:
1864 *
1865 * { PidFrom, { call, Mod, Fun, Args, user }}
1866 *
1867 * Returns a non-negative number for success and a negative number for
1868 * failure.
1869 *
1870 */
ei_xrpc_to(ei_cnode * ec,int fd,char * mod,char * fun,const char * buf,int len,int flags)1871 int ei_xrpc_to(ei_cnode *ec, int fd, char *mod, char *fun,
1872                const char *buf, int len, int flags)
1873 {
1874     ei_x_buff x;
1875     erlang_pid *self = ei_self(ec);
1876     int err = ERL_ERROR;
1877 
1878     /* encode header */
1879     if (ei_x_new_with_version(&x) < 0)
1880         goto einval;
1881     if (ei_x_encode_tuple_header(&x, 2) < 0)  /* A */
1882         goto einval;
1883 
1884     if (ei_x_encode_pid(&x, self) < 0)	      /* A 1 */
1885         goto einval;
1886 
1887     if (ei_x_encode_tuple_header(&x, 5) < 0)  /* B A 2 */
1888         goto einval;
1889     if (ei_x_encode_atom(&x, "call") < 0)     /* B 1 */
1890         goto einval;
1891     if (ei_x_encode_atom(&x, mod) < 0)	      /* B 2 */
1892         goto einval;
1893     if (ei_x_encode_atom(&x, fun) < 0)	      /* B 3 */
1894         goto einval;
1895     if (ei_x_append_buf(&x, buf, len) < 0)    /* B 4 */
1896         goto einval;
1897     if (flags & EI_RPC_FETCH_STDOUT) {
1898         if (ei_x_encode_atom(&x, "send_stdout_to_caller") < 0)     /* B 5 */
1899             goto einval;
1900     } else {
1901         if (ei_x_encode_atom(&x, "user") < 0)     /* B 5 */
1902             goto einval;
1903     }
1904 
1905     err = ei_send_reg_encoded(fd, self, "rex", x.buff, x.index);
1906     if (err)
1907         goto error;
1908 
1909     ei_x_free(&x);
1910 
1911     return 0;
1912 
1913 einval:
1914     EI_CONN_SAVE_ERRNO__(EINVAL);
1915 
1916 error:
1917     if (x.buff != NULL)
1918         ei_x_free(&x);
1919     return err;
1920 } /* xrpc_to */
1921 
1922 
ei_rpc_to(ei_cnode * ec,int fd,char * mod,char * fun,const char * buf,int len)1923 int ei_rpc_to(ei_cnode *ec, int fd, char *mod, char *fun,
1924 	      const char *buf, int len)
1925 {
1926     return ei_xrpc_to(ec, fd, mod, fun, buf, len, 0);
1927 } /* rpc_to */
1928 
1929   /*
1930   * And here is the rpc receiving part. A negative
1931   * timeout means 'infinity'. Returns either of: ERL_MSG,
1932   * ERL_TICK, ERL_ERROR or ERL_TIMEOUT.
1933 */
ei_rpc_from(ei_cnode * ec,int fd,int timeout,erlang_msg * msg,ei_x_buff * x)1934 int ei_rpc_from(ei_cnode *ec, int fd, int timeout, erlang_msg *msg,
1935 		ei_x_buff *x)
1936 {
1937     unsigned tmo = timeout < 0 ? EI_SCLBK_INF_TMO : (unsigned) timeout;
1938     int res = ei_xreceive_msg_tmo(fd, msg, x, tmo);
1939     if (res < 0 && erl_errno == ETIMEDOUT)
1940         return ERL_TIMEOUT;
1941     return res;
1942 } /* rpc_from */
1943 
ei_rpc(ei_cnode * ec,int fd,char * mod,char * fun,const char * inbuf,int inbuflen,ei_x_buff * x)1944 int ei_rpc(ei_cnode* ec, int fd, char *mod, char *fun,
1945 	   const char* inbuf, int inbuflen, ei_x_buff* x)
1946 {
1947     int i, index;
1948     ei_term t;
1949     erlang_msg msg;
1950     char rex[MAXATOMLEN];
1951 
1952     if (ei_rpc_to(ec, fd, mod, fun, inbuf, inbuflen) < 0) {
1953 	return ERL_ERROR;
1954     }
1955 
1956     /* ei_rpc_from() responds with a tick if it gets one... */
1957     while ((i = ei_rpc_from(ec, fd, ERL_NO_TIMEOUT, &msg, x)) == ERL_TICK)
1958 	;
1959 
1960     if (i == ERL_ERROR)  return i;
1961 
1962     /* Expect: {rex, RPC_Reply} */
1963 
1964     index = 0;
1965     if (ei_decode_version(x->buff, &index, &i) < 0)
1966         goto ebadmsg;
1967 
1968     if (ei_decode_ei_term(x->buff, &index, &t) < 0)
1969         goto ebadmsg;
1970 
1971     if (t.ei_type != ERL_SMALL_TUPLE_EXT && t.ei_type != ERL_LARGE_TUPLE_EXT)
1972         goto ebadmsg;
1973 
1974     if (t.arity != 2)
1975         goto ebadmsg;
1976 
1977     if (ei_decode_atom(x->buff, &index, rex) < 0)
1978         goto ebadmsg;
1979 
1980     if (strcmp("rex", rex) != 0)
1981         goto ebadmsg;
1982 
1983     /* remove header */
1984     x->index -= index;
1985     memmove(x->buff, &x->buff[index], x->index);
1986     return 0;
1987 
1988 ebadmsg:
1989 
1990     EI_CONN_SAVE_ERRNO__(EBADMSG);
1991     return ERL_ERROR;
1992 }
1993 
1994 
1995   /*
1996   ** Handshake
1997 */
1998 
1999 
2000 /* FROM RTP RFC 1889  (except that we use all bits, bug in RFC?) */
md_32(char * string,int length)2001 static unsigned int md_32(char* string, int length)
2002 {
2003     MD5_CTX ctx;
2004     union {
2005 	char c[16];
2006 	unsigned x[4];
2007     } digest;
2008     ei_MD5Init(&ctx);
2009     ei_MD5Update(&ctx, (unsigned char *) string,
2010 	       (unsigned) length);
2011     ei_MD5Final((unsigned char *) digest.c, &ctx);
2012     return (digest.x[0] ^ digest.x[1] ^ digest.x[2] ^ digest.x[3]);
2013 }
2014 
2015 #if defined(__WIN32__)
gen_challenge(void)2016 unsigned int gen_challenge(void)
2017 {
2018     struct {
2019 	SYSTEMTIME tv;
2020 	DWORD cpu;
2021 	int pid;
2022     } s;
2023     GetSystemTime(&s.tv);
2024     s.cpu  = GetTickCount();
2025     s.pid  = getpid();
2026     return md_32((char*) &s, sizeof(s));
2027 }
2028 
2029 #else  /* some unix */
2030 
gen_challenge(void)2031 static unsigned int gen_challenge(void)
2032 {
2033     struct {
2034 	struct timeval tv;
2035 	clock_t cpu;
2036 	pid_t pid;
2037 	u_long hid;
2038 	uid_t uid;
2039 	gid_t gid;
2040 	struct utsname name;
2041     } s;
2042 
2043     memset(&s, 0, sizeof(s));
2044     gettimeofday(&s.tv, 0);
2045     uname(&s.name);
2046     s.cpu  = clock();
2047     s.pid  = getpid();
2048 #if defined(__ANDROID__) || defined(__HAIKU__)
2049     s.hid  = 0;
2050 #else
2051     s.hid  = gethostid();
2052 #endif
2053     s.uid  = getuid();
2054     s.gid  = getgid();
2055 
2056     return md_32((char*) &s, sizeof(s));
2057 }
2058 #endif
2059 
gen_digest(unsigned challenge,char cookie[],unsigned char digest[16])2060 static void gen_digest(unsigned challenge, char cookie[],
2061 		       unsigned char digest[16])
2062 {
2063     MD5_CTX c;
2064 
2065     char chbuf[21];
2066 
2067     sprintf(chbuf,"%u", challenge);
2068     ei_MD5Init(&c);
2069     ei_MD5Update(&c, (unsigned char *) cookie,
2070 	       (unsigned) strlen(cookie));
2071     ei_MD5Update(&c, (unsigned char *) chbuf,
2072 	       (unsigned) strlen(chbuf));
2073     ei_MD5Final(digest, &c);
2074 }
2075 
2076 
hex(char digest[16],char buff[33])2077 static char *hex(char digest[16], char buff[33])
2078 {
2079     static char tab[] = "0123456789abcdef";
2080     unsigned char *d = (unsigned char *) digest;
2081     //static char buff[sizeof(digest)*2 + 1];
2082     char *p = buff;
2083     int i;
2084 
2085     for (i = 0; i < 16; ++i) {
2086 	*p++ = tab[(int)((*d) >> 4)];
2087 	*p++ = tab[(int)((*d++) & 0xF)];
2088     }
2089     *p = '\0';
2090     return buff;
2091 }
2092 
read_hs_package(ei_socket_callbacks * cbs,void * ctx,int pkt_sz,char ** buf,int * buflen,int * is_static,unsigned ms)2093 static int read_hs_package(ei_socket_callbacks *cbs, void *ctx,
2094                            int pkt_sz, char **buf, int *buflen,
2095                            int *is_static, unsigned ms)
2096 {
2097     unsigned char nbuf[4];
2098     unsigned char *x = nbuf;
2099     ssize_t len, need;
2100     int err;
2101 
2102     len = (ssize_t) pkt_sz;
2103     err = ei_read_fill_ctx_t__(cbs, ctx, (char *)nbuf, &len, ms);
2104     if (!err && len != (ssize_t) pkt_sz)
2105         err = EIO;
2106     if (err) {
2107         EI_CONN_SAVE_ERRNO__(err);
2108 	return -1;
2109     }
2110 
2111     switch (pkt_sz) {
2112     case 2:
2113         len = get16be(x);
2114         break;
2115     case 4:
2116         len = get32be(x);
2117         break;
2118     default:
2119         return -1;
2120     }
2121 
2122     if (len > *buflen) {
2123 	if (*is_static) {
2124 	    char *tmp = malloc(len);
2125 	    if (!tmp) {
2126 		erl_errno = ENOMEM;
2127 		return -1;
2128 	    }
2129 	    *buf = tmp;
2130 	    *is_static = 0;
2131 	    *buflen = len;
2132 	} else {
2133 	    char *tmp = realloc(*buf, len);
2134 	    if (!tmp) {
2135 		erl_errno = ENOMEM;
2136 		return -1;
2137 	    }
2138 	    *buf = tmp;
2139 	    *buflen = len;
2140 	}
2141     }
2142     need = len;
2143     err = ei_read_fill_ctx_t__(cbs, ctx, *buf, &len, ms);
2144     if (!err && len != need)
2145         err = EIO;
2146     if (err) {
2147         EI_CONN_SAVE_ERRNO__(err);
2148 	return -1;
2149     }
2150     return len;
2151 }
2152 
2153 
send_status(ei_socket_callbacks * cbs,void * ctx,int pkt_sz,char * status,unsigned ms)2154 static int send_status(ei_socket_callbacks *cbs, void *ctx,
2155                        int pkt_sz, char *status, unsigned ms)
2156 {
2157     char *buf, *s;
2158     char dbuf[DEFBUF_SIZ];
2159     int siz = strlen(status) + 1 + pkt_sz;
2160     int err, ret;
2161     ssize_t len;
2162 
2163     buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
2164     if (!buf) {
2165 	erl_errno = ENOMEM;
2166 	return -1;
2167     }
2168     s = buf;
2169     switch (pkt_sz) {
2170     case 2:
2171         put16be(s,siz - 2);
2172         break;
2173     case 4:
2174         put32be(s,siz - 4);
2175         break;
2176     default:
2177         ret = -1;
2178         goto done;
2179     }
2180     put8(s, 's');
2181     memcpy(s, status, strlen(status));
2182     len = (ssize_t) siz;
2183     err = ei_write_fill_ctx_t__(cbs, ctx, buf, &len, ms);
2184     if (!err && len != (ssize_t) siz)
2185         err = EIO;
2186     if (err) {
2187 	EI_TRACE_ERR2("send_status","-> SEND_STATUS socket write failed: %s (%d)",
2188                       estr(err), err);
2189         EI_CONN_SAVE_ERRNO__(err);
2190 	ret = -1;
2191     }
2192     else {
2193         EI_TRACE_CONN1("send_status","-> SEND_STATUS (%s)",status);
2194         ret =  0;
2195     }
2196 done:
2197     if (buf != dbuf)
2198 	free(buf);
2199     return ret;
2200 }
2201 
recv_status(ei_cnode * ec,void * ctx,int pkt_sz,unsigned ms)2202 static int recv_status(ei_cnode *ec, void *ctx,
2203                        int pkt_sz, unsigned ms)
2204 {
2205     char dbuf[DEFBUF_SIZ];
2206     char *buf = dbuf;
2207     int is_static = 1;
2208     int buflen = DEFBUF_SIZ;
2209     int rlen;
2210 
2211     if ((rlen = read_hs_package(ec->cbs, ctx, pkt_sz,
2212                                 &buf, &buflen, &is_static, ms)) <= 0) {
2213 	EI_TRACE_ERR1("recv_status",
2214 		      "<- RECV_STATUS socket read failed (%d)", rlen);
2215 	goto error;
2216     }
2217 
2218     EI_TRACE_CONN2("recv_status",
2219                    "<- RECV_STATUS (%.*s)", (rlen>20 ? 20 : rlen), buf);
2220 
2221     if (ec->thisnodename[0]) {
2222         if (rlen >= 3 && buf[0] == 's' && buf[1] == 'o' && buf[2] == 'k') {
2223             /* Expecting "sok" or "sok_simultaneous" */
2224             if (!is_static)
2225                 free(buf);
2226             return 0;
2227         }
2228     }
2229     else { /* dynamic node name */
2230         const char* at;
2231         int namelen;
2232         if (rlen >= 7 && strncmp(buf, "snamed:", 7) == 0) {
2233             buf += 7;
2234             rlen -= 7;
2235             namelen = get16be(buf);
2236             rlen -= 2;
2237             if (namelen > MAXNODELEN || (namelen+4) > rlen) {
2238                 EI_TRACE_ERR1("recv_status","<- RECV_STATUS nodename too long (%d)",
2239                               namelen);
2240                 goto error;
2241             }
2242             memcpy(ec->thisnodename, buf, namelen);
2243             ec->thisnodename[namelen] = '\0';
2244             buf += namelen;
2245             ec->creation = get32be(buf);
2246 
2247             /* extract alive part from nodename */
2248             if (!(at = strchr(ec->thisnodename,'@'))) {
2249                 EI_TRACE_ERR0("ei_connect","Dynamic node name has no @ in name");
2250                 return ERL_ERROR;
2251             } else {
2252                 const int alen = at - ec->thisnodename;
2253                 strncpy(ec->thisalivename, ec->thisnodename, alen);
2254                 ec->thisalivename[alen] = '\0';
2255             }
2256 
2257             strcpy(ec->self.node, ec->thisnodename);
2258             ec->self.num = 0;
2259             ec->self.serial = 0;
2260             ec->self.creation = ec->creation;
2261             return 0;
2262         }
2263     }
2264 error:
2265     if (!is_static)
2266 	free(buf);
2267     return -1;
2268 }
2269 
preferred_flags(void)2270 static DistFlags preferred_flags(void)
2271 {
2272     DistFlags flags =
2273         DFLAG_EXTENDED_REFERENCES
2274         | DFLAG_DIST_MONITOR
2275         | DFLAG_EXTENDED_PIDS_PORTS
2276         | DFLAG_FUN_TAGS
2277         | DFLAG_NEW_FUN_TAGS
2278         | DFLAG_NEW_FLOATS
2279         | DFLAG_SMALL_ATOM_TAGS
2280         | DFLAG_UTF8_ATOMS
2281         | DFLAG_MAP_TAG
2282         | DFLAG_BIG_CREATION
2283         | DFLAG_EXPORT_PTR_TAG
2284         | DFLAG_BIT_BINARIES
2285         | DFLAG_HANDSHAKE_23
2286         | DFLAG_V4_NC
2287         | DFLAG_UNLINK_ID;
2288     if (ei_internal_use_21_bitstr_expfun()) {
2289         flags &= ~(DFLAG_EXPORT_PTR_TAG
2290                    | DFLAG_BIT_BINARIES);
2291     }
2292     return flags;
2293 }
2294 
send_name(ei_cnode * ec,void * ctx,int pkt_sz,unsigned version,unsigned ms)2295 static int send_name(ei_cnode *ec,
2296                      void *ctx,
2297                      int pkt_sz,
2298                      unsigned version,
2299                      unsigned ms)
2300 {
2301     char *buf;
2302     unsigned char *s;
2303     char dbuf[DEFBUF_SIZ];
2304     const char* name_ptr;
2305     unsigned int name_len;
2306     int siz;
2307     int err, ret;
2308     ssize_t len;
2309     DistFlags flags = preferred_flags();
2310     char tag;
2311 
2312     if (ec->thisnodename[0]) {
2313         name_ptr = ec->thisnodename;
2314         tag = (version == EI_DIST_5) ? 'n' : 'N';
2315     }
2316     else {
2317         /* dynamic node name */
2318         name_ptr = ec->thishostname;
2319         tag = 'N'; /* presume ver 6 */
2320         flags |= DFLAG_NAME_ME;
2321     }
2322 
2323     name_len = strlen(name_ptr);
2324 
2325     if (tag == 'n')
2326         siz = pkt_sz + 1 + 2 + 4 + name_len;
2327     else
2328         siz = pkt_sz + 1 + 8 + 4 + 2 + name_len;
2329 
2330     buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
2331     if (!buf) {
2332 	erl_errno = ENOMEM;
2333 	return -1;
2334     }
2335     s = (unsigned char *)buf;
2336     switch (pkt_sz) {
2337     case 2:
2338         put16be(s,siz - 2);
2339         break;
2340     case 4:
2341         put32be(s,siz - 4);
2342         break;
2343     default:
2344         ret = -1;
2345         goto done;
2346     }
2347 
2348     put8(s, tag);
2349     if (tag == 'n') {
2350         put16be(s, EI_DIST_5); /* some impl (jinterface) demand ver==5 */
2351         put32be(s, flags);
2352     }
2353     else { /* tag == 'N' */
2354         put64be(s, flags);
2355         put32be(s, ec->creation);
2356         put16be(s, name_len);
2357     }
2358     memcpy(s, name_ptr, name_len);
2359     len = (ssize_t) siz;
2360     err = ei_write_fill_ctx_t__(ec->cbs, ctx, buf, &len, ms);
2361     if (!err && len != (ssize_t) siz)
2362         err = EIO;
2363     if (err) {
2364 	EI_TRACE_ERR0("send_name", "SEND_NAME -> socket write failed");
2365         EI_CONN_SAVE_ERRNO__(err);
2366 	ret = -1;
2367     }
2368     else {
2369         ret = 0;
2370     }
2371 done:
2372     if (buf != dbuf)
2373 	free(buf);
2374     return ret;
2375 }
2376 
send_challenge(ei_cnode * ec,void * ctx,int pkt_sz,unsigned challenge,DistFlags her_flags,unsigned ms)2377 static int send_challenge(ei_cnode *ec,
2378                           void *ctx,
2379                           int pkt_sz,
2380                           unsigned challenge,
2381                           DistFlags her_flags,
2382                           unsigned ms)
2383 {
2384     char *buf;
2385     unsigned char *s;
2386     char dbuf[DEFBUF_SIZ];
2387     const unsigned int nodename_len = strlen(ec->thisnodename);
2388     int siz;
2389     int err, ret;
2390     ssize_t len;
2391     DistFlags flags;
2392     const char tag = (her_flags & DFLAG_HANDSHAKE_23) ? 'N' : 'n';
2393 
2394     if (tag == 'n')
2395         siz = pkt_sz + 1 + 2 + 4 + 4 + nodename_len;
2396     else
2397         siz = pkt_sz + 1 + 8 + 4 + 4 + 2 + nodename_len;
2398 
2399     buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
2400     if (!buf) {
2401 	erl_errno = ENOMEM;
2402 	return -1;
2403     }
2404     s = (unsigned char *)buf;
2405     switch (pkt_sz) {
2406     case 2:
2407         put16be(s,siz - 2);
2408         break;
2409     case 4:
2410         put32be(s,siz - 4);
2411         break;
2412     default:
2413         ret = -1;
2414         goto done;
2415     }
2416 
2417     flags = preferred_flags();
2418     put8(s, tag);
2419     if (tag == 'n') {
2420         put16be(s, EI_DIST_5);  /* choosen version */
2421         put32be(s, flags);
2422         put32be(s, challenge);
2423     }
2424     else {
2425         put64be(s, flags);
2426         put32be(s, challenge);
2427         put32be(s, ec->creation);
2428         put16be(s, nodename_len);
2429     }
2430     memcpy(s, ec->thisnodename, nodename_len);
2431     len = (ssize_t) siz;
2432     err = ei_write_fill_ctx_t__(ec->cbs, ctx, buf, &len, ms);
2433     if (!err && len != (ssize_t) siz)
2434         err = EIO;
2435     if (err) {
2436 	EI_TRACE_ERR0("send_challenge", "-> SEND_CHALLENGE socket write failed");
2437         EI_CONN_SAVE_ERRNO__(err);
2438 	ret = -1;
2439     }
2440     else
2441         ret = 0;
2442 done:
2443     if (buf != dbuf)
2444 	free(buf);
2445     return ret;
2446 }
2447 
recv_challenge(ei_socket_callbacks * cbs,void * ctx,int pkt_sz,unsigned * challenge,DistFlags * flags,char * namebuf,unsigned ms)2448 static int recv_challenge(ei_socket_callbacks *cbs, void *ctx,
2449                           int pkt_sz, unsigned *challenge,
2450 			  DistFlags *flags, char *namebuf, unsigned ms)
2451 {
2452     char dbuf[DEFBUF_SIZ];
2453     char *buf = dbuf;
2454     int is_static = 1;
2455     int buflen = DEFBUF_SIZ;
2456     int rlen, nodename_len;
2457     unsigned version;
2458     char *s;
2459     char tag;
2460     char tmp_nodename[MAXNODELEN+1];
2461 
2462     erl_errno = EIO;		/* Default */
2463 
2464     if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen,
2465                                 &is_static, ms)) <= 0) {
2466 	EI_TRACE_ERR1("recv_challenge",
2467 		      "<- RECV_CHALLENGE socket read failed (%d)",rlen);
2468 	goto error;
2469     }
2470     s = buf;
2471     tag = get8(s);
2472     if (tag != 'n' && tag != 'N') {
2473 	EI_TRACE_ERR2("recv_challenge",
2474 		      "<- RECV_CHALLENGE incorrect tag, "
2475 		      "expected 'n' or 'N', got '%c' (%u)",tag,tag);
2476 	goto error;
2477     }
2478     if (tag == 'n') { /* OLD */
2479         if (rlen < 1+2+4+4) {
2480             EI_TRACE_ERR1("recv_challenge","<- RECV_CHALLENGE 'n' packet too short (%d)",
2481                           rlen)
2482             goto error;
2483         }
2484 
2485         version = get16be(s);
2486         if (version != EI_DIST_5) {
2487             EI_TRACE_ERR1("recv_challenge",
2488                           "<- RECV_CHALLENGE 'n' incorrect version=%d",
2489                           version);
2490             goto error;
2491         }
2492         *flags = get32be(s);
2493         *challenge = get32be(s);
2494         nodename_len = (buf + rlen) - s;
2495     }
2496     else { /* NEW */
2497         if (rlen < 1+8+4+4+2) {
2498             EI_TRACE_ERR1("recv_challenge","<- RECV_CHALLENGE 'N' packet too short (%d)",
2499                           rlen)
2500             goto error;
2501         }
2502         version = EI_DIST_6;
2503         *flags = get64be(s);
2504         *challenge = get32be(s);
2505         s += 4; /* ignore peer 'creation' */
2506         nodename_len = get16be(s);
2507         if (nodename_len > (buf + rlen) - s) {
2508             EI_TRACE_ERR1("recv_challenge",
2509                           "<- RECV_CHALLENGE 'N' nodename too long (%d)",
2510                           nodename_len);
2511             goto error;
2512         }
2513     }
2514 
2515     if (nodename_len > MAXNODELEN) {
2516         EI_TRACE_ERR1("recv_challenge",
2517                       "<- RECV_CHALLENGE nodename too long (%d)", nodename_len);
2518         goto error;
2519     }
2520 
2521     if (!(*flags & DFLAG_EXTENDED_REFERENCES)) {
2522 	EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE peer cannot "
2523 		      "handle extended references");
2524 	goto error;
2525     }
2526 
2527     if (!(*flags & DFLAG_EXTENDED_PIDS_PORTS)) {
2528 	EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE peer cannot "
2529 		      "handle extended pids and ports");
2530 	erl_errno = EIO;
2531 	goto error;
2532     }
2533 
2534     if (!(*flags & DFLAG_NEW_FLOATS)) {
2535 	EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE peer cannot "
2536 		      "handle binary float encoding");
2537 	goto error;
2538     }
2539 
2540     if (!namebuf)
2541         namebuf = &tmp_nodename[0];
2542 
2543     memcpy(namebuf, s, nodename_len);
2544     namebuf[nodename_len] = '\0';
2545 
2546     if (!is_static)
2547 	free(buf);
2548     EI_TRACE_CONN4("recv_challenge","<- RECV_CHALLENGE (ok) node = %s, "
2549 	    "version = %u, "
2550 	    "flags = %u, "
2551 	    "challenge = %d",
2552 	    namebuf,
2553 	    version,
2554 	    *flags,
2555 	    *challenge
2556 	    );
2557     erl_errno = 0;
2558     return 0;
2559 error:
2560     if (!is_static)
2561 	free(buf);
2562     return -1;
2563 }
2564 
send_complement(ei_cnode * ec,void * ctx,int pkt_sz,unsigned epmd_says_version,DistFlags her_flags,unsigned ms)2565 static int send_complement(ei_cnode *ec,
2566                             void *ctx,
2567                             int pkt_sz,
2568                             unsigned epmd_says_version,
2569                             DistFlags her_flags,
2570                             unsigned ms)
2571 {
2572     int ret = 0;
2573     if (epmd_says_version == EI_DIST_5 && (her_flags & DFLAG_HANDSHAKE_23)) {
2574         char *buf;
2575         unsigned char *s;
2576         char dbuf[DEFBUF_SIZ];
2577         int err;
2578         ssize_t len;
2579         unsigned int flagsHigh;
2580         const int siz = pkt_sz + 1 + 4 + 4;
2581 
2582         buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf;
2583         if (!buf) {
2584             erl_errno = ENOMEM;
2585             return -1;
2586         }
2587         s = (unsigned char *)buf;
2588         switch (pkt_sz) {
2589         case 2:
2590             put16be(s,siz - 2);
2591             break;
2592         case 4:
2593             put32be(s,siz - 4);
2594             break;
2595         default:
2596             ret = -1;
2597             goto done;
2598         }
2599         flagsHigh = preferred_flags() >> 32;
2600 
2601         put8(s, 'c');
2602         put32be(s, flagsHigh);
2603         put32be(s, ec->creation);
2604 
2605         len = (ssize_t) siz;
2606         err = ei_write_fill_ctx_t__(ec->cbs, ctx, buf, &len, ms);
2607         if (!err && len != (ssize_t) siz)
2608             err = EIO;
2609         if (err) {
2610             EI_TRACE_ERR0("send_name", "SEND_NAME -> socket write failed");
2611             EI_CONN_SAVE_ERRNO__(err);
2612             ret = -1;
2613         }
2614     done:
2615         if (buf != dbuf)
2616             free(buf);
2617     }
2618     return ret;
2619 }
2620 
2621 
send_challenge_reply(ei_socket_callbacks * cbs,void * ctx,int pkt_sz,unsigned char digest[16],unsigned challenge,unsigned ms)2622 static int send_challenge_reply(ei_socket_callbacks *cbs, void *ctx,
2623                                 int pkt_sz, unsigned char digest[16],
2624 				unsigned challenge, unsigned ms)
2625 {
2626     char *s;
2627     char buf[DEFBUF_SIZ];
2628     int siz = pkt_sz + 1 + 4 + 16;
2629     int err;
2630     ssize_t len;
2631 
2632     s = buf;
2633     switch (pkt_sz) {
2634     case 2:
2635         put16be(s,siz - 2);
2636         break;
2637     case 4:
2638         put32be(s,siz - 4);
2639         break;
2640     default:
2641         return -1;
2642     }
2643     put8(s, 'r');
2644     put32be(s, challenge);
2645     memcpy(s, digest, 16);
2646 
2647     len = (ssize_t) siz;
2648     err = ei_write_fill_ctx_t__(cbs, ctx, buf, &len, ms);
2649     if (!err && len != (ssize_t) siz)
2650         err = EIO;
2651     if (err) {
2652 	EI_TRACE_ERR2("send_challenge_reply",
2653 		      "-> SEND_CHALLENGE_REPLY socket write failed: %s (%d)",
2654                       estr(err), err);
2655         EI_CONN_SAVE_ERRNO__(err);
2656 	return -1;
2657     }
2658 
2659     if (ei_tracelevel >= 3) {
2660 	char buffer[33];
2661    	EI_TRACE_CONN2("send_challenge_reply",
2662 		   "-> SEND_CHALLENGE_REPLY (ok) challenge = %d, digest = %s",
2663 		   challenge,hex((char*)digest, buffer));
2664     }
2665     return 0;
2666 }
2667 
recv_complement(ei_socket_callbacks * cbs,void * ctx,int pkt_sz,unsigned ms)2668 static int recv_complement(ei_socket_callbacks *cbs,
2669                            void *ctx,
2670                            int pkt_sz,
2671                            unsigned ms)
2672 {
2673     char dbuf[DEFBUF_SIZ];
2674     char *buf = dbuf;
2675     int is_static = 1;
2676     int buflen = DEFBUF_SIZ;
2677     int rlen;
2678     char *s;
2679     char tag;
2680     unsigned int creation;
2681 
2682     erl_errno = EIO;		/* Default */
2683 
2684     if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen, &is_static, ms)) != 21) {
2685 	EI_TRACE_ERR1("recv_complement",
2686 		      "<- RECV_COMPLEMENT socket read failed (%d)",rlen);
2687 	goto error;
2688     }
2689 
2690     s = buf;
2691     if ((tag = get8(s)) != 'c') {
2692 	EI_TRACE_ERR2("recv_complement",
2693 		      "<- RECV_COMPLEMENT incorrect tag, "
2694 		      "expected 'c' got '%c' (%u)",tag,tag);
2695 	goto error;
2696     }
2697     creation = get32be(s);
2698     if (!is_static)
2699 	free(buf);
2700 
2701     if (ei_tracelevel >= 3) {
2702         EI_TRACE_CONN1("recv_complement",
2703                        "<- RECV_COMPLEMENT (ok) creation = %u",
2704                        creation);
2705     }
2706     /* We don't have any use for 'creation' of other node, so we drop it */
2707     erl_errno = 0;
2708     return 0;
2709 
2710 error:
2711     if (!is_static)
2712 	free(buf);
2713     return -1;
2714 }
2715 
recv_challenge_reply(ei_socket_callbacks * cbs,void * ctx,int pkt_sz,unsigned our_challenge,char cookie[],unsigned * her_challenge,unsigned ms)2716 static int recv_challenge_reply(ei_socket_callbacks *cbs,
2717                                 void *ctx,
2718                                 int pkt_sz,
2719                                 unsigned our_challenge,
2720                                 char cookie[],
2721                                 unsigned *her_challenge,
2722                                 unsigned ms)
2723 {
2724     char dbuf[DEFBUF_SIZ];
2725     char *buf = dbuf;
2726     int is_static = 1;
2727     int buflen = DEFBUF_SIZ;
2728     int rlen;
2729     char *s;
2730     char tag;
2731     char her_digest[16], expected_digest[16];
2732 
2733     erl_errno = EIO;		/* Default */
2734 
2735     if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen, &is_static, ms)) != 21) {
2736 	EI_TRACE_ERR1("recv_challenge_reply",
2737 		      "<- RECV_CHALLENGE_REPLY socket read failed (%d)",rlen);
2738 	goto error;
2739     }
2740 
2741     s = buf;
2742     if ((tag = get8(s)) != 'r') {
2743 	EI_TRACE_ERR2("recv_challenge_reply",
2744 		      "<- RECV_CHALLENGE_REPLY incorrect tag, "
2745 		      "expected 'r' got '%c' (%u)",tag,tag);
2746 	goto error;
2747     }
2748     *her_challenge = get32be(s);
2749     memcpy(her_digest, s, 16);
2750     gen_digest(our_challenge, cookie, (unsigned char*)expected_digest);
2751     if (memcmp(her_digest, expected_digest, 16)) {
2752 	EI_TRACE_ERR0("recv_challenge_reply",
2753 		      "<- RECV_CHALLENGE_REPLY authorization failure");
2754 	goto error;
2755     }
2756     if (!is_static)
2757 	free(buf);
2758 
2759 
2760     if (ei_tracelevel >= 3) {
2761 	char buffer[33];
2762         EI_TRACE_CONN2("recv_challenge_reply",
2763 		   "<- RECV_CHALLENGE_REPLY (ok) challenge = %u, digest = %s",
2764 		   *her_challenge,hex(her_digest,buffer));
2765     }
2766     erl_errno = 0;
2767     return 0;
2768 
2769 error:
2770     if (!is_static)
2771 	free(buf);
2772     return -1;
2773 }
2774 
send_challenge_ack(ei_socket_callbacks * cbs,void * ctx,int pkt_sz,unsigned char digest[16],unsigned ms)2775 static int send_challenge_ack(ei_socket_callbacks *cbs, void *ctx, int pkt_sz,
2776                               unsigned char digest[16], unsigned ms)
2777 {
2778     char *s;
2779     char buf[DEFBUF_SIZ];
2780     int siz = pkt_sz + 1 + 16;
2781     int err;
2782     ssize_t len;
2783 
2784     s = buf;
2785     switch (pkt_sz) {
2786     case 2:
2787         put16be(s,siz - 2);
2788         break;
2789     case 4:
2790         put32be(s,siz - 4);
2791         break;
2792     default:
2793         return -1;
2794     }
2795     put8(s, 'a');
2796     memcpy(s, digest, 16);
2797 
2798     len = (ssize_t) siz;
2799     err = ei_write_fill_ctx_t__(cbs, ctx, buf, &len, ms);
2800     if (!err && len != (ssize_t) siz)
2801         err = EIO;
2802     if (err) {
2803 	EI_TRACE_ERR2("recv_challenge_reply",
2804 		      "-> SEND_CHALLENGE_ACK socket write failed: %s (%d)",
2805                       estr(err), err);
2806         EI_CONN_SAVE_ERRNO__(err);
2807 	return -1;
2808     }
2809 
2810     if (ei_tracelevel >= 3) {
2811 	char buffer[33];
2812     	EI_TRACE_CONN1("recv_challenge_reply",
2813 		   "-> SEND_CHALLENGE_ACK (ok) digest = %s",hex((char *)digest,buffer));
2814     }
2815 
2816     return 0;
2817 }
2818 
recv_challenge_ack(ei_socket_callbacks * cbs,void * ctx,int pkt_sz,unsigned our_challenge,char cookie[],unsigned ms)2819 static int recv_challenge_ack(ei_socket_callbacks *cbs, void *ctx,
2820 			      int pkt_sz, unsigned our_challenge,
2821 			      char cookie[], unsigned ms)
2822 {
2823     char dbuf[DEFBUF_SIZ];
2824     char *buf = dbuf;
2825     int is_static = 1;
2826     int buflen = DEFBUF_SIZ;
2827     int rlen;
2828     char *s;
2829     char tag;
2830     char her_digest[16], expected_digest[16];
2831 
2832     erl_errno = EIO;		/* Default */
2833 
2834     if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen, &is_static, ms)) != 17) {
2835 	EI_TRACE_ERR1("recv_challenge_ack",
2836 		      "<- RECV_CHALLENGE_ACK socket read failed (%d)",rlen);
2837 	goto error;
2838     }
2839 
2840     s = buf;
2841     if ((tag = get8(s)) != 'a') {
2842 	EI_TRACE_ERR2("recv_challenge_ack",
2843 		      "<- RECV_CHALLENGE_ACK incorrect tag, "
2844 		      "expected 'a' got '%c' (%u)",tag,tag);
2845 	goto error;
2846     }
2847     memcpy(her_digest, s, 16);
2848     gen_digest(our_challenge, cookie, (unsigned char *)expected_digest);
2849     if (memcmp(her_digest, expected_digest, 16)) {
2850 	EI_TRACE_ERR0("recv_challenge_ack",
2851 		      "<- RECV_CHALLENGE_ACK authorization failure");
2852 	goto error;
2853     }
2854     if (!is_static)
2855 	free(buf);
2856 
2857     if (ei_tracelevel >= 3) {
2858 	char buffer[33];
2859 	EI_TRACE_CONN1("recv_challenge_ack",
2860 		   "<- RECV_CHALLENGE_ACK (ok) digest = %s",hex(her_digest,buffer));
2861     }
2862     erl_errno = 0;
2863     return 0;
2864 
2865 error:
2866     if (!is_static)
2867 	free(buf);
2868     return -1;
2869 }
2870 
recv_name(ei_socket_callbacks * cbs,void * ctx,int pkt_sz,char * send_name_tag,DistFlags * flags,char * namebuf,unsigned ms)2871 static int recv_name(ei_socket_callbacks *cbs, void *ctx,
2872                      int pkt_sz, char *send_name_tag,
2873                      DistFlags *flags, char *namebuf, unsigned ms)
2874 {
2875     char dbuf[DEFBUF_SIZ];
2876     char *buf = dbuf;
2877     int is_static = 1;
2878     int buflen = DEFBUF_SIZ;
2879     int rlen;
2880     unsigned int namelen;
2881     char *s;
2882     char tmp_nodename[MAXNODELEN+1];
2883     char tag;
2884 
2885     erl_errno = EIO;		/* Default */
2886 
2887     if ((rlen = read_hs_package(cbs, ctx, pkt_sz, &buf, &buflen,
2888                                 &is_static, ms)) <= 0) {
2889 	EI_TRACE_ERR1("recv_name","<- RECV_NAME socket read failed (%d)",rlen);
2890 	goto error;
2891     }
2892     s = buf;
2893     tag = get8(s);
2894     *send_name_tag = tag;
2895     if (tag != 'n' && tag != 'N') {
2896 	EI_TRACE_ERR2("recv_name","<- RECV_NAME incorrect tag, "
2897 		      "expected 'n' or 'N', got '%c' (%u)",tag,tag);
2898 	goto error;
2899     }
2900     if (tag == 'n') {
2901         unsigned int version;
2902         if (rlen < 1+2+4) {
2903             EI_TRACE_ERR1("recv_name","<- RECV_NAME 'n' packet too short (%d)",
2904                           rlen)
2905             goto error;
2906         }
2907         version = get16be(s);
2908         if (version < EI_DIST_5) {
2909             EI_TRACE_ERR1("recv_name","<- RECV_NAME 'n' invalid version=%d",
2910                           version)
2911             goto error;
2912         }
2913         *flags = get32be(s);
2914         namelen = rlen - (1+2+4);
2915     }
2916     else { /* tag == 'N' */
2917         if (rlen < 1+8+4+2) {
2918             EI_TRACE_ERR1("recv_name","<- RECV_NAME 'N' packet too short (%d)",
2919                           rlen)
2920             goto error;
2921         }
2922         *flags = get64be(s);
2923         s += 4; /* ignore peer 'creation' */
2924         namelen = get16be(s);
2925     }
2926 
2927     if (!(*flags & DFLAG_EXTENDED_REFERENCES)) {
2928 	EI_TRACE_ERR0("recv_name","<- RECV_NAME peer cannot handle"
2929 		      "extended references");
2930 	goto error;
2931     }
2932 
2933     if (!(*flags & DFLAG_EXTENDED_PIDS_PORTS)) {
2934 	EI_TRACE_ERR0("recv_name","<- RECV_NAME peer cannot "
2935 		      "handle extended pids and ports");
2936 	erl_errno = EIO;
2937 	goto error;
2938     }
2939 
2940     if (!namebuf)
2941         namebuf = &tmp_nodename[0];
2942 
2943     if (namelen > MAXNODELEN || s+namelen > buf+rlen) {
2944         EI_TRACE_ERR2("recv_name","<- RECV_NAME '%c' nodename too long (%d)",
2945                       tag, namelen);
2946         goto error;
2947     }
2948 
2949     memcpy(namebuf, s, namelen);
2950     namebuf[namelen] = '\0';
2951 
2952     if (!is_static)
2953 	free(buf);
2954     EI_TRACE_CONN3("recv_name",
2955 		   "<- RECV_NAME (ok) node = %s, tag = %c, flags = %u",
2956 		   namebuf,tag,*flags);
2957     erl_errno = 0;
2958     return 0;
2959 
2960 error:
2961     if (!is_static)
2962 	free(buf);
2963     return -1;
2964 }
2965 
2966 /***************************************************************************
2967  *
2968  *  Returns 1 on success and 0 on failure.
2969  *
2970  ***************************************************************************/
2971 
2972 
2973 /* size is the buffer size, e.i. string length + 1 */
2974 
get_home(char * buf,int size)2975 static int get_home(char *buf, int size)
2976 {
2977 #ifdef __WIN32__
2978     char* homedrive = getenv("HOMEDRIVE");
2979     char* homepath = getenv("HOMEPATH");
2980 
2981     if (homedrive && homepath) {
2982 	if (strlen(homedrive)+strlen(homepath) >= size)
2983 	    return 0;
2984 	strcpy(buf, homedrive);
2985 	strcat(buf, homepath);
2986 	return 1;
2987     }
2988     else {
2989 	int len = GetWindowsDirectory(buf, size);
2990 	if (len) {
2991 	    return (len < size);
2992 	}
2993     }
2994 #else
2995     char* homepath = getenv("HOME");
2996     if (homepath) {
2997 	if (strlen(homepath) >= size)
2998 	    return 0;
2999 	strcpy(buf, homepath);
3000 	return 1;
3001     }
3002 #endif
3003 
3004     buf[0] = '.';
3005     buf[1] = '\0';
3006     return 1;
3007 }
3008 
3009 
get_cookie(char * buf,int bufsize)3010 static int get_cookie(char *buf, int bufsize)
3011 {
3012     char fname[EI_MAX_HOME_PATH + sizeof(COOKIE_FILE) + 1];
3013     int fd;
3014     int len;
3015     unsigned char next_c;
3016 
3017     if (!get_home(fname, EI_MAX_HOME_PATH+1)) {
3018 	fprintf(stderr,"<ERROR> get_cookie: too long path to home");
3019 	return 0;
3020     }
3021 
3022     strcat(fname, COOKIE_FILE);
3023     if ((fd = open(fname, O_RDONLY, 0777)) < 0) {
3024 	fprintf(stderr,"<ERROR> get_cookie: can't open cookie file");
3025 	return 0;
3026     }
3027 
3028     if ((len = read(fd, buf, bufsize-1)) < 0) {
3029 	fprintf(stderr,"<ERROR> get_cookie: reading cookie file");
3030 	close(fd);
3031 	return 0;
3032     }
3033 
3034     /* If more to read it is too long. Not 100% correct test but will do. */
3035     if (read(fd, &next_c, 1) > 0 && !isspace(next_c)) {
3036 	fprintf(stderr,"<ERROR> get_cookie: cookie in %s is too long",fname);
3037 	close(fd);
3038 	return 0;
3039     }
3040 
3041     close(fd);
3042 
3043     /* Remove all newlines after the first newline */
3044     buf[len] = '\0';		/* Terminate string */
3045     len = strcspn(buf,"\r\n");
3046     buf[len] = '\0';		/* Terminate string again */
3047 
3048     return 1;			/* Success! */
3049 }
3050 
3051