/* Copyright (c) 2004, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have either included with the program or referenced in the documentation. Without limiting anything contained in the foregoing, this file, which is part of C Driver for MySQL (Connector/C), is also subject to the Universal FOSS Exception, version 1.0, a copy of which can be found at http://oss.oracle.com/licenses/universal-foss-exception. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file mysys/my_gethwaddr.cc Get hardware address for an interface. If there are many available, any non-zero one can be used. */ #include "my_config.h" #include #include #ifdef SYS_SOCKET_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "my_inttypes.h" #include "my_sys.h" // IWYU pragma: keep #if defined(_WIN32) #include "mysys/mysys_priv.h" #endif #include "mysql/service_mysql_alloc.h" #ifndef MAIN #ifdef __FreeBSD__ #include #include #include #include #include bool my_gethwaddr(uchar *to) { size_t len; char *buf, *next, *end; struct if_msghdr *ifm; struct sockaddr_dl *sdl; int res = 1, mib[6] = {CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0}; char zero_array[ETHER_ADDR_LEN] = {0}; if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) goto err; if (!(buf = static_cast(alloca(len)))) goto err; if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) goto err; end = buf + len; for (next = buf; res && next < end; next += ifm->ifm_msglen) { ifm = (struct if_msghdr *)next; if (ifm->ifm_type == RTM_IFINFO) { sdl = (struct sockaddr_dl *)(ifm + 1); memcpy(to, LLADDR(sdl), ETHER_ADDR_LEN); res = memcmp(to, zero_array, ETHER_ADDR_LEN) ? 0 : 1; } } err: return res; } #elif defined(__linux__) #include #include #include #define MAX_IFS 64 bool my_gethwaddr(uchar *to) { int fd = -1; int res = 1; struct ifreq ifr; struct ifreq ifs[MAX_IFS]; struct ifreq *ifri = nullptr; struct ifreq *ifend = nullptr; char zero_array[ETHER_ADDR_LEN] = {0}; struct ifconf ifc; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return true; /* Retrieve interfaces */ ifc.ifc_len = sizeof(ifs); ifc.ifc_req = ifs; if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) { close(fd); return true; } /* Initialize out parameter */ memcpy(to, zero_array, ETHER_ADDR_LEN); /* Calculate first address after array */ ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq)); /* Loop over all interfaces */ for (ifri = ifc.ifc_req; ifri < ifend; ifri++) { if (ifri->ifr_addr.sa_family == AF_INET) { /* Reset struct, copy interface name */ memset(&ifr, 0, sizeof(ifr)); memcpy(ifr.ifr_name, ifri->ifr_name, sizeof(ifr.ifr_name)); /* Get HW address, break if not 0 */ if (ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0) { memcpy(to, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN); if (memcmp(to, zero_array, ETHER_ADDR_LEN)) { res = 0; break; } } } } close(fd); return res; } #elif defined(_WIN32) /* Workaround for BUG#32082 (Definition of VOID in my_global.h conflicts with windows headers) */ #ifdef VOID #undef VOID #define VOID void #endif #include /* The following typedef is for dynamically loading iphlpapi.dll / GetAdaptersAddresses. Dynamic loading is used because GetAdaptersAddresses is not available on Windows 2000 which MySQL still supports. Static linking would cause an unresolved export. */ typedef DWORD(WINAPI *pfnGetAdaptersAddresses)( IN ULONG Family, IN DWORD Flags, IN PVOID Reserved, OUT PIP_ADAPTER_ADDRESSES pAdapterAddresses, IN OUT PULONG pOutBufLen); /* my_gethwaddr - Windows version @brief Retrieve MAC address from network hardware @param[out] to MAC address exactly six bytes @return Operation status @retval 0 OK @retval <>0 FAILED */ bool my_gethwaddr(uchar *to) { PIP_ADAPTER_ADDRESSES pAdapterAddresses; PIP_ADAPTER_ADDRESSES pCurrAddresses; IP_ADAPTER_ADDRESSES adapterAddresses; ULONG address_len; bool return_val = 1; static pfnGetAdaptersAddresses fnGetAdaptersAddresses = (pfnGetAdaptersAddresses)-1; if (fnGetAdaptersAddresses == (pfnGetAdaptersAddresses)-1) { /* Get the function from the DLL */ fnGetAdaptersAddresses = (pfnGetAdaptersAddresses)GetProcAddress( LoadLibrary("iphlpapi.dll"), "GetAdaptersAddresses"); } if (!fnGetAdaptersAddresses) return 1; /* failed to get function */ address_len = sizeof(IP_ADAPTER_ADDRESSES); /* Get the required size for the address data. */ if (fnGetAdaptersAddresses(AF_UNSPEC, 0, nullptr, &adapterAddresses, &address_len) == ERROR_BUFFER_OVERFLOW) { pAdapterAddresses = static_cast( my_malloc(key_memory_win_IP_ADAPTER_ADDRESSES, address_len, 0)); if (!pAdapterAddresses) return 1; /* error, alloc failed */ } else pAdapterAddresses = &adapterAddresses; /* one is enough don't alloc */ /* Get the hardware info. */ if (fnGetAdaptersAddresses(AF_UNSPEC, 0, nullptr, pAdapterAddresses, &address_len) == NO_ERROR) { pCurrAddresses = pAdapterAddresses; while (pCurrAddresses) { /* Look for ethernet cards. */ if (pCurrAddresses->IfType == IF_TYPE_ETHERNET_CSMACD) { /* check for a good address */ if (pCurrAddresses->PhysicalAddressLength < 6) continue; /* bad address */ /* save 6 bytes of the address in the 'to' parameter */ memcpy(to, pCurrAddresses->PhysicalAddress, 6); /* Network card found, we're done. */ return_val = 0; break; } pCurrAddresses = pCurrAddresses->Next; } } /* Clean up memory allocation. */ if (pAdapterAddresses != &adapterAddresses) my_free(pAdapterAddresses); return return_val; } #else /* __FreeBSD__ || __linux__ || _WIN32 */ /* just fail */ bool my_gethwaddr(uchar *to [[maybe_unused]]) { return 1; } #endif #else /* MAIN */ int main(int argc [[maybe_unused]], char **argv) { uchar mac[6]; uint i; MY_INIT(argv[0]); if (my_gethwaddr(mac)) { printf("my_gethwaddr failed with errno %d\n", errno); exit(1); } for (i = 0; i < sizeof(mac); i++) { if (i) printf(":"); printf("%02x", mac[i]); } printf("\n"); return 0; } #endif