/* dbus_mgr.c * * named module to provide dynamic forwarding zones in * response to D-BUS dhcp events or commands. * * Copyright(C) Jason Vas Dias, Red Hat Inc., 2005 * Modified by Adam Tkac, Red Hat Inc., 2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation at * http://www.fsf.org/licensing/licenses/gpl.txt * and included in this software distribution as the "LICENSE" file. * * 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 for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef void (*__free_fn_t) (void *__nodep); extern void tdestroy (void *__root, __free_fn_t __freefct); extern void free(void*); #ifdef ISC_USE_INTERNAL_MALLOC # if ISC_USE_INTERNAL_MALLOC # error dbus_mgr cannot be used if ISC_USE_INTERNAL_MALLOC==1 # endif #endif #define DBUSMGR_DESTINATION "com.redhat.named" #define DBUSMGR_OBJECT_PATH "/com/redhat/named" #define DBUSMGR_INTERFACE "com.redhat.named" #define DBUSMGR_MAGIC ISC_MAGIC('D', 'B', 'U', 'S') struct ns_dbus_mgr { unsigned int magic; isc_mem_t * mctx; /* Memory context. */ isc_taskmgr_t * taskmgr; /* Task manager. */ isc_socketmgr_t * socketmgr; /* Socket manager. */ isc_timermgr_t * timermgr; /* Timer manager. */ isc_task_t * task; /* task */ isc_timer_t * timer; /* dbus_init retry */ void * sockets; /* dbus fd tree */ void * dhc_if; /* dhcp interface tree */ void * ifwdt; /* initial forwarder tree */ char * dhcdbd_name; /* dhcdbd destination */ DBUS_SVC dbus; /* dbus handle */ }; typedef struct dbus_mgr_sock_s { int fd; struct ns_dbus_mgr *mgr; isc_socket_t *sock; isc_socketevent_t *ser; isc_socketevent_t *sew; isc_socketevent_t *sel; } DBusMgrSocket; typedef enum dhc_state_e { DHC_NBI, /* no broadcast interfaces found */ DHC_PREINIT, /* configuration started */ DHC_BOUND, /* lease obtained */ DHC_RENEW, /* lease renewed */ DHC_REBOOT, /* have valid lease, but now obtained a different one */ DHC_REBIND, /* new, different lease */ DHC_STOP, /* remove old lease */ DHC_MEDIUM, /* media selection begun */ DHC_TIMEOUT, /* timed out contacting DHCP server */ DHC_FAIL, /* all attempts to contact server timed out, sleeping */ DHC_EXPIRE, /* lease has expired, renewing */ DHC_RELEASE, /* releasing lease */ DHC_START, /* sent when dhclient started OK */ DHC_ABEND, /* dhclient exited abnormally */ DHC_END, /* dhclient exited normally */ DHC_END_OPTIONS, /* last option in subscription sent */ DHC_INVALID=255 } DHC_State; typedef ISC_LIST(dns_name_t) DNSNameList; typedef ISC_LIST(isc_sockaddr_t) SockAddrList; typedef struct dbm_fwdr_s { dns_fwdpolicy_t fwdpolicy; dns_name_t dn; SockAddrList sa; ISC_LINK( struct dbm_fwdr_s ) link; } DBusMgrInitialFwdr; typedef struct dhc_if_s { char *if_name; DHC_State dhc_state; DHC_State previous_state; struct in_addr ip; struct in_addr subnet_mask; DNSNameList dn; SockAddrList dns; } DHC_IF; static void dbus_mgr_watch_handler( int fd, dbus_svc_WatchFlags flags, void *mgrp ); static dbus_svc_HandlerResult dbus_mgr_message_handler ( DBusMsgHandlerArgs ); static void dbus_mgr_close_socket( const void *p, const VISIT which, const int level); static void dbus_mgr_destroy_socket( void *p ); static void dbus_mgr_free_dhc( void *p ); static void dbus_mgr_watches_selected(isc_task_t *t, isc_event_t *ev); static isc_result_t dbus_mgr_init_dbus(ns_dbus_mgr_t *); static isc_result_t dbus_mgr_record_initial_fwdtable(ns_dbus_mgr_t *); static dns_fwdtable_t *dbus_mgr_get_fwdtable(void); static void dbus_mgr_free_initial_fwdtable(ns_dbus_mgr_t *); static uint8_t dbus_mgr_subscribe_to_dhcdbd( ns_dbus_mgr_t * ); static void dbus_mgr_dbus_shutdown_handler ( ns_dbus_mgr_t * ); static int dbus_mgr_log_err( const char *fmt, ...) { va_list va; va_start(va, fmt); isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_DBUS, NS_LOGMODULE_DBUS, ISC_LOG_NOTICE, fmt, va ); va_end(va); return 0; } static int dbus_mgr_log_dbg( const char *fmt, ...) { va_list va; va_start(va, fmt); isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_DBUS, NS_LOGMODULE_DBUS, ISC_LOG_DEBUG(80), fmt, va ); va_end(va); return 0; } static int dbus_mgr_log_info( const char *fmt, ...) { va_list va; va_start(va, fmt); isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_DBUS, NS_LOGMODULE_DBUS, ISC_LOG_DEBUG(1), fmt, va ); va_end(va); return 0; } isc_result_t dbus_mgr_create ( isc_mem_t *mctx, isc_taskmgr_t *taskmgr, isc_socketmgr_t *socketmgr, isc_timermgr_t *timermgr, ns_dbus_mgr_t **dbus_mgr ) { isc_result_t result; ns_dbus_mgr_t *mgr; *dbus_mgr = 0L; mgr = isc_mem_get(mctx, sizeof(*mgr)); if (mgr == NULL) return (ISC_R_NOMEMORY); mgr->magic = DBUSMGR_MAGIC; mgr->mctx = mctx; mgr->taskmgr = taskmgr; mgr->socketmgr = socketmgr; mgr->timermgr = timermgr; mgr->task = 0L; mgr->sockets = 0L; mgr->timer = 0L; mgr->dhc_if = 0L; mgr->ifwdt = 0L; mgr->dhcdbd_name = 0L; if( (result = isc_task_create( taskmgr, 100, &(mgr->task))) != ISC_R_SUCCESS ) goto cleanup_mgr; isc_task_setname( mgr->task, "dbusmgr", mgr ); mgr->dbus = 0L; if( (result = dbus_mgr_record_initial_fwdtable( mgr )) != ISC_R_SUCCESS ) goto cleanup_mgr; if( (result = dbus_mgr_init_dbus( mgr )) != ISC_R_SUCCESS ) goto cleanup_mgr; *dbus_mgr = mgr; return ISC_R_SUCCESS; cleanup_mgr: if ( dbus_mgr_get_fwdtable() != NULL) dbus_mgr_free_initial_fwdtable (mgr); if( mgr->task != 0L ) isc_task_detach(&(mgr->task)); isc_mem_put(mctx, mgr, sizeof(*mgr)); return (result); } static isc_result_t dbus_mgr_init_dbus(ns_dbus_mgr_t * mgr) { char destination[]=DBUSMGR_DESTINATION; isc_result_t result; if( mgr->sockets != 0L ) { isc_task_purgerange(mgr->task, 0L, ISC_SOCKEVENT_READ_READY, ISC_SOCKEVENT_SELECTED, 0L); twalk(mgr->sockets, dbus_mgr_close_socket); tdestroy(mgr->sockets, dbus_mgr_destroy_socket); mgr->sockets = 0L; } if( mgr->dbus != 0L ) { dbus_svc_shutdown(mgr->dbus); mgr->dbus = 0L; } result = dbus_svc_init(DBUS_PRIVATE_SYSTEM, destination, &mgr->dbus, dbus_mgr_watch_handler, 0L, 0L, mgr); if(result != ISC_R_SUCCESS) goto cleanup; if( mgr->dbus == 0L ) { if( mgr->timer == 0L) { isc_task_purgerange(mgr->task, 0L, ISC_SOCKEVENT_READ_READY, ISC_SOCKEVENT_SELECTED, 0L); if( mgr->sockets != 0L ) { twalk(mgr->sockets, dbus_mgr_close_socket); tdestroy(mgr->sockets, dbus_mgr_destroy_socket); mgr->sockets = 0L; } dbus_mgr_dbus_shutdown_handler ( mgr ); return ISC_R_SUCCESS; } goto cleanup; } if( !dbus_svc_add_filter ( mgr->dbus, dbus_mgr_message_handler, mgr, 4, "type=signal,path=/org/freedesktop/DBus,member=NameOwnerChanged", "type=signal,path=/org/freedesktop/DBus/Local,member=Disconnected", "type=signal,interface=com.redhat.dhcp.subscribe.binary", "type=method_call,destination=com.redhat.named,path=/com/redhat/named" ) ) { dbus_mgr_log_err( "dbus_svc_add_filter failed" ); goto cleanup; } if( mgr->timer != 0L ) { isc_timer_reset(mgr->timer, isc_timertype_inactive, NULL, NULL, ISC_TRUE ); } if( !dbus_mgr_subscribe_to_dhcdbd( mgr ) ) dbus_mgr_log_err("D-BUS dhcdbd subscription disabled."); dbus_mgr_log_err("D-BUS service enabled."); return ISC_R_SUCCESS; cleanup: isc_task_purgerange(mgr->task, 0L, ISC_SOCKEVENT_READ_READY, ISC_SOCKEVENT_SELECTED, 0L); twalk(mgr->sockets, dbus_mgr_close_socket); tdestroy(mgr->sockets, dbus_mgr_destroy_socket); mgr->sockets = 0L; if( mgr->dbus ) { dbus_svc_shutdown(mgr->dbus); mgr->dbus = 0L; } return ISC_R_FAILURE; } static uint8_t dbus_mgr_subscribe_to_dhcdbd( ns_dbus_mgr_t *mgr ) { DBUS_SVC dbus = mgr->dbus; char subs[1024], path[1024], dhcdbd_destination[]="com.redhat.dhcp", *ddp[1]={ &(dhcdbd_destination[0]) }, *dhcdbd_name=0L; const char *options[] = { "reason", "ip-address", "subnet-mask", "domain-name", "domain-name-servers" }; dbus_svc_MessageHandle msg; int i, n_opts = 5; if( mgr->dhcdbd_name == 0L ) { msg = dbus_svc_call ( dbus, "org.freedesktop.DBus", "/org/freedesktop/DBus", "GetNameOwner", "org.freedesktop.DBus", TYPE_STRING, &ddp, TYPE_INVALID ); if( msg == 0L ) return 0; if( !dbus_svc_get_args(dbus, msg, TYPE_STRING, &(dhcdbd_name), TYPE_INVALID ) ) return 0; mgr->dhcdbd_name = isc_mem_get(mgr->mctx, strlen(dhcdbd_name) + 1); if( mgr->dhcdbd_name == 0L ) return 0; strcpy(mgr->dhcdbd_name, dhcdbd_name); } sprintf(path,"/com/redhat/dhcp/subscribe"); sprintf(subs,"com.redhat.dhcp.binary"); for(i = 0; i < n_opts; i++) { msg = dbus_svc_call ( dbus, "com.redhat.dhcp", path, "binary", subs, TYPE_STRING, &(options[i]), TYPE_INVALID ); if(msg == 0L) return 0; if ( dbus_svc_message_type( msg ) == ERROR ) return 0; } dbus_mgr_log_err("D-BUS dhcdbd subscription enabled."); return 1; } void dbus_mgr_shutdown ( ns_dbus_mgr_t *mgr ) { if( mgr->timer != 0L ) isc_timer_detach(&(mgr->timer)); if( mgr->dbus != 0L ) { isc_task_purgerange(mgr->task, 0L, ISC_SOCKEVENT_READ_READY, ISC_SOCKEVENT_SELECTED, 0L); if( mgr->sockets != 0L ) { twalk(mgr->sockets, dbus_mgr_close_socket); tdestroy(mgr->sockets, dbus_mgr_destroy_socket); mgr->sockets = 0L; } dbus_svc_shutdown(mgr->dbus); } if( mgr->dhc_if != 0L ) tdestroy(mgr->dhc_if, dbus_mgr_free_dhc); if( mgr->dhcdbd_name != 0L ) isc_mem_put(mgr->mctx, mgr->dhcdbd_name, strlen(mgr->dhcdbd_name) + 1); isc_task_detach(&(mgr->task)); dbus_mgr_free_initial_fwdtable(mgr); isc_mem_put(mgr->mctx, mgr, sizeof(ns_dbus_mgr_t)); } static void dbus_mgr_restart_dbus(isc_task_t *t, isc_event_t *ev) { ns_dbus_mgr_t *mgr = (ns_dbus_mgr_t*)(ev->ev_arg) ; t=t; isc_event_free(&ev); dbus_mgr_log_dbg("attempting to connect to D-BUS"); dbus_mgr_init_dbus( mgr ); } static void dbus_mgr_handle_dbus_shutdown_event(isc_task_t *t, isc_event_t *ev) { ns_dbus_mgr_t *mgr = ev->ev_arg; isc_time_t tick={10,0}; isc_interval_t tock={10,0}; DBUS_SVC dbus = mgr->dbus; t = t; mgr->dbus = 0L; isc_event_free(&ev); if ( dbus != 0L ) { isc_task_purgerange(mgr->task, 0L, ISC_SOCKEVENT_READ_READY, ISC_SOCKEVENT_SELECTED, 0L); if( mgr->sockets != 0L ) { twalk(mgr->sockets, dbus_mgr_close_socket); tdestroy(mgr->sockets, dbus_mgr_destroy_socket); mgr->sockets = 0L; } dbus_svc_shutdown(dbus); } dbus_mgr_log_err( "D-BUS service disabled." ); if( mgr->timer != 0L ) { isc_timer_reset(mgr->timer, isc_timertype_ticker, &tick, &tock, ISC_TRUE ); }else if( isc_timer_create ( mgr->timermgr, isc_timertype_ticker, &tick, &tock, mgr->task, dbus_mgr_restart_dbus, mgr, &(mgr->timer) ) != ISC_R_SUCCESS ) { dbus_mgr_log_err( "D-BUS service cannot be restored." ); } } static void dbus_mgr_dbus_shutdown_handler ( ns_dbus_mgr_t *mgr ) { isc_event_t *dbus_shutdown_event = isc_event_allocate ( mgr->mctx, mgr->task, 1, dbus_mgr_handle_dbus_shutdown_event, mgr, sizeof(isc_event_t) ); if( dbus_shutdown_event != 0L ) { isc_task_purgerange(mgr->task, 0L, ISC_SOCKEVENT_READ_READY, ISC_SOCKEVENT_SELECTED, 0L); isc_task_send( mgr->task, &dbus_shutdown_event ); }else dbus_mgr_log_err("unable to allocate dbus shutdown event"); } static dns_view_t *dbus_mgr_get_localhost_view(void) { dns_view_t *view; isc_netaddr_t localhost = { AF_INET, { { htonl( ( 127 << 24 ) | 1 ) } }, 0 }; int match; for (view = ISC_LIST_HEAD(ns_g_server->viewlist); view != NULL; view = ISC_LIST_NEXT(view, link) ) { /* return first view matching "localhost" source and dest */ if(( (view->matchclients != 0L ) /* 0L: accept "any" */ &&(( dns_acl_match( &localhost, NULL, /* unsigned queries */ view->matchclients, &(ns_g_server->aclenv), &match, NULL /* no match list */ ) != ISC_R_SUCCESS ) || (match <= 0) ) ) ||( (view->matchdestinations != 0L ) /* 0L: accept "any" */ &&(( dns_acl_match( &localhost, NULL, /* unsigned queries */ view->matchdestinations, &(ns_g_server->aclenv), &match, NULL /* no match list */ ) != ISC_R_SUCCESS ) || (match <= 0) ) ) ) continue; break; } return view; } static dns_fwdtable_t *dbus_mgr_get_fwdtable(void) { dns_view_t *view = dbus_mgr_get_localhost_view(); if( view != 0L ) return view->fwdtable; return 0L; } static dns_fwdtable_t *dbus_mgr_get_view_and_fwdtable( dns_view_t **viewp ) { *viewp = dbus_mgr_get_localhost_view(); if( *viewp != 0L ) return (*viewp)->fwdtable; return 0L; } static int dbus_mgr_ifwdr_comparator( const void *p1, const void *p2 ) { char n1buf[ DNS_NAME_FORMATSIZE ]="", *n1p=&(n1buf[0]), n2buf[ DNS_NAME_FORMATSIZE ]="", *n2p=&(n2buf[0]); dns_name_t *dn1; dns_name_t *dn2; DE_CONST(&(((const DBusMgrInitialFwdr*)p1)->dn), dn1); DE_CONST(&(((const DBusMgrInitialFwdr*)p2)->dn), dn2); dns_name_format(dn1, n1p, DNS_NAME_FORMATSIZE ); dns_name_format(dn2, n2p, DNS_NAME_FORMATSIZE ); return strcmp(n1buf, n2buf); } static int dbus_mgr_dhc_if_comparator( const void *p1, const void *p2 ); static void dbus_mgr_record_initial_forwarder( dns_name_t *name, dns_forwarders_t *fwdr, void *mp ) { ns_dbus_mgr_t *mgr = mp; isc_sockaddr_t *sa, *nsa; DBusMgrInitialFwdr *ifwdr; if( ISC_LIST_HEAD(fwdr->addrs) == 0L) return; if( (ifwdr = isc_mem_get(mgr->mctx, sizeof(DBusMgrInitialFwdr))) == 0L) return; ifwdr->fwdpolicy = fwdr->fwdpolicy; dns_name_init(&(ifwdr->dn), NULL); if( dns_name_dupwithoffsets(name, mgr->mctx, &(ifwdr->dn)) != ISC_R_SUCCESS ) goto namedup_err; ISC_LIST_INIT(ifwdr->sa); for( sa = ISC_LIST_HEAD(fwdr->addrs); sa != 0L; sa = ISC_LIST_NEXT(sa,link) ) { nsa = isc_mem_get(mgr->mctx, sizeof(isc_sockaddr_t)); if( nsa == 0L ) goto nsa_err; *nsa = *sa; ISC_LINK_INIT(nsa, link); ISC_LIST_APPEND(ifwdr->sa, nsa, link); } ISC_LINK_INIT(ifwdr, link); tsearch( ifwdr, &(mgr->ifwdt), dbus_mgr_ifwdr_comparator); return; nsa_err: while ( (sa = ISC_LIST_HEAD (ifwdr->sa)) != NULL) { ISC_LIST_UNLINK (ifwdr->sa, sa, link); isc_mem_put (mgr->mctx, sa, sizeof (*sa)); } namedup_err: isc_mem_put (mgr->mctx, ifwdr, sizeof (*ifwdr)); return; } static isc_result_t dbus_mgr_record_initial_fwdtable( ns_dbus_mgr_t *mgr ) { dns_fwdtable_t *fwdtable = dbus_mgr_get_fwdtable(); if( fwdtable == 0L ) return ISC_R_SUCCESS; /* no initial fwdtable */ dns_fwdtable_foreach( fwdtable, dbus_mgr_record_initial_forwarder, mgr); return ISC_R_SUCCESS; } static void dbus_mgr_free_initial_forwarder( void *p ) { DBusMgrInitialFwdr *ifwdr = p; isc_sockaddr_t *sa; dns_name_free(&(ifwdr->dn), ns_g_mctx); for( sa = ISC_LIST_HEAD( ifwdr->sa ); sa != 0L; sa = ISC_LIST_HEAD( ifwdr->sa ) ) { if( ISC_LINK_LINKED(sa, link) ) ISC_LIST_UNLINK(ifwdr->sa, sa, link); isc_mem_put(ns_g_mctx, sa, sizeof(isc_sockaddr_t)); } isc_mem_put(ns_g_mctx, ifwdr, sizeof(DBusMgrInitialFwdr)); } static void dbus_mgr_free_initial_fwdtable( ns_dbus_mgr_t *mgr ) { tdestroy(mgr->ifwdt, dbus_mgr_free_initial_forwarder); mgr->ifwdt = 0L; } static void dbus_mgr_log_forwarders( const char *pfx, dns_name_t *name, SockAddrList *saList) { isc_sockaddr_t *sa; char nameP[DNS_NAME_FORMATSIZE], addrP[128]; int s=0; dns_name_format(name, nameP, DNS_NAME_FORMATSIZE ); for( sa = ISC_LIST_HEAD(*saList); sa != 0L; sa = ISC_LIST_NEXT(sa,link) ) { isc_sockaddr_format(sa, addrP, 128); dbus_mgr_log_info("%s zone %s server %d: %s", pfx, nameP, s++, addrP); } } static isc_result_t dbus_mgr_set_forwarders ( ns_dbus_mgr_t *mgr, DNSNameList *nameList, SockAddrList *saList, dns_fwdpolicy_t fwdpolicy ) { isc_result_t result = ISC_R_SUCCESS; dns_fwdtable_t *fwdtable; dns_view_t *view=0L; dns_name_t *dnsName; isc_sockaddr_t *sa, *nsa; dns_forwarders_t *fwdr=0L; fwdtable = dbus_mgr_get_view_and_fwdtable(&view); if( fwdtable == 0L ) { if( ISC_LIST_HEAD(*saList) == 0L ) return ISC_R_SUCCESS;/* deletion not required */ view = dbus_mgr_get_localhost_view(); if( view == 0L ) return ISC_R_NOPERM; /* if configuration does not allow localhost clients, * then we really shouldn't be creating a forwarding table. */ result = isc_task_beginexclusive(mgr->task); if( result == ISC_R_SUCCESS ) { result = dns_fwdtable_create( mgr->mctx, &(view->fwdtable) ); isc_task_endexclusive(mgr->task); if( result != ISC_R_SUCCESS ) return result; if( view->fwdtable == 0L ) return ISC_R_NOMEMORY; if( isc_log_getdebuglevel(ns_g_lctx) >= 1 ) dbus_mgr_log_info("Created forwarder table."); } } for( dnsName = ISC_LIST_HEAD(*nameList); dnsName != NULL; dnsName = ISC_LIST_NEXT(dnsName,link) ) { fwdr = 0L; if( ( dns_fwdtable_find_exact( fwdtable, dnsName, &fwdr ) != ISC_R_SUCCESS ) ||( fwdr == 0L ) ) { if( ISC_LIST_HEAD( *saList ) == 0L ) continue; /* no forwarders for name - add forwarders */ result = isc_task_beginexclusive(mgr->task); if( result == ISC_R_SUCCESS ) { result = dns_fwdtable_add( fwdtable, dnsName, (isc_sockaddrlist_t*)saList, fwdpolicy ) ; if( view != 0L ) dns_view_flushcache( view ); isc_task_endexclusive(mgr->task); if( result != ISC_R_SUCCESS ) return result; if( isc_log_getdebuglevel(ns_g_lctx) >= 1 ) dbus_mgr_log_forwarders("Created forwarder",dnsName, saList); } continue; } if( ISC_LIST_HEAD( *saList ) == 0L ) { /* empty forwarders list - delete forwarder entry */ if( isc_log_getdebuglevel(ns_g_lctx) >= 1 ) dbus_mgr_log_forwarders("Deleting forwarder", dnsName, (SockAddrList*)&(fwdr->addrs)); result = isc_task_beginexclusive(mgr->task); if( result == ISC_R_SUCCESS ) { result = dns_fwdtable_delete( fwdtable, dnsName ); if( view != 0L ) dns_view_flushcache( view ); isc_task_endexclusive(mgr->task); if( result != ISC_R_SUCCESS ) return result; } continue; } result = isc_task_beginexclusive(mgr->task); if( result == ISC_R_SUCCESS ) { fwdr->fwdpolicy = fwdpolicy; if( isc_log_getdebuglevel(ns_g_lctx) >= 1 ) dbus_mgr_log_forwarders("Removing forwarder", dnsName, (SockAddrList*)&(fwdr->addrs)); for( sa = ISC_LIST_HEAD(fwdr->addrs); sa != 0L ; sa = ISC_LIST_HEAD(fwdr->addrs) ) { if( ISC_LINK_LINKED(sa, link) ) ISC_LIST_UNLINK(fwdr->addrs, sa, link); isc_mem_put(mgr->mctx, sa, sizeof(isc_sockaddr_t)); } ISC_LIST_INIT( fwdr->addrs ); for( sa = ISC_LIST_HEAD(*saList); sa != 0L; sa = ISC_LIST_NEXT(sa,link) ) { nsa = isc_mem_get(mgr->mctx, sizeof(isc_sockaddr_t)); if( nsa == 0L ) { result = ISC_R_NOMEMORY; break; } *nsa = *sa; ISC_LINK_INIT( nsa, link ); ISC_LIST_APPEND( fwdr->addrs, nsa, link ); } if( view != 0L ) dns_view_flushcache( view ); isc_task_endexclusive(mgr->task); if( isc_log_getdebuglevel(ns_g_lctx) >= 1 ) dbus_mgr_log_forwarders("Added forwarder", dnsName, (SockAddrList*)&(fwdr->addrs)); }else return result; } return (result); } static void dbus_mgr_get_name_list ( ns_dbus_mgr_t *mgr, char *domains, DNSNameList *nameList, char *error_name, char *error_message ) { char *name, *endName, *endp; dns_fixedname_t *fixedname; dns_name_t *dnsName; isc_buffer_t buffer; isc_result_t result; uint32_t total_length; total_length = strlen(domains); endp = domains + total_length; ISC_LIST_INIT( *nameList ); for( name = domains + strspn(domains," \t\n"), endName = name + strcspn(name," \t\n"); (name < endp) && (endName <= endp); name = endName + 1 + strspn(endName+1," \t\n"), endName = name + strcspn(name," \t\n") ) { /* name loop */ *endName = '\0'; isc_buffer_init( &buffer, name, endName - name ); isc_buffer_add(&buffer, endName - name); fixedname = isc_mem_get( mgr->mctx, sizeof( dns_fixedname_t )); dns_fixedname_init(fixedname); dnsName = dns_fixedname_name(fixedname); result= dns_name_fromtext ( dnsName, &buffer, ( *(endp-1) != '.') ? dns_rootname : NULL, 0, NULL ); if( result != ISC_R_SUCCESS ) { sprintf(error_name, "com.redhat.named.InvalidArgument"); sprintf(error_message,"Invalid DNS name initial argument: %s", name); isc_mem_put( mgr->mctx, fixedname, sizeof( dns_fixedname_t ) ); for( dnsName = ISC_LIST_HEAD( *nameList ); (dnsName != 0L); dnsName = ISC_LIST_HEAD( *nameList ) ) { if( ISC_LINK_LINKED(dnsName,link) ) ISC_LIST_DEQUEUE( *nameList, dnsName, link ); isc_mem_put( mgr->mctx, dnsName, sizeof( dns_fixedname_t ) ); } ISC_LIST_INIT(*nameList); return; } ISC_LINK_INIT(dnsName, link); ISC_LIST_ENQUEUE( *nameList, dnsName, link ); } } static isc_result_t dbus_mgr_get_sa_list ( ns_dbus_mgr_t *mgr, dbus_svc_MessageIterator iter, SockAddrList *saList , uint8_t *fwdpolicy, char *error_name, char *error_message ) { DBUS_SVC dbus = mgr->dbus; isc_sockaddr_t *nsSA=0L, *nsSA_Q=0L; uint32_t argType = dbus_svc_message_next_arg_type( dbus, iter ), length; isc_result_t result; in_port_t port; char *ip; uint8_t *iparray=0L; ISC_LIST_INIT(*saList); if( argType == TYPE_INVALID ) return ISC_R_SUCCESS; /* address list "removal" */ do { switch( argType ) { case TYPE_UINT32: nsSA = isc_mem_get(mgr->mctx, sizeof(isc_sockaddr_t)); if( nsSA != 0L ) { memset(nsSA,'\0', sizeof(isc_sockaddr_t)); nsSA_Q = nsSA; dbus_svc_message_next_arg(dbus, iter, &(nsSA->type.sin.sin_addr.s_addr)); nsSA->type.sa.sa_family = AF_INET; nsSA->length = sizeof( nsSA->type.sin ); } break; case TYPE_ARRAY: argType = dbus_svc_message_element_type( dbus, iter ); if( argType == TYPE_BYTE ) { iparray = 0L; length = 0; dbus_svc_message_get_elements(dbus, iter, &length, &iparray); if( iparray != 0L ) { if (length == sizeof( struct in_addr )) { nsSA = isc_mem_get(mgr->mctx, sizeof(isc_sockaddr_t)); if( nsSA != 0L ) { memset(nsSA,'\0', sizeof(isc_sockaddr_t)); nsSA_Q = nsSA; memcpy(&(nsSA->type.sin.sin_addr), iparray, sizeof( struct in_addr )); nsSA->type.sa.sa_family = AF_INET; nsSA->length = sizeof( nsSA->type.sin ); } }else if (length == sizeof( struct in6_addr )) { nsSA = isc_mem_get(mgr->mctx, sizeof(isc_sockaddr_t)); if( nsSA != 0L ) { memset(nsSA,'\0', sizeof(isc_sockaddr_t)); nsSA_Q = nsSA; memcpy(&(nsSA->type.sin6.sin6_addr), iparray, sizeof( struct in6_addr )); nsSA->type.sa.sa_family = AF_INET6; nsSA->length = sizeof( nsSA->type.sin6 ); } } } } break; case TYPE_STRING: ip = 0L; dbus_svc_message_next_arg(dbus, iter, &(ip)); if( ip != 0L ) { length = strlen(ip); if( strspn(ip, "0123456789.") == length ) { nsSA = isc_mem_get(mgr->mctx, sizeof(isc_sockaddr_t)); if( nsSA != 0L) { memset(nsSA,'\0', sizeof(isc_sockaddr_t)); if( inet_pton( AF_INET, ip, &(nsSA->type.sin.sin_addr)) ) { nsSA->type.sa.sa_family = AF_INET; nsSA->length = sizeof(nsSA->type.sin); nsSA_Q = nsSA; } } }else if( strspn(ip, "0123456789AaBbCcDdEeFf:.") == length) { nsSA = isc_mem_get(mgr->mctx, sizeof(isc_sockaddr_t)); if( nsSA != 0L ) { memset(nsSA,'\0', sizeof(isc_sockaddr_t)); if( inet_pton( AF_INET6, ip, &(nsSA->type.sin6.sin6_addr)) ) { nsSA->type.sa.sa_family = AF_INET6; nsSA->length = sizeof(nsSA->type.sin6); nsSA_Q = nsSA; } } } } break; case TYPE_UINT16: if( (nsSA == 0L) || (nsSA->type.sa.sa_family == AF_UNSPEC) ) break; else if( nsSA->type.sa.sa_family == AF_INET ) dbus_svc_message_next_arg(dbus, iter, &(nsSA->type.sin.sin_port)); else if( nsSA->type.sa.sa_family == AF_INET6 ) dbus_svc_message_next_arg(dbus, iter, &(nsSA->type.sin6.sin6_port)); break; case TYPE_BYTE: dbus_svc_message_next_arg(dbus, iter, fwdpolicy); if(*fwdpolicy > dns_fwdpolicy_only) *fwdpolicy = dns_fwdpolicy_only; break; default: if(nsSA != 0L) nsSA->type.sa.sa_family = AF_UNSPEC; sprintf(error_message,"Unhandled argument type: %c", argType); break; } if( (nsSA != 0L) &&(nsSA->type.sa.sa_family == AF_UNSPEC) ) { sprintf(error_name, "com.redhat.named.InvalidArgument"); if( error_message[0]=='\0') { if( nsSA == 0L ) sprintf(error_message,"Missing IP Address Name Server argument"); else sprintf(error_message,"Bad IP Address Name Server argument"); } if( nsSA != 0L ) isc_mem_put(mgr->mctx, nsSA, sizeof(isc_sockaddr_t)); nsSA = 0L; for( nsSA = ISC_LIST_HEAD( *saList ); (nsSA != 0L); nsSA = ISC_LIST_HEAD( *saList ) ) { if(ISC_LINK_LINKED(nsSA, link)) ISC_LIST_DEQUEUE( *saList, nsSA, link ); isc_mem_put( mgr->mctx, nsSA, sizeof( isc_sockaddr_t ) ); } ISC_LIST_INIT(*saList); return ISC_R_FAILURE; } if( nsSA != 0L ) { if( nsSA->type.sin.sin_port == 0 ) { if( ns_g_port != 0L ) nsSA->type.sin.sin_port = htons(ns_g_port); else { result = ns_config_getport(ns_g_config, &(port) ); if( result != ISC_R_SUCCESS ) port = 53; nsSA->type.sin.sin_port = htons( port ); } } if( nsSA_Q != 0L ) { ISC_LINK_INIT(nsSA,link); ISC_LIST_ENQUEUE(*saList, nsSA, link); nsSA_Q = 0L; } } argType = dbus_svc_message_next_arg_type( dbus, iter ); } while ( argType != TYPE_INVALID ); return ISC_R_SUCCESS; } static void dbus_mgr_handle_set_forwarders ( ns_dbus_mgr_t *mgr, DBUS_SVC dbus, uint8_t reply_expected, uint32_t serial, const char *path, const char *member, const char *interface, const char *sender, dbus_svc_MessageHandle msg ) { dbus_svc_MessageIterator iter; char error_name[1024]="", error_message[1024]="", *domains=0L; uint32_t argType, new_serial; DNSNameList nameList; dns_name_t *dnsName; SockAddrList saList; isc_sockaddr_t *nsSA; isc_result_t result; uint8_t fwdpolicy = dns_fwdpolicy_only; iter = dbus_svc_message_iterator_new( dbus, msg ); if( iter == 0L ) { if( reply_expected ) { sprintf(error_name, "com.redhat.named.InvalidArguments"); sprintf(error_message,"SetForwarders requires DNS name and nameservers arguments."); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); } return; } argType = dbus_svc_message_next_arg_type( dbus, iter ); if( argType != TYPE_STRING ) { if( reply_expected ) { sprintf(error_name, "com.redhat.named.InvalidArguments"); sprintf(error_message,"SetForwarders requires DNS name string initial argument."); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); } return; } dbus_svc_message_next_arg( dbus, iter, &domains ); if( ( domains == 0L ) || (*domains == '\0') ) { if( reply_expected ) { sprintf(error_name, "com.redhat.named.InvalidArguments"); sprintf(error_message,"SetForwarders requires DNS name string initial argument."); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); } return; } dbus_mgr_get_name_list( mgr, domains, &nameList, error_name, error_message ); if( error_name[0] != '\0' ) { if( reply_expected ) { dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); } return; } if( ISC_LIST_HEAD( nameList ) == 0L ) return; result = dbus_mgr_get_sa_list( mgr, iter, &saList , &fwdpolicy, error_name, error_message ); if( result == ISC_R_SUCCESS ) { result = dbus_mgr_set_forwarders( mgr, &nameList, &saList, fwdpolicy ); if( result != ISC_R_SUCCESS ) { if( reply_expected ) { sprintf(error_name, "com.redhat.named.Failure"); sprintf(error_message, isc_result_totext(result)); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); } }else if( reply_expected ) dbus_svc_send( dbus, RETURN, serial, &new_serial, sender, path, interface, member, TYPE_UINT32, &result, TYPE_INVALID ); }else { if( reply_expected ) { dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); } } for( dnsName = ISC_LIST_HEAD( nameList ); (dnsName != 0L) ; dnsName = ISC_LIST_HEAD( nameList ) ) { if( ISC_LINK_LINKED(dnsName,link) ) ISC_LIST_DEQUEUE( nameList, dnsName, link ); isc_mem_put( mgr->mctx, dnsName, sizeof( dns_fixedname_t ) ); } for( nsSA = ISC_LIST_HEAD(saList); (nsSA != 0L) ; nsSA = ISC_LIST_HEAD(saList) ) { if( ISC_LINK_LINKED(nsSA,link) ) ISC_LIST_DEQUEUE( saList, nsSA, link ); isc_mem_put(mgr->mctx, nsSA, sizeof(isc_sockaddr_t)); } } static int dbus_mgr_msg_append_dns_name ( DBUS_SVC dbus, dbus_svc_MessageHandle msg, dns_name_t *name ) { char nameBuf[ DNS_NAME_FORMATSIZE ]="", *nameP=&(nameBuf[0]); dns_name_format(name, nameP, DNS_NAME_FORMATSIZE ); if( *nameP == '\0' ) return 0; return dbus_svc_message_append_args( dbus, msg, TYPE_STRING, &nameP, TYPE_INVALID ) > 0; } typedef enum dbmoi_e { OUTPUT_BINARY, OUTPUT_TEXT } DBusMgrOutputInterface; static int dbus_mgr_msg_append_forwarders ( DBUS_SVC dbus, dbus_svc_MessageHandle msg, dns_forwarders_t *fwdr, DBusMgrOutputInterface outputType ) { isc_sockaddr_t *sa; char policyBuf[16]="", *pbp[1]={&(policyBuf[0])}, addressBuf[64]="", *abp[1]={&(addressBuf[0])}; uint8_t *byteArray[1]; if( outputType == OUTPUT_BINARY ) { if(!dbus_svc_message_append_args ( dbus, msg, TYPE_BYTE, &(fwdr->fwdpolicy), TYPE_INVALID ) ) return 0; }else if( outputType == OUTPUT_TEXT ) { sprintf(policyBuf,"%s", (fwdr->fwdpolicy == dns_fwdpolicy_none) ? "none" : (fwdr->fwdpolicy == dns_fwdpolicy_first) ? "first" : "only" ); if(!dbus_svc_message_append_args ( dbus, msg, TYPE_STRING, pbp, TYPE_INVALID ) ) return 0; }else return 0; for( sa = ISC_LIST_HEAD(fwdr->addrs); sa != 0L; sa = ISC_LIST_NEXT(sa, link) ) { if( outputType == OUTPUT_BINARY ) { if( sa->type.sa.sa_family == AF_INET ) { if(!dbus_svc_message_append_args ( dbus, msg, TYPE_UINT32, &(sa->type.sin.sin_addr.s_addr), TYPE_INVALID ) ) return 0; if(!dbus_svc_message_append_args ( dbus, msg, TYPE_UINT16, &(sa->type.sin.sin_port), TYPE_INVALID ) ) return 0; }else if( sa->type.sa.sa_family == AF_INET6 ) { byteArray[0] = (uint8_t*)&(sa->type.sin6.sin6_addr); if(!dbus_svc_message_append_args ( dbus, msg, TYPE_ARRAY, TYPE_BYTE, &byteArray, sizeof(struct in6_addr), TYPE_INVALID ) ) return 0; if(!dbus_svc_message_append_args ( dbus, msg, TYPE_UINT16, &(sa->type.sin6.sin6_port), TYPE_INVALID ) ) return 0; }else continue; }else if( outputType == OUTPUT_TEXT ) { if( sa->type.sa.sa_family == AF_INET ) { if( inet_ntop( AF_INET, &(sa->type.sin.sin_addr), addressBuf, sizeof(addressBuf)) == 0L ) continue; if(!dbus_svc_message_append_args ( dbus, msg, TYPE_STRING, abp, TYPE_INVALID ) ) return 0; sprintf(addressBuf, "%hu", ntohs( sa->type.sin.sin_port )); if(!dbus_svc_message_append_args ( dbus, msg, TYPE_STRING, abp, TYPE_INVALID ) ) return 0; }else if( sa->type.sa.sa_family == AF_INET6 ) { if( inet_ntop( AF_INET6, &(sa->type.sin6.sin6_addr), addressBuf, sizeof(addressBuf)) == 0L ) continue; if(!dbus_svc_message_append_args ( dbus, msg, TYPE_STRING, abp, TYPE_INVALID ) ) return 0; sprintf(addressBuf, "%hu", ntohs( sa->type.sin6.sin6_port )); if(!dbus_svc_message_append_args ( dbus, msg, TYPE_STRING, abp, TYPE_INVALID ) ) return 0; }else continue; }else return 0; } return 1; } typedef struct dbm_m_s { DBUS_SVC dbus; dbus_svc_MessageHandle msg; DBusMgrOutputInterface outputType; } DBusMgrMsg; static void forwarders_to_msg( dns_name_t *name, dns_forwarders_t *fwdr, void *mp ) { DBusMgrMsg *m = mp; if( (fwdr == 0L) || (name == 0L) || (mp == 0L)) return; dbus_mgr_msg_append_dns_name ( m->dbus, m->msg, name ); dbus_mgr_msg_append_forwarders( m->dbus, m->msg, fwdr, m->outputType ); } static void dbus_mgr_handle_list_forwarders ( DBUS_SVC dbus, uint8_t reply_expected, uint32_t serial, const char *path, const char *member, const char *interface, const char *sender, dbus_svc_MessageHandle msg ) { char error_name[1024], error_message[1024]; DBusMgrMsg m; uint32_t new_serial; dns_fwdtable_t *fwdtable = dbus_mgr_get_fwdtable(); DBusMgrOutputInterface outputType = OUTPUT_BINARY; uint32_t length = strlen(interface); if( !reply_expected ) return; if( (length > 4) && (strcmp(interface + (length - 4), "text")==0)) outputType = OUTPUT_TEXT; if( fwdtable == 0L ) { sprintf(error_name,"com.redhat.dbus.Failure"); sprintf(error_message, "%s", isc_result_totext(ISC_R_NOPERM)); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); return; } msg = dbus_svc_new_message( dbus, RETURN, serial, sender, path, interface, member); m.dbus = dbus; m.msg = msg; m.outputType = outputType; if( msg == 0L ) { sprintf(error_name,"com.redhat.dbus.OutOfMemory"); sprintf(error_message,"out of memory"); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); } dns_fwdtable_foreach( fwdtable, forwarders_to_msg, &m ); dbus_svc_send_message( dbus, msg, &new_serial ); } static void dbus_mgr_handle_get_forwarders ( DBUS_SVC dbus, uint8_t reply_expected, uint32_t serial, const char *path, const char *member, const char *interface, const char *sender, dbus_svc_MessageHandle msg ) { char error_name[1024], error_message[1024], *domain=0L; isc_result_t result; dns_fixedname_t fixedname; dns_name_t *dnsName; isc_buffer_t buffer; uint32_t length, new_serial; dns_fwdtable_t *fwdtable; dns_forwarders_t *fwdr=0L; dns_name_t *foundname; dns_fixedname_t fixedFoundName; DBusMgrOutputInterface outputType = OUTPUT_BINARY; if( !reply_expected ) return; length = strlen(interface); if( (length > 4) && (strcmp(interface + (length - 4), "text")==0)) outputType = OUTPUT_TEXT; if( (!dbus_svc_get_args( dbus, msg, TYPE_STRING, &domain, TYPE_INVALID)) ||(domain == 0L) ||(*domain == '\0') ) { sprintf(error_name,"com.redhat.dbus.InvalidArguments"); sprintf(error_message,"domain name argument expected"); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); return; } length = strlen( domain ); isc_buffer_init( &buffer, domain, length); isc_buffer_add(&buffer, length); dns_fixedname_init(&fixedname); dnsName = dns_fixedname_name(&fixedname); result = dns_name_fromtext ( dnsName, &buffer, dns_rootname, 0, NULL ); if( result != ISC_R_SUCCESS ) { sprintf(error_name,"com.redhat.dbus.InvalidArguments"); sprintf(error_message,"invalid domain name argument: %s", domain); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); return; } msg = dbus_svc_new_message( dbus, RETURN, serial, sender, path, interface, member); if( msg == 0L ) { sprintf(error_name,"com.redhat.dbus.OutOfMemory"); sprintf(error_message,"out of memory"); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); return; } fwdtable = dbus_mgr_get_fwdtable(); if( fwdtable == 0L ) { sprintf(error_name,"com.redhat.dbus.Failure"); sprintf(error_message, "%s", isc_result_totext(ISC_R_NOPERM)); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); return; } dns_fixedname_init(&fixedFoundName); foundname = dns_fixedname_name(&fixedFoundName); if( ( dns_fwdtable_find_closest( fwdtable, dnsName, foundname, &fwdr ) == ISC_R_SUCCESS ) &&( fwdr != 0L ) ) { if( (!dbus_mgr_msg_append_dns_name( dbus, msg, foundname )) ||(!dbus_mgr_msg_append_forwarders( dbus, msg, fwdr, outputType )) ) { sprintf(error_name,"com.redhat.dbus.OutOfMemory"); sprintf(error_message,"out of memory"); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); return; } }else { result = ISC_R_NOTFOUND; if( outputType == OUTPUT_BINARY ) { dbus_svc_message_append_args( dbus, msg, TYPE_UINT32, &(result), TYPE_INVALID ) ; }else { sprintf(error_name,"com.redhat.dbus.NotFound"); sprintf(error_message,"Not Found"); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); return; } } dbus_svc_send_message( dbus, msg, &new_serial ); } static void dbus_mgr_check_dhcdbd_state( ns_dbus_mgr_t *mgr, dbus_svc_MessageHandle msg ) { DBUS_SVC dbus = mgr->dbus; char *name_owned = 0L, *old_owner = 0L, *new_owner = 0L; if( !dbus_svc_get_args( dbus, msg, TYPE_STRING, &name_owned, TYPE_STRING, &old_owner, TYPE_STRING, &new_owner, TYPE_INVALID ) ) return; dbus_mgr_log_dbg("NameOwnerChanged: %s %s %s ( %s )", name_owned, old_owner, new_owner, mgr->dhcdbd_name); if( (name_owned == 0L) || (new_owner == 0L) || (old_owner == 0L) ) return; if( strcmp( name_owned, "com.redhat.dhcp" ) == 0 ) { if( *new_owner == '\0' ) { isc_mem_put(mgr->mctx, mgr->dhcdbd_name, strlen(mgr->dhcdbd_name) + 1); mgr->dhcdbd_name = 0L; dbus_mgr_log_err("D-BUS dhcdbd subscription disabled."); return; } if( (mgr->dhcdbd_name == 0L) ||( strcmp( mgr->dhcdbd_name, new_owner) != 0 ) ) { if( mgr->dhcdbd_name != 0L ) { isc_mem_put(mgr->mctx, mgr->dhcdbd_name, strlen(mgr->dhcdbd_name)+1); mgr->dhcdbd_name = 0L; } mgr->dhcdbd_name = isc_mem_get(mgr->mctx, strlen(new_owner) + 1); if( mgr->dhcdbd_name == 0L ) return; strcpy( mgr->dhcdbd_name, new_owner ); dbus_mgr_subscribe_to_dhcdbd( mgr ); } }else if( ( mgr->dhcdbd_name != 0L ) && ( strcmp(mgr->dhcdbd_name, name_owned) == 0L ) && ( *new_owner == '\0' ) ) { isc_mem_put(mgr->mctx, mgr->dhcdbd_name, strlen(mgr->dhcdbd_name)); mgr->dhcdbd_name = 0L; dbus_mgr_log_err("D-BUS dhcdbd subscription disabled."); } } static int dbus_mgr_dhc_if_comparator( const void *p1, const void *p2 ) { return( strcmp( ((const DHC_IF*)p1)->if_name, ((const DHC_IF*)p2)->if_name) ); } static dns_name_t *dbus_mgr_if_reverse_ip_name ( ns_dbus_mgr_t *mgr, struct in_addr ip_address, struct in_addr subnet_mask ) { dns_name_t *dns_name =0L; dns_fixedname_t *fixedname=0L; char name [ DNS_NAME_FORMATSIZE ], *p; uint32_t ip = (ntohl(ip_address.s_addr) & ntohl(subnet_mask.s_addr)), i; isc_buffer_t buffer; isc_result_t result; if( (ip == 0) || (ip == 0xffffffff) ) return 0L; for(i = 8, p = name; (i < 32); i += 8) if( ip & ( 0xff << i ) ) p += sprintf(p, "%u.", (((ip & ( 0xff << i )) >> i ) & 0xff) ); if( p > name ) { p += sprintf(p, "in-addr.arpa"); isc_buffer_init( &buffer, name, p - name ); isc_buffer_add(&buffer, p - name); fixedname = isc_mem_get( mgr->mctx, sizeof( dns_fixedname_t )); dns_fixedname_init(fixedname); dns_name = dns_fixedname_name(fixedname); result= dns_name_fromtext ( dns_name, &buffer, dns_rootname, 0, NULL ); ISC_LINK_INIT(dns_name, link); if( result == ISC_R_SUCCESS ) return dns_name; } return 0L; } static void dbus_mgr_free_dhc( void *p ) { DHC_IF *d_if = p; dns_name_t *dn; isc_sockaddr_t *sa; isc_mem_put( ns_g_mctx, d_if->if_name, strlen(d_if->if_name) + 1); for( sa = ISC_LIST_HEAD( d_if->dns ); sa != NULL; sa = ISC_LIST_HEAD( d_if->dns ) ) { if( ISC_LINK_LINKED( sa, link ) ) ISC_LIST_UNLINK( d_if->dns, sa, link ); isc_mem_put(ns_g_mctx, sa, sizeof(isc_sockaddr_t)); } for( dn = ISC_LIST_HEAD( d_if->dn ); dn != NULL; dn = ISC_LIST_HEAD( d_if->dn ) ) { if( ISC_LINK_LINKED( dn, link ) ) ISC_LIST_UNLINK( d_if->dn, dn, link ); isc_mem_put( ns_g_mctx, dn, sizeof( dns_fixedname_t ) ); } isc_mem_put( ns_g_mctx, d_if, sizeof(DHC_IF)); } static void dbus_mgr_handle_dhcdbd_message ( ns_dbus_mgr_t *mgr, const char *path, const char *member, dbus_svc_MessageHandle msg ) { DBUS_SVC dbus = mgr->dbus; DHC_IF *d_if, *const*d_ifpp, dif; DHC_State dhc_state; char *if_name, *opt_name, error_name[1024]="", error_message[1024]=""; uint8_t *value=0L; uint32_t length; isc_result_t result; isc_sockaddr_t *sa = 0L; dns_name_t *dn = 0L; struct in_addr *ip; in_port_t port; char dnBuf[ DNS_NAME_FORMATSIZE ]; isc_buffer_t buffer; DBusMgrInitialFwdr *ifwdr, *const*ifwdpp, ifwd; ISC_LIST(DBusMgrInitialFwdr) ifwdrList; DNSNameList nameList; dbus_mgr_log_dbg("Got dhcdbd message: %s %s %p", path, member, msg ); if( ( if_name = strrchr(path,'/') ) == 0L ) { dbus_mgr_log_err("bad path in dhcdbd message:", path); return; } ++if_name; dif.if_name = if_name; if( ((d_ifpp=tfind( &dif, &(mgr->dhc_if), dbus_mgr_dhc_if_comparator)) == 0L) ||((d_if = *d_ifpp) == 0L) ) { d_if = isc_mem_get( mgr->mctx, sizeof(DHC_IF)); if( d_if == 0L ) { dbus_mgr_log_err("out of memory"); return; } memset(d_if, '\0', sizeof(DHC_IF)); if((d_if->if_name = isc_mem_get( mgr->mctx, strlen(if_name) + 1)) == 0L) { dbus_mgr_log_err("out of memory"); return; } strcpy(d_if->if_name, if_name); d_if->dhc_state = DHC_INVALID; d_if->previous_state = DHC_INVALID; ISC_LIST_INIT( d_if->dn ); ISC_LIST_INIT( d_if->dns ); if( tsearch( d_if, &(mgr->dhc_if), dbus_mgr_dhc_if_comparator) == 0L ) { dbus_mgr_log_err("out of memory"); return; } } if( strcmp(member, "reason") == 0 ) { if( (!dbus_svc_get_args( dbus, msg, TYPE_STRING, &opt_name, TYPE_ARRAY, TYPE_BYTE, &value, &length, TYPE_INVALID ) ) ||( value == 0L) ||( length != sizeof(uint32_t)) ||( *((uint32_t*)value) > DHC_END_OPTIONS) ) { dbus_mgr_log_err("Invalid DHC reason value received from dhcdbd"); return; } dhc_state = (DHC_State) *((uint32_t*)value); dbus_mgr_log_dbg("reason: %d %d %d", dhc_state, d_if->dhc_state, d_if->previous_state); switch( dhc_state ) { case DHC_END_OPTIONS: switch( d_if->dhc_state ) { case DHC_END_OPTIONS: break; case DHC_RENEW: case DHC_REBIND: if( ( d_if->previous_state != DHC_INVALID ) &&( d_if->previous_state != DHC_RELEASE ) ) break; /* DHC_RENEW means the same lease parameters were obtained. * Only do configuration if we started up with existing dhclient * which has now renewed - else we are already configured correctly. */ dbus_mgr_log_err("D-BUS: existing dhclient for interface %s RENEWed lease", if_name); case DHC_REBOOT: case DHC_BOUND: d_if->previous_state = d_if->dhc_state; d_if->dhc_state = DHC_BOUND; if( (dn = dbus_mgr_if_reverse_ip_name(mgr, d_if->ip, d_if->subnet_mask )) != 0L ) { ISC_LIST_APPEND(d_if->dn, dn, link ); } if( ( ISC_LIST_HEAD( d_if->dn ) != NULL ) &&( ISC_LIST_HEAD( d_if->dns ) != NULL ) ) { dbus_mgr_log_err("D-BUS: dhclient for interface %s acquired new lease - creating forwarders.", if_name ); result = dbus_mgr_set_forwarders( mgr, &(d_if->dn), &(d_if->dns), dns_fwdpolicy_only ); if( result != ISC_R_SUCCESS ) { dbus_mgr_log_err("D-BUS: forwarder configuration failed: %s", isc_result_totext(result)); } } break; case DHC_STOP: case DHC_TIMEOUT: case DHC_FAIL: case DHC_EXPIRE: case DHC_RELEASE: d_if->previous_state = d_if->dhc_state; d_if->dhc_state = DHC_RELEASE; if( ISC_LIST_HEAD( d_if->dn ) != NULL ) { dbus_mgr_log_err("D-BUS: dhclient for interface %s released lease - removing forwarders.", if_name); for( sa = ISC_LIST_HEAD( d_if->dns ); sa != 0L; sa = ISC_LIST_HEAD( d_if->dns ) ) { if( ISC_LINK_LINKED( sa, link ) ) ISC_LIST_UNLINK( d_if->dns, sa, link ); isc_mem_put( mgr->mctx, sa, sizeof(isc_sockaddr_t)); } ISC_LIST_INIT( d_if->dns ); ISC_LIST_INIT( ifwdrList ); for( dn = ISC_LIST_HEAD( d_if->dn ); dn != 0L; dn = ISC_LIST_NEXT( dn, link ) ) { dns_name_init( &(ifwd.dn), NULL ); isc_buffer_init( &buffer, dnBuf, DNS_NAME_FORMATSIZE); dns_name_setbuffer( &(ifwd.dn), &buffer); dns_name_copy(dn, &(ifwd.dn), NULL); if( ((ifwdpp = tfind(&ifwd, &(mgr->ifwdt), dbus_mgr_ifwdr_comparator)) != 0L ) &&((ifwdr = *ifwdpp) != 0L) ) { ISC_LIST_APPEND( ifwdrList, ifwdr, link ); } } result = dbus_mgr_set_forwarders( mgr, &(d_if->dn), &(d_if->dns), dns_fwdpolicy_none ); if( result != ISC_R_SUCCESS ) { dbus_mgr_log_err("D-BUS: removal of forwarders failed: %s", isc_result_totext(result)); } for( dn = ISC_LIST_HEAD( d_if->dn ); dn != 0L; dn = ISC_LIST_HEAD( d_if->dn ) ) { if( ISC_LINK_LINKED( dn, link ) ) ISC_LIST_UNLINK( d_if->dn, dn, link ); isc_mem_put( mgr->mctx, dn, sizeof( dns_fixedname_t ) ); } ISC_LIST_INIT( d_if->dn ); for( ifwdr = ISC_LIST_HEAD( ifwdrList ); ifwdr != 0L; ifwdr = ISC_LIST_HEAD( ifwdrList ) ) { if( ISC_LINK_LINKED( ifwdr, link ) ) ISC_LIST_UNLINK( ifwdrList, ifwdr, link ); ISC_LINK_INIT(ifwdr, link); ISC_LIST_INIT(nameList); ISC_LINK_INIT(&(ifwdr->dn), link); ISC_LIST_APPEND( nameList, &(ifwdr->dn), link ); result = dbus_mgr_set_forwarders( mgr, &nameList, &(ifwdr->sa), ifwdr->fwdpolicy ); if( result != ISC_R_SUCCESS ) { dbus_mgr_log_err("D-BUS: restore of forwarders failed: %s", isc_result_totext(result)); } } } case DHC_ABEND: case DHC_END: case DHC_NBI: case DHC_PREINIT: case DHC_MEDIUM: case DHC_START: case DHC_INVALID: default: break; } break; case DHC_BOUND: case DHC_REBOOT: case DHC_REBIND: case DHC_RENEW: case DHC_STOP: case DHC_TIMEOUT: case DHC_FAIL: case DHC_EXPIRE: case DHC_RELEASE: d_if->previous_state = d_if->dhc_state; d_if->dhc_state = dhc_state; case DHC_ABEND: case DHC_END: case DHC_NBI: case DHC_PREINIT: case DHC_MEDIUM: case DHC_START: case DHC_INVALID: default: break; } }else if( strcmp( member, "domain_name" ) == 0 ) { if( (!dbus_svc_get_args( dbus, msg, TYPE_STRING, &opt_name, TYPE_ARRAY, TYPE_BYTE, &value, &length, TYPE_INVALID ) ) ||( value == 0L) ||( length == 0) ) { dbus_mgr_log_err("Invalid domain_name value received from dhcdbd"); return; } dbus_mgr_log_dbg("domain-name %s", (char*)value); dbus_mgr_get_name_list( mgr, (char*)value, &(d_if->dn), error_name, error_message ); if( ( error_message[0] != '\0' ) || (ISC_LIST_HEAD(d_if->dn) == 0L )) { dbus_mgr_log_err("Bad domain_name value: %s", error_message ); } }else if( strcmp( member, "domain_name_servers") == 0 ) { if( (!dbus_svc_get_args( dbus, msg, TYPE_STRING, &opt_name, TYPE_ARRAY, TYPE_BYTE, &value, &length, TYPE_INVALID ) ) ||( value == 0L) ||( length == 0) ) { dbus_mgr_log_err("Invalid domain_name_servers value received from dhcdbd"); return; } for(ip = (struct in_addr*) value; ip < ((struct in_addr*)(value + length)); ip++) { dbus_mgr_log_dbg("domain-name-servers: %s", inet_ntop(AF_INET, value, error_name, 16)); sa = isc_mem_get(mgr->mctx, sizeof(isc_sockaddr_t)); memset(sa, '\0', sizeof(isc_sockaddr_t)); sa->type.sin.sin_addr = *ip; sa->type.sa.sa_family = AF_INET; sa->length = sizeof(sa->type.sin); result = ns_config_getport(ns_g_config, &(port) ); if( result != ISC_R_SUCCESS ) port = 53; sa->type.sin.sin_port = htons( port ); ISC_LIST_APPEND(d_if->dns, sa, link); } }else if( strcmp(member, "ip_address") == 0) { if( (!dbus_svc_get_args( dbus, msg, TYPE_STRING, &opt_name, TYPE_ARRAY, TYPE_BYTE, &value, &length, TYPE_INVALID ) ) ||( value == 0L) ||( length != sizeof(struct in_addr) ) ) { dbus_mgr_log_err("Invalid ip_address value received from dhcdbd"); return; } dbus_mgr_log_dbg("ip-address: %s", inet_ntop(AF_INET, value, error_name, 16)); d_if->ip = *((struct in_addr*)value); }else if( strcmp(member, "subnet_mask") == 0 ) { if( (!dbus_svc_get_args( dbus, msg, TYPE_STRING, &opt_name, TYPE_ARRAY, TYPE_BYTE, &value, &length, TYPE_INVALID ) ) ||( value == 0L) ||( length != sizeof(struct in_addr) ) ) { dbus_mgr_log_err("Invalid subnet_mask value received from dhcdbd"); return; } dbus_mgr_log_dbg("subnet-mask: %s", inet_ntop(AF_INET, value, error_name, 16)); d_if->subnet_mask = *((struct in_addr*)value); } } static dbus_svc_HandlerResult dbus_mgr_message_handler ( DBusMsgHandlerArgs ) { char error_name[1024], error_message[1024]; ns_dbus_mgr_t *mgr = object; uint32_t new_serial; if_suffix = prefix = suffix = prefixObject = 0L; dbus_mgr_log_dbg("D-BUS message: %u %u %u %s %s %s %s %s %s", type, reply_expected, serial, destination, path, member, interface, sender, signature ); if ( ( type == SIGNAL ) &&( strcmp(path,"/org/freedesktop/DBus/Local") == 0 ) ) { if( strcmp(member,"Disconnected") == 0 ) dbus_mgr_dbus_shutdown_handler( mgr ); }else if( ( type == SIGNAL ) &&( strcmp(path,"/org/freedesktop/DBus") == 0 ) &&(strcmp(member,"NameOwnerChanged") == 0) &&(strcmp(signature, "sss") == 0) ) { dbus_mgr_check_dhcdbd_state( mgr, msg ); }else if( ( type == SIGNAL ) &&( (sender != 0L) && (mgr->dhcdbd_name != 0L) && (strcmp(sender,mgr->dhcdbd_name) == 0)) &&( strcmp(interface,"com.redhat.dhcp.subscribe.binary") == 0 ) ) { dbus_mgr_handle_dhcdbd_message( mgr, path, member, msg ); }else if( (type == CALL) &&( strcmp(destination, DBUSMGR_DESTINATION)==0) &&( strcmp(path, DBUSMGR_OBJECT_PATH)==0) ) { if( strcmp(member, "SetForwarders") == 0 ) dbus_mgr_handle_set_forwarders ( mgr, dbus, reply_expected, serial, path, member, interface, sender, msg ); else if( strcmp(member, "GetForwarders") == 0 ) { if( *signature != '\0' ) dbus_mgr_handle_get_forwarders ( dbus, reply_expected, serial, path, member, interface, sender, msg ); else dbus_mgr_handle_list_forwarders ( dbus, reply_expected, serial, path, member, interface, sender, msg ); }else if( reply_expected ) { sprintf(error_name, "InvalidOperation"); sprintf(error_message, "Unrecognized path / interface / member"); dbus_svc_send( dbus, ERROR, serial, &new_serial, sender, path, interface, member, TYPE_STRING, error_name, TYPE_STRING, error_message, TYPE_INVALID ); } } return HANDLED; } static void dbus_mgr_read_watch_activated(isc_task_t *t, isc_event_t *ev) { DBusMgrSocket *sfd = (DBusMgrSocket*)(ev->ev_arg); t = t; isc_mem_put(sfd->mgr->mctx, ev, ev->ev_size); dbus_mgr_log_dbg("watch %d READ",sfd->fd); isc_socket_fd_handle_reads( sfd->sock, sfd->ser ); dbus_svc_handle_watch( sfd->mgr->dbus, sfd->fd, WATCH_ENABLE | WATCH_READ ); } static void dbus_mgr_write_watch_activated(isc_task_t *t, isc_event_t *ev) { DBusMgrSocket *sfd = (DBusMgrSocket*)(ev->ev_arg); t = t; isc_mem_put(sfd->mgr->mctx, ev, ev->ev_size); dbus_mgr_log_dbg("watch %d WRITE",sfd->fd); isc_socket_fd_handle_writes( sfd->sock, sfd->ser ); dbus_svc_handle_watch( sfd->mgr->dbus, sfd->fd, WATCH_ENABLE | WATCH_WRITE ); } static void dbus_mgr_watches_selected(isc_task_t *t, isc_event_t *ev) { ns_dbus_mgr_t *mgr = (ns_dbus_mgr_t*)(ev->ev_arg); t = t; isc_mem_put(mgr->mctx, ev, ev->ev_size); if( ( mgr->dbus == 0L ) || (mgr->sockets == 0L)) { return; } dbus_mgr_log_dbg("watches selected"); dbus_svc_dispatch( mgr->dbus ); dbus_mgr_log_dbg("dispatch complete"); } static int dbus_mgr_socket_comparator( const void *p1, const void *p2 ) { return( ( ((const DBusMgrSocket*)p1)->fd == ((const DBusMgrSocket*)p2)->fd ) ? 0 : ( ((const DBusMgrSocket*)p1)->fd > ((const DBusMgrSocket*)p2)->fd ) ? 1 : -1 ); } static void dbus_mgr_watch_handler( int fd, dbus_svc_WatchFlags flags, void *mgrp ) { ns_dbus_mgr_t *mgr = mgrp; DBusMgrSocket sockFd, *sfd=0L, *const*spp=0L; isc_result_t result=ISC_R_SUCCESS; isc_socketevent_t *sev; isc_event_t *pev[1]; if(mgr == 0L) return; if( (flags & 7) == WATCH_ERROR ) return; sockFd.fd = fd; dbus_mgr_log_dbg("watch handler: fd %d %d", fd, flags); if( ((spp = tfind( &sockFd, &(mgr->sockets), dbus_mgr_socket_comparator) ) == 0L ) ||((sfd = *spp) == 0L ) ) { if( ( flags & WATCH_ENABLE ) == 0 ) return; sfd = isc_mem_get(mgr->mctx, sizeof(DBusMgrSocket)); if( sfd == 0L ) { dbus_mgr_log_err("dbus_mgr: out of memory" ); return; } sfd->fd = fd; sfd->mgr = mgr; sfd->ser = sfd->sew = sfd->sel = 0L; if( tsearch(sfd, &(mgr->sockets), dbus_mgr_socket_comparator) == 0L ) { dbus_mgr_log_err("dbus_mgr: out of memory" ); isc_mem_put(mgr->mctx, sfd, sizeof(DBusMgrSocket)); return; } sfd->sock = 0L; result = isc_socket_create( mgr->socketmgr, fd, isc_sockettype_fd, &(sfd->sock) ); if( result != ISC_R_SUCCESS ) { dbus_mgr_log_err("dbus_mgr: isc_socket_create failed: %s", isc_result_totext(result) ); tdelete(sfd, &(mgr->sockets), dbus_mgr_socket_comparator); isc_mem_put(mgr->mctx, sfd, sizeof(DBusMgrSocket)); return; } } if( (flags & WATCH_ENABLE) == WATCH_ENABLE ) { if( (flags & WATCH_READ) == WATCH_READ ) { if( sfd->ser == 0L ) { sfd->ser = (isc_socketevent_t *) isc_event_allocate ( mgr->mctx, mgr->task, ISC_SOCKEVENT_READ_READY, dbus_mgr_read_watch_activated, sfd, sizeof(isc_socketevent_t) ); if( sfd->ser == 0L ) { dbus_mgr_log_err("dbus_mgr: out of memory" ); tdelete(sfd, &(mgr->sockets), dbus_mgr_socket_comparator); isc_mem_put(mgr->mctx, sfd, sizeof(DBusMgrSocket)); return; } sev = isc_socket_fd_handle_reads(sfd->sock, sfd->ser ); }else { sev = isc_socket_fd_handle_reads(sfd->sock, sfd->ser ); } } if( (flags & WATCH_WRITE) == WATCH_WRITE ) { if( sfd->sew == 0L ) { sfd->sew = (isc_socketevent_t *) isc_event_allocate ( mgr->mctx, mgr->task, ISC_SOCKEVENT_WRITE_READY, dbus_mgr_write_watch_activated, sfd, sizeof(isc_socketevent_t) ); if( sfd->sew == 0L ) { dbus_mgr_log_err("dbus_mgr: out of memory" ); tdelete(sfd, &(mgr->sockets), dbus_mgr_socket_comparator); isc_mem_put(mgr->mctx, sfd, sizeof(DBusMgrSocket)); return; } sev = isc_socket_fd_handle_writes(sfd->sock, sfd->sew ); }else { sev = isc_socket_fd_handle_writes(sfd->sock, sfd->sew ); } } if( (sfd->ser != 0L) || (sfd->sew != 0L) ) { if( sfd->sel == 0L ) { sfd->sel = (isc_socketevent_t *) isc_event_allocate ( mgr->mctx, mgr->task, ISC_SOCKEVENT_SELECTED, dbus_mgr_watches_selected, mgr, sizeof(isc_socketevent_t) ); if( sfd->sel == 0L ) { dbus_mgr_log_err("dbus_mgr: out of memory" ); tdelete(sfd, &(mgr->sockets), dbus_mgr_socket_comparator); isc_mem_put(mgr->mctx, sfd, sizeof(DBusMgrSocket)); return; } sev = isc_socket_fd_handle_selected(sfd->sock, sfd->sel ); }else { sev = isc_socket_fd_handle_selected(sfd->sock, sfd->sel); } } }else { dbus_mgr_log_dbg("watch %d disabled",fd); if(flags & WATCH_READ) { sev = isc_socket_fd_handle_reads( sfd->sock, 0L ); if( sev != 0L ) { pev[0]=(isc_event_t*)sev; isc_event_free(pev); } sfd->ser = 0L; } if( flags & WATCH_WRITE ) { sev = isc_socket_fd_handle_writes( sfd->sock, 0L ); if( sev != 0L ) { pev[0]=(isc_event_t*)sev; isc_event_free(pev); } sfd->sew = 0L; } if( (sfd->ser == 0L) && (sfd->sew == 0L) ) { sev = isc_socket_fd_handle_selected( sfd->sock, 0L ); if( sev != 0L ) { pev[0]=(isc_event_t*)sev; isc_event_free(pev); } sfd->sel = 0L; tdelete(sfd, &(mgr->sockets), dbus_mgr_socket_comparator); isc_mem_put(mgr->mctx, sfd, sizeof(DBusMgrSocket)); } } } static void dbus_mgr_close_socket( const void *p, const VISIT which, const int level) { DBusMgrSocket *const*spp=p, *sfd; isc_event_t *ev ; int i = level ? 0 :1; i &= i; if( (spp==0L) || ((sfd = *spp)==0L) ||((which != leaf) && (which != postorder)) ) return; if( sfd->ser != 0L ) { ev = (isc_event_t *)isc_socket_fd_handle_reads(sfd->sock, 0); if( ev != 0L ) isc_event_free((isc_event_t **)&ev); sfd->ser = 0L; } if( sfd->sew != 0L ) { ev = (isc_event_t *)isc_socket_fd_handle_writes(sfd->sock, 0); if( ev != 0L ) isc_event_free((isc_event_t **)&ev); sfd->sew = 0L; } if( sfd->sel != 0L ) { ev = (isc_event_t *)isc_socket_fd_handle_selected(sfd->sock, 0); if( ev != 0L ) isc_event_free((isc_event_t **)&ev); sfd->sel = 0L; dbus_mgr_log_dbg("CLOSED socket %d", sfd->fd); } } static void dbus_mgr_destroy_socket( void *p ) { DBusMgrSocket *sfd = p; isc_mem_put( sfd->mgr->mctx, sfd, sizeof(DBusMgrSocket) ); }