X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=net.c;h=be637ec7a5e24e7d1856ed21370139d7a05c5a2c;hp=b44d1d3e5d3a2012b1dfe135ddeec74d10b0fe58;hb=67900f49834e42887225eaf586a1d235ed11910a;hpb=8cf2884a87db0eca0aa2f382c9850335332682b3 diff --git a/net.c b/net.c index b44d1d3e..be637ec7 100644 --- a/net.c +++ b/net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2010 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -279,7 +279,7 @@ struct flowopts *flowopt_new(void) * Append new socket option to flowopt queue. * * \param fo The flowopt queue to append to. - * \param lev Level at which \opt resides. + * \param lev Level at which \a opt resides. * \param opt New option to add. * \param name Stringified name of \a opt. * \param val The value to set \a opt to. @@ -351,7 +351,6 @@ static void flowopt_cleanup(struct flowopts *fo) /** * Resolve IPv4/IPv6 address and create a ready-to-use active or passive socket. * - * \param l3type The layer-3 type (\p AF_INET, \p AF_INET6, \p AF_UNSPEC). * \param l4type The layer-4 type (\p IPPROTO_xxx). * \param passive Whether this is a passive (1) or active (0) socket. * \param host Remote or local hostname or IPv/6 address string. @@ -437,6 +436,7 @@ int makesock(unsigned l4type, bool passive, if (passive && setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { rc = errno; + close(sockfd); PARA_ERROR_LOG("can not set SO_REUSEADDR: %s\n", strerror(rc)); return -ERRNO_TO_PARA_ERROR(rc); @@ -521,6 +521,14 @@ static socklen_t salen(const struct sockaddr *sa) : sizeof(struct sockaddr_in); } +/** True if @ss holds a v6-mapped-v4 address (RFC 4291, 2.5.5.2) */ +static bool SS_IS_ADDR_V4MAPPED(const struct sockaddr_storage *ss) +{ + const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss; + + return ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ia6->sin6_addr); +} + /** * Process IPv4/v6 address, turn v6-mapped-v4 address into normal IPv4 address. * \param ss Container of IPv4/6 address. @@ -531,11 +539,10 @@ static socklen_t salen(const struct sockaddr *sa) static const struct sockaddr * normalize_ip_address(const struct sockaddr_storage *ss) { - const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss; - assert(ss->ss_family == AF_INET || ss->ss_family == AF_INET6); - if (ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ia6->sin6_addr)) { + if (SS_IS_ADDR_V4MAPPED(ss)) { + const struct sockaddr_in6 *ia6 = (const struct sockaddr_in6 *)ss; static struct sockaddr_in ia; ia.sin_family = AF_INET; @@ -546,30 +553,73 @@ normalize_ip_address(const struct sockaddr_storage *ss) return (const struct sockaddr *)ss; } +/** + * Generic/fallback MTU values + * + * These are taken from RFC 1122, RFC 2460, and RFC 5405. + * - RFC 1122, 3.3.3 defines EMTU_S ("Effective MTU for sending") and recommends + * to use an EMTU_S size of of 576 bytes if the IPv4 path MTU is unknown; + * - RFC 2460, 5. requires a minimum IPv6 MTU of 1280 bytes; + * - RFC 5405, 3.2 recommends that if path MTU discovery is not done, + * UDP senders should use the respective minimum values of EMTU_S. + */ +static inline int generic_mtu(const int af_type) +{ + return af_type == AF_INET6 ? 1280 : 576; +} + +/** Crude approximation of IP header overhead - neglecting options. */ +static inline int estimated_header_overhead(const int af_type) +{ + return af_type == AF_INET6 ? 40 : 20; +} + +/** + * Maximum transport-layer message size (MMS_S) as per RFC 1122, 3.3.3 + * Socket must be connected. + */ +int generic_max_transport_msg_size(int sockfd) +{ + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + int af_type = AF_INET; + + if (getpeername(sockfd, (struct sockaddr *)&ss, &sslen) < 0) { + PARA_ERROR_LOG("can not determine remote address type: %s\n", + strerror(errno)); + } else if (!SS_IS_ADDR_V4MAPPED(&ss)) { + af_type = ss.ss_family; + } + return generic_mtu(af_type) - estimated_header_overhead(af_type); +} + /** * Print numeric host and port number (beware - uses static char). * * \param sa The IPv4/IPv6 socket address to use. * + * \return Host string in numeric host:port format, \sa parse_url(). * \sa getnameinfo(3), services(5), nsswitch.conf(5) */ static char *host_and_port(const struct sockaddr_storage *ss) { const struct sockaddr *sa = normalize_ip_address(ss); char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; - static char output[sizeof(hbuf) + sizeof(sbuf) + 2]; + static char output[sizeof(hbuf) + sizeof(sbuf) + 4]; int ret; ret = getnameinfo(sa, salen(sa), hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), - NI_NUMERICHOST); - if (ret == 0) { - snprintf(output, sizeof(output), "%s#%s", hbuf, sbuf); - } else { + NI_NUMERICHOST | NI_NUMERICSERV); + if (ret) { snprintf(output, sizeof(output), "(unknown)"); PARA_WARNING_LOG("hostname lookup error (%s).\n", gai_strerror(ret)); + } else if (sa->sa_family == AF_INET6) { + snprintf(output, sizeof(output), "[%s]:%s", hbuf, sbuf); + } else { + snprintf(output, sizeof(output), "%s:%s", hbuf, sbuf); } return output; } @@ -756,50 +806,68 @@ int recv_buffer(int fd, char *buf, size_t size) * Wrapper around the accept system call. * * \param fd The listening socket. + * \param rfds An optional fd_set pointer. * \param addr Structure which is filled in with the address of the peer socket. * \param size Should contain the size of the structure pointed to by \a addr. + * \param new_fd Result pointer. * - * Accept incoming connections on \a addr. Retry if interrupted. + * Accept incoming connections on \a addr, retry if interrupted. If \a rfds is + * not \p NULL, return 0 if \a fd is not set in \a rfds without calling accept(). * - * \return The new file descriptor on success, negative on errors. + * \return Negative on errors, zero if no connections are present to be accepted, + * one otherwise. * * \sa accept(2). */ -int para_accept(int fd, void *addr, socklen_t size) +int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd) { - int new_fd; + int ret; + if (rfds && !FD_ISSET(fd, rfds)) + return 0; do - new_fd = accept(fd, (struct sockaddr *) addr, &size); - while (new_fd < 0 && errno == EINTR); - return new_fd < 0? -ERRNO_TO_PARA_ERROR(errno) : new_fd; + ret = accept(fd, (struct sockaddr *) addr, &size); + while (ret < 0 && errno == EINTR); + + if (ret >= 0) { + *new_fd = ret; + return 1; + } + if (errno == EAGAIN || errno == EWOULDBLOCK) + return 0; + return -ERRNO_TO_PARA_ERROR(errno); } /** - * Probe the list of DCCP CCIDs supported locally by the host. - * \param ccids Array to be filled in. - * \param nccids Length of \a ccids. - * \return Pointer to \a ccids, NULL on failure. + * Probe the list of DCCP CCIDs configured on this host. + * \param ccid_array Pointer to return statically allocated array in. + * \return Number of elements returned in \a ccid_array or error. * * NB: This feature is only available on Linux > 2.6.30; on older kernels * ENOPROTOOPT ("Protocol not available") will be returned. */ -const uint8_t *dccp_available_ccids(uint8_t *ccids, uint8_t *nccids) +int dccp_available_ccids(uint8_t **ccid_array) { - int fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL); + static uint8_t ccids[DCCP_MAX_HOST_CCIDS]; + socklen_t nccids = sizeof(ccids); + int ret, fd; - if (fd < 0) - return NULL; + ret = fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL); + if (ret < 0) + return ret; if (getsockopt(fd, SOL_DCCP, DCCP_SOCKOPT_AVAILABLE_CCIDS, - ccids, (socklen_t *)nccids) < 0) { + ccids, &nccids) < 0) { + ret = errno; + close(fd); PARA_ERROR_LOG("No DCCP_SOCKOPT_AVAILABLE_CCIDS: %s\n", - strerror(errno)); - *nccids = 0; + strerror(ret)); + return -ERRNO_TO_PARA_ERROR(ret); } - close(fd); - return *nccids ? ccids : NULL; + close(fd); + *ccid_array = ccids; + return nccids; } /** @@ -1012,41 +1080,3 @@ int recv_cred_buffer(int fd, char *buf, size_t size) return result; } #endif /* HAVE_UCRED */ - -/** - * Receive a buffer and check for a pattern. - * - * \param fd The file descriptor to receive from. - * \param pattern The expected pattern. - * \param bufsize The size of the internal buffer. - * - * \return Positive if \a pattern was received, negative otherwise. - * - * This function tries to receive at most \a bufsize bytes from file descriptor - * \a fd. If at least \p strlen(\a pattern) bytes were received, the beginning - * of the received buffer is compared with \a pattern, ignoring case. - * - * \sa recv_buffer(), \sa strncasecmp(3). - */ -int recv_pattern(int fd, const char *pattern, size_t bufsize) -{ - size_t len = strlen(pattern); - char *buf = para_malloc(bufsize + 1); - int ret = -E_RECV_PATTERN, n = recv_buffer(fd, buf, bufsize + 1); - - if (n < len) - goto out; - if (strncasecmp(buf, pattern, len)) - goto out; - ret = 1; -out: - if (ret < 0) { - PARA_NOTICE_LOG("did not receive pattern '%s'\n", pattern); - if (n > 0) - PARA_NOTICE_LOG("recvd %d bytes: %s\n", n, buf); - else if (n < 0) - PARA_NOTICE_LOG("%s\n", para_strerror(-n)); - } - free(buf); - return ret; -}