Blob


1 #include <u.h>
2 #define NOPLAN9DEFINES
3 #include <libc.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <netinet/tcp.h>
8 #include <sys/un.h>
9 #include <errno.h>
11 #undef sun
12 #define sun sockun
14 int
15 _p9netfd(char *dir)
16 {
17 int fd;
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 }
27 static void
28 putfd(char *dir, int fd)
29 {
30 snprint(dir, NETPATHLEN, "/dev/fd/%d", fd);
31 }
33 #undef unix
34 #define unix sockunix
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 }
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;
61 buf = strdup(addr);
62 if(buf == nil)
63 return -1;
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);
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;
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;
115 close(s);
116 return -1;
118 Success:
119 listen(s, 8);
120 putfd(dir, s);
121 return s;
124 int
125 p9listen(char *dir, char *newdir)
127 int fd, one;
129 if((fd = _p9netfd(dir)) < 0){
130 werrstr("bad 'directory' in listen: %s", dir);
131 return -1;
134 if((fd = accept(fd, nil, nil)) < 0)
135 return -1;
137 one = 1;
138 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
140 putfd(newdir, fd);
141 return fd;
144 int
145 p9accept(int cfd, char *dir)
147 int fd;
149 if((fd = _p9netfd(dir)) < 0){
150 werrstr("bad 'directory' in accept");
151 return -1;
153 /* need to dup because the listen fd will be closed */
154 return dup(fd);