/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2003-2016. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* %CopyrightEnd%
*/
#include <stdio.h>
#include <string.h>
#ifdef VXWORKS
#include "reclaim.h"
#endif
#ifdef __WIN32__
#include <winsock2.h>
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include "ei_runner.h"
#ifndef __WIN32__
#define closesocket(X) close(X)
#endif
#define DEBUG 1
#ifdef DEBUG
#include <stdarg.h>
FILE *debugfile = NULL;
#define OPEN_DEBUGFILE(Number) debugf_open(Number)
#define CLOSE_DEBUGFILE() debugf_close()
#define DEBUGF(X) debugf X
static void debugf(char *format, ...)
{
va_list ap;
va_start(ap,format);
if (debugfile) {
vfprintf(debugfile,format,ap);
fflush(debugfile);
} else {
fprintf(stderr,"Attempt to write to debugfile when not open...\n");
}
va_end(ap);
}
static void debugf_open(int number)
{
char filename[1024];
sprintf(filename,"ei_tmo_test%d.debug",number);
#if !defined(VXWORKS) && !defined(__WIN32__)
close(2);
#endif
debugfile = fopen(filename,"a");
fprintf(debugfile,"===================================================\n");
}
static void debugf_close(void)
{
if (debugfile)
fclose(debugfile);
}
#else
#define OPEN_DEBUGFILE(X) /* noop */
#define CLOSE_DEBUGFILE() /* noop */
#define DEBUGF(X) /* noop */
#endif
TESTCASE(framework_check)
{
char *ptr = NULL;
int len;
#ifdef DEBUG
int version;
int i;
#endif
OPEN_DEBUGFILE(1);
DEBUGF(("B�rjar... \n"));
ptr = read_packet(&len);
if (*ptr != 't') {
DEBUGF(("Gick fel \n"));
report(1);
} else {
ei_x_buff x;
ei_x_new(&x);
ei_x_append_buf(&x, ptr+1,len-1);
DEBUGF(("Gick bra? %d\n",x.index));
#ifdef DEBUG
for(i=0;i < x.index; ++i)
DEBUGF(("%d ",(int) ((unsigned char *) x.buff)[i]));
DEBUGF(("\n"));
len = 0;
ei_decode_version(x.buff,&len,&version);
ei_print_term(debugfile,x.buff,&len);
fflush(debugfile);
#endif
send_bin_term(&x);
ei_x_free(&x);
}
if (ptr != NULL)
free(ptr);
CLOSE_DEBUGFILE();
report(1);
}
int decode_request(char **nodename_p, char **cookie_p, char **peername_p)
{
char *nodename = NULL;
char *cookie = NULL;
char *peername = NULL;
char *ptr = NULL;
ei_x_buff x;
int len;
int version;
int type;
int size;
int expected_size = (peername_p == NULL) ? 2 : 3;
int ret = -1;
ptr = read_packet(&len);
ei_x_new(&x);
if (*ptr != 't') {
goto cleanup;
}
ei_x_append_buf(&x, ptr+1,len-1);
len = 0;
ei_decode_version(x.buff,&len,&version);
#ifdef DEBUG
{
int tlen = len;
ei_print_term(debugfile,x.buff,&tlen);
DEBUGF(("\n"));
}
#endif
if (ei_get_type(x.buff,&len,&type,&size) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (type != ERL_SMALL_TUPLE_EXT || size != expected_size) {
DEBUGF(("Failure at line %d, type=%d, size = %d\n",__LINE__,
type,size));
goto cleanup;
}
if (ei_decode_tuple_header(x.buff,&len,&size) != 0 || size != expected_size) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (ei_get_type(x.buff,&len,&type,&size) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (type != ERL_ATOM_EXT) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
nodename = malloc(size+1);
ei_decode_atom(x.buff,&len,nodename);
nodename[size] = '\0'; /* needed????? */
if (ei_get_type(x.buff,&len,&type,&size) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (type != ERL_ATOM_EXT) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
cookie = malloc(size + 1);
ei_decode_atom(x.buff,&len,cookie);
cookie[size] = '\0'; /* needed????? */
if (expected_size > 2) {
if (ei_get_type(x.buff,&len,&type,&size) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (type != ERL_ATOM_EXT) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
peername = malloc(size + 1);
ei_decode_atom(x.buff,&len,peername);
peername[size] = '\0'; /* needed????? */
DEBUGF(("nodename = %s, cookie = %s, peername = %s\n",
nodename, cookie, peername));
*peername_p = peername;
peername = NULL;
} else {
DEBUGF(("nodename = %s, cookie = %s\n",
nodename, cookie));
}
*nodename_p = nodename;
nodename = NULL;
*cookie_p = cookie;
cookie = NULL;
ret = 0;
cleanup:
ei_x_free(&x);
if (ptr != NULL) {
free(ptr);
}
if (nodename != NULL) {
free(nodename);
}
if (cookie != NULL) {
free(cookie);
}
if (peername != NULL) {
free(peername);
}
return ret;
}
int get_message(int com_sock, ei_x_buff *buff,
char *atom_buff, erlang_pid *pid, int *iterations)
{
ei_x_buff buffer;
int ret_val,index;
erlang_msg msg;
int res = -1;
int totlen;
int type;
int size;
int version;
long tmp;
ei_x_new(&buffer);
for (;;) {
/* Reset buffer index before reading */
buffer.index = 0;
/* Receive message */
if ((ret_val = ei_xreceive_msg(com_sock, &msg, &buffer)) ==
ERL_TICK) {
/* Ticks are automatically answered, just continue */
continue;
} else if (ret_val != ERL_MSG) {
DEBUGF(("Peer has closed, ret_val = %d (%d).\n",
ret_val,erl_errno));
goto cleanup;
}
switch (msg.msgtype) {
case ERL_SEND:
case ERL_REG_SEND:
index = 0;
ei_decode_version(buffer.buff,&index,&version);
DEBUGF(("Peer sent the following message to me: "));
#ifdef DEBUG
{
int ndx = index;
/*in debug log on Unix*/
ei_print_term(debugfile, buffer.buff, &ndx);
}
#endif
DEBUGF(("\n"));
if (ei_get_type(buffer.buff,&index,&type,&size) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (type != ERL_SMALL_TUPLE_EXT || size != 3) {
DEBUGF(("Failure at line %d, type=%d, size = %d\n",__LINE__,
type,size));
goto cleanup;
}
if (ei_decode_tuple_header(buffer.buff,&index,&size) != 0 ||
size != 3) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (ei_get_type(buffer.buff,&index,&type,&size) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (type == ERL_ATOM_EXT) {
ei_decode_atom(buffer.buff,&index,atom_buff);
atom_buff[size] ='\0';
res = 2;
} else if (type == ERL_PID_EXT) {
ei_decode_pid(buffer.buff,&index,pid);
res = 1;
} else {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (ei_get_type(buffer.buff,&index,&type,&size) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
switch (type) {
case ERL_SMALL_INTEGER_EXT:
case ERL_INTEGER_EXT:
ei_decode_long(buffer.buff,&index,&tmp);
break;
default:
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
*iterations = (int)tmp;
totlen = buffer.index - index;
ei_x_append_buf(buff,buffer.buff+index,totlen);
goto cleanup;
default:
DEBUGF(("Unexpected message type from peer. Goodbye.\n"));
goto cleanup;
}
}
cleanup:
ei_x_free(&buffer);
return res;
}
TESTCASE(recv_tmo)
{
char *nodename = NULL;
char *cookie = NULL;
char *peername = NULL;
int com_sock = -1;
ei_cnode nodeinfo;
OPEN_DEBUGFILE(5);
if (decode_request(&nodename,&cookie,&peername) != 0) {
goto cleanup;
}
if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if ((com_sock = ei_connect_tmo(&nodeinfo, peername, 5000)) < 0) {
ei_x_buff answer;
DEBUGF(("Got error while connecting.{%d,%d}\n",com_sock,erl_errno));
ei_x_new(&answer);
ei_x_format(&answer,"{~i,~i,~i}",com_sock,erl_errno,ETIMEDOUT);
#ifdef DEBUG
{
int tlen = 0;
int v;
ei_decode_version(answer.buff,&tlen,&v);
ei_print_term(debugfile,answer.buff,&tlen);
DEBUGF(("\n"));
}
#endif
send_bin_term(&answer);
DEBUGF(("Binary term sent.\n"));
ei_x_free(&answer);
} else {
ei_x_buff answer;
int ret_val;
ei_x_buff buffer;
erlang_msg msg;
int index,version;
DEBUGF(("Success when connecting.{%d,%d}\n",com_sock,erl_errno));
ei_x_new(&answer);
ei_x_format(&answer,"~i",com_sock);
send_bin_term(&answer);
ei_x_free(&answer);
ei_x_new(&buffer);
for (;;) {
/* Reset buffer index before reading */
buffer.index = 0;
/* Receive message */
if ((ret_val = ei_xreceive_msg_tmo(com_sock, &msg, &buffer,5000))
== ERL_TICK) {
/* Ticks are automatically answered, just continue */
continue;
} else if (ret_val != ERL_MSG) {
ei_x_new(&answer);
ei_x_format(&answer,"{~i,~i,~i}",ret_val,erl_errno,ETIMEDOUT);
send_bin_term(&answer);
ei_x_free(&answer);
ei_x_free(&buffer);
DEBUGF(("Got error receiving, sending {%d,%d} and exiting\n",
ret_val,erl_errno));
goto cleanup;
}
switch (msg.msgtype) {
case ERL_SEND:
case ERL_REG_SEND:
index = 0;
ei_decode_version(buffer.buff,&index,&version);
DEBUGF(("Peer sent the following message to me: "));
#ifdef DEBUG
{
int ndx = index;
/*in debug log on Unix*/
ei_print_term(debugfile, buffer.buff, &ndx);
}
#endif
DEBUGF(("\n"));
send_bin_term(&buffer);
ei_x_free(&buffer);
goto cleanup;
default:
DEBUGF(("Unexpected message type from peer. Goodbye.\n"));
goto cleanup;
}
}
}
cleanup:
if (com_sock >= 0) {
closesocket(com_sock);
}
if (nodename != NULL) {
free(nodename);
}
if (cookie != NULL) {
free(cookie);
}
if (peername != NULL) {
free(peername);
}
CLOSE_DEBUGFILE();
report(1);
}
TESTCASE(send_tmo)
{
char *nodename = NULL;
char *cookie = NULL;
char *peername = NULL;
int com_sock = -1;
ei_cnode nodeinfo;
OPEN_DEBUGFILE(4);
if (decode_request(&nodename,&cookie,&peername) != 0) {
goto cleanup;
}
if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if ((com_sock = ei_connect_tmo(&nodeinfo, peername, 5000)) < 0) {
ei_x_buff answer;
DEBUGF(("Got error while connecting.{%d,%d}\n",com_sock,erl_errno));
ei_x_new(&answer);
ei_x_format(&answer,"{~i,~i,~i}",com_sock,erl_errno,ETIMEDOUT);
#ifdef DEBUG
{
int tlen = 0;
int v;
ei_decode_version(answer.buff,&tlen,&v);
ei_print_term(debugfile,answer.buff,&tlen);
DEBUGF(("\n"));
}
#endif
send_bin_term(&answer);
DEBUGF(("Binary term sent.\n"));
ei_x_free(&answer);
} else {
ei_x_buff answer;
char atom[256];
erlang_pid pid;
int res, iterations, i;
ei_x_buff send_buffer;
DEBUGF(("Success when connecting.{%d,%d}\n",com_sock,erl_errno));
ei_x_new(&answer);
ei_x_format(&answer,"~i",com_sock);
send_bin_term(&answer);
ei_x_free(&answer);
ei_x_new_with_version(&send_buffer);
if ((res = get_message(com_sock, &send_buffer,
atom ,&pid, &iterations)) < 0) {
DEBUGF(("Get_message_failure at line %d\n",__LINE__));
ei_x_free(&send_buffer);
goto cleanup;
}
DEBUGF(("Get_message success (%d), bindata:\n",res));
#ifdef DEBUG
{
int ndx = 0;
int v;
ei_decode_version(send_buffer.buff,&ndx,&v);
ei_print_term(debugfile, send_buffer.buff, &ndx);
}
#endif
DEBUGF(("\n"));
switch (res) {
case 1: /* Send to pid in 'pid' */
ei_x_new(&answer);
for (i=0;i < iterations; ++i) {
res = ei_send_tmo(com_sock, &pid, send_buffer.buff,
send_buffer.index, 5000);
if (res < 0) {
DEBUGF(("Sent bindata failed (%d) after %d iterations:\n", res, i));
break;
}
#ifdef DEBUG
if (i < 10 || (i % 100 == 0)) /* don't flood the log */
{
int ndx = 0;
int v;
DEBUGF(("%d: Sent bindata (%d): ", i, res));
ei_decode_version(send_buffer.buff,&ndx,&v);
ei_print_term(debugfile, send_buffer.buff, &ndx);
DEBUGF(("\n"));
}
#endif
}
if (res < 0) {
DEBUGF(("ei_send_tmo failure at line %d\n",__LINE__));
ei_x_format(&answer,"{~i,~i,~i,~i}",res,erl_errno,i,ETIMEDOUT);
} else {
ei_x_format(&answer,"~i",res);
}
send_bin_term(&answer);
ei_x_free(&answer);
ei_x_free(&send_buffer);
goto cleanup;
case 2: /* Registered name in 'atom' */
ei_x_new(&answer);
for (i=0;i < iterations; ++i) {
res = ei_reg_send_tmo(&nodeinfo, com_sock, atom,
send_buffer.buff,
send_buffer.index,5000);
if (res < 0)
break;
}
if (res < 0) {
DEBUGF(("ei_reg_send_tmo failure at line %d\n",__LINE__));
ei_x_format(&answer,"{~i,~i,~i,~i}",res,erl_errno,i,ETIMEDOUT);
} else {
ei_x_format(&answer,"~i",res);
}
send_bin_term(&answer);
ei_x_free(&answer);
ei_x_free(&send_buffer);
goto cleanup;
default:
DEBUGF(("unexpected request number %d at line %d\n",res,__LINE__));
ei_x_free(&send_buffer);
goto cleanup;
}
}
cleanup:
if (com_sock >= 0) {
closesocket(com_sock);
}
if (nodename != NULL) {
free(nodename);
}
if (cookie != NULL) {
free(cookie);
}
if (peername != NULL) {
free(peername);
}
CLOSE_DEBUGFILE();
report(1);
}
TESTCASE(connect_tmo)
{
char *nodename = NULL;
char *cookie = NULL;
char *peername = NULL;
int com_sock = -1;
ei_cnode nodeinfo;
OPEN_DEBUGFILE(3);
if (decode_request(&nodename,&cookie,&peername) != 0) {
goto cleanup;
}
if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if ((com_sock = ei_connect_tmo(&nodeinfo, peername, 5000)) < 0) {
ei_x_buff answer;
DEBUGF(("Got error while connecting.{%d,%d}\n",com_sock,erl_errno));
ei_x_new(&answer);
/* On some systems errno gets set to EHOSTUNREACH rather than
ETIMEDOUT, which is ok. Let's check for that and report timeout
if it happens.
Max OS X seems to respond EHOSTDOWN, which should be ok.
*/
#if defined(EHOSTUNREACH)
if (errno == EHOSTUNREACH)
ei_x_format(&answer,"{~i,~i,~i}",com_sock,ETIMEDOUT,ETIMEDOUT);
else
#endif
#if defined(EHOSTDOWN)
if (errno == EHOSTDOWN)
ei_x_format(&answer,"{~i,~i,~i}",com_sock,ETIMEDOUT,ETIMEDOUT);
else
#endif
ei_x_format(&answer,"{~i,~i,~i}",com_sock,erl_errno,ETIMEDOUT);
#ifdef DEBUG
{
int tlen = 0;
int v;
ei_decode_version(answer.buff,&tlen,&v);
ei_print_term(debugfile,answer.buff,&tlen);
DEBUGF(("\n"));
}
#endif
send_bin_term(&answer);
DEBUGF(("Binary term sent.\n"));
ei_x_free(&answer);
} else {
ei_x_buff answer;
DEBUGF(("Success when connecting.{%d,%d}\n",com_sock,erl_errno));
ei_x_new(&answer);
ei_x_format(&answer,"~i",com_sock);
send_bin_term(&answer);
ei_x_free(&answer);
}
cleanup:
if (com_sock >= 0) {
closesocket(com_sock);
}
if (nodename != NULL) {
free(nodename);
}
if (cookie != NULL) {
free(cookie);
}
if (peername != NULL) {
free(peername);
}
CLOSE_DEBUGFILE();
report(1);
}
TESTCASE(accept_tmo)
{
char *nodename = NULL;
char *cookie = NULL;
int listen_sock = -1;
int epmd_sock = -1;
int com_sock = -1;
struct sockaddr_in sin;
int sin_siz = sizeof(sin);
ErlConnect peer;
ei_cnode nodeinfo;
OPEN_DEBUGFILE(2);
putenv("EI_TRACELEVEL=10");
if (decode_request(&nodename,&cookie,NULL) != 0) {
goto cleanup;
}
if (ei_connect_init(&nodeinfo, nodename, cookie, 0) < 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
if (bind(listen_sock,(struct sockaddr *) &sin, sizeof(sin)) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (getsockname(listen_sock,
(struct sockaddr *) &sin, &sin_siz) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if (listen(listen_sock, 5) != 0) {
DEBUGF(("Failure at line %d\n",__LINE__));
goto cleanup;
}
if ((epmd_sock = ei_publish(&nodeinfo, ntohs(sin.sin_port))) < 0) {
DEBUGF(("Failure at line %d[%d,%d]\n",__LINE__,sin.sin_port,erl_errno));
goto cleanup;
}
if ((com_sock = ei_accept_tmo(&nodeinfo,
listen_sock, &peer, 5000)) == ERL_ERROR) {
ei_x_buff answer;
DEBUGF(("Got error while accepting.{%d,%d}\n",com_sock,erl_errno));
ei_x_new(&answer);
ei_x_format(&answer,"{~i,~i,~i}",com_sock,erl_errno,ETIMEDOUT);
#ifdef DEBUG
{
int tlen = 0;
int v;
ei_decode_version(answer.buff,&tlen,&v);
ei_print_term(debugfile,answer.buff,&tlen);
DEBUGF(("\n"));
}
#endif
send_bin_term(&answer);
DEBUGF(("Binary term sent.\n"));
ei_x_free(&answer);
} else {
ei_x_buff answer;
DEBUGF(("Success when connecting.{%d,%d}\n",com_sock,erl_errno));
ei_x_new(&answer);
ei_x_format(&answer,"~i",com_sock);
send_bin_term(&answer);
ei_x_free(&answer);
}
cleanup:
if (listen_sock >= 0) {
closesocket(listen_sock);
}
if (epmd_sock >= 0) {
closesocket(epmd_sock);
}
if (com_sock >= 0) {
closesocket(com_sock);
}
if (nodename != NULL) {
free(nodename);
}
if (cookie != NULL) {
free(cookie);
}
CLOSE_DEBUGFILE();
report(1);
}