9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

announce.c (2724B)


      1 #include <u.h>
      2 #define NOPLAN9DEFINES
      3 #include <libc.h>
      4 
      5 #include <sys/socket.h>
      6 #include <netinet/in.h>
      7 #include <netinet/tcp.h>
      8 #include <sys/un.h>
      9 #include <errno.h>
     10 
     11 #undef sun
     12 #define sun sockun
     13 
     14 int
     15 _p9netfd(char *dir)
     16 {
     17 	int fd;
     18 
     19 	if(strncmp(dir, "/dev/fd/", 8) != 0)
     20 		return -1;
     21 	fd = strtol(dir+8, &dir, 0);
     22 	if(*dir != 0)
     23 		return -1;
     24 	return fd;
     25 }
     26 
     27 static void
     28 putfd(char *dir, int fd)
     29 {
     30 	snprint(dir, NETPATHLEN, "/dev/fd/%d", fd);
     31 }
     32 
     33 #undef unix
     34 #define unix sockunix
     35 
     36 int
     37 p9announce(char *addr, char *dir)
     38 {
     39 	int proto;
     40 	char *buf, *unix;
     41 	char *net;
     42 	u32int host;
     43 	int port, s;
     44 	int n;
     45 	socklen_t sn;
     46 	struct sockaddr_in sa;
     47 	struct sockaddr_un sun;
     48 
     49 	buf = strdup(addr);
     50 	if(buf == nil)
     51 		return -1;
     52 
     53 	if(p9dialparse(buf, &net, &unix, &host, &port) < 0){
     54 		free(buf);
     55 		return -1;
     56 	}
     57 	if(strcmp(net, "tcp") == 0)
     58 		proto = SOCK_STREAM;
     59 	else if(strcmp(net, "udp") == 0)
     60 		proto = SOCK_DGRAM;
     61 	else if(strcmp(net, "unix") == 0)
     62 		goto Unix;
     63 	else{
     64 		werrstr("can only handle tcp, udp, and unix: not %s", net);
     65 		free(buf);
     66 		return -1;
     67 	}
     68 	free(buf);
     69 
     70 	memset(&sa, 0, sizeof sa);
     71 	memmove(&sa.sin_addr, &host, 4);
     72 	sa.sin_family = AF_INET;
     73 	sa.sin_port = htons(port);
     74 	if((s = socket(AF_INET, proto, 0)) < 0)
     75 		return -1;
     76 	sn = sizeof n;
     77 	if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
     78 	&& n == SOCK_STREAM){
     79 		n = 1;
     80 		setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
     81 	}
     82 	if(bind(s, (struct sockaddr*)&sa, sizeof sa) < 0){
     83 		close(s);
     84 		return -1;
     85 	}
     86 	if(proto == SOCK_STREAM){
     87 		listen(s, 8);
     88 		putfd(dir, s);
     89 	}
     90 	return s;
     91 
     92 Unix:
     93 	memset(&sun, 0, sizeof sun);
     94 	sun.sun_family = AF_UNIX;
     95 	strcpy(sun.sun_path, unix);
     96 	if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
     97 		return -1;
     98 	sn = sizeof sun;
     99 	if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){
    100 		if(errno == EADDRINUSE
    101 		&& connect(s, (struct sockaddr*)&sun, sizeof sun) < 0
    102 		&& errno == ECONNREFUSED){
    103 			/* dead socket, so remove it */
    104 			remove(unix);
    105 			close(s);
    106 			if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
    107 				return -1;
    108 			if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 0)
    109 				goto Success;
    110 		}
    111 		close(s);
    112 		return -1;
    113 	}
    114 Success:
    115 	listen(s, 8);
    116 	putfd(dir, s);
    117 	return s;
    118 }
    119 
    120 int
    121 p9listen(char *dir, char *newdir)
    122 {
    123 	int fd, one;
    124 
    125 	if((fd = _p9netfd(dir)) < 0){
    126 		werrstr("bad 'directory' in listen: %s", dir);
    127 		return -1;
    128 	}
    129 
    130 	if((fd = accept(fd, nil, nil)) < 0)
    131 		return -1;
    132 
    133 	one = 1;
    134 	setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
    135 
    136 	putfd(newdir, fd);
    137 	return fd;
    138 }
    139 
    140 int
    141 p9accept(int cfd, char *dir)
    142 {
    143 	int fd;
    144 
    145 	if((fd = _p9netfd(dir)) < 0){
    146 		werrstr("bad 'directory' in accept");
    147 		return -1;
    148 	}
    149 	/* need to dup because the listen fd will be closed */
    150 	return dup(fd);
    151 }
    152