From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KuoQQ-0007Az-Lf for qemu-devel@nongnu.org; Tue, 28 Oct 2008 09:15:58 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KuoQP-0007Ae-Qq for qemu-devel@nongnu.org; Tue, 28 Oct 2008 09:15:58 -0400 Received: from [199.232.76.173] (port=40328 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KuoQP-0007AV-Ol for qemu-devel@nongnu.org; Tue, 28 Oct 2008 09:15:57 -0400 Received: from mx1.redhat.com ([66.187.233.31]:58055) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1KuoQP-0004et-EW for qemu-devel@nongnu.org; Tue, 28 Oct 2008 09:15:57 -0400 Received: from int-mx1.corp.redhat.com (int-mx1.corp.redhat.com [172.16.52.254]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id m9SDFuZ2026537 for ; Tue, 28 Oct 2008 09:15:56 -0400 Date: Tue, 28 Oct 2008 13:15:54 +0000 From: "Daniel P. Berrange" Subject: Re: [Qemu-devel] [PATCH 2/4] sockets: helper functions for qemu. Message-ID: <20081028131554.GV18016@redhat.com> References: <1225198518-16529-1-git-send-email-kraxel@redhat.com> <1225198518-16529-3-git-send-email-kraxel@redhat.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1225198518-16529-3-git-send-email-kraxel@redhat.com> Reply-To: "Daniel P. Berrange" , qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Gerd Hoffmann On Tue, Oct 28, 2008 at 01:55:16PM +0100, Gerd Hoffmann wrote: > This patch creates a new source and header file qemu-sockets.[ch] with > a bunch of helper functions to create listening and connected sockets. > > New features of this code are (a) support for searching for a free port > in a given range and (b) support for IPv6. This is great - adding IPv6 was on my todo list for ages ! > + > +int inet_listen(const char *str, char *ostr, int olen, > + int socktype, int port_offset) > +{ > + static const int on=1, off=0; > + struct addrinfo ai,*res,*e; > + char addr[64]; > + char port[33]; > + char uaddr[INET6_ADDRSTRLEN+1]; > + char uport[33]; > + const char *opts, *h; > + int slisten,rc,pos,to,try_next; > + > + memset(&ai,0, sizeof(ai)); > + ai.ai_flags = AI_PASSIVE; You should also set AI_ADDRCONFIG here. This ensure that it only returns IPv6 addresses if a network interface actally has IPv6 enabled. So if someone's disabled IPv6 on a machine, and DNS still has IPv6 addrs, AI_ADDRCONFIG will stop QEMU pointlessly attempting to create IPv6 sockets that won't do anything > + ai.ai_family = default_family; > + ai.ai_socktype = socktype; > + > + /* parse string */ > + if (str[0] == ':') { > + /* no host given */ > + strcpy(addr,""); > + if (1 != sscanf(str,":%32[^,]%n",port,&pos)) { > + fprintf(stderr, "%s: portonly parse error (%s)\n", > + __FUNCTION__, str); > + return -1; > + } > + } else if (str[0] == '[') { > + /* IPv6 addr */ > + if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) { > + fprintf(stderr, "%s: ipv6 parse error (%s)\n", > + __FUNCTION__, str); > + return -1; > + } > + ai.ai_family = PF_INET6; > + } else if (isdigit(str[0])) { > + /* IPv4 addr */ > + if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) { > + fprintf(stderr, "%s: ipv4 parse error (%s)\n", > + __FUNCTION__, str); > + return -1; > + } > + ai.ai_family = PF_INET; > + } else { > + /* hostname */ > + if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) { > + fprintf(stderr, "%s: hostname parse error (%s)\n", > + __FUNCTION__, str); > + return -1; > + } > + } > + opts = str + pos; > + h = strstr(opts, ",to="); > + to = h ? atoi(h+4) : 0; > + > + /* lookup */ > + if (port_offset) > + snprintf(port, sizeof(port), "%d", atoi(port) + port_offset); > + if (0 != (rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res))) { > + fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__, > + addr, port, gai_strerror(rc)); > + return -1; > + } > + if (sockets_debug) > + inet_print_addrinfo(__FUNCTION__, res); > + > + /* create socket + bind */ > + for (e = res; e != NULL; e = e->ai_next) { > + getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, > + uaddr,INET6_ADDRSTRLEN,uport,32, > + NI_NUMERICHOST | NI_NUMERICSERV); > + if (-1 == (slisten = socket(e->ai_family, e->ai_socktype, > + e->ai_protocol))) { > + fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__, > + inet_strfamily(e->ai_family), strerror(errno)); > + continue; > + } > + > + setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); > + if (e->ai_family == PF_INET6) { > + if (default_family == PF_INET6) > + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&on,sizeof(on)); > + else > + setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,&off,sizeof(off)); > + } > + > + for (;;) { > + if (0 == bind(slisten, e->ai_addr, e->ai_addrlen)) { > + if (sockets_debug) > + fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__, > + inet_strfamily(e->ai_family), uaddr, inet_getport(e)); > + goto listen; > + } > + try_next = to && (inet_getport(e) <= to + port_offset); > + if (!try_next || sockets_debug) > + fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__, > + inet_strfamily(e->ai_family), uaddr, inet_getport(e), > + strerror(errno)); > + if (try_next) { > + inet_setport(e, inet_getport(e) + 1); > + continue; > + } > + break; > + } > + close(slisten); > + } > + fprintf(stderr, "%s: FAILED\n", __FUNCTION__); > + return -1; > + > +listen: > + if (0 != listen(slisten,1)) { > + perror("listen"); > + close(slisten); > + return -1; > + } > + if (ostr) { > + if (e->ai_family == PF_INET6) { > + snprintf(ostr, olen, "[%s]:%d%s", uaddr, > + inet_getport(e) - port_offset, opts); > + } else { > + snprintf(ostr, olen, "%s:%d%s", uaddr, > + inet_getport(e) - port_offset, opts); > + } > + } > + return slisten; > +} One small problem here - for a server you need to expect more than one socket will be required. This is because some operating systems require you to bind to IPv4 and IPv6 sockets separately. On Linux by default a IPv6 socket can accept IPv4 client connections, but this behaviour can be turned off by sysctl, and other non-Linux OS don't allow this at all. So we really need an array of server sockets, and attempt to bind to all addresses returned by getaddrinfo(). There's more info on this here http://people.redhat.com/drepper/userapi-ipv6.html > +int inet_connect(const char *str, int socktype) > +{ > + static const int on=1; > + struct addrinfo ai,*res,*e; > + char addr[64]; > + char port[33]; > + char uaddr[INET6_ADDRSTRLEN+1]; > + char uport[33]; > + int sock,rc; > + > + memset(&ai,0, sizeof(ai)); > + ai.ai_flags = AI_CANONNAME; This also needs AI_ADDRCONFIG set > + ai.ai_family = default_family; > + ai.ai_socktype = socktype; > + > + /* parse string */ > + if (str[0] == '[') { > + /* IPv6 addr */ > + if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) { > + fprintf(stderr, "%s: ipv6 parse error (%s)\n", > + __FUNCTION__, str); > + return -1; > + } > + ai.ai_family = PF_INET6; > + } else if (isdigit(str[0])) { > + /* IPv4 addr */ > + if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) { > + fprintf(stderr, "%s: ipv4 parse error (%s)\n", > + __FUNCTION__, str); > + return -1; > + } > + ai.ai_family = PF_INET; > + } else { > + /* hostname */ > + if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) { > + fprintf(stderr, "%s: hostname parse error (%s)\n", > + __FUNCTION__, str); > + return -1; > + } > + } > + > + /* lookup */ > + if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) { > + fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc), > + addr, port); > + return -1; > + } > + if (sockets_debug) > + inet_print_addrinfo(__FUNCTION__, res); > + > + for (e = res; e != NULL; e = e->ai_next) { > + if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, > + uaddr,INET6_ADDRSTRLEN,uport,32, > + NI_NUMERICHOST | NI_NUMERICSERV)) { > + fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__); > + continue; > + } > + if (-1 == (sock = socket(e->ai_family, e->ai_socktype, > + e->ai_protocol))) { > + fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__, > + inet_strfamily(e->ai_family), strerror(errno)); > + continue; > + } > + setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); > + > + /* connect to peer */ > + if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) { > + if (sockets_debug || NULL == e->ai_next) > + fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__, > + inet_strfamily(e->ai_family), > + e->ai_canonname, uaddr, uport, strerror(errno)); > + close(sock); > + continue; > + } > + if (sockets_debug) > + fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__, > + inet_strfamily(e->ai_family), > + e->ai_canonname, uaddr, uport); > + return sock; > + } > + return -1; > +} > + REgards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|