unrealircd

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

crashreport.c (21443B)

      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_get_ssl(socket, &ssl);
    605 	if (!ssl)
    606 	{
    607 		printf("ERROR: Could not get TLS connection from BIO\n");
    608 		return 0;
    609 	}
    610 
    611 	SSL_set_tlsext_host_name(ssl, CRASH_REPORT_HOST); /* SNI needs to be set explicitly */
    612 
    613 	BIO_set_conn_hostname(socket, CRASH_REPORT_HOST ":443");
    614 
    615 	if (BIO_do_connect(socket) != 1)
    616 	{
    617 		printf("ERROR: Could not connect to %s\n", CRASH_REPORT_HOST);
    618 		return 0;
    619 	}
    620 	
    621 	if (BIO_do_handshake(socket) != 1)
    622 	{
    623 		printf("ERROR: Could not connect to %s (TLS handshake failed)\n", CRASH_REPORT_HOST);
    624 		return 0;
    625 	}
    626 
    627 	if (!verify_certificate(ssl, CRASH_REPORT_HOST, &errstr))
    628 	{
    629 		printf("Certificate problem with crash.unrealircd.org: %s\n", errstr);
    630 		printf("Fatal error. See above.\n");
    631 		return 0;
    632 	}
    633 
    634 	snprintf(buf, sizeof(buf), "POST /crash.php HTTP/1.1\r\n"
    635 	                    "User-Agent: UnrealIRCd %s\r\n"
    636 	                    "Host: %s\r\n"
    637 	                    "Accept: */*\r\n"
    638 	                    "Content-Length: %d\r\n"
    639 	                    "Expect: 100-continue\r\n"
    640 	                    "Content-Type: multipart/form-data; boundary=%s\r\n"
    641 	                    "\r\n",
    642 	                    VERSIONONLY,
    643 	                    CRASH_REPORT_HOST,
    644 	                    (int)(filesize+strlen(header)+strlen(footer)),
    645 	                    delimiter);
    646 	
    647 	BIO_puts(socket, buf);
    648 	
    649 	memset(buf, 0, sizeof(buf));
    650 	n = BIO_read(socket, buf, 255);
    651 	if ((n < 0) || strncmp(buf, "HTTP/1.1 100", 12))
    652 	{
    653 		printf("Error transmitting bug report (stage II, n=%d)\n", n);
    654 		if (!strncmp(buf, "HTTP/1.1 403", 12))
    655 		{
    656 			printf("Your crash report was rejected automatically.\n"
    657 			       "This normally means your UnrealIRCd version is too old and unsupported.\n"
    658 			       "Chances are that your crash issue is already fixed in a later release.\n"
    659 			       "Check https://www.unrealircd.org/ for latest releases!\n");
    660 		}
    661 		return 0;
    662 	}
    663 	
    664 	fd = fopen(fname, "rb");
    665 	if (!fd)
    666 		return 0;
    667 
    668 	BIO_puts(socket, header);
    669 
    670 #ifndef _WIN32
    671 	printf("Sending...");
    672 #endif
    673 	while ((fgets(buf, sizeof(buf), fd)))
    674 	{
    675 		BIO_puts(socket, buf);
    676 #ifndef _WIN32
    677 		if ((++xfr % 1000) == 0)
    678 		{
    679 			printf(".");
    680 			fflush(stdout);
    681 		}
    682 #endif
    683 	}
    684 	fclose(fd);
    685 
    686 	BIO_puts(socket, footer);
    687 
    688 	do { } while(BIO_should_retry(socket)); /* make sure we are really finished (you never know with TLS) */
    689 
    690 #ifndef _WIN32
    691 	printf("\n");
    692 #endif
    693 	BIO_free_all(socket);
    694 	
    695 	SSL_CTX_free(ctx_client);
    696 	
    697 	return 1;
    698 }
    699 
    700 void mark_coredump_as_read(char *coredump)
    701 {
    702 	char buf[512];
    703 	
    704 	snprintf(buf, sizeof(buf), "%s.%ld.done", coredump, (long)time(NULL));
    705 	
    706 	(void)rename(coredump, buf);
    707 }
    708 
    709 static int report_pref = REPORT_ASK;
    710 
    711 void report_crash_not_sent(char *fname)
    712 {
    713 		printf("Crash report will not be sent to UnrealIRCd Team.\n"
    714 		       "\n"
    715 		       "Feel free to read the report at %s and delete it.\n"
    716 		       "Or, if you change your mind, you can submit it anyway at https://bugs.unrealircd.org/\n"
    717 		       " (if you do, please set the option 'View Status' at the end of the bug report page to 'private'!!)\n", fname);
    718 }
    719 
    720 /** This checks if there are indications that 3rd party modules are
    721  * loaded. This is used to provide a small warning to the user that
    722  * the crash may be likely due to that.
    723  */
    724 int check_third_party_mods_present(void)
    725 {
    726 #ifndef _WIN32
    727 	struct dirent *dir;
    728 	DIR *fd = opendir(TMPDIR);
    729 
    730 	if (!fd)
    731 		return 0;
    732 
    733 	/* We search for files like tmp/FC5C3116.third.somename.so */
    734 	while ((dir = readdir(fd)))
    735 	{
    736 		char *fname = dir->d_name;
    737 		if (strstr(fname, ".third.") && strstr(fname, ".so"))
    738 		{
    739 			closedir(fd);
    740 			return 1;
    741 		}
    742 	}
    743 	closedir(fd);
    744 #endif
    745 	return 0;
    746 }
    747 
    748 void report_crash(void)
    749 {
    750 	char *coredump, *fname;
    751 	int thirdpartymods = 0;
    752 	int crashed_secs_ago;
    753 
    754 	if (!running_interactively() && (report_pref != REPORT_AUTO))
    755 		exit(0); /* don't bother if we run through cron or something similar */
    756 
    757 	coredump = find_best_coredump();
    758 	if (!coredump)
    759 		return; /* no crashes */
    760 
    761 	crashed_secs_ago = time(NULL) - get_file_time(coredump);
    762 	if (crashed_secs_ago > 86400*7)
    763 		return; /* stop bothering about it after a while */
    764 
    765 	fname = generate_crash_report(coredump, &thirdpartymods);
    766 	
    767 	if (!fname)
    768 		return;
    769 
    770 	if (thirdpartymods == 0)
    771 		thirdpartymods = check_third_party_mods_present();
    772 #ifndef _WIN32
    773 	printf("The IRCd has been started now (and is running), but it did crash %d seconds ago.\n", crashed_secs_ago);
    774 	printf("Crash report generated in: %s\n\n", fname);
    775 
    776 	if (thirdpartymods)
    777 	{
    778 	    printf("** IMPORTANT **\n"
    779                "Your UnrealIRCd crashed and you have 3rd party modules loaded (modules created\n"
    780                "by someone other than the UnrealIRCd team). If you installed new 3rd party\n"
    781                "module(s) in the past few weeks we suggest to unload these modules and see if\n"
    782                "the crash issue dissapears. If so, that module is probably to blame.\n"
    783                "If you keep crashing without any 3rd party modules loaded then please do report\n"
    784                "it to the UnrealIRCd team.\n"
    785                "The reason we ask you to do this is because MORE THAN 95%% OF ALL CRASH ISSUES\n"
    786                "ARE CAUSED BY 3RD PARTY MODULES and not by an UnrealIRCd bug.\n"
    787                "\n");
    788 	}
    789 		
    790 	if (report_pref == REPORT_NEVER)
    791 	{
    792 		report_crash_not_sent(fname);
    793 		return;
    794 	} else
    795 	if (report_pref == REPORT_ASK)
    796 	{
    797 		char answerbuf[64], *answer;
    798 		printf("Shall I send a crash report to the UnrealIRCd developers?\n");
    799 		if (!thirdpartymods)
    800 			printf("Crash reports help us greatly with fixing bugs that affect you and others\n");
    801 		else
    802 			printf("NOTE: If the crash is caused by a 3rd party module then UnrealIRCd devs can't fix that.\n");
    803 		printf("\n");
    804 		
    805 		do
    806 		{
    807 			printf("Answer (Y/N): ");
    808 			*answerbuf = '\0';
    809 			answer = fgets(answerbuf, sizeof(answerbuf), stdin);
    810 			
    811 			if (answer && (toupper(*answer) == 'N'))
    812 			{
    813 				report_crash_not_sent(fname);
    814 				return;
    815 			}
    816 			if (answer && (toupper(*answer) == 'Y'))
    817 			{
    818 				break;
    819 			}
    820 			
    821 			printf("Invalid response. Please enter either Y or N\n\n");
    822 		} while(1);
    823 	} else if (report_pref != REPORT_AUTO)
    824 	{
    825 		printf("Huh. report_pref setting is weird. Aborting.\n");
    826 		return;
    827 	}
    828 
    829 	if (running_interactively())
    830 	{
    831 		char buf[8192], *line;
    832 
    833 		printf("\nDo you want to give your e-mail address so we could ask for additional information or "
    834 		       "give you feedback about the crash? This is completely optional, just press ENTER to skip.\n\n"
    835 		       "E-mail address (optional): ");
    836 		line = fgets(buf, sizeof(buf), stdin);
    837 		
    838 		if (line && *line && (*line != '\n'))
    839 		{
    840 			FILE *fd = fopen(fname, "a");
    841 			if (fd)
    842 			{
    843 				fprintf(fd, "\nUSER E-MAIL ADDRESS: %s\n", line);
    844 				fclose(fd);
    845 			}
    846 		}
    847 
    848 		printf("\nDo you have anything else to tell about the crash, maybe the circumstances? You can type 1 single line\n"
    849 		       "Again, this is completely optional. Just press ENTER to skip.\n\n"
    850 		       "Additional information (optional): ");
    851 		line = fgets(buf, sizeof(buf), stdin);
    852 		
    853 		if (line && *line && (*line != '\n'))
    854 		{
    855 			FILE *fd = fopen(fname, "a");
    856 			if (fd)
    857 			{
    858 				fprintf(fd, "\nCOMMENT BY USER: %s\n", line);
    859 				fclose(fd);
    860 			}
    861 		}
    862 	}
    863 	if (crashreport_send(fname))
    864 	{
    865 		printf("\nThe crash report has been sent to the UnrealIRCd developers. "
    866 		       "Thanks a lot for helping to make UnrealIRCd a better product!\n\n");
    867 	}
    868 
    869 #else
    870 	/* Windows */
    871 	if (MessageBox(NULL, "UnrealIRCd crashed. May I send a report about this to the UnrealIRCd developers? This helps us a lot.",
    872 	                     "UnrealIRCd crash",
    873 	                     MB_YESNO|MB_ICONQUESTION) == IDYES)
    874 	{
    875 		/* Yay */
    876 		
    877 		if (crashreport_send(fname))
    878 		{
    879 			MessageBox(NULL, "The crash report has been sent to the UnrealIRCd developers. "
    880 			                 "If you have any additional information (like details surrounding "
    881 			                 "the crash) then please e-mail syzop@unrealircd.org, such "
    882 			                 "information is most welcome. Thanks!",
    883 			           "UnrealIRCd crash report sent", MB_ICONINFORMATION|MB_OK);
    884 		}
    885 	}
    886 #endif
    887 	mark_coredump_as_read(coredump);
    888 	
    889 #ifdef _WIN32
    890 	if (MessageBox(NULL, "Start UnrealIRCd again?",
    891 	                     "UnrealIRCd crash",
    892 	                     MB_YESNO|MB_ICONQUESTION) == IDYES)
    893 	{
    894 		StartUnrealAgain();
    895 	}
    896 #endif
    897 }