guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len, GAttribResultFunc func, gpointer user_data, GDestroyNotify notify) { struct command *c; GQueue *queue; uint8_t opcode; if (attrib->stale) return 0; c = g_try_new0(struct command, 1); if (c == NULL) return 0; opcode = pdu[0]; c->opcode = opcode; c->expected = opcode2expected(opcode); c->pdu = g_malloc(len); memcpy(c->pdu, pdu, len); c->len = len; c->func = func; c->user_data = user_data; c->notify = notify; if (is_response(opcode)) queue = attrib->responses; else queue = attrib->requests; if (id) { c->id = id; if (!is_response(opcode)) g_queue_push_head(queue, c); else /* Don't re-order responses even if an ID is given */ g_queue_push_tail(queue, c); } else { c->id = ++attrib->next_cmd_id; g_queue_push_tail(queue, c); } /* * If a command was added to the queue and it was empty before, wake up * the sender. If the sender was already woken up by the second queue, * wake_up_sender will just return. */ if (g_queue_get_length(queue) == 1) wake_up_sender(attrib); return c->id; }
static void hf_call_prefix_handler(struct hfp_hf *hfp, const char *data) { struct event_handler *handler; const char *separators = ";:\0"; struct hfp_context context; enum hfp_result result; enum hfp_error cme_err; char lookup_prefix[18]; uint8_t pref_len = 0; const char *prefix; int i; context.offset = 0; context.data = data; hf_skip_whitespace(&context); if (strlen(data + context.offset) < 2) return; prefix = data + context.offset; pref_len = strcspn(prefix, separators); if (pref_len > 17 || pref_len < 2) return; for (i = 0; i < pref_len; i++) lookup_prefix[i] = toupper(prefix[i]); lookup_prefix[pref_len] = '\0'; context.offset += pref_len + 1; if (is_response(lookup_prefix, &result, &cme_err, &context)) { struct cmd_response *cmd; cmd = queue_peek_head(hfp->cmd_queue); if (!cmd) return; cmd->resp_cb(result, cme_err, cmd->user_data); queue_remove(hfp->cmd_queue, cmd); free(cmd); hf_wakeup_writer(hfp); return; } handler = queue_find(hfp->event_handlers, match_handler_event_prefix, lookup_prefix); if (!handler) return; handler->callback(&context, handler->user_data); }
void local_actor::forward_message(const actor_ptr& new_receiver) { if (new_receiver == nullptr) { return; } auto& from = last_sender(); auto id = m_current_node->mid; if (id.valid() == false || id.is_response()) { new_receiver->enqueue(from.get(), m_current_node->msg); } else { new_receiver->sync_enqueue(from.get(), id, m_current_node->msg); // treat this message as asynchronous message from now on m_current_node->mid = message_id_t(); } }
msg_type filter_msg(Actor* self, mailbox_element* node) { const message& msg = node->msg; auto mid = node->mid; if (msg.size() == 1) { if (msg.type_at(0)->equal_to(typeid(exit_msg))) { auto& em = msg.get_as<exit_msg>(0); CAF_REQUIRE(!mid.valid()); // make sure to get rid of attachables if they're no longer needed self->unlink_from(em.source); if (self->trap_exit() == false) { if (em.reason != exit_reason::normal) { self->quit(em.reason); return msg_type::non_normal_exit; } return msg_type::normal_exit; } } else if (msg.type_at(0)->equal_to(typeid(timeout_msg))) { auto& tm = msg.get_as<timeout_msg>(0); auto tid = tm.timeout_id; CAF_REQUIRE(!mid.valid()); if (self->is_active_timeout(tid)) { return msg_type::timeout; } return self->waits_for_timeout(tid) ? msg_type::inactive_timeout : msg_type::expired_timeout; } else if (msg.type_at(0)->equal_to(typeid(sync_timeout_msg)) && mid.is_response()) { return msg_type::timeout_response; } } if (mid.is_response()) { return self->awaits(mid) ? msg_type::sync_response : msg_type::expired_sync_response; } return msg_type::ordinary; }
void local_actor::reply_message(any_tuple&& what) { auto& whom = last_sender(); if (whom == nullptr) { return; } auto id = m_current_node->mid; if (id.valid() == false || id.is_response()) { send_message(whom.get(), std::move(what)); } else if (chaining_enabled()) { if (whom->chained_sync_enqueue(this, id.response_id(), std::move(what))) { m_chained_actor = whom; } } else whom->sync_enqueue(this, id.response_id(), std::move(what)); }
static bool match_event(struct event *evt, const uint8_t *pdu, gsize len) { guint16 handle; if (evt->expected == GATTRIB_ALL_EVENTS) return true; if (!is_response(pdu[0]) && evt->expected == GATTRIB_ALL_REQS) return true; if (evt->expected == pdu[0] && evt->handle == GATTRIB_ALL_HANDLES) return true; if (len < 3) return false; handle = att_get_u16(&pdu[1]); if (evt->expected == pdu[0] && evt->handle == handle) return true; return false; }
isc_result_t dns_tsig_sign(dns_message_t *msg) { dns_tsigkey_t *key; dns_rdata_any_tsig_t tsig, querytsig; unsigned char data[128]; isc_buffer_t databuf, sigbuf; isc_buffer_t *dynbuf; dns_name_t *owner; dns_rdata_t *rdata = NULL; dns_rdatalist_t *datalist; dns_rdataset_t *dataset; isc_region_t r; isc_stdtime_t now; isc_mem_t *mctx; dst_context_t *ctx = NULL; isc_result_t ret; unsigned char badtimedata[BADTIMELEN]; unsigned int sigsize = 0; isc_boolean_t response = is_response(msg); REQUIRE(msg != NULL); REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg))); /* * If this is a response, there should be a query tsig. */ if (response && msg->querytsig == NULL) return (DNS_R_EXPECTEDTSIG); dynbuf = NULL; mctx = msg->mctx; key = dns_message_gettsigkey(msg); tsig.mctx = mctx; tsig.common.rdclass = dns_rdataclass_any; tsig.common.rdtype = dns_rdatatype_tsig; ISC_LINK_INIT(&tsig.common, link); dns_name_init(&tsig.algorithm, NULL); dns_name_clone(key->algorithm, &tsig.algorithm); isc_stdtime_get(&now); tsig.timesigned = now + msg->timeadjust; tsig.fudge = DNS_TSIG_FUDGE; tsig.originalid = msg->id; isc_buffer_init(&databuf, data, sizeof(data)); if (response) tsig.error = msg->querytsigstatus; else tsig.error = dns_rcode_noerror; if (tsig.error != dns_tsigerror_badtime) { tsig.otherlen = 0; tsig.other = NULL; } else { isc_buffer_t otherbuf; tsig.otherlen = BADTIMELEN; tsig.other = badtimedata; isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen); isc_buffer_putuint48(&otherbuf, tsig.timesigned); } if (key->key != NULL && tsig.error != dns_tsigerror_badsig) { unsigned char header[DNS_MESSAGE_HEADERLEN]; isc_buffer_t headerbuf; isc_uint16_t digestbits; ret = dst_context_create3(key->key, mctx, DNS_LOGCATEGORY_DNSSEC, ISC_TRUE, &ctx); if (ret != ISC_R_SUCCESS) return (ret); /* * If this is a response, digest the query signature. */ if (response) { dns_rdata_t querytsigrdata = DNS_RDATA_INIT; ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) goto cleanup_context; dns_rdataset_current(msg->querytsig, &querytsigrdata); ret = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_putuint16(&databuf, querytsig.siglen); if (isc_buffer_availablelength(&databuf) < querytsig.siglen) { ret = ISC_R_NOSPACE; goto cleanup_context; } isc_buffer_putmem(&databuf, querytsig.signature, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } #if defined(__clang__) && \ ( __clang_major__ < 3 || \ (__clang_major__ == 3 && __clang_minor__ < 2) || \ (__clang_major__ == 4 && __clang_minor__ < 2)) /* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */ else memset(&querytsig, 0, sizeof(querytsig)); #endif /* * Digest the header. */ isc_buffer_init(&headerbuf, header, sizeof(header)); dns_message_renderheader(msg, &headerbuf); isc_buffer_usedregion(&headerbuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the remainder of the message. */ isc_buffer_usedregion(msg->buffer, &r); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (msg->tcp_continuation == 0) { /* * Digest the name, class, ttl, alg. */ dns_name_toregion(&key->name, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_clear(&databuf); isc_buffer_putuint16(&databuf, dns_rdataclass_any); isc_buffer_putuint32(&databuf, 0); /* ttl */ isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; dns_name_toregion(&tsig.algorithm, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } /* Digest the timesigned and fudge */ isc_buffer_clear(&databuf); if (tsig.error == dns_tsigerror_badtime) { INSIST(response); tsig.timesigned = querytsig.timesigned; } isc_buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (msg->tcp_continuation == 0) { /* * Digest the error and other data length. */ isc_buffer_clear(&databuf); isc_buffer_putuint16(&databuf, tsig.error); isc_buffer_putuint16(&databuf, tsig.otherlen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest other data. */ if (tsig.otherlen > 0) { r.length = tsig.otherlen; r.base = tsig.other; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } } ret = dst_key_sigsize(key->key, &sigsize); if (ret != ISC_R_SUCCESS) goto cleanup_context; tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize); if (tsig.signature == NULL) { ret = ISC_R_NOMEMORY; goto cleanup_context; } isc_buffer_init(&sigbuf, tsig.signature, sigsize); ret = dst_context_sign(ctx, &sigbuf); if (ret != ISC_R_SUCCESS) goto cleanup_signature; dst_context_destroy(&ctx); digestbits = dst_key_getbits(key->key); if (digestbits != 0) { unsigned int bytes = (digestbits + 1) / 8; if (response && bytes < querytsig.siglen) bytes = querytsig.siglen; if (bytes > isc_buffer_usedlength(&sigbuf)) bytes = isc_buffer_usedlength(&sigbuf); tsig.siglen = bytes; } else tsig.siglen = isc_buffer_usedlength(&sigbuf); } else { tsig.siglen = 0; tsig.signature = NULL; } ret = dns_message_gettemprdata(msg, &rdata); if (ret != ISC_R_SUCCESS) goto cleanup_signature; ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512); if (ret != ISC_R_SUCCESS) goto cleanup_rdata; ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &tsig, dynbuf); if (ret != ISC_R_SUCCESS) goto cleanup_dynbuf; dns_message_takebuffer(msg, &dynbuf); if (tsig.signature != NULL) { isc_mem_put(mctx, tsig.signature, sigsize); tsig.signature = NULL; } owner = NULL; ret = dns_message_gettempname(msg, &owner); if (ret != ISC_R_SUCCESS) goto cleanup_rdata; dns_name_init(owner, NULL); ret = dns_name_dup(&key->name, msg->mctx, owner); if (ret != ISC_R_SUCCESS) goto cleanup_owner; datalist = NULL; ret = dns_message_gettemprdatalist(msg, &datalist); if (ret != ISC_R_SUCCESS) goto cleanup_owner; dataset = NULL; ret = dns_message_gettemprdataset(msg, &dataset); if (ret != ISC_R_SUCCESS) goto cleanup_rdatalist; datalist->rdclass = dns_rdataclass_any; datalist->type = dns_rdatatype_tsig; datalist->covers = 0; datalist->ttl = 0; ISC_LIST_INIT(datalist->rdata); ISC_LIST_APPEND(datalist->rdata, rdata, link); RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) == ISC_R_SUCCESS); msg->tsig = dataset; msg->tsigname = owner; /* Windows does not like the tsig name being compressed. */ msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; return (ISC_R_SUCCESS); cleanup_rdatalist: dns_message_puttemprdatalist(msg, &datalist); cleanup_owner: dns_message_puttempname(msg, &owner); goto cleanup_rdata; cleanup_dynbuf: isc_buffer_free(&dynbuf); cleanup_rdata: dns_message_puttemprdata(msg, &rdata); cleanup_signature: if (tsig.signature != NULL) isc_mem_put(mctx, tsig.signature, sigsize); cleanup_context: if (ctx != NULL) dst_context_destroy(&ctx); return (ret); }
static isc_result_t tsig_verify_tcp(isc_buffer_t *source, dns_message_t *msg) { dns_rdata_any_tsig_t tsig, querytsig; isc_region_t r, source_r, header_r, sig_r; isc_buffer_t databuf; unsigned char data[32]; dns_name_t *keyname; dns_rdata_t rdata = DNS_RDATA_INIT; isc_stdtime_t now; isc_result_t ret; dns_tsigkey_t *tsigkey; dst_key_t *key = NULL; unsigned char header[DNS_MESSAGE_HEADERLEN]; isc_uint16_t addcount, id; isc_boolean_t has_tsig = ISC_FALSE; isc_mem_t *mctx; REQUIRE(source != NULL); REQUIRE(msg != NULL); REQUIRE(dns_message_gettsigkey(msg) != NULL); REQUIRE(msg->tcp_continuation == 1); REQUIRE(msg->querytsig != NULL); if (!is_response(msg)) return (DNS_R_EXPECTEDRESPONSE); mctx = msg->mctx; tsigkey = dns_message_gettsigkey(msg); /* * Extract and parse the previous TSIG */ ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) return (ret); dns_rdataset_current(msg->querytsig, &rdata); ret = dns_rdata_tostruct(&rdata, &querytsig, NULL); if (ret != ISC_R_SUCCESS) return (ret); dns_rdata_reset(&rdata); /* * If there is a TSIG in this message, do some checks. */ if (msg->tsig != NULL) { has_tsig = ISC_TRUE; keyname = msg->tsigname; ret = dns_rdataset_first(msg->tsig); if (ret != ISC_R_SUCCESS) goto cleanup_querystruct; dns_rdataset_current(msg->tsig, &rdata); ret = dns_rdata_tostruct(&rdata, &tsig, NULL); if (ret != ISC_R_SUCCESS) goto cleanup_querystruct; /* * Do the key name and algorithm match that of the query? */ if (!dns_name_equal(keyname, &tsigkey->name) || !dns_name_equal(&tsig.algorithm, &querytsig.algorithm)) { msg->tsigstatus = dns_tsigerror_badkey; ret = DNS_R_TSIGVERIFYFAILURE; tsig_log(msg->tsigkey, 2, "key name and algorithm do not match"); goto cleanup_querystruct; } /* * Is the time ok? */ isc_stdtime_get(&now); if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { msg->tsigstatus = dns_tsigerror_badtime; tsig_log(msg->tsigkey, 2, "signature has expired"); ret = DNS_R_CLOCKSKEW; goto cleanup_querystruct; } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) { msg->tsigstatus = dns_tsigerror_badtime; tsig_log(msg->tsigkey, 2, "signature is in the future"); ret = DNS_R_CLOCKSKEW; goto cleanup_querystruct; } } key = tsigkey->key; if (msg->tsigctx == NULL) { ret = dst_context_create3(key, mctx, DNS_LOGCATEGORY_DNSSEC, ISC_FALSE, &msg->tsigctx); if (ret != ISC_R_SUCCESS) goto cleanup_querystruct; /* * Digest the length of the query signature */ isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint16(&databuf, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(msg->tsigctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the data of the query signature */ if (querytsig.siglen > 0) { r.length = querytsig.siglen; r.base = querytsig.signature; ret = dst_context_adddata(msg->tsigctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } } /* * Extract the header. */ isc_buffer_usedregion(source, &r); memmove(header, r.base, DNS_MESSAGE_HEADERLEN); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); /* * Decrement the additional field counter if necessary. */ if (has_tsig) { memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2); addcount = htons((isc_uint16_t)(ntohs(addcount) - 1)); memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2); } /* * Put in the original id. */ /* XXX Can TCP transfers be forwarded? How would that work? */ if (has_tsig) { id = htons(tsig.originalid); memmove(&header[0], &id, 2); } /* * Digest the modified header. */ header_r.base = (unsigned char *) header; header_r.length = DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(msg->tsigctx, &header_r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest all non-TSIG records. */ isc_buffer_usedregion(source, &source_r); r.base = source_r.base + DNS_MESSAGE_HEADERLEN; if (has_tsig) r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; else r.length = source_r.length - DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(msg->tsigctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the time signed and fudge. */ if (has_tsig) { isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(msg->tsigctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; sig_r.base = tsig.signature; sig_r.length = tsig.siglen; if (tsig.siglen == 0) { if (tsig.error != dns_rcode_noerror) { if (tsig.error == dns_tsigerror_badtime) ret = DNS_R_CLOCKSKEW; else ret = DNS_R_TSIGERRORSET; } else { tsig_log(msg->tsigkey, 2, "signature is empty"); ret = DNS_R_TSIGVERIFYFAILURE; } goto cleanup_context; } ret = dst_context_verify(msg->tsigctx, &sig_r); if (ret == DST_R_VERIFYFAILURE) { msg->tsigstatus = dns_tsigerror_badsig; tsig_log(msg->tsigkey, 2, "signature failed to verify(2)"); ret = DNS_R_TSIGVERIFYFAILURE; goto cleanup_context; } else if (ret != ISC_R_SUCCESS) goto cleanup_context; dst_context_destroy(&msg->tsigctx); } msg->tsigstatus = dns_rcode_noerror; return (ISC_R_SUCCESS); cleanup_context: dst_context_destroy(&msg->tsigctx); cleanup_querystruct: dns_rdata_freestruct(&querytsig); return (ret); }
isc_result_t dns_tsig_verify(isc_buffer_t *source, dns_message_t *msg, dns_tsig_keyring_t *ring1, dns_tsig_keyring_t *ring2) { dns_rdata_any_tsig_t tsig, querytsig; isc_region_t r, source_r, header_r, sig_r; isc_buffer_t databuf; unsigned char data[32]; dns_name_t *keyname; dns_rdata_t rdata = DNS_RDATA_INIT; isc_stdtime_t now; isc_result_t ret; dns_tsigkey_t *tsigkey; dst_key_t *key = NULL; unsigned char header[DNS_MESSAGE_HEADERLEN]; dst_context_t *ctx = NULL; isc_mem_t *mctx; isc_uint16_t addcount, id; unsigned int siglen; unsigned int alg; isc_boolean_t response; REQUIRE(source != NULL); REQUIRE(DNS_MESSAGE_VALID(msg)); tsigkey = dns_message_gettsigkey(msg); response = is_response(msg); REQUIRE(tsigkey == NULL || VALID_TSIG_KEY(tsigkey)); msg->verify_attempted = 1; if (msg->tcp_continuation) { if (tsigkey == NULL || msg->querytsig == NULL) return (DNS_R_UNEXPECTEDTSIG); return (tsig_verify_tcp(source, msg)); } /* * There should be a TSIG record... */ if (msg->tsig == NULL) return (DNS_R_EXPECTEDTSIG); /* * If this is a response and there's no key or query TSIG, there * shouldn't be one on the response. */ if (response && (tsigkey == NULL || msg->querytsig == NULL)) return (DNS_R_UNEXPECTEDTSIG); mctx = msg->mctx; /* * If we're here, we know the message is well formed and contains a * TSIG record. */ keyname = msg->tsigname; ret = dns_rdataset_first(msg->tsig); if (ret != ISC_R_SUCCESS) return (ret); dns_rdataset_current(msg->tsig, &rdata); ret = dns_rdata_tostruct(&rdata, &tsig, NULL); if (ret != ISC_R_SUCCESS) return (ret); dns_rdata_reset(&rdata); if (response) { ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) return (ret); dns_rdataset_current(msg->querytsig, &rdata); ret = dns_rdata_tostruct(&rdata, &querytsig, NULL); if (ret != ISC_R_SUCCESS) return (ret); } #if defined(__clang__) && \ ( __clang_major__ < 3 || \ (__clang_major__ == 3 && __clang_minor__ < 2) || \ (__clang_major__ == 4 && __clang_minor__ < 2)) /* false positive: http://llvm.org/bugs/show_bug.cgi?id=14461 */ else memset(&querytsig, 0, sizeof(querytsig)); #endif /* * Do the key name and algorithm match that of the query? */ if (response && (!dns_name_equal(keyname, &tsigkey->name) || !dns_name_equal(&tsig.algorithm, &querytsig.algorithm))) { msg->tsigstatus = dns_tsigerror_badkey; tsig_log(msg->tsigkey, 2, "key name and algorithm do not match"); return (DNS_R_TSIGVERIFYFAILURE); } /* * Get the current time. */ isc_stdtime_get(&now); /* * Find dns_tsigkey_t based on keyname. */ if (tsigkey == NULL) { ret = ISC_R_NOTFOUND; if (ring1 != NULL) ret = dns_tsigkey_find(&tsigkey, keyname, &tsig.algorithm, ring1); if (ret == ISC_R_NOTFOUND && ring2 != NULL) ret = dns_tsigkey_find(&tsigkey, keyname, &tsig.algorithm, ring2); if (ret != ISC_R_SUCCESS) { msg->tsigstatus = dns_tsigerror_badkey; ret = dns_tsigkey_create(keyname, &tsig.algorithm, NULL, 0, ISC_FALSE, NULL, now, now, mctx, NULL, &msg->tsigkey); if (ret != ISC_R_SUCCESS) return (ret); tsig_log(msg->tsigkey, 2, "unknown key"); return (DNS_R_TSIGVERIFYFAILURE); } msg->tsigkey = tsigkey; } key = tsigkey->key; /* * Is the time ok? */ if (now + msg->timeadjust > tsig.timesigned + tsig.fudge) { msg->tsigstatus = dns_tsigerror_badtime; tsig_log(msg->tsigkey, 2, "signature has expired"); return (DNS_R_CLOCKSKEW); } else if (now + msg->timeadjust < tsig.timesigned - tsig.fudge) { msg->tsigstatus = dns_tsigerror_badtime; tsig_log(msg->tsigkey, 2, "signature is in the future"); return (DNS_R_CLOCKSKEW); } /* * Check digest length. */ alg = dst_key_alg(key); ret = dst_key_sigsize(key, &siglen); if (ret != ISC_R_SUCCESS) return (ret); if (alg == DST_ALG_HMACMD5 || alg == DST_ALG_HMACSHA1 || alg == DST_ALG_HMACSHA224 || alg == DST_ALG_HMACSHA256 || alg == DST_ALG_HMACSHA384 || alg == DST_ALG_HMACSHA512) { isc_uint16_t digestbits = dst_key_getbits(key); if (tsig.siglen > siglen) { tsig_log(msg->tsigkey, 2, "signature length too big"); return (DNS_R_FORMERR); } if (tsig.siglen > 0 && (tsig.siglen < 10 || tsig.siglen < ((siglen + 1) / 2))) { tsig_log(msg->tsigkey, 2, "signature length below minimum"); return (DNS_R_FORMERR); } if (tsig.siglen > 0 && digestbits != 0 && tsig.siglen < ((digestbits + 1) / 8)) { msg->tsigstatus = dns_tsigerror_badtrunc; tsig_log(msg->tsigkey, 2, "truncated signature length too small"); return (DNS_R_TSIGVERIFYFAILURE); } if (tsig.siglen > 0 && digestbits == 0 && tsig.siglen < siglen) { msg->tsigstatus = dns_tsigerror_badtrunc; tsig_log(msg->tsigkey, 2, "signature length too small"); return (DNS_R_TSIGVERIFYFAILURE); } } if (tsig.siglen > 0) { sig_r.base = tsig.signature; sig_r.length = tsig.siglen; ret = dst_context_create3(key, mctx, DNS_LOGCATEGORY_DNSSEC, ISC_FALSE, &ctx); if (ret != ISC_R_SUCCESS) return (ret); if (response) { isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint16(&databuf, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (querytsig.siglen > 0) { r.length = querytsig.siglen; r.base = querytsig.signature; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } } /* * Extract the header. */ isc_buffer_usedregion(source, &r); memmove(header, r.base, DNS_MESSAGE_HEADERLEN); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); /* * Decrement the additional field counter. */ memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2); addcount = htons((isc_uint16_t)(ntohs(addcount) - 1)); memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2); /* * Put in the original id. */ id = htons(tsig.originalid); memmove(&header[0], &id, 2); /* * Digest the modified header. */ header_r.base = (unsigned char *) header; header_r.length = DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(ctx, &header_r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest all non-TSIG records. */ isc_buffer_usedregion(source, &source_r); r.base = source_r.base + DNS_MESSAGE_HEADERLEN; r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the key name. */ dns_name_toregion(&tsigkey->name, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_init(&databuf, data, sizeof(data)); isc_buffer_putuint16(&databuf, tsig.common.rdclass); isc_buffer_putuint32(&databuf, msg->tsig->ttl); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the key algorithm. */ dns_name_toregion(tsigkey->algorithm, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_clear(&databuf); isc_buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_putuint16(&databuf, tsig.error); isc_buffer_putuint16(&databuf, tsig.otherlen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (tsig.otherlen > 0) { r.base = tsig.other; r.length = tsig.otherlen; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } ret = dst_context_verify(ctx, &sig_r); if (ret == DST_R_VERIFYFAILURE) { msg->tsigstatus = dns_tsigerror_badsig; ret = DNS_R_TSIGVERIFYFAILURE; tsig_log(msg->tsigkey, 2, "signature failed to verify(1)"); goto cleanup_context; } else if (ret != ISC_R_SUCCESS) goto cleanup_context; dst_context_destroy(&ctx); } else if (tsig.error != dns_tsigerror_badsig && tsig.error != dns_tsigerror_badkey) { msg->tsigstatus = dns_tsigerror_badsig; tsig_log(msg->tsigkey, 2, "signature was empty"); return (DNS_R_TSIGVERIFYFAILURE); } msg->tsigstatus = dns_rcode_noerror; if (tsig.error != dns_rcode_noerror) { if (tsig.error == dns_tsigerror_badtime) return (DNS_R_CLOCKSKEW); else return (DNS_R_TSIGERRORSET); } msg->verified_sig = 1; return (ISC_R_SUCCESS); cleanup_context: if (ctx != NULL) dst_context_destroy(&ctx); return (ret); }
isc_result_t dns_tsig_sign(dns_message_t *msg) { dns_tsigkey_t *key; dns_rdata_any_tsig_t tsig, querytsig; unsigned char data[128]; isc_buffer_t databuf, sigbuf; isc_buffer_t *dynbuf; dns_name_t *owner; dns_rdata_t *rdata; dns_rdatalist_t *datalist; dns_rdataset_t *dataset; isc_region_t r; isc_stdtime_t now; isc_mem_t *mctx; dst_context_t *ctx = NULL; isc_result_t ret; unsigned char badtimedata[BADTIMELEN]; unsigned int sigsize = 0; REQUIRE(msg != NULL); REQUIRE(VALID_TSIG_KEY(dns_message_gettsigkey(msg))); /* * If this is a response, there should be a query tsig. */ if (is_response(msg) && msg->querytsig == NULL) return (DNS_R_EXPECTEDTSIG); dynbuf = NULL; mctx = msg->mctx; key = dns_message_gettsigkey(msg); tsig.mctx = mctx; tsig.common.rdclass = dns_rdataclass_any; tsig.common.rdtype = dns_rdatatype_tsig; ISC_LINK_INIT(&tsig.common, link); dns_name_init(&tsig.algorithm, NULL); dns_name_clone(key->algorithm, &tsig.algorithm); isc_stdtime_get(&now); tsig.timesigned = now + msg->timeadjust; tsig.fudge = DNS_TSIG_FUDGE; tsig.originalid = msg->id; isc_buffer_init(&databuf, data, sizeof(data)); if (is_response(msg)) tsig.error = msg->querytsigstatus; else tsig.error = dns_rcode_noerror; if (tsig.error != dns_tsigerror_badtime) { tsig.otherlen = 0; tsig.other = NULL; } else { isc_buffer_t otherbuf; tsig.otherlen = BADTIMELEN; tsig.other = badtimedata; isc_buffer_init(&otherbuf, tsig.other, tsig.otherlen); buffer_putuint48(&otherbuf, tsig.timesigned); } if (key->key != NULL && tsig.error != dns_tsigerror_badsig) { unsigned char header[DNS_MESSAGE_HEADERLEN]; isc_buffer_t headerbuf; ret = dst_context_create(key->key, mctx, &ctx); if (ret != ISC_R_SUCCESS) return (ret); /* * If this is a response, digest the query signature. */ if (is_response(msg)) { dns_rdata_t querytsigrdata = DNS_RDATA_INIT; ret = dns_rdataset_first(msg->querytsig); if (ret != ISC_R_SUCCESS) goto cleanup_context; dns_rdataset_current(msg->querytsig, &querytsigrdata); ret = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_putuint16(&databuf, querytsig.siglen); if (isc_buffer_availablelength(&databuf) < querytsig.siglen) { ret = ISC_R_NOSPACE; goto cleanup_context; } isc_buffer_putmem(&databuf, querytsig.signature, querytsig.siglen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } /* * Digest the header. */ isc_buffer_init(&headerbuf, header, sizeof(header)); dns_message_renderheader(msg, &headerbuf); isc_buffer_usedregion(&headerbuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the remainder of the message. */ isc_buffer_usedregion(msg->buffer, &r); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (msg->tcp_continuation == 0) { /* * Digest the name, class, ttl, alg. */ dns_name_toregion(&key->name, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; isc_buffer_clear(&databuf); isc_buffer_putuint16(&databuf, dns_rdataclass_any); isc_buffer_putuint32(&databuf, 0); /* ttl */ isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; dns_name_toregion(&tsig.algorithm, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } /* Digest the timesigned and fudge */ isc_buffer_clear(&databuf); if (tsig.error == dns_tsigerror_badtime) tsig.timesigned = querytsig.timesigned; buffer_putuint48(&databuf, tsig.timesigned); isc_buffer_putuint16(&databuf, tsig.fudge); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; if (msg->tcp_continuation == 0) { /* * Digest the error and other data length. */ isc_buffer_clear(&databuf); isc_buffer_putuint16(&databuf, tsig.error); isc_buffer_putuint16(&databuf, tsig.otherlen); isc_buffer_usedregion(&databuf, &r); ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; /* * Digest the error and other data. */ if (tsig.otherlen > 0) { r.length = tsig.otherlen; r.base = tsig.other; ret = dst_context_adddata(ctx, &r); if (ret != ISC_R_SUCCESS) goto cleanup_context; } } ret = dst_key_sigsize(key->key, &sigsize); if (ret != ISC_R_SUCCESS) goto cleanup_context; tsig.signature = (unsigned char *) isc_mem_get(mctx, sigsize); if (tsig.signature == NULL) { ret = ISC_R_NOMEMORY; goto cleanup_context; } isc_buffer_init(&sigbuf, tsig.signature, sigsize); ret = dst_context_sign(ctx, &sigbuf); if (ret != ISC_R_SUCCESS) goto cleanup_signature; dst_context_destroy(&ctx); tsig.siglen = isc_buffer_usedlength(&sigbuf); } else { tsig.siglen = 0; tsig.signature = NULL; } rdata = NULL; ret = dns_message_gettemprdata(msg, &rdata); if (ret != ISC_R_SUCCESS) goto cleanup_signature; ret = isc_buffer_allocate(msg->mctx, &dynbuf, 512); if (ret != ISC_R_SUCCESS) goto cleanup_signature; ret = dns_rdata_fromstruct(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &tsig, dynbuf); if (ret != ISC_R_SUCCESS) goto cleanup_dynbuf; dns_message_takebuffer(msg, &dynbuf); if (tsig.signature != NULL) { isc_mem_put(mctx, tsig.signature, sigsize); tsig.signature = NULL; } owner = NULL; ret = dns_message_gettempname(msg, &owner); if (ret != ISC_R_SUCCESS) goto cleanup_dynbuf; dns_name_init(owner, NULL); ret = dns_name_dup(&key->name, msg->mctx, owner); if (ret != ISC_R_SUCCESS) goto cleanup_owner; datalist = NULL; ret = dns_message_gettemprdatalist(msg, &datalist); if (ret != ISC_R_SUCCESS) goto cleanup_owner; datalist->rdclass = dns_rdataclass_any; datalist->type = dns_rdatatype_tsig; datalist->covers = 0; datalist->ttl = 0; ISC_LIST_INIT(datalist->rdata); ISC_LIST_APPEND(datalist->rdata, rdata, link); dataset = NULL; ret = dns_message_gettemprdataset(msg, &dataset); if (ret != ISC_R_SUCCESS) goto cleanup_owner; dns_rdataset_init(dataset); RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) == ISC_R_SUCCESS); msg->tsig = dataset; msg->tsigname = owner; return (ISC_R_SUCCESS); cleanup_owner: if (owner != NULL) dns_message_puttempname(msg, &owner); cleanup_dynbuf: if (dynbuf != NULL) isc_buffer_free(&dynbuf); cleanup_signature: if (tsig.signature != NULL) isc_mem_put(mctx, tsig.signature, sigsize); cleanup_context: if (ctx != NULL) dst_context_destroy(&ctx); return (ret); }
isc_result_t dns_dnssec_verifymessage(isc_buffer_t *source, dns_message_t *msg, dst_key_t *key) { dns_rdata_sig_t sig; /* SIG(0) */ unsigned char header[DNS_MESSAGE_HEADERLEN]; dns_rdata_t rdata = DNS_RDATA_INIT; isc_region_t r, source_r, sig_r, header_r; isc_stdtime_t now; dst_context_t *ctx = NULL; isc_mem_t *mctx; isc_result_t result; isc_uint16_t addcount; isc_boolean_t signeedsfree = ISC_FALSE; REQUIRE(source != NULL); REQUIRE(msg != NULL); REQUIRE(key != NULL); mctx = msg->mctx; msg->verify_attempted = 1; if (is_response(msg)) { if (msg->query.base == NULL) return (DNS_R_UNEXPECTEDTSIG); } isc_buffer_usedregion(source, &source_r); RETERR(dns_rdataset_first(msg->sig0)); dns_rdataset_current(msg->sig0, &rdata); RETERR(dns_rdata_tostruct(&rdata, &sig, NULL)); signeedsfree = ISC_TRUE; if (sig.labels != 0) { result = DNS_R_SIGINVALID; goto failure; } if (isc_serial_lt(sig.timeexpire, sig.timesigned)) { result = DNS_R_SIGINVALID; msg->sig0status = dns_tsigerror_badtime; goto failure; } isc_stdtime_get(&now); if (isc_serial_lt((isc_uint32_t)now, sig.timesigned)) { result = DNS_R_SIGFUTURE; msg->sig0status = dns_tsigerror_badtime; goto failure; } else if (isc_serial_lt(sig.timeexpire, (isc_uint32_t)now)) { result = DNS_R_SIGEXPIRED; msg->sig0status = dns_tsigerror_badtime; goto failure; } if (!dns_name_equal(dst_key_name(key), &sig.signer)) { result = DNS_R_SIGINVALID; msg->sig0status = dns_tsigerror_badkey; goto failure; } RETERR(dst_context_create(key, mctx, &ctx)); /* * Digest the SIG(0) record, except for the signature. */ dns_rdata_toregion(&rdata, &r); r.length -= sig.siglen; RETERR(dst_context_adddata(ctx, &r)); /* * If this is a response, digest the query. */ if (is_response(msg)) RETERR(dst_context_adddata(ctx, &msg->query)); /* * Extract the header. */ memcpy(header, source_r.base, DNS_MESSAGE_HEADERLEN); /* * Decrement the additional field counter. */ memcpy(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2); addcount = htons((isc_uint16_t)(ntohs(addcount) - 1)); memcpy(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2); /* * Digest the modified header. */ header_r.base = (unsigned char *) header; header_r.length = DNS_MESSAGE_HEADERLEN; RETERR(dst_context_adddata(ctx, &header_r)); /* * Digest all non-SIG(0) records. */ r.base = source_r.base + DNS_MESSAGE_HEADERLEN; r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; RETERR(dst_context_adddata(ctx, &r)); sig_r.base = sig.signature; sig_r.length = sig.siglen; result = dst_context_verify(ctx, &sig_r); if (result != ISC_R_SUCCESS) { msg->sig0status = dns_tsigerror_badsig; goto failure; } msg->verified_sig = 1; dst_context_destroy(&ctx); dns_rdata_freestruct(&sig); return (ISC_R_SUCCESS); failure: if (signeedsfree) dns_rdata_freestruct(&sig); if (ctx != NULL) dst_context_destroy(&ctx); return (result); }
isc_result_t dns_dnssec_signmessage(dns_message_t *msg, dst_key_t *key) { dns_rdata_sig_t sig; /* SIG(0) */ unsigned char data[512]; unsigned char header[DNS_MESSAGE_HEADERLEN]; isc_buffer_t headerbuf, databuf, sigbuf; unsigned int sigsize; isc_buffer_t *dynbuf = NULL; dns_rdata_t *rdata; dns_rdatalist_t *datalist; dns_rdataset_t *dataset; isc_region_t r; isc_stdtime_t now; dst_context_t *ctx = NULL; isc_mem_t *mctx; isc_result_t result; isc_boolean_t signeedsfree = ISC_TRUE; REQUIRE(msg != NULL); REQUIRE(key != NULL); if (is_response(msg)) REQUIRE(msg->query.base != NULL); mctx = msg->mctx; memset(&sig, 0, sizeof(sig)); sig.mctx = mctx; sig.common.rdclass = dns_rdataclass_any; sig.common.rdtype = dns_rdatatype_sig; /* SIG(0) */ ISC_LINK_INIT(&sig.common, link); sig.covered = 0; sig.algorithm = dst_key_alg(key); sig.labels = 0; /* the root name */ sig.originalttl = 0; isc_stdtime_get(&now); sig.timesigned = now - DNS_TSIG_FUDGE; sig.timeexpire = now + DNS_TSIG_FUDGE; sig.keyid = dst_key_id(key); dns_name_init(&sig.signer, NULL); dns_name_clone(dst_key_name(key), &sig.signer); sig.siglen = 0; sig.signature = NULL; isc_buffer_init(&databuf, data, sizeof(data)); RETERR(dst_context_create(key, mctx, &ctx)); /* * Digest the fields of the SIG - we can cheat and use * dns_rdata_fromstruct. Since siglen is 0, the digested data * is identical to dns format. */ RETERR(dns_rdata_fromstruct(NULL, dns_rdataclass_any, dns_rdatatype_sig /* SIG(0) */, &sig, &databuf)); isc_buffer_usedregion(&databuf, &r); RETERR(dst_context_adddata(ctx, &r)); /* * If this is a response, digest the query. */ if (is_response(msg)) RETERR(dst_context_adddata(ctx, &msg->query)); /* * Digest the header. */ isc_buffer_init(&headerbuf, header, sizeof(header)); dns_message_renderheader(msg, &headerbuf); isc_buffer_usedregion(&headerbuf, &r); RETERR(dst_context_adddata(ctx, &r)); /* * Digest the remainder of the message. */ isc_buffer_usedregion(msg->buffer, &r); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); RETERR(dst_context_adddata(ctx, &r)); RETERR(dst_key_sigsize(key, &sigsize)); sig.siglen = sigsize; sig.signature = (unsigned char *) isc_mem_get(mctx, sig.siglen); if (sig.signature == NULL) { result = ISC_R_NOMEMORY; goto failure; } isc_buffer_init(&sigbuf, sig.signature, sig.siglen); RETERR(dst_context_sign(ctx, &sigbuf)); dst_context_destroy(&ctx); rdata = NULL; RETERR(dns_message_gettemprdata(msg, &rdata)); RETERR(isc_buffer_allocate(msg->mctx, &dynbuf, 1024)); RETERR(dns_rdata_fromstruct(rdata, dns_rdataclass_any, dns_rdatatype_sig /* SIG(0) */, &sig, dynbuf)); isc_mem_put(mctx, sig.signature, sig.siglen); signeedsfree = ISC_FALSE; dns_message_takebuffer(msg, &dynbuf); datalist = NULL; RETERR(dns_message_gettemprdatalist(msg, &datalist)); datalist->rdclass = dns_rdataclass_any; datalist->type = dns_rdatatype_sig; /* SIG(0) */ datalist->covers = 0; datalist->ttl = 0; ISC_LIST_INIT(datalist->rdata); ISC_LIST_APPEND(datalist->rdata, rdata, link); dataset = NULL; RETERR(dns_message_gettemprdataset(msg, &dataset)); dns_rdataset_init(dataset); RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) == ISC_R_SUCCESS); msg->sig0 = dataset; return (ISC_R_SUCCESS); failure: if (dynbuf != NULL) isc_buffer_free(&dynbuf); if (signeedsfree) isc_mem_put(mctx, sig.signature, sig.siglen); if (ctx != NULL) dst_context_destroy(&ctx); return (result); }
static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data) { struct _GAttrib *attrib = data; struct command *cmd = NULL; GSList *l; uint8_t buf[512], status; gsize len; GIOStatus iostat; gboolean qempty; if (attrib->timeout_watch > 0) { g_source_remove(attrib->timeout_watch); attrib->timeout_watch = 0; } if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { attrib->read_watch = 0; return FALSE; } memset(buf, 0, sizeof(buf)); iostat = g_io_channel_read_chars(io, (gchar *) buf, sizeof(buf), &len, NULL); if (iostat != G_IO_STATUS_NORMAL) { status = ATT_ECODE_IO; goto done; } for (l = attrib->events; l; l = l->next) { struct event *evt = l->data; if (evt->expected == buf[0] || evt->expected == GATTRIB_ALL_EVENTS || (is_response(buf[0]) == FALSE && evt->expected == GATTRIB_ALL_REQS)) evt->func(buf, len, evt->user_data); } if (is_response(buf[0]) == FALSE) return TRUE; cmd = g_queue_pop_head(attrib->queue); if (cmd == NULL) { /* Keep the watch if we have events to report */ return attrib->events != NULL; } if (buf[0] == ATT_OP_ERROR) { status = buf[4]; goto done; } if (cmd->expected != buf[0]) { status = ATT_ECODE_IO; goto done; } status = 0; done: qempty = attrib->queue == NULL || g_queue_is_empty(attrib->queue); if (cmd) { if (cmd->func) cmd->func(status, buf, len, cmd->user_data); command_destroy(cmd); } if (!qempty) wake_up_sender(attrib); return TRUE; }
inline bool is_request() const { return valid() && ! is_response(); }
static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data) { struct _GAttrib *attrib = data; struct command *cmd = NULL; GSList *l; uint8_t buf[512], status; gsize len; GIOStatus iostat; if (attrib->stale) return FALSE; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { struct command *c; while ((c = g_queue_pop_head(attrib->requests))) { if (c->func) c->func(ATT_ECODE_IO, NULL, 0, c->user_data); command_destroy(c); } attrib->read_watch = 0; return FALSE; } memset(buf, 0, sizeof(buf)); iostat = g_io_channel_read_chars(io, (char *) buf, sizeof(buf), &len, NULL); if (iostat != G_IO_STATUS_NORMAL) { status = ATT_ECODE_IO; goto done; } for (l = attrib->events; l; l = l->next) { struct event *evt = l->data; if (match_event(evt, buf, len)) evt->func(buf, len, evt->user_data); } if (!is_response(buf[0])) return TRUE; if (attrib->timeout_watch > 0) { g_source_remove(attrib->timeout_watch); attrib->timeout_watch = 0; } cmd = g_queue_pop_head(attrib->requests); if (cmd == NULL) { /* Keep the watch if we have events to report */ return attrib->events != NULL; } if (buf[0] == ATT_OP_ERROR) { status = buf[4]; goto done; } if (cmd->expected != buf[0]) { status = ATT_ECODE_IO; goto done; } status = 0; done: if (!g_queue_is_empty(attrib->requests) || !g_queue_is_empty(attrib->responses)) wake_up_sender(attrib); if (cmd) { if (cmd->func) cmd->func(status, buf, len, cmd->user_data); command_destroy(cmd); } return TRUE; }
inline message_id response_id() const { // the response to a response is an asynchronous message if (is_response()) return message_id{0}; return message_id{valid() ? m_value | response_flag_mask : 0}; }