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