Imported Upstream version 20070115
[aiccu.git] / common / common.c
blob488c14567dccb598c5cbdfb1d35e5083d3e50a7e
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 **********************************************************/
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"
18 extern CAICCUApp theApp;
19 #endif
20 #endif
22 #include "aiccu.h"
23 #include "common.h"
25 /* getline debugging? */
27 #define E(x) x
29 #define E(x) {}
31 void dologA(int level, const char *fmt, va_list ap)
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;
39 #ifndef _WIN32
40 if (g_aiccu && g_aiccu->daemonize > 0) vsyslog(LOG_LOCAL7|level, fmt, ap);
41 else
43 vfprintf(stderr, fmt, ap);
44 fflush(stderr);
46 #else
47 vsnprintf(buf, sizeof(buf), fmt, ap);
49 #ifndef AICCU_CONSOLE
50 /* Use the debug facility */
51 OutputDebugString(buf);
53 /* Store it in a log file if we are running in verbose mode */
54 if (g_aiccu && g_aiccu->verbose)
56 char logfile[1024];
57 FILE *f;
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)
66 fwrite(buf, strlen(buf), 1, f);
67 fclose(f);
72 * Always store the last message
73 * which can be displayed as errors etc.
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 */
87 void dolog(int level, const char *fmt, ...)
89 va_list ap;
90 va_start(ap, fmt);
91 dologA(level, fmt, ap);
92 va_end(ap);
95 /*
96 * Check if an address is RFC1918 based
97 * This allows us to warn the user that they are behind a NAT
99 bool is_rfc1918(char *ipv4)
101 unsigned int addr = inet_addr(ipv4);
102 bool ret = false;
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;
113 dolog(LOG_DEBUG, "is_rfc1918(%s) = %s\n", ipv4, ret ? "yes" : "false");
115 return ret;
118 void sock_printf(TLSSOCKET sock, const char *fmt, ...)
120 char buf[2048];
121 unsigned int len = 0, done = 0;
122 int ret;
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
130 /* Format the string */
131 len = vsnprintf(buf, sizeof(buf), fmt, ap);
133 /* Send the line(s) over the network */
135 while (done < len)
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);
143 if (ret > 0) done+=ret;
144 else break;
147 /* Show this as debug output */
148 if (g_aiccu->verbose)
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);
157 va_end(ap);
160 extern char tic_buf[2048];
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.
167 int sock_getline(TLSSOCKET sock, char *rbuf, unsigned int rbuflen, unsigned int *filled, char *ubuf, unsigned int ubuflen)
169 unsigned int i;
171 if (!sock) return -1;
173 /* A closed socket? -> clear the buffer */
174 if (sock->socket == -1)
176 memset(rbuf, 0, rbuflen);
177 *filled = 0;
178 return -1;
181 /* Clear the caller supplied buffer, just in case */
182 memset(ubuf, 0, ubuflen);
184 for (;;)
186 E(dolog(LOG_DEBUG, "gl() - Filled %d\n", *filled);)
188 /* Did we still have something in the buffer? */
189 if (*filled > 0)
191 E(dolog(LOG_DEBUG, "gl() - Seeking newline\n");)
193 /* Walk to the end or until we reach a \n */
194 for (i=0; (i < (*filled-1)) && (rbuf[i] != '\n'); i++);
196 E(dolog(LOG_DEBUG, "gl() - Seeking newline - end\n");)
198 /* Did we find a newline? */
199 if (rbuf[i] == '\n')
201 E(dolog(LOG_DEBUG, "gl() - Found newline at %i\n", i+1);)
203 /* Newline with a Linefeed in front of it ? -> remove it */
204 if (rbuf[i] == '\n' && rbuf[i-1] == '\r')
206 E(dolog(LOG_DEBUG, "gl() - Removing LF\n");)
207 i--;
209 E(else dolog(LOG_DEBUG, "gl() - No LF\n");)
211 /* Copy this over to the caller */
212 memcpy(ubuf, rbuf, i);
214 E(dolog(LOG_DEBUG, "gl() - Copied %d bytes from %x to %x\n", i, rbuf, ubuf);)
216 /* Count the \r if it is there */
217 if (rbuf[i] == '\r') i++;
218 /* Count the \n */
219 i++;
221 /* filled = what is left in the buffer */
222 *filled -= i;
224 E(dolog(LOG_DEBUG, "gl() - %d bytes left in the buffer\n", *filled);)
226 /* Now move the rest of the buffer to the front */
227 if (*filled > 0) memmove(rbuf, &rbuf[i], *filled);
228 else *filled = 0;
230 /* Show this as debug output */
231 if (g_aiccu->verbose) dolog(LOG_DEBUG, "sock_getline() : \"%s\"\n", ubuf);
233 /* We got ourselves a line in 'buf' thus return to the caller */
234 return i;
238 E(dolog(LOG_DEBUG, "gl() - Trying to receive (max=%d)...\n", rbuflen-*filled-10);)
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);
247 E(dolog(LOG_DEBUG, "gl() - Received %d\n", i);)
249 /* Fail on errors */
250 if (i <= 0) return -1;
252 /* We got more filled space! */
253 *filled+=i;
255 /* Buffer overflow? */
256 if ( *filled >= (rbuflen-10) ||
257 *filled >= (ubuflen-10) )
259 dolog(LOG_ERR, "Buffer almost flowed over without receiving a newline\n");
260 return -1;
263 /* And try again in this loop ;) */
266 /* Never reached */
267 return -1;
270 TLSSOCKET sock_alloc(void);
271 TLSSOCKET sock_alloc(void)
273 #ifdef AICCU_GNUTLS
274 /* Allow connections to servers that have OpenPGP keys as well */
275 const int cert_type_priority[3] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 };
276 int ret;
277 #endif /* AICCU_GNUTLS*/
279 TLSSOCKET sock;
281 sock = (TLSSOCKET)malloc(sizeof(*sock));
282 if (!sock) return NULL;
284 sock->socket = -1;
286 #ifdef AICCU_GNUTLS
287 /* TLS is not active yet (use sock_gotls() for that) */
288 sock->tls_active = false;
290 /* Initialize TLS session */
291 ret = gnutls_init(&sock->session, GNUTLS_CLIENT);
292 if (ret != 0)
294 dolog(LOG_ERR, "TLS Init failed: %s (%d)\n", gnutls_strerror(ret), ret);
295 free(sock);
296 return NULL;
299 /* Use default priorities */
300 gnutls_set_default_priority(sock->session);
301 /* XXX: Return value is not documented in GNUTLS documentation! */
303 gnutls_certificate_type_set_priority(sock->session, cert_type_priority);
304 /* XXX: Return value is not documented in GNUTLS documentation! */
306 /* Configure the x509 credentials for the current session */
307 gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, g_aiccu->tls_cred);
308 /* XXX: Return value is not documented in GNUTLS documentation! */
310 #endif /* AICCU_GNUTLS*/
312 return sock;
315 void sock_free(TLSSOCKET sock)
317 if (!sock) return;
319 #ifdef AICCU_GNUTLS
320 if (sock->tls_active)
322 sock->tls_active = false;
323 gnutls_bye(sock->session, GNUTLS_SHUT_RDWR);
325 #endif /* AICCU_GNUTLS*/
327 if (sock->socket >= 0)
329 /* Stop communications */
330 shutdown(sock->socket, SHUT_RDWR);
331 closesocket(sock->socket);
332 sock->socket = -1;
335 #ifdef AICCU_GNUTLS
336 gnutls_deinit(sock->session);
337 #endif /* AICCU_GNUTLS*/
339 free(sock);
342 /* Connect this client to a server */
343 TLSSOCKET connect_client(const char *hostname, const char *service, int family, int socktype)
345 TLSSOCKET sock;
346 struct addrinfo hints, *res, *ressave;
348 sock = sock_alloc();
349 if (!sock) return NULL;
351 memset(&hints, 0, sizeof(struct addrinfo));
352 hints.ai_family = family;
353 hints.ai_socktype = socktype;
355 if (getaddrinfo(hostname, service, &hints, &res) != 0)
357 dolog(LOG_ERR, "Couldn't resolve host %s, service %s\n", hostname, service);
358 sock_free(sock);
359 return NULL;
362 ressave = res;
364 while (res)
366 sock->socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
367 if (sock->socket == -1) continue;
368 if (connect(sock->socket, res->ai_addr, (unsigned int)res->ai_addrlen) == 0) break;
369 closesocket(sock->socket);
370 sock->socket = -1;
371 res = res->ai_next;
374 freeaddrinfo(ressave);
376 if (sock->socket == -1)
378 sock_free(sock);
379 sock = NULL;
382 return sock;
385 TLSSOCKET listen_server(const char *description, const char *hostname, const char *service, int family, int socktype)
387 struct addrinfo hints, *res, *ressave;
388 int n;
389 TLSSOCKET sock;
390 socklen_t on = 1;
392 D(dolog(LOG_DEBUG, "[%s] Trying to get socket for [%s]:%s over %s (%d) using %s (%d)\n",
393 description, hostname, service,
394 family == AF_INET ? "IPv4" : (family == AF_INET6 ? "IPv6" : "??"),
395 family,
396 socktype == IPPROTO_UDP ? "UDP" : (socktype == IPPROTO_TCP ? "TCP" : "??"),
397 socktype);)
399 sock = sock_alloc();
400 if (!sock) return NULL;
402 memset(&hints, 0, sizeof(struct addrinfo));
404 /* AI_PASSIVE flag: the resulting address is used to bind
405 to a socket for accepting incoming connections.
406 So, when the hostname==NULL, getaddrinfo function will
407 return one entry per allowed protocol family containing
408 the unspecified address for that family. */
410 hints.ai_flags = AI_PASSIVE;
411 hints.ai_family = family;
412 hints.ai_socktype = socktype;
414 n = getaddrinfo(hostname, service, &hints, &res);
415 if (n < 0)
417 dolog(LOG_ERR, "[%s] listen_server setup: getaddrinfo error: %s\n", description, gai_strerror(n));
418 sock_free(sock);
419 return NULL;
422 ressave=res;
424 /* Try to open socket with each address getaddrinfo returned,
425 until we get one valid listening socket. */
426 while (res)
428 sock->socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
429 if (!(sock->socket < 0))
431 setsockopt(sock->socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
432 if (bind(sock->socket, res->ai_addr, (unsigned int)res->ai_addrlen) == 0) break;
433 closesocket(sock->socket);
434 sock->socket = -1;
436 res = res->ai_next;
439 freeaddrinfo(ressave);
441 if (sock->socket < 0)
443 dolog(LOG_ERR, "[%s] listen setup: socket error: could not open socket\n", description);
444 sock_free(sock);
445 return NULL;
448 if (listen(sock->socket, LISTEN_QUEUE) == -1)
450 dolog(LOG_ERR, "[%s] listen setup: socket error: could not listen on socket\n", description);
451 sock_free(sock);
452 return NULL;
455 dolog(LOG_INFO, "[%s] Listening on [%s]:%s\n", description, hostname, service);
457 return sock;
461 * Put a socket into TLS mode
463 #ifdef AICCU_GNUTLS
464 bool sock_gotls(TLSSOCKET sock)
466 int ret = 0;
468 if (!sock) return false;
470 if (sock->tls_active)
472 dolog(LOG_ERR, "Can't go into TLS mode twice!?\n");
473 return false;
476 /* Set the transport */
477 gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr)sock->socket);
479 /* Perform the TLS handshake */
480 ret = gnutls_handshake(sock->session);
481 if (ret < 0)
483 dolog(LOG_ERR, "TLS Handshake failed: %s (%d)\n", gnutls_strerror(ret), ret);
484 return false;
487 dolog(LOG_DEBUG, "TLS Handshake completed succesfully\n");
489 sock->tls_active = true;
490 return true;
492 #endif
494 /* Count the number of fields in <s> */
495 unsigned int countfields(char *s)
497 int n = 1, i;
498 if (s == NULL) return 0;
499 for (i=0; s[i] != '\0'; i++) if (s[i] == ' ') n++;
500 return n;
504 * Copy field <n> of string <s> into <buf> with a maximum of buflen
505 * First field is 1
507 bool copyfield(char *s, unsigned int n, char *buf, unsigned int buflen)
509 unsigned int begin = 0, i=0;
511 /* Clear the buffer */
512 memset(buf, 0, buflen);
514 while (s[i] != '\0')
516 n--;
517 begin = i;
519 /* Find next delimiter */
520 for (; s[i] != '\0' && s[i] != ' '; i++);
522 if (n == 0)
524 i-=begin;
525 strncpy(buf, s+begin, i > buflen ? buflen : i);
526 /* dolog(LOG_DEBUG, "copyfield() : '%s', begin = %d, len = %d\n", buf, begin, i); */
527 return true;
530 i++;
532 dolog(LOG_WARNING, "copyfield() - Field %u didn't exist in '%s'\n", n, s);
533 return false;
536 bool parseline(char *line, const char *split, struct pl_rule *rules, void *data)
538 unsigned int r, len;
539 char *end = NULL, *val = NULL, *p = NULL;
540 void *store;
542 /* Chop off \n and \r and white space */
543 p = &line[strlen(line)-1];
544 while ( p >= line && (
545 *p == '\n' ||
546 *p == '\r' ||
547 *p == '\t' ||
548 *p == ' ')) *p-- = '\0';
550 /* Ignore comments and emtpy lines */
551 if ( strlen(line) == 0 ||
552 line[0] == '#' ||
553 line[0] == ';' ||
554 (line[0] == '/' && line[1] == '/'))
556 return true;
559 /* Get the end of the first argument */
560 p = line;
561 end = &line[strlen(line)-1];
562 /* Skip until whitespace */
563 while ( p < end &&
564 strncmp(p, split, strlen(split)) != 0) p++;
565 /* Terminate this argument */
566 *p = '\0';
567 p++;
569 /* Skip whitespace */
570 while ( p < end &&
571 *p == ' ' &&
572 *p == '\t') p++;
574 /* Start of the value */
575 val = p+(strlen(split)-1);
577 /* If starting with quotes, skip until next quote */
578 if (*p == '"' || *p == '\'')
580 p++;
581 /* Find next quote */
582 while (p <= end &&
583 *p != *val &&
584 *p != '\0') p++;
585 /* Terminate */
586 *p = '\0';
587 /* Skip the first quote */
588 val++;
590 /* Otherwise it is already terminated above */
592 /* Walk through all the rules */
593 for (r = 0; rules[r].type != PLRT_END; r++)
595 len = (int)strlen(rules[r].title);
596 if (strncmp(line, rules[r].title, len) != 0) continue;
598 store = (void *)((char *)data + rules[r].offset);
600 switch (rules[r].type)
602 case PLRT_STRING:
603 if (*((char **)store)) free(*((char **)store));
604 *((char **)store) = strdup(val);
605 break;
607 case PLRT_INTEGER:
608 *((uint32_t *)store) = atoi(val);
609 break;
611 case PLRT_BOOL:
612 if ( strcmp(val, "yes") == 0 ||
613 strcmp(val, "true") == 0)
615 *((bool *)store) = true;
617 else if (strcmp(val, "no") == 0 ||
618 strcmp(val, "false") == 0)
620 *((bool *)store) = false;
622 else
624 dolog(LOG_WARNING, "Unknown boolean value \"%s\" for option \"%s\"\n", val, rules[r].title);
626 break;
628 case PLRT_IPV4:
629 inet_pton(AF_INET, val, store);
630 break;
632 case PLRT_IPV6:
633 inet_pton(AF_INET6, val, store);
634 break;
636 case PLRT_END:
637 return false;
639 return true;
641 return false;
645 * MD5 a string
646 * sSignature's size MUST be 32 bytes!
648 void MD5String(const char *sString, char *sSignature, unsigned int siglen)
650 struct MD5Context md5c;
651 unsigned char signature[16];
652 unsigned int i;
654 if (siglen < 32) return;
656 /* Initialize MD5 structure */
657 MD5Init(&md5c);
658 /* Calculate MD5 of the string */
659 MD5Update(&md5c, (unsigned char *)sString, (unsigned int)strlen(sString));
660 MD5Final(signature, &md5c);
662 memset(sSignature, 0, siglen);
664 for (i=0; i < sizeof(signature); i++)
666 snprintf(&sSignature[i*2], 3, "%02x", signature[i]);
670 #ifdef _AIX
671 /* AIX doesn't have vsyslog() thus we implement it here */
672 void vsyslog(int priority, const char *format, va_list ap)
674 char buf[1024];
675 vsnprintf(buf, sizeof(buf), format, ap);
676 syslog(priority, buf);
678 #endif
680 #ifdef _WIN32
681 const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
683 if (af == AF_INET)
685 struct sockaddr_in in;
686 memset(&in, 0, sizeof(in));
687 in.sin_family = AF_INET;
688 memcpy(&in.sin_addr, src, sizeof(struct in_addr));
689 getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
690 return dst;
692 else if (af == AF_INET6)
694 struct sockaddr_in6 in;
695 memset(&in, 0, sizeof(in));
696 in.sin6_family = AF_INET6;
697 memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
698 getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
699 return dst;
701 return NULL;
704 int inet_pton(int af, const char *src, void *dst)
706 struct addrinfo hints, *res, *ressave;
708 memset(&hints, 0, sizeof(struct addrinfo));
709 hints.ai_family = af;
711 if (getaddrinfo(src, NULL, &hints, &res) != 0)
713 dolog(LOG_ERR, "Couldn't resolve host %s\n", src);
714 return -1;
717 ressave = res;
719 while (res)
721 /* Check if AF is correct */
722 if (res->ai_family != af)
724 res = res->ai_next;
725 continue;
728 /* This is the one we want */
729 memcpy(dst, res->ai_addr, af == AF_INET6 ? sizeof(struct in_addr6) : sizeof(struct in_addr));
731 /* We only need one */
732 break;
735 freeaddrinfo(ressave);
736 return 0;
739 #endif