set( major_version "1" ) # should be automatically populated from git tag later, but until CI process sets a tag we use this
set( minor_version "0" )
-set( patch_level "27" )
+set( patch_level "28" )
set( install_root "${CMAKE_INSTALL_PREFIX}" )
set( install_lib "lib" )
rmr_get_src.3
rmr_mt_call.3
rmr_mt_rcv.3
+ rmr_get_srcip.3
)
# empty list of roff/troff input files we generated
rmr_init(3),
rmr_init_trace(3),
rmr_get_src(3),
+rmr_get_srcip(3),
rmr_get_trace(3),
rmr_get_trlen(3),
rmr_payload_size(3),
&half_space
&di(ENOMEM) A nil pointer was passed for &ital( dest, ) however it was not possible to allocate a
buffer using malloc().
-&end_dilist
+&end_dlist
&beg_dlist(.75i : ^&bold_font )
&half_space
&di(EINVAL) The message, or an internal portion of the message, was corrupted or the pointer was invalid.
-&end_dilist
+&end_dlist
rmr_call(3),
rmr_free_msg(3),
rmr_get_rcvfd(3),
+rmr_get_srcip(3),
rmr_payload_size(3),
rmr_send_msg(3),
rmr_rcv_msg(3),
#define RMR_MAX_XID 32 // space in header reserved for user xaction id
-#define RMR_MAX_SID 32 // spece in header reserved for sender id
#define RMR_MAX_MEID 32 // spece in header reserved for managed element id
#define RMR_MAX_SRC 64 // max length of hostname (which could be IPv6 addr with [...]:port so more than the 39 bytes of a plain addr
+#define RMR_MAX_SID 32 // misc sender info/data (reserved)
#define RMR_MAX_RCV_BYTES 4096 // max bytes we support in a receive message
// various flags for function calls
#define RMRFL_NOTHREAD 0x01 // do not start an additional route collector thread
#define RMRFL_MTCALL 0x02 // set up multi-threaded call support (rmr_init)
#define RMRFL_AUTO_ALLOC 0x03 // send auto allocates a zerocopy buffer
+#define RMRFL_NAME_ONLY 0x04 // only the hostname:ip is provided as source information for rts() calls
#define RMR_DEF_SIZE 0 // pass as size to have msg allocation use the default msg size
extern void rmr_free_msg( rmr_mbuf_t* mbuf );
extern unsigned char* rmr_get_meid( rmr_mbuf_t* mbuf, unsigned char* dest );
extern unsigned char* rmr_get_src( rmr_mbuf_t* mbuf, unsigned char* dest );
+extern unsigned char* rmr_get_srcip( rmr_mbuf_t* msg, unsigned char* dest );
extern rmr_mbuf_t* rmr_realloc_msg( rmr_mbuf_t* mbuf, int new_tr_size );
extern int rmr_str2meid( rmr_mbuf_t* mbuf, unsigned char const* str );
extern void rmr_str2payload( rmr_mbuf_t* mbuf, unsigned char const* str );
#define QUOTE_DEF(a) QUOTE(a) // allow a #define value to be quoted (e.g. QUOTE(MAJOR_VERSION) )
-#define RMR_MSG_VER 2 // message version this code was designed to handle
+#define RMR_MSG_VER 3 // message version this code was designed to handle
- // environment variable names we'll suss out
-#define ENV_BIND_IF "RMR_BIND_IF" // the interface to bind to for both normal comma and RTG (0.0.0.0 if missing)
-#define ENV_RTG_PORT "RMR_RTG_SVC" // the port we'll listen on for rtg connections
-#define ENV_SEED_RT "RMR_SEED_RT" // where we expect to find the name of the seed route table
-#define ENV_RTG_RAW "RMR_RTG_ISRAW" // if > 0 we expect route table gen messages as raw (not sent from an RMr application)
+ // environment variable names we'll suss out
+#define ENV_BIND_IF "RMR_BIND_IF" // the interface to bind to for both normal comma and RTG (0.0.0.0 if missing)
+#define ENV_RTG_PORT "RMR_RTG_SVC" // the port we'll listen on for rtg connections
+#define ENV_SEED_RT "RMR_SEED_RT" // where we expect to find the name of the seed route table
+#define ENV_RTG_RAW "RMR_RTG_ISRAW" // if > 0 we expect route table gen messages as raw (not sent from an RMr application)
#define ENV_VERBOSE_FILE "RMR_VCTL_FILE" // file where vlevel may be managed for some (non-time critical) functions
+#define ENV_NAME_ONLY "RMR_SRC_NAMEONLY" // src in message is name only
#define NO_FLAGS 0 // no flags to pass to a function
#define SET_HDR_D1_LEN(h,l) (((uta_mhdr_t *)h)->len2=htonl((int32_t)l))
#define SET_HDR_D2_LEN(h,l) (((uta_mhdr_t *)h)->len3=htonl((int32_t)l))
+#define HDR_VERSION(h) htonl((((uta_mhdr_t *)h)->rmr_ver))
+
// index of things in the d1 data space
#define D1_CALLID_IDX 0 // the call-id to match on return
int32_t len2; // length of data 1 (d1)
int32_t len3; // length of data 2 (d2)
int32_t sub_id; // subscription id (-1 invalid)
+
+ // v3 extension
+ unsigned char srcip[RMR_MAX_SRC]; // ip address and port of the source
} uta_mhdr_t;
int32_t plen; // payload length
int32_t rmr_ver; // our internal message version number
unsigned char xid[RMR_MAX_XID]; // space for user transaction id or somesuch
- unsigned char sid[RMR_MAX_SID]; // sender ID for return to sender needs
+ unsigned char sid[RMR_MAX_SID]; // misc sender info/data
unsigned char src[16]; // name of the sender (source) (old size was 16)
unsigned char meid[RMR_MAX_MEID]; // managed element id.
struct timespec ts; // timestamp ???
static char* uta_h2ip( char const* hname );
static int uta_lookup_rtg( uta_ctx_t* ctx );
static int uta_has_str( char const* buf, char const* str, char sep, int max );
+static char* get_default_ip( if_addrs_t* iplist );
// --- message ring --------------------------
static void* uta_mk_ring( int size );
return dest;
}
+
+/*
+ Returns the string with the IP address as reported by the sender. This is
+ the IP address that the sender has sussed off of one of the interfaces
+ and cannot be guarenteed to be the acutal IP address which was used to
+ establish the connection. The caller must provide a buffer of at least
+ 64 bytes; the string will be nil terminated. A pointer to the user's buffer
+ is returned on success, nil on failure.
+*/
+extern unsigned char* rmr_get_srcip( rmr_mbuf_t* msg, unsigned char* dest ) {
+ uta_mhdr_t* hdr = NULL;
+ char* rstr = NULL;
+
+ errno = EINVAL;
+
+ if( dest != NULL && msg != NULL ) {
+ hdr = msg->header;
+ if( HDR_VERSION( msg->header ) > 2 ) { // src ip was not present in hdr until ver 3
+ errno = 0;
+ strcpy( dest, hdr->srcip );
+ rstr = dest;
+ } else {
+ errno = 0;
+ strcpy( dest, hdr->src ); // reutrn the name:port for old messages
+ rstr = dest;
+ }
+ }
+
+ return rstr;
+}
that could be in the route table.
If the environment variable which limits the binding of our listen port
- to a single interface (ENV_BIND_IF) then ONLY that address is added to
- the list so that we don't pick up entries from the rtable that are for other
+ to a single interface (ENV_BIND_IF) then ONLY that interface/address is added
+ to the list so that we don't pick up entries from the rtable that are for other
processes listening on different interfaces.
+
+ The ENV_BIN_IF environment variable may be either an IP address (v6 must be in
+ square braces), or an interface name (e.g. eth0).
*/
if_addrs_t* mk_ip_list( char* port ) {
if_addrs_t* l;
char wbuf[NI_MAXHOST+128];
char* fmt;
char* envp; // at the environment var if there
-
+ char* target_if = NULL; // target interface supplied by ENV_BIND_IF
+ char* tok;
if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) {
}
if( (envp = getenv( ENV_BIND_IF )) != NULL ) {
- snprintf( wbuf, sizeof( wbuf ), "%s:%s", envp, port ); // smash port onto the addr as is
- l->addrs[l->naddrs] = strdup( wbuf );
- l->naddrs++;
- if( DEBUG ) fprintf( stderr, "[INFO] rmr: using only specific bind interface when searching specific RT entries: %s\n", wbuf );
- return l;
+ if( isdigit( *envp ) || *envp == '[' ) { // ip address given and not device name
+ snprintf( wbuf, sizeof( wbuf ), "%s:%s", envp, port ); // smash port onto the addr as is
+ l->addrs[l->naddrs] = strdup( wbuf );
+ l->naddrs++;
+ if( DEBUG ) fprintf( stderr, "[INFO] rmr: using only specific bind interface when searching specific RT entries: %s\n", wbuf );
+ return l;
+ }
+
+ target_if = envp; // device name given, suss it out below
}
getifaddrs( &ifs );
for( ele = ifs; ele; ele = ele->ifa_next ) {
- *octs = 0;
+ memset( octs, 0, sizeof( octs ) );
+ if( ele && strcmp( ele->ifa_name, "lo" ) && // do NOT capture the loopback interface address
+ (target_if == NULL || strcmp( ele->ifa_name, target_if ) == 0 ) ) { // no target, or matches ENV_BIND_IF target
- if( ele && strcmp( ele->ifa_name, "lo" ) ) {
if( ele->ifa_addr->sa_family == AF_INET ) {
getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
fmt = "%s:%s";
}
if( *octs ) {
+ if( (tok = strchr( octs, '%' )) != NULL ) { // for unknown reasons some ip6 addrs have %if-name appended; truncate
+ *tok = 0;
+ }
if( l->naddrs < 128 ) {
+ if( DEBUG ) fprintf( stderr, "[DBUG] capture address: %s: %s\n", ele->ifa_name, octs );
+
snprintf( wbuf, sizeof( wbuf ), fmt, octs, port ); // smash port onto the addr
l->addrs[l->naddrs] = strdup( wbuf );
l->naddrs++;
return rc;
}
+/*
+ Given a list manager block, return the default IP address.
+ For now, that is just the first address on the list which
+ easily could be non-deterministic and change with each restart
+ of the application if a specific interface is not provided via
+ the environment variable (ENV_BIND_IF) and if there is more than
+ one device available on the container/physical host.
+*/
+static char* get_default_ip( if_addrs_t* iplist ) {
+
+ if( iplist != NULL && iplist->naddrs > 0 && iplist->addrs != NULL ) {
+ return strdup( iplist->addrs[0] );
+ }
+
+ return NULL;
+}
+
#endif
*/
struct uta_ctx {
char* my_name; // dns name of this host to set in sender field of a message
+ char* my_ip; // default IP address
int shutdown; // threads should exit if this is set
int max_mlen; // max message length payload+header
int max_plen; // max payload length
The caller must check for this and handle.
*/
extern rmr_mbuf_t* rmr_rts_msg( void* vctx, rmr_mbuf_t* msg ) {
- int nn_sock; // endpoint socket for send
+ int nn_sock = -1; // endpoint socket for send
uta_ctx_t* ctx;
int state;
uta_mhdr_t* hdr;
char* hold_src; // we need the original source if send fails
+ char* hold_ip;
if( (ctx = (uta_ctx_t *) vctx) == NULL || msg == NULL ) { // bad stuff, bail fast
errno = EINVAL; // if msg is null, this is their clue
return msg;
}
- nn_sock = uta_epsock_byname( ctx->rtable, (char *) ((uta_mhdr_t *)msg->header)->src ); // socket of specific endpoint
- if( nn_sock < 0 ) {
- msg->state = RMR_ERR_NOENDPT;
- return msg; // preallocated msg can be reused since not given back to nn
+ if( HDR_VERSION( msg->header ) > 2 ) { // new version uses sender's ip address for rts
+ nn_sock = uta_epsock_byname( ctx->rtable, (char *) ((uta_mhdr_t *)msg->header)->srcip ); // socket of specific endpoint
+ }
+ if( nn_sock < 0 ) {
+ nn_sock = uta_epsock_byname( ctx->rtable, (char *) ((uta_mhdr_t *)msg->header)->src ); // socket of specific endpoint
+ if( nn_sock < 0 ) {
+ msg->state = RMR_ERR_NOENDPT;
+ return msg; // preallocated msg can be reused since not given back to nn
+ }
}
hold_src = strdup( (char *) ((uta_mhdr_t *)msg->header)->src ); // the dest where we're returning the message to
- strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SID ); // must overlay the source to be ours
+ hold_ip = strdup( (char *) ((uta_mhdr_t *)msg->header)->srcip );
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SRC ); // must overlay the source to be ours
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->srcip, ctx->my_ip, RMR_MAX_SRC );
+
msg = send_msg( ctx, msg, nn_sock );
if( msg ) {
- strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, hold_src, RMR_MAX_SID ); // always return original source so rts can be called again
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, hold_src, RMR_MAX_SRC ); // always return original source so rts can be called again
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->srcip, hold_ip, RMR_MAX_SRC );
msg->flags |= MFL_ADDSRC; // if msg given to send() it must add source
}
free( hold_src );
+ free( hold_ip );
return msg;
}
if( (tok = strchr( wbuf, '.' )) != NULL ) {
*tok = 0; // we don't keep domain portion
}
- ctx->my_name = (char *) malloc( sizeof( char ) * RMR_MAX_SID );
- if( snprintf( ctx->my_name, RMR_MAX_SID, "%s:%s", wbuf, port ) >= RMR_MAX_SID ) { // our registered name is host:port
- fprintf( stderr, "[CRIT] rmr_init: hostname + port must be less than %d characters; %s:%s is not\n", RMR_MAX_SID, wbuf, port );
+ ctx->my_name = (char *) malloc( sizeof( char ) * RMR_MAX_SRC );
+ if( snprintf( ctx->my_name, RMR_MAX_SRC, "%s:%s", wbuf, port ) >= RMR_MAX_SRC ) { // our registered name is host:port
+ fprintf( stderr, "[CRIT] rmr_init: hostname + port must be less than %d characters; %s:%s is not\n", RMR_MAX_SRC, wbuf, port );
return NULL;
}
return NULL;
}
+ if( (tok = getenv( ENV_NAME_ONLY )) != NULL ) {
+ if( atoi( tok ) > 0 ) {
+ flags |= RMRFL_NAME_ONLY; // don't allow IP addreess to go out in messages
+ }
+ }
+
+ if( flags & RMRFL_NAME_ONLY ) {
+ ctx->my_ip = strdup( ctx->my_name ); // user application or env var has specified that IP address is NOT sent out, use name
+ if( DEBUG ) fprintf( stderr, "[DBUG] name only mode is set; not sending IP address as source\n" );
+ } else {
+ ctx->ip_list = mk_ip_list( port ); // suss out all IP addresses we can find on the box, and bang on our port for RT comparisons
+ ctx->my_ip = get_default_ip( ctx->ip_list ); // and (guess) at what should be the default to put into messages as src
+ if( ctx->my_ip == NULL ) {
+ strcpy( ctx->my_ip, ctx->my_name ); // revert to name if we cant suss out ip address
+ fprintf( stderr, "[WARN] rmr_init: default ip address could not be sussed out, using name as source\n" );
+ } else {
+ if( DEBUG ) fprintf( stderr, "[DBUG] default ip address: %s\n", ctx->my_ip );
+ }
+ }
+
if( ! (flags & FL_NOTHREAD) ) { // skip if internal context that does not need rout table thread
if( pthread_create( &ctx->rtc_th, NULL, rtc, (void *) ctx ) ) { // kick the rt collector thread
fprintf( stderr, "[WARN] rmr_init: unable to start route table collector thread: %s", strerror( errno ) );
msg->xaction = ((uta_mhdr_t *)msg->header)->xid; // point at transaction id in header area
msg->state = state; // fill in caller's state (likely the state of the last operation)
msg->flags |= MFL_ZEROCOPY; // this is a zerocopy sendable message
- strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SID );
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SRC );
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->srcip, ctx->my_ip, RMR_MAX_SRC );
if( DEBUG > 1 ) fprintf( stderr, "[DBUG] alloc_zcmsg mlen = %d size=%d mpl=%d flags=%02x %p m=%p @%p\n", mlen, size, ctx->max_plen, msg->flags, &msg->flags, msg, msg->header );
hdr->plen = htonl( msg->len );
if( msg->flags & MFL_ADDSRC ) { // buffer was allocated as a receive buffer; must add our source
- strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SID ); // must overlay the source to be ours
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SRC ); // must overlay the source to be ours
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->srcip, ctx->my_ip, RMR_MAX_SRC );
}
tr_len = RMR_TR_LEN( hdr );
*/
struct uta_ctx {
char* my_name; // dns name of this host to set in sender field of a message
+ char* my_ip; // the ip address we _think_ we are using sent in src_ip of the message for rts
int shutdown; // thread notification if we need to tell them to stop
int max_mlen; // max message length payload+header
int max_plen; // max payload length
uta_ctx_t* ctx;
int state;
char* hold_src; // we need the original source if send fails
- int sock_ok; // true if we found a valid endpoint socket
+ int sock_ok = 0; // true if we found a valid endpoint socket
if( (ctx = (uta_ctx_t *) vctx) == NULL || msg == NULL ) { // bad stuff, bail fast
errno = EINVAL; // if msg is null, this is their clue
}
((uta_mhdr_t *) msg->header)->flags &= ~HFL_CALL_MSG; // must ensure call flag is off
- sock_ok = uta_epsock_byname( ctx->rtable, (char *) ((uta_mhdr_t *)msg->header)->src, &nn_sock ); // socket of specific endpoint
+ if( HDR_VERSION( msg->header ) > 2 ) { // new version uses sender's ip address for rts
+ sock_ok = uta_epsock_byname( ctx->rtable, (char *) ((uta_mhdr_t *)msg->header)->srcip, &nn_sock ); // default to IP based rts
+ }
if( ! sock_ok ) {
- msg->state = RMR_ERR_NOENDPT;
- return msg; // preallocated msg can be reused since not given back to nn
+ sock_ok = uta_epsock_byname( ctx->rtable, (char *) ((uta_mhdr_t *)msg->header)->src, &nn_sock ); // IP not in rt, try name
+ if( ! sock_ok ) {
+ msg->state = RMR_ERR_NOENDPT;
+ return msg; // preallocated msg can be reused since not given back to nn
+ }
}
msg->state = RMR_OK; // ensure it is clear before send
hold_src = strdup( (char *) ((uta_mhdr_t *)msg->header)->src ); // the dest where we're returning the message to
- strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SID ); // must overlay the source to be ours
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SRC ); // must overlay the source to be ours
msg = send_msg( ctx, msg, nn_sock, -1 );
if( msg ) {
- strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, hold_src, RMR_MAX_SID ); // always return original source so rts can be called again
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, hold_src, RMR_MAX_SRC ); // always return original source so rts can be called again
msg->flags |= MFL_ADDSRC; // if msg given to send() it must add source
}
if( (tok = strchr( wbuf, '.' )) != NULL ) {
*tok = 0; // we don't keep domain portion
}
- ctx->my_name = (char *) malloc( sizeof( char ) * RMR_MAX_SID );
- if( snprintf( ctx->my_name, RMR_MAX_SID, "%s:%s", wbuf, port ) >= RMR_MAX_SID ) { // our registered name is host:port
- fprintf( stderr, "[CRI] rmr_init: hostname + port must be less than %d characters; %s:%s is not\n", RMR_MAX_SID, wbuf, port );
+ ctx->my_name = (char *) malloc( sizeof( char ) * RMR_MAX_SRC );
+ if( snprintf( ctx->my_name, RMR_MAX_SRC, "%s:%s", wbuf, port ) >= RMR_MAX_SRC ) { // our registered name is host:port
+ fprintf( stderr, "[CRI] rmr_init: hostname + port must be less than %d characters; %s:%s is not\n", RMR_MAX_SRC, wbuf, port );
return NULL;
}
- ctx->ip_list = mk_ip_list( port ); // suss out all IP addresses we can find on the box, and bang on our port for RT comparisons
+ if( (tok = getenv( ENV_NAME_ONLY )) != NULL ) {
+ if( atoi( tok ) > 0 ) {
+ flags |= RMRFL_NAME_ONLY; // don't allow IP addreess to go out in messages
+ }
+ }
+
+ ctx->ip_list = mk_ip_list( port ); // suss out all IP addresses we can find on the box, and bang on our port for RT comparisons
+ if( flags & RMRFL_NAME_ONLY ) {
+ ctx->my_ip = strdup( ctx->my_name ); // user application or env var has specified that IP address is NOT sent out, use name
+ } else {
+ ctx->my_ip = get_default_ip( ctx->ip_list ); // and (guess) at what should be the default to put into messages as src
+ if( ctx->my_ip == NULL ) {
+ fprintf( stderr, "[WARN] rmr_init: default ip address could not be sussed out, using name\n" );
+ strcpy( ctx->my_ip, ctx->my_name ); // if we cannot suss it out, use the name rather than a nil pointer
+ }
+ }
+ if( DEBUG ) fprintf( stderr, "[DBUG] default ip address: %s\n", ctx->my_ip );
msg->xaction = ((uta_mhdr_t *)msg->header)->xid; // point at transaction id in header area
msg->state = state; // fill in caller's state (likely the state of the last operation)
msg->flags |= MFL_ZEROCOPY; // this is a zerocopy sendable message
- strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SID );
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SRC );
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->srcip, ctx->my_ip, RMR_MAX_SRC );
if( DEBUG > 1 ) fprintf( stderr, "[DBUG] alloc_zcmsg mlen=%ld size=%d mpl=%d flags=%02x\n", (long) mlen, size, ctx->max_plen, msg->flags );
is somewhat based on header version.
*/
static void ref_tpbuf( rmr_mbuf_t* msg, size_t alen ) {
- uta_mhdr_t* hdr; // current header
+ uta_mhdr_t* hdr = NULL; // current header
uta_v1mhdr_t* v1hdr; // version 1 header
int ver;
int hlen; // header len to use for a truncation check
break;
}
- if( msg->len > (msg->alloc_len - hlen ) ) { // more than we should have had room for; error
+ if( msg->len > (msg->alloc_len - hlen ) ) {
msg->state = RMR_ERR_TRUNC;
msg->len = msg->alloc_len - hlen; // adjust len down so user app doesn't overrun
} else {
tr_len = RMR_TR_LEN( hdr ); // snarf trace len before sending as hdr is invalid after send
if( msg->flags & MFL_ADDSRC ) { // buffer was allocated as a receive buffer; must add our source
- strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SID ); // must overlay the source to be ours
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SRC ); // must overlay the source to be ours
+ strncpy( (char *) ((uta_mhdr_t *)msg->header)->srcip, ctx->my_ip, RMR_MAX_SRC );
}
errno = 0;
-# run all of the tests, building rmr before the first one.
+# run all of the tests, building rmr before the first one if -B is on the command line.
+
+build=""
+
+while [[ $1 == "-"* ]]
+do
+ case $1 in
+ -B) build="-B";;
+
+ *) echo "'$1' is not a recognised option and is ignored";;
+ esac
+
+ shift
+done
+
set -e
-ksh run_app_test.ksh -B
+ksh run_app_test.ksh $build
ksh run_multi_test.ksh
ksh run_rr_test.ksh
ksh run_rts_test.ksh -s 20
errors += fail_not_nil( buf, "rmr_get_src returned a pointer when given a nil dest buffer" );
buf = rmr_get_src( mbuf, src_buf );
- errors += fail_not_equal( (int) buf, (int) src_buf, "rmr_get_src didn't return expexted buffer pointer" );
+ errors += fail_not_equalp( buf, src_buf, "rmr_get_src didn't return expexted buffer pointer" );
+ buf = rmr_get_srcip( NULL, NULL );
+ errors += fail_not_nil( buf, "get_srcip did not return nil when given nil pointers" );
+
+ buf = rmr_get_srcip( mbuf, NULL );
+ errors += fail_not_nil( buf, "get_srcip did not return nil when given nil destination" );
+
+ buf = rmr_get_srcip( mbuf, src_buf );
+ errors += fail_not_equalp( buf, src_buf, "rmr_get_srcip didn't return expexted buffer pointer" );
+
+ test_set_ver( mbuf, 2 ); // set older message version to ensure properly handled
+ buf = rmr_get_srcip( mbuf, src_buf );
return errors > 0; // overall exit code bad if errors
}
#include "mbuf_api.c"
+#define MSG_VER 3
#include "test_support.c" // our private library of test tools
#include "mbuf_api_static_test.c" // test functions
v = rmr_ready( NULL );
errors += fail_if( v != 0, "rmr_ready returned true before initialisation " );
+ em_set_long_hostname( 1 );
if( (rmc = rmr_init( "4560", 1024, FL_NOTHREAD )) == NULL ) {
fail_if_nil( rmc, "rmr_init returned a nil pointer " );
return 1;
msg = rmr_alloc_msg( NULL, 1024 ); // should return nil pointer
errors += fail_not_nil( msg, "rmr_alloc_msg didn't return nil when given nil context " );
+
msg = rmr_alloc_msg( rmc, 2048 ); // allocate larger than default size given on init
errors += fail_if_nil( msg, "rmr_alloc_msg returned nil msg pointer " );
+ if( msg ) {
+ rmr_get_srcip( msg, wbuf );
+ errors += fail_if_equal( 0, strlen( wbuf ), "rmr_get_srcip did not did not return string with length (b) after alloc_msg" );
+ fprintf( stderr, "<INFO> ip: %s\n", wbuf );
+ }
+
v = rmr_payload_size( NULL );
errors += fail_if( v >= 0, "rmr_payload_size returned valid size for nil message " );
if( msg2->state != RMR_ERR_EMPTY ) {
errors += fail_not_equal( msg2->state, RMR_OK, "receive given nil message did not return msg with good state (not empty) " );
}
- //errors += fail_not_equal( msg2->state, RMR_OK, "receive given nil message did not return msg with good state " );
}
msg->state = 0;
msg = rmr_rts_msg( NULL, msg ); // should set state in msg
- errors += fail_if_equal( msg->state, 0, "rmr_rts_msg did not set state when given valid message but no context " );
+ if( msg ) {
+ errors += fail_if_equal( msg->state, 0, "rmr_rts_msg did not set state when given valid message but no context " );
+ } else {
+ errors += fail_if_nil( msg, "rmr_rts_msg returned a nil msg when given a good one" );
+ }
msg = rmr_rts_msg( rmc, msg ); // return the buffer to the sender
errors += fail_if_nil( msg, "rmr_rts_msg did not return a message pointer " );
- errors += fail_if( errno != 0, "rmr_rts_msg did not reset errno " );
+ errors += fail_not_equal( msg->state, 0, "rts_msg did not return a good state (a) when expected" );
+ errors += fail_not_equal( errno, 0, "rmr_rts_msg did not reset errno (a) expected (b)" );
fprintf( stderr, "<INFO> starting RMr API tests\n" );
errors += rmr_api_test();
+
+ fprintf( stderr, "<INFO> run RMr API tests with src name only env var set\n" );
+ setenv( "RMR_SRC_NAMEONLY", "1", 1 );
+ errors += rmr_api_test();
fprintf( stderr, "<INFO> error count: %d\n", errors );
fprintf( stderr, "<INFO> starting wormhole tests\n" );
ctx->max_plen = RMR_MAX_RCV_BYTES + sizeof( uta_mhdr_t );
ctx->max_mlen = ctx->max_plen + sizeof( uta_mhdr_t );
ctx->my_name = strdup( "dummy-test" );
+ ctx->my_ip = strdup( "30.4.19.86:1111" );
uta_lookup_rtg( ctx );
gen_rt( ctx ); // forces a static load with some known info since we don't start the rtc()
ctx->max_plen = RMR_MAX_RCV_BYTES + sizeof( uta_mhdr_t );
ctx->max_mlen = ctx->max_plen + sizeof( uta_mhdr_t );
ctx->my_name = strdup( "dummy-test" );
+ ctx->my_ip = strdup( "30.4.19.86:1111" );
uta_lookup_rtg( ctx );
gen_rt( ctx ); // forces a static load with some known info since we don't start the rtc()
static int gates_ok = 0;
static pthread_mutex_t rcv_gate;
+static int em_gen_long_hostname = 0; // if set the emulated hostname generates a longer name (>40 char)
+// ----------- gethostname emulation ---------------------------------------
+#define gethostname em_gethostname
+static int em_gethostname( char* buf, size_t len ) {
+ if( len < 1 ) {
+ errno = EINVAL;
+ return 1;
+ }
+
+ if( em_gen_long_hostname ) {
+ snprintf( buf, len, "hostname-which-is-long-a860430b890219-dfw82" );
+ } else {
+ snprintf( buf, len, "em-hostname" );
+ }
+
+ return 0;
+}
+
+static int em_set_long_hostname( int v ) {
+ em_gen_long_hostname = !!v;
+}
+
// ----------- epoll emulation ---------------------------------------------
-// CAUTION: sys/epoll.h must be included before this define and function will properly compile.
+// CAUTION: sys/epoll.h must be included before these define and function will properly compile.
#define epoll_wait em_wait
+#define epoll_ctl em_ep_ctl
+#define epoll_create em_ep_create
+
/*
Every other call returns 1 ready; alternate calls return 0 ready.
Mostly for testing the timeout receive call. First call should return
return ready;
}
+int em_ep_ctl( int epfd, int op, int fd, struct epoll_event *event ) {
+ return 0;
+}
+
+int em_ep_create( int size ) {
+ return 0;
+}
+
/*
we don't add a payload even if setting a v1 type.
*/
#define ALT_MSG_VER 1 // alternate every so often
-#define MSG_VER 2 // default version to insert
+#define MSG_VER 3 // default version to insert
struct em_msg {
int32_t mtype; // message type ("long" network integer)
int32_t plen; // payload length
int32_t len2; // length of data 1 (d1)
int32_t len3; // length of data 2 (d2)
int32_t sub_id; // subscription id (-1 invalid)
+
+ // V3 stuff
+ unsigned char srcip[64]; // sender ID for return to sender needs
};
d1_size = 4;
}
- b = (void *) malloc( 2048 );
if( m != NULL ) {
+ b = (void *) malloc( 2048 );
memset( b, 0, 2048 );
*m = (nng_msg *) b;
}
pthread_mutex_unlock( &rcv_gate );
snprintf( msg->xid, 32, "%015d", rcv_count ); // simple transaction id so we can test receive specific and ring stuff
- snprintf( msg->src, 16, "localhost:4562" ); // set src id (unrealistic) so that rts() can be tested
+ snprintf( msg->src, 64, "localhost:4562" ); // set src id (unrealistic) so that rts() can be tested
+ snprintf( msg->srcip, 64, "89.2.19.19:4562" ); // set src ip for rts testing
+
+ //fprintf( stderr, ">>> simulated received message: %s %s p=%p len0=%d\n", msg->src, msg->srcip, msg, (int) ntohl( msg->len0 ) );
+ } else {
+ fprintf( stderr, "<WARN> em: simulated receive no msg pointer provided\n" );
}
- //fprintf( stderr, ">>> simulated received message: %s\n", msg->xid );
return return_value;
}
static int count = 0; // we'll simulate a message going in by dropping an rmr-ish msg with transaction id only
int trace_size = 0;
static int counter = 0; // if timeout value is set; we return timeout (eagain) every 3 calls
+ int d1_size = 0;
if( em_timeout > 0 ) {
counter++;
}
}
+ if( em_mtc_msgs ) {
+ d1_size = 4;
+ }
+
b = (void *) malloc( 2048 );
if( m != NULL ) { // blindly we assume this is 2k or bigger
memset( m, 0, 2048 );
msg->plen = htonl( 220 );
msg->len0 = htonl( sizeof( struct em_msg ) );
msg->len1 = htonl( trace_size );
+ msg->len2 = htonl( d1_size );
+ msg->len3 = htonl( 0 );
snprintf( msg->xid, 32, "%015d", count++ ); // simple transaction id so we can test receive specific and ring stuff
- snprintf( msg->src, 16, "localhost:4562" ); // set src id (unrealistic) so that rts() can be tested
+ snprintf( msg->src, 64, "localhost:4562" ); // set src id (unrealistic) so that rts() can be tested
+ snprintf( msg->srcip, 64, "89.2.19.19:4562" ); // set src ip for rts testing
//fprintf( stderr, "<EM> returning message len=%d\n\n", ntohl( msg->plen ) );
} else {
fprintf( stderr, "<EM> message was nil\n\n" );
}
- //fprintf( stderr, ">>> simulated received message: %s len=%d\n", msg->xid, msg->plen );
+ //fprintf( stderr, ">>> simulated received message: %s %s len=%d p=%p\n", msg->src, msg->srcip, ntohl( msg->plen ), m );
return 2048;
}
return a != b ? GOOD : BAD; // user may override good/bad so do NOT return a==b directly!
}
+static int fail_not_equalp( void* a, void* b, char* what ) {
+ if( a != b ) {
+ fprintf( stderr, "<FAIL> %s: pointers were not equal a=%p b=%p\n", what, a, b );
+ }
+ return a == b ? GOOD : BAD; // user may override good/bad so do NOT return a==b directly!
+}
+
+static int fail_if_equalp( void* a, void* b, char* what ) {
+ if( a == b ) {
+ fprintf( stderr, "<FAIL> %s pointers were equal a=%p b=%p\n", what, a, b );
+ }
+ return a != b ? GOOD : BAD; // user may override good/bad so do NOT return a==b directly!
+}
+
#ifndef NO_DUMMY_RMR
/*
Dummy message allocator for testing without sr_static functions
*/
+#ifndef MSG_VER
+#define MSG_VER 3
+#endif
+
static rmr_mbuf_t* test_mk_msg( int len, int tr_len ) {
rmr_mbuf_t* new_msg;
uta_mhdr_t* hdr;
hdr = (uta_mhdr_t*) new_msg->tp_buf;
SET_HDR_LEN( hdr );
SET_HDR_TR_LEN( hdr, tr_len );
+ hdr->rmr_ver = htonl( MSG_VER );
+ strcpy( hdr->src, "dummyhost:1111" );
+ strcpy( hdr->srcip, "30.4.19.86:1111" );
new_msg->header = new_msg->tp_buf;
new_msg->payload = new_msg->header + PAYLOAD_OFFSET( hdr );
return new_msg;
}
+
+static void test_set_ver( rmr_mbuf_t* msg, int ver ) {
+ uta_mhdr_t* hdr;
+
+ hdr = (uta_mhdr_t*) msg->tp_buf;
+ hdr->rmr_ver = htonl( ver );
+ strcpy( hdr->src, "dummyhost-v2:1111" );
+ strcpy( hdr->srcip, "30.4.19.86:2222" );
+
+ return;
+}
#endif
+
#endif
Date: 3 April 2019
*/
+/*
+ Returns an interface name that is valid in this environment (keeps us from
+ having to know/guess a name to test with.
+*/
+static char* get_ifname( ) {
+ if_addrs_t* l;
+ struct ifaddrs *ifs; // pointer to head
+ struct ifaddrs *ele; // pointer into the list
+ char* rstr = NULL; // return string
+
+
+ if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) {
+ return NULL;
+ }
+ memset( l, 0, sizeof( if_addrs_t ) );
+ l->addrs = (char **) malloc( sizeof( char* ) * 128 );
+ if( l->addrs == NULL ) {
+ free( l );
+ return NULL;
+ }
+
+ getifaddrs( &ifs );
+ for( ele = ifs; ele; ele = ele->ifa_next ) {
+ if( ele && strcmp( ele->ifa_name, "lo" ) ) {
+ rstr = strdup( ele->ifa_name );
+ break;
+ }
+ }
+
+ free( l );
+ return rstr;
+}
static int tools_test( ) {
int i;
char* buf = "2,Fred,Wilma,Barney,Betty,Dino,Pebbles,Bambam,Mr. Slate,Gazoo";
char* dbuf; // duplicated buf since C marks a const string is unumtable
char* hname;
+ char* ip; // ip address string
uta_ctx_t ctx; // context for uta_lookup test
void* if_list;
i = has_myip( "192.168.4.30:1235", if_list, ',', 128 ); // should find our ip when only in list
errors += fail_if_false( i, "has_myip did not find IP when only one in list" );
+ ip = get_default_ip( NULL );
+ errors += fail_not_nil( ip, "get_default_ip returned non-nil pointer when given nil information" );
+
+ ip = get_default_ip( if_list );
+ if( ip ) {
+ } else {
+ errors += fail_if_nil( ip, "get_defaul_ip returned nil pointer when valid pointer expected" );
+ }
+
+ ip = get_ifname(); // suss out a valid interface name (not lo)
+ if( ip ) {
+ setenv( "RMR_BIND_IF", ip, 1 ); // drive the case where we have a hard set interface; and set known interface in list
+ free( ip );
+ if_list = mk_ip_list( "1235" );
+ if( if_list ) {
+ ip = get_default_ip( if_list );
+ errors += fail_if_nil( ip, "get_default_ip did not return valid pointer when list created from interface name" );
+ } else {
+ errors += fail_if_nil( if_list, "mk_ip_list with a specific interface name returned a nil list" );
+ }
+ }
+
+// -------------------------------------------------------------------------------------------------
+
return !!errors; // 1 or 0 regardless of count
}
#include "tools_static.c"
+#include "tools_static_test.c"
int main( ) {
- int i;
- int j;
- int errors = 0;
- char* tokens[127];
- char* buf = "2,Fred,Wilma,Barney,Betty,Dino,Pebbles,Bambam,Mr. Slate,Gazoo";
- char* dbuf; // duplicated buf since C marks a const string is unumtable
- char* hname;
- uta_ctx_t ctx; // context for uta_lookup test
- void* if_list;
-
-
- // ------------------ tokenise tests -----------------------------------------------------------
- dbuf = strdup( buf );
- i = uta_tokenise( dbuf, tokens, 127, ',' );
- errors += fail_not_equal( i, 10, "unexpected number of tokens returned (comma sep)" );
- for( j = 0; j < i; j++ ) {
- //fprintf( stderr, ">>>> [%d] (%s)\n", j, tokens[j] );
- errors += fail_if_nil( tokens[j], "token from buffer" );
- }
- errors += fail_not_equal( strcmp( tokens[4], "Betty" ), 0, "4th token wasn't 'Betty'" );
-
- free( dbuf );
- dbuf = strdup( buf );
- i = uta_tokenise( dbuf, tokens, 127, '|' );
- errors += fail_not_equal( i, 1, "unexpected number of tokens returned (bar sep)" );
- free( dbuf );
-
- // ------------ has str tests -----------------------------------------------------------------
- j = uta_has_str( buf, "Mr. Slate", ',', 1 ); // should fail (-1) because user should use strcmp in this situation
- errors += fail_if_true( j >= 0, "test to ensure has str rejects small max" );
-
- j = uta_has_str( buf, "Mr. Slate", ',', 27 );
- errors += fail_if_true( j < 0, "has string did not find Mr. Slate" );
-
- j = uta_has_str( buf, "Mrs. Slate", ',', 27 );
- errors += fail_if_true( j >= 0, "has string not found Mrs. Slate" );
-
- // ------------ host name 2 ip tests ---------------------------------------------------------
- hname = uta_h2ip( "192.168.1.2" );
- errors += fail_not_equal( strcmp( hname, "192.168.1.2" ), 0, "h2ip did not return IP address when given address" );
- errors += fail_if_nil( hname, "h2ip did not return a pointer" );
-
- hname = uta_h2ip( "yahoo.com" );
- errors += fail_if_nil( hname, "h2ip did not return a pointer" );
-
- hname = uta_h2ip( "yahoo.com:1234" ); // should ignore the port
- errors += fail_if_nil( hname, "h2ip did not return a pointer" );
-
- // ------------ rtg lookup test -------------------------------------------------------------
- ctx.rtg_port = 0;
- ctx.rtg_addr = NULL;
-
- i = uta_lookup_rtg( NULL ); // ensure it handles a nil context
- errors += fail_if_true( i, "rtg lookup returned that it found something when not expected to (nil context)" );
-
- setenv( "RMR_RTG_SVC", "localhost:1234", 1);
- i = uta_lookup_rtg( &ctx );
- errors += fail_if_false( i, "rtg lookup returned that it did not find something when expected to" );
- errors += fail_if_nil( ctx.rtg_addr, "rtg lookup did not return a pointer (with port)" );
- errors += fail_not_equal( ctx.rtg_port, 1234, "rtg lookup did not capture the port" );
-
- setenv( "RMR_RTG_SVC", "localhost", 1); // test ability to generate default port
- uta_lookup_rtg( &ctx );
- errors += fail_if_nil( ctx.rtg_addr, "rtg lookup did not return a pointer (no port)" );
- errors += fail_not_equal( ctx.rtg_port, 5656, "rtg lookup did not return default port" );
-
- unsetenv( "RMR_RTG_SVC" ); // this should fail as the default name (rtg) will be unknown during testing
- i = uta_lookup_rtg( &ctx );
- errors += fail_if_true( i, "rtg lookup returned that it found something when not expected to" );
-
-
- // ------------ my ip stuff -----------------------------------------------------------------
-
- if_list = mk_ip_list( "1235" );
- errors += fail_if_nil( if_list, "mk_ip_list returned nil pointer" );
-
- i = has_myip( NULL, NULL, ',', 128 ); // should be false if pointers are nil
- errors += fail_if_true( i, "has_myip returned true when given nil buffer" );
-
- i = has_myip( "buffer contents not valid", NULL, ',', 128 ); // should be false if pointers are nil
- errors += fail_if_true( i, "has_myip returned true when given nil list" );
-
- i = has_myip( "buffer contents not valid", NULL, ',', 1 ); // should be false if max < 2
- errors += fail_if_true( i, "has_myip returned true when given small max value" );
-
- i = has_myip( "buffer.contents.not.valid", if_list, ',', 128 ); // should be false as there is nothing valid in the list
- errors += fail_if_true( i, "has_myip returned true when given a buffer with no valid info" );
-
-
- setenv( "RMR_BIND_IF", "192.168.4.30", 1 ); // drive the case where we have a hard set interface; and set known interface in list
- if_list = mk_ip_list( "1235" );
- errors += fail_if_nil( if_list, "mk_ip_list with env set returned nil pointer" );
-
- i = has_myip( "192.168.1.2:1235,192.168.4.30:1235,192.168.2.19:4567", if_list, ',', 128 ); // should find our ip in middle
- errors += fail_if_false( i, "has_myip did not find IP in middle of list" );
-
- i = has_myip( "192.168.4.30:1235,192.168.2.19:4567,192.168.2.19:2222", if_list, ',', 128 ); // should find our ip at head
- errors += fail_if_false( i, "has_myip did not find IP at head of list" );
-
- i = has_myip( "192.168.23.45:4444,192.168.1.2:1235,192.168.4.30:1235", if_list, ',', 128 ); // should find our ip at end
- errors += fail_if_false( i, "has_myip did not find IP at tail of list" );
-
- i = has_myip( "192.168.4.30:1235", if_list, ',', 128 ); // should find our ip when only in list
- errors += fail_if_false( i, "has_myip did not find IP when only one in list" );
-
- return errors > 0; // overall exit code bad if errors
+ fprintf( stderr, ">>>> starting tools_test\n" );
+ return tools_test() > 0;
}
+
}
memset( ctx, 0, sizeof( *ctx ) );
ctx->my_name = strdup( "tester" );
+ ctx->my_ip = strdup( "30.4.19.86:1111" );
gen_rt( ctx );