/* * Copyright (C) 2014 Linux Test Project, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Further, this software is distributed without any warranty that it * is free of the rightful claim of any third person regarding * infringement or the like. Any license provided herein, whether * implied or otherwise, applies only to this software file. Patent * licenses, if any, provided herein do not apply to combinations of * this program with other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, Inc. */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include "test.h" unsigned short tst_get_unused_port(void (cleanup_fn)(void), unsigned short family, int type) { int sock; socklen_t slen; struct sockaddr_storage _addr; struct sockaddr *addr = (struct sockaddr *)&_addr; struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; switch (family) { case AF_INET: addr4->sin_family = AF_INET; addr4->sin_port = 0; addr4->sin_addr.s_addr = INADDR_ANY; slen = sizeof(*addr4); break; case AF_INET6: addr6->sin6_family = AF_INET6; addr6->sin6_port = 0; addr6->sin6_addr = in6addr_any; slen = sizeof(*addr6); break; default: tst_brkm(TBROK, cleanup_fn, "tst_get_unused_port unknown family"); return -1; } sock = socket(addr->sa_family, type, 0); if (sock < 0) { tst_brkm(TBROK | TERRNO, cleanup_fn, "socket failed"); return -1; } if (bind(sock, addr, slen) < 0) { tst_brkm(TBROK | TERRNO, cleanup_fn, "bind failed"); return -1; } if (getsockname(sock, addr, &slen) == -1) { tst_brkm(TBROK | TERRNO, cleanup_fn, "getsockname failed"); return -1; } if (close(sock) == -1) { tst_brkm(TBROK | TERRNO, cleanup_fn, "close failed"); return -1; } switch (family) { case AF_INET: return addr4->sin_port; case AF_INET6: return addr6->sin6_port; default: return -1; } }