plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }