📄 common.c
字号:
/*- * Copyright (c) 1998-2004 Dag-Erling Co飀an Sm鴕grav * 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. */#define ALLOW_OS_CODE 1#include <sys/cdefs.h>#include <sys/param.h>#include <sys/socket.h>#include <sys/time.h>#include <sys/uio.h>#include <sys/fcntl.h>#include <netinet/in.h>#include <errno.h>#include <netdb.h>#include <pwd.h>#include <stdarg.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <unistd.h>#include "../include/rmlibhttp.h"#include "common.h"/*** Local data **************************************************************//* * Error messages for resolver errors */static struct fetcherr _netdb_errlist[] = {#ifdef EAI_NODATA { EAI_NODATA, FETCH_RESOLV, "Host not found" },#endif { 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" }};/* End-of-Line */static const RMascii ENDL[3] = "\r\n";/*** Error-reporting functions ***********************************************//* * Map error code to string */static struct fetcherr *_fetch_finderr(struct fetcherr *p, RMint32 e){ while (p->num != -1 && p->num != e) p++; return (p);}/* * Set error code */void_fetch_seterr(struct fetcherr *p, RMint32 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){ switch (errno) { case 0: fetchLastErrCode = FETCH_OK; break; case EPERM: case EACCES: case EROFS: 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 EPIPE: fetchLastErrCode = FETCH_PIPE; break; case ECONNREFUSED: case EHOSTDOWN: fetchLastErrCode = FETCH_DOWN; break;default: fetchLastErrCode = FETCH_UNKNOWN; } snprintf(fetchLastErrString, MAXERRSTRING, "%s", strerror(errno));}/* * Emit status message */void_fetch_info(const RMascii *fmt, ...){ va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fputc('\n', stderr);}/*** Network-related utility functions ***************************************//* * Return the default port for a scheme */RMint32_fetch_default_port(const RMascii *scheme){ return (HTTP_DEFAULT_PORT);}/* * Return the default proxy port for a scheme */RMint32_fetch_default_proxy_port(const RMascii *scheme){ return (HTTP_DEFAULT_PROXY_PORT);}/* * Create a connection for an existing descriptor. */conn_t *_fetch_reopen(RMint32 sd){ conn_t *conn; /* allocate and fill connection structure */ if ((conn = RMCalloc(1, sizeof(*conn))) == NULL) return (NULL); conn->sd = sd; ++conn->ref; return (conn);}/* * Bump a connection's reference count. */conn_t *_fetch_ref(conn_t *conn){ ++conn->ref; return (conn);}/* * Bind a socket to a specific local address */RMint32_fetch_bind(RMint32 sd, RMint32 af, const RMascii *addr){ struct addrinfo hints, *res, *res0; RMint32 err; RMMemset(&hints, 0, sizeof(hints)); hints.ai_family = af; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0) return (-1); for (res = res0; res; res = res->ai_next) if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) return (0); return (-1);}/* * Establish a TCP connection to the specified port on the specified host. */conn_t *_fetch_connect(const RMascii *host, RMint32 port, RMint32 af, RMint32 verbose){ conn_t *conn; RMascii pbuf[10]; const RMascii *bindaddr; struct addrinfo hints, *res, *res0; RMint32 sd, err, r; long fcntl_arg=0; RMDBGLOG((HTTPDEBUG, "---> %s:%ld\n", host, port)); if (verbose) _fetch_info("looking up %s", host); /* look up host name and set up socket address structure */ snprintf(pbuf, sizeof(pbuf), "%ld", port); RMMemset(&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 (NULL); } bindaddr = getenv("FETCH_BIND_ADDRESS"); if (verbose) _fetch_info("connecting to %s:%d", host, port); /* try to connect */ for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) { if ((sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) continue; if (bindaddr != NULL && *bindaddr != '\0' && _fetch_bind(sd, res->ai_family, bindaddr) != 0) { _fetch_info("failed to bind to '%s'", bindaddr); close(sd); continue; } /* make the socket non blocking */ if (fetchTimeout) { fcntl_arg = fcntl(sd, F_GETFL); fcntl(sd, F_SETFL, fcntl_arg|O_NONBLOCK); } if (connect(sd, res->ai_addr, res->ai_addrlen) == 0) break; else if (fetchTimeout && (errno == EINPROGRESS)) { fd_set writefds; struct timeval now, timeout, wait; FD_ZERO(&writefds); gettimeofday(&timeout, NULL); /* give more timeout connect than for read/write */ timeout.tv_sec += 2*fetchTimeout; while (!FD_ISSET(sd, &writefds)) { 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; _fetch_syserr(); break; } errno = 0; FD_SET(sd, &writefds); r = select(sd + 1, NULL, &writefds, NULL, &wait); if (r == -1) { if (errno == EINTR && fetchRestartCalls) continue; _fetch_syserr(); break; } } if (FD_ISSET(sd, &writefds)) { break; } } close(sd); } /* restore the blocking flag */ if (fetchTimeout) fcntl(sd, F_SETFL, fcntl_arg&~O_NONBLOCK); freeaddrinfo(res0); if (sd == -1) { _fetch_syserr(); return (NULL); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -