8d41b098bb634ff9df0f82e473a3b5bb4e1b5bbf
[ric-plt/lib/rmr.git] / src / 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_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
28
29                                         uta_link2       -- establish a nanomsg connection to a host
30
31         Author:         E. Scott Daniels
32         Date:           30 November 2018
33 */
34
35 #ifndef _tools_static_c
36 #define _tools_static_c
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <netdb.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <pthread.h>
45 #include <ctype.h>
46
47 #include <sys/types.h>          // these are needed to suss out ip addresses from interfaces
48 #include <ifaddrs.h>
49 #include <arpa/inet.h>
50 #include <sys/socket.h>
51 #include <netdb.h>
52
53
54 /*
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.
59
60         CAUTION: this modifies the string passed in!!
61 */
62 static int uta_tokenise( char* buf, char** tokens, int max, char sep ) {
63         char* end;                                      // end of token
64         int     n = 0;
65
66         if( !buf || ! tokens || !(*buf) ) {
67                 return 0;
68         }
69
70         tokens[n++] = buf;
71         end = buf;
72         while( n < max && *end && (end = strchr( end, sep )) != NULL ) {
73                 *end = 0;
74                 tokens[n++] = ++end;
75         }
76
77         return n;
78 }
79
80 /*
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.
84
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.
88 */
89 static char* uta_h2ip( char const* hname ) {
90         char                    buf[120];
91         struct hostent* hent;
92         unsigned int    octs[4];
93         unsigned int    a;
94         int                             i;
95         char*                   tok;
96         char*                   dname;          // duplicated name for distruction
97
98         dname = strdup( hname );
99
100         if( isdigit( *dname ) || *dname == '[' ) {              // hostnames can't start with digit, or ipv6 [; assume ip address
101                 return dname;
102         }
103
104         if( (tok = strchr( dname, ':' )) != NULL ) {
105                 *(tok++) = 0;
106         }
107
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 );
111                 free( dname );
112                 return NULL;
113         }
114
115         a = ntohl( *((unsigned int *)hent->h_addr_list[0]) );
116         for( i = 3; i >= 0; i-- ) {
117                 octs[i] = a & 0xff;
118                 a = a >> 8;
119         }
120
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 );
123         } else {
124                 snprintf( buf, sizeof( buf ), "%d.%d.%d.%d", octs[0], octs[1], octs[2], octs[3] );
125         }
126
127         free( dname );
128         return strdup( buf );
129 }
130
131
132 /*
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.
136
137         Returns true (1) if lookup found something;
138
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.
142 */
143 static int uta_lookup_rtg( uta_ctx_t* ctx ) {
144         char*   ev;                                     // pointer to the env value
145         char*   def_port = "5656";
146         char*   port = NULL;
147         char*   dstr = NULL;
148
149         if( ctx == NULL ) {
150                 return 0;
151         }
152
153
154         if( ctx->rtg_addr ) {
155                 free( ctx->rtg_addr );
156         }
157
158         if( (ev = getenv( "RMR_RTG_SVC" )) == NULL ) {
159                 ev = "rtg";
160                 port = def_port;
161         } else {
162                 dstr = strdup( ev );                    // copy so we can trash it
163                 if( (port = strchr( dstr, ':' )) == NULL ) {
164                         port = def_port;
165                 } else {
166                         *port = 0;
167                         port++;                                         // point at the first digit
168                 }
169                 ev = dstr;                                              // all references below assume ev
170         }
171
172         ctx->rtg_addr = uta_h2ip( ev );         // convert name to IP addr
173         ctx->rtg_port = atoi( port );
174         if( dstr ) {
175                 free( dstr );
176         }
177
178         return ctx->rtg_addr != NULL;
179 }
180         
181
182 /*
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
187         the buffer.
188
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.
193 */
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
198         int             i;
199         int             rc;                             // return code
200
201         if( max < 2 ) {
202                 return -1;
203         }
204
205         dbuf = strdup( buf );
206         if( dbuf == NULL  ) {
207                 errno = ENOMEM;
208                 return -1;
209         }
210
211         if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
212                 errno = ENOMEM;
213                 free( dbuf );
214                 return -1;
215         }
216
217         ntokens = uta_tokenise( dbuf, tokens, max, sep );
218         errno = 0;
219         rc = -1;
220         for( i = 0; rc < 0 && i < ntokens; i++ ) {
221                 if( tokens[i] ) {
222                         if( strcmp( tokens[i], str ) == 0 ) {
223                                 rc = i; 
224                         }
225                 }
226         }
227
228         free( dbuf );
229         free( tokens );
230         return rc;
231 }
232
233 /*
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.
238
239         If the environment variable which limits the binding of our listen port
240         to a single interface (ENV_BIND_IF) then ONLY that address is added to
241         the list so that we don't pick up entries from the rtable that are for other
242         processes listening on different interfaces.
243 */
244 if_addrs_t*  mk_ip_list( char* port ) {
245         if_addrs_t* l;
246         struct  ifaddrs *ifs;           // pointer to head
247         struct  ifaddrs *ele;           // pointer into the list
248         char    octs[NI_MAXHOST+1];
249         char    wbuf[NI_MAXHOST+128];
250         char*   fmt;
251         char*   envp;                           // at the environment var if there
252
253         
254
255         if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) {
256                 return NULL;
257         }
258         memset( l, 0, sizeof( if_addrs_t ) );
259         l->addrs = (char **) malloc( sizeof( char* ) * 128 );
260         if( l->addrs == NULL ) {
261                 free( l );
262                 return NULL;
263         }
264
265         if( (envp = getenv( ENV_BIND_IF )) != NULL ) {
266                 snprintf( wbuf, sizeof( wbuf ), "%s:%s", envp, port );          // smash port onto the addr as is
267                 l->addrs[l->naddrs] = strdup( wbuf );
268                 l->naddrs++;
269                 if( DEBUG ) fprintf( stderr, "[INFO] rmr: using only specific bind interface when searching specific RT entries: %s\n", wbuf );
270                 return l;
271         }
272
273         getifaddrs( &ifs );
274         for( ele = ifs; ele; ele = ele->ifa_next ) {
275                 *octs = 0;
276
277                 if( ele && strcmp( ele->ifa_name, "lo" )  ) {
278                         if( ele->ifa_addr->sa_family == AF_INET ) {
279                                 getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in ),  octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
280                                 fmt = "%s:%s";
281                         } else {
282                                 if( ele->ifa_addr->sa_family == AF_INET6 ) {
283                                         getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in6 ),  octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
284                                         fmt = "[%s]:%s";
285                                 }
286                         }
287
288                         if( *octs ) {
289                                 if( l->naddrs < 128 ) {
290                                         snprintf( wbuf, sizeof( wbuf ), fmt, octs, port );              // smash port onto the addr
291                                         l->addrs[l->naddrs] = strdup( wbuf );
292                                         l->naddrs++;
293                                 }
294                         }
295                 }
296         }
297
298         if( ifs ) {
299                 freeifaddrs( ifs );
300         }
301
302         return l;
303 }
304
305 /*
306         Check the address:port passed in and return true if it matches
307         one of the addresses we saw when we built the list. Right now
308         this isn't a speed intensive part of our processing, so we just
309         do a straight search through the list. We don't expect this to 
310         ever be a higly driven functions so not bothering to optimise.
311 */
312 int is_this_myip( if_addrs_t* l, char* addr ) {
313         int i;
314
315         if( l == NULL ) {
316                 return 0;
317         }
318
319         for( i = 0; i < l->naddrs; i++ ) {
320                 if( strcmp( addr, l->addrs[i] ) == 0 ) {
321                         return 1;
322                 }
323         }
324
325         return 0;
326 }
327
328 /*
329         Expects a buffer containing "sep" separated tokens, and a list of
330         IP addresses anchored by ip_list.  Searches the tokens to see if
331         any are an ip address:port which is in the ip list.  Returns true
332         (1) if a token is in the list, false otherwise.
333 */
334 static int has_myip( char const* buf, if_addrs_t* list, char sep, int max ) {
335         char*   dbuf;                   // duplicated buf so we can trash
336         char** tokens;                  // pointer to tokens from the string
337         int             ntokens;                // number of tokens buf split into
338         int             i;
339         int             rc = 0;                 // return code
340
341         if( max < 2 ) {
342                 return 0;
343         }
344         
345         if( buf == NULL ) {
346                 return 0;
347         }
348
349         if( list == NULL ) {
350                 return 0;
351         }
352
353
354         dbuf = strdup( buf );                   // get a copy we can mess with
355         if( dbuf == NULL  ) {
356                 errno = ENOMEM;
357                 return 0;
358         }
359
360         if( (tokens = (char **) malloc( sizeof( char * ) * max )) == NULL ) {
361                 errno = ENOMEM;
362                 free( dbuf );
363                 return 0;
364         }
365
366         ntokens = uta_tokenise( dbuf, tokens, max, sep );
367         errno = 0;
368         rc = 0;
369         for( i = 0; ! rc  && i < ntokens; i++ ) {
370                 if( tokens[i] ) {
371                         if( is_this_myip( list, tokens[i] ) ) {
372                                 rc = 1; 
373                                 break;
374                         }
375                 }
376         }
377
378         free( dbuf );
379         free( tokens );
380         return rc;
381 }
382
383 #endif