aboutsummaryrefslogblamecommitdiff
path: root/lib/libfetch/common.c
blob: 59f92cbc14ed69f229ac343ef162a848d0d4120e (plain) (tree)


























                                                                            
            

   
                      
                       
                     



                       
                   

                   





                   
 





                                                                               



                                                                             
                                                                  
  






                                                                               
                        
                                         
 


                                       







                                        


                                                                







                                    

              
    

                    
                                    





                   
                                      


                          
                                         

                
                                        


                        
                                      

                
                                        

                
                                      






                       
                                         


                      
                                       

                   
                                         


                      
                                      

              

                                         
                                                                  





                      
    


                           
    
                      
                              
               
                        








                                                                               
                                                         
 


                                       



                                                             


                                           
    







                                                               


                  


                                                       
                        








                                                            
     
                 
                        




                  

 
















































































                                                               







































                                                                               
/*-
 * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD$
 */

#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>

#include <errno.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "fetch.h"
#include "common.h"


/*** Local data **************************************************************/

/*
 * Error messages for resolver errors
 */
static struct fetcherr _netdb_errlist[] = {
    { EAI_NODATA,	FETCH_RESOLV,	"Host not found" },
    { EAI_AGAIN,	FETCH_TEMP,	"Transient resolver failure" },
    { EAI_FAIL,		FETCH_RESOLV,	"Non-recoverable resolver failure" },
    { EAI_NONAME,	FETCH_RESOLV,	"No address record" },
    { -1,		FETCH_UNKNOWN,	"Unknown resolver error" }
};


/*** Error-reporting functions ***********************************************/

/*
 * Map error code to string
 */
static struct fetcherr *
_fetch_finderr(struct fetcherr *p, int e)
{
    while (p->num != -1 && p->num != e)
	p++;
    return p;
}

/*
 * Set error code
 */
void
_fetch_seterr(struct fetcherr *p, int e)
{
    p = _fetch_finderr(p, e);
    fetchLastErrCode = p->cat;
    snprintf(fetchLastErrString, MAXERRSTRING, "%s", p->string);
}

/*
 * Set error code according to errno
 */
void
_fetch_syserr(void)
{
    int e;
    e = errno;
    
    switch (errno) {
    case 0:
	fetchLastErrCode = FETCH_OK;
	break;
    case EPERM:
    case EACCES:
    case EROFS:
    case EAUTH:
    case ENEEDAUTH:
	fetchLastErrCode = FETCH_AUTH;
	break;
    case ENOENT:
    case EISDIR: /* XXX */
	fetchLastErrCode = FETCH_UNAVAIL;
	break;
    case ENOMEM:
	fetchLastErrCode = FETCH_MEMORY;
	break;
    case EBUSY:
    case EAGAIN:	
	fetchLastErrCode = FETCH_TEMP;
	break;
    case EEXIST:
	fetchLastErrCode = FETCH_EXISTS;
	break;
    case ENOSPC:
	fetchLastErrCode = FETCH_FULL;
	break;
    case EADDRINUSE:
    case EADDRNOTAVAIL:
    case ENETDOWN:
    case ENETUNREACH:
    case ENETRESET:
    case EHOSTUNREACH:
	fetchLastErrCode = FETCH_NETWORK;
	break;
    case ECONNABORTED:
    case ECONNRESET:
	fetchLastErrCode = FETCH_ABORT;
	break;
    case ETIMEDOUT:
	fetchLastErrCode = FETCH_TIMEOUT;
	break;
    case ECONNREFUSED:
    case EHOSTDOWN:
	fetchLastErrCode = FETCH_DOWN;
	break;
    default:
	fetchLastErrCode = FETCH_UNKNOWN;
    }
    snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(e));
}


/*
 * Emit status message
 */
void
_fetch_info(char *fmt, ...)
{
    va_list ap;
    
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    fputc('\n', stderr);
}


/*** Network-related utility functions ***************************************/

/*
 * Establish a TCP connection to the specified port on the specified host.
 */
int
_fetch_connect(char *host, int port, int af, int verbose)
{
    char pbuf[10];
    struct addrinfo hints, *res, *res0;
    int sd, err;

#ifndef NDEBUG
    fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port);
