Browse code

CL_EXPERIMENTAL test code without libcurl

git-svn: trunk@2299

Nigel Horne authored on 2006/09/22 01:40:51
Showing 2 changed files
... ...
@@ -1,3 +1,8 @@
1
+Thu Sep 21 17:39:29 BST 2006 (njh)
2
+----------------------------------
3
+  * libclamav/mbox.c:	Experimental mode now no longer uses libcurl to
4
+				download referenced pages to be scanned
5
+
1 6
 Thu Sep 21 10:36:32 BST 2006 (njh)
2 7
 ----------------------------------
3 8
   * libclamav/mbox.c:	Handle segfaults in libcurl
... ...
@@ -16,7 +16,7 @@
16 16
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 17
  *  MA 02110-1301, USA.
18 18
  */
19
-static	char	const	rcsid[] = "$Id: mbox.c,v 1.339 2006/09/21 14:42:06 njh Exp $";
19
+static	char	const	rcsid[] = "$Id: mbox.c,v 1.340 2006/09/21 16:38:33 njh Exp $";
20 20
 
21 21
 #if HAVE_CONFIG_H
22 22
 #include "clamav-config.h"
... ...
@@ -4214,6 +4214,408 @@ checkURLs(message *m, mbox_ctx *mctx, int* rc, int is_html)
4214 4214
  */
4215 4215
 #ifdef	WITH_CURL
4216 4216
 
