1 /*
2 * HTTPFS: import a file from a web server to local file system
3 * the main use is, to mount an iso on a web server with loop device
4 *
5 * depends on:
6 * FUSE: Filesystem in Userspace
7 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
8 *
9 * This program can be distributed under the terms of the GNU GPL.
10 *
11 */
12
13 /*
14 * (c) 2006 hmb marionraven at users.sourceforge.net
15 *
16 */
17
18 /*
19 * Modified to work with fuse 2.7.
20 * Added keepalive
21 * The passthru functionality removed to simplify the code.
22 * (c) 2008-2012 Michal Suchanek <hramrach@gmail.com>
23 *
24 */
25
26 #define FUSE_USE_VERSION 26
27
28 #include <fuse_lowlevel.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <assert.h>
36 #include <ctype.h>
37 #include <sys/stat.h>
38 #include <dirent.h>
39 #include <sys/types.h>
40 #include <sys/time.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <netdb.h>
44 #include <time.h>
45 #include <stddef.h>
46 #include <inttypes.h>
47
48 #ifdef USE_THREAD
49 #include <pthread.h>
50 static pthread_key_t url_key;
51 #define FUSE_LOOP fuse_session_loop_mt
52 #else
53 #define FUSE_LOOP fuse_session_loop
54 #endif
55
56 #ifdef USE_SSL
57 #include <gnutls/gnutls.h>
58 #include <gnutls/x509.h>
59 #endif
60
61 #define TIMEOUT 30
62 #define CONSOLE "/dev/console"
63 #define HEADER_SIZE 1024
64 #define VERSION "0.1.5 \"The Message\""
65
66 static char* argv0;
67
68 #define MAX_REQUEST (32*1024)
69 #define SOCK_CLOSED 0
70 #define SOCK_OPEN 1
71 #define SOCK_KEEPALIVE 2
72
73 typedef struct url {
74 int proto;
75 long timeout;
76 char * host; /*hostname*/
77 int port;
78 char * path; /*get path*/
79 char * name; /*file name*/
80 #ifdef USE_AUTH
81 char * auth; /*encoded auth data*/
82 #endif
83 #ifdef RETRY_ON_RESET
84 int retry_reset; /*retry reset connections*/
85 #endif
86 int sockfd;
87 int sock_type;
88 #ifdef USE_SSL
89 long ssl_log_level;
90 unsigned md5;
91 unsigned md2;
92 int ssl_initialized;
93 int ssl_connected;
94 gnutls_certificate_credentials_t sc;
95 gnutls_session_t ss;
96 const char * cafile;
97 #endif
98 char * req_buf;
99 size_t req_buf_size;
100 off_t file_size;
101 time_t last_modified;
102 } struct_url;
103
104 static struct_url main_url;
105
106 static off_t get_stat(struct_url*, struct stat * stbuf);
107 static ssize_t get_data(struct_url*, off_t start, size_t size);
108 static int open_client_socket(struct_url *url);
109 static int close_client_socket(struct_url *url);
110 static int close_client_force(struct_url *url);
111 static struct_url * thread_setup(void);
112 static void destroy_url_copy(void *);
113
114 /* Protocol symbols. */
115 #define PROTO_HTTP 0
116 #ifdef USE_SSL
117 #define PROTO_HTTPS 1
118 #endif
119
120 #ifdef USE_AUTH
121
122 static char b64_encode_table[64] = {
123 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', /* 0-7 */
124 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 8-15 */
125 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', /* 16-23 */
126 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', /* 24-31 */
127 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 32-39 */
128 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', /* 40-47 */
129 'w', 'x', 'y', 'z', '0', '1', '2', '3', /* 48-55 */
130 '4', '5', '6', '7', '8', '9', '+', '/' /* 56-63 */
131 };
132
133 /* Do base-64 encoding on a hunk of bytes. Return pointer to the
134 ** bytes generated. Base-64 encoding takes up 4/3 the space of the original,
135 ** plus a bit for end-padding. 3/2+5 gives a safe margin.
136 */
b64_encode(unsigned const char * ptr,long len)137 static char * b64_encode(unsigned const char* ptr, long len) {
138 char * space;
139 int ptr_idx;
140 int c = 0;
141 int d = 0;
142 int space_idx = 0;
143 int phase = 0;
144
145 /*FIXME calculate the occupied space properly*/
146 size_t size = ((size_t)len * 3) /2 + 5;
147 space = malloc(size+1);
148 space[size] = 0;
149
150 for (ptr_idx = 0; ptr_idx < len; ++ptr_idx) {
151 switch (phase++) {
152 case 0:
153 c = ptr[ptr_idx] >> 2;
154 d = (ptr[ptr_idx] & 0x3) << 4;
155 break;
156 case 1:
157 c = d | (ptr[ptr_idx] >> 4);
158 d = (ptr[ptr_idx] & 0xf) << 2;
159 break;
160 case 2:
161 c = d | (ptr[ptr_idx] >> 6);
162 if (space_idx < size) space[space_idx++] = b64_encode_table[c];
163 c = ptr[ptr_idx] & 0x3f;
164 break;
165 }
166 space[space_idx++] = b64_encode_table[c];
167 if (space_idx == size) return space;
168 phase %= 3;
169 }
170 if (phase != 0) {
171 space[space_idx++] = b64_encode_table[d];
172 if (space_idx == size) return space;
173 /* Pad with ='s. */
174 while (phase++ > 0) {
175 space[space_idx++] = '=';
176 if (space_idx == size) return space;
177 phase %= 3;
178 }
179 }
180 return space;
181 }
182
183 #endif /* USE_AUTH */
184
185 /*
186 * The FUSE operations originally ripped from the hello_ll sample.
187 */
188
httpfs_stat(fuse_ino_t ino,struct stat * stbuf)189 static int httpfs_stat(fuse_ino_t ino, struct stat *stbuf)
190 {
191 stbuf->st_ino = (uint32_t)ino;
192 switch (ino) {
193 case 1:
194 stbuf->st_mode = S_IFDIR | 0755;
195 stbuf->st_nlink = 2;
196 break;
197
198 case 2: {
199 struct_url * url = thread_setup();
200 stbuf->st_mode = S_IFREG | 0444;
201 stbuf->st_nlink = 1;
202 return (int) get_stat(url, stbuf);
203 }; break;
204
205 default:
206 errno = ENOENT;
207 return -1;
208 }
209 return 0;
210 }
211
httpfs_getattr(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)212 static void httpfs_getattr(fuse_req_t req, fuse_ino_t ino,
213 struct fuse_file_info *fi)
214 {
215 struct stat stbuf;
216
217 (void) fi;
218
219 memset(&stbuf, 0, sizeof(stbuf));
220 if (httpfs_stat(ino, &stbuf) < 0)
221 assert(errno),fuse_reply_err(req, errno);
222 else
223 fuse_reply_attr(req, &stbuf, 1.0);
224 }
225
httpfs_lookup(fuse_req_t req,fuse_ino_t parent,const char * name)226 static void httpfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
227 {
228 struct fuse_entry_param e;
229 memset(&e, 0, sizeof(e));
230 e.attr_timeout = 1.0;
231 e.entry_timeout = 1.0;
232
233 if (parent != 1 || strcmp(name, main_url.name) != 0){
234 e.ino = 0;
235 } else {
236 e.ino = 2;
237 if(httpfs_stat(e.ino, &e.attr) < 0){
238 assert(errno);
239 fuse_reply_err(req, errno);
240 return;
241 }
242
243 }
244 fuse_reply_entry(req, &e);
245 }
246
247 struct dirbuf {
248 char *p;
249 size_t size;
250 };
251
dirbuf_add(fuse_req_t req,struct dirbuf * b,const char * name,fuse_ino_t ino)252 static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
253 fuse_ino_t ino)
254 {
255 struct stat stbuf;
256 size_t oldsize = b->size;
257 b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
258 b->p = (char *) realloc(b->p, b->size);
259 memset(&stbuf, 0, sizeof(stbuf));
260 stbuf.st_ino = (uint32_t)ino;
261 fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
262 (off_t) b->size);
263 }
264
265 #define min(x, y) ((x) < (y) ? (x) : (y))
266
reply_buf_limited(fuse_req_t req,const char * buf,size_t bufsize,off_t off,size_t maxsize)267 static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
268 off_t off, size_t maxsize)
269 {
270 assert(off >= 0);
271
272 if (off < bufsize)
273 return fuse_reply_buf(req, buf + off,
274 min(bufsize - (size_t)off, maxsize));
275 else
276 return fuse_reply_buf(req, NULL, 0);
277 }
278
httpfs_readdir(fuse_req_t req,fuse_ino_t ino,size_t size,off_t off,struct fuse_file_info * fi)279 static void httpfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
280 off_t off, struct fuse_file_info *fi)
281 {
282 (void) fi;
283
284 if (ino != 1)
285 fuse_reply_err(req, ENOTDIR);
286 else {
287 struct dirbuf b;
288
289 memset(&b, 0, sizeof(b));
290 dirbuf_add(req, &b, ".", 1);
291 dirbuf_add(req, &b, "..", 1);
292 dirbuf_add(req, &b, main_url.name, 2);
293 reply_buf_limited(req, b.p, b.size, off, size);
294 free(b.p);
295 }
296 }
297
httpfs_open(fuse_req_t req,fuse_ino_t ino,struct fuse_file_info * fi)298 static void httpfs_open(fuse_req_t req, fuse_ino_t ino,
299 struct fuse_file_info *fi)
300 {
301 if (ino != 2)
302 fuse_reply_err(req, EISDIR);
303 else if ((fi->flags & 3) != O_RDONLY)
304 fuse_reply_err(req, EACCES);
305 else{
306 /* direct_io is supposed to allow partial reads. However, setting
307 * the flag causes read length max at 4096 bytes which leads to
308 * *many* requests, poor performance, and errors. Some resources
309 * like TCP ports are recycled too fast for Linux to cope.
310 */
311 //fi->direct_io = 1;
312 fuse_reply_open(req, fi);
313 }
314 }
315
httpfs_read(fuse_req_t req,fuse_ino_t ino,size_t size,off_t off,struct fuse_file_info * fi)316 static void httpfs_read(fuse_req_t req, fuse_ino_t ino, size_t size,
317 off_t off, struct fuse_file_info *fi)
318 {
319 (void) fi;
320
321 struct_url * url = thread_setup();
322 ssize_t res;
323
324 assert(ino == 2);
325
326 assert(url->file_size >= off);
327
328 size=min(size, (size_t)(url->file_size - off));
329
330 if(url->file_size == off) {
331 /* Handling of EOF is not well documented, returning EOF as error
332 * does not work but this does. */
333 fuse_reply_buf(req, NULL, 0);
334 return;
335 }
336 /* since we have to return all stuff requested the buffer cannot be
337 * allocated in advance */
338 if(url->req_buf
339 && ( (url->req_buf_size < size )
340 || ( (url->req_buf_size > size )
341 && (url->req_buf_size > MAX_REQUEST) ) ) ){
342 free(url->req_buf);
343 url->req_buf = 0;
344 }
345 if(! url->req_buf){
346 url->req_buf_size = size;
347 url->req_buf = malloc(size);
348 }
349
350 if((res = get_data(url, off, size)) < 0){
351 assert(errno);
352 fuse_reply_err(req, errno);
353 }else{
354 fuse_reply_buf(req, url->req_buf, (size_t)res);
355 }
356 }
357
358 static struct fuse_lowlevel_ops httpfs_oper = {
359 .lookup = httpfs_lookup,
360 .getattr = httpfs_getattr,
361 .readdir = httpfs_readdir,
362 .open = httpfs_open,
363 .read = httpfs_read,
364 };
365
366 /*
367 * A few utility functions
368 */
369 #ifdef NEED_STRNDUP
strndup(const char * str,size_t n)370 static char * strndup(const char * str, size_t n){
371 if(n > strlen(str)) n = strlen(str);
372 char * res = malloc(n + 1);
373 memcpy(res, str, n);
374 res[n] = 0;
375 return res;
376 }
377 #endif
378
mempref(const char * mem,const char * pref,size_t size,int case_sensitive)379 static int mempref(const char * mem, const char * pref, size_t size, int case_sensitive)
380 {
381 /* return true if found */
382 if (size < strlen(pref)) return 0;
383 if (case_sensitive)
384 return ! memcmp(mem, pref, strlen(pref));
385 else {
386 int i;
387 for (i = 0; i < strlen(pref); i++)
388 /* Unless somebody calling setlocale() behind our back locale should be C. */
389 /* It is important to not uppercase in languages like Turkish. */
390 if (tolower(mem[i]) != tolower(pref[i]))
391 return 0;
392 return 1;
393 }
394 }
395
396 #ifdef USE_SSL
397
398 static void errno_report(const char * where);
399 static void ssl_error(int error, gnutls_session_t ss, const char * where);
400 /* Functions to deal with gnutls_datum_t stolen from gnutls docs.
401 * The structure does not seem documented otherwise.
402 */
403 static gnutls_datum_t
load_file(const char * file)404 load_file (const char *file)
405 {
406 FILE *f;
407 gnutls_datum_t loaded_file = { NULL, 0 };
408 long filelen;
409 void *ptr;
410 f = fopen (file, "r");
411 if (!f)
412 errno_report(file);
413 else if (fseek (f, 0, SEEK_END) != 0)
414 errno_report(file);
415 else if ((filelen = ftell (f)) < 0)
416 errno_report(file);
417 else if (fseek (f, 0, SEEK_SET) != 0)
418 errno_report(file);
419 else if (!(ptr = malloc ((size_t) filelen)))
420 errno_report(file);
421 else if (fread (ptr, 1, (size_t) filelen, f) < (size_t) filelen)
422 errno_report(file);
423 else {
424 loaded_file.data = ptr;
425 loaded_file.size = (unsigned int) filelen;
426 fprintf(stderr, "Loaded '%s' %ld bytes\n", file, filelen);
427 /* fwrite(ptr, filelen, 1, stderr); */
428 }
429 return loaded_file;
430 }
431
432 static void
unload_file(gnutls_datum_t data)433 unload_file (gnutls_datum_t data)
434 {
435 free (data.data);
436 }
437
438 /* This function will print some details of the
439 * given session.
440 *
441 * Stolen from the GNUTLS docs.
442 */
443 int
print_ssl_info(gnutls_session_t session)444 print_ssl_info (gnutls_session_t session)
445 {
446 const char *tmp;
447 gnutls_credentials_type_t cred;
448 gnutls_kx_algorithm_t kx;
449 int dhe, ecdh;
450 dhe = ecdh = 0;
451 /* print the key exchange’s algorithm name
452 */
453 kx = gnutls_kx_get (session);
454 tmp = gnutls_kx_get_name (kx);
455 printf ("- Key Exchange: %s\n", tmp);
456 /* Check the authentication type used and switch
457 * to the appropriate.
458 */
459 cred = gnutls_auth_get_type (session);
460 switch (cred)
461 {
462 case GNUTLS_CRD_CERTIFICATE:
463 /* certificate authentication */
464 /* Check if we have been using ephemeral Diffie-Hellman.
465 */
466 if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS)
467 dhe = 1;
468 #if (GNUTLS_VERSION_MAJOR > 3 )
469 else if (kx == GNUTLS_KX_ECDHE_RSA || kx == GNUTLS_KX_ECDHE_ECDSA)
470 ecdh = 1;
471 #endif
472 /* cert should have been printed when it was verified */
473 break;
474 default:
475 printf("Not a x509 sesssion !?!\n");
476
477 }
478 #if (GNUTLS_VERSION_MAJOR > 3 )
479 /* switch */
480 if (ecdh != 0)
481 printf ("- Ephemeral ECDH using curve %s\n",
482 gnutls_ecc_curve_get_name (gnutls_ecc_curve_get (session)));
483 else
484 #endif
485 if (dhe != 0)
486 printf ("- Ephemeral DH using prime of %d bits\n",
487 gnutls_dh_get_prime_bits (session));
488 /* print the protocol’s name (ie TLS 1.0)
489 */
490 tmp = gnutls_protocol_get_name (gnutls_protocol_get_version (session));
491 printf ("- Protocol: %s\n", tmp);
492 /* print the certificate type of the peer.
493 * ie X.509
494 */
495 tmp =
496 gnutls_certificate_type_get_name (gnutls_certificate_type_get (session));
497 printf ("- Certificate Type: %s\n", tmp);
498 /* print the compression algorithm (if any)
499 */
500 tmp = gnutls_compression_get_name (gnutls_compression_get (session));
501 printf ("- Compression: %s\n", tmp);
502 /* print the name of the cipher used.
503 * ie 3DES.
504 */
505 tmp = gnutls_cipher_get_name (gnutls_cipher_get (session));
506 printf ("- Cipher: %s\n", tmp);
507 /* Print the MAC algorithms name.
508 * ie SHA1
509 */
510 tmp = gnutls_mac_get_name (gnutls_mac_get (session));
511 printf ("- MAC: %s\n", tmp);
512 printf ("Note: SSL paramaters may change as new connections are established to the server.\n");
513 return 0;
514 }
515
516
517
518 /* This function will try to verify the peer’s certificate, and
519 * also check if the hostname matches, and the activation, expiration dates.
520 *
521 * Stolen from the gnutls manual.
522 */
523 static int
verify_certificate_callback(gnutls_session_t session)524 verify_certificate_callback (gnutls_session_t session)
525 {
526 unsigned int status;
527 const gnutls_datum_t *cert_list;
528 unsigned int cert_list_size;
529 int ret;
530 gnutls_x509_crt_t cert;
531 gnutls_datum_t data = {0};
532 struct_url * url = gnutls_session_get_ptr (session);
533 const char *hostname = url->host;
534
535 /* This verification function uses the trusted CAs in the credentials
536 * structure. So you must have installed one or more CA certificates.
537 */
538 ret = gnutls_certificate_verify_peers2 (session, &status);
539 if (ret < 0)
540 {
541 ssl_error(ret, session, "verify certificate");
542 return GNUTLS_E_CERTIFICATE_ERROR;
543 }
544 if (status & GNUTLS_CERT_INVALID)
545 printf ("The server certificate is NOT trusted.\n");
546 if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
547 printf ("The server certificate uses an insecure algorithm.\n");
548 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
549 printf ("The server certificate hasn’t got a known issuer.\n");
550 if (status & GNUTLS_CERT_REVOKED)
551 printf ("The server certificate has been revoked.\n");
552 if (status & GNUTLS_CERT_EXPIRED)
553 printf ("The server certificate has expired\n");
554 if (status & GNUTLS_CERT_NOT_ACTIVATED)
555 printf ("The server certificate is not yet activated\n");
556 /* Up to here the process is the same for X.509 certificates and
557 * OpenPGP keys. From now on X.509 certificates are assumed. This can
558 * be easily extended to work with openpgp keys as well.
559 */
560 if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
561 return GNUTLS_E_CERTIFICATE_ERROR;
562 if (gnutls_x509_crt_init (&cert) < 0)
563 {
564 ssl_error(ret, session, "verify certificate");
565 return GNUTLS_E_CERTIFICATE_ERROR;
566 }
567 cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
568 if (cert_list == NULL)
569 {
570 printf ("No server certificate was found!\n");
571 return GNUTLS_E_CERTIFICATE_ERROR;
572 }
573 /* Check the hostname matches the certificate.
574 */
575 ret = gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER);
576 if (ret < 0)
577 {
578 ssl_error(ret, session, "parsing certificate");
579 return GNUTLS_E_CERTIFICATE_ERROR;
580 }
581 if (!(url->ssl_connected)) if (!gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_FULL, &data)) {
582 printf("%s", data.data);
583 gnutls_free(data.data);
584 }
585 if (!hostname || !gnutls_x509_crt_check_hostname (cert, hostname))
586 {
587 int found = 0;
588 if (hostname) {
589 int i;
590 size_t len = strlen(hostname);
591 if (*(hostname+len-1) == '.') len--;
592 if (!(url->ssl_connected)) printf ("Server hostname verification failed. Trying to peek into the cert.\n");
593 for (i=0;;i++) {
594 char * dn = NULL;
595 size_t dn_size = 0;
596 int dn_ret = 0;
597 int match=0;
598 gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, i, 0, dn, &dn_size);
599 if (dn_size) dn = malloc(dn_size + 1); /* nul not counted */
600 if (dn)
601 dn_ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, i, 0, dn, &dn_size);
602 if (!dn_ret){
603 if (dn) {
604 if (*(dn+dn_size-1) == '.') dn_size--;
605 if (len == dn_size)
606 match = ! strncmp(dn, hostname, len);
607 if (match) found = 1;
608 if (!(url->ssl_connected)) printf("Cert CN(%i): %s: %c\n", i, dn, match?'*':'X');
609 }}
610 else
611 ssl_error(dn_ret, session, "getting cert subject data");
612 if (dn) free(dn);
613 if (dn_ret || !dn)
614 break;
615 }
616 }
617 if(!found){
618 printf ("The server certificate’s owner does not match hostname ’%s’\n",
619 hostname);
620 return GNUTLS_E_CERTIFICATE_ERROR;
621 }
622 }
623 gnutls_x509_crt_deinit (cert);
624 /*
625 * It the status includes GNUTLS_CERT_INVALID whenever
626 * there is a problem and the other flags are just informative.
627 */
628 if (status & GNUTLS_CERT_INVALID)
629 return GNUTLS_E_CERTIFICATE_ERROR;
630 /* notify gnutls to continue handshake normally */
631 return 0;
632 }
633
634
logfunc(int level,const char * str)635 static void logfunc(int level, const char * str)
636 {
637 fputs(str, stderr);
638 }
639
ssl_error(int error,gnutls_session_t ss,const char * where)640 static void ssl_error(int error, gnutls_session_t ss, const char * where)
641 {
642 const char * err_desc;
643 if((error == GNUTLS_E_FATAL_ALERT_RECEIVED) || (error == GNUTLS_E_WARNING_ALERT_RECEIVED))
644 err_desc = gnutls_alert_get_name(gnutls_alert_get(ss));
645 else
646 err_desc = gnutls_strerror(error);
647
648 fprintf(stderr, "%s: %s: %d %s.\n", argv0, where, error, err_desc);
649 errno = EIO; /* FIXME is this used anywhere? */
650 }
651 #endif
652
errno_report(const char * where)653 static void errno_report(const char * where)
654 {
655 int e = errno;
656 fprintf(stderr, "%s: %s: %d %s.\n", argv0, where, errno, strerror(errno));
657 errno = e;
658 }
659
url_encode(char * path)660 static char * url_encode(char * path) {
661 return strdup(path); /*FIXME encode*/
662 }
663
664 /*
665 * functions for handling struct_url
666 */
667
init_url(struct_url * url)668 static int init_url(struct_url* url)
669 {
670 memset(url, 0, sizeof(*url));
671 url->sock_type = SOCK_CLOSED;
672 url->timeout = TIMEOUT;
673 #ifdef USE_SSL
674 url->cafile = CERT_STORE;
675 #endif
676 return 0;
677 }
678
free_url(struct_url * url)679 static int free_url(struct_url* url)
680 {
681 if(url->host) free(url->host);
682 url->host = 0;
683 if(url->path) free(url->path);
684 url->path = 0;
685 if(url->name) free(url->name);
686 url->name = 0;
687 #ifdef USE_AUTH
688 if(url->auth) free(url->auth);
689 url->auth = 0;
690 #endif
691 if(url->sock_type != SOCK_CLOSED)
692 close_client_force(url);
693 url->port = 0;
694 url->proto = 0; /* only after socket closed */
695 url->file_size=0;
696 url->last_modified=0;
697 return 0;
698 }
699
print_url(FILE * f,const struct_url * url)700 static void print_url(FILE *f, const struct_url * url)
701 {
702 char * protocol = "?!?";
703 switch(url->proto){
704 case PROTO_HTTP:
705 protocol = "http";
706 break;;
707 #ifdef USE_SSL
708 case PROTO_HTTPS:
709 protocol = "https";
710 break;;
711 #endif
712 }
713 fprintf(f, "file name: \t%s\n", url->name);
714 fprintf(f, "host name: \t%s\n", url->host);
715 fprintf(f, "port number: \t%d\n", url->port);
716 fprintf(f, "protocol: \t%s\n", protocol);
717 fprintf(f, "request path: \t%s\n", url->path);
718 #ifdef USE_AUTH
719 fprintf(f, "auth data: \t%s\n", url->auth ? "(present)" : "(null)");
720 #endif
721 }
722
parse_url(const char * url,struct_url * res)723 static int parse_url(const char * url, struct_url* res)
724 {
725 const char * url_orig = url;
726 char* http = "http://";
727 #ifdef USE_SSL
728 char* https = "https://";
729 #endif /* USE_SSL */
730 int path_start = '/';
731 assert(url);
732
733 if (strncmp(http, url, strlen(http)) == 0) {
734 url += strlen(http);
735 res->proto = PROTO_HTTP;
736 res->port = 80;
737 #ifdef USE_SSL
738 } else if (strncmp(https, url, strlen(https)) == 0) {
739 url += strlen(https);
740 res->proto = PROTO_HTTPS;
741 res->port = 443;
742 #endif /* USE_SSL */
743 } else {
744 fprintf(stderr, "Invalid protocol in url: %s\n", url_orig);
745 return -1;
746 }
747
748 /* determine if path was given */
749 if(strchr(url, path_start))
750 res->path = url_encode(strchr(url, path_start));
751 else{
752 path_start = 0;
753 res->path = strdup("/");
754 }
755
756
757 #ifdef USE_AUTH
758 /* Get user and password */
759 if(strchr(url, '@') && (strchr(url, '@') < strchr(url, path_start))){
760 res->auth = b64_encode((unsigned char *)url, strchr(url, '@') - url);
761 url = strchr(url, '@') + 1;
762 }else{
763 res->auth = 0;
764 }
765 #endif /* USE_AUTH */
766
767 /* Get port number. */
768 int host_end = path_start;
769 if(strchr(url, ':') && (strchr(url, ':') < strchr(url, path_start))){
770 /* FIXME check that port is a valid numeric value */
771 res->port = atoi(strchr(url, ':') + 1);
772 if (! res->port) {
773 fprintf(stderr, "Invalid port in url: %s\n", url_orig);
774 return -1;
775 }
776 host_end = ':';
777 }
778 /* Get the host name. */
779 if (url == strchr(url, host_end)){ /*no hastname in the url */
780 fprintf(stderr, "No hostname in url: %s\n", url_orig);
781 return -1;
782 }
783 res->host = strndup(url, (size_t)(strchr(url, host_end) - url));
784
785 /* Get the file name. */
786 url = strchr(url, path_start);
787 const char * end = url + strlen(url);
788 end--;
789
790 /* Handle broken urls with multiple slashes. */
791 while((end > url) && (*end == '/')) end--;
792 end++;
793 if((path_start == 0) || (end == url)
794 || (strncmp(url, "/", (size_t)(end - url)) == 0)){
795 res->name = strdup(res->host);
796 }else{
797 while(strchr(url, '/') && (strchr(url, '/') < end))
798 url = strchr(url, '/') + 1;
799 res->name = strndup(url, (size_t)(end - url));
800 }
801
802 return res->proto;
803 }
804
usage(void)805 static void usage(void)
806 {
807 fprintf(stderr, "%s >>> Version: %s <<<\n", __FILE__, VERSION);
808 fprintf(stderr, "usage: %s [-c [console]] "
809 #ifdef USE_SSL
810 "[-a file] [-d n] [-5] [-2] "
811 #endif
812 "[-f] [-t timeout] [-r] url mount-parameters\n\n", argv0);
813 fprintf(stderr, "\t -c \tuse console for standard input/output/error (default: %s)\n", CONSOLE);
814 #ifdef USE_SSL
815 fprintf(stderr, "\t -a \tCA file used to verify server certificate\n");
816 fprintf(stderr, "\t -d \tGNUTLS debug level\n");
817 fprintf(stderr, "\t -5 \tAllow RSA-MD5 cert\n");
818 fprintf(stderr, "\t -2 \tAllow RSA-MD2 cert\n");
819 #endif
820 fprintf(stderr, "\t -f \tstay in foreground - do not fork\n");
821 #ifdef RETRY_ON_RESET
822 fprintf(stderr, "\t -r \tretry connection on reset\n");
823 #endif
824 fprintf(stderr, "\t -t \tset socket timeout in seconds (default: %i)\n", TIMEOUT);
825 fprintf(stderr, "\tmount-parameters should include the mount point\n");
826 }
827
828 #define shift { if(!argv[1]) { usage(); return 4; };\
829 argc--; argv[1] = argv[0]; argv = argv + 1;}
830
convert_num(long * num,char ** argv)831 static int convert_num(long * num, char ** argv)
832 {
833 char * end = " ";
834 if( isdigit(*(argv[1]))) {
835 *num = strtol(argv[1], &end, 0);
836 /* now end should point to '\0' */
837 }
838 if(*end){
839 usage();
840 fprintf(stderr, "'%s' is not a number.\n",
841 argv[1]);
842 return -1;
843 }
844 return 0;
845 }
846
847
848
main(int argc,char * argv[])849 int main(int argc, char *argv[])
850 {
851 char * fork_terminal = CONSOLE;
852 int do_fork = 1;
853 putenv("TZ=");/*UTC*/
854 argv0 = argv[0];
855 init_url(&main_url);
856
857 while( argv[1] && (*(argv[1]) == '-') )
858 {
859 char * arg = argv[1]; shift;
860 while (*++arg){
861 switch (*arg){
862 case 'c': if( *(argv[1]) != '-' ) {
863 fork_terminal = argv[1]; shift;
864 }else{
865 fork_terminal = 0;
866 }
867 break;
868 #ifdef USE_SSL
869 case '2': main_url.md2 = GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2;
870 break;
871 case '5': main_url.md5 = GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5;
872 break;
873 case 'a': main_url.cafile = argv[1];
874 shift;
875 break;
876 case 'd': if (convert_num(&main_url.ssl_log_level, argv))
877 return 4;
878 shift;
879 break;
880 #endif
881 #ifdef RETRY_ON_RESET
882 case 'r': main_url.retry_reset = 1;
883 break;
884 #endif
885 case 't': if (convert_num(&main_url.timeout, argv))
886 return 4;
887 shift;
888 break;
889 case 'f': do_fork = 0;
890 break;
891 default:
892 usage();
893 fprintf(stderr, "Unknown option '%c'.\n", *arg);
894 return 4;
895 }
896 }
897 }
898
899 if (argc < 3) {
900 usage();
901 return 1;
902 }
903 if(parse_url(argv[1], &main_url) == -1){
904 fprintf(stderr, "invalid url: %s\n", argv[1]);
905 return 2;
906 }
907 print_url(stderr, &main_url);
908 int sockfd = open_client_socket(&main_url);
909 if(sockfd < 0) {
910 fprintf(stderr, "Connection failed.\n");
911 return 3;
912 }
913 #ifdef USE_SSL
914 else {
915 print_ssl_info(main_url.ss);
916 }
917 #endif
918 close_client_socket(&main_url);
919 struct stat st;
920 off_t size = get_stat(&main_url, &st);
921 if(size >= 0) {
922 fprintf(stderr, "file size: \t%" PRIdMAX "\n", (intmax_t)size);
923 }else{
924 return 3;
925 }
926
927 shift;
928 if(fork_terminal && access(fork_terminal, O_RDWR)){
929 errno_report(fork_terminal);
930 fork_terminal=0;
931 }
932
933 #ifdef USE_THREAD
934 close_client_force(&main_url); /* each thread should open its own socket */
935 pthread_key_create(&url_key, &destroy_url_copy);
936 #endif
937 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
938 struct fuse_chan *ch;
939 char *mountpoint;
940 int err = -1;
941 int fork_res = 0;
942
943 if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 &&
944 (ch = fuse_mount(mountpoint, &args)) != NULL) {
945 /* try to fork at some point where the setup is mostly done */
946 /* FIXME try to close std* and the like ? */
947 if(do_fork) fork_res = fork();
948
949 switch (fork_res) {
950 case 0:
951
952 {
953 if(fork_terminal){
954 /* if we can access the console use it */
955 int fd = open(fork_terminal, O_RDONLY);
956 dup2(fd, 0);
957 close (fd);
958 fd = open(fork_terminal, O_WRONLY);
959 dup2(fd, 1);
960 close (fd);
961 fd = open(fork_terminal, O_WRONLY|O_SYNC);
962 dup2(fd, 2);
963 close (fd);
964 }
965
966 struct fuse_session *se;
967 se = fuse_lowlevel_new(&args, &httpfs_oper,
968 sizeof(httpfs_oper), NULL);
969 if (se != NULL) {
970 if (fuse_set_signal_handlers(se) != -1) {
971 fuse_session_add_chan(se, ch);
972 err = FUSE_LOOP(se);
973 fuse_remove_signal_handlers(se);
974 fuse_session_remove_chan(ch);
975 }
976 fuse_session_destroy(se);
977 }
978 fuse_unmount(mountpoint, ch);
979 }
980 break;;
981 case -1:
982 errno_report("fork");
983 break;;
984 default:
985 err = 0;
986 break;;
987 }
988 }
989 fuse_opt_free_args(&args);
990
991 return err ? err : 0;
992 }
993
994
995
996 /*
997 * Socket operations that abstract ssl and keepalive as much as possible.
998 * Keepalive is set when parsing the headers.
999 *
1000 */
1001
close_client_socket(struct_url * url)1002 static int close_client_socket(struct_url *url) {
1003 if (url->sock_type == SOCK_KEEPALIVE) return SOCK_KEEPALIVE;
1004 return close_client_force(url);
1005 }
1006
close_client_force(struct_url * url)1007 static int close_client_force(struct_url *url) {
1008 if(url->sock_type != SOCK_CLOSED){
1009 #ifdef USE_SSL
1010 if (url->proto == PROTO_HTTPS) {
1011 gnutls_deinit(url->ss);
1012 }
1013 #endif
1014 close(url->sockfd);
1015 }
1016 return url->sock_type = SOCK_CLOSED;
1017 }
1018
1019 #ifdef USE_THREAD
1020
destroy_url_copy(void * urlptr)1021 static void destroy_url_copy(void * urlptr)
1022 {
1023 if(urlptr){
1024 fprintf(stderr, "%s: Thread %08lX ended.\n", argv0, pthread_self());
1025 close_client_force(urlptr);
1026 free(urlptr);
1027 }
1028 }
1029
create_url_copy(const struct_url * url)1030 static void * create_url_copy(const struct_url * url)
1031 {
1032 void * res = malloc(sizeof(struct_url));
1033 memcpy(res, url, sizeof(struct_url));
1034 return res;
1035 }
1036
thread_setup(void)1037 static struct_url * thread_setup(void)
1038 {
1039 struct_url * res = pthread_getspecific(url_key);
1040 if(!res) {
1041 fprintf(stderr, "%s: Thread %08lX started.\n", argv0, pthread_self());
1042 res = create_url_copy(&main_url);
1043 pthread_setspecific(url_key, res);
1044 }
1045 return res;
1046 }
1047
1048 #else /*USE_THREAD*/
thread_setup(void)1049 static struct_url * thread_setup(void) { return &main_url; }
1050 #endif
1051
1052
read_client_socket(struct_url * url,void * buf,size_t len)1053 static ssize_t read_client_socket(struct_url *url, void * buf, size_t len) {
1054 ssize_t res;
1055 struct timeval timeout;
1056 timeout.tv_sec = url->timeout;
1057 timeout.tv_usec = 0;
1058 setsockopt(url->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
1059 #ifdef USE_SSL
1060 if (url->proto == PROTO_HTTPS) {
1061 res = gnutls_record_recv(url->ss, buf, len);
1062 if (res <= 0) ssl_error((int)res, url->ss, "read");
1063 } else
1064 #endif
1065 {
1066 res = read(url->sockfd, buf, len);
1067 if (res <= 0) errno_report("read");
1068 }
1069 return res;
1070 }
1071
1072 static ssize_t
write_client_socket(struct_url * url,const void * buf,size_t len)1073 write_client_socket(struct_url *url, const void * buf, size_t len)
1074 {
1075 do {
1076 int fd = open_client_socket(url);
1077 ssize_t res;
1078
1079 if (fd < 0) return -1; /*error hopefully reported by open*/
1080 #ifdef USE_SSL
1081 if (url->proto == PROTO_HTTPS) {
1082 res = gnutls_record_send(url->ss, buf, len);
1083 if (res <= 0) ssl_error((int)res, url->ss, "write");
1084 /*
1085 * It is suggested to retry GNUTLS_E_INTERRUPTED and GNUTLS_E_AGAIN
1086 * However, retrying only causes delay in practice. FIXME
1087 */
1088 } else
1089 #endif
1090 {
1091 res = write(url->sockfd, buf, len);
1092 if (res <= 0) errno_report("write");
1093 }
1094 if ( !(res <= 0) || (url->sock_type != SOCK_KEEPALIVE )) return res;
1095
1096 /* retry a failed keepalive socket */
1097 close_client_force(url);
1098 } while (url->sock_type == SOCK_KEEPALIVE);
1099 return -1; /*should not reach*/
1100 }
1101
1102 /*
1103 * Function yields either a positive int after connecting to
1104 * host 'hostname' on port 'port' or < 0 in case of error
1105 *
1106 * It handles keepalive by not touching keepalive sockets.
1107 * The SSL context is created so that read/write can use it.
1108 *
1109 * hostname is something like 'www.tmtd.de' or 192.168.0.86
1110 * port is expected in machine order (not net order)
1111 *
1112 * ((Flonix defines USE_IPV6))
1113 *
1114 */
1115 #if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED)
1116 #define USE_IPV6
1117 #endif
1118
open_client_socket(struct_url * url)1119 static int open_client_socket(struct_url *url) {
1120 #ifdef USE_IPV6
1121 struct addrinfo hints;
1122 char portstr[10];
1123 int gaierr;
1124 struct addrinfo* ai;
1125 struct addrinfo* aiv4;
1126 struct addrinfo* aiv6 = 0;
1127 struct sockaddr_in6 sa;
1128 #else /* USE_IPV6 */
1129 struct hostent *he;
1130 struct sockaddr_in sa;
1131 #endif /* USE_IPV6 */
1132 socklen_t sa_len;
1133 int sock_family, sock_type, sock_protocol;
1134
1135 if(url->sock_type == SOCK_KEEPALIVE) return url->sock_type;
1136 if(url->sock_type != SOCK_CLOSED) close_client_socket(url);
1137
1138 (void) memset((void*) &sa, 0, sizeof(sa));
1139
1140 #ifdef USE_IPV6
1141 (void) memset(&hints, 0, sizeof(hints));
1142 hints.ai_family = PF_UNSPEC;
1143 hints.ai_socktype = SOCK_STREAM;
1144 (void) snprintf(portstr, sizeof(portstr), "%d", (int) url->port);
1145 if ((gaierr = getaddrinfo(url->host, portstr, &hints, &ai)) != 0) {
1146 (void) fprintf(stderr, "%s: getaddrinfo %s - %s\n",
1147 argv0, url->host, gai_strerror(gaierr));
1148 return -1;
1149 }
1150
1151 /* Find the first IPv4 and IPv6 entries. */
1152 for (aiv4 = ai; aiv4 != NULL; aiv4 = aiv4->ai_next) {
1153 if (aiv4->ai_family == AF_INET)
1154 break;
1155 if ((aiv4->ai_family == AF_INET6) && (aiv6 == NULL))
1156 aiv6 = aiv4;
1157 }
1158
1159 /* If there's an IPv4 address, use that, otherwise try IPv6. */
1160 if (aiv4 == NULL)
1161 aiv4 = aiv6;
1162 if (aiv4 == NULL) {
1163 (void) fprintf(stderr, "%s: no valid address found for host %s\n",
1164 argv0, url->host);
1165 errno = EIO;
1166 return -1;
1167 }
1168 if (sizeof(sa) < aiv4->ai_addrlen) {
1169 (void) fprintf(stderr, "%s - sockaddr too small (%lu < %lu)\n",
1170 url->host, (unsigned long) sizeof(sa),
1171 (unsigned long) aiv4->ai_addrlen);
1172 errno = EIO;
1173 return -1;
1174 }
1175 sock_family = aiv4->ai_family;
1176 sock_type = aiv4->ai_socktype;
1177 sock_protocol = aiv4->ai_protocol;
1178 sa_len = aiv4->ai_addrlen;
1179 (void) memmove(&sa, aiv4->ai_addr, sa_len);
1180 freeaddrinfo(ai);
1181
1182 #else /* USE_IPV6 */
1183
1184 he = gethostbyname(url->host);
1185 if (he == NULL) {
1186 (void) fprintf(stderr, "%s: unknown host - %s\n", argv0, url->host);
1187 errno = EIO;
1188 return -1;
1189 }
1190 sock_family = sa.sin_family = he->h_addrtype;
1191 sock_type = SOCK_STREAM;
1192 sock_protocol = 0;
1193 sa_len = sizeof(sa);
1194 (void) memmove(&sa.sin_addr, he->h_addr, he->h_length);
1195 sa.sin_port = htons(url->port);
1196
1197 #endif /* USE_IPV6 */
1198
1199 url->sockfd = socket(sock_family, sock_type, sock_protocol);
1200 if (url->sockfd < 0) {
1201 errno_report("couldn't get socket");
1202
1203 return -1;
1204 }
1205 if (connect(url->sockfd, (struct sockaddr*) &sa, sa_len) < 0) {
1206 errno_report("couldn't connect socket");
1207 return -1;
1208 }
1209
1210 #ifdef USE_SSL
1211 if ((url->proto) == PROTO_HTTPS) {
1212 /* Make SSL connection. */
1213 int r = 0;
1214 const char * ps = "NORMAL"; /* FIXME allow user setting */
1215 const char * errp = NULL;
1216 if (!url->ssl_initialized) {
1217 r = gnutls_global_init();
1218 if (!r)
1219 r = gnutls_certificate_allocate_credentials (&url->sc); /* docs suggest to share creds */
1220 if (url->cafile) {
1221 if (!r)
1222 r = gnutls_certificate_set_x509_trust_file (url->sc, url->cafile, GNUTLS_X509_FMT_PEM);
1223 if (r>0)
1224 fprintf(stderr, "%s: SSL init: loaded %i CA certificate(s).\n", argv0, r);
1225 if (r>0) r = 0;
1226 }
1227 if (!r)
1228 gnutls_certificate_set_verify_function (url->sc, verify_certificate_callback);
1229 gnutls_certificate_set_verify_flags (url->sc, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT /* suggested */
1230 | url->md5 | url->md2 ); /* oprional for old cert compat */
1231 if (!r) url->ssl_initialized = 1;
1232 gnutls_global_set_log_level((int)url->ssl_log_level);
1233 gnutls_global_set_log_function(&logfunc);
1234 }
1235 if (r) {
1236 ssl_error(r, url->ss, "SSL init");
1237 return -1;
1238 }
1239
1240 r = gnutls_init(&url->ss, GNUTLS_CLIENT);
1241 if (!r) gnutls_session_set_ptr(url->ss, url); /* used in cert verifier */
1242 if (!r) r = gnutls_priority_set_direct(url->ss, ps, &errp);
1243 if (!r) r = gnutls_credentials_set(url->ss, GNUTLS_CRD_CERTIFICATE, url->sc);
1244 if (!r) gnutls_transport_set_ptr(url->ss, (gnutls_transport_ptr_t) (intptr_t) url->sockfd);
1245 if (!r) r = gnutls_handshake (url->ss); /* FIXME gnutls_error_is_fatal is recommended here */
1246 if (r) {
1247 close(url->sockfd);
1248 if (errp) fprintf(stderr, "%s: invalid SSL priority\n %s\n %*s\n", argv0, ps, (int)(errp - ps), "^");
1249 fprintf(stderr, "%s: %s:%d - ", argv0, url->host, url->port);
1250 ssl_error(r, url->ss, "SSL connection failed");
1251 gnutls_deinit(url->ss);
1252 errno = EIO;
1253 return -1;
1254 }
1255 url->ssl_connected = 1; /* Prevent printing cert data over and over again */
1256 }
1257 #endif
1258 return url->sock_type = SOCK_OPEN;
1259 }
1260
1261 static void
plain_report(const char * reason,const char * method,const char * buf,size_t len)1262 plain_report(const char * reason, const char * method,
1263 const char * buf, size_t len)
1264 {
1265 fprintf(stderr, "%s: %s: %s\n", argv0, method, reason);
1266 fwrite(buf, len, 1, stderr);
1267 if(len && ( *(buf+len-1) != '\n')) fputc('\n', stderr);
1268 }
1269
1270 /*
1271 * Scan the received header for interesting fields. Since C does not have
1272 * tools for working with potentially unterminated strings this is quite
1273 * long and ugly.
1274 *
1275 * Return the length of the header in case part of the data was
1276 * read with the header.
1277 * Content-Length means different thing whith GET and HEAD.
1278 */
1279
1280 static ssize_t
parse_header(struct_url * url,const char * buf,size_t bytes,const char * method,off_t * content_length,int expect)1281 parse_header(struct_url *url, const char * buf, size_t bytes,
1282 const char * method, off_t * content_length, int expect)
1283 {
1284 /* FIXME check the header parser */
1285 int status;
1286 const char * ptr = buf;
1287 const char * end;
1288 int seen_accept = 0, seen_length = 0, seen_close = 0;
1289
1290 if (bytes <= 0) {
1291 return -1;
1292 }
1293
1294 end = memchr(ptr, '\n', bytes);
1295 if(!end) {
1296 plain_report ( "reply does not contain newline!", method, buf, 0);
1297 errno = EIO;
1298 return -1;
1299 }
1300 end = ptr;
1301 while(1){
1302 end = memchr(end + 1, '\n', bytes - (size_t)(end - ptr));
1303 if(!end || ((end + 1) >= (ptr + bytes)) ) {
1304 plain_report ("reply does not contain end of header!",
1305 method, buf, bytes);
1306 errno = EIO;
1307 return -1;
1308 }
1309 if(mempref(end, "\n\r\n", bytes - (size_t)(end - ptr), 1)) break;
1310 }
1311 ssize_t header_len = (end + 3) - ptr;
1312
1313 end = memchr(ptr, '\n', bytes);
1314 char * http = "HTTP/1.1 ";
1315 if(!mempref(ptr, http, (size_t)(end - ptr), 1) || !isdigit( *(ptr + strlen(http))) ) {
1316 plain_report ("reply does not contain status!",
1317 method, buf, (size_t)header_len);
1318 errno = EIO;
1319 return -1;
1320 }
1321 status = (int)strtol( ptr + strlen(http), (char **)&ptr, 10);
1322 if (status != expect) {
1323 fprintf(stderr, "%s: %s: failed with status: %d%.*s.\n",
1324 argv0, method, status, (int)((end - ptr) - 1), ptr);
1325 if (!strcmp("HEAD", method)) fwrite(buf, bytes, 1, stderr); /*DEBUG*/
1326 errno = EIO;
1327 if (status == 404) errno = ENOENT;
1328 return -1;
1329 }
1330
1331 char * content_length_str = "Content-Length: ";
1332 char * accept = "Accept-Ranges: bytes";
1333 char * range = "Content-Range: bytes";
1334 char * date = "Last-Modified: ";
1335 char * close = "Connection: close";
1336 struct tm tm;
1337 while(1)
1338 {
1339 ptr = end+1;
1340 if( !(ptr < buf + (header_len - 4))){
1341 if(seen_accept && seen_length){
1342 if(url->sock_type == SOCK_OPEN && !seen_close)
1343 url->sock_type = SOCK_KEEPALIVE;
1344 if(url->sock_type == SOCK_KEEPALIVE && seen_close)
1345 url->sock_type = SOCK_OPEN;
1346 return header_len;
1347 }
1348 close_client_force(url);
1349 errno = EIO;
1350 if(! seen_accept){
1351 plain_report("server must Accept-Range: bytes",
1352 method, buf, 0);
1353 return -1;
1354 }
1355 if(! seen_length){
1356 plain_report("reply didn't contain Content-Length!",
1357 method, buf, 0);
1358 return -1;
1359 }
1360 /* fallback - should not reach */
1361 plain_report("error parsing header.",
1362 method, buf, 0);
1363 return -1;
1364
1365 }
1366 end = memchr(ptr, '\n', bytes - (size_t)(ptr - buf));
1367 if( mempref(ptr, content_length_str, (size_t)(end - ptr), 0)
1368 && isdigit( *(ptr + strlen(content_length_str))) ){
1369 *content_length = atoll(ptr + strlen(content_length_str));
1370 seen_length = 1;
1371 continue;
1372 }
1373 if( mempref(ptr, range, (size_t)(end - ptr), 0) ){
1374 seen_accept = 1;
1375 continue;
1376 }
1377 if( mempref(ptr, accept, (size_t)(end - ptr), 0) ){
1378 seen_accept = 1;
1379 continue;
1380 }
1381 if( mempref(ptr, date, (size_t)(end - ptr), 0) ){
1382 memset(&tm, 0, sizeof(tm));
1383 if(!strptime(ptr + strlen(date),
1384 "%n%a, %d %b %Y %T %Z", &tm)){
1385 plain_report("invalid time",
1386 method, ptr + strlen(date),
1387 (size_t)(end - ptr) - strlen(date)) ;
1388 continue;
1389 }
1390 url->last_modified = mktime(&tm);
1391 continue;
1392 }
1393 if( mempref(ptr, close, (size_t)(end - ptr), 0) ){
1394 seen_close = 1;
1395 }
1396 }
1397 }
1398
1399 /*
1400 * Send the header, and get a reply.
1401 * This relies on 1k reads and writes being generally atomic -
1402 * - they fit into a single frame. The header should fit into that
1403 * and we do not need partial read handling so the exchange is simple.
1404 * However, broken sockets have to be handled here.
1405 */
1406
1407 static ssize_t
exchange(struct_url * url,char * buf,const char * method,off_t * content_length,off_t start,off_t end,size_t * header_length)1408 exchange(struct_url *url, char * buf, const char * method,
1409 off_t * content_length, off_t start, off_t end, size_t * header_length)
1410 {
1411 ssize_t res;
1412 size_t bytes;
1413 int range = (end > 0);
1414
1415 /* Build request buffer, starting with the request method. */
1416
1417 bytes = (size_t)snprintf(buf, HEADER_SIZE, "%s %s HTTP/1.1\r\nHost: %s\r\n",
1418 method, url->path, url->host);
1419 bytes += (size_t)snprintf(buf + bytes, HEADER_SIZE - bytes,
1420 "User-Agent: %s %s\r\n", __FILE__, VERSION);
1421 if (range) bytes += (size_t)snprintf(buf + bytes, HEADER_SIZE - bytes,
1422 "Range: bytes=%" PRIdMAX "-%" PRIdMAX "\r\n", (intmax_t)start, (intmax_t)end);
1423 #ifdef USE_AUTH
1424 if ( url->auth )
1425 bytes += (size_t)snprintf(buf + bytes, HEADER_SIZE - bytes,
1426 "Authorization: Basic %s\r\n", url->auth);
1427 #endif
1428 bytes += (size_t)snprintf(buf + bytes, HEADER_SIZE - bytes, "\r\n");
1429
1430
1431 /* Now actually send it. */
1432 while(1){
1433 /*
1434 * It looks like the sockets abandoned by the server do not go away.
1435 * Instead of returning EPIPE they allow zero writes and zero reads. So
1436 * this is the place where a stale socket would be detected.
1437 *
1438 * Socket that return EAGAIN cause long delays. Reopen.
1439 *
1440 * Reset errno because reads/writes of 0 bytes are a success and are not
1441 * required to touch it but are handled as error below.
1442 *
1443 */
1444 /* ECONNRESET happens with some dodgy servers so may need to handle that.
1445 * Allow for building without it in case it is not defined.
1446 */
1447 #ifdef RETRY_ON_RESET
1448 #define CONNFAIL ((res <= 0) && ! errno) || (errno == EAGAIN) || (errno == EPIPE) || \
1449 (url->retry_reset && (errno == ECONNRESET))
1450 #else
1451 #define CONNFAIL ((res <= 0) && ! errno) || (errno == EAGAIN) || (errno == EPIPE)
1452 #endif
1453 errno = 0;
1454 res = write_client_socket(url, buf, bytes);
1455 if (CONNFAIL) {
1456 errno_report("exchange: failed to send request, retrying"); /* DEBUG */
1457 close_client_force(url);
1458 continue;
1459 }
1460 if (res <= 0){
1461 errno_report("exchange: failed to send request"); /* DEBUG */
1462 return res;
1463 }
1464 res = read_client_socket(url, buf, HEADER_SIZE);
1465 if (CONNFAIL) {
1466 errno_report("exchange: did not receive a reply, retrying"); /* DEBUG */
1467 close_client_force(url);
1468 continue;
1469 } else if (res <= 0) {
1470 errno_report("exchange: failed receving reply from server"); /* DEBUG */
1471 return res;
1472 } else break;
1473 /* Not reached */
1474 }
1475 bytes = (size_t)res;
1476
1477 res = parse_header(url, buf, bytes, method, content_length,
1478 range ? 206 : 200);
1479 if (res <= 0){
1480 plain_report("exchange: server error", method, buf, bytes);
1481 return res;
1482 }
1483
1484 if (header_length) *header_length = (size_t)res;
1485
1486 return (ssize_t)bytes;
1487 }
1488
1489 /*
1490 * Function uses HEAD-HTTP-Request
1491 * to determine the file size
1492 */
1493
get_stat(struct_url * url,struct stat * stbuf)1494 static off_t get_stat(struct_url *url, struct stat * stbuf) {
1495 char buf[HEADER_SIZE];
1496
1497 if( exchange(url, buf, "HEAD", &(url->file_size), 0, 0, 0) < 0 )
1498 return -1;
1499
1500 close_client_socket(url);
1501
1502 stbuf->st_mtime = url->last_modified;
1503 return stbuf->st_size = url->file_size;
1504 }
1505
1506 /*
1507 * get_data does all the magic
1508 * a GET-Request with Range-Header
1509 * allows to read arbitrary bytes
1510 */
1511
get_data(struct_url * url,off_t start,size_t size)1512 static ssize_t get_data(struct_url *url, off_t start, size_t size)
1513 {
1514 char buf[HEADER_SIZE];
1515 const char * b;
1516 ssize_t bytes;
1517 off_t end = start + (off_t)size - 1;
1518 char * destination = url->req_buf;
1519 off_t content_length;
1520 size_t header_length;
1521
1522 bytes = exchange(url, buf, "GET", &content_length,
1523 start, end, &header_length);
1524 if(bytes <= 0) return -1;
1525
1526 if (content_length != size) {
1527 plain_report("didn't yield the whole piece.", "GET", 0, 0);
1528 size = min((size_t)content_length, size);
1529 }
1530
1531
1532 b = buf + header_length;
1533
1534 bytes -= (b - buf);
1535 memcpy(destination, b, (size_t)bytes);
1536 size -= (size_t)bytes;
1537 destination +=bytes;
1538 for (; size > 0; size -= (size_t)bytes, destination += bytes) {
1539
1540 bytes = read_client_socket(url, destination, size);
1541 if (bytes < 0) {
1542 errno_report("GET (read)");
1543 return -1;
1544 }
1545 if (bytes == 0) {
1546 break;
1547 }
1548 }
1549
1550 close_client_socket(url);
1551
1552 return (ssize_t)(end - start) + 1 - (ssize_t)size;
1553 }
1554