1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1998-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 #ifdef __WIN32__
21 # include <winsock2.h>
22 # include <windows.h>
23 # include <winbase.h>
24 #else /* Unix/VxWorks */
25 # include <unistd.h>
26 #endif
27
28 /* common */
29 #include <string.h>
30 #include <stdlib.h>
31 #include <errno.h>
32
33 #include "eidef.h"
34 #include "eiext.h"
35 #include "eirecv.h"
36 #include "ei_portio.h"
37 #include "ei_internal.h"
38 #include "putget.h"
39 #include "ei_trace.h"
40 #include "show_msg.h"
41 #include "ei_connect_int.h"
42
43 #include <errno.h>
44
45 #define EIRECVBUF 2048 /* largest possible header is approx 1300 bytes */
46
47 static int
48 send_unlink_id_ack(ei_socket_callbacks *cbs, void *ctx,
49 char *id_ext, size_t id_size,
50 erlang_pid *from, erlang_pid *to,
51 unsigned tmo);
52
53 /* length (4), PASS_THOUGH (1), header, message */
54 int
ei_recv_internal(int fd,char ** mbufp,int * bufsz,erlang_msg * msg,int * msglenp,int staticbufp,unsigned ms)55 ei_recv_internal (int fd,
56 char **mbufp, int *bufsz,
57 erlang_msg *msg, int *msglenp,
58 int staticbufp, unsigned ms)
59 {
60 char header[EIRECVBUF];
61 char *s=header;
62 char *mbuf=*mbufp;
63 int len = 0;
64 int msglen = 0;
65 int bytesread = 0;
66 int remain;
67 int arity;
68 int version;
69 int index = 0;
70 int err;
71 int show_this_msg = 0;
72 ei_socket_callbacks *cbs;
73 void *ctx;
74 ssize_t rlen;
75 unsigned tmo = ms == 0 ? EI_SCLBK_INF_TMO : ms;
76
77 err = EI_GET_CBS_CTX__(&cbs, &ctx, fd);
78 if (err) {
79 EI_CONN_SAVE_ERRNO__(err);
80 return ERL_ERROR;
81 }
82
83 /* get length field */
84 rlen = 4;
85 err = ei_read_fill_ctx_t__(cbs, ctx, header, &rlen, tmo);
86 if (!err && rlen != 4)
87 err = EIO;
88 if (err) {
89 EI_CONN_SAVE_ERRNO__(err);
90 return ERL_ERROR;
91 }
92
93 len = get32be(s);
94
95 /* got tick - respond and return */
96 if (!len) {
97 char tock[] = {0,0,0,0};
98 ssize_t wlen = sizeof(tock);
99 ei_write_fill_ctx_t__(cbs, ctx, tock, &wlen, tmo); /* Failure no problem */
100 *msglenp = 0;
101 return ERL_TICK;
102 }
103
104 /* turn off tracing on each receive. it will be turned back on if
105 * we receive a trace token.
106 */
107 ei_trace(-1,NULL);
108
109 /* read enough to get at least entire header */
110 rlen = bytesread = (len > EIRECVBUF ? EIRECVBUF : len);
111 err = ei_read_fill_ctx_t__(cbs, ctx, header, &rlen, tmo);
112 if (!err && rlen != bytesread)
113 err = EIO;
114 if (err) {
115 EI_CONN_SAVE_ERRNO__(err);
116 return ERL_ERROR;
117 }
118
119 /* now decode header */
120 /* pass-through, version, control tuple header, control message type */
121 s = header;
122 index = 1;
123 if ((get8(s) != ERL_PASS_THROUGH)
124 || ei_decode_version(header,&index,&version)
125 || (version != ERL_VERSION_MAGIC)
126 || ei_decode_tuple_header(header,&index,&arity)
127 || ei_decode_long(header,&index,&msg->msgtype))
128 {
129 EI_CONN_SAVE_ERRNO__(EBADMSG);
130 return ERL_ERROR;
131 }
132
133 switch (msg->msgtype) {
134 case ERL_SEND: /* { SEND, Cookie, ToPid } */
135 if (ei_tracelevel >= 4) show_this_msg = 1;
136 if (ei_decode_atom_as(header,&index,msg->cookie,sizeof(msg->cookie),ERLANG_UTF8,NULL,NULL)
137 || ei_decode_pid(header,&index,&msg->to))
138 {
139 EI_CONN_SAVE_ERRNO__(EBADMSG);
140 return ERL_ERROR;
141 }
142
143 break;
144
145 case ERL_REG_SEND: /* { REG_SEND, From, Cookie, ToName } */
146 if (ei_tracelevel >= 4) show_this_msg = 1;
147 if (ei_decode_pid(header,&index,&msg->from)
148 || ei_decode_atom_as(header,&index,msg->cookie,sizeof(msg->cookie),ERLANG_UTF8,NULL,NULL)
149 || ei_decode_atom_as(header,&index,msg->toname,sizeof(msg->toname),ERLANG_UTF8,NULL,NULL))
150 {
151 EI_CONN_SAVE_ERRNO__(EBADMSG);
152 return ERL_ERROR;
153 }
154
155 /* actual message is remaining part of headerbuf, plus any unread bytes */
156 break;
157
158 case ERL_LINK: /* { LINK, From, To } */
159 case ERL_UNLINK: /* { UNLINK, From, To } */
160 case ERL_GROUP_LEADER: /* { GROUP_LEADER, From, To } */
161 if (ei_tracelevel >= 4) show_this_msg = 1;
162 if (ei_decode_pid(header,&index,&msg->from)
163 || ei_decode_pid(header,&index,&msg->to))
164 {
165 EI_CONN_SAVE_ERRNO__(EBADMSG);
166 return ERL_ERROR;
167 }
168
169 break;
170
171 case ERL_UNLINK_ID: { /* {UNLINK_ID, Id, From, To} */
172 int id;
173 size_t id_size;
174
175 /* Expose old ERL_UNLINK tag to user... */
176 msg->msgtype = ERL_UNLINK;
177
178 if (ei_tracelevel >= 4) show_this_msg = 1;
179 /* Save id on external format... */
180 id = index;
181 if (ei_skip_term(header, &index)) {
182 EI_CONN_SAVE_ERRNO__(EBADMSG);
183 return ERL_ERROR;
184 }
185 id_size = index - id;
186
187 if (ei_decode_pid(header,&index,&msg->from)
188 || ei_decode_pid(header,&index,&msg->to)) {
189 EI_CONN_SAVE_ERRNO__(EBADMSG);
190 return ERL_ERROR;
191 }
192
193 if (send_unlink_id_ack(cbs, ctx, &header[0] + id, id_size,
194 &msg->to, &msg->from, tmo)) {
195 return ERL_ERROR;
196 }
197
198 break;
199 }
200
201 case ERL_UNLINK_ID_ACK: /* {UNLINK_ID_ACK, Id, From, To} */
202 /*
203 * ei currently do not support setting up links and removing
204 * links from the ei side, so an 'unlink id ack' signal should
205 * never arrive...
206 */
207 EI_CONN_SAVE_ERRNO__(EBADMSG);
208 return ERL_ERROR;
209
210 case ERL_EXIT: /* { EXIT, From, To, Reason } */
211 case ERL_EXIT2: /* { EXIT2, From, To, Reason } */
212 if (ei_tracelevel >= 4) show_this_msg = 1;
213 if (ei_decode_pid(header,&index,&msg->from)
214 || ei_decode_pid(header,&index,&msg->to))
215 {
216 EI_CONN_SAVE_ERRNO__(EBADMSG);
217 return ERL_ERROR;
218 }
219
220 break;
221
222 case ERL_SEND_TT: /* { SEND_TT, Cookie, ToPid, TraceToken } */
223 if (ei_tracelevel >= 4) show_this_msg = 1;
224 if (ei_decode_atom_as(header,&index,msg->cookie,sizeof(msg->cookie),ERLANG_UTF8,NULL,NULL)
225 || ei_decode_pid(header,&index,&msg->to)
226 || ei_decode_trace(header,&index,&msg->token))
227 {
228 EI_CONN_SAVE_ERRNO__(EBADMSG);
229 return ERL_ERROR;
230 }
231
232 ei_trace(1,&msg->token); /* turn on tracing */
233 break;
234
235 case ERL_REG_SEND_TT: /* { REG_SEND_TT, From, Cookie, ToName, TraceToken } */
236 if (ei_tracelevel >= 4) show_this_msg = 1;
237 if (ei_decode_pid(header,&index,&msg->from)
238 || ei_decode_atom_as(header,&index,msg->cookie,sizeof(msg->cookie),ERLANG_UTF8,NULL,NULL)
239 || ei_decode_atom_as(header,&index,msg->toname,sizeof(msg->toname),ERLANG_UTF8,NULL,NULL)
240 || ei_decode_trace(header,&index,&msg->token))
241 {
242 EI_CONN_SAVE_ERRNO__(EBADMSG);
243 return ERL_ERROR;
244 }
245
246 ei_trace(1,&msg->token); /* turn on tracing */
247 break;
248
249 case ERL_EXIT_TT: /* { EXIT_TT, From, To, TraceToken, Reason } */
250 case ERL_EXIT2_TT: /* { EXIT2_TT, From, To, TraceToken, Reason } */
251 if (ei_tracelevel >= 4) show_this_msg = 1;
252 if (ei_decode_pid(header,&index,&msg->from)
253 || ei_decode_pid(header,&index,&msg->to)
254 || ei_decode_trace(header,&index,&msg->token))
255 {
256 EI_CONN_SAVE_ERRNO__(EBADMSG);
257 return ERL_ERROR;
258 }
259
260 ei_trace(1,&msg->token); /* turn on tracing */
261 break;
262
263 default:
264 /* unknown type, just put any remaining bytes into buffer */
265 break;
266 }
267
268 /* actual message is remaining part of headerbuf, plus any unread bytes */
269 msglen = len - index; /* message size (payload) */
270 remain = len - bytesread; /* bytes left to read */
271
272 /* if callers buffer is too small, we flush in the rest of the
273 * message and discard it, unless we know that we can reallocate
274 * the buffer in which case we do that and read the message.
275 */
276 if (msglen > *bufsz) {
277 if (staticbufp) {
278 /* flush in rest of packet */
279 while (remain > 0) {
280 rlen = remain > EIRECVBUF ? EIRECVBUF : remain;
281 err = ei_read_fill_ctx_t__(cbs, ctx, header, &rlen, tmo);
282 if (err) {
283 EI_CONN_SAVE_ERRNO__(err);
284 return ERL_ERROR;
285 }
286 if (rlen == 0)
287 break;
288 remain -= rlen;
289 }
290 erl_errno = EMSGSIZE;
291 return ERL_ERROR;
292 }
293 else {
294 /* Dynamic buffer --- grow it. */
295 #ifdef DEBUG
296 fprintf(stderr, "Growing buffer from %d bytes to %d bytes\n",
297 *bufsz, msglen);
298 #endif
299 if ((mbuf = realloc(*mbufp, msglen)) == NULL)
300 {
301 erl_errno = ENOMEM;
302 return ERL_ERROR;
303 }
304
305 *mbufp = mbuf;
306 *bufsz = msglen;
307 }
308 }
309
310 /* move remaining bytes to callers buffer */
311 memmove(mbuf,header+index,bytesread-index);
312
313 /* let the caller know how big the message is in his buffer */
314 *msglenp = msglen;
315
316 /* read the rest of the message into callers buffer */
317 if (remain > 0) {
318 rlen = remain;
319 err = ei_read_fill_ctx_t__(cbs, ctx, mbuf+bytesread-index, &rlen, tmo);
320 if (!err && rlen != remain)
321 err = EIO;
322 if (err) {
323 *msglenp = bytesread-index+1; /* actual bytes in users buffer */
324 EI_CONN_SAVE_ERRNO__(err);
325 return ERL_ERROR;
326 }
327 }
328
329 if (show_this_msg)
330 ei_show_recmsg(stderr,msg,mbuf);
331
332 /* the caller only sees "untraced" message types */
333 /* the trace token is buried in the message struct */
334 if (msg->msgtype > 10) msg->msgtype -= 10;
335
336 return msg->msgtype;
337 }
338
339 static int
send_unlink_id_ack(ei_socket_callbacks * cbs,void * ctx,char * id_ext,size_t id_size,erlang_pid * from,erlang_pid * to,unsigned tmo)340 send_unlink_id_ack(ei_socket_callbacks *cbs, void *ctx,
341 char *id_ext, size_t id_size,
342 erlang_pid *from, erlang_pid *to,
343 unsigned tmo)
344 {
345 /* Send: {ERL_UNLINK_ID_ACK, Id, From, To} */
346 int err, index;
347 ssize_t wlen;
348 char ctl[EIRECVBUF]; /* should be large enough for any ctrl msg */
349 char *s;
350
351 EI_CONN_SAVE_ERRNO__(EINVAL);
352
353 /* leave space for packet size and pass through marker... */
354 index = 5;
355 if (ei_encode_version(ctl, &index) < 0)
356 return ERL_ERROR;
357 if (ei_encode_tuple_header(ctl, &index, 4) < 0)
358 return ERL_ERROR;
359 if (ei_encode_long(ctl, &index, ERL_UNLINK_ID_ACK) < 0)
360 return ERL_ERROR;
361 s = &ctl[0] + index;
362 index += id_size;
363 memcpy((void *) s, (void *) id_ext, id_size);
364 if (ei_encode_pid(ctl, &index, from) < 0)
365 return ERL_ERROR;
366 if (ei_encode_pid(ctl, &index, to) < 0)
367 return ERL_ERROR;
368
369 s = &ctl[0];
370 /* packet size */
371 put32be(s, index - 4);
372 /* pass throug */
373 put8(s, ERL_PASS_THROUGH);
374
375 if (ei_tracelevel >= 4) {
376 if (ei_show_sendmsg(stderr, ctl, NULL) < 0)
377 return ERL_ERROR;
378 }
379
380 wlen = (ssize_t) index;
381 err = ei_write_fill_ctx_t__(cbs, ctx, ctl, &wlen, tmo);
382 if (!err && wlen != (ssize_t) index)
383 err = EIO;
384 if (err) {
385 EI_CONN_SAVE_ERRNO__(err);
386 return ERL_ERROR;
387 }
388
389 erl_errno = 0;
390 return 0;
391 }
392
ei_receive_encoded(int fd,char ** mbufp,int * bufsz,erlang_msg * msg,int * msglen)393 int ei_receive_encoded(int fd, char **mbufp, int *bufsz,
394 erlang_msg *msg, int *msglen)
395 {
396 return ei_recv_internal(fd, mbufp, bufsz, msg, msglen, 0, 0);
397 }
398
ei_receive_encoded_tmo(int fd,char ** mbufp,int * bufsz,erlang_msg * msg,int * msglen,unsigned ms)399 int ei_receive_encoded_tmo(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen, unsigned ms)
400 {
401 return ei_recv_internal(fd, mbufp, bufsz, msg, msglen, 0, ms);
402 }
403
404