static int send_challenge_ack(int fd, unsigned char digest[16], unsigned ms) { char *s; char buf[DEFBUF_SIZ]; int siz = 2 + 1 + 16; int res; s = buf; put16be(s,siz - 2); put8(s, 'a'); memcpy(s, digest, 16); if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) { EI_TRACE_ERR0("recv_challenge_reply", "-> SEND_CHALLENGE_ACK socket write failed"); erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } if (ei_tracelevel >= 3) { char buffer[33]; EI_TRACE_CONN1("recv_challenge_reply", "-> SEND_CHALLENGE_ACK (ok) digest = %s",hex((char *)digest,buffer)); } return 0; }
/* 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; }
/* this sends either link or unlink ('which' decides) */ static int link_unlink(int fd, const erlang_pid *from, const erlang_pid *to, int which, unsigned ms) { char msgbuf[EISMALLBUF]; char *s; int index = 0; int n; index = 5; /* max sizes: */ ei_encode_version(msgbuf,&index); /* 1 */ ei_encode_tuple_header(msgbuf,&index,3); ei_encode_long(msgbuf,&index,which); ei_encode_pid(msgbuf,&index,from); /* 268 */ ei_encode_pid(msgbuf,&index,to); /* 268 */ /* 5 byte header missing */ s = msgbuf; put32be(s, index - 4); /* 4 */ put8(s, ERL_PASS_THROUGH); /* 1 */ /* sum: 542 */ #ifdef DEBUG_DIST if (ei_trace_distribution > 1) ei_show_sendmsg(stderr,msgbuf,NULL); #endif n = ei_write_fill_t(fd,msgbuf,index,ms); return (n==index ? 0 : -1); }
static int send_status(int fd, char *status, unsigned ms) { char *buf, *s; char dbuf[DEFBUF_SIZ]; int siz = strlen(status) + 1 + 2; int res; buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf; if (!buf) { erl_errno = ENOMEM; return -1; } s = buf; put16be(s,siz - 2); put8(s, 's'); memcpy(s, status, strlen(status)); if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) { EI_TRACE_ERR0("send_status","-> SEND_STATUS socket write failed"); if (buf != dbuf) free(buf); erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } EI_TRACE_CONN1("send_status","-> SEND_STATUS (%s)",status); if (buf != dbuf) free(buf); return 0; }
int ei_send_exit_tmo(int fd, const erlang_pid *from, const erlang_pid *to, const char *reason, unsigned ms) { char sbuf[EISMALLBUF]; erlang_trace *token = NULL; char *dbuf = NULL; char *msgbuf; char *s; int index = 0; int len = strlen(reason) + 1080; /* see below */ if (len > EISMALLBUF) if (!(dbuf = malloc(len))) return -1; msgbuf = (dbuf ? dbuf : sbuf); /* are we tracing? */ /* check that he can receive trace tokens first */ if (ei_distversion(fd) > 0) token = ei_trace(0,NULL); index = 5; /* max sizes: */ ei_encode_version(msgbuf,&index); /* 1 */ if (token) { ei_encode_tuple_header(msgbuf,&index,5); /* 2 */ ei_encode_long(msgbuf,&index,ERL_EXIT_TT); /* 2 */ } else { ei_encode_tuple_header(msgbuf,&index,4); ei_encode_long(msgbuf,&index,ERL_EXIT); } ei_encode_pid(msgbuf,&index,from); /* 268 */ ei_encode_pid(msgbuf,&index,to); /* 268 */ if (token) ei_encode_trace(msgbuf,&index,token); /* 534 */ /* Reason */ ei_encode_string(msgbuf,&index,reason); /* len */ /* 5 byte header missing */ s = msgbuf; put32be(s, index - 4); /* 4 */ put8(s, ERL_PASS_THROUGH); /* 1 */ /*** sum: len + 1080 */ /* FIXME incorrect level */ if (ei_tracelevel > 1) ei_show_sendmsg(stderr,msgbuf,NULL); ei_write_fill_t(fd,msgbuf,index,ms); /* FIXME ignore timeout etc? erl_errno?! */ if (dbuf) free(dbuf); return 0; }
static int send_name_or_challenge(int fd, char *nodename, int f_chall, unsigned challenge, unsigned version, unsigned ms) { char *buf; unsigned char *s; char dbuf[DEFBUF_SIZ]; int siz = 2 + 1 + 2 + 4 + strlen(nodename); const char* function[] = {"SEND_NAME", "SEND_CHALLENGE"}; int res; if (f_chall) siz += 4; buf = (siz > DEFBUF_SIZ) ? malloc(siz) : dbuf; if (!buf) { erl_errno = ENOMEM; return -1; } s = (unsigned char *)buf; put16be(s,siz - 2); put8(s, 'n'); put16be(s, version); put32be(s, (DFLAG_EXTENDED_REFERENCES | DFLAG_DIST_MONITOR | DFLAG_EXTENDED_PIDS_PORTS | DFLAG_FUN_TAGS | DFLAG_NEW_FUN_TAGS | DFLAG_NEW_FLOATS | DFLAG_SMALL_ATOM_TAGS | DFLAG_UTF8_ATOMS | DFLAG_MAP_TAG | DFLAG_BIG_CREATION)); if (f_chall) put32be(s, challenge); memcpy(s, nodename, strlen(nodename)); if ((res = ei_write_fill_t(fd, buf, siz, ms)) != siz) { EI_TRACE_ERR1("send_name_or_challenge", "-> %s socket write failed", function[f_chall]); if (buf != dbuf) free(buf); erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } if (buf != dbuf) free(buf); return 0; }
/* 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; }
int ei_send_encoded_tmo(int fd, const erlang_pid *to, char *msg, int msglen, unsigned ms) { char *s, header[1200]; /* see size calculation below */ erlang_trace *token = NULL; int index = 5; /* reserve 5 bytes for control message */ int res; #ifdef HAVE_WRITEV struct iovec v[2]; #endif /* are we tracing? */ /* check that he can receive trace tokens first */ if (ei_distversion(fd) > 0) token = ei_trace(0,NULL); /* header = SEND, cookie, to max sizes: */ ei_encode_version(header,&index); /* 1 */ if (token) { ei_encode_tuple_header(header,&index,4); /* 2 */ ei_encode_long(header,&index,ERL_SEND_TT); /* 2 */ } else { ei_encode_tuple_header(header,&index,3); ei_encode_long(header,&index,ERL_SEND); } ei_encode_atom(header,&index,ei_getfdcookie(fd)); /* 258 */ ei_encode_pid(header,&index,to); /* 268 */ if (token) ei_encode_trace(header,&index,token); /* 534 */ /* control message (precedes header actually) */ /* length = 1 ('p') + header len + message len */ s = header; put32be(s, index + msglen - 4); /* 4 */ put8(s, ERL_PASS_THROUGH); /* 1 */ /*** sum: 1070 */ if (ei_tracelevel >= 4) ei_show_sendmsg(stderr,header,msg); #ifdef HAVE_WRITEV v[0].iov_base = (char *)header; v[0].iov_len = index; v[1].iov_base = (char *)msg; v[1].iov_len = msglen; if ((res = ei_writev_fill_t(fd,v,2,ms)) != index+msglen) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } #else /* !HAVE_WRITEV */ if ((res = ei_write_fill_t(fd,header,index,ms)) != index) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } if ((res = ei_write_fill_t(fd,msg,msglen,ms)) != msglen) { erl_errno = (res == -2) ? ETIMEDOUT : EIO; return -1; } #endif /* !HAVE_WRITEV */ 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_write_fill(int fd, char *buf, int len) { return ei_write_fill_t(fd, buf, len, 0); }