#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "common.h" #include "incoming.h" struct incoming { struct peer peer; char id[UUID_LEN]; const char *node; const char *service; incoming_connection_handler handler; void *passthrough; }; static void incoming_handler(struct peer *peer) { struct incoming *incoming = (struct incoming *) peer; struct sockaddr peer_addr, local_addr; socklen_t peer_addrlen = sizeof(peer_addr), local_addrlen = sizeof(local_addr); int fd = accept4(incoming->peer.fd, &peer_addr, &peer_addrlen, SOCK_NONBLOCK); if (fd == -1) { fprintf(stderr, "I %s: Failed to accept new connection on %s/%s: %s\n", incoming->id, incoming->node, incoming->service, strerror(errno)); return; } char peer_hbuf[NI_MAXHOST], local_hbuf[NI_MAXHOST], peer_sbuf[NI_MAXSERV], local_sbuf[NI_MAXSERV]; assert(getsockname(fd, &local_addr, &local_addrlen) == 0); assert(getnameinfo(&peer_addr, peer_addrlen, peer_hbuf, sizeof(peer_hbuf), peer_sbuf, sizeof(peer_sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0); assert(getnameinfo(&local_addr, local_addrlen, local_hbuf, sizeof(local_hbuf), local_sbuf, sizeof(local_sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0); fprintf(stderr, "I %s: New incoming connection on %s/%s (%s/%s) from %s/%s\n", incoming->id, incoming->node, incoming->service, local_hbuf, local_sbuf, peer_hbuf, peer_sbuf); incoming->handler(fd, incoming->passthrough); } void incoming_new(const char *node, const char *service, incoming_connection_handler handler, void *passthrough) { struct incoming *incoming = malloc(sizeof(*incoming)); incoming->peer.event_handler = incoming_handler; uuid_gen(incoming->id); incoming->node = strdup(node); incoming->service = strdup(service); incoming->handler = handler; incoming->passthrough = passthrough; fprintf(stderr, "I %s: Resolving %s/%s...\n", incoming->id, incoming->node, incoming->service); struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE | AI_V4MAPPED | AI_ADDRCONFIG, }; struct addrinfo *addrs; int gai_err = getaddrinfo(incoming->node, incoming->service, &hints, &addrs); if (gai_err) { fprintf(stderr, "I %s: Failed to resolve %s/%s: %s\n", incoming->id, incoming->node, incoming->service, gai_strerror(gai_err)); // TODO: add incoming_del, free strdup values free(incoming); return; } struct addrinfo *addr; for (addr = addrs; addr; addr = addr->ai_next) { char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; assert(getnameinfo(addr->ai_addr, addr->ai_addrlen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0); fprintf(stderr, "I %s: Listening on %s/%s...\n", incoming->id, hbuf, sbuf); incoming->peer.fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); assert(incoming->peer.fd >= 0); int optval = 1; setsockopt(incoming->peer.fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); if (bind(incoming->peer.fd, addr->ai_addr, addr->ai_addrlen) != 0) { fprintf(stderr, "I %s: Failed to bind to %s/%s: %s\n", incoming->id, hbuf, sbuf, strerror(errno)); close(incoming->peer.fd); continue; } assert(listen(incoming->peer.fd, 255) == 0); break; } freeaddrinfo(addrs); if (addr == NULL) { fprintf(stderr, "I %s: Failed to bind any addresses for %s/%s...\n", incoming->id, incoming->node, incoming->service); // TODO: use incoming_del free(incoming); return; } peer_epoll_add((struct peer *) incoming, EPOLLIN); }