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_h2ip -- look up host name and return an ip address
26 uta_lookup_rtg -- looks in env for rtg host:port
27 uta_has_str -- searches buffer of tokens for a string
29 uta_link2 -- establish a nanomsg connection to a host
31 Author: E. Scott Daniels
32 Date: 30 November 2018
35 #ifndef _tools_static_c
36 #define _tools_static_c
47 #include <sys/types.h> // these are needed to suss out ip addresses from interfaces
49 #include <arpa/inet.h>
50 #include <sys/socket.h>
55 Simple tokeniser. Split a null terminated string into tokens recording the
56 pointers in the tokens array provided. Tokens MUST be large enough. Max is
57 the max number of tokens to split into. Returns the actual number of tokens
58 recorded in the pointer array.
60 CAUTION: this modifies the string passed in!!
62 static int uta_tokenise( char* buf, char** tokens, int max, char sep ) {
63 char* end; // end of token
66 if( !buf || ! tokens || !(*buf) ) {
72 while( n < max && *end && (end = strchr( end, sep )) != NULL ) {
81 Xlate hostname (expected to be name:port) to an IP address that nano will tolerate.
82 We'll use the first address from the list to keep it simple. If the first character
83 of the name is a digit, we assume it's really an IP address and just return that.
85 Return is a string which the caller must free. Even if the string passed in is already
86 an IP address, a duplicate will be returend so that it can always be freed.
87 On error a nil pointer is returned.
89 static char* uta_h2ip( char const* hname ) {
96 char* dname; // duplicated name for distruction
98 dname = strdup( hname );
100 if( isdigit( *dname ) || *dname == '[' ) { // hostnames can't start with digit, or ipv6 [; assume ip address
104 if( (tok = strchr( dname, ':' )) != NULL ) {
108 hent = gethostbyname( dname );
109 if( hent == NULL || hent->h_addr_list == NULL ) {
110 //fprintf( stderr, "[WARN] h2ip: dns lookup failed for: %s\n", dname );
115 a = ntohl( *((unsigned int *)hent->h_addr_list[0]) );
116 for( i = 3; i >= 0; i-- ) {
121 if( tok ) { // if :port was given, smash it back on
122 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d:%s", octs[0], octs[1], octs[2], octs[3], tok );
124 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d", octs[0], octs[1], octs[2], octs[3] );
128 return strdup( buf );
133 Looks for the environment variable RMR_RTG_SVC which we assume to be name[:port], and
134 does a dns lookup on the name. If the env does not have such a variable, we default to
135 "rtg" and a port of 5656.
137 Returns true (1) if lookup found something;
139 CAUTION: this is ONLY used if the RTG is a pub and we are using pub/sub to get updates.
140 There are issues with some underlying transport pub/sub implementations so this
141 is likley NOT needed/used.
143 static int uta_lookup_rtg( uta_ctx_t* ctx ) {
144 char* ev; // pointer to the env value
145 char* def_port = "5656";
154 if( ctx->rtg_addr ) {
155 free( ctx->rtg_addr );
158 if( (ev = getenv( "RMR_RTG_SVC" )) == NULL ) {
162 dstr = strdup( ev ); // copy so we can trash it
163 if( (port = strchr( dstr, ':' )) == NULL ) {
167 port++; // point at the first digit
169 ev = dstr; // all references below assume ev
172 ctx->rtg_addr = uta_h2ip( ev ); // convert name to IP addr
173 ctx->rtg_port = atoi( port );
178 return ctx->rtg_addr != NULL;
183 Expects a buffer of 'sep' separated tokens and looks to see if
184 the given string is one of those tokens. Returns the token
185 index (0 - n-1) if the string is found; -1 otherwise. The max
186 parameter supplies the maximum number of tokens to search in
189 On failure (-1) errno will be set in cases where memory cannot
190 be alocated (is this even possible any more?). If errno is 0
191 and failure is returned, then the caller should assume that
192 the token isn't in the list, or the list had no elements.
194 static int uta_has_str( char const* buf, char const* str, char sep, int max ) {
195 char* dbuf; // duplicated buf so we can trash
196 char** tokens; // pointer to tokens from the string
197 int ntokens; // number of tokens buf split into
199 int rc; // return code
205 dbuf = strdup( buf );
211 if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
217 ntokens = uta_tokenise( dbuf, tokens, max, sep );
220 for( i = 0; rc < 0 && i < ntokens; i++ ) {
222 if( strcmp( tokens[i], str ) == 0 ) {
234 Generate a list of all IP address associated with the interfaces available.
235 For now we capture them all, but we may need to limit. The port is smashed
236 onto each IP we find so that we can do a direct compare against the addr
237 that could be in the route table.
239 If the environment variable which limits the binding of our listen port
240 to a single interface (ENV_BIND_IF) then ONLY that interface/address is added
241 to the list so that we don't pick up entries from the rtable that are for other
242 processes listening on different interfaces.
244 The ENV_BIN_IF environment variable may be either an IP address (v6 must be in
245 square braces), or an interface name (e.g. eth0).
247 if_addrs_t* mk_ip_list( char* port ) {
249 struct ifaddrs *ifs; // pointer to head
250 struct ifaddrs *ele; // pointer into the list
251 char octs[NI_MAXHOST+1];
252 char wbuf[NI_MAXHOST+128];
254 char* envp; // at the environment var if there
255 char* target_if = NULL; // target interface supplied by ENV_BIND_IF
259 if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) {
262 memset( l, 0, sizeof( if_addrs_t ) );
263 l->addrs = (char **) malloc( sizeof( char* ) * 128 );
264 if( l->addrs == NULL ) {
269 if( (envp = getenv( ENV_BIND_IF )) != NULL ) {
270 if( isdigit( *envp ) || *envp == '[' ) { // ip address given and not device name
271 snprintf( wbuf, sizeof( wbuf ), "%s:%s", envp, port ); // smash port onto the addr as is
272 l->addrs[l->naddrs] = strdup( wbuf );
274 if( DEBUG ) fprintf( stderr, "[INFO] rmr: using only specific bind interface when searching specific RT entries: %s\n", wbuf );
278 target_if = envp; // device name given, suss it out below
282 for( ele = ifs; ele; ele = ele->ifa_next ) {
283 memset( octs, 0, sizeof( octs ) );
284 if( ele && strcmp( ele->ifa_name, "lo" ) && // do NOT capture the loopback interface address
285 (target_if == NULL || strcmp( ele->ifa_name, target_if ) == 0 ) ) { // no target, or matches ENV_BIND_IF target
287 if( ele->ifa_addr->sa_family == AF_INET ) {
288 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
291 if( ele->ifa_addr->sa_family == AF_INET6 ) {
292 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in6 ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
298 if( (tok = strchr( octs, '%' )) != NULL ) { // for unknown reasons some ip6 addrs have %if-name appended; truncate
301 if( l->naddrs < 128 ) {
302 if( DEBUG ) fprintf( stderr, "[DBUG] capture address: %s: %s\n", ele->ifa_name, octs );
304 snprintf( wbuf, sizeof( wbuf ), fmt, octs, port ); // smash port onto the addr
305 l->addrs[l->naddrs] = strdup( wbuf );
320 Check the address:port passed in and return true if it matches
321 one of the addresses we saw when we built the list. Right now
322 this isn't a speed intensive part of our processing, so we just
323 do a straight search through the list. We don't expect this to
324 ever be a higly driven functions so not bothering to optimise.
326 int is_this_myip( if_addrs_t* l, char* addr ) {
333 for( i = 0; i < l->naddrs; i++ ) {
334 if( strcmp( addr, l->addrs[i] ) == 0 ) {
343 Expects a buffer containing "sep" separated tokens, and a list of
344 IP addresses anchored by ip_list. Searches the tokens to see if
345 any are an ip address:port which is in the ip list. Returns true
346 (1) if a token is in the list, false otherwise.
348 static int has_myip( char const* buf, if_addrs_t* list, char sep, int max ) {
349 char* dbuf; // duplicated buf so we can trash
350 char** tokens; // pointer to tokens from the string
351 int ntokens; // number of tokens buf split into
353 int rc = 0; // return code
368 dbuf = strdup( buf ); // get a copy we can mess with
374 if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
380 ntokens = uta_tokenise( dbuf, tokens, max, sep );
383 for( i = 0; ! rc && i < ntokens; i++ ) {
385 if( is_this_myip( list, tokens[i] ) ) {
398 Given a list manager block, return the default IP address.
399 For now, that is just the first address on the list which
400 easily could be non-deterministic and change with each restart
401 of the application if a specific interface is not provided via
402 the environment variable (ENV_BIND_IF) and if there is more than
403 one device available on the container/physical host.
405 static char* get_default_ip( if_addrs_t* iplist ) {
407 if( iplist != NULL && iplist->naddrs > 0 && iplist->addrs != NULL ) {
408 return strdup( iplist->addrs[0] );