📄 socket.c
字号:
*/static intdoio_send(isc_socket_t *sock, isc_socketevent_t *dev) { int cc; struct iovec iov[MAXSCATTERGATHER_SEND]; size_t write_count; struct msghdr msghdr; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; int attempts = 0; int send_errno; char strbuf[ISC_STRERRORSIZE]; build_msghdr_send(sock, dev, &msghdr, iov, &write_count); resend: cc = sendmsg(sock->fd, &msghdr, 0); send_errno = errno; /* * Check for error or block condition. */ if (cc < 0) { if (send_errno == EINTR && ++attempts < NRETRIES) goto resend; if (SOFT_ERROR(send_errno)) return (DOIO_SOFT);#define SOFT_OR_HARD(_system, _isc) \ if (send_errno == _system) { \ if (sock->connected) { \ dev->result = _isc; \ return (DOIO_HARD); \ } \ return (DOIO_SOFT); \ }#define ALWAYS_HARD(_system, _isc) \ if (send_errno == _system) { \ dev->result = _isc; \ return (DOIO_HARD); \ } SOFT_OR_HARD(ECONNREFUSED, ISC_R_CONNREFUSED); ALWAYS_HARD(EACCES, ISC_R_NOPERM); ALWAYS_HARD(EAFNOSUPPORT, ISC_R_ADDRNOTAVAIL); ALWAYS_HARD(EADDRNOTAVAIL, ISC_R_ADDRNOTAVAIL); ALWAYS_HARD(EHOSTUNREACH, ISC_R_HOSTUNREACH);#ifdef EHOSTDOWN ALWAYS_HARD(EHOSTDOWN, ISC_R_HOSTUNREACH);#endif ALWAYS_HARD(ENETUNREACH, ISC_R_NETUNREACH); ALWAYS_HARD(ENOBUFS, ISC_R_NORESOURCES); ALWAYS_HARD(EPERM, ISC_R_HOSTUNREACH); ALWAYS_HARD(EPIPE, ISC_R_NOTCONNECTED); ALWAYS_HARD(ECONNRESET, ISC_R_CONNECTIONRESET);#undef SOFT_OR_HARD#undef ALWAYS_HARD /* * The other error types depend on whether or not the * socket is UDP or TCP. If it is UDP, some errors * that we expect to be fatal under TCP are merely * annoying, and are really soft errors. * * However, these soft errors are still returned as * a status. */ isc_sockaddr_format(&dev->address, addrbuf, sizeof(addrbuf)); isc__strerror(send_errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "internal_send: %s: %s", addrbuf, strbuf); dev->result = isc__errno2result(send_errno); return (DOIO_HARD); } if (cc == 0) UNEXPECTED_ERROR(__FILE__, __LINE__, "internal_send: send() %s 0", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_RETURNED, "returned")); /* * If we write less than we expected, update counters, poke. */ dev->n += cc; if ((size_t)cc != write_count) return (DOIO_SOFT); /* * Exactly what we wanted to write. We're done with this * entry. Post its completion event. */ dev->result = ISC_R_SUCCESS; return (DOIO_SUCCESS);}/* * Kill. * * Caller must ensure that the socket is not locked and no external * references exist. */static voiddestroy(isc_socket_t **sockp) { isc_socket_t *sock = *sockp; isc_socketmgr_t *manager = sock->manager; socket_log(sock, NULL, CREATION, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_DESTROYING, "destroying"); INSIST(ISC_LIST_EMPTY(sock->accept_list)); INSIST(ISC_LIST_EMPTY(sock->recv_list)); INSIST(ISC_LIST_EMPTY(sock->send_list)); INSIST(sock->connect_ev == NULL); REQUIRE(sock->fd >= 0 && sock->fd < (int)FD_SETSIZE); LOCK(&manager->lock); /* * No one has this socket open, so the watcher doesn't have to be * poked, and the socket doesn't have to be locked. */ manager->fds[sock->fd] = NULL; manager->fdstate[sock->fd] = CLOSE_PENDING; select_poke(manager, sock->fd, SELECT_POKE_CLOSE); ISC_LIST_UNLINK(manager->socklist, sock, link);#ifdef ISC_PLATFORM_USETHREADS if (ISC_LIST_EMPTY(manager->socklist)) SIGNAL(&manager->shutdown_ok);#endif /* ISC_PLATFORM_USETHREADS */ /* * XXX should reset manager->maxfd here */ UNLOCK(&manager->lock); free_socket(sockp);}static isc_result_tallocate_socket(isc_socketmgr_t *manager, isc_sockettype_t type, isc_socket_t **socketp){ isc_socket_t *sock; isc_result_t ret; ISC_SOCKADDR_LEN_T cmsgbuflen; sock = isc_mem_get(manager->mctx, sizeof(*sock)); if (sock == NULL) return (ISC_R_NOMEMORY); ret = ISC_R_UNEXPECTED; sock->magic = 0; sock->references = 0; sock->manager = manager; sock->type = type; sock->fd = -1; ISC_LINK_INIT(sock, link); sock->recvcmsgbuf = NULL; sock->sendcmsgbuf = NULL; /* * set up cmsg buffers */ cmsgbuflen = 0;#if defined(USE_CMSG) && defined(ISC_PLATFORM_HAVEIPV6) cmsgbuflen = cmsg_space(sizeof(struct in6_pktinfo));#endif#if defined(USE_CMSG) && defined(SO_TIMESTAMP) cmsgbuflen += cmsg_space(sizeof(struct timeval));#endif sock->recvcmsgbuflen = cmsgbuflen; if (sock->recvcmsgbuflen != 0) { sock->recvcmsgbuf = isc_mem_get(manager->mctx, cmsgbuflen); if (sock->recvcmsgbuf == NULL) goto error; } cmsgbuflen = 0;#if defined(USE_CMSG) && defined(ISC_PLATFORM_HAVEIPV6) cmsgbuflen = cmsg_space(sizeof(struct in6_pktinfo));#endif sock->sendcmsgbuflen = cmsgbuflen; if (sock->sendcmsgbuflen != 0) { sock->sendcmsgbuf = isc_mem_get(manager->mctx, cmsgbuflen); if (sock->sendcmsgbuf == NULL) goto error; } /* * set up list of readers and writers to be initially empty */ ISC_LIST_INIT(sock->recv_list); ISC_LIST_INIT(sock->send_list); ISC_LIST_INIT(sock->accept_list); sock->connect_ev = NULL; sock->pending_recv = 0; sock->pending_send = 0; sock->pending_accept = 0; sock->listener = 0; sock->connected = 0; sock->connecting = 0; sock->bound = 0; /* * initialize the lock */ if (isc_mutex_init(&sock->lock) != ISC_R_SUCCESS) { sock->magic = 0; UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_mutex_init() %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed")); ret = ISC_R_UNEXPECTED; goto error; } /* * Initialize readable and writable events */ ISC_EVENT_INIT(&sock->readable_ev, sizeof(intev_t), ISC_EVENTATTR_NOPURGE, NULL, ISC_SOCKEVENT_INTR, NULL, sock, sock, NULL, NULL); ISC_EVENT_INIT(&sock->writable_ev, sizeof(intev_t), ISC_EVENTATTR_NOPURGE, NULL, ISC_SOCKEVENT_INTW, NULL, sock, sock, NULL, NULL); sock->magic = SOCKET_MAGIC; *socketp = sock; return (ISC_R_SUCCESS); error: if (sock->recvcmsgbuf != NULL) isc_mem_put(manager->mctx, sock->recvcmsgbuf, sock->recvcmsgbuflen); if (sock->sendcmsgbuf != NULL) isc_mem_put(manager->mctx, sock->sendcmsgbuf, sock->sendcmsgbuflen); isc_mem_put(manager->mctx, sock, sizeof(*sock)); return (ret);}/* * This event requires that the various lists be empty, that the reference * count be 1, and that the magic number is valid. The other socket bits, * like the lock, must be initialized as well. The fd associated must be * marked as closed, by setting it to -1 on close, or this routine will * also close the socket. */static voidfree_socket(isc_socket_t **socketp) { isc_socket_t *sock = *socketp; INSIST(sock->references == 0); INSIST(VALID_SOCKET(sock)); INSIST(!sock->connecting); INSIST(!sock->pending_recv); INSIST(!sock->pending_send); INSIST(!sock->pending_accept); INSIST(ISC_LIST_EMPTY(sock->recv_list)); INSIST(ISC_LIST_EMPTY(sock->send_list)); INSIST(ISC_LIST_EMPTY(sock->accept_list)); INSIST(!ISC_LINK_LINKED(sock, link)); if (sock->recvcmsgbuf != NULL) isc_mem_put(sock->manager->mctx, sock->recvcmsgbuf, sock->recvcmsgbuflen); if (sock->sendcmsgbuf != NULL) isc_mem_put(sock->manager->mctx, sock->sendcmsgbuf, sock->sendcmsgbuflen); sock->magic = 0; DESTROYLOCK(&sock->lock); isc_mem_put(sock->manager->mctx, sock, sizeof(*sock)); *socketp = NULL;}/* * Create a new 'type' socket managed by 'manager'. Events * will be posted to 'task' and when dispatched 'action' will be * called with 'arg' as the arg value. The new socket is returned * in 'socketp'. */isc_result_tisc_socket_create(isc_socketmgr_t *manager, int pf, isc_sockettype_t type, isc_socket_t **socketp){ isc_socket_t *sock = NULL; isc_result_t ret;#if defined(USE_CMSG) || defined(SO_BSDCOMPAT) int on = 1;#endif char strbuf[ISC_STRERRORSIZE]; REQUIRE(VALID_MANAGER(manager)); REQUIRE(socketp != NULL && *socketp == NULL); ret = allocate_socket(manager, type, &sock); if (ret != ISC_R_SUCCESS) return (ret); sock->pf = pf; switch (type) { case isc_sockettype_udp: sock->fd = socket(pf, SOCK_DGRAM, IPPROTO_UDP); break; case isc_sockettype_tcp: sock->fd = socket(pf, SOCK_STREAM, IPPROTO_TCP); break; }#ifdef F_DUPFD /* * Leave a space for stdio to work in. */ if (sock->fd >= 0 && sock->fd < 20) { int new, tmp; new = fcntl(sock->fd, F_DUPFD, 20); tmp = errno; (void)close(sock->fd); errno = tmp; sock->fd = new; }#endif if (sock->fd >= (int)FD_SETSIZE) { (void)close(sock->fd); isc_log_iwrite(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_TOOMANYFDS, "%s: too many open file descriptors", "socket"); free_socket(&sock); return (ISC_R_NORESOURCES); } if (sock->fd < 0) { free_socket(&sock); switch (errno) { case EMFILE: case ENFILE: case ENOBUFS: return (ISC_R_NORESOURCES); case EPROTONOSUPPORT: case EPFNOSUPPORT: case EAFNOSUPPORT: /* * Linux 2.2 (and maybe others) return EINVAL instead of * EAFNOSUPPORT. */ case EINVAL: return (ISC_R_FAMILYNOSUPPORT); default: isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() %s: %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed"), strbuf); return (ISC_R_UNEXPECTED); } } if (make_nonblock(sock->fd) != ISC_R_SUCCESS) { (void)close(sock->fd); free_socket(&sock); return (ISC_R_UNEXPECTED); }#ifdef SO_BSDCOMPAT if (setsockopt(sock->fd, SOL_SOCKET, SO_BSDCOMPAT, (void *)&on, sizeof(on)) < 0) { isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "setsockopt(%d, SO_BSDCOMPAT) %s: %s", sock->fd, isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed"), strbuf); /* Press on... */ }#endif#if defined(USE_CMSG) if (type == isc_sockettype_udp) {#if defined(SO_TIMESTAMP) if (setsockopt(sock->fd, SOL_SOCKET, SO_TIMESTAMP, (void *)&on, sizeof(on)) < 0 && errno != ENOPROTOOPT) { isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "setsockopt(%d, SO_TIMESTAMP) %s: %s", sock->fd, isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed"), strbuf); /* Press on... */ }#endif /* SO_TIMESTAMP */#if defined(ISC_PLATFORM_HAVEIPV6) if (pf == AF_INET6 && sock->recvcmsgbuflen == 0) { /* * Warn explicitly because this anomaly can be hidden * in usual operation (and unexpectedly appear later). */ UNEXPECTED_ERROR(__FILE__, __LINE__, "No buffer available to receive " "IPv6 destination"); }#ifdef IPV6_RECVPKTINFO /* 2292bis */ if ((pf == AF_INET6) && (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (void *)&on, sizeof(on)) < 0)) { isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "setsockopt(%d, IPV6_RECVPKTINFO) " "%s: %s", sock->fd, isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed"), strbuf); }#else /* 2292 */ if ((pf == AF_INET6) && (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_PKTINFO, (void *)&on, sizeof(on)) < 0)) { isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "setsockopt(%d, IPV6_PKTINFO) %s: %s", sock->fd, isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed"), strbuf); }#endif /* IPV6_RECVPKTINFO */#ifdef IPV6_USE_MIN_MTU /*2292bis, not too common yet*/ /* use minimum MTU */ if (pf == AF_INET6) { (void)setsockopt(sock->fd, IPPROTO_IPV6, IPV6_USE_MIN_MTU, (void *)&on, sizeof(on)); }#endif#endif /* ISC_PLATFORM_HAVEIPV6 */ }#endif /* USE_CMSG */ sock->references = 1; *socketp = sock; LOCK(&manager->lock); /* * Note we don't have to lock the socket like we normally would because * there are no external references to it yet. */ manager->fds[sock->fd] = sock; manager->fdstate[sock->fd] = MANAGED; ISC_LIST_APPEND(manager->socklist, sock, link); if (manager->maxfd < sock->fd) manager->maxfd = sock->fd; UNLOCK(&manager->lock); socket_log(sock, NULL, CREATION, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_CREATED, "created"); return (ISC_R_SUCCESS);}/* * Attach to a socket. Caller must explicitly detach when it is done. */voidisc_socket_attach(isc_socket_t *sock, isc_socket_t **socketp) { REQUIRE(VALID_SOCKET(sock)); REQUIRE(socketp != NULL && *socketp == NULL); LOCK(&sock->lock); sock->references++; UNLOCK(&sock->lock); *socketp = sock;}/* * Dereference a socket. If this is the last reference to it, clean things * up by destroying the socket. */voidisc_socket_detach(isc_socket_t **socketp) { isc_socket_t *sock; isc_boolean_t kill_socket = ISC_FALSE; REQUIRE(socketp != NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -