Make this WARNS?=6 clean by explicitly using __DECONST for the write
[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.3 2004/08/16 14:19:31 joerg 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         /* This is correct, because writev doesn't change the buffer */
491         iov.iov_base = __DECONST(char *, buf);
492         iov.iov_len = len;
493         return _fetch_writev(conn, &iov, 1);
494 }
495
496 /*
497  * Write a vector to a connection w/ timeout
498  * Note: can modify the iovec.
499  */
500 ssize_t
501 _fetch_writev(conn_t *conn, struct iovec *iov, int iovcnt)
502 {
503         struct timeval now, timeout, wait;
504         fd_set writefds;
505         ssize_t wlen, total;
506         int r;
507
508         if (fetchTimeout) {
509                 FD_ZERO(&writefds);
510                 gettimeofday(&timeout, NULL);
511                 timeout.tv_sec += fetchTimeout;
512         }
513
514         total = 0;
515         while (iovcnt > 0) {
516                 while (fetchTimeout && !FD_ISSET(conn->sd, &writefds)) {
517                         FD_SET(conn->sd, &writefds);
518                         gettimeofday(&now, NULL);
519                         wait.tv_sec = timeout.tv_sec - now.tv_sec;
520                         wait.tv_usec = timeout.tv_usec - now.tv_usec;
521                         if (wait.tv_usec < 0) {
522                                 wait.tv_usec += 1000000;
523                                 wait.tv_sec--;
524                         }
525                         if (wait.tv_sec < 0) {
526                                 errno = ETIMEDOUT;
527                                 _fetch_syserr();
528                                 return (-1);
529                         }
530                         errno = 0;
531                         r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);
532                         if (r == -1) {
533                                 if (errno == EINTR && fetchRestartCalls)
534                                         continue;
535                                 return (-1);
536                         }
537                 }
538                 errno = 0;
539 #ifdef WITH_SSL
540                 if (conn->ssl != NULL)
541                         wlen = SSL_write(conn->ssl,
542                             iov->iov_base, iov->iov_len);
543                 else
544 #endif
545                         wlen = writev(conn->sd, iov, iovcnt);
546                 if (wlen == 0) {
547                         /* we consider a short write a failure */
548                         errno = EPIPE;
549                         _fetch_syserr();
550                         return (-1);
551                 }
552                 if (wlen < 0) {
553                         if (errno == EINTR && fetchRestartCalls)
554                                 continue;
555                         return (-1);
556                 }
557                 total += wlen;
558                 while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
559                         wlen -= iov->iov_len;
560                         iov++;
561                         iovcnt--;
562                 }
563                 if (iovcnt > 0) {
564                         iov->iov_len -= wlen;
565                         iov->iov_base = (char *)iov->iov_base + wlen;
566                 }
567         }
568         return (total);
569 }
570
571
572 /*
573  * Write a line of text to a connection w/ timeout
574  */
575 int
576 _fetch_putln(conn_t *conn, const char *str, size_t len)
577 {
578         struct iovec iov[2];
579         int ret;
580
581         DEBUG(fprintf(stderr, ">>> %s\n", str));
582         /* This is correct, because writev doesn't change the buffer */
583         iov[0].iov_base = __DECONST(char *, str);
584         iov[0].iov_len = len;
585         iov[1].iov_base = __DECONST(char *, ENDL);
586         iov[1].iov_len = sizeof(ENDL);
587         if (len == 0)
588                 ret = _fetch_writev(conn, &iov[1], 1);
589         else
590                 ret = _fetch_writev(conn, iov, 2);
591         if (ret == -1)
592                 return (-1);
593         return (0);
594 }
595
596
597 /*
598  * Close connection
599  */
600 int
601 _fetch_close(conn_t *conn)
602 {
603         int ret;
604
605         if (--conn->ref > 0)
606                 return (0);
607         ret = close(conn->sd);
608         free(conn);
609         return (ret);
610 }
611
612
613 /*** Directory-related utility functions *************************************/
614
615 int
616 _fetch_add_entry(struct url_ent **p, int *size, int *len,
617     const char *name, struct url_stat *us)
618 {
619         struct url_ent *tmp;
620
621         if (*p == NULL) {
622                 *size = 0;
623                 *len = 0;
624         }
625
626         if (*len >= *size - 1) {
627                 tmp = realloc(*p, (*size * 2 + 1) * sizeof(**p));
628                 if (tmp == NULL) {
629                         errno = ENOMEM;
630                         _fetch_syserr();
631                         return (-1);
632                 }
633                 *size = (*size * 2 + 1);
634                 *p = tmp;
635         }
636
637         tmp = *p + *len;
638         snprintf(tmp->name, PATH_MAX, "%s", name);
639         bcopy(us, &tmp->stat, sizeof(*us));
640
641         (*len)++;
642         (++tmp)->name[0] = 0;
643
644         return (0);
645 }
646
647
648 /*** Authentication-related utility functions ********************************/
649
650 static const char *
651 _fetch_read_word(FILE *f)
652 {
653         static char word[1024];
654
655         if (fscanf(f, " %1024s ", word) != 1)
656                 return (NULL);
657         return (word);
658 }
659
660 /*
661  * Get authentication data for a URL from .netrc
662  */
663 int
664 _fetch_netrc_auth(struct url *url)
665 {
666         char fn[PATH_MAX];
667         const char *word;
668         char *p;
669         FILE *f;
670
671         if ((p = getenv("NETRC")) != NULL) {
672                 if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {
673                         _fetch_info("$NETRC specifies a file name "
674                             "longer than PATH_MAX");
675                         return (-1);
676                 }
677         } else {
678                 if ((p = getenv("HOME")) != NULL) {
679                         struct passwd *pwd;
680
681                         if ((pwd = getpwuid(getuid())) == NULL ||
682                             (p = pwd->pw_dir) == NULL)
683                                 return (-1);
684                 }
685                 if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))
686                         return (-1);
687         }
688
689         if ((f = fopen(fn, "r")) == NULL)
690                 return (-1);
691         while ((word = _fetch_read_word(f)) != NULL) {
692                 if (strcmp(word, "default") == 0) {
693                         DEBUG(_fetch_info("Using default .netrc settings"));
694                         break;
695                 }
696                 if (strcmp(word, "machine") == 0 &&
697                     (word = _fetch_read_word(f)) != NULL &&
698                     strcasecmp(word, url->host) == 0) {
699                         DEBUG(_fetch_info("Using .netrc settings for %s", word));
700                         break;
701                 }
702         }
703         if (word == NULL)
704                 goto ferr;
705         while ((word = _fetch_read_word(f)) != NULL) {
706                 if (strcmp(word, "login") == 0) {
707                         if ((word = _fetch_read_word(f)) == NULL)
708                                 goto ferr;
709                         if (snprintf(url->user, sizeof(url->user),
710                                 "%s", word) > (int)sizeof(url->user)) {
711                                 _fetch_info("login name in .netrc is too long");
712                                 url->user[0] = '\0';
713                         }
714                 } else if (strcmp(word, "password") == 0) {
715                         if ((word = _fetch_read_word(f)) == NULL)
716                                 goto ferr;
717                         if (snprintf(url->pwd, sizeof(url->pwd),
718                                 "%s", word) > (int)sizeof(url->pwd)) {
719                                 _fetch_info("password in .netrc is too long");
720                                 url->pwd[0] = '\0';
721                         }
722                 } else if (strcmp(word, "account") == 0) {
723                         if ((word = _fetch_read_word(f)) == NULL)
724                                 goto ferr;
725                         /* XXX not supported! */
726                 } else {
727                         break;
728                 }
729         }
730         fclose(f);
731         return (0);
732  ferr:
733         fclose(f);
734         return (-1);
735 }