fix(rtable): Prevent direct send hairpins
[ric-plt/lib/rmr.git] / src / rmr / common / src / tools_static.c
1 // :vi sw=4 ts=4 noet:
2 /*
3 ==================================================================================
4         Copyright (c) 2019 Nokia
5         Copyright (c) 2018-2019 AT&T Intellectual Property.
6
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
10
11            http://www.apache.org/licenses/LICENSE-2.0
12
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 ==================================================================================
19 */
20
21 /*
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
29
30                                         uta_link2       -- establish a nanomsg connection to a host
31
32         Author:         E. Scott Daniels
33         Date:           30 November 2018
34 */
35
36 #ifndef _tools_static_c
37 #define _tools_static_c
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <netdb.h>
42 #include <errno.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <pthread.h>
46 #include <ctype.h>
47
48 #include <sys/types.h>          // these are needed to suss out ip addresses from interfaces
49 #include <ifaddrs.h>
50 #include <arpa/inet.h>
51 #include <sys/socket.h>
52 #include <netdb.h>
53
54 // --- some protos needed for better organisation --------
55 int is_this_myip( if_addrs_t* l, char* addr );
56
57
58 // ----------------------------------------------------------------------------------
59
60 /*
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.
65
66         CAUTION: this modifies the string passed in!!
67 */
68 static int uta_tokenise( char* buf, char** tokens, int max, char sep ) {
69         char* end;                                      // end of token
70         int     n = 0;
71
72         if( !buf || ! tokens || !(*buf) ) {
73                 return 0;
74         }
75
76         tokens[n++] = buf;
77         end = buf;
78         while( n < max && *end && (end = strchr( end, sep )) != NULL ) {
79                 *end = 0;
80                 tokens[n++] = ++end;
81         }
82
83         return n;
84 }
85
86 /*
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.
95
96         Returns the number of tokens referenced by toks.
97 */
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
101         char**  all_toks;
102         int i;
103         int j;
104
105         
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
108         j = 0;
109         if( ntoks > 0 ) {
110                 for( i = 0; i < ntoks; i++ ) {
111                         if( is_this_myip( iplist, all_toks[i] ) ) {
112                                 pcount--;                                                                       // ours, prune
113                         } else {
114                                 toks[j++] = all_toks[i];                                        // not one of ours, keep it
115                         }
116                 }
117         }
118
119         free( all_toks );
120         return pcount;
121 }
122
123 /*
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.
127
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.
131 */
132 static char* uta_h2ip( char const* hname ) {
133         char                    buf[120];
134         struct hostent* hent;
135         unsigned int    octs[4];
136         unsigned int    a;
137         int                             i;
138         char*                   tok;
139         char*                   dname;          // duplicated name for distruction
140
141         dname = strdup( hname );
142
143         if( isdigit( *dname ) || *dname == '[' ) {              // hostnames can't start with digit, or ipv6 [; assume ip address
144                 return dname;
145         }
146
147         if( (tok = strchr( dname, ':' )) != NULL ) {
148                 *(tok++) = 0;
149         }
150
151         hent = gethostbyname( dname );
152         if( hent == NULL || hent->h_addr_list == NULL ) {
153                 //fprintf( stderr, "[WARN] h2ip: dns lookup failed for: %s\n", dname );
154                 free( dname );
155                 return NULL;
156         }
157
158         a = ntohl( *((unsigned int *)hent->h_addr_list[0]) );
159         for( i = 3; i >= 0; i-- ) {
160                 octs[i] = a & 0xff;
161                 a = a >> 8;
162         }
163
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 );
166         } else {
167                 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d", octs[0], octs[1], octs[2], octs[3] );
168         }
169
170         free( dname );
171         return strdup( buf );
172 }
173
174
175 /*
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.
179
180         Returns true (1) if lookup found something;
181
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.
185 */
186 static int uta_lookup_rtg( uta_ctx_t* ctx ) {
187         char*   ev;                                     // pointer to the env value
188         char*   def_port = "5656";
189         char*   port = NULL;
190         char*   dstr = NULL;
191
192         if( ctx == NULL ) {
193                 return 0;
194         }
195
196
197         if( ctx->rtg_addr ) {
198                 free( ctx->rtg_addr );
199         }
200
201         if( (ev = getenv( "RMR_RTG_SVC" )) == NULL ) {
202                 ev = "rtg";
203                 port = def_port;
204         } else {
205                 dstr = strdup( ev );                    // copy so we can trash it
206                 if( (port = strchr( dstr, ':' )) == NULL ) {
207                         port = def_port;
208                 } else {
209                         *port = 0;
210                         port++;                                         // point at the first digit
211                 }
212                 ev = dstr;                                              // all references below assume ev
213         }
214
215         ctx->rtg_addr = uta_h2ip( ev );         // convert name to IP addr
216         ctx->rtg_port = atoi( port );
217         if( dstr ) {
218                 free( dstr );
219         }
220
221         return ctx->rtg_addr != NULL;
222 }
223
224
225 /*
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
230         the buffer.
231
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.
236 */
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
241         int             i;
242         int             rc;                             // return code
243
244         if( max < 2 ) {
245                 return -1;
246         }
247
248         dbuf = strdup( buf );
249         if( dbuf == NULL  ) {
250                 errno = ENOMEM;
251                 return -1;
252         }
253
254         if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
255                 errno = ENOMEM;
256                 free( dbuf );
257                 return -1;
258         }
259
260         ntokens = uta_tokenise( dbuf, tokens, max, sep );
261         errno = 0;
262         rc = -1;
263         for( i = 0; rc < 0 && i < ntokens; i++ ) {
264                 if( tokens[i] ) {
265                         if( strcmp( tokens[i], str ) == 0 ) {
266                                 rc = i;
267                         }
268                 }
269         }
270
271         free( dbuf );
272         free( tokens );
273         return rc;
274 }
275
276 /*
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.
281
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.
286
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).
289 */
290 if_addrs_t*  mk_ip_list( char* port ) {
291         if_addrs_t* l;
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];
296         char*   fmt;
297         char*   envp;                           // at the environment var if there
298         char*   target_if = NULL;       // target interface supplied by ENV_BIND_IF
299         char*   tok;
300
301
302         if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) {
303                 return NULL;
304         }
305         memset( l, 0, sizeof( if_addrs_t ) );
306         l->addrs = (char **) malloc( sizeof( char* ) * 128 );
307         if( l->addrs == NULL ) {
308                 free( l );
309                 return NULL;
310         }
311
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 );
316                         l->naddrs++;
317                         if( DEBUG ) fprintf( stderr, "[INFO] rmr: using only specific bind interface when searching specific RT entries: %s\n", wbuf );
318                         return l;
319                 }
320
321                 target_if = envp;               // device name given, suss it out below
322         }
323
324         getifaddrs( &ifs );
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
329
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 );
332                                 fmt = "%s:%s";
333                         } else {
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 );
336                                         fmt = "[%s]:%s";
337                                 }
338                         }
339
340                         if( *octs ) {
341                                 if( (tok = strchr( octs, '%' )) != NULL ) {                     // for unknown reasons some ip6 addrs have %if-name appended; truncate
342                                         *tok = 0;
343                                 }
344                                 if( l->naddrs < 128 ) {
345                                         if( DEBUG ) fprintf( stderr, "[DBUG] capture address: %s: %s\n", ele->ifa_name, octs );
346
347                                         snprintf( wbuf, sizeof( wbuf ), fmt, octs, port );              // smash port onto the addr
348                                         l->addrs[l->naddrs] = strdup( wbuf );
349                                         l->naddrs++;
350                                 }
351                         }
352                 }
353         }
354
355         if( ifs ) {
356                 freeifaddrs( ifs );
357         }
358
359         return l;
360 }
361
362 /*
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.
368 */
369 int is_this_myip( if_addrs_t* l, char* addr ) {
370         int i;
371
372         if( l == NULL ) {
373                 return 0;
374         }
375
376         if( addr == NULL ) {
377                 return 0;
378         }
379
380         for( i = 0; i < l->naddrs; i++ ) {
381                 if( l->addrs[i] != NULL  &&  strcmp( addr, l->addrs[i] ) == 0 ) {
382                         return 1;
383                 }
384         }
385
386         return 0;
387 }
388
389 /*
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.
394 */
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
399         int             i;
400         int             rc = 0;                 // return code
401
402         if( max < 2 ) {
403                 return 0;
404         }
405
406         if( buf == NULL ) {
407                 return 0;
408         }
409
410         if( list == NULL ) {
411                 return 0;
412         }
413
414
415         dbuf = strdup( buf );                   // get a copy we can mess with
416         if( dbuf == NULL  ) {
417                 errno = ENOMEM;
418                 return 0;
419         }
420
421         if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
422                 errno = ENOMEM;
423                 free( dbuf );
424                 return 0;
425         }
426
427         ntokens = uta_tokenise( dbuf, tokens, max, sep );
428         errno = 0;
429         rc = 0;
430         for( i = 0; ! rc  && i < ntokens; i++ ) {
431                 if( tokens[i] ) {
432                         if( is_this_myip( list, tokens[i] ) ) {
433                                 rc = 1;
434                                 break;
435                         }
436                 }
437         }
438
439         free( dbuf );
440         free( tokens );
441         return rc;
442 }
443
444 /*
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.
451 */
452 static char* get_default_ip( if_addrs_t* iplist ) {
453
454         if( iplist != NULL  &&  iplist->naddrs > 0  &&  iplist->addrs != NULL ) {
455                 return strdup( iplist->addrs[0] );
456         }
457
458         return NULL;
459 }
460
461 #endif