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 Author: E. Scott Daniels
33 Date: 30 November 2018
36 #ifndef _tools_static_c
37 #define _tools_static_c
48 #include <sys/types.h> // these are needed to suss out ip addresses from interfaces
50 #include <arpa/inet.h>
51 #include <sys/socket.h>
54 // --- some protos needed for better organisation --------
55 int is_this_myip( if_addrs_t* l, char* addr );
58 // ----------------------------------------------------------------------------------
61 Simple tokeniser. Split a null terminated string into tokens recording the
62 pointers in the tokens array provided. Tokens MUST be large enough. Max is
63 the max number of tokens to split into. Returns the actual number of tokens
64 recorded in the pointer array.
66 CAUTION: this modifies the string passed in!!
68 static int uta_tokenise( char* buf, char** tokens, int max, char sep ) {
69 char* end; // end of token
72 if( !buf || ! tokens || !(*buf) ) {
78 while( n < max && *end && (end = strchr( end, sep )) != NULL ) {
87 Tokenise and remove matches.
88 Given a buffer of 'sep' separated tokens, and a list of things,
89 return up to max tokens with any tokens that matched things in
90 the list. Toks is the user supplied array of char* which we will
91 fill in (up to max) with pointers to tokens from buf. This
92 damages buf, so the caller must dup the string if it must be
93 preserved for later, original, use. The pointers returned in
94 toks will reference portions of bufs.
96 Returns the number of tokens referenced by toks.
98 static int uta_rmip_tokenise( char* buf, if_addrs_t* iplist, char** toks, int max, char sep ) {
99 int ntoks = 0; // total toks in the original buffer
100 int pcount = 0; // count after prune
106 all_toks = malloc( sizeof( char * ) * max ); // refernce to all tokens; we'll prune
107 pcount = ntoks = uta_tokenise( buf, all_toks, max, sep ); // split them up
110 for( i = 0; i < ntoks; i++ ) {
111 if( is_this_myip( iplist, all_toks[i] ) ) {
112 pcount--; // ours, prune
114 toks[j++] = all_toks[i]; // not one of ours, keep it
124 Xlate hostname (expected to be name:port) to an IP address that nano will tolerate.
125 We'll use the first address from the list to keep it simple. If the first character
126 of the name is a digit, we assume it's really an IP address and just return that.
128 Return is a string which the caller must free. Even if the string passed in is already
129 an IP address, a duplicate will be returend so that it can always be freed.
130 On error a nil pointer is returned.
132 static char* uta_h2ip( char const* hname ) {
134 struct hostent* hent;
135 unsigned int octs[4];
139 char* dname; // duplicated name for distruction
141 dname = strdup( hname );
143 if( isdigit( *dname ) || *dname == '[' ) { // hostnames can't start with digit, or ipv6 [; assume ip address
147 if( (tok = strchr( dname, ':' )) != NULL ) {
151 hent = gethostbyname( dname );
152 if( hent == NULL || hent->h_addr_list == NULL ) {
153 //rmr_vlog( RMR_VL_WARN, "h2ip: dns lookup failed for: %s\n", dname );
158 a = ntohl( *((unsigned int *)hent->h_addr_list[0]) );
159 for( i = 3; i >= 0; i-- ) {
164 if( tok ) { // if :port was given, smash it back on
165 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d:%s", octs[0], octs[1], octs[2], octs[3], tok );
167 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d", octs[0], octs[1], octs[2], octs[3] );
171 return strdup( buf );
176 Looks for the environment variable RMR_RTG_SVC which we assume to be name[:port], and
177 does a dns lookup on the name. If the env does not have such a variable, we default to
178 "rtg" and a port of 5656.
180 Returns true (1) if lookup found something;
182 CAUTION: this is ONLY used if the RTG is a pub and we are using pub/sub to get updates.
183 There are issues with some underlying transport pub/sub implementations so this
184 is likley NOT needed/used.
186 static int uta_lookup_rtg( uta_ctx_t* ctx ) {
187 char* ev; // pointer to the env value
188 char* def_port = "5656";
197 if( ctx->rtg_addr ) {
198 free( ctx->rtg_addr );
201 if( (ev = getenv( "RMR_RTG_SVC" )) == NULL ) {
205 dstr = strdup( ev ); // copy so we can trash it
206 if( (port = strchr( dstr, ':' )) == NULL ) {
210 port++; // point at the first digit
212 ev = dstr; // all references below assume ev
215 ctx->rtg_addr = uta_h2ip( ev ); // convert name to IP addr
216 ctx->rtg_port = atoi( port );
221 return ctx->rtg_addr != NULL;
226 Expects a buffer of 'sep' separated tokens and looks to see if
227 the given string is one of those tokens. Returns the token
228 index (0 - n-1) if the string is found; -1 otherwise. The max
229 parameter supplies the maximum number of tokens to search in
232 On failure (-1) errno will be set in cases where memory cannot
233 be alocated (is this even possible any more?). If errno is 0
234 and failure is returned, then the caller should assume that
235 the token isn't in the list, or the list had no elements.
237 static int uta_has_str( char const* buf, char const* str, char sep, int max ) {
238 char* dbuf; // duplicated buf so we can trash
239 char** tokens; // pointer to tokens from the string
240 int ntokens; // number of tokens buf split into
242 int rc; // return code
248 dbuf = strdup( buf );
254 if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
260 ntokens = uta_tokenise( dbuf, tokens, max, sep );
263 for( i = 0; rc < 0 && i < ntokens; i++ ) {
265 if( strcmp( tokens[i], str ) == 0 ) {
277 Generate a list of all IP address associated with the interfaces available.
278 For now we capture them all, but we may need to limit. The port is smashed
279 onto each IP we find so that we can do a direct compare against the addr
280 that could be in the route table.
282 If the environment variable which limits the binding of our listen port
283 to a single interface (ENV_BIND_IF) then ONLY that interface/address is added
284 to the list so that we don't pick up entries from the rtable that are for other
285 processes listening on different interfaces.
287 The ENV_BIN_IF environment variable may be either an IP address (v6 must be in
288 square braces), or an interface name (e.g. eth0).
290 if_addrs_t* mk_ip_list( char* port ) {
292 struct ifaddrs *ifs; // pointer to head
293 struct ifaddrs *ele; // pointer into the list
294 char octs[NI_MAXHOST+1];
295 char wbuf[NI_MAXHOST+128];
297 char* envp; // at the environment var if there
298 char* target_if = NULL; // target interface supplied by ENV_BIND_IF
302 if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) {
305 memset( l, 0, sizeof( if_addrs_t ) );
306 l->addrs = (char **) malloc( sizeof( char* ) * 128 );
307 if( l->addrs == NULL ) {
312 if( (envp = getenv( ENV_BIND_IF )) != NULL ) {
313 if( isdigit( *envp ) || *envp == '[' ) { // ip address given and not device name
314 snprintf( wbuf, sizeof( wbuf ), "%s:%s", envp, port ); // smash port onto the addr as is
315 l->addrs[l->naddrs] = strdup( wbuf );
317 if( DEBUG ) rmr_vlog( RMR_VL_INFO, "rmr: using only specific bind interface when searching specific RT entries: %s\n", wbuf );
321 target_if = envp; // device name given, suss it out below
325 for( ele = ifs; ele; ele = ele->ifa_next ) {
326 memset( octs, 0, sizeof( octs ) );
327 if( ele && strcmp( ele->ifa_name, "lo" ) && // do NOT capture the loopback interface address
328 (target_if == NULL || strcmp( ele->ifa_name, target_if ) == 0 ) ) { // no target, or matches ENV_BIND_IF target
330 if( ele->ifa_addr->sa_family == AF_INET ) {
331 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
334 if( ele->ifa_addr->sa_family == AF_INET6 ) {
335 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in6 ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
341 if( (tok = strchr( octs, '%' )) != NULL ) { // for unknown reasons some ip6 addrs have %if-name appended; truncate
344 if( l->naddrs < 128 ) {
345 if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "capture address: %s: %s\n", ele->ifa_name, octs );
347 snprintf( wbuf, sizeof( wbuf ), fmt, octs, port ); // smash port onto the addr
348 l->addrs[l->naddrs] = strdup( wbuf );
363 Check the address:port passed in and return true if it matches
364 one of the addresses we saw when we built the list. Right now
365 this isn't a speed intensive part of our processing, so we just
366 do a straight search through the list. We don't expect this to
367 ever be a higly driven functions so not bothering to optimise.
369 int is_this_myip( if_addrs_t* l, char* addr ) {
380 for( i = 0; i < l->naddrs; i++ ) {
381 if( l->addrs[i] != NULL && strcmp( addr, l->addrs[i] ) == 0 ) {
390 Expects a buffer containing "sep" separated tokens, and a list of
391 IP addresses anchored by ip_list. Searches the tokens to see if
392 any are an ip address:port which is in the ip list. Returns true
393 (1) if a token is in the list, false otherwise.
395 static int has_myip( char const* buf, if_addrs_t* list, char sep, int max ) {
396 char* dbuf; // duplicated buf so we can trash
397 char** tokens; // pointer to tokens from the string
398 int ntokens; // number of tokens buf split into
400 int rc = 0; // return code
415 dbuf = strdup( buf ); // get a copy we can mess with
421 if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
427 ntokens = uta_tokenise( dbuf, tokens, max, sep );
430 for( i = 0; ! rc && i < ntokens; i++ ) {
432 if( is_this_myip( list, tokens[i] ) ) {
445 Given a list manager block, return the default IP address.
446 For now, that is just the first address on the list which
447 easily could be non-deterministic and change with each restart
448 of the application if a specific interface is not provided via
449 the environment variable (ENV_BIND_IF) and if there is more than
450 one device available on the container/physical host.
452 static char* get_default_ip( if_addrs_t* iplist ) {
454 if( iplist != NULL && iplist->naddrs > 0 && iplist->addrs != NULL ) {
455 return strdup( iplist->addrs[0] );