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