⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 comm.c

📁 -
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * $Id: comm.c,v 1.299.2.1 1999/04/20 17:55:03 wessels Exp $ * * DEBUG: section 5     Socket Functions * AUTHOR: Harvest Derived * * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/ * ---------------------------------------------------------- * *  Squid is the result of efforts by numerous individuals from the *  Internet community.  Development is led by Duane Wessels of the *  National Laboratory for Applied Network Research and funded by the *  National Science Foundation.  Squid is Copyrighted (C) 1998 by *  Duane Wessels and the University of California San Diego.  Please *  see the COPYRIGHT file for full details.  Squid incorporates *  software developed and/or copyrighted by other sources.  Please see *  the CREDITS file for full details. * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. *   *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. *   *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */#include "squid.h"#ifdef HAVE_NETINET_TCP_H#include <netinet/tcp.h>#endif#if USE_ASYNC_IO#define MAX_POLL_TIME 10#else#define MAX_POLL_TIME 1000#endiftypedef struct {    char *host;    u_short port;    struct sockaddr_in S;    CNCB *callback;    void *data;    struct in_addr in_addr;    int locks;    int fd;    int tries;    int addrcount;    int connstart;} ConnectStateData;/* STATIC */static int commBind(int s, struct in_addr, u_short port);static void commSetReuseAddr(int);static void commSetNoLinger(int);static void CommWriteStateCallbackAndFree(int fd, int code);#ifdef TCP_NODELAYstatic void commSetTcpNoDelay(int);#endifstatic void commSetTcpRcvbuf(int, int);static PF commConnectFree;static PF commConnectHandle;static PF commHandleWrite;static IPH commConnectDnsHandle;static void commConnectCallback(ConnectStateData * cs, int status);static int commResetFD(ConnectStateData * cs);static int commRetryConnect(ConnectStateData * cs);static voidCommWriteStateCallbackAndFree(int fd, int code){    CommWriteStateData *CommWriteState = fd_table[fd].rwstate;    CWCB *callback = NULL;    void *data;    fd_table[fd].rwstate = NULL;    if (CommWriteState == NULL)	return;    if (CommWriteState->free_func) {	CommWriteState->free_func(CommWriteState->buf);	CommWriteState->buf = NULL;    }    callback = CommWriteState->handler;    data = CommWriteState->handler_data;    CommWriteState->handler = NULL;    if (callback && cbdataValid(data))	callback(fd, CommWriteState->buf, CommWriteState->offset, code, data);    cbdataUnlock(data);    safe_free(CommWriteState);}/* Return the local port associated with fd. */u_shortcomm_local_port(int fd){    struct sockaddr_in addr;    socklen_t addr_len = 0;    fde *F = &fd_table[fd];    /* If the fd is closed already, just return */    if (!F->flags.open) {	debug(5, 0) ("comm_local_port: FD %d has been closed.\n", fd);	return 0;    }    if (F->local_port)	return F->local_port;    addr_len = sizeof(addr);    if (getsockname(fd, (struct sockaddr *) &addr, &addr_len)) {	debug(50, 1) ("comm_local_port: Failed to retrieve TCP/UDP port number for socket: FD %d: %s\n", fd, xstrerror());	return 0;    }    F->local_port = ntohs(addr.sin_port);    debug(5, 6) ("comm_local_port: FD %d: port %d\n", fd, (int) F->local_port);    return F->local_port;}static intcommBind(int s, struct in_addr in_addr, u_short port){    struct sockaddr_in S;    memset(&S, '\0', sizeof(S));    S.sin_family = AF_INET;    S.sin_port = htons(port);    S.sin_addr = in_addr;    Counter.syscalls.sock.binds++;    if (bind(s, (struct sockaddr *) &S, sizeof(S)) == 0)	return COMM_OK;    debug(50, 0) ("commBind: Cannot bind socket FD %d to %s:%d: %s\n",	s,	S.sin_addr.s_addr == INADDR_ANY ? "*" : inet_ntoa(S.sin_addr),	(int) port,	xstrerror());    return COMM_ERROR;}/* Create a socket. Default is blocking, stream (TCP) socket.  IO_TYPE * is OR of flags specified in comm.h. */intcomm_open(int sock_type,    int proto,    struct in_addr addr,    u_short port,    int flags,    const char *note){    int new_socket;    fde *F = NULL;    /* Create socket for accepting new connections. */    Counter.syscalls.sock.sockets++;    if ((new_socket = socket(AF_INET, sock_type, proto)) < 0) {	/* Increase the number of reserved fd's if calls to socket()	 * are failing because the open file table is full.  This	 * limits the number of simultaneous clients */	switch (errno) {	case ENFILE:	case EMFILE:	    debug(50, 1) ("comm_open: socket failure: %s\n", xstrerror());	    break;	default:	    debug(50, 0) ("comm_open: socket failure: %s\n", xstrerror());	}	fdAdjustReserved();	return -1;    }    /* update fdstat */    debug(5, 5) ("comm_open: FD %d is a new socket\n", new_socket);    fd_open(new_socket, FD_SOCKET, note);    F = &fd_table[new_socket];    if (!(flags & COMM_NOCLOEXEC))	commSetCloseOnExec(new_socket);    if ((flags & COMM_REUSEADDR))	commSetReuseAddr(new_socket);    if (port > (u_short) 0) {	commSetNoLinger(new_socket);	if (opt_reuseaddr)	    commSetReuseAddr(new_socket);    }    if (addr.s_addr != no_addr.s_addr) {	if (commBind(new_socket, addr, port) != COMM_OK) {	    comm_close(new_socket);	    return -1;	}    }    F->local_port = port;    if (flags & COMM_NONBLOCKING)	if (commSetNonBlocking(new_socket) == COMM_ERROR)	    return -1;#ifdef TCP_NODELAY    if (sock_type == SOCK_STREAM)	commSetTcpNoDelay(new_socket);#endif    if (Config.tcpRcvBufsz > 0 && sock_type == SOCK_STREAM)	commSetTcpRcvbuf(new_socket, Config.tcpRcvBufsz);    return new_socket;}/* * NOTE: set the listen queue to Squid_MaxFD/4 and rely on the kernel to       * impose an upper limit.  Solaris' listen(3n) page says it has    * no limit on this parameter, but sys/socket.h sets SOMAXCONN  * to 5.  HP-UX currently has a limit of 20.  SunOS is 5 and * OSF 3.0 is 8. */intcomm_listen(int sock){    int x;    if ((x = listen(sock, Squid_MaxFD >> 2)) < 0) {	debug(50, 0) ("comm_listen: listen(%d, %d): %s\n",	    Squid_MaxFD >> 2,	    sock, xstrerror());	return x;    }    return sock;}voidcommConnectStart(int fd, const char *host, u_short port, CNCB * callback, void *data){    ConnectStateData *cs = xcalloc(1, sizeof(ConnectStateData));    debug(5, 3) ("commConnectStart: FD %d, %s:%d\n", fd, host, (int) port);    cbdataAdd(cs, cbdataXfree, 0);    cs->fd = fd;    cs->host = xstrdup(host);    cs->port = port;    cs->callback = callback;    cs->data = data;    cbdataLock(cs->data);    comm_add_close_handler(fd, commConnectFree, cs);    cs->locks++;    ipcache_nbgethostbyname(host, commConnectDnsHandle, cs);}static voidcommConnectDnsHandle(const ipcache_addrs * ia, void *data){    ConnectStateData *cs = data;    assert(cs->locks == 1);    cs->locks--;    if (ia == NULL) {	debug(5, 3) ("commConnectDnsHandle: Unknown host: %s\n", cs->host);	if (!dns_error_message) {	    dns_error_message = "Unknown DNS error";	    debug(5, 1) ("commConnectDnsHandle: Bad dns_error_message\n");	}	assert(dns_error_message != NULL);	commConnectCallback(cs, COMM_ERR_DNS);	return;    }    assert(ia->cur < ia->count);    cs->in_addr = ia->in_addrs[ia->cur];    ipcacheCycleAddr(cs->host, NULL);    cs->addrcount = ia->count;    cs->connstart = squid_curtime;    commConnectHandle(cs->fd, cs);}static voidcommConnectCallback(ConnectStateData * cs, int status){    CNCB *callback = cs->callback;    void *data = cs->data;    int fd = cs->fd;    comm_remove_close_handler(fd, commConnectFree, cs);    cs->callback = NULL;    cs->data = NULL;    commSetTimeout(fd, -1, NULL, NULL);    commConnectFree(fd, cs);    if (cbdataValid(data))	callback(fd, status, data);    cbdataUnlock(data);}static voidcommConnectFree(int fd, void *data){    ConnectStateData *cs = data;    debug(5, 3) ("commConnectFree: FD %d\n", fd);    if (cs->locks)	ipcacheUnregister(cs->host, cs);    if (cs->data)	cbdataUnlock(cs->data);    safe_free(cs->host);    cbdataFree(cs);}/* Reset FD so that we can connect() again */static intcommResetFD(ConnectStateData * cs){    int fd2;    if (!cbdataValid(cs->data))	return 0;    Counter.syscalls.sock.sockets++;    fd2 = socket(AF_INET, SOCK_STREAM, 0);    Counter.syscalls.sock.sockets++;    if (fd2 < 0) {	debug(5, 0) ("commResetFD: socket: %s\n", xstrerror());	fdAdjustReserved();	return 0;    }    if (dup2(fd2, cs->fd) < 0) {	debug(5, 0) ("commResetFD: dup2: %s\n", xstrerror());	fdAdjustReserved();	return 0;    }    close(fd2);    fd_table[cs->fd].flags.called_connect = 0;    /*     * yuck, this has assumptions about comm_open() arguments for     * the original socket     */    commSetCloseOnExec(cs->fd);    if (Config.Addrs.tcp_outgoing.s_addr != no_addr.s_addr) {	if (commBind(cs->fd, Config.Addrs.tcp_outgoing, 0) != COMM_OK) {	    return 0;	}    }    commSetNonBlocking(cs->fd);#ifdef TCP_NODELAY    commSetTcpNoDelay(cs->fd);#endif    if (Config.tcpRcvBufsz > 0)	commSetTcpRcvbuf(cs->fd, Config.tcpRcvBufsz);    return 1;}static intcommRetryConnect(ConnectStateData * cs){    assert(cs->addrcount > 0);    if (cs->addrcount == 1) {	if (cs->tries >= Config.retry.maxtries)	    return 0;	if (squid_curtime - cs->connstart > Config.Timeout.connect)	    return 0;    } else {	if (cs->tries > cs->addrcount)	    return 0;    }    return commResetFD(cs);}/* Connect SOCK to specified DEST_PORT at DEST_HOST. */static voidcommConnectHandle(int fd, void *data){    ConnectStateData *cs = data;    if (cs->S.sin_addr.s_addr == 0) {	cs->S.sin_family = AF_INET;	cs->S.sin_addr = cs->in_addr;	cs->S.sin_port = htons(cs->port);	if (Config.onoff.log_fqdn)	    fqdncache_gethostbyaddr(cs->S.sin_addr, FQDN_LOOKUP_IF_MISS);    }    switch (comm_connect_addr(fd, &cs->S)) {    case COMM_INPROGRESS:	debug(5, 5) ("commConnectHandle: FD %d: COMM_INPROGRESS\n", fd);	commSetSelect(fd, COMM_SELECT_WRITE, commConnectHandle, cs, 0);	break;    case COMM_OK:	ipcacheMarkGoodAddr(cs->host, cs->S.sin_addr);	commConnectCallback(cs, COMM_OK);	break;    default:	cs->tries++;	ipcacheMarkBadAddr(cs->host, cs->S.sin_addr);	if (Config.onoff.test_reachability)	    netdbDeleteAddrNetwork(cs->S.sin_addr);	if (commRetryConnect(cs)) {	    cs->locks++;	    ipcache_nbgethostbyname(cs->host, commConnectDnsHandle, cs);	} else {	    commConnectCallback(cs, COMM_ERR_CONNECT);	}	break;    }}intcommSetTimeout(int fd, int timeout, PF * handler, void *data){    fde *F;    debug(5, 3) ("commSetTimeout: FD %d timeout %d\n", fd, timeout);    assert(fd >= 0);    assert(fd < Squid_MaxFD);    F = &fd_table[fd];    assert(F->flags.open);    if (timeout < 0) {	F->timeout_handler = NULL;	F->timeout_data = NULL;	return F->timeout = 0;    }    assert(handler || F->timeout_handler);    if (handler || data) {	F->timeout_handler = handler;	F->timeout_data = data;    }    return F->timeout = squid_curtime + (time_t) timeout;}intcomm_connect_addr(int sock, const struct sockaddr_in *address){    int status = COMM_OK;    fde *F = &fd_table[sock];    int x;    int err = 0;    socklen_t errlen;    assert(ntohs(address->sin_port) != 0);    /* Establish connection. */    errno = 0;    if (!F->flags.called_connect) {	F->flags.called_connect = 1;	Counter.syscalls.sock.connects++;	x = connect(sock, (struct sockaddr *) address, sizeof(*address));	if (x < 0)	    debug(5, 9) ("connect FD %d: %s\n", sock, xstrerror());    } else {#if defined(_SQUID_NEWSOS6_)	/* Makoto MATSUSHITA <matusita@ics.es.osaka-u.ac.jp> */	connect(sock, (struct sockaddr *) address, sizeof(*address));	if (errno == EINVAL) {	    errlen = sizeof(err);	    x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);	    if (x >= 0)		errno = x;	}#else	errlen = sizeof(err);	x = getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen);	if (x == 0)	    errno = err;#if defined(_SQUID_SOLARIS_)	/*	 * Solaris 2.4's socket emulation doesn't allow you	 * to determine the error from a failed non-blocking	 * connect and just returns EPIPE.  Create a fake	 * error message for connect.   -- fenner@parc.xerox.com	 */	if (x < 0 && errno == EPIPE)	    errno = ENOTCONN;#endif#endif    }    if (errno == 0 || errno == EISCONN)	status = COMM_OK;    else if (ignoreErrno(errno))	status = COMM_INPROGRESS;    else	return COMM_ERROR;    xstrncpy(F->ipaddr, inet_ntoa(address->sin_addr), 16);    F->remote_port = ntohs(address->sin_port);    if (status == COMM_OK) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -