9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

dial.c (2943B)


      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 int
     23 p9dial(char *addr, char *local, char *dummy2, int *dummy3)
     24 {
     25 	char *buf;
     26 	char *net, *unix;
     27 	u32int host;
     28 	int port;
     29 	int proto;
     30 	socklen_t sn;
     31 	int n;
     32 	struct sockaddr_in sa, sal;	
     33 	struct sockaddr_un su;
     34 	int s;
     35 
     36 	if(dummy2 || dummy3){
     37 		werrstr("cannot handle extra arguments in dial");
     38 		return -1;
     39 	}
     40 
     41 	buf = strdup(addr);
     42 	if(buf == nil)
     43 		return -1;
     44 
     45 	if(p9dialparse(buf, &net, &unix, &host, &port) < 0){
     46 		free(buf);
     47 		return -1;
     48 	}
     49 	if(strcmp(net, "unix") != 0 && host == 0){
     50 		werrstr("invalid dial address 0.0.0.0 (aka *)");
     51 		free(buf);
     52 		return -1;
     53 	}
     54 
     55 	if(strcmp(net, "tcp") == 0)
     56 		proto = SOCK_STREAM;
     57 	else if(strcmp(net, "udp") == 0)
     58 		proto = SOCK_DGRAM;
     59 	else if(strcmp(net, "unix") == 0)
     60 		goto Unix;
     61 	else{
     62 		werrstr("can only handle tcp, udp, and unix: not %s", net);
     63 		free(buf);
     64 		return -1;
     65 	}
     66 	free(buf);
     67 
     68 	if((s = socket(AF_INET, proto, 0)) < 0)
     69 		return -1;
     70 		
     71 	if(local){
     72 		buf = strdup(local);
     73 		if(buf == nil){
     74 			close(s);
     75 			return -1;
     76 		}
     77 		if(p9dialparse(buf, &net, &unix, &host, &port) < 0){
     78 		badlocal:
     79 			free(buf);
     80 			close(s);
     81 			return -1;
     82 		}
     83 		if(unix){
     84 			werrstr("bad local address %s for dial %s", local, addr);
     85 			goto badlocal;
     86 		}
     87 		memset(&sal, 0, sizeof sal);
     88 		memmove(&sal.sin_addr, &local, 4);
     89 		sal.sin_family = AF_INET;
     90 		sal.sin_port = htons(port);
     91 		sn = sizeof n;
     92 		if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
     93 		&& n == SOCK_STREAM){
     94 			n = 1;
     95 			setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
     96 		}
     97 		if(bind(s, (struct sockaddr*)&sal, sizeof sal) < 0)
     98 			goto badlocal;
     99 		free(buf);
    100 	}
    101 
    102 	n = 1;
    103 	setsockopt(s, SOL_SOCKET, SO_BROADCAST, &n, sizeof n);
    104 	if(host != 0){
    105 		memset(&sa, 0, sizeof sa);
    106 		memmove(&sa.sin_addr, &host, 4);
    107 		sa.sin_family = AF_INET;
    108 		sa.sin_port = htons(port);
    109 		if(connect(s, (struct sockaddr*)&sa, sizeof sa) < 0){
    110 			close(s);
    111 			return -1;
    112 		}
    113 	}
    114 	if(proto == SOCK_STREAM){
    115 		int one = 1;
    116 		setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
    117 	}
    118 	return s;
    119 
    120 Unix:
    121 	if(local){
    122 		werrstr("local address not supported on unix network");
    123 		free(buf);
    124 		return -1;
    125 	}
    126 	/* Allow regular files in addition to Unix sockets. */
    127 	if((s = open(unix, ORDWR)) >= 0)
    128 		return s;
    129 	memset(&su, 0, sizeof su);
    130 	su.sun_family = AF_UNIX;
    131 	if(strlen(unix)+1 > sizeof su.sun_path){
    132 		werrstr("unix socket name too long");
    133 		free(buf);
    134 		return -1;
    135 	}
    136 	strcpy(su.sun_path, unix);
    137 	free(buf);
    138 	if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
    139 		werrstr("socket: %r");
    140 		return -1;
    141 	}
    142 	if(connect(s, (struct sockaddr*)&su, sizeof su) < 0){
    143 		werrstr("connect %s: %r", su.sun_path);
    144 		close(s);
    145 		return -1;
    146 	}
    147 	return s;
    148 }
    149