diff --git a/src/interface.c b/src/interface.c index 3928a97..7057234 100644 --- a/src/interface.c +++ b/src/interface.c @@ -18,17 +18,25 @@ #include #include #include +#include #include "interface.h" #include "common.h" #include "config.h" +#define INTERFACE_CACHE_TTL 5 + +static CRITICAL_SECTION interface_cache_cs; + +static ipx_interface_t *interface_cache = NULL; +static time_t interface_cache_ctime = 0; + /* Fetch a list of network interfaces available on the system. * * Returns a linked list of IP_ADAPTER_INFO structures, all allocated within a * single memory block beginning at the first node. */ -IP_ADAPTER_INFO *get_sys_interfaces(void) +IP_ADAPTER_INFO *load_sys_interfaces(void) { IP_ADAPTER_INFO *ifroot = NULL, *ifptr; ULONG bufsize = sizeof(IP_ADAPTER_INFO) * 8; @@ -68,12 +76,10 @@ IP_ADAPTER_INFO *get_sys_interfaces(void) return ifroot; } -/* Get virtual IPX interfaces - * Select a single interface by setting ifnum >= 0 -*/ -ipx_interface_t *get_interfaces(int ifnum) +/* Load a list of virtual IPX interfaces. */ +ipx_interface_t *load_ipx_interfaces(void) { - IP_ADAPTER_INFO *ifroot = get_sys_interfaces(), *ifptr; + IP_ADAPTER_INFO *ifroot = load_sys_interfaces(), *ifptr; addr48_t primary = get_primary_iface(); @@ -98,7 +104,7 @@ ipx_interface_t *get_interfaces(int ifnum) { log_printf(LOG_ERROR, "Couldn't allocate ipx_interface!"); - free_ipx_interfaces(&nics); + free_ipx_interface_list(&nics); return NULL; } @@ -126,7 +132,7 @@ ipx_interface_t *get_interfaces(int ifnum) log_printf(LOG_ERROR, "Couldn't allocate ipx_interface_ip!"); free_ipx_interface(iface); - free_ipx_interfaces(&nics); + free_ipx_interface_list(&nics); continue; } @@ -169,28 +175,46 @@ ipx_interface_t *get_interfaces(int ifnum) free(ifroot); - /* Delete every entry in the NIC list except the requested one. - * - * This is done here rather than when building the list as the primary - * interface may change the indexes if it isn't the first. - */ - - if(ifnum >= 0) + return nics; +} + +/* Deep copy an ipx_interface structure. + * Returns NULL on malloc failure. +*/ +ipx_interface_t *copy_ipx_interface(const ipx_interface_t *src) +{ + ipx_interface_t *dest = malloc(sizeof(ipx_interface_t)); + if(!dest) { - int this_ifnum = 0; - ipx_interface_t *iface, *tmp; - - DL_FOREACH_SAFE(nics, iface, tmp) - { - if(this_ifnum++ != ifnum) - { - DL_DELETE(nics, iface); - free_ipx_interface(iface); - } - } + log_printf(LOG_ERROR, "Cannot allocate ipx_interface!"); + return NULL; } - return nics; + *dest = *src; + + dest->ipaddr = NULL; + dest->prev = NULL; + dest->next = NULL; + + ipx_interface_ip_t *ip; + + DL_FOREACH(src->ipaddr, ip) + { + ipx_interface_ip_t *new_ip = malloc(sizeof(ipx_interface_ip_t)); + if(!new_ip) + { + log_printf(LOG_ERROR, "Cannot allocate ipx_interface_ip!"); + + free_ipx_interface(dest); + return NULL; + } + + *new_ip = *ip; + + DL_APPEND(dest->ipaddr, new_ip); + } + + return dest; } /* Free an ipx_interface structure and any memory allocated within. */ @@ -207,8 +231,32 @@ void free_ipx_interface(ipx_interface_t *iface) free(iface); } +/* Deep copy an entire list of ipx_interface structures. + * Returns NULL on malloc failure. +*/ +ipx_interface_t *copy_ipx_interface_list(const ipx_interface_t *src) +{ + ipx_interface_t *dest = NULL; + + const ipx_interface_t *s; + + DL_FOREACH(src, s) + { + ipx_interface_t *d = copy_ipx_interface(s); + if(!d) + { + free_ipx_interface_list(&dest); + return NULL; + } + + DL_APPEND(dest, d); + } + + return dest; +} + /* Free a list of ipx_interface structures */ -void free_ipx_interfaces(ipx_interface_t **list) +void free_ipx_interface_list(ipx_interface_t **list) { ipx_interface_t *iface, *tmp; @@ -218,3 +266,125 @@ void free_ipx_interfaces(ipx_interface_t **list) free_ipx_interface(iface); } } + +/* Initialise the IPX interface cache. */ +void ipx_interfaces_init(void) +{ + interface_cache = NULL; + interface_cache_ctime = 0; + + if(!InitializeCriticalSectionAndSpinCount(&interface_cache_cs, 0x80000000)) + { + log_printf(LOG_ERROR, "Failed to initialise critical section: %s", w32_error(GetLastError())); + abort(); + } +} + +/* Release any resources used by the IPX interface cache. */ +void ipx_interfaces_cleanup(void) +{ + DeleteCriticalSection(&interface_cache_cs); + + free_ipx_interface_list(&interface_cache); +} + +/* Check the age of the IPX interface cache and reload it if necessary. + * Ensure you hold interface_cache_cs before calling. +*/ +static void renew_interface_cache(void) +{ + if(time(NULL) - interface_cache_ctime > INTERFACE_CACHE_TTL) + { + free_ipx_interface_list(&interface_cache); + + interface_cache = load_ipx_interfaces(); + interface_cache_ctime = time(NULL); + } +} + +/* Return a copy of the IPX interface cache. The cache will be reloaded before + * copying if too old. +*/ +ipx_interface_t *get_ipx_interfaces(void) +{ + EnterCriticalSection(&interface_cache_cs); + + renew_interface_cache(); + + ipx_interface_t *copy = copy_ipx_interface_list(interface_cache); + + LeaveCriticalSection(&interface_cache_cs); + + return copy; +} + +/* Search for an IPX interface by address. + * Returns NULL if the interface doesn't exist or malloc failure. +*/ +ipx_interface_t *ipx_interface_by_addr(addr32_t net, addr48_t node) +{ + EnterCriticalSection(&interface_cache_cs); + + renew_interface_cache(); + + ipx_interface_t *iface; + + DL_FOREACH(interface_cache, iface) + { + if(iface->ipx_net == net && iface->ipx_node == node) + { + iface = copy_ipx_interface(iface); + break; + } + } + + LeaveCriticalSection(&interface_cache_cs); + + return iface; +} + +/* Search for an IPX interface by index. + * Returns NULL if the interface doesn't exist or malloc failure. +*/ +ipx_interface_t *ipx_interface_by_index(int index) +{ + EnterCriticalSection(&interface_cache_cs); + + renew_interface_cache(); + + int iface_index = 0; + ipx_interface_t *iface; + + DL_FOREACH(interface_cache, iface) + { + if(iface_index++ == index) + { + iface = copy_ipx_interface(iface); + break; + } + } + + LeaveCriticalSection(&interface_cache_cs); + + return iface; +} + +/* Returns the number of IPX interfaces. */ +int ipx_interface_count(void) +{ + EnterCriticalSection(&interface_cache_cs); + + renew_interface_cache(); + + int count = 0; + ipx_interface_t *iface; + + DL_FOREACH(interface_cache, iface) + { + count++; + } + + LeaveCriticalSection(&interface_cache_cs); + + return count; +} diff --git a/src/interface.h b/src/interface.h index 4a07c16..00644f2 100644 --- a/src/interface.h +++ b/src/interface.h @@ -52,12 +52,22 @@ struct ipx_interface { ipx_interface_t *next; }; -IP_ADAPTER_INFO *get_sys_interfaces(void); - -ipx_interface_t *get_interfaces(int ifnum); +IP_ADAPTER_INFO *load_sys_interfaces(void); +ipx_interface_t *load_ipx_interfaces(void); +ipx_interface_t *copy_ipx_interface(const ipx_interface_t *src); void free_ipx_interface(ipx_interface_t *iface); -void free_ipx_interfaces(ipx_interface_t **list); + +ipx_interface_t *copy_ipx_interface_list(const ipx_interface_t *src); +void free_ipx_interface_list(ipx_interface_t **list); + +void ipx_interfaces_init(void); +void ipx_interfaces_cleanup(void); + +ipx_interface_t *get_ipx_interfaces(void); +ipx_interface_t *ipx_interface_by_addr(addr32_t net, addr48_t node); +ipx_interface_t *ipx_interface_by_index(int index); +int ipx_interface_count(void); #ifdef __cplusplus } diff --git a/src/ipxwrapper.c b/src/ipxwrapper.c index 014f17c..10d5e11 100644 --- a/src/ipxwrapper.c +++ b/src/ipxwrapper.c @@ -86,6 +86,8 @@ BOOL WINAPI DllMain(HINSTANCE me, DWORD why, LPVOID res) { addr_cache_init(); + ipx_interfaces_init(); + if(!rclient_init(&g_rclient)) { return FALSE; } @@ -140,6 +142,8 @@ BOOL WINAPI DllMain(HINSTANCE me, DWORD why, LPVOID res) { DeleteCriticalSection(&addrs_cs); DeleteCriticalSection(&sockets_cs); + ipx_interfaces_cleanup(); + addr_cache_cleanup(); unload_dlls(); @@ -192,7 +196,7 @@ BOOL ip_is_local(uint32_t ipaddr) { if(local_updated + main_config.iface_ttl < time(NULL)) { /* TODO: Use all local IPs rather than just the ones with associated IPX addresses? */ - struct ipx_interface *ifaces = get_interfaces(-1); + struct ipx_interface *ifaces = get_ipx_interfaces(); struct ipx_interface *i = ifaces; while(i) { @@ -215,7 +219,7 @@ BOOL ip_is_local(uint32_t ipaddr) { i = i->next; } - free_ipx_interfaces(&ifaces); + free_ipx_interface_list(&ifaces); local_updated = time(NULL); } diff --git a/src/router.c b/src/router.c index 5a6ec52..d7c1fe0 100644 --- a/src/router.c +++ b/src/router.c @@ -336,7 +336,7 @@ static int router_bind(struct router_vars *router, SOCKET control, SOCKET sock, * PLEASE email me with corrections! */ - struct ipx_interface *ifaces = get_interfaces(-1), *iface; + struct ipx_interface *ifaces = get_ipx_interfaces(), *iface; addr32_t sa_netnum = addr32_in(addr->sa_netnum); addr48_t sa_nodenum = addr48_in(addr->sa_nodenum); @@ -356,7 +356,7 @@ static int router_bind(struct router_vars *router, SOCKET control, SOCKET sock, if(!iface) { log_printf(LOG_ERROR, "bind failed: no such address"); - free_ipx_interfaces(&ifaces); + free_ipx_interface_list(&ifaces); WSASetLastError(WSAEADDRNOTAVAIL); return -1; @@ -372,7 +372,7 @@ static int router_bind(struct router_vars *router, SOCKET control, SOCKET sock, uint32_t iface_ipaddr = iface->ipaddr->ipaddr; uint32_t iface_netmask = iface->ipaddr->netmask; - free_ipx_interfaces(&ifaces); + free_ipx_interface_list(&ifaces); EnterCriticalSection(&(router->crit_sec)); diff --git a/src/winsock.c b/src/winsock.c index 479a85c..cd89d48 100644 --- a/src/winsock.c +++ b/src/winsock.c @@ -524,7 +524,7 @@ int WSAAPI getsockopt(SOCKET fd, int level, int optname, char FAR *optval, int F IPX_ADDRESS_DATA *ipxdata = (IPX_ADDRESS_DATA*)optval; - struct ipx_interface *nic = get_interfaces(ipxdata->adapternum); + struct ipx_interface *nic = ipx_interface_by_index(ipxdata->adapternum); if(!nic) { RETURN_WSA(ERROR_NO_DATA, -1); @@ -552,16 +552,7 @@ int WSAAPI getsockopt(SOCKET fd, int level, int optname, char FAR *optval, int F if(optname == IPX_MAX_ADAPTER_NUM) { CHECK_OPTLEN(sizeof(int)); - *intval = 0; - - struct ipx_interface *ifaces = get_interfaces(-1), *nic; - - for(nic = ifaces; nic;) { - (*intval)++; - nic = nic->next; - } - - free_ipx_interfaces(&ifaces); + *intval = ipx_interface_count(); RETURN(0); }