4217
+#ifdef	CL_EXPERIMENTAL
4218
+/*
4219
+ * Removing the reliance on libcurl
4220
+ * Includes some of the freshclam hacks by Everton da Silva Marques
4221
+ * everton.marques@gmail.com>
4222
+ */
4223
+#include <netdb.h>
4224
+#include <sys/socket.h>
4225
+#include <netinet/in.h>
4226
+#include <net/if.h>
4227
+#include <arpa/inet.h>
4228
+#include <ctype.h>
4229
+#include <errno.h>
4230
+#include <fcntl.h>
4231
+#include <sys/time.h>
4232
+#include <stdlib.h>
4233
+
4234
+#ifndef timercmp
4235
+# define timercmp(a, b, cmp)          \
4236
+  (((a)->tv_sec == (b)->tv_sec) ?     \
4237
+   ((a)->tv_usec cmp (b)->tv_usec) :  \
4238
+   ((a)->tv_sec cmp (b)->tv_sec))
4239
+#endif /* timercmp */
4240
+
4241
+#ifndef timersub
4242
+# define timersub(a, b, result)                       \
4243
+  do {                                                \
4244
+    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;     \
4245
+    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;  \
4246
+    if ((result)->tv_usec < 0) {                      \
4247
+      --(result)->tv_sec;                             \
4248
+      (result)->tv_usec += 1000000;                   \
4249
+    }                                                 \
4250
+  } while (0)
4251
+#endif /* timersub */
4252
+
4253
+static long nonblock_fcntl(int sock);
4254
+static void restore_fcntl(int sock, long fcntl_flags);
4255
+static int nonblock_connect(int sock, const struct sockaddr *addr, socklen_t addrlen, int secs);
4256
+static int connect_error(int sock);
4257
+
4258
+#define NONBLOCK_SELECT_MAX_FAILURES 3
4259
+#define NONBLOCK_MAX_BOGUS_LOOPS     10
4260
+static void *
4261
+#ifdef	CL_THREAD_SAFE
4262
+getURL(void *a)
4263
+#else
4264
+getURL(struct arg *arg)
4265
+#endif
4266
+{
4267
+	FILE *fp;
4268
+#ifdef	CL_THREAD_SAFE
4269
+	struct arg *arg = (struct arg *)a;
4270
+#endif
4271
+	const char *url = arg->url;
4272
+	const char *dir = arg->dir;
4273
+	const char *filename = arg->filename;
4274
+	char fout[NAME_MAX + 1];
4275
+	const struct protoent *proto;
4276
+	int sd, n;
4277
+	struct sockaddr_in server;
4278
+	in_addr_t ip;
4279
+	char buf[BUFSIZ];
4280
+	char site[BUFSIZ];
4281
+	in_port_t port = 80;
4282
+	int doingsite = 1;
4283
+	char *ptr;
4284
+	int flags;
4285
+	const char *proxy;
4286
+
4287
+	snprintf(fout, sizeof(fout) - 1, "%s/%s", dir, filename);
4288
+
4289
+	cli_dbgmsg("Saving %s to %s\n", url, fout);
4290
+	fp = fopen(fout, "wb");
4291
+
4292
+	if(fp == NULL) {
4293
+		cli_errmsg("Can't open '%s' for writing", fout);
4294
+		return NULL;
4295
+	}
4296
+	proxy = getenv("http_proxy");	/* FIXME: handle no_proxy */
4297
+	if(proxy && *proxy) {
4298
+		cli_dbgmsg("Getting %s via %s\n", url, proxy);
4299
+		snprintf(buf, sizeof(buf) - 1,
4300
+			"GET /%s HTTP/1.0\nHost: %s\nUser-Agent: www.clamav.net\n\n",
4301
+			url, site);
4302
+		if(strncasecmp(proxy, "http://", 7) != 0) {
4303
+			cli_warnmsg("Unsupported proxy protocol\n");
4304
+			fclose(fp);
4305
+			return NULL;
4306
+		}
4307
+
4308
+		proxy += 7;
4309
+		ptr = site;
4310
+		while(*proxy) {
4311
+			if(doingsite && (*proxy == ':')) {
4312
+				port = 0;
4313
+				while(isdigit(*++proxy)) {
4314
+					port *= 10;
4315
+					port += *proxy - '0';
4316
+				}
4317
+				continue;
4318
+			}
4319
+			if(doingsite && (*proxy == '/')) {
4320
+				doingsite = 0;
4321
+				proxy++;
4322
+				*ptr = '\0';
4323
+				break;
4324
+			}
4325
+			*ptr++ = *proxy++;
4326
+		}
4327
+
4328
+		memset((char *)&server, 0, sizeof(struct sockaddr_in));
4329
+		server.sin_family = AF_INET;
4330
+		server.sin_port = htons(port);
4331
+
4332
+		ip = inet_addr(site);
4333
+#ifdef	INADDR_NONE
4334
+		if(ip == INADDR_NONE) {
4335
+#else
4336
+		if(ip == (in_addr_t)-1) {
4337
+#endif
4338
+			const struct hostent *h = gethostbyname(site);
4339
+
4340
+			if(h == NULL) {
4341
+				cli_dbgmsg("Unknown host %s\n", site);
4342
+				fclose(fp);
4343
+				return NULL;
4344
+			}
4345
+
4346
+			memcpy((char *)&ip, h->h_addr, sizeof(ip));
4347
+		}
4348
+		server.sin_addr.s_addr = ip;
4349
+
4350
+		proto = getprotobyname("tcp");
4351
+		if(proto == NULL) {
4352
+			cli_warnmsg("Unknown prototol tcp, check /etc/protocols\n");
4353
+			fclose(fp);
4354
+			return NULL;
4355
+		}
4356
+		if((sd = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0)
4357
+			return NULL;
4358
+		flags = nonblock_fcntl(sd);
4359
+		if(nonblock_connect(sd, (struct sockaddr *)&server, sizeof(struct sockaddr_in), 5) < 0) {
4360
+			close(sd);
4361
+			fclose(fp);
4362
+			return NULL;
4363
+		}
4364
+		restore_fcntl(sd, flags);
4365
+
4366
+		snprintf(buf, sizeof(buf) - 1,
4367
+			"GET %s HTTP/1.0\nHost: %s\nUser-Agent: www.clamav.net\n\n",
4368
+				url, site);
4369
+	} else {
4370
+		cli_dbgmsg("Getting %s\n", url);
4371
+
4372
+		if(strncasecmp(url, "http://", 7) != 0) {
4373
+			cli_warnmsg("Unsupported protocol\n");
4374
+			fclose(fp);
4375
+			return NULL;
4376
+		}
4377
+
4378
+		url += 7;
4379
+		ptr = site;
4380
+		while(*url) {
4381
+			if(doingsite && (*url == ':')) {
4382
+				port = 0;
4383
+				while(isdigit(*++url)) {
4384
+					port *= 10;
4385
+					port += *url - '0';
4386
+				}
4387
+				continue;
4388
+			}
4389
+			if(doingsite && (*url == '/')) {
4390
+				doingsite = 0;
4391
+				url++;
4392
+				*ptr = '\0';
4393
+				break;
4394
+			}
4395
+			*ptr++ = *url++;
4396
+		}
4397
+
4398
+		memset((char *)&server, 0, sizeof(struct sockaddr_in));
4399
+		server.sin_family = AF_INET;
4400
+		server.sin_port = htons(port);
4401
+
4402
+		ip = inet_addr(site);
4403
+#ifdef	INADDR_NONE
4404
+		if(ip == INADDR_NONE) {
4405
+#else
4406
+		if(ip == (in_addr_t)-1) {
4407
+#endif
4408
+			const struct hostent *h = gethostbyname(site);
4409
+
4410
+			if(h == NULL) {
4411
+				cli_dbgmsg("Unknown host %s\n", site);
4412
+				fclose(fp);
4413
+				return NULL;
4414
+			}
4415
+
4416
+			memcpy((char *)&ip, h->h_addr, sizeof(ip));
4417
+		}
4418
+		server.sin_addr.s_addr = ip;
4419
+
4420
+		proto = getprotobyname("tcp");
4421
+		if(proto == NULL) {
4422
+			cli_warnmsg("Unknown prototol tcp, check /etc/protocols\n");
4423
+			fclose(fp);
4424
+			return NULL;
4425
+		}
4426
+		if((sd = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0) {
4427
+			fclose(fp);
4428
+			return NULL;
4429
+		}
4430
+		flags = nonblock_fcntl(sd);
4431
+		if(nonblock_connect(sd, (struct sockaddr *)&server, sizeof(struct sockaddr_in), 5) < 0) {
4432
+			close(sd);
4433
+			fclose(fp);
4434
+			return NULL;
4435
+		}
4436
+		restore_fcntl(sd, flags);
4437
+
4438
+		snprintf(buf, sizeof(buf) - 1,
4439
+			"GET /%s HTTP/1.0\nHost: %s\nUser-Agent: www.clamav.net\n\n",
4440
+			url, site);
4441
+	}
4442
+
4443
+	if(send(sd, buf, strlen(buf), 0) < 0) {
4444
+		close(sd);
4445
+		fclose(fp);
4446
+		return NULL;
4447
+	}
4448
+
4449
+	shutdown(sd, SHUT_WR);
4450
+
4451
+	for(;;) {
4452
+		fd_set set;
4453
+		struct timeval tv;
4454
+
4455
+		FD_ZERO(&set);
4456
+		FD_SET(sd, &set);
4457
+
4458
+		tv.tv_sec = 30;	/* FIXME: make this customisable */
4459
+		tv.tv_usec = 0;
4460
+
4461
+		if(select(sd + 1, &set, NULL, NULL, &tv) < 0) {
4462
+			if(errno == EINTR)
4463
+				continue;
4464
+			close(sd);
4465
+			fclose(fp);
4466
+			return NULL;
4467
+		}
4468
+		if(!FD_ISSET(sd, &set)) {
4469
+			fclose(fp);
4470
+			close(sd);
4471
+			return NULL;
4472
+		}
4473
+		n = recv(sd, buf, BUFSIZ, 0);
4474
+		if(n < 0) {
4475
+			fclose(fp);
4476
+			close(sd);
4477
+			return NULL;
4478
+		}
4479
+		if(n == 0)
4480
+			break;
4481
+		if(fwrite(buf, n, 1, fp) != 1) {
4482
+			cli_warnmsg("Error writing %d bytes to %s\n",
4483
+				n, fout);
4484
+			break;
4485
+		}
4486
+	}
4487
+
4488
+	fclose(fp);
4489
+	close(sd);
4490
+	return NULL;
4491
+}
4492
+
4493
+static long
4494
+nonblock_fcntl(int sock)
4495
+{
4496
+	long fcntl_flags;	/* Save fcntl() flags */
4497
+
4498
+	fcntl_flags = fcntl(sock, F_GETFL, 0);
4499
+	if(fcntl_flags < 0)
4500
+		cli_warnmsg("nonblock_fcntl: saving: fcntl(%d, F_GETFL): errno=%d: %s\n",
4501
+			sock, errno, strerror(errno));
4502
+	else if(fcntl(sock, F_SETFL, fcntl_flags | O_NONBLOCK))
4503
+		cli_warnmsg("nonblock_fcntl: fcntl(%d, F_SETFL, O_NONBLOCK): errno=%d: %s\n",
4504
+			sock, errno, strerror(errno));
4505
+
4506
+	return fcntl_flags;
4507
+}
4508
+
4509
+static void
4510
+restore_fcntl(int sock, long fcntl_flags)
4511
+{
4512
+	if (fcntl_flags != -1)
4513
+		if (fcntl(sock, F_SETFL, fcntl_flags)) {
4514
+			cli_warnmsg("restore_fcntl: restoring: fcntl(%d, F_SETFL): errno=%d: %s\n",
4515
+				sock, errno, strerror(errno));
4516
+		}
4517
+}
4518
+
4519
+static int
4520
+nonblock_connect(int sock, const struct sockaddr *addr, socklen_t addrlen, int secs)
4521
+{
4522
+	/* Max. of unexpected select() failures */
4523
+	int select_failures = NONBLOCK_SELECT_MAX_FAILURES;
4524
+	/* Max. of useless loops */
4525
+	int bogus_loops = NONBLOCK_MAX_BOGUS_LOOPS;
4526
+	struct timeval timeout;  /* When we should time out */
4527
+	int numfd;		/* Highest fdset fd plus 1 */
4528
+
4529
+	/* Calculate into 'timeout' when we should time out */
4530
+	gettimeofday(&timeout, 0);
4531
+	timeout.tv_sec += secs;
4532
+
4533
+	/* Launch (possibly) non-blocking connect() request */
4534
+	if(connect(sock, addr, addrlen)) {
4535
+		int e = errno;
4536
+		cli_dbgmsg("DEBUG nonblock_connect: connect(): fd=%d errno=%d: %s\n",
4537
+			sock, e, strerror(e));
4538
+		switch (e) {
4539
+			case EALREADY:
4540
+			case EINPROGRESS:
4541
+				break; /* wait for connection */
4542
+			case EISCONN:
4543
+				return 0; /* connected */
4544
+			default:
4545
+				cli_warnmsg("nonblock_connect: connect(): fd=%d errno=%d: %s\n",
4546
+					sock, e, strerror(e));
4547
+				return -1; /* failed */
4548
+		}
4549
+	} else
4550
+		return connect_error(sock);
4551
+
4552
+	numfd = sock + 1; /* Highest fdset fd plus 1 */
4553
+
4554
+	for (;;) {
4555
+		fd_set fds;
4556
+		struct timeval now;
4557
+		struct timeval wait;
4558
+		int n;
4559
+
4560
+		/* Force timeout if we ran out of time */
4561
+		gettimeofday(&now, 0);
4562
+		if (timercmp(&now, &timeout, >)) {
4563
+			cli_warnmsg("connect timing out (%d secs)\n",
4564
+				secs);
4565
+			break; /* failed */
4566
+		}
4567
+
4568
+		/* Calculate into 'wait' how long to wait */
4569
+		timersub(&timeout, &now, &wait); /* wait = timeout - now */
4570
+
4571
+		/* Init fds with 'sock' as the only fd */
4572
+		FD_ZERO(&fds);
4573
+		FD_SET(sock, &fds);
4574
+
4575
+		n = select(numfd, 0, &fds, 0, &wait);
4576
+		if (n < 0) {
4577
+			cli_warnmsg("nonblock_connect: select() failure %d: errno=%d: %s\n",
4578
+				select_failures, errno, strerror(errno));
4579
+			if (--select_failures >= 0)
4580
+				continue; /* keep waiting */
4581
+			break; /* failed */
4582
+		}
4583
+
4584
+		cli_dbgmsg("DEBUG nonblock_connect: select = %d\n", n);
4585
+
4586
+		if (n) {
4587
+			return connect_error(sock);
4588
+		}
4589
+
4590
+		/* Select returned, but there is no work to do... */
4591
+		if (--bogus_loops < 0) {
4592
+			cli_warnmsg("nonblock_connect: giving up due to excessive bogus loops\n");
4593
+			break; /* failed */
4594
+		}
4595
+
4596
+	} /* for loop: keep waiting */
4597
+
4598
+	return -1; /* failed */
4599
+}
4600
+
4601
+static int
4602
+connect_error(int sock)
4603
+{
4604
+	int optval;
4605
+	socklen_t optlen;
4606
+
4607
+	optlen = sizeof(optval);
4608
+	getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen);
4609
+
4610
+	if(optval)
4611
+		cli_warnmsg("connect_error: getsockopt(SO_ERROR): fd=%d error=%d: %s\n",
4612
+			sock, optval, strerror(optval));
4613
+
4614
+	return optval ? -1 : 0;
4615
+}
4616
+
4617
+#else
4618
+
4217 4619
 static	int	curl_has_segfaulted;
4218 4620
 /*
4219 4621
  * Inspite of numerious bug reports, curl is still buggy :-(
... ...
@@ -4352,6 +4754,7 @@ getURL(struct arg *arg)
4352 4352
 	signal(SIGSEGV, oldsegv);
4353 4353
 	return NULL;
4354 4354
 }
4355
+#endif
4355 4356
 
4356 4357
 #endif
4357 4358
 #endif