]> git.ipfire.org Git - people/stevee/aiccu.git/blame - common/common.c
Add setup script functionality to Linux client
[people/stevee/aiccu.git] / common / common.c
CommitLineData
d98f6a46
SS
1/**********************************************************
2 SixXS - Automatic IPv6 Connectivity Configuration Utility
3***********************************************************
4 Copyright 2003-2005 SixXS - http://www.sixxs.net
5***********************************************************
6 common/common.c - Common Functions
7***********************************************************
8 $Author: jeroen $
9 $Id: common.c,v 1.14 2006-12-21 14:08:50 jeroen Exp $
10 $Date: 2006-12-21 14:08:50 $
11**********************************************************/
12
13/* Dirty dependency for Windows:GUI version */
14#ifdef _WIN32
15#ifndef AICCU_CONSOLE
16#include "../windows-gui/stdafx.h"
17#include "../windows-gui/AICCUApp.h"
18extern CAICCUApp theApp;
19#endif
20#endif
21
22#include "aiccu.h"
23#include "common.h"
24
25/* getline debugging? */
26/*
27#define E(x) x
28 */
29#define E(x) {}
30
31void dologA(int level, const char *fmt, va_list ap)
32{
33#ifdef _WIN32
34 char buf[1024];
35#endif
36 /* Don't show noise */
37 if (g_aiccu && !g_aiccu->verbose && level == LOG_DEBUG) return;
38
39#ifndef _WIN32
40 if (g_aiccu && g_aiccu->daemonize > 0) vsyslog(LOG_LOCAL7|level, fmt, ap);
41 else
42 {
43 vfprintf(stderr, fmt, ap);
44 fflush(stderr);
45 }
46#else
47 vsnprintf(buf, sizeof(buf), fmt, ap);
48
49#ifndef AICCU_CONSOLE
50 /* Use the debug facility */
51 OutputDebugString(buf);
52
53 /* Store it in a log file if we are running in verbose mode */
54 if (g_aiccu && g_aiccu->verbose)
55 {
56 char logfile[1024];
57 FILE *f;
58
59 /* Figure out the "C:\Windows" location */
60 /* as that is where we store our configuration */
61 GetWindowsDirectory(logfile, sizeof(logfile));
62 strncat(logfile, "\\aiccu.log", sizeof(logfile));
63 f = fopen(logfile, "w+");
64 if (f)
65 {
66 fwrite(buf, strlen(buf), 1, f);
67 fclose(f);
68 }
69 }
70
71 /*
72 * Always store the last message
73 * which can be displayed as errors etc.
74 */
75
76 /* strip the \n */
77 if (strlen(buf) > 0) buf[strlen(buf)-1] = '\0';
78 theApp.m_sMessage = buf;
79#else
80 OutputDebugString("dolog() - ");
81 OutputDebugString(buf);
82 fprintf(stderr, "%s", buf);
83#endif /* AICCU_CONSOLE */
84#endif /* !_WIN32 */
85}
86
87void dolog(int level, const char *fmt, ...)
88{
89 va_list ap;
90 va_start(ap, fmt);
91 dologA(level, fmt, ap);
92 va_end(ap);
93}
94
95/*
96 * Check if an address is RFC1918 based
97 * This allows us to warn the user that they are behind a NAT
98 */
99bool is_rfc1918(char *ipv4)
100{
101 unsigned int addr = inet_addr(ipv4);
102 bool ret = false;
103
104
105
106 ret = ( /* 10.0.0.0/8 */
107 ((addr & htonl(0xff000000)) == htonl(0x0a000000)) ||
108 /* 172.16.0.0/12 */
109 ((addr & htonl(0xfff00000)) == htonl(0xac100000)) ||
110 /* 192.168.0.0/16 */
111 ((addr & htonl(0xffff0000)) == htonl(0xc0a80000))) ? true : false;
112
113 dolog(LOG_DEBUG, "is_rfc1918(%s) = %s\n", ipv4, ret ? "yes" : "false");
114
115 return ret;
116}
117
118void sock_printf(TLSSOCKET sock, const char *fmt, ...)
119{
120 char buf[2048];
121 unsigned int len = 0, done = 0;
122 int ret;
123
124 va_list ap;
125 va_start(ap, fmt);
126 /* When not a socket send it to the logs */
127 if (sock == NULL || sock->socket == -1) dologA(LOG_INFO, fmt, ap);
128 else
129 {
130 /* Format the string */
131 len = vsnprintf(buf, sizeof(buf), fmt, ap);
132
133 /* Send the line(s) over the network */
134
135 while (done < len)
136 {
137#ifdef AICCU_GNUTLS
138 if (sock->tls_active) ret = gnutls_record_send(sock->session, &buf[done], len-done);
139 else
140#endif
141 ret = send(sock->socket, &buf[done], len-done, 0);
142
143 if (ret > 0) done+=ret;
144 else break;
145 }
146
147 /* Show this as debug output */
148 if (g_aiccu->verbose)
149 {
150 /* Strip the last \n */
151 len = (int)strlen(buf);
152 if (len > 0) buf[len-1] = '\0';
153 /* dump the information */
154 dolog(LOG_DEBUG, "sock_printf() : \"%s\"\n", buf);
155 }
156 }
157 va_end(ap);
158}
159
160extern char tic_buf[2048];
161
162/*
163 * Read a line from a socket and store it in ubuf
164 * Note: uses internal caching, this should be the only function
165 * used to read from the sock! The internal cache is rbuf.
166 */
167int sock_getline(TLSSOCKET sock, char *rbuf, unsigned int rbuflen, unsigned int *filled, char *ubuf, unsigned int ubuflen)
168{
169 unsigned int i;
170
171 if (!sock) return -1;
172
173 /* A closed socket? -> clear the buffer */
174 if (sock->socket == -1)
175 {
176 memset(rbuf, 0, rbuflen);
177 *filled = 0;
178 return -1;
179 }
180
181 /* Clear the caller supplied buffer, just in case */
182 memset(ubuf, 0, ubuflen);
183
184 for (;;)
185 {
186 E(dolog(LOG_DEBUG, "gl() - Filled %d\n", *filled);)
187
188 /* Did we still have something in the buffer? */
189 if (*filled > 0)
190 {
191 E(dolog(LOG_DEBUG, "gl() - Seeking newline\n");)
192
193 /* Walk to the end or until we reach a \n */
194 for (i=0; (i < (*filled-1)) && (rbuf[i] != '\n'); i++);
195
196 E(dolog(LOG_DEBUG, "gl() - Seeking newline - end\n");)
197
198 /* Did we find a newline? */
199 if (rbuf[i] == '\n')
200 {
201 E(dolog(LOG_DEBUG, "gl() - Found newline at %i\n", i+1);)
202
203 /* Newline with a Linefeed in front of it ? -> remove it */
204 if (rbuf[i] == '\n' && rbuf[i-1] == '\r')
205 {
206 E(dolog(LOG_DEBUG, "gl() - Removing LF\n");)
207 i--;
208 }
209 E(else dolog(LOG_DEBUG, "gl() - No LF\n");)
210
211 /* Copy this over to the caller */
212 memcpy(ubuf, rbuf, i);
213
214 E(dolog(LOG_DEBUG, "gl() - Copied %d bytes from %x to %x\n", i, rbuf, ubuf);)
215
216 /* Count the \r if it is there */
217 if (rbuf[i] == '\r') i++;
218 /* Count the \n */
219 i++;
220
221 /* filled = what is left in the buffer */
222 *filled -= i;
223
224 E(dolog(LOG_DEBUG, "gl() - %d bytes left in the buffer\n", *filled);)
225
226 /* Now move the rest of the buffer to the front */
227 if (*filled > 0) memmove(rbuf, &rbuf[i], *filled);
228 else *filled = 0;
229
230 /* Show this as debug output */
231 if (g_aiccu->verbose) dolog(LOG_DEBUG, "sock_getline() : \"%s\"\n", ubuf);
232
233 /* We got ourselves a line in 'buf' thus return to the caller */
234 return i;
235 }
236 }
237
238 E(dolog(LOG_DEBUG, "gl() - Trying to receive (max=%d)...\n", rbuflen-*filled-10);)
239
240 /* Fill the rest of the buffer */
241#ifdef AICCU_GNUTLS
242 if (sock->tls_active) i = gnutls_record_recv(sock->session, &rbuf[*filled], rbuflen-*filled-10);
243 else
244#endif
245 i = recv(sock->socket, &rbuf[*filled], rbuflen-*filled-10, 0);
246
247 E(dolog(LOG_DEBUG, "gl() - Received %d\n", i);)
248
249 /* Fail on errors */
250 if (i <= 0) return -1;
251
252 /* We got more filled space! */
253 *filled+=i;
254
255 /* Buffer overflow? */
256 if ( *filled >= (rbuflen-10) ||
257 *filled >= (ubuflen-10) )
258 {
259 dolog(LOG_ERR, "Buffer almost flowed over without receiving a newline\n");
260 return -1;
261 }
262
263 /* And try again in this loop ;) */
264 }
265
266 /* Never reached */
267 return -1;
268}
269
270TLSSOCKET sock_alloc(void);
271TLSSOCKET sock_alloc(void)
272{
273#ifdef AICCU_GNUTLS
d98f6a46
SS
274 int ret;
275#endif /* AICCU_GNUTLS*/
276
277 TLSSOCKET sock;
278
279 sock = (TLSSOCKET)malloc(sizeof(*sock));
280 if (!sock) return NULL;
281
282 sock->socket = -1;
283
284#ifdef AICCU_GNUTLS
285 /* TLS is not active yet (use sock_gotls() for that) */
286 sock->tls_active = false;
287
288 /* Initialize TLS session */
289 ret = gnutls_init(&sock->session, GNUTLS_CLIENT);
61a319c2 290 if (ret != GNUTLS_E_SUCCESS)
d98f6a46
SS
291 {
292 dolog(LOG_ERR, "TLS Init failed: %s (%d)\n", gnutls_strerror(ret), ret);
293 free(sock);
294 return NULL;
295 }
296
297 /* Use default priorities */
61a319c2
BP
298 ret = gnutls_priority_set_direct(sock->session, "NORMAL", NULL);
299 if (ret != GNUTLS_E_SUCCESS)
300 {
301 dolog(LOG_ERR, "TLS set default priority failed: %s (%d)\n", gnutls_strerror(ret), ret);
302 gnutls_deinit(sock->session);
303 free(sock);
304 return NULL;
305 }
d98f6a46
SS
306
307 /* Configure the x509 credentials for the current session */
61a319c2
BP
308 ret = gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, g_aiccu->tls_cred);
309 if (ret != GNUTLS_E_SUCCESS)
310 {
311 dolog(LOG_ERR, "TLS credentials set failed: %s (%d)\n", gnutls_strerror(ret), ret);
312 gnutls_deinit(sock->session);
313 free(sock);
314 return NULL;
315 }
d98f6a46
SS
316
317#endif /* AICCU_GNUTLS*/
318
319 return sock;
320}
321
322void sock_free(TLSSOCKET sock)
323{
324 if (!sock) return;
325
326#ifdef AICCU_GNUTLS
327 if (sock->tls_active)
328 {
329 sock->tls_active = false;
330 gnutls_bye(sock->session, GNUTLS_SHUT_RDWR);
331 }
332#endif /* AICCU_GNUTLS*/
333
334 if (sock->socket >= 0)
335 {
336 /* Stop communications */
337 shutdown(sock->socket, SHUT_RDWR);
338 closesocket(sock->socket);
339 sock->socket = -1;
340 }
341
342#ifdef AICCU_GNUTLS
343 gnutls_deinit(sock->session);
344#endif /* AICCU_GNUTLS*/
345
346 free(sock);
347}
348
349/* Connect this client to a server */
350TLSSOCKET connect_client(const char *hostname, const char *service, int family, int socktype)
351{
352 TLSSOCKET sock;
353 struct addrinfo hints, *res, *ressave;
354
355 sock = sock_alloc();
356 if (!sock) return NULL;
357
358 memset(&hints, 0, sizeof(struct addrinfo));
359 hints.ai_family = family;
360 hints.ai_socktype = socktype;
361
362 if (getaddrinfo(hostname, service, &hints, &res) != 0)
363 {
364 dolog(LOG_ERR, "Couldn't resolve host %s, service %s\n", hostname, service);
365 sock_free(sock);
366 return NULL;
367 }
368
369 ressave = res;
370
371 while (res)
372 {
373 sock->socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
374 if (sock->socket == -1) continue;
375 if (connect(sock->socket, res->ai_addr, (unsigned int)res->ai_addrlen) == 0) break;
376 closesocket(sock->socket);
377 sock->socket = -1;
378 res = res->ai_next;
379 }
380
381 freeaddrinfo(ressave);
382
383 if (sock->socket == -1)
384 {
385 sock_free(sock);
386 sock = NULL;
387 }
388
389 return sock;
390}
391
392TLSSOCKET listen_server(const char *description, const char *hostname, const char *service, int family, int socktype)
393{
394 struct addrinfo hints, *res, *ressave;
395 int n;
396 TLSSOCKET sock;
397 socklen_t on = 1;
398/*
399 D(dolog(LOG_DEBUG, "[%s] Trying to get socket for [%s]:%s over %s (%d) using %s (%d)\n",
400 description, hostname, service,
401 family == AF_INET ? "IPv4" : (family == AF_INET6 ? "IPv6" : "??"),
402 family,
403 socktype == IPPROTO_UDP ? "UDP" : (socktype == IPPROTO_TCP ? "TCP" : "??"),
404 socktype);)
405*/
406 sock = sock_alloc();
407 if (!sock) return NULL;
408
409 memset(&hints, 0, sizeof(struct addrinfo));
410
411 /* AI_PASSIVE flag: the resulting address is used to bind
412 to a socket for accepting incoming connections.
413 So, when the hostname==NULL, getaddrinfo function will
414 return one entry per allowed protocol family containing
415 the unspecified address for that family. */
416
417 hints.ai_flags = AI_PASSIVE;
418 hints.ai_family = family;
419 hints.ai_socktype = socktype;
420
421 n = getaddrinfo(hostname, service, &hints, &res);
422 if (n < 0)
423 {
424 dolog(LOG_ERR, "[%s] listen_server setup: getaddrinfo error: %s\n", description, gai_strerror(n));
425 sock_free(sock);
426 return NULL;
427 }
428
429 ressave=res;
430
431 /* Try to open socket with each address getaddrinfo returned,
432 until we get one valid listening socket. */
433 while (res)
434 {
435 sock->socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
436 if (!(sock->socket < 0))
437 {
438 setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
439 if (bind(sock->socket, res->ai_addr, (unsigned int)res->ai_addrlen) == 0) break;
440 closesocket(sock->socket);
441 sock->socket = -1;
442 }
443 res = res->ai_next;
444 }
445
446 freeaddrinfo(ressave);
447
448 if (sock->socket < 0)
449 {
450 dolog(LOG_ERR, "[%s] listen setup: socket error: could not open socket\n", description);
451 sock_free(sock);
452 return NULL;
453 }
454
455 if (listen(sock->socket, LISTEN_QUEUE) == -1)
456 {
457 dolog(LOG_ERR, "[%s] listen setup: socket error: could not listen on socket\n", description);
458 sock_free(sock);
459 return NULL;
460 }
461
462 dolog(LOG_INFO, "[%s] Listening on [%s]:%s\n", description, hostname, service);
463
464 return sock;
465}
466
467/*
468 * Put a socket into TLS mode
469 */
470#ifdef AICCU_GNUTLS
471bool sock_gotls(TLSSOCKET sock)
472{
473 int ret = 0;
474
475 if (!sock) return false;
476
477 if (sock->tls_active)
478 {
479 dolog(LOG_ERR, "Can't go into TLS mode twice!?\n");
480 return false;
481 }
482
483 /* Set the transport */
484 gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr)sock->socket);
485
486 /* Perform the TLS handshake */
487 ret = gnutls_handshake(sock->session);
488 if (ret < 0)
489 {
490 dolog(LOG_ERR, "TLS Handshake failed: %s (%d)\n", gnutls_strerror(ret), ret);
491 return false;
492 }
493
ddeba48a 494 dolog(LOG_DEBUG, "TLS Handshake completed successfully\n");
d98f6a46
SS
495
496 sock->tls_active = true;
497 return true;
498}
499#endif
500
501/* Count the number of fields in <s> */
502unsigned int countfields(char *s)
503{
504 int n = 1, i;
505 if (s == NULL) return 0;
506 for (i=0; s[i] != '\0'; i++) if (s[i] == ' ') n++;
507 return n;
508}
509
510/*
511 * Copy field <n> of string <s> into <buf> with a maximum of buflen
512 * First field is 1
513 */
514bool copyfield(char *s, unsigned int n, char *buf, unsigned int buflen)
515{
516 unsigned int begin = 0, i=0;
517
518 /* Clear the buffer */
519 memset(buf, 0, buflen);
520
521 while (s[i] != '\0')
522 {
523 n--;
524 begin = i;
525
526 /* Find next delimiter */
527 for (; s[i] != '\0' && s[i] != ' '; i++);
528
529 if (n == 0)
530 {
531 i-=begin;
532 strncpy(buf, s+begin, i > buflen ? buflen : i);
533 /* dolog(LOG_DEBUG, "copyfield() : '%s', begin = %d, len = %d\n", buf, begin, i); */
534 return true;
535 }
536
537 i++;
538 }
539 dolog(LOG_WARNING, "copyfield() - Field %u didn't exist in '%s'\n", n, s);
540 return false;
541}
542
543bool parseline(char *line, const char *split, struct pl_rule *rules, void *data)
544{
545 unsigned int r, len;
546 char *end = NULL, *val = NULL, *p = NULL;
547 void *store;
548
549 /* Chop off \n and \r and white space */
550 p = &line[strlen(line)-1];
551 while ( p >= line && (
552 *p == '\n' ||
553 *p == '\r' ||
554 *p == '\t' ||
555 *p == ' ')) *p-- = '\0';
556
557 /* Ignore comments and emtpy lines */
558 if ( strlen(line) == 0 ||
559 line[0] == '#' ||
560 line[0] == ';' ||
561 (line[0] == '/' && line[1] == '/'))
562 {
563 return true;
564 }
565
566 /* Get the end of the first argument */
567 p = line;
568 end = &line[strlen(line)-1];
569 /* Skip until whitespace */
570 while ( p < end &&
571 strncmp(p, split, strlen(split)) != 0) p++;
572 /* Terminate this argument */
573 *p = '\0';
574 p++;
575
576 /* Skip whitespace */
577 while ( p < end &&
578 *p == ' ' &&
579 *p == '\t') p++;
580
581 /* Start of the value */
582 val = p+(strlen(split)-1);
583
584 /* If starting with quotes, skip until next quote */
585 if (*p == '"' || *p == '\'')
586 {
587 p++;
588 /* Find next quote */
589 while (p <= end &&
590 *p != *val &&
591 *p != '\0') p++;
592 /* Terminate */
593 *p = '\0';
594 /* Skip the first quote */
595 val++;
596 }
597 /* Otherwise it is already terminated above */
598
599 /* Walk through all the rules */
600 for (r = 0; rules[r].type != PLRT_END; r++)
601 {
602 len = (int)strlen(rules[r].title);
603 if (strncmp(line, rules[r].title, len) != 0) continue;
604
605 store = (void *)((char *)data + rules[r].offset);
606
607 switch (rules[r].type)
608 {
609 case PLRT_STRING:
610 if (*((char **)store)) free(*((char **)store));
611 *((char **)store) = strdup(val);
612 break;
613
614 case PLRT_INTEGER:
615 *((uint32_t *)store) = atoi(val);
616 break;
617
618 case PLRT_BOOL:
619 if ( strcmp(val, "yes") == 0 ||
620 strcmp(val, "true") == 0)
621 {
622 *((bool *)store) = true;
623 }
624 else if (strcmp(val, "no") == 0 ||
625 strcmp(val, "false") == 0)
626 {
627 *((bool *)store) = false;
628 }
629 else
630 {
631 dolog(LOG_WARNING, "Unknown boolean value \"%s\" for option \"%s\"\n", val, rules[r].title);
632 }
633 break;
634
635 case PLRT_IPV4:
636 inet_pton(AF_INET, val, store);
637 break;
638
639 case PLRT_IPV6:
640 inet_pton(AF_INET6, val, store);
641 break;
642
643 case PLRT_END:
644 return false;
645 }
646 return true;
647 }
648 return false;
649}
650
651/*
652 * MD5 a string
653 * sSignature's size MUST be 32 bytes!
654 */
655void MD5String(const char *sString, char *sSignature, unsigned int siglen)
656{
657 struct MD5Context md5c;
658 unsigned char signature[16];
659 unsigned int i;
660
661 if (siglen < 32) return;
662
663 /* Initialize MD5 structure */
664 MD5Init(&md5c);
665 /* Calculate MD5 of the string */
666 MD5Update(&md5c, (unsigned char *)sString, (unsigned int)strlen(sString));
667 MD5Final(signature, &md5c);
668
669 memset(sSignature, 0, siglen);
670
671 for (i=0; i < sizeof(signature); i++)
672 {
673 snprintf(&sSignature[i*2], 3, "%02x", signature[i]);
674 }
675}
676
677#ifdef _AIX
678/* AIX doesn't have vsyslog() thus we implement it here */
679void vsyslog(int priority, const char *format, va_list ap)
680{
681 char buf[1024];
682 vsnprintf(buf, sizeof(buf), format, ap);
683 syslog(priority, buf);
684}
685#endif
686
687#ifdef _WIN32
688const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
689{
690 if (af == AF_INET)
691 {
692 struct sockaddr_in in;
693 memset(&in, 0, sizeof(in));
694 in.sin_family = AF_INET;
695 memcpy(&in.sin_addr, src, sizeof(struct in_addr));
696 getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
697 return dst;
698 }
699 else if (af == AF_INET6)
700 {
701 struct sockaddr_in6 in;
702 memset(&in, 0, sizeof(in));
703 in.sin6_family = AF_INET6;
704 memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
705 getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
706 return dst;
707 }
708 return NULL;
709}
710
711int inet_pton(int af, const char *src, void *dst)
712{
713 struct addrinfo hints, *res, *ressave;
714
715 memset(&hints, 0, sizeof(struct addrinfo));
716 hints.ai_family = af;
717
718 if (getaddrinfo(src, NULL, &hints, &res) != 0)
719 {
720 dolog(LOG_ERR, "Couldn't resolve host %s\n", src);
721 return -1;
722 }
723
724 ressave = res;
725
726 while (res)
727 {
728 /* Check if AF is correct */
729 if (res->ai_family != af)
730 {
731 res = res->ai_next;
732 continue;
733 }
734
735 /* This is the one we want */
736 memcpy(dst, res->ai_addr, af == AF_INET6 ? sizeof(struct in_addr6) : sizeof(struct in_addr));
737
738 /* We only need one */
739 break;
740 }
741
742 freeaddrinfo(ressave);
743 return 0;
744}
745
746#endif