📄 comm.c
字号:
/* * $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 + -