📄 dispatch.c
字号:
if (disp->maxrequests < maxrequests) disp->maxrequests = maxrequests; if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) == 0 && (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) { disp->attributes |= DNS_DISPATCHATTR_NOLISTEN; if (disp->recv_pending != 0) isc_socket_cancel(disp->socket, disp->task, ISC_SOCKCANCEL_RECV); } UNLOCK(&disp->lock); UNLOCK(&mgr->lock); *dispp = disp; return (ISC_R_SUCCESS); } /* * Nope, create one. */ result = dispatch_createudp(mgr, sockmgr, taskmgr, localaddr, maxrequests, attributes, &disp); if (result != ISC_R_SUCCESS) { UNLOCK(&mgr->lock); return (result); } UNLOCK(&mgr->lock); *dispp = disp; return (ISC_R_SUCCESS);}/* * mgr should be locked. */static isc_result_tdispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, isc_taskmgr_t *taskmgr, isc_sockaddr_t *localaddr, unsigned int maxrequests, unsigned int attributes, dns_dispatch_t **dispp){ isc_result_t result; dns_dispatch_t *disp; isc_socket_t *sock; /* * dispatch_allocate() checks mgr for us. */ disp = NULL; result = dispatch_allocate(mgr, maxrequests, &disp); if (result != ISC_R_SUCCESS) return (result); /* * This assumes that the IP stack will *not* quickly reallocate * the same port. If it does continually reallocate the same port * then we need a mechanism to hold all the blacklisted sockets * until we find a usable socket. */ getsocket: result = create_socket(sockmgr, localaddr, &sock); if (result != ISC_R_SUCCESS) goto deallocate_dispatch; if (isc_sockaddr_getport(localaddr) == 0 && blacklisted(mgr, sock)) { isc_socket_detach(&sock); goto getsocket; } disp->socktype = isc_sockettype_udp; disp->socket = sock; disp->local = *localaddr; disp->task = NULL; result = isc_task_create(taskmgr, 0, &disp->task); if (result != ISC_R_SUCCESS) goto kill_socket; disp->ctlevent = isc_event_allocate(mgr->mctx, disp, DNS_EVENT_DISPATCHCONTROL, destroy_disp, disp, sizeof(isc_event_t)); if (disp->ctlevent == NULL) goto kill_task; isc_task_setname(disp->task, "udpdispatch", disp); attributes &= ~DNS_DISPATCHATTR_TCP; attributes |= DNS_DISPATCHATTR_UDP; disp->attributes = attributes; /* * Append it to the dispatcher list. */ ISC_LIST_APPEND(mgr->list, disp, link); mgr_log(mgr, LVL(90), "created UDP dispatcher %p", disp); dispatch_log(disp, LVL(90), "created task %p", disp->task); dispatch_log(disp, LVL(90), "created socket %p", disp->socket); *dispp = disp; return (ISC_R_SUCCESS); /* * Error returns. */ kill_task: isc_task_detach(&disp->task); kill_socket: isc_socket_detach(&disp->socket); deallocate_dispatch: dispatch_free(&disp); return (result);}voiddns_dispatch_attach(dns_dispatch_t *disp, dns_dispatch_t **dispp) { REQUIRE(VALID_DISPATCH(disp)); REQUIRE(dispp != NULL && *dispp == NULL); LOCK(&disp->lock); disp->refcount++; UNLOCK(&disp->lock); *dispp = disp;}/* * It is important to lock the manager while we are deleting the dispatch, * since dns_dispatch_getudp will call dispatch_find, which returns to * the caller a dispatch but does not attach to it until later. _getudp * locks the manager, however, so locking it here will keep us from attaching * to a dispatcher that is in the process of going away. */voiddns_dispatch_detach(dns_dispatch_t **dispp) { dns_dispatch_t *disp; isc_boolean_t killit; REQUIRE(dispp != NULL && VALID_DISPATCH(*dispp)); disp = *dispp; *dispp = NULL; LOCK(&disp->lock); INSIST(disp->refcount > 0); disp->refcount--; killit = ISC_FALSE; if (disp->refcount == 0) { if (disp->recv_pending > 0) isc_socket_cancel(disp->socket, disp->task, ISC_SOCKCANCEL_RECV); disp->shutting_down = 1; } dispatch_log(disp, LVL(90), "detach: refcount %d", disp->refcount); killit = destroy_disp_ok(disp); UNLOCK(&disp->lock); if (killit) isc_task_send(disp->task, &disp->ctlevent);}isc_result_tdns_dispatch_addresponse(dns_dispatch_t *disp, isc_sockaddr_t *dest, isc_task_t *task, isc_taskaction_t action, void *arg, dns_messageid_t *idp, dns_dispentry_t **resp){ dns_dispentry_t *res; unsigned int bucket; dns_messageid_t id; int i; isc_boolean_t ok; dns_qid_t *qid; REQUIRE(VALID_DISPATCH(disp)); REQUIRE(task != NULL); REQUIRE(dest != NULL); REQUIRE(resp != NULL && *resp == NULL); REQUIRE(idp != NULL); LOCK(&disp->lock); if (disp->shutting_down == 1) { UNLOCK(&disp->lock); return (ISC_R_SHUTTINGDOWN); } if (disp->requests >= disp->maxrequests) { UNLOCK(&disp->lock); return (ISC_R_QUOTA); } /* * Try somewhat hard to find an unique ID. */ qid = DNS_QID(disp); LOCK(&qid->lock); id = dns_randomid(qid); bucket = dns_hash(qid, dest, id); ok = ISC_FALSE; for (i = 0; i < 64; i++) { if (bucket_search(qid, dest, id, bucket) == NULL) { ok = ISC_TRUE; break; } id += qid->qid_increment; id &= 0x0000ffff; bucket = dns_hash(qid, dest, id); } if (!ok) { UNLOCK(&qid->lock); UNLOCK(&disp->lock); return (ISC_R_NOMORE); } res = isc_mempool_get(disp->mgr->rpool); if (res == NULL) { UNLOCK(&qid->lock); UNLOCK(&disp->lock); return (ISC_R_NOMEMORY); } disp->refcount++; disp->requests++; res->task = NULL; isc_task_attach(task, &res->task); res->disp = disp; res->id = id; res->bucket = bucket; res->host = *dest; res->action = action; res->arg = arg; res->item_out = ISC_FALSE; ISC_LIST_INIT(res->items); ISC_LINK_INIT(res, link); res->magic = RESPONSE_MAGIC; ISC_LIST_APPEND(qid->qid_table[bucket], res, link); UNLOCK(&qid->lock); request_log(disp, res, LVL(90), "attached to task %p", res->task); if (((disp->attributes & DNS_DISPATCHATTR_UDP) != 0) || ((disp->attributes & DNS_DISPATCHATTR_CONNECTED) != 0)) startrecv(disp); UNLOCK(&disp->lock); *idp = id; *resp = res; return (ISC_R_SUCCESS);}voiddns_dispatch_starttcp(dns_dispatch_t *disp) { REQUIRE(VALID_DISPATCH(disp)); dispatch_log(disp, LVL(90), "starttcp %p", disp->task); LOCK(&disp->lock); disp->attributes |= DNS_DISPATCHATTR_CONNECTED; startrecv(disp); UNLOCK(&disp->lock);}voiddns_dispatch_removeresponse(dns_dispentry_t **resp, dns_dispatchevent_t **sockevent){ dns_dispatchmgr_t *mgr; dns_dispatch_t *disp; dns_dispentry_t *res; dns_dispatchevent_t *ev; unsigned int bucket; isc_boolean_t killit; unsigned int n; isc_eventlist_t events; dns_qid_t *qid; REQUIRE(resp != NULL); REQUIRE(VALID_RESPONSE(*resp)); res = *resp; *resp = NULL; disp = res->disp; REQUIRE(VALID_DISPATCH(disp)); mgr = disp->mgr; REQUIRE(VALID_DISPATCHMGR(mgr)); qid = DNS_QID(disp); if (sockevent != NULL) { REQUIRE(*sockevent != NULL); ev = *sockevent; *sockevent = NULL; } else { ev = NULL; } LOCK(&disp->lock); INSIST(disp->requests > 0); disp->requests--; INSIST(disp->refcount > 0); disp->refcount--; killit = ISC_FALSE; if (disp->refcount == 0) { if (disp->recv_pending > 0) isc_socket_cancel(disp->socket, disp->task, ISC_SOCKCANCEL_RECV); disp->shutting_down = 1; } bucket = res->bucket; LOCK(&qid->lock); ISC_LIST_UNLINK(qid->qid_table[bucket], res, link); UNLOCK(&qid->lock); if (ev == NULL && res->item_out) { /* * We've posted our event, but the caller hasn't gotten it * yet. Take it back. */ ISC_LIST_INIT(events); n = isc_task_unsend(res->task, res, DNS_EVENT_DISPATCH, NULL, &events); /* * We had better have gotten it back. */ INSIST(n == 1); ev = (dns_dispatchevent_t *)ISC_LIST_HEAD(events); } if (ev != NULL) { REQUIRE(res->item_out == ISC_TRUE); res->item_out = ISC_FALSE; if (ev->buffer.base != NULL) free_buffer(disp, ev->buffer.base, ev->buffer.length); free_event(disp, ev); } request_log(disp, res, LVL(90), "detaching from task %p", res->task); isc_task_detach(&res->task); /* * Free any buffered requests as well */ ev = ISC_LIST_HEAD(res->items); while (ev != NULL) { ISC_LIST_UNLINK(res->items, ev, ev_link); if (ev->buffer.base != NULL) free_buffer(disp, ev->buffer.base, ev->buffer.length); free_event(disp, ev); ev = ISC_LIST_HEAD(res->items); } res->magic = 0; isc_mempool_put(disp->mgr->rpool, res); if (disp->shutting_down == 1) do_cancel(disp); else startrecv(disp); killit = destroy_disp_ok(disp); UNLOCK(&disp->lock); if (killit) isc_task_send(disp->task, &disp->ctlevent);}static voiddo_cancel(dns_dispatch_t *disp) { dns_dispatchevent_t *ev; dns_dispentry_t *resp; dns_qid_t *qid; if (disp->shutdown_out == 1) return; qid = DNS_QID(disp); /* * Search for the first response handler without packets outstanding. */ LOCK(&qid->lock); for (resp = linear_first(qid); resp != NULL && resp->item_out != ISC_FALSE; /* Empty. */) resp = linear_next(qid, resp); /* * No one to send the cancel event to, so nothing to do. */ if (resp == NULL) goto unlock; /* * Send the shutdown failsafe event to this resp. */ ev = disp->failsafe_ev; ISC_EVENT_INIT(ev, sizeof(*ev), 0, NULL, DNS_EVENT_DISPATCH, resp->action, resp->arg, resp, NULL, NULL); ev->result = disp->shutdown_why; ev->buffer.base = NULL; ev->buffer.length = 0; disp->shutdown_out = 1; request_log(disp, resp, LVL(10), "cancel: failsafe event %p -> task %p", ev, resp->task); resp->item_out = ISC_TRUE; isc_task_send(resp->task, ISC_EVENT_PTR(&ev)); unlock: UNLOCK(&qid->lock);}isc_socket_t *dns_dispatch_getsocket(dns_dispatch_t *disp) { REQUIRE(VALID_DISPATCH(disp)); return (disp->socket);}isc_result_tdns_dispatch_getlocaladdress(dns_dispatch_t *disp, isc_sockaddr_t *addrp) { REQUIRE(VALID_DISPATCH(disp)); REQUIRE(addrp != NULL); if (disp->socktype == isc_sockettype_udp) { *addrp = disp->local; return (ISC_R_SUCCESS); } return (ISC_R_NOTIMPLEMENTED);}voiddns_dispatch_cancel(dns_dispatch_t *disp) { REQUIRE(VALID_DISPATCH(disp)); LOCK(&disp->lock); if (disp->shutting_down == 1) { UNLOCK(&disp->lock); return; } disp->shutdown_why = ISC_R_CANCELED; disp->shutting_down = 1; do_cancel(disp); UNLOCK(&disp->lock); return;}voiddns_dispatch_changeattributes(dns_dispatch_t *disp, unsigned int attributes, unsigned int mask){ REQUIRE(VALID_DISPATCH(disp)); /* XXXMLG * Should check for valid attributes here! */ LOCK(&disp->lock); if ((mask & DNS_DISPATCHATTR_NOLISTEN) != 0) { if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0 && (attributes & DNS_DISPATCHATTR_NOLISTEN) == 0) { disp->attributes &= ~DNS_DISPATCHATTR_NOLISTEN; startrecv(disp); } else if ((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) == 0 && (attributes & DNS_DISPATCHATTR_NOLISTEN) != 0) { disp->attributes |= DNS_DISPATCHATTR_NOLISTEN; if (disp->recv_pending != 0) isc_socket_cancel(disp->socket, disp->task, ISC_SOCKCANCEL_RECV); } } disp->attributes &= ~mask; disp->attributes |= (attributes & mask); UNLOCK(&disp->lock);}voiddns_dispatch_importrecv(dns_dispatch_t *disp, isc_event_t *event) { void *buf; isc_socketevent_t *sevent, *newsevent; REQUIRE(VALID_DISPATCH(disp)); REQUIRE((disp->attributes & DNS_DISPATCHATTR_NOLISTEN) != 0); REQUIRE(event != NULL); sevent = (isc_socketevent_t *)event; INSIST(sevent->n <= disp->mgr->buffersize); newsevent = (isc_socketevent_t *) isc_event_allocate(disp->mgr->mctx, NULL, DNS_EVENT_IMPORTRECVDONE, udp_recv, disp, sizeof(isc_socketevent_t)); if (newsevent == NULL) return; buf = allocate_udp_buffer(disp); if (buf == NULL) { isc_event_free(ISC_EVENT_PTR(&newsevent)); return; } memcpy(buf, sevent->region.base, sevent->n); newsevent->region.base = buf; newsevent->region.length = disp->mgr->buffersize; newsevent->n = sevent->n; newsevent->result = sevent->result; newsevent->address = sevent->address; newsevent->timestamp = sevent->timestamp; newsevent->pktinfo = sevent->pktinfo; newsevent->attributes = sevent->attributes; isc_task_send(disp->task, ISC_EVENT_PTR(&newsevent));}#if 0voiddns_dispatchmgr_dump(dns_dispatchmgr_t *mgr) { dns_dispatch_t *disp; char foo[1024]; disp = ISC_LIST_HEAD(mgr->list); while (disp != NULL) { isc_sockaddr_format(&disp->local, foo, sizeof(foo)); printf("\tdispatch %p, addr %s\n", disp, foo); disp = ISC_LIST_NEXT(disp, link); }}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -