3 ==================================================================================
4 Copyright (c) 2019 Nokia
5 Copyright (c) 2018-2019 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 int is_this_myip( if_addrs_t* l, char* addr );
61 // ----------------------------------------------------------------------------------
64 Simple tokeniser. Split a null terminated string into tokens recording the
65 pointers in the tokens array provided. Tokens MUST be large enough. Max is
66 the max number of tokens to split into. Returns the actual number of tokens
67 recorded in the pointer array.
69 CAUTION: this modifies the string passed in!!
71 static int uta_tokenise( char* buf, char** tokens, int max, char sep ) {
72 char* end; // end of token
75 if( !buf || ! tokens || !(*buf) ) {
81 while( n < max && *end && (end = strchr( end, sep )) != NULL ) {
90 Tokenise and remove matches.
91 Given a buffer of 'sep' separated tokens, and a list of things,
92 return up to max tokens with any tokens that matched things in
93 the list. Toks is the user supplied array of char* which we will
94 fill in (up to max) with pointers to tokens from buf. This
95 damages buf, so the caller must dup the string if it must be
96 preserved for later, original, use. The pointers returned in
97 toks will reference portions of bufs.
99 Returns the number of tokens referenced by toks.
101 static int uta_rmip_tokenise( char* buf, if_addrs_t* iplist, char** toks, int max, char sep ) {
102 int ntoks = 0; // total toks in the original buffer
103 int pcount = 0; // count after prune
109 all_toks = malloc( sizeof( char * ) * max ); // refernce to all tokens; we'll prune
110 pcount = ntoks = uta_tokenise( buf, all_toks, max, sep ); // split them up
113 for( i = 0; i < ntoks; i++ ) {
114 if( is_this_myip( iplist, all_toks[i] ) ) {
115 pcount--; // ours, prune
117 toks[j++] = all_toks[i]; // not one of ours, keep it
127 Xlate hostname (expected to be name:port) to an IP address that nano will tolerate.
128 We'll use the first address from the list to keep it simple. If the first character
129 of the name is a digit, we assume it's really an IP address and just return that.
131 Return is a string which the caller must free. Even if the string passed in is already
132 an IP address, a duplicate will be returend so that it can always be freed.
133 On error a nil pointer is returned.
135 static char* uta_h2ip( char const* hname ) {
137 struct hostent* hent;
138 unsigned int octs[4];
142 char* dname; // duplicated name for distruction
144 dname = strdup( hname );
146 if( isdigit( *dname ) || *dname == '[' ) { // hostnames can't start with digit, or ipv6 [; assume ip address
150 if( (tok = strchr( dname, ':' )) != NULL ) {
154 hent = gethostbyname( dname );
155 if( hent == NULL || hent->h_addr_list == NULL ) {
156 //rmr_vlog( RMR_VL_WARN, "h2ip: dns lookup failed for: %s\n", dname );
161 a = ntohl( *((unsigned int *)hent->h_addr_list[0]) );
162 for( i = 3; i >= 0; i-- ) {
167 if( tok ) { // if :port was given, smash it back on
168 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d:%s", octs[0], octs[1], octs[2], octs[3], tok );
170 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d", octs[0], octs[1], octs[2], octs[3] );
174 return strdup( buf );
179 Looks for the environment variable RMR_RTG_SVC which we assume to be name[:port], and
180 does a dns lookup on the name. If the env does not have such a variable, we default to
181 "rtg" and a port of 5656.
183 Returns true (1) if lookup found something;
185 CAUTION: this is ONLY used if the RTG is a pub and we are using pub/sub to get updates.
186 There are issues with some underlying transport pub/sub implementations so this
187 is likley NOT needed/used.
189 static int uta_lookup_rtg( uta_ctx_t* ctx ) {
190 char* ev; // pointer to the env value
191 char* def_port = "5656";
200 if( ctx->rtg_addr ) {
201 free( ctx->rtg_addr );
204 if( (ev = getenv( "RMR_RTG_SVC" )) == NULL ) {
208 dstr = strdup( ev ); // copy so we can trash it
209 if( (port = strchr( dstr, ':' )) == NULL ) {
213 port++; // point at the first digit
215 ev = dstr; // all references below assume ev
218 ctx->rtg_addr = uta_h2ip( ev ); // convert name to IP addr
219 ctx->rtg_port = atoi( port );
224 return ctx->rtg_addr != NULL;
229 Expects a buffer of 'sep' separated tokens and looks to see if
230 the given string is one of those tokens. Returns the token
231 index (0 - n-1) if the string is found; -1 otherwise. The max
232 parameter supplies the maximum number of tokens to search in
235 On failure (-1) errno will be set in cases where memory cannot
236 be alocated (is this even possible any more?). If errno is 0
237 and failure is returned, then the caller should assume that
238 the token isn't in the list, or the list had no elements.
240 static int uta_has_str( char const* buf, char const* str, char sep, int max ) {
241 char* dbuf; // duplicated buf so we can trash
242 char** tokens; // pointer to tokens from the string
243 int ntokens; // number of tokens buf split into
245 int rc; // return code
251 dbuf = strdup( buf );
257 if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
263 ntokens = uta_tokenise( dbuf, tokens, max, sep );
266 for( i = 0; rc < 0 && i < ntokens; i++ ) {
268 if( strcmp( tokens[i], str ) == 0 ) {
280 Generate a list of all IP address associated with the interfaces available.
281 For now we capture them all, but we may need to limit. The port is smashed
282 onto each IP we find so that we can do a direct compare against the addr
283 that could be in the route table.
285 If the environment variable which limits the binding of our listen port
286 to a single interface (ENV_BIND_IF) then ONLY that interface/address is added
287 to the list so that we don't pick up entries from the rtable that are for other
288 processes listening on different interfaces.
290 The ENV_BIN_IF environment variable may be either an IP address (v6 must be in
291 square braces), or an interface name (e.g. eth0).
293 if_addrs_t* mk_ip_list( char* port ) {
295 struct ifaddrs *ifs; // pointer to head
296 struct ifaddrs *ele; // pointer into the list
297 char octs[NI_MAXHOST+1];
298 char wbuf[NI_MAXHOST+128];
300 char* envp; // at the environment var if there
301 char* target_if = NULL; // target interface supplied by ENV_BIND_IF
305 if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) {
308 memset( l, 0, sizeof( if_addrs_t ) );
309 l->addrs = (char **) malloc( sizeof( char* ) * 128 );
310 if( l->addrs == NULL ) {
315 if( (envp = getenv( ENV_BIND_IF )) != NULL ) {
316 if( isdigit( *envp ) || *envp == '[' ) { // ip address given and not device name
317 snprintf( wbuf, sizeof( wbuf ), "%s:%s", envp, port ); // smash port onto the addr as is
318 l->addrs[l->naddrs] = strdup( wbuf );
320 if( DEBUG ) rmr_vlog( RMR_VL_INFO, "rmr: using only specific bind interface when searching specific RT entries: %s\n", wbuf );
324 target_if = envp; // device name given, suss it out below
328 for( ele = ifs; ele; ele = ele->ifa_next ) {
329 memset( octs, 0, sizeof( octs ) );
330 if( ele && strcmp( ele->ifa_name, "lo" ) && // do NOT capture the loopback interface address
331 (target_if == NULL || strcmp( ele->ifa_name, target_if ) == 0 ) ) { // no target, or matches ENV_BIND_IF target
333 if( ele->ifa_addr != NULL ) { // possible for some interfaces to not have an address
334 if( ele->ifa_addr->sa_family == AF_INET ) {
335 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
338 if( ele->ifa_addr->sa_family == AF_INET6 ) {
339 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in6 ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
346 if( (tok = strchr( octs, '%' )) != NULL ) { // for unknown reasons some ip6 addrs have %if-name appended; truncate
349 if( l->naddrs < 128 ) {
350 if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "capture address: %s: %s\n", ele->ifa_name, octs );
352 snprintf( wbuf, sizeof( wbuf ), fmt, octs, port ); // smash port onto the addr
353 l->addrs[l->naddrs] = strdup( wbuf );
368 Check the address:port passed in and return true if it matches
369 one of the addresses we saw when we built the list. Right now
370 this isn't a speed intensive part of our processing, so we just
371 do a straight search through the list. We don't expect this to
372 ever be a higly driven functions so not bothering to optimise.
374 int is_this_myip( if_addrs_t* l, char* addr ) {
385 for( i = 0; i < l->naddrs; i++ ) {
386 if( l->addrs[i] != NULL && strcmp( addr, l->addrs[i] ) == 0 ) {
395 Expects a buffer containing "sep" separated tokens, and a list of
396 IP addresses anchored by ip_list. Searches the tokens to see if
397 any are an ip address:port which is in the ip list. Returns true
398 (1) if a token is in the list, false otherwise.
400 static int has_myip( char const* buf, if_addrs_t* list, char sep, int max ) {
401 char* dbuf; // duplicated buf so we can trash
402 char** tokens; // pointer to tokens from the string
403 int ntokens; // number of tokens buf split into
405 int rc = 0; // return code
420 dbuf = strdup( buf ); // get a copy we can mess with
426 if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
432 ntokens = uta_tokenise( dbuf, tokens, max, sep );
435 for( i = 0; ! rc && i < ntokens; i++ ) {
437 if( is_this_myip( list, tokens[i] ) ) {
450 Given a list manager block, return the default IP address.
451 For now, that is just the first address on the list which
452 easily could be non-deterministic and change with each restart
453 of the application if a specific interface is not provided via
454 the environment variable (ENV_BIND_IF) and if there is more than
455 one device available on the container/physical host.
457 static char* get_default_ip( if_addrs_t* iplist ) {
459 if( iplist != NULL && iplist->naddrs > 0 && iplist->addrs != NULL ) {
460 return strdup( iplist->addrs[0] );
467 Write all environment variables that we consider to be important to stderr.
469 static void uta_dump_env( ) {
489 for( i = 0; i < sizeof( elist ) / sizeof( char *); i ++ ) {
490 token = getenv( elist[i] );
491 if( token != NULL ) {
492 rmr_vlog( RMR_VL_INFO, "dump_env: %s = '%s'\n", elist[i], token );
494 rmr_vlog( RMR_VL_INFO, "dump_env: %s = <unset>\n", elist[i] );