udphole

Basic UDP wormhole proxy
git clone git://git.finwo.net/app/udphole
Log | Files | Refs | README | LICENSE

socket_util.c (10449B)


      1 #include "common/socket_util.h"
      2 #include "rxi/log.h"
      3 #include <fcntl.h>
      4 #include <unistd.h>
      5 #include <string.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <sys/socket.h>
      9 #include <netinet/in.h>
     10 #include <arpa/inet.h>
     11 #include <netdb.h>
     12 #include <errno.h>
     13 #include <sys/un.h>
     14 #include <sys/stat.h>
     15 #include <pwd.h>
     16 #include <grp.h>
     17 
     18 int set_socket_nonblocking(int fd, int nonblock) {
     19   int flags = fcntl(fd, F_GETFL, 0);
     20   if (flags < 0) return -1;
     21   if (nonblock)
     22     flags |= O_NONBLOCK;
     23   else
     24     flags &= ~O_NONBLOCK;
     25   return fcntl(fd, F_SETFL, flags) == 0 ? 0 : -1;
     26 }
     27 
     28 int *tcp_listen(const char *addr, const char *default_host, const char *default_port) {
     29   char host[256] = "";
     30   char port[32] = "";
     31 
     32   if (default_host && default_host[0]) {
     33     snprintf(host, sizeof(host), "%s", default_host);
     34   }
     35   if (default_port && default_port[0]) {
     36     snprintf(port, sizeof(port), "%s", default_port);
     37   }
     38 
     39   if (!addr || !addr[0]) {
     40     if (!host[0] || !port[0]) {
     41       log_error("tcp_listen: empty address and no defaults");
     42       return NULL;
     43     }
     44   } else if (addr[0] == '[') {
     45     const char *close_bracket = strchr(addr, ']');
     46     if (!close_bracket) {
     47       log_error("tcp_listen: invalid IPv6 format: missing ']'");
     48       return NULL;
     49     }
     50     size_t hlen = (size_t)(close_bracket - addr - 1);
     51     if (hlen > 0) {
     52       if (hlen >= sizeof(host)) hlen = sizeof(host) - 1;
     53       memcpy(host, addr + 1, hlen);
     54       host[hlen] = '\0';
     55     }
     56     const char *colon = close_bracket + 1;
     57     if (*colon == ':') {
     58       snprintf(port, sizeof(port), "%s", colon + 1);
     59     } else if (*colon != '\0') {
     60       log_error("tcp_listen: invalid IPv6 format: expected ':' after ']'");
     61       return NULL;
     62     }
     63   } else {
     64     int leading_colon = (addr[0] == ':');
     65     const char *p = leading_colon ? addr + 1 : addr;
     66     int is_port_only = 1;
     67     for (const char *q = p; *q; q++) {
     68       if (*q < '0' || *q > '9') { is_port_only = 0; break; }
     69     }
     70 
     71     const char *colon = strrchr(addr, ':');
     72     if (leading_colon && is_port_only) {
     73       snprintf(port, sizeof(port), "%s", p);
     74     } else if (is_port_only) {
     75       if (default_host && default_host[0]) {
     76         snprintf(host, sizeof(host), "%s", default_host);
     77       }
     78       snprintf(port, sizeof(port), "%s", p);
     79     } else if (colon) {
     80       size_t hlen = (size_t)(colon - addr);
     81       if (hlen > 0) {
     82         if (hlen >= sizeof(host)) hlen = sizeof(host) - 1;
     83         memcpy(host, addr, hlen);
     84         host[hlen] = '\0';
     85       }
     86       snprintf(port, sizeof(port), "%s", colon + 1);
     87     } else {
     88       snprintf(host, sizeof(host), "%s", addr);
     89     }
     90   }
     91 
     92   if (!port[0]) {
     93     log_error("tcp_listen: no port specified");
     94     return NULL;
     95   }
     96 
     97   struct addrinfo hints, *res = NULL;
     98   memset(&hints, 0, sizeof(hints));
     99   hints.ai_family = AF_UNSPEC;
    100   hints.ai_socktype = SOCK_STREAM;
    101   hints.ai_flags = AI_PASSIVE;
    102   if (getaddrinfo(host[0] ? host : NULL, port, &hints, &res) != 0 || !res) {
    103     log_error("tcp_listen: getaddrinfo failed for %s:%s", host, port);
    104     return NULL;
    105   }
    106 
    107   int *fds = malloc(sizeof(int) * 3);
    108   if (!fds) {
    109     freeaddrinfo(res);
    110     return NULL;
    111   }
    112   fds[0] = 0;
    113 
    114   int listen_all = (host[0] == '\0');
    115   struct addrinfo *p;
    116 
    117   for (p = res; p; p = p->ai_next) {
    118     if (p->ai_family == AF_INET) {
    119       int fd = socket(AF_INET, SOCK_STREAM, 0);
    120       if (fd < 0) continue;
    121       int opt = 1;
    122       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    123       if (bind(fd, p->ai_addr, p->ai_addrlen) == 0 && listen(fd, 8) == 0) {
    124         set_socket_nonblocking(fd, 1);
    125         fds[++fds[0]] = fd;
    126       } else {
    127         close(fd);
    128       }
    129     } else if (p->ai_family == AF_INET6 && listen_all) {
    130       int fd = socket(AF_INET6, SOCK_STREAM, 0);
    131       if (fd < 0) continue;
    132       int opt = 1;
    133       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    134       setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
    135       if (bind(fd, p->ai_addr, p->ai_addrlen) == 0 && listen(fd, 8) == 0) {
    136         set_socket_nonblocking(fd, 1);
    137         fds[++fds[0]] = fd;
    138       } else {
    139         close(fd);
    140       }
    141     }
    142   }
    143 
    144   freeaddrinfo(res);
    145 
    146   if (fds[0] == 0) {
    147     log_error("tcp_listen: failed to bind to %s:%s", host, port);
    148     free(fds);
    149     return NULL;
    150   }
    151 
    152   return fds;
    153 }
    154 
    155 int *udp_recv(const char *addr, const char *default_host, const char *default_port) {
    156   char host[256] = "";
    157   char port[32] = "";
    158 
    159   if (default_host && default_host[0]) {
    160     snprintf(host, sizeof(host), "%s", default_host);
    161   }
    162   if (default_port && default_port[0]) {
    163     snprintf(port, sizeof(port), "%s", default_port);
    164   }
    165 
    166   if (!addr || !addr[0]) {
    167     if (!host[0] || !port[0]) {
    168       log_error("udp_recv: empty address and no defaults");
    169       return NULL;
    170     }
    171   } else if (addr[0] == '[') {
    172     const char *close_bracket = strchr(addr, ']');
    173     if (!close_bracket) {
    174       log_error("udp_recv: invalid IPv6 format: missing ']'");
    175       return NULL;
    176     }
    177     size_t hlen = (size_t)(close_bracket - addr - 1);
    178     if (hlen > 0) {
    179       if (hlen >= sizeof(host)) hlen = sizeof(host) - 1;
    180       memcpy(host, addr + 1, hlen);
    181       host[hlen] = '\0';
    182     }
    183     const char *colon = close_bracket + 1;
    184     if (*colon == ':') {
    185       snprintf(port, sizeof(port), "%s", colon + 1);
    186     } else if (*colon != '\0') {
    187       log_error("udp_recv: invalid IPv6 format: expected ':' after ']'");
    188       return NULL;
    189     }
    190   } else {
    191     int leading_colon = (addr[0] == ':');
    192     const char *p = leading_colon ? addr + 1 : addr;
    193     int is_port_only = 1;
    194     for (const char *q = p; *q; q++) {
    195       if (*q < '0' || *q > '9') { is_port_only = 0; break; }
    196     }
    197 
    198     const char *colon = strrchr(addr, ':');
    199     if (leading_colon && is_port_only) {
    200       snprintf(port, sizeof(port), "%s", p);
    201     } else if (is_port_only) {
    202       if (default_host && default_host[0]) {
    203         snprintf(host, sizeof(host), "%s", default_host);
    204       }
    205       snprintf(port, sizeof(port), "%s", p);
    206     } else if (colon) {
    207       size_t hlen = (size_t)(colon - addr);
    208       if (hlen > 0) {
    209         if (hlen >= sizeof(host)) hlen = sizeof(host) - 1;
    210         memcpy(host, addr, hlen);
    211         host[hlen] = '\0';
    212       }
    213       snprintf(port, sizeof(port), "%s", colon + 1);
    214     } else {
    215       snprintf(host, sizeof(host), "%s", addr);
    216     }
    217   }
    218 
    219   if (!port[0]) {
    220     log_error("udp_recv: no port specified");
    221     return NULL;
    222   }
    223 
    224   struct addrinfo hints, *res = NULL;
    225   memset(&hints, 0, sizeof(hints));
    226   hints.ai_family = AF_UNSPEC;
    227   hints.ai_socktype = SOCK_DGRAM;
    228   hints.ai_flags = AI_PASSIVE;
    229   if (getaddrinfo(host[0] ? host : NULL, port, &hints, &res) != 0 || !res) {
    230     log_error("udp_recv: getaddrinfo failed for %s:%s", host, port);
    231     return NULL;
    232   }
    233 
    234   int *fds = malloc(sizeof(int) * 3);
    235   if (!fds) {
    236     freeaddrinfo(res);
    237     return NULL;
    238   }
    239   fds[0] = 0;
    240 
    241   int listen_all = (host[0] == '\0');
    242   struct addrinfo *p;
    243 
    244   for (p = res; p; p = p->ai_next) {
    245     if (p->ai_family == AF_INET) {
    246       int fd = socket(AF_INET, SOCK_DGRAM, 0);
    247       if (fd < 0) continue;
    248       int opt = 1;
    249       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    250       if (bind(fd, p->ai_addr, p->ai_addrlen) == 0) {
    251         set_socket_nonblocking(fd, 1);
    252         fds[++fds[0]] = fd;
    253       } else {
    254         close(fd);
    255       }
    256     } else if (p->ai_family == AF_INET6 && listen_all) {
    257       int fd = socket(AF_INET6, SOCK_DGRAM, 0);
    258       if (fd < 0) continue;
    259       int opt = 1;
    260       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    261       setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
    262       if (bind(fd, p->ai_addr, p->ai_addrlen) == 0) {
    263         set_socket_nonblocking(fd, 1);
    264         fds[++fds[0]] = fd;
    265       } else {
    266         close(fd);
    267       }
    268     }
    269   }
    270 
    271   freeaddrinfo(res);
    272 
    273   if (fds[0] == 0) {
    274     log_error("udp_recv: failed to bind to %s:%s", host, port);
    275     free(fds);
    276     return NULL;
    277   }
    278 
    279   return fds;
    280 }
    281 
    282 int *unix_listen(const char *path, int sock_type, const char *owner) {
    283   if (!path || !path[0]) {
    284     log_error("unix_listen: empty path");
    285     return NULL;
    286   }
    287 
    288   char *path_copy = strdup(path);
    289   if (!path_copy) {
    290     return NULL;
    291   }
    292 
    293   char *dir = strdup(path);
    294   if (!dir) {
    295     free(path_copy);
    296     return NULL;
    297   }
    298 
    299   char *last_slash = strrchr(dir, '/');
    300   if (last_slash && last_slash != dir) {
    301     *last_slash = '\0';
    302     if (strlen(dir) > 0) {
    303       mkdir(dir, 0755);
    304     }
    305   } else if (!last_slash) {
    306     dir[0] = '.';
    307     dir[1] = '\0';
    308   }
    309   free(dir);
    310 
    311   unlink(path_copy);
    312 
    313   int *fds = malloc(sizeof(int) * 2);
    314   if (!fds) {
    315     free(path_copy);
    316     return NULL;
    317   }
    318   fds[0] = 0;
    319 
    320   int fd = socket(AF_UNIX, sock_type, 0);
    321   if (fd < 0) {
    322     log_error("unix_listen: socket failed: %s", strerror(errno));
    323     free(path_copy);
    324     free(fds);
    325     return NULL;
    326   }
    327 
    328   struct sockaddr_un addr;
    329   memset(&addr, 0, sizeof(addr));
    330   addr.sun_family = AF_UNIX;
    331   strncpy(addr.sun_path, path_copy, sizeof(addr.sun_path) - 1);
    332 
    333   if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    334     log_error("unix_listen: bind failed: %s", strerror(errno));
    335     close(fd);
    336     free(path_copy);
    337     free(fds);
    338     return NULL;
    339   }
    340 
    341   if (owner && owner[0]) {
    342     uid_t uid = -1;
    343     gid_t gid = -1;
    344     char *owner_copy = strdup(owner);
    345     if (owner_copy) {
    346       char *colon = strchr(owner_copy, ':');
    347       if (colon) {
    348         *colon = '\0';
    349         colon++;
    350         if (colon[0]) {
    351           struct passwd *pw = getpwnam(owner_copy);
    352           if (pw) {
    353             uid = pw->pw_uid;
    354             struct group *gr = getgrnam(colon);
    355             if (gr) {
    356               gid = gr->gr_gid;
    357             }
    358           }
    359         }
    360       } else {
    361         struct passwd *pw = getpwnam(owner_copy);
    362         if (pw) {
    363           uid = pw->pw_uid;
    364           gid = pw->pw_gid;
    365         }
    366       }
    367       free(owner_copy);
    368     }
    369     if (uid != (uid_t)-1 || gid != (gid_t)-1) {
    370       if (fchown(fd, uid, gid) < 0) {
    371         log_error("unix_listen: fchown failed: %s", strerror(errno));
    372         close(fd);
    373         unlink(path_copy);
    374         free(path_copy);
    375         free(fds);
    376         return NULL;
    377       }
    378     }
    379   }
    380 
    381   if (sock_type == SOCK_STREAM) {
    382     if (listen(fd, 8) < 0) {
    383       log_error("unix_listen: listen failed: %s", strerror(errno));
    384       close(fd);
    385       unlink(path_copy);
    386       free(path_copy);
    387       free(fds);
    388       return NULL;
    389     }
    390   }
    391 
    392   set_socket_nonblocking(fd, 1);
    393 
    394   fds[++fds[0]] = fd;
    395 
    396   free(path_copy);
    397   return fds;
    398 }