X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=net.c;h=312a68546f08bc5493699a9ae32d6a4b96164e13;hp=09963f2e8b4b55d4931b8dd5236aecc2a9012015;hb=35993f0cd49206666262cfdd2be89af41ec28a1d;hpb=d261c537236503faaa31dfb44278b08a492020ec diff --git a/net.c b/net.c index 09963f2e..312a6854 100644 --- a/net.c +++ b/net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -19,11 +19,13 @@ #define AI_ADDRCONFIG 0 #endif +#include #include "para.h" #include "error.h" #include "net.h" #include "string.h" +#include "fd.h" /** Information about one encrypted connection. */ @@ -155,9 +157,13 @@ int makesock(unsigned l3type, unsigned l4type, int passive, /* Set up address hint structure */ memset(&hints, 0, sizeof(hints)); hints.ai_family = l3type; - /* getaddrinfo does not really work well with SOCK_DCCP */ - if (socktype == SOCK_DGRAM || socktype == SOCK_STREAM) - hints.ai_socktype = socktype; + hints.ai_socktype = socktype; + /* + * getaddrinfo does not support SOCK_DCCP, so for the sake of lookup + * (and only then) pretend to be UDP. + */ + if (l4type == IPPROTO_DCCP) + hints.ai_socktype = SOCK_DGRAM; /* only use addresses available on the host */ hints.ai_flags = AI_ADDRCONFIG; @@ -172,7 +178,7 @@ int makesock(unsigned l3type, unsigned l4type, int passive, if ((rc = getaddrinfo(host, port, &hints, passive ? &local : &remote))) { PARA_ERROR_LOG("can not resolve %s address %s#%s: %s.\n", layer4_name(l4type), - host? : (passive? "[loopback]" : "[localhost]"), + host? host : (passive? "[loopback]" : "[localhost]"), port, gai_strerror(rc)); return -E_ADDRESS_LOOKUP; } @@ -226,7 +232,7 @@ get_next_src: if (src == NULL && dst == NULL) { PARA_ERROR_LOG("can not create %s socket %s#%s.\n", - layer4_name(l4type), host? : (passive? + layer4_name(l4type), host? host : (passive? "[loopback]" : "[localhost]"), port); return -ERRNO_TO_PARA_ERROR(errno); } @@ -345,31 +351,6 @@ struct in_addr extract_v4_addr(const struct sockaddr_storage *ss) return ia; } -/* - * Send out a buffer, resend on short writes. - * - * \param fd The file descriptor. - * \param buf The buffer to be sent. - * \param len The length of \a buf. - * - * \return Standard. In any case, the number of bytes actually sent is stored - * in \a len. - */ -static int sendall(int fd, const char *buf, size_t *len) -{ - size_t total = *len; - - assert(total); - *len = 0; - while (*len < total) { - int ret = write(fd, buf + *len, total - *len); - if (ret == -1) - return -ERRNO_TO_PARA_ERROR(errno); - *len += ret; - } - return 1; -} - /** * Encrypt and send a binary buffer. * @@ -389,7 +370,7 @@ int send_bin_buffer(int fd, const char *buf, size_t len) crypt_function *cf = NULL; if (!len) - PARA_CRIT_LOG("%s", "len == 0\n"); + PARA_CRIT_LOG("len == 0\n"); if (fd + 1 <= cda_size) cf = crypt_data_array[fd].send; if (cf) { @@ -397,10 +378,10 @@ int send_bin_buffer(int fd, const char *buf, size_t len) /* RC4 may write more than len to the output buffer */ unsigned char *outbuf = para_malloc(ROUND_UP(len, 8)); (*cf)(len, (unsigned char *)buf, outbuf, private); - ret = sendall(fd, (char *)outbuf, &len); + ret = write_all(fd, (char *)outbuf, &len); free(outbuf); } else - ret = sendall(fd, buf, &len); + ret = write_all(fd, buf, &len); return ret; } @@ -747,10 +728,9 @@ int recv_cred_buffer(int fd, char *buf, size_t size) * * \return Positive if \a pattern was received, negative otherwise. * - * This function creates a buffer of size \a bufsize and 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. + * 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). */ @@ -758,7 +738,7 @@ 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); + int ret = -E_RECV_PATTERN, n = recv_buffer(fd, buf, bufsize + 1); if (n < len) goto out; @@ -775,3 +755,142 @@ out: free(buf); return ret; } + +static int resolve(const char *hostname, unsigned short port, + struct sockaddr_in *addr) +{ + struct hostent *host; + + assert(hostname); + host = gethostbyname(hostname); + if (!host) + return -ERRNO_TO_PARA_ERROR(h_errno); + if (addr) { + memcpy(&addr->sin_addr, host->h_addr_list[0], host->h_length); + addr->sin_port = port; + } + return 1; +} + +/* + * Create an UDP socket. + * + * If the given address is a multicast adress, the socket will be set + * to use the multicast TTL ttl and sets the datagrams to loop back. + * + * \return The fd of the socket on success, negative on errors. + */ +static int create_udp_socket(struct sockaddr_in *addr, + unsigned short port, unsigned char ttl) +{ + int ret, fd, yes = 1; + + assert(addr); + ret = socket(PF_INET, SOCK_DGRAM, 0); + if (ret < 0) + return -ERRNO_TO_PARA_ERROR(errno); + fd = ret; + /* reuse addresses */ + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + addr->sin_family = AF_INET; + addr->sin_port = htons(port); + /* set the TTL and turn on multicast loop */ + if (IN_MULTICAST(htonl(addr->sin_addr.s_addr))) { + unsigned char loop = 1; + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, + sizeof(loop)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + } + return fd; +err: + close(fd); + return ret; +} + +/** + * Create and connect a sending UDP socket. + * + * \param hostname Where to send to (name or IPv4 address). + * \param port The udp port to use. + * \param ttl Time to live (only relevant for multicast). + * + * \return The fd of the socket on success, negative on error. + */ +int create_udp_send_socket(char *hostname, unsigned short port, + unsigned char ttl) +{ + struct sockaddr_in addr; + int fd, ret = resolve(hostname, port, &addr); + + if (ret < 0) + return ret; + ret = create_udp_socket(&addr, port, ttl); + if (ret < 0) + return ret; + fd = ret; + ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret >= 0) + return fd; + ret = -ERRNO_TO_PARA_ERROR(errno); + close(fd); + return ret; +} + +/** + * Create and bind a receiving UDP socket. + * + * Bind the created UDP socket to \a hostname, and add multicast membership if + * hostname is a multicast hostname. + * + * \param hostname Name or IPv4 address to receive from. + * \param port The udp port. + * + * \return The fd of the socket on success, negative on errors. + */ +int create_udp_recv_socket(char *hostname, unsigned short port) +{ + struct sockaddr_in addr; + int fd, ret = resolve(hostname, port, &addr); + + if (ret < 0) + memset(&addr.sin_addr, 0, sizeof(addr.sin_addr)); + ret = create_udp_socket(&addr, port, 1); + if (ret < 0) + return ret; + fd = ret; + ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + /* Add multicast membership */ + if (IN_MULTICAST(htonl(addr.sin_addr.s_addr))) { + struct ip_mreq mreq; + + mreq.imr_multiaddr.s_addr = addr.sin_addr.s_addr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)); + if (ret < 0) { + ret = -ERRNO_TO_PARA_ERROR(errno); + goto err; + } + } + return fd; +err: + close(fd); + return ret; +}