/* Receives a message from an Erlang socket. * If the message was a TICK it is immediately * answered. Returns: ERL_ERROR, ERL_TICK or * the number of bytes read. */ int ei_receive_tmo(int fd, unsigned char *bufp, int bufsize, unsigned ms) { int len; unsigned char fourbyte[4]={0,0,0,0}; int res; if ((res = ei_read_fill_t(fd, (char *) bufp, 4, ms)) != 4) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return ERL_ERROR; } /* Tick handling */ if ((len = get_int32(bufp)) == ERL_TICK) { ei_write_fill_t(fd, (char *) fourbyte, 4, ms); /* FIXME ok to ignore error or timeout? */ erl_errno = EAGAIN; return ERL_TICK; } else if (len > bufsize) { /* FIXME: We should drain the message. */ erl_errno = EMSGSIZE; return ERL_ERROR; } else if ((res = ei_read_fill_t(fd, (char *) bufp, len, ms)) != len) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return ERL_ERROR; } return len; }
static int read_2byte_package(int fd, char **buf, int *buflen, int *is_static, unsigned ms) { unsigned char nbuf[2]; unsigned char *x = nbuf; unsigned len; int res; if((res = ei_read_fill_t(fd, (char *)nbuf, 2, ms)) != 2) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } len = get16be(x); if (len > *buflen) { if (*is_static) { char *tmp = malloc(len); if (!tmp) { erl_errno = ENOMEM; return -1; } *buf = tmp; *is_static = 0; *buflen = len; } else { char *tmp = realloc(*buf, len); if (!tmp) { erl_errno = ENOMEM; return -1; } *buf = tmp; *buflen = len; } } if ((res = ei_read_fill_t(fd, *buf, len, ms)) != len) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } return len; }
/* stop the specified node */ int ei_unpublish_tmo(const char *alive, unsigned ms) { char buf[EPMDBUF]; char *s = (char*)buf; int len = 1 + strlen(alive); int fd, res; if (len > sizeof(buf)-3) { erl_errno = ERANGE; return -1; } put16be(s,len); put8(s,EI_EPMD_STOP_REQ); strcpy(s, alive); /* FIXME can't connect, return success?! At least commen whats up */ if ((fd = ei_epmd_connect_tmo(NULL,ms)) < 0) return fd; if ((res = ei_write_fill_t(fd, buf, len+2,ms)) != len+2) { closesocket(fd); erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } EI_TRACE_CONN1("ei_unpublish_tmo","-> STOP %s",alive); if ((res = ei_read_fill_t(fd, buf, 7, ms)) != 7) { closesocket(fd); erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } closesocket(fd); buf[7]=(char)0; /* terminate the string */ if (!strcmp("STOPPED",(char *)buf)) { EI_TRACE_CONN0("ei_unpublish_tmo","<- STOPPED (success)"); return 0; } else if (!strcmp("NOEXIST",(char *)buf)) { EI_TRACE_ERR0("ei_unpublish_tmo","<- NOEXIST (failure)"); erl_errno = EIO; return -1; } else { EI_TRACE_ERR0("ei_unpublish_tmo","<- unknown (failure)"); erl_errno = EIO; return -1; /* this shouldn't happen */ } return 0; }
/* this protocol is a lot more complex than the old one */ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms) { char buf[EPMDBUF]; char *s = buf; int fd; int elen = 0; int nlen = strlen(alive); int len = elen + nlen + 13; /* hard coded: be careful! */ int n; int res, creation; if (len > sizeof(buf)-2) { erl_errno = ERANGE; return -1; } s = buf; put16be(s,len); put8(s,EI_EPMD_ALIVE2_REQ); put16be(s,port); /* port number */ put8(s,'h'); /* h = r4 hidden node */ put8(s, EI_MYPROTO); /* protocol 0 ?? */ put16be(s,EI_DIST_HIGH); /* highest understood version: 1 = R4 */ put16be(s,EI_DIST_LOW); /* lowest: 0 = R3 */ put16be(s,nlen); /* length of alivename */ strcpy(s, alive); s += nlen; put16be(s,elen); /* length of extra string = 0 */ /* no extra string */ if ((fd = ei_epmd_connect_tmo(NULL,ms)) < 0) return fd; if ((res = ei_write_fill_t(fd, buf, len+2, ms)) != len+2) { closesocket(fd); erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } EI_TRACE_CONN6("ei_epmd_r4_publish", "-> ALIVE2_REQ alive=%s port=%d ntype=%d " "proto=%d dist-high=%d dist-low=%d", alive,port,'H',EI_MYPROTO,EI_DIST_HIGH,EI_DIST_LOW); if ((n = ei_read_fill_t(fd, buf, 4, ms)) != 4) { EI_TRACE_ERR0("ei_epmd_r4_publish","<- CLOSE"); closesocket(fd); erl_errno = (n == -2) ? ETIMEDOUT : EIO; return -2; /* version mismatch */ } /* Don't close fd here! It keeps us registered with epmd */ s = buf; if (((res=get8(s)) != EI_EPMD_ALIVE2_RESP)) { /* response */ EI_TRACE_ERR1("ei_epmd_r4_publish","<- unknown (%d)",res); EI_TRACE_ERR0("ei_epmd_r4_publish","-> CLOSE"); closesocket(fd); erl_errno = EIO; return -1; } EI_TRACE_CONN0("ei_epmd_r4_publish","<- ALIVE2_RESP"); if (((res=get8(s)) != 0)) { /* 0 == success */ EI_TRACE_ERR1("ei_epmd_r4_publish"," result=%d (fail)",res); closesocket(fd); erl_errno = EIO; return -1; } creation = get16be(s); EI_TRACE_CONN2("ei_epmd_r4_publish", " result=%d (ok) creation=%d",res,creation); /* probably should save fd so we can close it later... */ /* epmd_saveconn(OPEN,fd,alive); */ /* return the creation number, for no good reason */ /* return creation;*/ /* no - return the descriptor */ return fd; }
/* length (4), PASS_THOUGH (1), header, message */ int ei_recv_internal (int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglenp, int staticbufp, unsigned ms) { char header[EIRECVBUF]; char *s=header; char *mbuf=*mbufp; int len = 0; int msglen = 0; int bytesread = 0; int remain; int arity; int version; int index = 0; int i = 0; int res; int show_this_msg = 0; /* get length field */ if ((res = ei_read_fill_t(fd, header, 4, ms)) != 4) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } len = get32be(s); /* got tick - respond and return */ if (!len) { char tock[] = {0,0,0,0}; ei_write_fill_t(fd, tock, sizeof(tock), ms); /* Failure no problem */ *msglenp = 0; return 0; /* maybe flag ERL_EAGAIN [sverkerw] */ } /* turn off tracing on each receive. it will be turned back on if * we receive a trace token. */ ei_trace(-1,NULL); /* read enough to get at least entire header */ bytesread = (len > EIRECVBUF ? EIRECVBUF : len); if ((i = ei_read_fill_t(fd,header,bytesread,ms)) != bytesread) { erl_errno = (i == -2) ? ETIMEDOUT : EIO; return -1; } /* now decode header */ /* pass-through, version, control tuple header, control message type */ s = header; index = 1; if ((get8(s) != ERL_PASS_THROUGH) || ei_decode_version(header,&index,&version) || (version != ERL_VERSION_MAGIC) || ei_decode_tuple_header(header,&index,&arity) || ei_decode_long(header,&index,&msg->msgtype)) { erl_errno = EIO; /* Maybe another code for decoding errors */ return -1; } switch (msg->msgtype) { case ERL_SEND: /* { SEND, Cookie, ToPid } */ if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_atom(header,&index,msg->cookie) || ei_decode_pid(header,&index,&msg->to)) { erl_errno = EIO; return -1; } break; case ERL_REG_SEND: /* { REG_SEND, From, Cookie, ToName } */ if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_atom(header,&index,msg->cookie) || ei_decode_atom(header,&index,msg->toname)) { erl_errno = EIO; return -1; } /* actual message is remaining part of headerbuf, plus any unread bytes */ break; case ERL_LINK: /* { LINK, From, To } */ case ERL_UNLINK: /* { UNLINK, From, To } */ case ERL_GROUP_LEADER: /* { GROUP_LEADER, From, To } */ if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_pid(header,&index,&msg->to)) { erl_errno = EIO; return -1; } break; case ERL_EXIT: /* { EXIT, From, To, Reason } */ case ERL_EXIT2: /* { EXIT2, From, To, Reason } */ if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_pid(header,&index,&msg->to)) { erl_errno = EIO; return -1; } break; case ERL_SEND_TT: /* { SEND_TT, Cookie, ToPid, TraceToken } */ if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_atom(header,&index,msg->cookie) || ei_decode_pid(header,&index,&msg->to) || ei_decode_trace(header,&index,&msg->token)) { erl_errno = EIO; return -1; } ei_trace(1,&msg->token); /* turn on tracing */ break; case ERL_REG_SEND_TT: /* { REG_SEND_TT, From, Cookie, ToName, TraceToken } */ if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_atom(header,&index,msg->cookie) || ei_decode_atom(header,&index,msg->toname) || ei_decode_trace(header,&index,&msg->token)) { erl_errno = EIO; return -1; } ei_trace(1,&msg->token); /* turn on tracing */ break; case ERL_EXIT_TT: /* { EXIT_TT, From, To, TraceToken, Reason } */ case ERL_EXIT2_TT: /* { EXIT2_TT, From, To, TraceToken, Reason } */ if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_pid(header,&index,&msg->to) || ei_decode_trace(header,&index,&msg->token)) { erl_errno = EIO; return -1; } ei_trace(1,&msg->token); /* turn on tracing */ break; case ERL_NODE_LINK: /* { NODE_LINK } */ if (ei_tracelevel >= 4) show_this_msg = 1; break; default: /* unknown type, just put any remaining bytes into buffer */ break; } /* actual message is remaining part of headerbuf, plus any unread bytes */ msglen = len - index; /* message size (payload) */ remain = len - bytesread; /* bytes left to read */ /* if callers buffer is too small, we flush in the rest of the * message and discard it, unless we know that we can reallocate * the buffer in which case we do that and read the message. */ if (msglen > *bufsz) { if (staticbufp) { int sz = EIRECVBUF; /* flush in rest of packet */ while (remain > 0) { if (remain < sz) sz = remain; if ((i=ei_read_fill_t(fd,header,sz,ms)) <= 0) break; remain -= i; } erl_errno = EMSGSIZE; return -1; } else { /* Dynamic buffer --- grow it. */ #ifdef DEBUG fprintf(stderr, "Growing buffer from %d bytes to %d bytes\n", *bufsz, msglen); #endif if ((mbuf = realloc(*mbufp, msglen)) == NULL) { erl_errno = ENOMEM; return -1; } *mbufp = mbuf; *bufsz = msglen; } } /* move remaining bytes to callers buffer */ memmove(mbuf,header+index,bytesread-index); /* let the caller know how big the message is in his buffer */ *msglenp = msglen; /* read the rest of the message into callers buffer */ if (remain > 0) { if ((i = ei_read_fill_t(fd,mbuf+bytesread-index,remain,ms)) != remain) { *msglenp = bytesread-index+1; /* actual bytes in users buffer */ erl_errno = (i == -2) ? ETIMEDOUT : EIO; return -1; } } if (show_this_msg) ei_show_recmsg(stderr,msg,mbuf); /* the caller only sees "untraced" message types */ /* the trace token is buried in the message struct */ if (msg->msgtype > 10) msg->msgtype -= 10; return msg->msgtype; }
static int ei_epmd_r4_port (struct in_addr *addr, const char *alive, int *dist, unsigned ms) { char buf[EPMDBUF]; char *s = buf; int len = strlen(alive) + 1; int fd; int ntype; int port; int dist_high, dist_low, proto; int res; #if defined(VXWORKS) char ntoabuf[32]; #endif if (len > sizeof(buf) - 3) { erl_errno = ERANGE; return -1; } put16be(s,len); put8(s,EI_EPMD_PORT2_REQ); strcpy(s,alive); /* connect to epmd */ if ((fd = ei_epmd_connect_tmo(addr,ms)) < 0) { return -1; } if ((res = ei_write_fill_t(fd, buf, len+2, ms)) != len+2) { closesocket(fd); erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } #ifdef VXWORKS /* FIXME use union/macro for level. Correct level? */ if (ei_tracelevel > 2) { inet_ntoa_b(*addr,ntoabuf); EI_TRACE_CONN2("ei_epmd_r4_port", "-> PORT2_REQ alive=%s ip=%s",alive,ntoabuf); } #else EI_TRACE_CONN2("ei_epmd_r4_port", "-> PORT2_REQ alive=%s ip=%s",alive,inet_ntoa(*addr)); #endif /* read first two bytes (response type, response) */ if ((res = ei_read_fill_t(fd, buf, 2, ms)) != 2) { EI_TRACE_ERR0("ei_epmd_r4_port","<- CLOSE"); erl_errno = (res == -2) ? ETIMEDOUT : EIO; closesocket(fd); return -2; /* version mismatch */ } s = buf; res = get8(s); if (res != EI_EPMD_PORT2_RESP) { /* response type */ EI_TRACE_ERR1("ei_epmd_r4_port","<- unknown (%d)",res); EI_TRACE_ERR0("ei_epmd_r4_port","-> CLOSE"); closesocket(fd); erl_errno = EIO; return -1; } /* got negative response */ if ((res = get8(s))) { /* got negative response */ EI_TRACE_ERR1("ei_epmd_r4_port","<- PORT2_RESP result=%d (failure)",res); closesocket(fd); erl_errno = EIO; return -1; } EI_TRACE_CONN1("ei_epmd_r4_port","<- PORT2_RESP result=%d (ok)",res); /* expecting remaining 8 bytes */ if ((res = ei_read_fill_t(fd,buf,8,ms)) != 8) { EI_TRACE_ERR0("ei_epmd_r4_port","<- CLOSE"); erl_errno = (res == -2) ? ETIMEDOUT : EIO; closesocket(fd); return -1; } closesocket(fd); s = buf; port = get16be(s); ntype = get8(s); proto = get8(s); dist_high = get16be(s); dist_low = get16be(s); EI_TRACE_CONN5("ei_epmd_r4_port", " port=%d ntype=%d proto=%d dist-high=%d dist-low=%d", port,ntype,proto,dist_high,dist_low); /* right network protocol? */ if (EI_MYPROTO != proto) { erl_errno = EIO; return -1; } /* is there overlap in our distribution versions? */ if ((EI_DIST_HIGH < dist_low) || (EI_DIST_LOW > dist_high)) { erl_errno = EIO; return -1; } /* choose the highest common version */ /* i.e. min(his-max, my-max) */ *dist = (dist_high > EI_DIST_HIGH ? EI_DIST_HIGH : dist_high); /* ignore the remaining fields */ return port; }
int ei_read_fill(int fd, char* buf, int len) { return ei_read_fill_t(fd, buf, len, 0); }