tannounce.c - plan9port - [fork] Plan 9 from user space
HTML git clone git://src.adamsgaard.dk/plan9port
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tannounce.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 }