plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

announce.c (2737B)


      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 static int
     37 addrlen(struct sockaddr_storage *ss)
     38 {
     39 	switch(ss->ss_family){
     40 	case AF_INET:
     41 		return sizeof(struct sockaddr_in);
     42 	case AF_INET6:
     43 		return sizeof(struct sockaddr_in6);
     44 	case AF_UNIX:
     45 		return sizeof(struct sockaddr_un);
     46 	}
     47 	return 0;
     48 }
     49 
     50 int
     51 p9announce(char *addr, char *dir)
     52 {
     53 	int proto;
     54 	char *buf, *unix;
     55 	char *net;
     56 	int port, s;
     57 	int n;
     58 	socklen_t sn;
     59 	struct sockaddr_storage ss;
     60 
     61 	buf = strdup(addr);
     62 	if(buf == nil)
     63 		return -1;
     64 
     65 	if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){
     66 		free(buf);
     67 		return -1;
     68 	}
     69 	if(strcmp(net, "tcp") == 0)
     70 		proto = SOCK_STREAM;
     71 	else if(strcmp(net, "udp") == 0)
     72 		proto = SOCK_DGRAM;
     73 	else if(strcmp(net, "unix") == 0)
     74 		goto Unix;
     75 	else{
     76 		werrstr("can only handle tcp, udp, and unix: not %s", net);
     77 		free(buf);
     78 		return -1;
     79 	}
     80 	free(buf);
     81 
     82 	if((s = socket(ss.ss_family, proto, 0)) < 0)
     83 		return -1;
     84 	sn = sizeof n;
     85 	if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
     86 	&& n == SOCK_STREAM){
     87 		n = 1;
     88 		setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
     89 	}
     90 	if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){
     91 		close(s);
     92 		return -1;
     93 	}
     94 	if(proto == SOCK_STREAM){
     95 		listen(s, 8);
     96 		putfd(dir, s);
     97 	}
     98 	return s;
     99 
    100 Unix:
    101 	if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0)
    102 		return -1;
    103 	if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){
    104 		if(errno == EADDRINUSE
    105 		&& connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0
    106 		&& errno == ECONNREFUSED){
    107 			/* dead socket, so remove it */
    108 			remove(unix);
    109 			close(s);
    110 			if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0)
    111 				return -1;
    112 			if(bind(s, (struct sockaddr*)&ss, addrlen(&ss)) >= 0)
    113 				goto Success;
    114 		}
    115 		close(s);
    116 		return -1;
    117 	}
    118 Success:
    119 	listen(s, 8);
    120 	putfd(dir, s);
    121 	return s;
    122 }
    123 
    124 int
    125 p9listen(char *dir, char *newdir)
    126 {
    127 	int fd, one;
    128 
    129 	if((fd = _p9netfd(dir)) < 0){
    130 		werrstr("bad 'directory' in listen: %s", dir);
    131 		return -1;
    132 	}
    133 
    134 	if((fd = accept(fd, nil, nil)) < 0)
    135 		return -1;
    136 
    137 	one = 1;
    138 	setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
    139 
    140 	putfd(newdir, fd);
    141 	return fd;
    142 }
    143 
    144 int
    145 p9accept(int cfd, char *dir)
    146 {
    147 	int fd;
    148 
    149 	if((fd = _p9netfd(dir)) < 0){
    150 		werrstr("bad 'directory' in accept");
    151 		return -1;
    152 	}
    153 	/* need to dup because the listen fd will be closed */
    154 	return dup(fd);
    155 }