3 ==================================================================================
4 Copyright (c) 2019-2020 Nokia
5 Copyright (c) 2018-2020 AT&T Intellectual Property.
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
11 http://www.apache.org/licenses/LICENSE-2.0
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 ==================================================================================
22 Mnemonic: tools_static.c
23 Abstract: A small set of very simple tools to support Uta == RMR.
24 uta_tokenise -- simple string tokeniser
25 uta_rmip_tokenise -- tokenise and remove ip addresses from the list
26 uta_h2ip -- look up host name and return an ip address
27 uta_lookup_rtg -- looks in env for rtg host:port
28 uta_has_str -- searches buffer of tokens for a string
30 uta_link2 -- establish a nanomsg connection to a host
32 uta_dump_env -- dump the environment variables to stdout that are
35 Author: E. Scott Daniels
36 Date: 30 November 2018
39 #ifndef _tools_static_c
40 #define _tools_static_c
51 #include <sys/types.h> // these are needed to suss out ip addresses from interfaces
53 #include <arpa/inet.h>
54 #include <sys/socket.h>
57 // --- some protos needed for better organisation --------
58 static int is_this_myip( if_addrs_t* l, char* addr );
61 // ----------------------------------------------------------------------------------
65 A strncpy() replacement that ensures the resulting dest buffer has
66 a zero (nil) terminator even if the source is longer than the dest
68 A max of len-1 bytes are copied from src to dest. The copy stops when
69 a zero (nil) is encountered in the src, or len-1 bytes are copied.
70 The string is always nil terminated.
71 The string length is returned.
73 It is the responsiblity of the caller to ensure that dest is at
74 least len bytes in length.
76 If either src/dest is invalid (nil) a value of -1 is returned.
78 static inline int zt_buf_fill( char* dest, char const* src, int len ) {
83 if( dest == NULL || src == NULL ) {
90 while( *sp && n < len-1 ) {
100 Simple tokeniser. Split a null terminated string into tokens recording the
101 pointers in the tokens array provided. Tokens MUST be large enough. Max is
102 the max number of tokens to split into. Returns the actual number of tokens
103 recorded in the pointer array.
105 CAUTION: this modifies the string passed in!!
107 static int uta_tokenise( char* buf, char** tokens, int max, char sep ) {
108 char* end; // end of token
111 if( !buf || ! tokens || !(*buf) ) {
117 while( n < max && *end && (end = strchr( end, sep )) != NULL ) {
126 Tokenise and remove matches.
127 Given a buffer of 'sep' separated tokens, and a list of things,
128 return up to max tokens with any tokens that matched things in
129 the list. Toks is the user supplied array of char* which we will
130 fill in (up to max) with pointers to tokens from buf. This
131 damages buf, so the caller must dup the string if it must be
132 preserved for later, original, use. The pointers returned in
133 toks will reference portions of bufs.
135 Returns the number of tokens referenced by toks.
137 static int uta_rmip_tokenise( char* buf, if_addrs_t* iplist, char** toks, int max, char sep ) {
138 int ntoks = 0; // total toks in the original buffer
139 int pcount = 0; // count after prune
145 all_toks = malloc( sizeof( char * ) * max ); // refernce to all tokens; we'll prune
146 pcount = ntoks = uta_tokenise( buf, all_toks, max, sep ); // split them up
149 for( i = 0; i < ntoks; i++ ) {
150 if( is_this_myip( iplist, all_toks[i] ) ) {
151 pcount--; // ours, prune
153 toks[j++] = all_toks[i]; // not one of ours, keep it
163 Xlate hostname (expected to be name:port) to an IP address that nano will tolerate.
164 We'll use the first address from the list to keep it simple. If the first character
165 of the name is a digit, we assume it's really an IP address and just return that.
167 Return is a string which the caller must free. Even if the string passed in is already
168 an IP address, a duplicate will be returend so that it can always be freed.
169 On error a nil pointer is returned.
171 static char* uta_h2ip( char const* hname ) {
173 struct hostent* hent;
174 unsigned int octs[4];
178 char* dname; // duplicated name for distruction
180 dname = strdup( hname );
182 if( isdigit( *dname ) || *dname == '[' ) { // hostnames can't start with digit, or ipv6 [; assume ip address
186 if( (tok = strchr( dname, ':' )) != NULL ) {
190 hent = gethostbyname( dname ); // valgrind will complain that this leaks, but we cannot free it!
191 if( hent == NULL || hent->h_addr_list == NULL ) {
192 //rmr_vlog( RMR_VL_WARN, "h2ip: dns lookup failed for: %s\n", dname );
197 a = ntohl( *((unsigned int *)hent->h_addr_list[0]) );
198 for( i = 3; i >= 0; i-- ) {
203 if( tok ) { // if :port was given, smash it back on
204 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d:%s", octs[0], octs[1], octs[2], octs[3], tok );
206 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d", octs[0], octs[1], octs[2], octs[3] );
210 return strdup( buf );
216 Looks for the environment variable RMR_RTG_SVC which we assume to be name[:port], and
217 does a dns lookup on the name. If the env does not have such a variable, we default to
218 "rtg" and a port of 5656.
220 Returns true (1) if lookup found something;
222 CAUTION: this is ONLY used if the RTG is a pub and we are using pub/sub to get updates.
223 There are issues with some underlying transport pub/sub implementations so this
224 is likley NOT needed/used.
226 static int uta_lookup_rtg( uta_ctx_t* ctx ) {
227 char* ev; // pointer to the env value
228 char* def_port = "5656";
237 if( ctx->rtg_addr ) {
238 free( ctx->rtg_addr );
241 if( (ev = getenv( "RMR_RTG_SVC" )) == NULL ) {
245 dstr = strdup( ev ); // copy so we can trash it
246 if( (port = strchr( dstr, ':' )) == NULL ) {
250 port++; // point at the first digit
252 ev = dstr; // all references below assume ev
255 ctx->rtg_addr = uta_h2ip( ev ); // convert name to IP addr
256 ctx->rtg_port = atoi( port );
261 return ctx->rtg_addr != NULL;
267 Expects a buffer of 'sep' separated tokens and looks to see if
268 the given string is one of those tokens. Returns the token
269 index (0 - n-1) if the string is found; -1 otherwise. The max
270 parameter supplies the maximum number of tokens to search in
273 On failure (-1) errno will be set in cases where memory cannot
274 be alocated (is this even possible any more?). If errno is 0
275 and failure is returned, then the caller should assume that
276 the token isn't in the list, or the list had no elements.
278 static int uta_has_str( char const* buf, char const* str, char sep, int max ) {
279 char* dbuf; // duplicated buf so we can trash
280 char** tokens; // pointer to tokens from the string
281 int ntokens; // number of tokens buf split into
283 int rc; // return code
289 dbuf = strdup( buf );
295 if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
301 ntokens = uta_tokenise( dbuf, tokens, max, sep );
304 for( i = 0; rc < 0 && i < ntokens; i++ ) {
306 if( strcmp( tokens[i], str ) == 0 ) {
318 Generate a list of all IP address associated with the interfaces available.
319 For now we capture them all, but we may need to limit. The port is smashed
320 onto each IP we find so that we can do a direct compare against the addr
321 that could be in the route table.
323 If the environment variable which limits the binding of our listen port
324 to a single interface (ENV_BIND_IF) then ONLY that interface/address is added
325 to the list so that we don't pick up entries from the rtable that are for other
326 processes listening on different interfaces.
328 The ENV_BIN_IF environment variable may be either an IP address (v6 must be in
329 square braces), or an interface name (e.g. eth0).
331 static if_addrs_t* mk_ip_list( char* port ) {
333 struct ifaddrs *ifs; // pointer to head
334 struct ifaddrs *ele; // pointer into the list
335 char octs[NI_MAXHOST+1];
336 char wbuf[NI_MAXHOST+128];
337 char* fmt = NULL; // address format (v4 or v6)
338 char* envp; // at the environment var if there
339 char* target_if = NULL; // target interface supplied by ENV_BIND_IF
343 if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) {
346 memset( l, 0, sizeof( if_addrs_t ) );
347 l->addrs = (char **) malloc( sizeof( char* ) * 128 );
348 if( l->addrs == NULL ) {
353 if( (envp = getenv( ENV_BIND_IF )) != NULL ) {
354 if( isdigit( *envp ) || *envp == '[' ) { // ip address given and not device name
355 snprintf( wbuf, sizeof( wbuf ), "%s:%s", envp, port ); // smash port onto the addr as is
356 l->addrs[l->naddrs] = strdup( wbuf );
358 if( DEBUG ) rmr_vlog( RMR_VL_INFO, "rmr: using only specific bind interface when searching specific RT entries: %s\n", wbuf );
362 target_if = envp; // device name given, suss it out below
366 for( ele = ifs; ele; ele = ele->ifa_next ) {
367 memset( octs, 0, sizeof( octs ) );
368 if( ele && strcmp( ele->ifa_name, "lo" ) && // do NOT capture the loopback interface address
369 (target_if == NULL || strcmp( ele->ifa_name, target_if ) == 0 ) ) { // no target, or matches ENV_BIND_IF target
371 if( ele->ifa_addr != NULL ) { // possible for some interfaces to not have an address
372 if( ele->ifa_addr->sa_family == AF_INET ) {
373 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
376 if( ele->ifa_addr->sa_family == AF_INET6 ) {
377 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in6 ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
382 if( *octs && fmt != NULL ) { // possible that we didn't recognise the format (v4 or v6), don't try if we didn't
383 if( (tok = strchr( octs, '%' )) != NULL ) { // for unknown reasons some ip6 addrs have %if-name appended; truncate
386 if( l->naddrs < 128 ) {
387 if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "capture address: %s: %s\n", ele->ifa_name, octs );
389 snprintf( wbuf, sizeof( wbuf ), fmt, octs, port ); // smash port onto the addr
390 l->addrs[l->naddrs] = strdup( wbuf );
406 Check the address:port passed in and return true if it matches
407 one of the addresses we saw when we built the list. Right now
408 this isn't a speed intensive part of our processing, so we just
409 do a straight search through the list. We don't expect this to
410 ever be a higly driven functions so not bothering to optimise.
412 static int is_this_myip( if_addrs_t* l, char* addr ) {
423 for( i = 0; i < l->naddrs; i++ ) {
424 if( l->addrs[i] != NULL && strcmp( addr, l->addrs[i] ) == 0 ) {
433 Expects a buffer containing "sep" separated tokens, and a list of
434 IP addresses anchored by ip_list. Searches the tokens to see if
435 any are an ip address:port which is in the ip list. Returns true
436 (1) if a token is in the list, false otherwise.
438 static int has_myip( char const* buf, if_addrs_t* list, char sep, int max ) {
439 char* dbuf; // duplicated buf so we can trash
440 char** tokens; // pointer to tokens from the string
441 int ntokens; // number of tokens buf split into
443 int rc = 0; // return code
458 dbuf = strdup( buf ); // get a copy we can mess with
464 if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
470 ntokens = uta_tokenise( dbuf, tokens, max, sep );
473 for( i = 0; ! rc && i < ntokens; i++ ) {
475 if( is_this_myip( list, tokens[i] ) ) {
488 Given a list manager block, return the default IP address.
489 For now, that is just the first address on the list which
490 easily could be non-deterministic and change with each restart
491 of the application if a specific interface is not provided via
492 the environment variable (ENV_BIND_IF) and if there is more than
493 one device available on the container/physical host.
495 static char* get_default_ip( if_addrs_t* iplist ) {
497 if( iplist != NULL && iplist->naddrs > 0 && iplist->addrs != NULL ) {
498 return strdup( iplist->addrs[0] );
505 Write all environment variables that we consider to be important to stderr.
507 static void uta_dump_env( ) {
527 for( i = 0; i < sizeof( elist ) / sizeof( char *); i ++ ) {
528 token = getenv( elist[i] );
529 if( token != NULL ) {
530 rmr_vlog( RMR_VL_INFO, "dump_env: %s = '%s'\n", elist[i], token );
532 rmr_vlog( RMR_VL_INFO, "dump_env: %s = <unset>\n", elist[i] );