| #include <u.h> |
| #include <libc.h> |
| |
| #undef accept |
| #undef announce |
| #undef dial |
| #undef setnetmtpt |
| #undef hangup |
| #undef listen |
| #undef netmkaddr |
| #undef reject |
| |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <netinet/tcp.h> |
| #include <sys/un.h> |
| #include <netdb.h> |
| |
| #undef unix |
| #define unix xunix |
| |
| static int |
| isany(struct sockaddr_storage *ss) |
| { |
| switch(ss->ss_family){ |
| case AF_INET: |
| return (((struct sockaddr_in*)ss)->sin_addr.s_addr == INADDR_ANY); |
| case AF_INET6: |
| return (memcmp(((struct sockaddr_in6*)ss)->sin6_addr.s6_addr, |
| in6addr_any.s6_addr, sizeof (struct in6_addr)) == 0); |
| } |
| return 0; |
| } |
| |
| static int |
| addrlen(struct sockaddr_storage *ss) |
| { |
| switch(ss->ss_family){ |
| case AF_INET: |
| return sizeof(struct sockaddr_in); |
| case AF_INET6: |
| return sizeof(struct sockaddr_in6); |
| case AF_UNIX: |
| return sizeof(struct sockaddr_un); |
| } |
| return 0; |
| } |
| |
| int |
| p9dial(char *addr, char *local, char *dummy2, int *dummy3) |
| { |
| char *buf; |
| char *net, *unix; |
| int port; |
| int proto; |
| socklen_t sn; |
| int n; |
| struct sockaddr_storage ss, ssl; |
| int s; |
| |
| if(dummy2 || dummy3){ |
| werrstr("cannot handle extra arguments in dial"); |
| return -1; |
| } |
| |
| buf = strdup(addr); |
| if(buf == nil) |
| return -1; |
| |
| if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){ |
| free(buf); |
| return -1; |
| } |
| if(strcmp(net, "unix") != 0 && isany(&ss)){ |
| werrstr("invalid dial address 0.0.0.0 (aka *)"); |
| free(buf); |
| return -1; |
| } |
| |
| if(strcmp(net, "tcp") == 0) |
| proto = SOCK_STREAM; |
| else if(strcmp(net, "udp") == 0) |
| proto = SOCK_DGRAM; |
| else if(strcmp(net, "unix") == 0) |
| goto Unix; |
| else{ |
| werrstr("can only handle tcp, udp, and unix: not %s", net); |
| free(buf); |
| return -1; |
| } |
| free(buf); |
| |
| if((s = socket(ss.ss_family, proto, 0)) < 0) |
| return -1; |
| |
| if(local){ |
| buf = strdup(local); |
| if(buf == nil){ |
| close(s); |
| return -1; |
| } |
| if(p9dialparse(buf, &net, &unix, &ss, &port) < 0){ |
| badlocal: |
| free(buf); |
| close(s); |
| return -1; |
| } |
| if(unix){ |
| werrstr("bad local address %s for dial %s", local, addr); |
| goto badlocal; |
| } |
| sn = sizeof n; |
| if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0 |
| && n == SOCK_STREAM){ |
| n = 1; |
| setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); |
| } |
| if(bind(s, (struct sockaddr*)&ssl, addrlen(&ssl)) < 0) |
| goto badlocal; |
| free(buf); |
| } |
| |
| n = 1; |
| setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof n); |
| if(!isany(&ss)){ |
| if(connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){ |
| close(s); |
| return -1; |
| } |
| } |
| if(proto == SOCK_STREAM){ |
| int one = 1; |
| setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one); |
| } |
| return s; |
| |
| Unix: |
| if(local){ |
| werrstr("local address not supported on unix network"); |
| free(buf); |
| return -1; |
| } |
| /* Allow regular files in addition to Unix sockets. */ |
| if((s = open(unix, ORDWR)) >= 0) |
| return s; |
| free(buf); |
| if((s = socket(ss.ss_family, SOCK_STREAM, 0)) < 0){ |
| werrstr("socket: %r"); |
| return -1; |
| } |
| if(connect(s, (struct sockaddr*)&ss, addrlen(&ss)) < 0){ |
| werrstr("connect %s: %r", ((struct sockaddr_un*)&ss)->sun_path); |
| close(s); |
| return -1; |
| } |
| return s; |
| } |
| |