#endif

    if (verbose)
	_fetch_info("looking up %s", host);
    
    /* look up host name and set up socket address structure */
    snprintf(pbuf, sizeof(pbuf), "%d", port);
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = af;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;
    if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
	_netdb_seterr(err);
	return -1;
    }

    if (verbose)
	_fetch_info("connecting to %s:%d", host, port);
    
    /* try to connect */
    sd = -1;
    for (res = res0; res; res = res->ai_next) {
	if ((sd = socket(res->ai_family, res->ai_socktype,
			 res->ai_protocol)) < 0)
	    continue;
	if (connect(sd, res->ai_addr, res->ai_addrlen) >= 0)
	    break;
	close(sd);
	sd = -1;
    }
    if (sd < 0) {
	_fetch_syserr();
	return -1;
    }

    return sd;
}


/*
 * Read a line of text from a socket w/ timeout
 */
#define MIN_BUF_SIZE 1024

int
_fetch_getln(int fd, char **buf, size_t *size, size_t *len)
{
    struct timeval now, timeout, wait;
    fd_set readfds;
    int r;
    char c;
    
    if (*buf == NULL) {
	if ((*buf = malloc(MIN_BUF_SIZE)) == NULL) {
	    errno = ENOMEM;
	    return -1;
	}
	*size = MIN_BUF_SIZE;
    }

    **buf = '\0';
    *len = 0;

    if (fetchTimeout) {
	gettimeofday(&timeout, NULL);
	timeout.tv_sec += fetchTimeout;
	FD_ZERO(&readfds);
    }
    
    do {
	if (fetchTimeout) {
	    FD_SET(fd, &readfds);
	    gettimeofday(&now, NULL);
	    wait.tv_sec = timeout.tv_sec - now.tv_sec;
	    wait.tv_usec = timeout.tv_usec - now.tv_usec;
	    if (wait.tv_usec < 0) {
		wait.tv_usec += 1000000;
		wait.tv_sec--;
	    }
	    if (wait.tv_sec < 0) {
		errno = ETIMEDOUT;
		return -1;
	    }
	    r = select(fd+1, &readfds, NULL, NULL, &wait);
	    if (r == -1) {
		if (errno == EINTR)
		    continue;
		/* EBADF or EINVAL: shouldn't happen */
		return -1;
	    }
	    if (!FD_ISSET(fd, &readfds))
		continue;
	}
	r = read(fd, &c, 1);
	if (r == 0)
	    break;
	if (r == -1) {
	    if (errno == EINTR)
		continue;
	    /* any other error is bad news */
	    return -1;
	}
	(*buf)[*len] = c;
	*len += 1;
	if (*len == *size) {
	    char *tmp;
	    
	    if ((tmp = realloc(*buf, *size * 2 + 1)) == NULL) {
		errno = ENOMEM;
		return -1;
	    }
	    *buf = tmp;
	    *size = *size * 2 + 1;
	}
    } while (c != '\n');
    
    return 0;
}


/*** Directory-related utility functions *************************************/

int
_fetch_add_entry(struct url_ent **p, int *size, int *len,
		 char *name, struct url_stat *stat)
{
    struct url_ent *tmp;

    if (*p == NULL) {
#define INITIAL_SIZE 8
	if ((*p = malloc(INITIAL_SIZE * sizeof **p)) == NULL) {
	    errno = ENOMEM;
	    _fetch_syserr();
	    return -1;
	}
	*size = INITIAL_SIZE;
	*len = 0;
#undef INITIAL_SIZE
    }
    
    if (*len >= *size - 1) {
	tmp = realloc(*p, *size * 2 * sizeof **p);
	if (tmp == NULL) {
	    errno = ENOMEM;
	    _fetch_syserr();
	    return -1;
	}
	*size *= 2;
	*p = tmp;
    }

    tmp = *p + *len;
    snprintf(tmp->name, MAXPATHLEN, "%s", name);
    bcopy(stat, &tmp->stat, sizeof *stat);

    (*len)++;
    (++tmp)->name[0] = 0;

    return 0;
}