Blob


1 #include <u.h>
2 #include <libc.h>
4 #undef accept
5 #undef announce
6 #undef dial
7 #undef setnetmtpt
8 #undef hangup
9 #undef listen
10 #undef netmkaddr
11 #undef reject
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <netinet/tcp.h>
16 #include <sys/un.h>
17 #include <netdb.h>
19 #undef unix
20 #define unix xunix
22 static int
23 isany(struct sockaddr_storage *ss)
24 {
25 switch(ss->ss_family){
26 case AF_INET:
27 return (((struct sockaddr_in*)ss)->sin_addr.s_addr == INADDR_ANY);
28 case AF_INET6:
29 return (memcmp(((struct sockaddr_in6*)ss)->sin6_addr.s6_addr,
30 in6addr_any.s6_addr, sizeof (struct in6_addr)) == 0);
31 }
32 return 0;
33 }
35 static int
36 addrlen(struct sockaddr_storage *ss)
37 {
38 switch(ss->ss_family){
39 case AF_INET:
40 return sizeof(struct sockaddr_in);
41 case AF_INET6:
42 return sizeof(struct sockaddr_in6);
43 case AF_UNIX:
44 return sizeof(struct sockaddr_un);
45 }
46 return 0;
47 }
49 int
50 p9dial(char *addr, char *local, char *dummy2, int *dummy3)
51 {
52 char *buf;
53 char *net, *unix;
54 int port;
55 int proto;
56 socklen_t sn;
57 int n;
58 struct sockaddr_storage ss, ssl;
59 int s;
61 if(dummy2 || dummy3){
62 werrstr("cannot handle extra arguments in dial");
63 return -1;
64 }
66 buf = strdup(addr);
67 if(buf == nil)
68 return -1;
70 if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){
71 free(buf);
72 return -1;
73 }
74 if(strcmp(net, "unix") != 0 && isany(&ss)){
75 werrstr("invalid dial address 0.0.0.0 (aka *)");
76 free(buf);
77 return -1;
78 }
80 if(strcmp(net, "tcp") == 0)
81 proto = SOCK_STREAM;
82 else if(strcmp(net, "udp") == 0)
83 proto = SOCK_DGRAM;
84 else if(strcmp(net, "unix") == 0)
85 goto Unix;
86 else{
87 werrstr("can only handle tcp, udp, and unix: not %s", net);
88 free(buf);
89 return -1;
90 }
91 free(buf);
93 if((s = socket(ss.ss_family, proto, 0)) < 0)
94 return -1;
96 if(local){
97 buf = strdup(local);
98 if(buf == nil){
99 close(s);
100 return -1;
102 if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){
103 badlocal:
104 free(buf);
105 close(s);
106 return -1;
108 if(unix){
109 werrstr("bad local address %s for dial %s", local, addr);
110 goto badlocal;
112 sn = sizeof n;
113 if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
114 && n == SOCK_STREAM){
115 n = 1;
116 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
118 if(bind(s, (struct sockaddr*)&ssl, addrlen(&ssl)) < 0)
119 goto badlocal;
120 free(buf);
123 n = 1;
124 setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof n);
125 if(!isany(&ss)){
126 if(connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){
127 close(s);
128 return -1;
131 if(proto == SOCK_STREAM){
132 int one = 1;
133 setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
135 return s;
137 Unix:
138 if(local){
139 werrstr("local address not supported on unix network");
140 free(buf);
141 return -1;
143 /* Allow regular files in addition to Unix sockets. */
144 if((s = open(unix, ORDWR)) >= 0){
145 free(buf);
146 return s;
148 free(buf);
149 if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0){
150 werrstr("socket: %r");
151 return -1;
153 if(connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){
154 werrstr("connect %s: %r", ((struct sockaddr_un*)&ss)->sun_path);
155 close(s);
156 return -1;
158 return s;