libixp

git clone git://oldgit.suckless.org/libixp/
Log | Files | Refs | LICENSE

socket.c (4977B)


      1 /* Copyright ©2007-2010 Kris Maglione <maglione.k at Gmail>
      2  * Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
      3  * See LICENSE file for license details.
      4  */
      5 #include <errno.h>
      6 #include <netdb.h>
      7 #include <netinet/in.h>
      8 #include <signal.h>
      9 #include <string.h>
     10 #include <stdlib.h>
     11 #include <stdio.h>
     12 #include <sys/types.h>
     13 #include <sys/stat.h>
     14 #include <sys/socket.h>
     15 #include <sys/un.h>
     16 #include <unistd.h>
     17 #include "ixp_local.h"
     18 
     19 /* Note: These functions modify the strings that they are passed.
     20  *   The lookup function duplicates the original string, so it is
     21  *   not modified.
     22  */
     23 
     24 /* From FreeBSD's sys/su.h */
     25 #define SUN_LEN(su) \
     26 	(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
     27 
     28 typedef struct addrinfo addrinfo;
     29 typedef struct sockaddr sockaddr;
     30 typedef struct sockaddr_un sockaddr_un;
     31 typedef struct sockaddr_in sockaddr_in;
     32 
     33 static char*
     34 get_port(char *addr) {
     35 	char *s;
     36 
     37 	s = strchr(addr, '!');
     38 	if(s == nil) {
     39 		werrstr("no port provided");
     40 		return nil;
     41 	}
     42 
     43 	*s++ = '\0';
     44 	if(*s == '\0') {
     45 		werrstr("invalid port number");
     46 		return nil;
     47 	}
     48 	return s;
     49 }
     50 
     51 static int
     52 sock_unix(char *address, sockaddr_un *sa, socklen_t *salen) {
     53 	int fd;
     54 
     55 	memset(sa, 0, sizeof *sa);
     56 
     57 	sa->sun_family = AF_UNIX;
     58 	strncpy(sa->sun_path, address, sizeof sa->sun_path);
     59 	*salen = SUN_LEN(sa);
     60 
     61 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
     62 	if(fd < 0)
     63 		return -1;
     64 	return fd;
     65 }
     66 
     67 static int
     68 dial_unix(char *address) {
     69 	sockaddr_un sa;
     70 	socklen_t salen;
     71 	int fd;
     72 
     73 	fd = sock_unix(address, &sa, &salen);
     74 	if(fd == -1)
     75 		return fd;
     76 
     77 	if(connect(fd, (sockaddr*) &sa, salen)) {
     78 		close(fd);
     79 		return -1;
     80 	}
     81 	return fd;
     82 }
     83 
     84 static int
     85 announce_unix(char *file) {
     86 	const int yes = 1;
     87 	sockaddr_un sa;
     88 	socklen_t salen;
     89 	int fd;
     90 
     91 	signal(SIGPIPE, SIG_IGN);
     92 
     93 	fd = sock_unix(file, &sa, &salen);
     94 	if(fd == -1)
     95 		return fd;
     96 
     97 	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof yes) < 0)
     98 		goto fail;
     99 
    100 	unlink(file);
    101 	if(bind(fd, (sockaddr*)&sa, salen) < 0)
    102 		goto fail;
    103 
    104 	chmod(file, S_IRWXU);
    105 	if(listen(fd, IXP_MAX_CACHE) < 0)
    106 		goto fail;
    107 
    108 	return fd;
    109 
    110 fail:
    111 	close(fd);
    112 	return -1;
    113 }
    114 
    115 static addrinfo*
    116 alookup(char *host, int announce) {
    117 	addrinfo hints, *ret;
    118 	char *port;
    119 	int err;
    120 
    121 	/* Truncates host at '!' */
    122 	port = get_port(host);
    123 	if(port == nil)
    124 		return nil;
    125 
    126 	memset(&hints, 0, sizeof hints);
    127 	hints.ai_family = AF_INET;
    128 	hints.ai_socktype = SOCK_STREAM;
    129 
    130 	if(announce) {
    131 		hints.ai_flags = AI_PASSIVE;
    132 		if(!strcmp(host, "*"))
    133 			host = nil;
    134 	}
    135 
    136 	err = getaddrinfo(host, port, &hints, &ret);
    137 	if(err) {
    138 		werrstr("getaddrinfo: %s", gai_strerror(err));
    139 		return nil;
    140 	}
    141 	return ret;
    142 }
    143 
    144 static int
    145 ai_socket(addrinfo *ai) {
    146 	return socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    147 }
    148 
    149 static int
    150 dial_tcp(char *host) {
    151 	addrinfo *ai, *aip;
    152 	int fd;
    153 
    154 	aip = alookup(host, 0);
    155 	if(aip == nil)
    156 		return -1;
    157 
    158 	SET(fd);
    159 	for(ai = aip; ai; ai = ai->ai_next) {
    160 		fd = ai_socket(ai);
    161 		if(fd == -1) {
    162 			werrstr("socket: %s", strerror(errno));
    163 			continue;
    164 		}
    165 
    166 		if(connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
    167 			break;
    168 
    169 		werrstr("connect: %s", strerror(errno));
    170 		close(fd);
    171 		fd = -1;
    172 	}
    173 
    174 	freeaddrinfo(aip);
    175 	return fd;
    176 }
    177 
    178 static int
    179 announce_tcp(char *host) {
    180 	addrinfo *ai, *aip;
    181 	int fd;
    182 
    183 	aip = alookup(host, 1);
    184 	if(aip == nil)
    185 		return -1;
    186 
    187 	/* Probably don't need to loop */
    188 	SET(fd);
    189 	for(ai = aip; ai; ai = ai->ai_next) {
    190 		fd = ai_socket(ai);
    191 		if(fd == -1)
    192 			continue;
    193 
    194 		if(bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
    195 			goto fail;
    196 
    197 		if(listen(fd, IXP_MAX_CACHE) < 0)
    198 			goto fail;
    199 		break;
    200 	fail:
    201 		close(fd);
    202 		fd = -1;
    203 	}
    204 
    205 	freeaddrinfo(aip);
    206 	return fd;
    207 }
    208 
    209 typedef struct addrtab addrtab;
    210 static
    211 struct addrtab {
    212 	char *type;
    213 	int (*fn)(char*);
    214 } dtab[] = {
    215 	{"tcp", dial_tcp},
    216 	{"unix", dial_unix},
    217 	{0, 0}
    218 }, atab[] = {
    219 	{"tcp", announce_tcp},
    220 	{"unix", announce_unix},
    221 	{0, 0}
    222 };
    223 
    224 static int
    225 lookup(const char *address, addrtab *tab) {
    226 	char *addr, *type;
    227 	int ret;
    228 
    229 	ret = -1;
    230 	type = estrdup(address);
    231 
    232 	addr = strchr(type, '!');
    233 	if(addr == nil)
    234 		werrstr("no address type defined");
    235 	else {
    236 		*addr++ = '\0';
    237 		for(; tab->type; tab++)
    238 			if(strcmp(tab->type, type) == 0) break;
    239 		if(tab->type == nil)
    240 			werrstr("unsupported address type");
    241 		else
    242 			ret = tab->fn(addr);
    243 	}
    244 
    245 	free(type);
    246 	return ret;
    247 }
    248 
    249 /**
    250  * Function: ixp_dial
    251  * Function: ixp_announce
    252  *
    253  * Params:
    254  *	address: An address on which to connect or listen,
    255  *		 specified in the Plan 9 resources
    256  *		 specification format
    257  *		 (<protocol>!address[!<port>])
    258  *
    259  * These functions hide some of the ugliness of Berkely
    260  * Sockets. ixp_dial connects to the resource at P<address>,
    261  * while ixp_announce begins listening on P<address>.
    262  *
    263  * Returns:
    264  *	These functions return file descriptors on success, and -1
    265  *	on failure. ixp_errbuf(3) may be inspected on failure.
    266  * See also:
    267  *	socket(2)
    268  */
    269 
    270 int
    271 ixp_dial(const char *address) {
    272 	return lookup(address, dtab);
    273 }
    274 
    275 int
    276 ixp_announce(const char *address) {
    277 	return lookup(address, atab);
    278 }
    279