Skip to content

Instantly share code, notes, and snippets.

@neoxic
Last active August 27, 2024 13:46
Show Gist options
  • Save neoxic/0d9314ec756d37ca4303bced49b94543 to your computer and use it in GitHub Desktop.
Save neoxic/0d9314ec756d37ca4303bced49b94543 to your computer and use it in GitHub Desktop.
SOCK_DGRAM: race between 'bind()' and 'connect()' leads to a connected socket receiving unfiltered datagrams
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#define check(expr) if ((expr) == -1) err(1, "%s", #expr);
int main(int argc, char *argv[]) {
struct sockaddr_storage sa;
struct addrinfo *ai;
socklen_t sl;
int res, i, j, fd;
char buf[64];
if (argc != 3) errx(1, "Invalid number of arguments");
if ((res = getaddrinfo(argv[1], argv[2], 0, &ai))) errx(1, "%s: %s", argv[0], gai_strerror(res));
memcpy(&sa, ai->ai_addr, sl = ai->ai_addrlen);
freeaddrinfo(ai);
for (i = 0; i < 20; ++i) {
check(fd = socket(sa.ss_family, SOCK_DGRAM, 0));
check(connect(fd, (struct sockaddr *)&sa, sl));
for (j = 0; j < 20; ++j) check(write(fd, buf, sprintf(buf, "%d-%d", i, j)));
check(close(fd));
}
return 0;
}
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <poll.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#define check(expr) if ((expr) == -1) err(1, "%s", #expr);
int main(int argc, char *argv[]) {
struct sockaddr_storage lsa, csa, csa_;
struct addrinfo *ai;
struct pollfd fds[1000];
socklen_t lsl, csl, csl_;
int res, fd, i, nfd = 0, on = 1;
if (argc != 3) errx(1, "Invalid number of arguments");
if ((res = getaddrinfo(argv[1], argv[2], 0, &ai))) errx(1, "%s: %s", argv[0], gai_strerror(res));
memcpy(&lsa, ai->ai_addr, lsl = ai->ai_addrlen);
freeaddrinfo(ai);
check(fd = socket(lsa.ss_family, SOCK_DGRAM, 0));
check(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK));
#ifdef __linux__
check(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on));
#else
check(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof on));
#endif
check(bind(fd, (struct sockaddr *)&lsa, lsl));
fds[nfd].fd = fd;
fds[nfd].events = POLLIN;
++nfd;
for (;;) {
check(poll(fds, nfd, -1));
for (i = 0; i < nfd; ++i) {
if (!(fds[i].revents & POLLIN)) continue;
fd = fds[i].fd;
if (i == nfd - 1) { /* New connection */
csl = sizeof csa;
check(res = recvfrom(fd, 0, 0, 0, (struct sockaddr *)&csa, &csl));
/* Create new socket */
check(fd = socket(csa.ss_family, SOCK_DGRAM, 0));
check(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK));
#ifdef __linux__
check(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on));
#else
check(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof on));
#endif
check(getsockname(fds[i].fd, (struct sockaddr *)&lsa, &lsl));
check(bind(fd, (struct sockaddr *)&lsa, lsl));
if (connect(fd, (struct sockaddr *)&csa, csl)) { /* Duplicate connection */
close(fd);
continue;
}
fds[nfd].fd = fds[i].fd;
fds[nfd].events = POLLIN;
fds[i].fd = fd;
++nfd;
break;
}
/* Check peer's address */
csl = sizeof csa;
check(res = recvfrom(fd, 0, 0, 0, (struct sockaddr *)&csa, &csl));
check(getpeername(fd, (struct sockaddr *)&csa_, &csl_));
if (csl != csl_ || memcmp(&csa, &csa_, csl_)) printf("#%d: address mismatch!\n", fd);
}
}
return 0;
}
@neoxic
Copy link
Author

neoxic commented Dec 28, 2019

Compile:

$ cc -o udp_server udp_server.c -Wall -Wextra
$ cc -o udp_client udp_client.c -Wall -Wextra

Run:

$ ./udp_server :: 5555
#27: address mismatch!
#46: address mismatch!
#87: address mismatch!
#206: address mismatch!
#228: address mismatch!
udp_server: fd = socket(csa.ss_family, SOCK_DGRAM, 0): Too many open files

while firing packets with:

$ ./udp_client ::1 5555

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment