announce.c - 9base - revived minimalist port of Plan 9 userland to Unix
 (HTM) git clone git://git.suckless.org/9base
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       announce.c (2724B)
       ---
            1 #include <u.h>
            2 #define NOPLAN9DEFINES
            3 #include <libc.h>
            4 
            5 #include <sys/socket.h>
            6 #include <netinet/in.h>
            7 #include <netinet/tcp.h>
            8 #include <sys/un.h>
            9 #include <errno.h>
           10 
           11 #undef sun
           12 #define sun sockun
           13 
           14 int
           15 _p9netfd(char *dir)
           16 {
           17         int fd;
           18 
           19         if(strncmp(dir, "/dev/fd/", 8) != 0)
           20                 return -1;
           21         fd = strtol(dir+8, &dir, 0);
           22         if(*dir != 0)
           23                 return -1;
           24         return fd;
           25 }
           26 
           27 static void
           28 putfd(char *dir, int fd)
           29 {
           30         snprint(dir, NETPATHLEN, "/dev/fd/%d", fd);
           31 }
           32 
           33 #undef unix
           34 #define unix sockunix
           35 
           36 int
           37 p9announce(char *addr, char *dir)
           38 {
           39         int proto;
           40         char *buf, *unix;
           41         char *net;
           42         u32int host;
           43         int port, s;
           44         int n;
           45         socklen_t sn;
           46         struct sockaddr_in sa;
           47         struct sockaddr_un sun;
           48 
           49         buf = strdup(addr);
           50         if(buf == nil)
           51                 return -1;
           52 
           53         if(p9dialparse(buf, &net, &unix, &host, &port) < 0){
           54                 free(buf);
           55                 return -1;
           56         }
           57         if(strcmp(net, "tcp") == 0)
           58                 proto = SOCK_STREAM;
           59         else if(strcmp(net, "udp") == 0)
           60                 proto = SOCK_DGRAM;
           61         else if(strcmp(net, "unix") == 0)
           62                 goto Unix;
           63         else{
           64                 werrstr("can only handle tcp, udp, and unix: not %s", net);
           65                 free(buf);
           66                 return -1;
           67         }
           68         free(buf);
           69 
           70         memset(&sa, 0, sizeof sa);
           71         memmove(&sa.sin_addr, &host, 4);
           72         sa.sin_family = AF_INET;
           73         sa.sin_port = htons(port);
           74         if((s = socket(AF_INET, proto, 0)) < 0)
           75                 return -1;
           76         sn = sizeof n;
           77         if(port && getsockopt(s, SOL_SOCKET, SO_TYPE, (void*)&n, &sn) >= 0
           78         && n == SOCK_STREAM){
           79                 n = 1;
           80                 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
           81         }
           82         if(bind(s, (struct sockaddr*)&sa, sizeof sa) < 0){
           83                 close(s);
           84                 return -1;
           85         }
           86         if(proto == SOCK_STREAM){
           87                 listen(s, 8);
           88                 putfd(dir, s);
           89         }
           90         return s;
           91 
           92 Unix:
           93         memset(&sun, 0, sizeof sun);
           94         sun.sun_family = AF_UNIX;
           95         strcpy(sun.sun_path, unix);
           96         if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
           97                 return -1;
           98         sn = sizeof sun;
           99         if(bind(s, (struct sockaddr*)&sun, sizeof sun) < 0){
          100                 if(errno == EADDRINUSE
          101                 && connect(s, (struct sockaddr*)&sun, sizeof sun) < 0
          102                 && errno == ECONNREFUSED){
          103                         /* dead socket, so remove it */
          104                         remove(unix);
          105                         close(s);
          106                         if((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
          107                                 return -1;
          108                         if(bind(s, (struct sockaddr*)&sun, sizeof sun) >= 0)
          109                                 goto Success;
          110                 }
          111                 close(s);
          112                 return -1;
          113         }
          114 Success:
          115         listen(s, 8);
          116         putfd(dir, s);
          117         return s;
          118 }
          119 
          120 int
          121 p9listen(char *dir, char *newdir)
          122 {
          123         int fd, one;
          124 
          125         if((fd = _p9netfd(dir)) < 0){
          126                 werrstr("bad 'directory' in listen: %s", dir);
          127                 return -1;
          128         }
          129 
          130         if((fd = accept(fd, nil, nil)) < 0)
          131                 return -1;
          132 
          133         one = 1;
          134         setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
          135 
          136         putfd(newdir, fd);
          137         return fd;
          138 }
          139 
          140 int
          141 p9accept(int cfd, char *dir)
          142 {
          143         int fd;
          144 
          145         if((fd = _p9netfd(dir)) < 0){
          146                 werrstr("bad 'directory' in accept");
          147                 return -1;
          148         }
          149         /* need to dup because the listen fd will be closed */
          150         return dup(fd);
          151 }
          152