X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=net.c;h=312a68546f08bc5493699a9ae32d6a4b96164e13;hp=b2c9c43a81ad356ec021373c12d5c764fa5b76f4;hb=de9a70360fcd3b5285d280ad1f229f679d453603;hpb=97727c2e95cce064e03f495e97119cae447397d7 diff --git a/net.c b/net.c index b2c9c43a..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. */ @@ -157,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; @@ -724,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). */ @@ -735,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; @@ -752,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; +}