socket-util.c (13971B)
1 #include "socket-util.h" 2 3 #include <arpa/inet.h> 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <grp.h> 7 #include <netdb.h> 8 #include <netinet/in.h> 9 #include <pwd.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <sys/socket.h> 14 #include <sys/stat.h> 15 #include <sys/un.h> 16 #include <unistd.h> 17 18 #include "rxi/log.h" 19 20 int set_socket_nonblocking(int fd, int nonblock) { 21 int flags = fcntl(fd, F_GETFL, 0); 22 if (flags < 0) return -1; 23 if (nonblock) 24 flags |= O_NONBLOCK; 25 else 26 flags &= ~O_NONBLOCK; 27 return fcntl(fd, F_SETFL, flags) == 0 ? 0 : -1; 28 } 29 30 int *tcp_listen(const char *addr, const char *default_host, const char *default_port) { 31 char host[256] = ""; 32 char port[32] = ""; 33 34 if (default_host && default_host[0]) { 35 snprintf(host, sizeof(host), "%s", default_host); 36 } 37 if (default_port && default_port[0]) { 38 snprintf(port, sizeof(port), "%s", default_port); 39 } 40 41 if (!addr || !addr[0]) { 42 if (!host[0] || !port[0]) { 43 log_error("tcp_listen: empty address and no defaults"); 44 return NULL; 45 } 46 } else if (addr[0] == '[') { 47 const char *close_bracket = strchr(addr, ']'); 48 if (!close_bracket) { 49 log_error("tcp_listen: invalid IPv6 format: missing ']'"); 50 return NULL; 51 } 52 size_t hlen = (size_t)(close_bracket - addr - 1); 53 if (hlen > 0) { 54 if (hlen >= sizeof(host)) hlen = sizeof(host) - 1; 55 memcpy(host, addr + 1, hlen); 56 host[hlen] = '\0'; 57 } 58 const char *colon = close_bracket + 1; 59 if (*colon == ':') { 60 snprintf(port, sizeof(port), "%s", colon + 1); 61 } else if (*colon != '\0') { 62 log_error("tcp_listen: invalid IPv6 format: expected ':' after ']'"); 63 return NULL; 64 } 65 } else { 66 int leading_colon = (addr[0] == ':'); 67 const char *p = leading_colon ? addr + 1 : addr; 68 int is_port_only = 1; 69 for (const char *q = p; *q; q++) { 70 if (*q < '0' || *q > '9') { 71 is_port_only = 0; 72 break; 73 } 74 } 75 76 const char *colon = strrchr(addr, ':'); 77 if (leading_colon && is_port_only) { 78 snprintf(port, sizeof(port), "%s", p); 79 } else if (is_port_only) { 80 if (default_host && default_host[0]) { 81 snprintf(host, sizeof(host), "%s", default_host); 82 } 83 snprintf(port, sizeof(port), "%s", p); 84 } else if (colon) { 85 size_t hlen = (size_t)(colon - addr); 86 if (hlen > 0) { 87 if (hlen >= sizeof(host)) hlen = sizeof(host) - 1; 88 memcpy(host, addr, hlen); 89 host[hlen] = '\0'; 90 } 91 snprintf(port, sizeof(port), "%s", colon + 1); 92 } else { 93 snprintf(host, sizeof(host), "%s", addr); 94 } 95 } 96 97 if (!port[0]) { 98 log_error("tcp_listen: no port specified"); 99 return NULL; 100 } 101 102 struct addrinfo hints, *res = NULL; 103 memset(&hints, 0, sizeof(hints)); 104 hints.ai_family = AF_UNSPEC; 105 hints.ai_socktype = SOCK_STREAM; 106 hints.ai_flags = AI_PASSIVE; 107 if (getaddrinfo(host[0] ? host : NULL, port, &hints, &res) != 0 || !res) { 108 log_error("tcp_listen: getaddrinfo failed for %s:%s", host, port); 109 return NULL; 110 } 111 112 int *fds = malloc(sizeof(int) * 3); 113 if (!fds) { 114 freeaddrinfo(res); 115 return NULL; 116 } 117 fds[0] = 0; 118 119 int listen_all = (host[0] == '\0'); 120 struct addrinfo *p; 121 122 for (p = res; p; p = p->ai_next) { 123 if (p->ai_family == AF_INET) { 124 int fd = socket(AF_INET, SOCK_STREAM, 0); 125 if (fd < 0) continue; 126 int opt = 1; 127 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 128 if (bind(fd, p->ai_addr, p->ai_addrlen) == 0 && listen(fd, 8) == 0) { 129 set_socket_nonblocking(fd, 1); 130 fds[++fds[0]] = fd; 131 } else { 132 close(fd); 133 } 134 } else if (p->ai_family == AF_INET6 && listen_all) { 135 int fd = socket(AF_INET6, SOCK_STREAM, 0); 136 if (fd < 0) continue; 137 int opt = 1; 138 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 139 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); 140 if (bind(fd, p->ai_addr, p->ai_addrlen) == 0 && listen(fd, 8) == 0) { 141 set_socket_nonblocking(fd, 1); 142 fds[++fds[0]] = fd; 143 } else { 144 close(fd); 145 } 146 } 147 } 148 149 freeaddrinfo(res); 150 151 if (fds[0] == 0) { 152 log_error("tcp_listen: failed to bind to %s:%s", host, port); 153 free(fds); 154 return NULL; 155 } 156 157 return fds; 158 } 159 160 int *udp_recv(const char *addr, const char *default_host, const char *default_port) { 161 char host[256] = ""; 162 char port[32] = ""; 163 164 if (default_host && default_host[0]) { 165 snprintf(host, sizeof(host), "%s", default_host); 166 } 167 if (default_port && default_port[0]) { 168 snprintf(port, sizeof(port), "%s", default_port); 169 } 170 171 if (!addr || !addr[0]) { 172 if (!host[0] || !port[0]) { 173 log_error("udp_recv: empty address and no defaults"); 174 return NULL; 175 } 176 } else if (addr[0] == '[') { 177 const char *close_bracket = strchr(addr, ']'); 178 if (!close_bracket) { 179 log_error("udp_recv: invalid IPv6 format: missing ']'"); 180 return NULL; 181 } 182 size_t hlen = (size_t)(close_bracket - addr - 1); 183 if (hlen > 0) { 184 if (hlen >= sizeof(host)) hlen = sizeof(host) - 1; 185 memcpy(host, addr + 1, hlen); 186 host[hlen] = '\0'; 187 } 188 const char *colon = close_bracket + 1; 189 if (*colon == ':') { 190 snprintf(port, sizeof(port), "%s", colon + 1); 191 } else if (*colon != '\0') { 192 log_error("udp_recv: invalid IPv6 format: expected ':' after ']'"); 193 return NULL; 194 } 195 } else { 196 int leading_colon = (addr[0] == ':'); 197 const char *p = leading_colon ? addr + 1 : addr; 198 int is_port_only = 1; 199 for (const char *q = p; *q; q++) { 200 if (*q < '0' || *q > '9') { 201 is_port_only = 0; 202 break; 203 } 204 } 205 206 const char *colon = strrchr(addr, ':'); 207 if (leading_colon && is_port_only) { 208 snprintf(port, sizeof(port), "%s", p); 209 } else if (is_port_only) { 210 if (default_host && default_host[0]) { 211 snprintf(host, sizeof(host), "%s", default_host); 212 } 213 snprintf(port, sizeof(port), "%s", p); 214 } else if (colon) { 215 size_t hlen = (size_t)(colon - addr); 216 if (hlen > 0) { 217 if (hlen >= sizeof(host)) hlen = sizeof(host) - 1; 218 memcpy(host, addr, hlen); 219 host[hlen] = '\0'; 220 } 221 snprintf(port, sizeof(port), "%s", colon + 1); 222 } else { 223 snprintf(host, sizeof(host), "%s", addr); 224 } 225 } 226 227 if (!port[0]) { 228 log_error("udp_recv: no port specified"); 229 return NULL; 230 } 231 232 struct addrinfo hints, *res = NULL; 233 memset(&hints, 0, sizeof(hints)); 234 hints.ai_family = AF_UNSPEC; 235 hints.ai_socktype = SOCK_DGRAM; 236 hints.ai_flags = AI_PASSIVE; 237 if (getaddrinfo(host[0] ? host : NULL, port, &hints, &res) != 0 || !res) { 238 log_error("udp_recv: getaddrinfo failed for %s:%s", host, port); 239 return NULL; 240 } 241 242 int *fds = malloc(sizeof(int) * 3); 243 if (!fds) { 244 freeaddrinfo(res); 245 return NULL; 246 } 247 fds[0] = 0; 248 249 int listen_all = (host[0] == '\0'); 250 struct addrinfo *p; 251 252 for (p = res; p; p = p->ai_next) { 253 if (p->ai_family == AF_INET) { 254 int fd = socket(AF_INET, SOCK_DGRAM, 0); 255 if (fd < 0) continue; 256 int opt = 1; 257 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 258 if (bind(fd, p->ai_addr, p->ai_addrlen) == 0) { 259 set_socket_nonblocking(fd, 1); 260 fds[++fds[0]] = fd; 261 } else { 262 close(fd); 263 } 264 } else if (p->ai_family == AF_INET6 && listen_all) { 265 int fd = socket(AF_INET6, SOCK_DGRAM, 0); 266 if (fd < 0) continue; 267 int opt = 1; 268 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 269 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); 270 if (bind(fd, p->ai_addr, p->ai_addrlen) == 0) { 271 set_socket_nonblocking(fd, 1); 272 fds[++fds[0]] = fd; 273 } else { 274 close(fd); 275 } 276 } 277 } 278 279 freeaddrinfo(res); 280 281 if (fds[0] == 0) { 282 log_error("udp_recv: failed to bind to %s:%s", host, port); 283 free(fds); 284 return NULL; 285 } 286 287 return fds; 288 } 289 290 int *unix_listen(const char *path, int sock_type, const char *owner) { 291 if (!path || !path[0]) { 292 log_error("unix_listen: empty path"); 293 return NULL; 294 } 295 296 char *path_copy = strdup(path); 297 if (!path_copy) { 298 return NULL; 299 } 300 301 char *dir = strdup(path); 302 if (!dir) { 303 free(path_copy); 304 return NULL; 305 } 306 307 char *last_slash = strrchr(dir, '/'); 308 if (last_slash && last_slash != dir) { 309 *last_slash = '\0'; 310 if (strlen(dir) > 0) { 311 mkdir(dir, 0755); 312 } 313 } else if (!last_slash) { 314 dir[0] = '.'; 315 dir[1] = '\0'; 316 } 317 free(dir); 318 319 unlink(path_copy); 320 321 int *fds = malloc(sizeof(int) * 2); 322 if (!fds) { 323 free(path_copy); 324 return NULL; 325 } 326 fds[0] = 0; 327 328 int fd = socket(AF_UNIX, sock_type, 0); 329 if (fd < 0) { 330 log_error("unix_listen: socket failed: %s", strerror(errno)); 331 free(path_copy); 332 free(fds); 333 return NULL; 334 } 335 336 struct sockaddr_un addr; 337 memset(&addr, 0, sizeof(addr)); 338 addr.sun_family = AF_UNIX; 339 strncpy(addr.sun_path, path_copy, sizeof(addr.sun_path) - 1); 340 341 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 342 log_error("unix_listen: bind failed: %s", strerror(errno)); 343 close(fd); 344 free(path_copy); 345 free(fds); 346 return NULL; 347 } 348 349 if (owner && owner[0]) { 350 uid_t uid = -1; 351 gid_t gid = -1; 352 char *owner_copy = strdup(owner); 353 if (owner_copy) { 354 char *colon = strchr(owner_copy, ':'); 355 if (colon) { 356 *colon = '\0'; 357 colon++; 358 if (colon[0]) { 359 struct passwd *pw = getpwnam(owner_copy); 360 if (pw) { 361 uid = pw->pw_uid; 362 struct group *gr = getgrnam(colon); 363 if (gr) { 364 gid = gr->gr_gid; 365 } 366 } 367 } 368 } else { 369 struct passwd *pw = getpwnam(owner_copy); 370 if (pw) { 371 uid = pw->pw_uid; 372 gid = pw->pw_gid; 373 } 374 } 375 free(owner_copy); 376 } 377 if (uid != (uid_t)-1 || gid != (gid_t)-1) { 378 if (fchown(fd, uid, gid) < 0) { 379 log_error("unix_listen: fchown failed: %s", strerror(errno)); 380 close(fd); 381 unlink(path_copy); 382 free(path_copy); 383 free(fds); 384 return NULL; 385 } 386 } 387 } 388 389 if (sock_type == SOCK_STREAM) { 390 if (listen(fd, 8) < 0) { 391 log_error("unix_listen: listen failed: %s", strerror(errno)); 392 close(fd); 393 unlink(path_copy); 394 free(path_copy); 395 free(fds); 396 return NULL; 397 } 398 } 399 400 set_socket_nonblocking(fd, 1); 401 402 fds[++fds[0]] = fd; 403 404 free(path_copy); 405 return fds; 406 } 407 408 int *merge_fd_arrays(int **arrays, int count) { 409 if (!arrays || count <= 0) { 410 return NULL; 411 } 412 413 int total_count = 0; 414 for (int i = 0; i < count; i++) { 415 if (arrays[i] && arrays[i][0] > 0) { 416 total_count += arrays[i][0]; 417 } 418 } 419 420 if (total_count == 0) { 421 for (int i = 0; i < count; i++) { 422 free(arrays[i]); 423 } 424 return NULL; 425 } 426 427 int *merged = malloc(sizeof(int) * (total_count + 1)); 428 if (!merged) { 429 for (int i = 0; i < count; i++) { 430 free(arrays[i]); 431 } 432 return NULL; 433 } 434 435 merged[0] = 0; 436 int idx = 1; 437 438 for (int i = 0; i < count; i++) { 439 if (arrays[i] && arrays[i][0] > 0) { 440 for (int j = 1; j <= arrays[i][0]; j++) { 441 merged[idx++] = arrays[i][j]; 442 } 443 merged[0] += arrays[i][0]; 444 } 445 free(arrays[i]); 446 } 447 448 return merged; 449 } 450 451 void sockaddr_to_string(const struct sockaddr *addr, char *buf, size_t buf_size) { 452 if (!addr || !buf || buf_size == 0) return; 453 454 if (addr->sa_family == AF_INET) { 455 struct sockaddr_in *sin = (struct sockaddr_in *)addr; 456 inet_ntop(AF_INET, &sin->sin_addr, buf, buf_size); 457 size_t len = strlen(buf); 458 if (buf_size - len > 6) { 459 snprintf(buf + len, buf_size - len, ":%d", ntohs(sin->sin_port)); 460 } 461 } else if (addr->sa_family == AF_INET6) { 462 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; 463 buf[0] = '['; 464 inet_ntop(AF_INET6, &sin6->sin6_addr, buf + 1, (socklen_t)(buf_size - 1)); 465 size_t len = strlen(buf); 466 if (buf_size - len > 6) { 467 snprintf(buf + len, buf_size - len, "]:%d", ntohs(sin6->sin6_port)); 468 } 469 } else { 470 buf[0] = '\0'; 471 } 472 } 473 474 int string_to_sockaddr(const char *str, struct sockaddr_storage *addr) { 475 if (!str || !addr) return -1; 476 memset(addr, 0, sizeof(*addr)); 477 478 const char *port_str = strrchr(str, ':'); 479 if (!port_str) return -1; 480 481 char host[256]; 482 size_t host_len = (size_t)(port_str - str); 483 if (host_len >= sizeof(host)) return -1; 484 memcpy(host, str, host_len); 485 host[host_len] = '\0'; 486 487 int port = atoi(port_str + 1); 488 if (port <= 0 || port > 65535) return -1; 489 490 if (host[0] == '[' && host[host_len - 1] == ']') { 491 host[host_len - 1] = '\0'; 492 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; 493 sin6->sin6_family = AF_INET6; 494 sin6->sin6_port = htons((uint16_t)port); 495 if (inet_pton(AF_INET6, host + 1, &sin6->sin6_addr) != 1) return -1; 496 } else { 497 struct sockaddr_in *sin = (struct sockaddr_in *)addr; 498 sin->sin_family = AF_INET; 499 sin->sin_port = htons((uint16_t)port); 500 if (inet_pton(AF_INET, host, &sin->sin_addr) != 1) return -1; 501 } 502 503 return 0; 504 } 505 506 int sockaddr_equal(const struct sockaddr *a, const struct sockaddr *b) { 507 if (!a || !b || a->sa_family != b->sa_family) return 0; 508 509 if (a->sa_family == AF_INET) { 510 struct sockaddr_in *sin_a = (struct sockaddr_in *)a; 511 struct sockaddr_in *sin_b = (struct sockaddr_in *)b; 512 return sin_a->sin_addr.s_addr == sin_b->sin_addr.s_addr && sin_a->sin_port == sin_b->sin_port; 513 } else if (a->sa_family == AF_INET6) { 514 struct sockaddr_in6 *sin6_a = (struct sockaddr_in6 *)a; 515 struct sockaddr_in6 *sin6_b = (struct sockaddr_in6 *)b; 516 return memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr, sizeof(sin6_a->sin6_addr)) == 0 && 517 sin6_a->sin6_port == sin6_b->sin6_port; 518 } 519 return 0; 520 }