/* * Common hostapd/wpa_supplicant ctrl iface code. * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> * Copyright (c) 2015, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include <netdb.h> #include <sys/un.h> #include "utils/common.h" #include "ctrl_iface_common.h" static int sockaddr_compare(struct sockaddr_storage *a, socklen_t a_len, struct sockaddr_storage *b, socklen_t b_len) { if (a->ss_family != b->ss_family) return 1; switch (a->ss_family) { #ifdef CONFIG_CTRL_IFACE_UDP case AF_INET: { struct sockaddr_in *in_a, *in_b; in_a = (struct sockaddr_in *) a; in_b = (struct sockaddr_in *) b; if (in_a->sin_port != in_b->sin_port) return 1; if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr) return 1; break; } case AF_INET6: { struct sockaddr_in6 *in6_a, *in6_b; in6_a = (struct sockaddr_in6 *) a; in6_b = (struct sockaddr_in6 *) b; if (in6_a->sin6_port != in6_b->sin6_port) return 1; if (os_memcmp(&in6_a->sin6_addr, &in6_b->sin6_addr, sizeof(in6_a->sin6_addr)) != 0) return 1; break; } #endif /* CONFIG_CTRL_IFACE_UDP */ #ifdef CONFIG_CTRL_IFACE_UNIX case AF_UNIX: { struct sockaddr_un *u_a, *u_b; u_a = (struct sockaddr_un *) a; u_b = (struct sockaddr_un *) b; if (a_len != b_len || os_memcmp(u_a->sun_path, u_b->sun_path, a_len - offsetof(struct sockaddr_un, sun_path)) != 0) return 1; break; } #endif /* CONFIG_CTRL_IFACE_UNIX */ default: return 1; } return 0; } void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, socklen_t socklen) { switch (sock->ss_family) { #ifdef CONFIG_CTRL_IFACE_UDP case AF_INET: case AF_INET6: { char host[NI_MAXHOST] = { 0 }; char service[NI_MAXSERV] = { 0 }; getnameinfo((struct sockaddr *) sock, socklen, host, sizeof(host), service, sizeof(service), NI_NUMERICHOST); wpa_printf(level, "%s %s:%s", msg, host, service); break; } #endif /* CONFIG_CTRL_IFACE_UDP */ #ifdef CONFIG_CTRL_IFACE_UNIX case AF_UNIX: { char addr_txt[200]; printf_encode(addr_txt, sizeof(addr_txt), (u8 *) ((struct sockaddr_un *) sock)->sun_path, socklen - offsetof(struct sockaddr_un, sun_path)); wpa_printf(level, "%s %s", msg, addr_txt); break; } #endif /* CONFIG_CTRL_IFACE_UNIX */ default: wpa_printf(level, "%s", msg); break; } } int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, socklen_t fromlen) { struct wpa_ctrl_dst *dst; dst = os_zalloc(sizeof(*dst)); if (dst == NULL) return -1; os_memcpy(&dst->addr, from, fromlen); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; dl_list_add(ctrl_dst, &dst->list); sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen); return 0; } int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, socklen_t fromlen) { struct wpa_ctrl_dst *dst; dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { if (!sockaddr_compare(from, fromlen, &dst->addr, dst->addrlen)) { sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor detached", from, fromlen); dl_list_del(&dst->list); os_free(dst); return 0; } } return -1; } int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, socklen_t fromlen, const char *level) { struct wpa_ctrl_dst *dst; wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { if (!sockaddr_compare(from, fromlen, &dst->addr, dst->addrlen)) { sockaddr_print(MSG_DEBUG, "CTRL_IFACE changed monitor level", from, fromlen); dst->debug_level = atoi(level); return 0; } } return -1; }