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 }