unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

crashreport.c (21354B)

      1 /* UnrealIRCd crash reporter code.
      2  * (C) Copyright 2015-2019 Bram Matthys ("Syzop") and the UnrealIRCd Team.
      3  * License: GPLv2 or later
      4  */
      5 
      6 #include "unrealircd.h"
      7 #ifdef _WIN32
      8 extern void StartUnrealAgain(void);
      9 #endif
     10 #include "version.h"
     11 
     12 extern char *getosname(void);
     13 
     14 char *find_best_coredump(void)
     15 {
     16 	static char best_fname[512];
     17 	time_t best_time = 0, t;
     18 	struct dirent *dir;
     19 #ifndef _WIN32
     20 	DIR *fd = opendir(TMPDIR);
     21 
     22 	if (!fd)
     23 		return NULL;
     24 	
     25 	*best_fname = '\0';
     26 	
     27 	while ((dir = readdir(fd)))
     28 	{
     29 		char *fname = dir->d_name;
     30 		if (strstr(fname, "core") && !strstr(fname, ".so") &&
     31 		    !strstr(fname, ".conf") && !strstr(fname, ".txt") &&
     32 		    !strstr(fname, ".done"))
     33 		{
     34 			char buf[512];
     35 			
     36 			snprintf(buf, sizeof(buf), "%s/%s", TMPDIR, fname);
     37 			t = get_file_time(buf);
     38 			if (t && (t > best_time))
     39 			{
     40 				best_time = t;
     41 				strlcpy(best_fname, buf, sizeof(best_fname));
     42 			}
     43 		}
     44 	}
     45 	closedir(fd);
     46 #else
     47 	/* Windows */
     48 	WIN32_FIND_DATA hData;
     49 	HANDLE hFile;
     50 	
     51 	hFile = FindFirstFile("unrealircd.*.core", &hData);
     52 	if (hFile == INVALID_HANDLE_VALUE)
     53 		return NULL;
     54 	
     55 	do
     56 	{
     57 		char *fname = hData.cFileName;
     58 		if (!strstr(fname, ".done"))
     59 		{
     60 			char buf[512];
     61 			strlcpy(buf, fname, sizeof(buf));
     62 			t = get_file_time(buf);
     63 			if (t && (t > best_time))
     64 			{
     65 				best_time = t;
     66 				strlcpy(best_fname, buf, sizeof(best_fname));
     67 			}
     68 		}
     69 	} while (FindNextFile(hFile, &hData));
     70 	FindClose(hFile);
     71 #endif	
     72 	
     73 	if (*best_fname)
     74 		return best_fname;
     75 	
     76 	return NULL; /* none found */
     77 }
     78 
     79 /** Find the latest AddressSanitizer log file */
     80 char *find_best_asan_log(void)
     81 {
     82 #ifndef _WIN32
     83 	static char best_fname[512];
     84 	time_t best_time = 0, t;
     85 	struct dirent *dir;
     86 	DIR *fd = opendir(TMPDIR);
     87 
     88 	if (!fd)
     89 		return NULL;
     90 
     91 	*best_fname = '\0';
     92 
     93 	while ((dir = readdir(fd)))
     94 	{
     95 		char *fname = dir->d_name;
     96 		if (strstr(fname, "unrealircd_asan.") && !strstr(fname, ".so") &&
     97 		    !strstr(fname, ".conf") && !strstr(fname, ".txt") &&
     98 		    !strstr(fname, ".done"))
     99 		{
    100 			char buf[512];
    101 
    102 			snprintf(buf, sizeof(buf), "%s/%s", TMPDIR, fname);
    103 			t = get_file_time(buf);
    104 			if (t && (t > best_time))
    105 			{
    106 				best_time = t;
    107 				strlcpy(best_fname, buf, sizeof(best_fname));
    108 			}
    109 		}
    110 	}
    111 	closedir(fd);
    112 	return *best_fname ? best_fname : NULL;
    113 #else
    114 	return NULL;
    115 #endif
    116 }
    117 
    118 #define EL_AR_MAX MAXPARA
    119 char **explode(char *str, char *delimiter)
    120 {
    121 	static char *ret[EL_AR_MAX+1];
    122 	static char buf[1024];
    123 	char *p, *name;
    124 	int cnt = 0;
    125 	
    126 	memset(&ret, 0, sizeof(ret)); /* make sure all elements are NULL */
    127 	
    128 	strlcpy(buf, str, sizeof(buf));
    129 	for (name = strtoken(&p, buf, delimiter); name; name = strtoken(&p, NULL, delimiter))
    130 	{
    131 		ret[cnt++] = name;
    132 		if (cnt == EL_AR_MAX)
    133 			break;
    134 	}
    135 	ret[cnt] = NULL;
    136 	
    137 	return ret;
    138 }
    139 
    140 void crash_report_fix_libs(char *coredump, int *thirdpartymods)
    141 {
    142 #ifndef _WIN32
    143 	FILE *fd;
    144 	char cmd[512], buf[1024];
    145 
    146 	/* This is needed for this function to work, but we keep it since it's
    147 	 * useful in general to have the bug report in English as well.
    148 	 */
    149 	setenv("LANG", "C", 1);
    150 	setenv("LC_ALL", "C", 1);
    151 
    152 	snprintf(cmd, sizeof(cmd), "echo info sharedlibrary|gdb %s/unrealircd %s 2>&1",
    153 		BINDIR, coredump);
    154 
    155 	fd = popen(cmd, "r");
    156 	if (!fd)
    157 		return;
    158 
    159 	while((fgets(buf, sizeof(buf), fd)))
    160 	{
    161 		char *file, *path;
    162 		char target[512];
    163 		char **arr;
    164 
    165 		stripcrlf(buf);
    166 
    167 		if (strstr(buf, ".third."))
    168 		    *thirdpartymods = 1;
    169 
    170 		/* Output we are interested is something like this:
    171 		 * <many spaces>    No        /home/blabla/unrealircd/tmp/5114DF16.m_kick.so
    172 		 */
    173 		if (!strstr(buf, " No "))
    174 			continue;
    175 		
    176 		path = strchr(buf, '/');
    177 		if (!path)
    178 			continue;
    179 
    180 		if (!strstr(path, TMPDIR))
    181 			continue; /* we only care about our TMPDIR stuff */
    182 		
    183 		file = strrchr(path, '/');
    184 		if (!file)
    185 			continue;
    186 		file++;
    187 		
    188 		/* files have the following two formats:
    189 		 * 5BE7DF9.m_svsnline.so          for modules/m_svsnline.so
    190 		 * 300AA138.chanmodes.nokick.so   for modules/chanmodes/nokick.so
    191 		 */
    192 		arr = explode(file, ".");
    193 		if (!arr[3])
    194 			snprintf(target, sizeof(target), "%s/%s.%s", MODULESDIR, arr[1], arr[2]);
    195 		else
    196 			snprintf(target, sizeof(target), "%s/%s/%s.%s", MODULESDIR, arr[1], arr[2], arr[3]);
    197 		
    198 		if (!file_exists(target))
    199 		{
    200 			printf("WARNING: could not resolve %s: %s does not exist\n", path, target);
    201 		} else {
    202 			if (symlink(target, path) < 0)
    203 				printf("WARNING: could not create symlink %s -> %s\n", path, target);
    204 		}
    205 		
    206 	}
    207 	pclose(fd);
    208 #endif
    209 }
    210 
    211 int crash_report_backtrace(FILE *reportfd, char *coredump)
    212 {
    213 	FILE *fd;
    214 	char cmd[512], buf[1024];
    215 	int n;
    216 
    217 #ifndef _WIN32
    218 	snprintf(buf, sizeof(buf), "%s/gdb.commands", TMPDIR);
    219 	fd = fopen(buf, "w");
    220 	if (!fd)
    221 	{
    222 		printf("ERROR: Could not write to %s.\n", buf);
    223 		return 0;
    224 	}
    225 	fprintf(fd, "frame\n"
    226 	            "echo \\n\n"
    227 	            "list\n"
    228 	            "echo \\n\n"
    229 	            "x/s our_mod_version\n"
    230 	            "echo \\n\n"
    231 	            "x/s backupbuf\n"
    232 	            "echo \\n\n"
    233 	            "bt\n"
    234 	            "echo \\n\n"
    235 	            "bt full\n"
    236 	            "echo \\n\n"
    237 	            "quit\n");
    238 	fclose(fd);
    239 
    240 	
    241 	snprintf(cmd, sizeof(cmd), "gdb -batch -x %s %s/unrealircd %s 2>&1",
    242 		buf, BINDIR, coredump);
    243 	
    244 	fd = popen(cmd, "r");
    245 	if (!fd)
    246 		return 0;
    247 	
    248 	fprintf(reportfd, "START OF BACKTRACE\n");
    249 	while((fgets(buf, sizeof(buf), fd)))
    250 	{
    251 		stripcrlf(buf);
    252 		fprintf(reportfd, " %s\n", buf);
    253 	}
    254 	n = pclose(fd);
    255 
    256 	fprintf(reportfd, "END OF BACKTRACE\n");
    257 
    258 	if (WEXITSTATUS(n) == 127)
    259 		return 0;
    260 
    261 	return 1;
    262 #else
    263 	fd = fopen(coredump, "r");
    264 	if (!fd)
    265 		return 0;
    266 	fprintf(reportfd, "START OF CRASH DUMP\n");
    267 	while((fgets(buf, sizeof(buf), fd)))
    268 	{
    269 		stripcrlf(buf);
    270 		fprintf(reportfd, " %s\n", buf);
    271 	}
    272 	fclose(fd);
    273 	fprintf(reportfd, "END OF CRASH DUMP\n");
    274 	return 1;
    275 #endif
    276 }
    277 
    278 int crash_report_asan_log(FILE *reportfd, char *coredump)
    279 {
    280 #ifndef _WIN32
    281 	time_t coretime, asantime;
    282 	FILE *fd;
    283 	char buf[1024];
    284 	char *asan_log = find_best_asan_log();
    285 	int n;
    286 
    287 	if (!asan_log)
    288 		return 0;
    289 
    290 	coretime = get_file_time(coredump);
    291 	asantime = get_file_time(asan_log);
    292 
    293 	fprintf(reportfd, "ASan log file found '%s' which is %ld seconds older than core file\n",
    294 		asan_log,
    295 		(long)((long)(coretime) - (long)asantime));
    296 
    297 	fd = fopen(asan_log, "r");
    298 	if (!fd)
    299 	{
    300 		fprintf(reportfd, "Could not open ASan log (%s)\n", strerror(errno));
    301 		return 0;
    302 	}
    303 	fprintf(reportfd, "START OF ASAN LOG\n");
    304 	while((fgets(buf, sizeof(buf), fd)))
    305 	{
    306 		stripcrlf(buf);
    307 		fprintf(reportfd, " %s\n", buf);
    308 	}
    309 	n = fclose(fd);
    310 	fprintf(reportfd, "END OF ASAN LOG\n");
    311 
    312 	if (WEXITSTATUS(n) == 127)
    313 		return 0;
    314 
    315 	return 1;
    316 #else
    317 	return 0;
    318 #endif
    319 }
    320 
    321 void crash_report_header(FILE *reportfd, char *coredump)
    322 {
    323 	time_t t;
    324 	
    325 	fprintf(reportfd, "== UNREALIRCD CRASH REPORT ==\n"
    326 	                  "\n"
    327 	                  "SYSTEM INFORMATION:\n");
    328 	
    329 	fprintf(reportfd, "UnrealIRCd version: %s\n", VERSIONONLY);
    330 #if defined(__VERSION__)
    331 	fprintf(reportfd, "          Compiler: %s\n", __VERSION__);
    332 #endif
    333 	
    334 	fprintf(reportfd, "  Operating System: %s\n", MYOSNAME);
    335 
    336 	
    337 	fprintf(reportfd, "Using core file: %s\n", coredump);
    338 	
    339 	t = get_file_time(coredump);
    340 	if (t != 0)
    341 	{
    342 		fprintf(reportfd, "Crash date/time: %s\n", myctime(t) ? myctime(t) : "???");
    343 		fprintf(reportfd, " Crash secs ago: %ld\n",
    344 			(long)(time(NULL) - t));
    345 	} else {
    346 		fprintf(reportfd, "Crash date/time: UNKNOWN\n");
    347 		fprintf(reportfd, " Crash secs ago: UNKNOWN\n");
    348 	}
    349 	
    350 	fprintf(reportfd, "\n");
    351 }
    352 
    353 /** Checks if the binary is newer than the coredump.
    354  * If that's the case (1) then the core dump is likely not very usable.
    355  */
    356 int corefile_vs_binary_mismatch(char *coredump)
    357 {
    358 #ifndef _WIN32
    359 	time_t core, binary;
    360 	char fname[512];
    361 	
    362 	snprintf(fname, sizeof(fname), "%s/unrealircd", BINDIR);
    363 	
    364 	core = get_file_time(coredump);
    365 	binary = get_file_time(fname);
    366 	
    367 	if (!core || !binary)
    368 		return 0; /* don't know then */
    369 	
    370 	if (binary > core)
    371 		return 1; /* yup, mismatch ;/ */
    372 	
    373 	return 0; /* GOOD! */
    374 #else
    375 	return 0; /* guess we don't check this on Windows? Or will we check UnrealIRCd.exe... hmm.. yeah maybe good idea */
    376 #endif
    377 }
    378 
    379 int attach_file(FILE *fdi, FILE *fdo)
    380 {
    381 	char binbuf[60];
    382 	char printbuf[100];
    383 	size_t n, total = 0;
    384 
    385 	fprintf(fdo, "\n*** ATTACHMENT ****\n");
    386 	while((n = fread(binbuf, 1, sizeof(binbuf), fdi)) > 0)
    387 	{
    388 		b64_encode(binbuf, n, printbuf, sizeof(printbuf));
    389 		fprintf(fdo, "%s\n", printbuf);
    390 
    391 		total += strlen(printbuf);
    392 
    393 		if (total > 15000000)
    394 			return 0; /* Safety limit */
    395 	}
    396 
    397 	fprintf(fdo, "*** END OF ATTACHMENT ***\n");
    398 	return 1;
    399 }
    400 
    401 /** Figure out the libc library name (.so file), copy it to tmp/
    402  * to include it in the bug report. This can improve the backtrace
    403  * a lot (read: make it actually readable / useful) in case we
    404  * crash in a libc function.
    405  */
    406 char *copy_libc_so(void)
    407 {
    408 #ifdef _WIN32
    409 	return "";
    410 #else
    411 	FILE *fd;
    412 	char buf[1024];
    413 	static char ret[512];
    414 	char *basename = NULL, *libcname = NULL, *p, *start;
    415 
    416 	snprintf(buf, sizeof(buf), "ldd %s/unrealircd 2>/dev/null", BINDIR);
    417 	fd = popen(buf, "r");
    418 	if (!fd)
    419 		return "";
    420 
    421 	while ((fgets(buf, sizeof(buf), fd)))
    422 	{
    423 		stripcrlf(buf);
    424 		p = strstr(buf, "libc.so");
    425 		if (!p)
    426 			continue;
    427 		basename = p;
    428 		p = strchr(p, ' ');
    429 		if (!p)
    430 			continue;
    431 		*p++ = '\0';
    432 		p = strstr(p, "=> ");
    433 		if (!p)
    434 			continue;
    435 		start = p += 3; /* skip "=> " */
    436 		p = strchr(start, ' ');
    437 		if (!p)
    438 			continue;
    439 		*p = '\0';
    440 		libcname = start;
    441 		break;
    442 	}
    443 	pclose(fd);
    444 
    445 	if (!basename || !libcname)
    446 		return ""; /* not found, weird */
    447 
    448 	snprintf(ret, sizeof(ret), "%s/%s", TMPDIR, basename);
    449 	if (!unreal_copyfile(libcname, ret))
    450 		return ""; /* copying failed */
    451 
    452 	return ret;
    453 #endif
    454 }
    455 int attach_coredump(FILE *fdo, char *coredump)
    456 {
    457 	FILE *fdi;
    458 	char fname[512];
    459 	char *libcname = copy_libc_so();
    460 
    461 #ifndef _WIN32
    462 	/* On *NIX we create a .tar.bz2 / .tar.gz (may take a couple of seconds) */
    463 	printf("Please wait...\n");
    464 	snprintf(fname, sizeof(fname), "tar c %s/unrealircd %s %s %s 2>/dev/null|(bzip2 || gzip) 2>/dev/null",
    465 		BINDIR, coredump, MODULESDIR, libcname);
    466 
    467 	fdi = popen(fname, "r");
    468 #else
    469 	/* On Windows we attach de .mdmp, the small minidump file */
    470 	strlcpy(fname, coredump, sizeof(fname));
    471 	if (strlen(fname) > 5)
    472 		fname[strlen(fname)-5] = '\0'; /* cut off the '.core' part */
    473 	strlcat(fname, ".mdmp", sizeof(fname)); /* and add '.mdmp' */
    474 	fprintf(fdo, "Windows MINIDUMP: %s\n", fname);
    475 	fdi = fopen(fname, "rb");
    476 #endif
    477 	if (!fdi)
    478 		return 0;
    479 
    480 	attach_file(fdi, fdo);
    481 
    482 #ifndef _WIN32
    483 	pclose(fdi);
    484 #else
    485 	fclose(fdi);
    486 #endif
    487 	return 1;
    488 }
    489 
    490 char *generate_crash_report(char *coredump, int *thirdpartymods)
    491 {
    492 	static char reportfname[512];
    493 	FILE *reportfd;
    494 
    495 	*thirdpartymods = 0;
    496 
    497 	if (coredump == NULL)
    498 		coredump = find_best_coredump();
    499 	
    500 	if (coredump == NULL)
    501 		return NULL; /* nothing available */
    502 
    503 	if (corefile_vs_binary_mismatch(coredump))
    504 		return NULL;
    505 	
    506 	snprintf(reportfname, sizeof(reportfname), "%s/crash.report.%s.%ld.txt",
    507 		TMPDIR, unreal_getfilename(coredump), (long)time(NULL));
    508 
    509 	reportfd = fopen(reportfname, "w");
    510 	if (!reportfd)
    511 	{
    512 		printf("ERROR: could not open '%s' for writing\n", reportfname);
    513 		return NULL;
    514 	}
    515 
    516 	crash_report_header(reportfd, coredump);
    517 	crash_report_fix_libs(coredump, thirdpartymods);
    518 	
    519 	crash_report_backtrace(reportfd, coredump);
    520 	crash_report_asan_log(reportfd, coredump);
    521 	attach_coredump(reportfd, coredump);
    522 
    523 	fclose(reportfd);
    524 
    525 	return reportfname;
    526 }
    527 
    528 #define REPORT_NEVER	-1
    529 #define REPORT_ASK		0
    530 #define REPORT_AUTO		1
    531 
    532 #define CRASH_REPORT_HOST "crash.unrealircd.org"
    533 
    534 SSL_CTX *crashreport_init_tls(void)
    535 {
    536 	SSL_CTX *ctx_client;
    537 	char buf[512];
    538 	
    539 	SSL_load_error_strings();
    540 	SSLeay_add_ssl_algorithms();
    541 
    542 	ctx_client = SSL_CTX_new(SSLv23_client_method());
    543 	if (!ctx_client)
    544 		return NULL;
    545 #ifdef HAS_SSL_CTX_SET_MIN_PROTO_VERSION
    546 	SSL_CTX_set_min_proto_version(ctx_client, TLS1_2_VERSION);
    547 #endif
    548 	SSL_CTX_set_options(ctx_client, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1);
    549 
    550 	/* Verify peer certificate */
    551 	snprintf(buf, sizeof(buf), "%s/tls/curl-ca-bundle.crt", CONFDIR);
    552 	SSL_CTX_load_verify_locations(ctx_client, buf, NULL);
    553 	SSL_CTX_set_verify(ctx_client, SSL_VERIFY_PEER, NULL);
    554 
    555 	/* Limit ciphers as well */
    556 	SSL_CTX_set_cipher_list(ctx_client, UNREALIRCD_DEFAULT_CIPHERS);
    557 
    558 	return ctx_client;
    559 }	
    560 
    561 int crashreport_send(char *fname)
    562 {
    563 	char buf[1024];
    564 	char header[512], footer[512];
    565 	char delimiter[41];
    566 	int filesize;
    567 	int n;
    568 	FILE *fd;
    569 	SSL_CTX *ctx_client;
    570 	SSL *ssl = NULL;
    571 	BIO *socket = NULL;
    572 	int xfr = 0;
    573 	char *errstr = NULL;
    574 	
    575 	filesize = get_file_size(fname);
    576 	if (filesize < 0)
    577 		return 0;
    578 	
    579 	for (n = 0; n < sizeof(delimiter); n++)
    580 		delimiter[n] = getrandom8()%26 + 'a';
    581 	delimiter[sizeof(delimiter)-1] = '\0';
    582 	
    583 	snprintf(header, sizeof(header), "--%s\r\n"
    584 	                           "Content-Disposition: form-data; name=\"upload\"; filename=\"crash.txt\"\r\n"
    585 	                           "Content-Type: text/plain\r\n"
    586 	                           "\r\n",
    587 	                           delimiter);
    588 	snprintf(footer, sizeof(footer), "\r\n--%s--\r\n", delimiter);
    589 
    590 	ctx_client = crashreport_init_tls();
    591 	if (!ctx_client)
    592 	{
    593 		printf("ERROR: TLS initalization failure (I)\n");
    594 		return 0;
    595 	}
    596 	
    597 	socket = BIO_new_ssl_connect(ctx_client);
    598 	if (!socket)
    599 	{
    600 		printf("ERROR: TLS initalization failure (II)\n");
    601 		return 0;
    602 	}
    603 	
    604 	BIO_set_conn_hostname(socket, CRASH_REPORT_HOST ":443");
    605 
    606 	if (BIO_do_connect(socket) != 1)
    607 	{
    608 		printf("ERROR: Could not connect to %s\n", CRASH_REPORT_HOST);
    609 		return 0;
    610 	}
    611 	
    612 	if (BIO_do_handshake(socket) != 1)
    613 	{
    614 		printf("ERROR: Could not connect to %s (TLS handshake failed)\n", CRASH_REPORT_HOST);
    615 		return 0;
    616 	}
    617 
    618 	BIO_get_ssl(socket, &ssl);
    619 	if (!ssl)
    620 	{
    621 		printf("ERROR: Could not get TLS connection from BIO\n");
    622 		return 0;
    623 	}
    624 
    625 	if (!verify_certificate(ssl, CRASH_REPORT_HOST, &errstr))
    626 	{
    627 		printf("Certificate problem with crash.unrealircd.org: %s\n", errstr);
    628 		printf("Fatal error. See above.\n");
    629 		return 0;
    630 	}
    631 
    632 	snprintf(buf, sizeof(buf), "POST /crash.php HTTP/1.1\r\n"
    633 	                    "User-Agent: UnrealIRCd %s\r\n"
    634 	                    "Host: %s\r\n"
    635 	                    "Accept: */*\r\n"
    636 	                    "Content-Length: %d\r\n"
    637 	                    "Expect: 100-continue\r\n"
    638 	                    "Content-Type: multipart/form-data; boundary=%s\r\n"
    639 	                    "\r\n",
    640 	                    VERSIONONLY,
    641 	                    CRASH_REPORT_HOST,
    642 	                    (int)(filesize+strlen(header)+strlen(footer)),
    643 	                    delimiter);
    644 	
    645 	BIO_puts(socket, buf);
    646 	
    647 	memset(buf, 0, sizeof(buf));
    648 	n = BIO_read(socket, buf, 255);
    649 	if ((n < 0) || strncmp(buf, "HTTP/1.1 100", 12))
    650 	{
    651 		printf("Error transmitting bug report (stage II, n=%d)\n", n);
    652 		if (!strncmp(buf, "HTTP/1.1 403", 12))
    653 		{
    654 			printf("Your crash report was rejected automatically.\n"
    655 			       "This normally means your UnrealIRCd version is too old and unsupported.\n"
    656 			       "Chances are that your crash issue is already fixed in a later release.\n"
    657 			       "Check https://www.unrealircd.org/ for latest releases!\n");
    658 		}
    659 		return 0;
    660 	}
    661 	
    662 	fd = fopen(fname, "rb");
    663 	if (!fd)
    664 		return 0;
    665 
    666 	BIO_puts(socket, header);
    667 
    668 #ifndef _WIN32
    669 	printf("Sending...");
    670 #endif
    671 	while ((fgets(buf, sizeof(buf), fd)))
    672 	{
    673 		BIO_puts(socket, buf);
    674 #ifndef _WIN32
    675 		if ((++xfr % 1000) == 0)
    676 		{
    677 			printf(".");
    678 			fflush(stdout);
    679 		}
    680 #endif
    681 	}
    682 	fclose(fd);
    683 
    684 	BIO_puts(socket, footer);
    685 
    686 	do { } while(BIO_should_retry(socket)); /* make sure we are really finished (you never know with TLS) */
    687 
    688 #ifndef _WIN32
    689 	printf("\n");
    690 #endif
    691 	BIO_free_all(socket);
    692 	
    693 	SSL_CTX_free(ctx_client);
    694 	
    695 	return 1;
    696 }
    697 
    698 void mark_coredump_as_read(char *coredump)
    699 {
    700 	char buf[512];
    701 	
    702 	snprintf(buf, sizeof(buf), "%s.%ld.done", coredump, (long)time(NULL));
    703 	
    704 	(void)rename(coredump, buf);
    705 }
    706 
    707 static int report_pref = REPORT_ASK;
    708 
    709 void report_crash_not_sent(char *fname)
    710 {
    711 		printf("Crash report will not be sent to UnrealIRCd Team.\n"
    712 		       "\n"
    713 		       "Feel free to read the report at %s and delete it.\n"
    714 		       "Or, if you change your mind, you can submit it anyway at https://bugs.unrealircd.org/\n"
    715 		       " (if you do, please set the option 'View Status' at the end of the bug report page to 'private'!!)\n", fname);
    716 }
    717 
    718 /** This checks if there are indications that 3rd party modules are
    719  * loaded. This is used to provide a small warning to the user that
    720  * the crash may be likely due to that.
    721  */
    722 int check_third_party_mods_present(void)
    723 {
    724 #ifndef _WIN32
    725 	struct dirent *dir;
    726 	DIR *fd = opendir(TMPDIR);
    727 
    728 	if (!fd)
    729 		return 0;
    730 
    731 	/* We search for files like tmp/FC5C3116.third.somename.so */
    732 	while ((dir = readdir(fd)))
    733 	{
    734 		char *fname = dir->d_name;
    735 		if (strstr(fname, ".third.") && strstr(fname, ".so"))
    736 		{
    737 			closedir(fd);
    738 			return 1;
    739 		}
    740 	}
    741 	closedir(fd);
    742 #endif
    743 	return 0;
    744 }
    745 
    746 void report_crash(void)
    747 {
    748 	char *coredump, *fname;
    749 	int thirdpartymods = 0;
    750 	int crashed_secs_ago;
    751 
    752 	if (!running_interactively() && (report_pref != REPORT_AUTO))
    753 		exit(0); /* don't bother if we run through cron or something similar */
    754 
    755 	coredump = find_best_coredump();
    756 	if (!coredump)
    757 		return; /* no crashes */
    758 
    759 	crashed_secs_ago = time(NULL) - get_file_time(coredump);
    760 	if (crashed_secs_ago > 86400*7)
    761 		return; /* stop bothering about it after a while */
    762 
    763 	fname = generate_crash_report(coredump, &thirdpartymods);
    764 	
    765 	if (!fname)
    766 		return;
    767 
    768 	if (thirdpartymods == 0)
    769 		thirdpartymods = check_third_party_mods_present();
    770 #ifndef _WIN32
    771 	printf("The IRCd has been started now (and is running), but it did crash %d seconds ago.\n", crashed_secs_ago);
    772 	printf("Crash report generated in: %s\n\n", fname);
    773 
    774 	if (thirdpartymods)
    775 	{
    776 	    printf("** IMPORTANT **\n"
    777                "Your UnrealIRCd crashed and you have 3rd party modules loaded (modules created\n"
    778                "by someone other than the UnrealIRCd team). If you installed new 3rd party\n"
    779                "module(s) in the past few weeks we suggest to unload these modules and see if\n"
    780                "the crash issue dissapears. If so, that module is probably to blame.\n"
    781                "If you keep crashing without any 3rd party modules loaded then please do report\n"
    782                "it to the UnrealIRCd team.\n"
    783                "The reason we ask you to do this is because MORE THAN 95%% OF ALL CRASH ISSUES\n"
    784                "ARE CAUSED BY 3RD PARTY MODULES and not by an UnrealIRCd bug.\n"
    785                "\n");
    786 	}
    787 		
    788 	if (report_pref == REPORT_NEVER)
    789 	{
    790 		report_crash_not_sent(fname);
    791 		return;
    792 	} else
    793 	if (report_pref == REPORT_ASK)
    794 	{
    795 		char answerbuf[64], *answer;
    796 		printf("Shall I send a crash report to the UnrealIRCd developers?\n");
    797 		if (!thirdpartymods)
    798 			printf("Crash reports help us greatly with fixing bugs that affect you and others\n");
    799 		else
    800 			printf("NOTE: If the crash is caused by a 3rd party module then UnrealIRCd devs can't fix that.\n");
    801 		printf("\n");
    802 		
    803 		do
    804 		{
    805 			printf("Answer (Y/N): ");
    806 			*answerbuf = '\0';
    807 			answer = fgets(answerbuf, sizeof(answerbuf), stdin);
    808 			
    809 			if (answer && (toupper(*answer) == 'N'))
    810 			{
    811 				report_crash_not_sent(fname);
    812 				return;
    813 			}
    814 			if (answer && (toupper(*answer) == 'Y'))
    815 			{
    816 				break;
    817 			}
    818 			
    819 			printf("Invalid response. Please enter either Y or N\n\n");
    820 		} while(1);
    821 	} else if (report_pref != REPORT_AUTO)
    822 	{
    823 		printf("Huh. report_pref setting is weird. Aborting.\n");
    824 		return;
    825 	}
    826 
    827 	if (running_interactively())
    828 	{
    829 		char buf[8192], *line;
    830 
    831 		printf("\nDo you want to give your e-mail address so we could ask for additional information or "
    832 		       "give you feedback about the crash? This is completely optional, just press ENTER to skip.\n\n"
    833 		       "E-mail address (optional): ");
    834 		line = fgets(buf, sizeof(buf), stdin);
    835 		
    836 		if (line && *line && (*line != '\n'))
    837 		{
    838 			FILE *fd = fopen(fname, "a");
    839 			if (fd)
    840 			{
    841 				fprintf(fd, "\nUSER E-MAIL ADDRESS: %s\n", line);
    842 				fclose(fd);
    843 			}
    844 		}
    845 
    846 		printf("\nDo you have anything else to tell about the crash, maybe the circumstances? You can type 1 single line\n"
    847 		       "Again, this is completely optional. Just press ENTER to skip.\n\n"
    848 		       "Additional information (optional): ");
    849 		line = fgets(buf, sizeof(buf), stdin);
    850 		
    851 		if (line && *line && (*line != '\n'))
    852 		{
    853 			FILE *fd = fopen(fname, "a");
    854 			if (fd)
    855 			{
    856 				fprintf(fd, "\nCOMMENT BY USER: %s\n", line);
    857 				fclose(fd);
    858 			}
    859 		}
    860 	}
    861 	if (crashreport_send(fname))
    862 	{
    863 		printf("\nThe crash report has been sent to the UnrealIRCd developers. "
    864 		       "Thanks a lot for helping to make UnrealIRCd a better product!\n\n");
    865 	}
    866 
    867 #else
    868 	/* Windows */
    869 	if (MessageBox(NULL, "UnrealIRCd crashed. May I send a report about this to the UnrealIRCd developers? This helps us a lot.",
    870 	                     "UnrealIRCd crash",
    871 	                     MB_YESNO|MB_ICONQUESTION) == IDYES)
    872 	{
    873 		/* Yay */
    874 		
    875 		if (crashreport_send(fname))
    876 		{
    877 			MessageBox(NULL, "The crash report has been sent to the UnrealIRCd developers. "
    878 			                 "If you have any additional information (like details surrounding "
    879 			                 "the crash) then please e-mail syzop@unrealircd.org, such "
    880 			                 "information is most welcome. Thanks!",
    881 			           "UnrealIRCd crash report sent", MB_ICONINFORMATION|MB_OK);
    882 		}
    883 	}
    884 #endif
    885 	mark_coredump_as_read(coredump);
    886 	
    887 #ifdef _WIN32
    888 	if (MessageBox(NULL, "Start UnrealIRCd again?",
    889 	                     "UnrealIRCd crash",
    890 	                     MB_YESNO|MB_ICONQUESTION) == IDYES)
    891 	{
    892 		StartUnrealAgain();
    893 	}
    894 #endif
    895 }