Merge from vendor branch DIFFUTILS:
[dragonfly.git] / lib / libfetch / common.c
1 /*-
2  * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD: src/lib/libfetch/common.c,v 1.7.2.13 2003/06/06 06:45:25 des Exp $
29  * $DragonFly: src/lib/libfetch/common.c,v 1.2 2003/06/17 04:26:49 dillon Exp $
30  */
31
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <sys/time.h>
35 #include <sys/uio.h>
36 #include <netinet/in.h>
37
38 #include <errno.h>
39 #include <netdb.h>
40 #include <pwd.h>
41 #include <stdarg.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "fetch.h"
48 #include "common.h"
49
50
51 /*** Local data **************************************************************/
52
53 /*
54  * Error messages for resolver errors
55  */
56 static struct fetcherr _netdb_errlist[] = {
57         { EAI_NODATA,   FETCH_RESOLV,   "Host not found" },
58         { EAI_AGAIN,    FETCH_TEMP,     "Transient resolver failure" },
59         { EAI_FAIL,     FETCH_RESOLV,   "Non-recoverable resolver failure" },
60         { EAI_NONAME,   FETCH_RESOLV,   "No address record" },
61         { -1,           FETCH_UNKNOWN,  "Unknown resolver error" }
62 };
63
64 /* End-of-Line */
65 static const char ENDL[2] = "\r\n";
66
67
68 /*** Error-reporting functions ***********************************************/
69
70 /*
71  * Map error code to string
72  */
73 static struct fetcherr *
74 _fetch_finderr(struct fetcherr *p, int e)
75 {
76         while (p->num != -1 && p->num != e)
77                 p++;
78         return (p);
79 }
80
81 /*
82  * Set error code
83  */
84 void
85 _fetch_seterr(struct fetcherr *p, int e)
86 {
87         p = _fetch_finderr(p, e);
88         fetchLastErrCode = p->cat;
89         snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
90 }
91
92 /*
93  * Set error code according to errno
94  */
95 void
96 _fetch_syserr(void)
97 {
98         switch (errno) {
99         case 0:
100                 fetchLastErrCode = FETCH_OK;
101                 break;
102         case EPERM:
103         case EACCES:
104         case EROFS:
105         case EAUTH:
106         case ENEEDAUTH:
107                 fetchLastErrCode = FETCH_AUTH;
108                 break;
109         case ENOENT:
110         case EISDIR: /* XXX */
111                 fetchLastErrCode = FETCH_UNAVAIL;
112                 break;
113         case ENOMEM:
114                 fetchLastErrCode = FETCH_MEMORY;
115                 break;
116         case EBUSY:
117         case EAGAIN:
118                 fetchLastErrCode = FETCH_TEMP;
119                 break;
120         case EEXIST:
121                 fetchLastErrCode = FETCH_EXISTS;
122                 break;
123         case ENOSPC:
124                 fetchLastErrCode = FETCH_FULL;
125                 break;
126         case EADDRINUSE:
127         case EADDRNOTAVAIL:
128         case ENETDOWN:
129         case ENETUNREACH:
130         case ENETRESET:
131         case EHOSTUNREACH:
132                 fetchLastErrCode = FETCH_NETWORK;
133                 break;
134         case ECONNABORTED:
135         case ECONNRESET:
136                 fetchLastErrCode = FETCH_ABORT;
137                 break;
138         case ETIMEDOUT:
139                 fetchLastErrCode = FETCH_TIMEOUT;
140                 break;
141         case ECONNREFUSED:
142         case EHOSTDOWN:
143                 fetchLastErrCode = FETCH_DOWN;
144                 break;
145 default:
146                 fetchLastErrCode = FETCH_UNKNOWN;
147         }
148         snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));
149 }
150
151
152 /*
153  * Emit status message
154  */
155 void
156 _fetch_info(const char *fmt, ...)
157 {
158         va_list ap;
159
160         va_start(ap, fmt);
161         vfprintf(stderr, fmt, ap);
162         va_end(ap);
163         fputc('\n', stderr);
164 }
165
166
167 /*** Network-related utility functions ***************************************/
168
169 /*
170  * Return the default port for a scheme
171  */
172 int
173 _fetch_default_port(const char *scheme)
174 {
175         struct servent *se;
176
177         if ((se = getservbyname(scheme, "tcp")) != NULL)
178                 return (ntohs(se->s_port));
179         if (strcasecmp(scheme, SCHEME_FTP) == 0)
180                 return (FTP_DEFAULT_PORT);
181         if (strcasecmp(scheme, SCHEME_HTTP) == 0)
182                 return (HTTP_DEFAULT_PORT);
183         return (0);
184 }
185
186 /*
187  * Return the default proxy port for a scheme
188  */
189 int
190 _fetch_default_proxy_port(const char *scheme)
191 {
192         if (strcasecmp(scheme, SCHEME_FTP) == 0)
193                 return (FTP_DEFAULT_PROXY_PORT);
194         if (strcasecmp(scheme, SCHEME_HTTP) == 0)
195                 return (HTTP_DEFAULT_PROXY_PORT);
196         return (0);
197 }
198
199
200 /*
201  * Create a connection for an existing descriptor.
202  */
203 conn_t *
204 _fetch_reopen(int sd)
205 {
206         conn_t *conn;
207
208         /* allocate and fill connection structure */
209         if ((conn = calloc(1, sizeof(*conn))) == NULL)
210                 return (NULL);
211         conn->sd = sd;
212         ++conn->ref;
213         return (conn);
214 }
215
216
217 /*
218  * Bump a connection's reference count.
219  */
220 conn_t *
221 _fetch_ref(conn_t *conn)
222 {
223
224         ++conn->ref;
225         return (conn);
226 }
227
228
229 /*
230  * Bind a socket to a specific local address
231  */
232 int
233 _fetch_bind(int sd, int af, const char *addr)
234 {
235         struct addrinfo hints, *res, *res0;
236         int err;
237
238         memset(&hints, 0, sizeof(hints));
239         hints.ai_family = af;
240         hints.ai_socktype = SOCK_STREAM;
241         hints.ai_protocol = 0;
242         if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)
243                 return (-1);
244         for (res = res0; res; res = res->ai_next)
245                 if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)
246                         return (0);
247         return (-1);
248 }
249
250
251 /*
252  * Establish a TCP connection to the specified port on the specified host.
253  */
254 conn_t *
255 _fetch_connect(const char *host, int port, int af, int verbose)
256 {
257         conn_t *conn;
258         char pbuf[10];
259         const char *bindaddr;
260         struct addrinfo hints, *res, *res0;
261         int sd, err;
262
263         DEBUG(fprintf(stderr, "---> %s:%d\n", host, port));
264
265         if (verbose)
266                 _fetch_info("looking up %s", host);
267
268         /* look up host name and set up socket address structure */
269         snprintf(pbuf, sizeof(pbuf), "%d", port);
270         memset(&hints, 0, sizeof(hints));
271         hints.ai_family = af;
272         hints.ai_socktype = SOCK_STREAM;
273         hints.ai_protocol = 0;
274         if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
275                 _netdb_seterr(err);
276                 return (NULL);
277         }
278         bindaddr = getenv("FETCH_BIND_ADDRESS");
279
280         if (verbose)
281                 _fetch_info("connecting to %s:%d", host, port);
282
283         /* try to connect */
284         for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {
285                 if ((sd = socket(res->ai_family, res->ai_socktype,
286                          res->ai_protocol)) == -1)
287                         continue;
288                 if (bindaddr != NULL && *bindaddr != '\0' &&
289                     _fetch_bind(sd, res->ai_family, bindaddr) != 0) {
290                         _fetch_info("failed to bind to '%s'", bindaddr);
291                         close(sd);
292                         continue;
293                 }
294                 if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)
295                         break;
296                 close(sd);
297         }
298         freeaddrinfo(res0);
299         if (sd == -1) {
300                 _fetch_syserr();
301                 return (NULL);
302         }
303
304         if ((conn = _fetch_reopen(sd)) == NULL) {
305                 _fetch_syserr();
306                 close(sd);
307         }
308         return (conn);
309 }
310
311
312 /*
313  * Enable SSL on a connection.
314  */
315 int
316 _fetch_ssl(conn_t *conn, int verbose)
317 {
318
319 #ifdef WITH_SSL
320         /* Init the SSL library and context */
321         if (!SSL_library_init()){
322                 fprintf(stderr, "SSL library init failed\n");
323                 return (-1);
324         }
325
326         SSL_load_error_strings();
327
328         conn->ssl_meth = SSLv23_client_method();
329         conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);
330         SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);
331
332         conn->ssl = SSL_new(conn->ssl_ctx);
333         if (conn->ssl == NULL){
334                 fprintf(stderr, "SSL context creation failed\n");
335                 return (-1);
336         }
337         SSL_set_fd(conn->ssl, conn->sd);
338         if (SSL_connect(conn->ssl) == -1){
339                 ERR_print_errors_fp(stderr);
340                 return (-1);
341         }
342
343         if (verbose) {
344                 X509_NAME *name;
345                 char *str;
346
347                 fprintf(stderr, "SSL connection established using %s\n",
348                     SSL_get_cipher(conn->ssl));
349                 conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);
350                 name = X509_get_subject_name(conn->ssl_cert);
351                 str = X509_NAME_oneline(name, 0, 0);
352                 printf("Certificate subject: %s\n", str);
353                 free(str);
354                 name = X509_get_issuer_name(conn->ssl_cert);
355                 str = X509_NAME_oneline(name, 0, 0);
356                 printf("Certificate issuer: %s\n", str);
357                 free(str);
358         }
359
360         return (0);
361 #else
362         (void)conn;
363         (void)verbose;
364         fprintf(stderr, "SSL support disabled\n");
365         return (-1);
366 #endif
367 }
368
369
370 /*
371  * Read a character from a connection w/ timeout
372  */
373 ssize_t
374 _fetch_read(conn_t *conn, char *buf, size_t len)
375 {
376         struct timeval now, timeout, wait;
377         fd_set readfds;
378         ssize_t rlen, total;
379         int r;
380
381         if (fetchTimeout) {
382                 FD_ZERO(&readfds);
383                 gettimeofday(&timeout, NULL);
384                 timeout.tv_sec += fetchTimeout;
385         }
386
387         total = 0;
388         while (len > 0) {
389                 while (fetchTimeout && !FD_ISSET(conn->sd, &readfds)) {
390                         FD_SET(conn->sd, &readfds);
391                         gettimeofday(&now, NULL);
392                         wait.tv_sec = timeout.tv_sec - now.tv_sec;
393                         wait.tv_usec = timeout.tv_usec - now.tv_usec;
394                         if (wait.tv_usec < 0) {
395                                 wait.tv_usec += 1000000;
396                                 wait.tv_sec--;
397                         }
398                         if (wait.tv_sec < 0) {
399                                 errno = ETIMEDOUT;
400                                 _fetch_syserr();
401                                 return (-1);
402                         }
403                         errno = 0;
404                         r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);
405                         if (r == -1) {
406                                 if (errno == EINTR && fetchRestartCalls)
407                                         continue;
408                                 _fetch_syserr();
409                                 return (-1);
410                         }
411                 }
412 #ifdef WITH_SSL
413                 if (conn->ssl != NULL)
414                         rlen = SSL_read(conn->ssl, buf, len);
415                 else
416 #endif
417                         rlen = read(conn->sd, buf, len);
418                 if (rlen == 0)
419                         break;
420                 if (rlen < 0) {
421                         if (errno == EINTR && fetchRestartCalls)
422                                 continue;
423                         return (-1);
424                 }
425                 len -= rlen;
426                 buf += rlen;
427                 total += rlen;
428         }
429         return (total);
430 }
431
432
433 /*
434  * Read a line of text from a connection w/ timeout
435  */
436 #define MIN_BUF_SIZE 1024
437
438 int
439 _fetch_getln(conn_t *conn)
440 {
441         char *tmp;
442         size_t tmpsize;
443         ssize_t len;
444         char c;
445
446         if (conn->buf == NULL) {
447                 if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {
448                         errno = ENOMEM;
449                         return (-1);
450                 }
451                 conn->bufsize = MIN_BUF_SIZE;
452         }
453
454         conn->buf[0] = '\0';
455         conn->buflen = 0;
456
457         do {
458                 len = _fetch_read(conn, &c, 1);
459                 if (len == -1)
460                         return (-1);
461                 if (len == 0)
462                         break;
463                 conn->buf[conn->buflen++] = c;
464                 if (conn->buflen == conn->bufsize) {
465                         tmp = conn->buf;
466                         tmpsize = conn->bufsize * 2 + 1;
467                         if ((tmp = realloc(tmp, tmpsize)) == NULL) {
468                                 errno = ENOMEM;
469                                 return (-1);
470                         }
471                         conn->buf = tmp;
472                         conn->bufsize = tmpsize;
473                 }
474         } while (c != '\n');
475
476         conn->buf[conn->buflen] = '\0';
477         DEBUG(fprintf(stderr, "<<< %s", conn->buf));
478         return (0);
479 }
480
481
482 /*
483  * Write to a connection w/ timeout
484  */
485 ssize_t
486 _fetch_write(conn_t *conn, const char *buf, size_t len)
487 {
488         struct iovec iov;
489
490         iov.iov_base = (char *)buf;
491         iov.iov_len = len;
492         return _fetch_writev(conn, &iov, 1);
493 }
494
495 /*
496  * Write a vector to a connection w/ timeout
497  * Note: can modify the iovec.
498  */
499 ssize_t
500 _fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
501 {
502         struct timeval now, timeout, wait;
503         fd_set writefds;
504         ssize_t wlen, total;
505         int r;
506
507         if (fetchTimeout) {
508                 FD_ZERO(&writefds);
509                 gettimeofday(&timeout, NULL);
510                 timeout.tv_sec += fetchTimeout;
511         }
512
513         total = 0;
514         while (iovcnt > 0) {
515                 while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
516                         FD_SET(conn->sd, &writefds);
517                         gettimeofday(&now, NULL);
518                         wait.tv_sec = timeout.tv_sec - now.tv_sec;
519                         wait.tv_usec = timeout.tv_usec - now.tv_usec;
520                         if (wait.tv_usec < 0) {
521                                 wait.tv_usec += 1000000;
522                                 wait.tv_sec--;
523                         }
524                         if (wait.tv_sec < 0) {
525                                 errno = ETIMEDOUT;
526                                 _fetch_syserr();
527                                 return (-1);
528                         }
529                         errno = 0;
530                         r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
531                         if (r == -1) {
532                                 if (errno == EINTR && fetchRestartCalls)
533                                         continue;
534                                 return (-1);
535                         }
536                 }
537                 errno = 0;
538 #ifdef WITH_SSL
539                 if (conn->ssl != NULL)
540                         wlen = SSL_write(conn->ssl,
541                             iov->iov_base, iov->iov_len);
542                 else
543 #endif
544                         wlen = writev(conn->sd, iov, iovcnt);
545                 if (wlen == 0) {
546                         /* we consider a short write a failure */
547                         errno = EPIPE;
548                         _fetch_syserr();
549                         return (-1);
550                 }
551                 if (wlen < 0) {
552                         if (errno == EINTR && fetchRestartCalls)
553                                 continue;
554                         return (-1);
555                 }
556                 total += wlen;
557                 while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
558                         wlen -= iov->iov_len;
559                         iov++;
560                         iovcnt--;
561                 }
562                 if (iovcnt > 0) {
563                         iov->iov_len -= wlen;
564                         iov->iov_base = (char *)iov->iov_base + wlen;
565                 }
566         }
567         return (total);
568 }
569
570
571 /*
572  * Write a line of text to a connection w/ timeout
573  */
574 int
575 _fetch_putln(conn_t *conn, const char *str, size_t len)
576 {
577         struct iovec iov[2];
578         int ret;
579
580         DEBUG(fprintf(stderr, ">>> %s\n", str));
581         iov[0].iov_base = (char *)str;
582         iov[0].iov_len = len;
583         iov[1].iov_base = (char *)ENDL;
584         iov[1].iov_len = sizeof(ENDL);
585         if (len == 0)
586                 ret = _fetch_writev(conn, &iov[1], 1);
587         else
588                 ret = _fetch_writev(conn, iov, 2);
589         if (ret == -1)
590                 return (-1);
591         return (0);
592 }
593
594
595 /*
596  * Close connection
597  */
598 int
599 _fetch_close(conn_t *conn)
600 {
601         int ret;
602
603         if (--conn->ref > 0)
604                 return (0);
605         ret = close(conn->sd);
606         free(conn);
607         return (ret);
608 }
609
610
611 /*** Directory-related utility functions *************************************/
612
613 int
614 _fetch_add_entry(struct url_ent **p, int *size, int *len,
615     const char *name, struct url_stat *us)
616 {
617         struct url_ent *tmp;
618
619         if (*p == NULL) {
620                 *size = 0;
621                 *len = 0;
622         }
623
624         if (*len >= *size - 1) {
625                 tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
626                 if (tmp == NULL) {
627                         errno = ENOMEM;
628                         _fetch_syserr();
629                         return (-1);
630                 }
631                 *size = (*size * 2 + 1);
632                 *p = tmp;
633         }
634
635         tmp = *p + *len;
636         snprintf(tmp->name, PATH_MAX, "%s", name);
637         bcopy(us, &tmp->stat, sizeof(*us));
638
639         (*len)++;
640         (++tmp)->name[0] = 0;
641
642         return (0);
643 }
644
645
646 /*** Authentication-related utility functions ********************************/
647
648 static const char *
649 _fetch_read_word(FILE *f)
650 {
651         static char word[1024];
652
653         if (fscanf(f, " %1024s ", word) != 1)
654                 return (NULL);
655         return (word);
656 }
657
658 /*
659  * Get authentication data for a URL from .netrc
660  */
661 int
662 _fetch_netrc_auth(struct url *url)
663 {
664         char fn[PATH_MAX];
665         const char *word;
666         char *p;
667         FILE *f;
668
669         if ((p = getenv("NETRC")) != NULL) {
670                 if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
671                         _fetch_info("$NETRC specifies a file name "
672                             "longer than PATH_MAX");
673                         return (-1);
674                 }
675         } else {
676                 if ((p = getenv("HOME")) != NULL) {
677                         struct passwd *pwd;
678
679                         if ((pwd = getpwuid(getuid())) == NULL ||
680                             (p = pwd->pw_dir) == NULL)
681                                 return (-1);
682                 }
683                 if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
684                         return (-1);
685         }
686
687         if ((f = fopen(fn, "r")) == NULL)
688                 return (-1);
689         while ((word = _fetch_read_word(f)) != NULL) {
690                 if (strcmp(word, "default") == 0) {
691                         DEBUG(_fetch_info("Using default .netrc settings"));
692                         break;
693                 }
694                 if (strcmp(word, "machine") == 0 &&
695                     (word = _fetch_read_word(f)) != NULL &&
696                     strcasecmp(word, url->host) == 0) {
697                         DEBUG(_fetch_info("Using .netrc settings for %s", word));
698                         break;
699                 }
700         }
701         if (word == NULL)
702                 goto ferr;
703         while ((word = _fetch_read_word(f)) != NULL) {
704                 if (strcmp(word, "login") == 0) {
705                         if ((word = _fetch_read_word(f)) == NULL)
706                                 goto ferr;
707                         if (snprintf(url->user, sizeof(url->user),
708                                 "%s", word) > (int)sizeof(url->user)) {
709                                 _fetch_info("login name in .netrc is too long");
710                                 url->user[0] = '\0';
711                         }
712                 } else if (strcmp(word, "password") == 0) {
713                         if ((word = _fetch_read_word(f)) == NULL)
714                                 goto ferr;
715                         if (snprintf(url->pwd, sizeof(url->pwd),
716                                 "%s", word) > (int)sizeof(url->pwd)) {
717                                 _fetch_info("password in .netrc is too long");
718                                 url->pwd[0] = '\0';
719                         }
720                 } else if (strcmp(word, "account") == 0) {
721                         if ((word = _fetch_read_word(f)) == NULL)
722                                 goto ferr;
723                         /* XXX not supported! */
724                 } else {
725                         break;
726                 }
727         }
728         fclose(f);
729         return (0);
730  ferr:
731         fclose(f);
732         return (-1);
733 }