#pragma once #include #include #include #include #include #include #include #define CC_ADDRLEN (INET6_ADDRSTRLEN + 8) // [...:12345] static int sockaddr2str(const struct sockaddr *sa, char *s, const size_t maxlen) { if (sa->sa_family == AF_INET) { const struct sockaddr_in *sin = (struct sockaddr_in *)sa; inet_ntop(AF_INET, &sin->sin_addr, s, maxlen); const size_t len = strnlen(s, maxlen); return len + snprintf(s + len, maxlen - len, ":%u", ntohs(sin->sin_port)); } if (sa->sa_family == AF_INET6) { const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; s[0] = '['; inet_ntop(AF_INET6, &sin6->sin6_addr, s + 1, maxlen - 1); const size_t len = strnlen(s, maxlen); return len + snprintf(s + len, maxlen - len, "]:%u", ntohs(sin6->sin6_port)); } strncpy(s, "unknown", maxlen); return 7; } #define MAX_MESSAGE_LENGTH 1024 #define streq(a, b) (strcmp((a), (b)) == 0) #define exit_error(...) do { \ fprintf(stderr, __VA_ARGS__); \ exit(EXIT_FAILURE); \ } while (0) #define exit_perror(s) do { \ perror(s); \ exit(EXIT_FAILURE); \ } while (0) static int resolve_domain_to_ipv6(const char *hostname, struct in6_addr *server_addr) { struct addrinfo hints, *res; int found = 0; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(hostname, NULL, &hints, &res) != 0) return 0; for (struct addrinfo *p = res; p != NULL; p = p->ai_next) { if (p->ai_family == AF_INET6) { struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; memcpy(server_addr, &ipv6->sin6_addr, sizeof(struct in6_addr)); found = 1; break; } if (p->ai_family == AF_INET && !found) { struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; memset(server_addr, 0, sizeof(struct in6_addr)); server_addr->s6_addr[10] = 0xff; server_addr->s6_addr[11] = 0xff; memcpy(&server_addr->s6_addr[12], &(ipv4->sin_addr), 4); found = 1; } } freeaddrinfo(res); return found; } static int cc_client_parse_args_and_connect(int argc, char* argv[], struct sockaddr_in6* server_addr, int* server_fd, FILE** reader, FILE** writer) { struct in6_addr bind_addr = in6addr_any; int port = 5000; if (argc >= 2) { if (inet_pton(AF_INET6, argv[1], &bind_addr) != 1) { struct in_addr v4; if (inet_pton(AF_INET, argv[1], &v4) != 1) { if (!resolve_domain_to_ipv6(argv[1], &bind_addr)) { exit_error("invalid address or domain: %s\n", argv[1]); } } else { memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.s6_addr[10] = 0xff; bind_addr.s6_addr[11] = 0xff; memcpy(&bind_addr.s6_addr[12], &v4, 4); } } } if (argc >= 3) { port = atoi(argv[2]); if (port <= 0) exit_error("unknown argument: %s\n", argv[2]); } *server_fd = socket(AF_INET6, SOCK_STREAM, 0); if (*server_fd < 0) exit_perror("socket"); const int server_fd2 = dup(*server_fd); if (server_fd2 < 0) exit_perror("dup"); server_addr->sin6_family = AF_INET6; server_addr->sin6_addr = bind_addr; server_addr->sin6_port = htons(port); int opt = 0; if (setsockopt(*server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) { // i guess so bro } if (connect(*server_fd, (struct sockaddr *)server_addr, sizeof(*server_addr)) < 0) exit_perror("connect"); if ((*reader = fdopen(*server_fd, "r")) == NULL) exit_perror("fdopen reader"); if ((*writer = fdopen(server_fd2, "w")) == NULL) exit_perror("fdopen writer"); return 0; }