URI:
       tdial.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tdial.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 }