Rewrite NTS Framework.
[sim/o1-interface.git] / ntsimulator / ntsim-ng / utils / sys_utils.c
1 /*************************************************************************
2 *
3 * Copyright 2020 highstreet technologies GmbH and others
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ***************************************************************************/
17
18 #define _GNU_SOURCE
19
20 #include "sys_utils.h"
21 #include "log_utils.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <math.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <ifaddrs.h>
31 #include <netdb.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/socket.h>
36 #include <arpa/inet.h>
37 #include <linux/if_link.h>
38 #include <assert.h>
39
40 static char b64_encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
41                                     'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
42                                     'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
43                                     'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
44                                     'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
45                                     'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
46                                     'w', 'x', 'y', 'z', '0', '1', '2', '3',
47                                     '4', '5', '6', '7', '8', '9', '+', '/'};
48
49 static int b64_mod_table[] = {0, 2, 1};
50
51 bool dir_exists(const char *path) {
52     assert(path);
53
54     struct stat st = {0};
55     return (stat(path, &st) != -1);
56 }
57
58 bool file_exists(const char *fname) {
59     assert(fname);
60
61     return (access(fname, F_OK) != -1);
62 }
63
64 void file_touch(const char *fname, const char *content) {
65     assert(fname);
66
67     FILE *f = fopen(fname, "w");
68     if(f == 0) {
69         log_error("fopen failed");
70         return;
71     }
72
73     if(content) {
74         fprintf(f, "%s", content);
75     }
76     fclose(f);
77 }
78
79 char *file_read_content(const char *fname) {
80     assert(fname);
81
82     char *buffer = 0;
83     long length;
84     FILE *f = fopen(fname, "rb");
85     if(f) {
86         fseek(f, 0, SEEK_END);
87         length = ftell(f);
88         fseek(f, 0, SEEK_SET);
89         buffer = (char*)malloc(sizeof(char) * length);
90         if(buffer) {
91             fread(buffer, 1, length, f);
92         }
93         fclose(f);
94     }
95
96     return buffer;
97 }
98
99 int get_int_from_string_with_default(const char *string, int default_value) {
100     int rc;
101     int value = default_value;
102
103     if(string != 0) {
104         rc = sscanf(string, "%d", &value);
105         if (rc != 1) {
106             value = default_value;
107         }
108     }
109     return value;
110 }
111
112 char *get_current_date_and_time(void) {
113     char *date_and_time = 0;
114
115         time_t t = time(0);
116         struct tm tm = *localtime(&t);
117         struct timeval tv;
118         int millisec;
119
120         gettimeofday(&tv, 0);
121         millisec = lrint(tv.tv_usec/1000.0); // Round to nearest millisec
122         if(millisec>=1000)      { // Allow for rounding up to nearest second
123                 millisec -=1000;
124                 tv.tv_sec++;
125                 millisec /= 100;
126         }
127
128         asprintf(&date_and_time, "%04d-%02d-%02dT%02d:%02d:%02d.%01dZ",
129                 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
130                 tm.tm_hour, tm.tm_min, tm.tm_sec, millisec/100);
131
132     return date_and_time;
133 }
134
135 long int get_microseconds_since_epoch(void) {
136     time_t t = time(0);
137     struct timeval tv;
138     long int useconds;
139
140     gettimeofday(&tv, 0);
141     useconds = t*1000 + tv.tv_usec; //add the microseconds to the seconds
142
143     return useconds;
144 }
145
146
147 bool get_local_ips(const char *ifname, char **ipv4, char **ipv6) {
148     assert(ifname);
149     assert(ipv4);
150     assert(ipv6);
151
152     int s;
153     struct ifaddrs *ifaddr;
154     struct ifaddrs *ifa;
155     char host[NI_MAXHOST];
156     bool ret = true;
157
158     *ipv4 = 0;
159     *ipv6 = 0;
160
161     if (getifaddrs(&ifaddr) == -1)  {
162         ret = false;
163         goto get_local_ips_free;
164     }
165
166     for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)  {
167         if (ifa->ifa_addr == NULL) {
168             continue;
169         }
170
171         s = getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
172         if((strcmp(ifa->ifa_name, ifname) == 0) && (ifa->ifa_addr->sa_family == AF_INET)) {
173             if (s != 0) {
174                 ret = false;
175                 goto get_local_ips_free;
176             }
177             
178             *ipv4 = strdup(host);
179             break;
180         }
181     }
182
183
184     for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)  {
185         if (ifa->ifa_addr == NULL) {
186             continue;
187         }
188
189         s = getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in6),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
190         if((strcmp(ifa->ifa_name, ifname) == 0) && (ifa->ifa_addr->sa_family == AF_INET6)) {
191             if (s != 0) {
192                 ret = false;
193                 goto get_local_ips_free;
194             }
195             
196             *ipv6 = strdup(host);
197             break;
198         }
199     }
200
201     get_local_ips_free:
202     if(ret == false) {
203         free(*ipv4);
204         free(*ipv6);
205         *ipv4 = 0;
206         *ipv6 = 0;
207     }
208
209     freeifaddrs(ifaddr);
210     return ret;
211 }
212
213 bool check_port_open(const char *host, uint16_t port) {
214     assert(host);
215
216     int simpleSocket = 0;
217     int returnStatus = 0; 
218     struct addrinfo simpleServer;
219     struct addrinfo *res;
220
221     memset(&simpleServer, 0, sizeof simpleServer);
222     simpleServer.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
223     simpleServer.ai_socktype = SOCK_STREAM;
224     simpleServer.ai_flags = AI_ADDRCONFIG;
225
226     char sport[10];
227     sprintf(sport, "%d", port);
228
229     returnStatus = getaddrinfo(host, sport, &simpleServer, &res);
230     if(returnStatus != 0) {
231         return false;
232     }
233
234     simpleSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
235     if(simpleSocket < 0) {
236         freeaddrinfo(res);
237         return false;
238     }
239
240     char s[INET6_ADDRSTRLEN];
241     switch(res->ai_addr->sa_family) {
242         case AF_INET: {
243             struct sockaddr_in *addr_in = (struct sockaddr_in *)res->ai_addr; 
244             inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
245             returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
246             break;
247         }
248
249         case AF_INET6: {
250             struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res->ai_addr;
251             inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
252             returnStatus = connect(simpleSocket, res->ai_addr, res->ai_addrlen);
253             break;
254         }
255
256         default:
257             break;
258     }
259
260     freeaddrinfo(res);
261     if(returnStatus == 0) {
262         close(simpleSocket);
263         return true;
264     }
265     
266     return false;
267 }
268
269 char *b64_encode(const unsigned char *data, size_t input_length) {
270     assert(data);
271     assert(input_length);
272
273     int output_length = 4 * ((input_length + 2) / 3);
274
275     char *encoded_data = (char *)malloc(sizeof(char) * (output_length + 1));
276     if (encoded_data == 0) {
277         return 0;
278     }
279
280     for (int i = 0, j = 0; i < input_length;) {
281         uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
282         uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
283         uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
284
285         uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
286
287         encoded_data[j++] = b64_encoding_table[(triple >> 3 * 6) & 0x3F];
288         encoded_data[j++] = b64_encoding_table[(triple >> 2 * 6) & 0x3F];
289         encoded_data[j++] = b64_encoding_table[(triple >> 1 * 6) & 0x3F];
290         encoded_data[j++] = b64_encoding_table[(triple >> 0 * 6) & 0x3F];
291     }
292
293     for (int i = 0; i < b64_mod_table[input_length % 3]; i++) {
294         encoded_data[output_length - 1 - i] = '=';
295     }
296
297     encoded_data[output_length] = 0;
298
299     return encoded_data;
300 }
301
302 char *str_replace(const char *orig, const char *rep, const char *with) {
303     assert(orig);
304     assert(rep);
305     assert(with);
306
307     char *result; // the return string
308     const char *ins;    // the next insert point
309     char *tmp;    // varies
310     int len_rep;  // length of rep (the string to remove)
311     int len_with; // length of with (the string to replace rep with)
312     int len_front; // distance between rep and end of last rep
313     int count;    // number of replacements
314
315     // sanity checks and initialization
316     if(!orig || !rep) {
317         return 0;
318     }
319
320     len_rep = strlen(rep);
321     if(len_rep == 0) {
322         return 0; // empty rep causes infinite loop during count
323     }
324
325     if (!with) {
326         with = "";
327     }
328     len_with = strlen(with);
329
330     // count the number of replacements needed
331     ins = orig;
332     for(count = 0; (tmp = strstr(ins, rep)); ++count) {
333         ins = tmp + len_rep;
334     }
335
336     tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);
337
338     if(!result) {
339         return 0;
340     }
341
342     // first time through the loop, all the variable are set correctly
343     // from here on,
344     //    tmp points to the end of the result string
345     //    ins points to the next occurrence of rep in orig
346     //    orig points to the remainder of orig after "end of rep"
347     while(count--) {
348         ins = strstr(orig, rep);
349         len_front = ins - orig;
350         tmp = strncpy(tmp, orig, len_front) + len_front;
351         tmp = strcpy(tmp, with) + len_with;
352         orig += len_front + len_rep; // move to next "end of rep"
353     }
354
355     strcpy(tmp, orig);
356     return result;
357 }
358
359 char *read_key(const char *filename) {
360     assert(filename);
361
362     FILE * fp = 0;
363     char * line = 0;
364     size_t len = 0;
365     ssize_t read;
366     char *key_string = 0;
367
368     fp = fopen(filename, "r");
369     if(fp == 0) {
370         log_error("could not open file %s", filename);
371         return 0;
372     }
373
374     while((read = getline(&line, &len, fp)) != -1) {
375         // we ignore the first and last lines forPrivate keys, Public keys and Certificates
376         if(strstr(line, "PRIVATE KEY-----") || strstr(line, "PUBLIC KEY-----") || strstr(line, "CERTIFICATE-----")) {
377             free(line);
378             line = 0;
379             len = 0;
380             continue;
381         }
382         else {
383             if(key_string) {
384                 key_string = (char *)realloc(key_string, strlen(key_string) + read + 1);
385                 if(key_string == 0) {
386                     log_error("bad allocation");
387                     free(line);
388                     return 0;
389                 }
390
391                 strcat(key_string, line);
392             }
393             else {
394                 key_string = strdup(line);
395                 if(key_string == 0) {
396                     log_error("bad allocation");
397                     free(line);
398                     return 0;
399                 }
400             }
401             
402             free(line);
403             line = 0;
404             len = 0;
405         }
406     }
407
408     fclose(fp);
409     if(line) {
410         free(line);
411     }
412
413     return key_string;
414 }