dial.c (3086B)
1 #include <u.h> 2 #include <libc.h> 3 4 #undef accept 5 #undef announce 6 #undef dial 7 #undef setnetmtpt 8 #undef hangup 9 #undef listen 10 #undef netmkaddr 11 #undef reject 12 13 #include <sys/socket.h> 14 #include <netinet/in.h> 15 #include <netinet/tcp.h> 16 #include <sys/un.h> 17 #include <netdb.h> 18 19 #undef unix 20 #define unix xunix 21 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 } 34 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 } 48 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; 60 61 if(dummy2 || dummy3){ 62 werrstr("cannot handle extra arguments in dial"); 63 return -1; 64 } 65 66 buf = strdup(addr); 67 if(buf == nil) 68 return -1; 69 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 } 79 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); 92 93 if((s = socket(ss.ss_family, proto, 0)) < 0) 94 return -1; 95 96 if(local){ 97 buf = strdup(local); 98 if(buf == nil){ 99 close(s); 100 return -1; 101 } 102 if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){ 103 badlocal: 104 free(buf); 105 close(s); 106 return -1; 107 } 108 if(unix){ 109 werrstr("bad local address %s for dial %s", local, addr); 110 goto badlocal; 111 } 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); 117 } 118 if(bind(s, (struct sockaddr*)&ssl, addrlen(&ssl)) < 0) 119 goto badlocal; 120 free(buf); 121 } 122 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; 129 } 130 } 131 if(proto == SOCK_STREAM){ 132 int one = 1; 133 setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one); 134 } 135 return s; 136 137 Unix: 138 if(local){ 139 werrstr("local address not supported on unix network"); 140 free(buf); 141 return -1; 142 } 143 /* Allow regular files in addition to Unix sockets. */ 144 if((s = open(unix, ORDWR)) >= 0){ 145 free(buf); 146 return s; 147 } 148 free(buf); 149 if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0){ 150 werrstr("socket: %r"); 151 return -1; 152 } 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; 157 } 158 return s; 159 }