1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1996-2016. 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.
22 */
23
24 /***************************************************************************
25 *
26 * 'erl_interface' node connection handling is to use 'ei' for all
27 * operations without access to the internal structure of saved data,
28 * e.i. it should use the public interface functions. The connection
29 * handling can be seen as a restricted node interface where only one
30 * node can be used in one operating system process.
31 *
32 ***************************************************************************/
33
34 #include "eidef.h"
35
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <fcntl.h>
39
40 #ifdef __WIN32__
41 #include <winsock2.h>
42 #include <windows.h>
43 #include <winbase.h>
44
45 #elif VXWORKS
46 #include <vxWorks.h>
47 #include <hostLib.h>
48 #include <selectLib.h>
49 #include <ifLib.h>
50 #include <sockLib.h>
51 #include <taskLib.h>
52 #include <inetLib.h>
53
54 #include <unistd.h>
55 #include <sys/types.h>
56 #include <sys/times.h>
57 #include <unistd.h>
58 #include <sys/types.h>
59 #include <sys/socket.h>
60 #include <netinet/in.h>
61 #include <netinet/tcp.h>
62 #include <timers.h>
63
64 #include "erl_error.h"
65
66 #else /* some other unix */
67 #include <unistd.h>
68 #include <sys/types.h>
69 #include <sys/times.h>
70
71 #if TIME_WITH_SYS_TIME
72 # include <sys/time.h>
73 # include <time.h>
74 #else
75 # if HAVE_SYS_TIME_H
76 # include <sys/time.h>
77 # else
78 # include <time.h>
79 # endif
80 #endif
81
82 #include <sys/socket.h>
83 #include <netinet/in.h>
84 #include <netinet/tcp.h>
85 #include <arpa/inet.h>
86 #include <netdb.h>
87 #include <sys/utsname.h> /* for gen_challenge (NEED FIX?) */
88 #endif
89
90 /* common includes */
91 #include <stdio.h>
92 #include <stdlib.h>
93 #include <string.h>
94 #include <errno.h>
95
96 /* FIXME include less */
97 #include "erl_interface.h"
98 #include "erl_connect.h"
99 #include "erl_eterm.h"
100 #include "erl_malloc.h"
101 #include "putget.h"
102 #include "ei.h"
103 #include "ei_connect_int.h"
104 #include "ei_locking.h"
105 #include "ei_epmd.h"
106 #include "ei_internal.h"
107
108 /* rpc_from() uses a buffer this size */
109 #ifndef MAX_RECEIVE_BUF
110 #define MAX_RECEIVE_BUF 32*1024
111 #endif
112
113 /* This is the global state of the old erl_* API */
114
115 static ei_cnode erl_if_ec;
116
117 /***************************************************************************
118 *
119 * API: erl_connect_init()
120 * API: erl_connect_xinit()
121 *
122 * Returns 1 on success and 0 on failure.
123 * Not documented to set erl_errno.
124 *
125 ***************************************************************************/
126
erl_connect_init(int this_node_number,char * cookie,short creation)127 int erl_connect_init(int this_node_number, char *cookie, short creation)
128 {
129 char nn[MAXATOMLEN];
130
131 sprintf(nn, "c%d", this_node_number);
132
133 return ei_connect_init(&erl_if_ec, nn, cookie, creation) == 0;
134 }
135
136 /* FIXME documented to use struct in_addr as addr */
137
erl_connect_xinit(char * thishostname,char * thisalivename,char * thisnodename,struct in_addr * thisipaddr,char * cookie,short creation)138 int erl_connect_xinit(char *thishostname,
139 char *thisalivename,
140 char *thisnodename,
141 struct in_addr *thisipaddr,
142 char *cookie,
143 short creation)
144 {
145 return ei_connect_xinit(&erl_if_ec, thishostname, thisalivename,
146 thisnodename, thisipaddr, cookie, creation) >= 0;
147 }
148
149 /***************************************************************************
150 *
151 * API: erl_connect()
152 * API: erl_xconnect()
153 *
154 * Set up a connection to a given Node, and interchange hand shake
155 * messages with it.
156 *
157 * Returns valid file descriptor on success and < 0 on failure.
158 * Set erl_errno to EHOSTUNREACH, ENOMEM, EIO or errno from socket(2)
159 * or connect(2).
160 *
161 ***************************************************************************/
162
erl_connect(char * nodename)163 int erl_connect(char *nodename)
164 {
165 int res = ei_connect(&erl_if_ec, nodename);
166 if (res < 0) erl_errno = EIO;
167 return res;
168 }
169
170 /* FIXME documented to use struct in_addr as addr */
171
erl_xconnect(Erl_IpAddr addr,char * alivename)172 int erl_xconnect(Erl_IpAddr addr, char *alivename)
173 {
174 return ei_xconnect(&erl_if_ec, addr, alivename);
175 }
176
177
178 /***************************************************************************
179 *
180 * API: erl_close_connection()
181 *
182 * Returns 0 on success and -1 on failure.
183 *
184 ***************************************************************************/
185
erl_close_connection(int fd)186 int erl_close_connection(int fd)
187 {
188 return ei_close_connection(fd);
189 }
190
191 /*
192 * Accept and initiate a connection from another
193 * Erlang node. Return a file descriptor at success,
194 * otherwise -1;
195 */
erl_accept(int lfd,ErlConnect * conp)196 int erl_accept(int lfd, ErlConnect *conp)
197 {
198 return ei_accept(&erl_if_ec, lfd, conp);
199 }
200
201
202 /* Receives a message from an Erlang socket.
203 * If the message was a TICK it is immediately
204 * answered. Returns: ERL_ERROR, ERL_TICK or
205 * the number of bytes read.
206 */
erl_receive(int s,unsigned char * bufp,int bufsize)207 int erl_receive(int s, unsigned char *bufp, int bufsize)
208 {
209 return ei_receive(s, bufp, bufsize);
210 }
211
212 /*
213 * Send an Erlang message to a registered process
214 * at the Erlang node, connected with a socket.
215 */
erl_reg_send(int fd,char * server_name,ETERM * msg)216 int erl_reg_send(int fd, char *server_name, ETERM *msg)
217 {
218 ei_x_buff x;
219 int r;
220
221 if (ei_x_new_with_version(&x) < 0) {
222 erl_errno = ENOMEM;
223 return 0;
224 }
225 if (ei_x_encode_term(&x, msg) < 0) {
226 erl_errno = EINVAL;
227 r = 0;
228 } else {
229 r = ei_reg_send(&erl_if_ec, fd, server_name, x.buff, x.index);
230 }
231 ei_x_free(&x);
232 return r == 0;
233 }
234
235 /*
236 * Sends an Erlang message to a process at an Erlang node
237 */
erl_send(int fd,ETERM * to,ETERM * msg)238 int erl_send(int fd, ETERM *to ,ETERM *msg)
239 {
240 erlang_pid topid;
241 ei_x_buff x;
242 int r;
243
244 ei_x_new_with_version(&x);
245 ei_x_encode_term(&x, msg);
246 /* make the to-pid */
247 if (!ERL_IS_PID(to)) {
248 ei_x_free(&x);
249 erl_errno = EINVAL;
250 return -1;
251 }
252
253 if (to->uval.pidval.node.latin1) {
254 strcpy(topid.node, to->uval.pidval.node.latin1);
255 }
256 else {
257 strcpy(topid.node, to->uval.pidval.node.utf8);
258 }
259 topid.num = ERL_PID_NUMBER(to);
260 topid.serial = ERL_PID_SERIAL(to);
261 topid.creation = ERL_PID_CREATION(to);
262 r = ei_send(fd, &topid, x.buff, x.index);
263 ei_x_free(&x);
264 return r == 0;
265 }
266
erl_do_receive_msg(int fd,ei_x_buff * x,ErlMessage * emsg)267 static int erl_do_receive_msg(int fd, ei_x_buff* x, ErlMessage* emsg)
268 {
269 erlang_msg msg;
270
271 int r;
272 msg.from.node[0] = msg.to.node[0] = msg.toname[0] = '\0';
273 r = ei_do_receive_msg(fd, 0, &msg, x, 0);
274
275 if (r == ERL_MSG) {
276 int index = 0;
277 emsg->type = msg.msgtype;
278
279 /*
280 We can't call ei_decode_term for cases where there are no
281 data following the type information. If there are other
282 types added later where there are data this case has to be
283 extended.
284 */
285
286 switch (msg.msgtype) {
287 case ERL_SEND:
288 case ERL_REG_SEND:
289 case ERL_EXIT:
290 case ERL_EXIT2:
291 if (ei_decode_term(x->buff, &index, &emsg->msg) < 0)
292 r = ERL_ERROR;
293 break;
294 default:
295 emsg->msg = NULL; /* Not needed but may avoid problems for unsafe caller */
296 break;
297 }
298 } else
299 emsg->msg = NULL;
300 if (msg.from.node[0] != '\0')
301 emsg->from = erl_mk_pid(msg.from.node, msg.from.num, msg.from.serial, msg.from.creation);
302 else
303 emsg->from = NULL;
304 if (msg.to.node[0] != '\0')
305 emsg->to = erl_mk_pid(msg.to.node, msg.to.num, msg.to.serial, msg.to.creation);
306 else
307 emsg->to = NULL;
308 strcpy(emsg->to_name, msg.toname);
309 return r;
310 }
311
erl_receive_msg(int fd,unsigned char * buf,int bufsize,ErlMessage * emsg)312 int erl_receive_msg(int fd, unsigned char *buf, int bufsize, ErlMessage *emsg)
313 {
314 ei_x_buff x;
315 int r;
316
317 ei_x_new(&x);
318 r = erl_do_receive_msg(fd, &x, emsg);
319 /* FIXME what is this about? */
320 if (bufsize > x.index)
321 bufsize = x.index;
322 memcpy(buf, x.buff, bufsize);
323 ei_x_free(&x);
324 return r;
325 }
326
erl_xreceive_msg(int fd,unsigned char ** buf,int * bufsize,ErlMessage * emsg)327 int erl_xreceive_msg(int fd, unsigned char **buf, int *bufsize,
328 ErlMessage *emsg)
329 {
330 ei_x_buff x;
331 int r;
332
333 ei_x_new(&x);
334 r = erl_do_receive_msg(fd, &x, emsg);
335 if (*bufsize < x.index)
336 *buf = erl_realloc(*buf, x.index);
337 *bufsize = x.index;
338 memcpy(*buf, x.buff, *bufsize);
339 ei_x_free(&x);
340 return r;
341 }
342
343 /*
344 * The RPC consists of two parts, send and receive.
345 * Here is the send part !
346 * { PidFrom, { call, Mod, Fun, Args, user }}
347 */
348 /*
349 * Now returns non-negative number for success, negative for failure.
350 */
erl_rpc_to(int fd,char * mod,char * fun,ETERM * args)351 int erl_rpc_to(int fd, char *mod, char *fun, ETERM *args)
352 {
353 int r;
354 ei_x_buff x;
355
356 ei_x_new(&x);
357 ei_x_encode_term(&x, args);
358 r = ei_rpc_to(&erl_if_ec, fd, mod, fun, x.buff, x.index);
359 ei_x_free(&x);
360 return r;
361 } /* rpc_to */
362
363 /*
364 * And here is the rpc receiving part. A negative
365 * timeout means 'infinity'. Returns either of: ERL_MSG,
366 * ERL_TICK, ERL_ERROR or ERL_TIMEOUT.
367 */
erl_rpc_from(int fd,int timeout,ErlMessage * emsg)368 int erl_rpc_from(int fd, int timeout, ErlMessage *emsg)
369 {
370 fd_set readmask;
371 struct timeval tv;
372 struct timeval *t = NULL;
373 unsigned char rbuf[MAX_RECEIVE_BUF];
374
375 if (timeout >= 0) {
376 tv.tv_sec = timeout / 1000;
377 tv.tv_usec = (timeout % 1000) * 1000;
378 t = &tv;
379 }
380
381 FD_ZERO(&readmask);
382 FD_SET(fd,&readmask);
383
384 switch (select(fd+1, &readmask, NULL, NULL, t)) {
385 case -1:
386 erl_errno = EIO;
387 return ERL_ERROR;
388 case 0:
389 erl_errno = ETIMEDOUT;
390 return ERL_TIMEOUT;
391 default:
392 if (FD_ISSET(fd, &readmask))
393 return erl_receive_msg(fd, rbuf, MAX_RECEIVE_BUF, emsg);
394 else {
395 erl_errno = EIO;
396 return ERL_ERROR;
397 }
398 }
399 } /* rpc_from */
400
401 /*
402 * A true RPC. It return a NULL pointer
403 * in case of failure, otherwise a valid
404 * (ETERM *) pointer containing the reply
405 */
erl_rpc(int fd,char * mod,char * fun,ETERM * args)406 ETERM *erl_rpc(int fd, char *mod, char *fun, ETERM *args)
407 {
408 int i;
409 ETERM *ep;
410 ErlMessage emsg;
411
412 if (erl_rpc_to(fd, mod, fun, args) < 0) {
413 return NULL; }
414 while ((i=erl_rpc_from(fd, ERL_NO_TIMEOUT, &emsg)) == ERL_TICK);
415
416 if (i == ERL_ERROR) return NULL;
417
418 ep = erl_element(2,emsg.msg); /* {RPC_Tag, RPC_Reply} */
419 erl_free_term(emsg.msg);
420 erl_free_term(emsg.to);
421 return ep;
422 } /* rpc */
423
424
425 /*
426 ** Handshake
427 */
428
erl_publish(int port)429 int erl_publish(int port)
430 {
431 return ei_publish(&erl_if_ec, port);
432 }
433
erl_unpublish(const char * alive)434 int erl_unpublish(const char *alive)
435 {
436 return ei_unpublish_tmo(alive,0);
437 }
438
erl_self(void)439 erlang_pid *erl_self(void)
440 {
441 return ei_self(&erl_if_ec);
442 }
443
erl_thisnodename(void)444 const char *erl_thisnodename(void)
445 {
446 return ei_thisnodename(&erl_if_ec);
447 }
448
erl_thishostname(void)449 const char *erl_thishostname(void)
450 {
451 return ei_thishostname(&erl_if_ec);
452 }
453
erl_thisalivename(void)454 const char *erl_thisalivename(void)
455 {
456 return ei_thisalivename(&erl_if_ec);
457 }
458
erl_thiscookie(void)459 const char *erl_thiscookie(void)
460 {
461 return ei_thiscookie(&erl_if_ec);
462 }
463
erl_thiscreation(void)464 short erl_thiscreation(void)
465 {
466 return ei_thiscreation(&erl_if_ec);
467 }
468