int snmp_msg_write(pool *p, unsigned char **buf, size_t *buflen, char *community, unsigned int community_len, long snmp_version, struct snmp_pdu *pdu) { unsigned char asn1_type; unsigned int asn1_len; unsigned char *msg_ptr, *msg_hdr_start, *msg_hdr_end; size_t msg_hdr_startlen, msg_len; int res; if (p == NULL || buf == NULL || buflen == NULL || community == NULL || pdu == NULL) { errno = EINVAL; return -1; } msg_ptr = msg_hdr_start = *buf; msg_hdr_startlen = *buflen; asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT); asn1_len = 0; res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_len, 0); if (res < 0) { return -1; } msg_hdr_end = *buf; asn1_type = (SNMP_ASN1_CLASS_UNIVERSAL|SNMP_ASN1_PRIMITIVE|SNMP_ASN1_TYPE_INTEGER); res = snmp_asn1_write_int(p, buf, buflen, asn1_type, snmp_version, 0); if (res < 0) { return -1; } asn1_type = (SNMP_ASN1_CLASS_UNIVERSAL|SNMP_ASN1_PRIMITIVE|SNMP_ASN1_TYPE_OCTETSTRING); res = snmp_asn1_write_string(p, buf, buflen, asn1_type, community, community_len); if (res < 0) { return -1; } if (pdu != NULL) { res = snmp_pdu_write(p, buf, buflen, pdu, snmp_version); if (res < 0) { return -1; } } /* Calculate the full message length, for use later. */ msg_len = (*buf - msg_hdr_start); /* Having written out the entire message now, we can go back and fill * in the appropriate length in the header. */ asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT); asn1_len = (*buf - msg_hdr_end); pr_trace_msg(trace_channel, 18, "updating SNMP message header to have length %u", asn1_len); res = snmp_asn1_write_header(p, &msg_hdr_start, &msg_hdr_startlen, asn1_type, asn1_len, 0); if (res < 0) { return -1; } /* XXX This is a bit of a hack here. We started with a buflen, and steadily * decremented that value as we wrote data into the buffer. * * However, buflen needs to the amount of data IN the buffer once we return * the caller, NOT the amount of data REMAINING in the buffer. So we * cheat here. * * We also cheat by resetting buf to point to the start of the message. */ *buflen = msg_len; *buf = msg_ptr; return 0; }
int snmp_pdu_write(pool *p, unsigned char **buf, size_t *buflen, struct snmp_pdu *pdu, long snmp_version) { unsigned char asn1_type, *pdu_hdr_start, *pdu_hdr_end; size_t pdu_hdr_startlen; unsigned int asn1_len; int flags, res; pr_trace_msg(trace_channel, 19, "writing %s PDU (0x%02x)", snmp_pdu_get_request_type_desc(pdu->request_type), pdu->request_type); /* Since the "type" in this header is the PDU request type, the trace logging * of the ASN.1 type will be wrong. That being the case, simply tell the * writers to not trace log that wrong invalid ASN.1 type. Makes the * trace logging confusing and incorrect. */ flags = SNMP_ASN1_FL_NO_TRACE_TYPESTR; asn1_type = pdu->request_type; asn1_len = 0; pdu_hdr_start = *buf; pdu_hdr_startlen = *buflen; res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_len, flags); if (res < 0) { return -1; } pdu_hdr_end = *buf; switch (pdu->request_type) { case SNMP_PDU_GETBULK: asn1_type = (SNMP_ASN1_CLASS_UNIVERSAL|SNMP_ASN1_PRIMITIVE|SNMP_ASN1_TYPE_INTEGER); /* Request ID */ pr_trace_msg(trace_channel, 19, "writing PDU request ID: %ld", pdu->request_id); res = snmp_asn1_write_int(p, buf, buflen, asn1_type, pdu->request_id, 0); if (res < 0) { return -1; } /* Non-repeaters */ pr_trace_msg(trace_channel, 19, "writing PDU non-repeaters: %ld", pdu->non_repeaters); res = snmp_asn1_write_int(p, buf, buflen, asn1_type, pdu->non_repeaters, 0); if (res < 0) { return -1; } /* Max-repetitions */ pr_trace_msg(trace_channel, 19, "writing PDU max-repetitions: %ld", pdu->max_repetitions); res = snmp_asn1_write_int(p, buf, buflen, asn1_type, pdu->max_repetitions, 0); if (res < 0) { return -1; } /* XXX write varlist? */ break; default: /* "Normal" PDU formatting. */ asn1_type = (SNMP_ASN1_CLASS_UNIVERSAL|SNMP_ASN1_PRIMITIVE|SNMP_ASN1_TYPE_INTEGER); /* Request ID */ pr_trace_msg(trace_channel, 19, "writing PDU request ID: %ld", pdu->request_id); res = snmp_asn1_write_int(p, buf, buflen, asn1_type, pdu->request_id, 0); if (res < 0) { return -1; } /* Error Status/Code */ pr_trace_msg(trace_channel, 19, "writing PDU error status/code: %ld", pdu->err_code); res = snmp_asn1_write_int(p, buf, buflen, asn1_type, pdu->err_code, 0); if (res < 0) { return -1; } /* Error Index */ pr_trace_msg(trace_channel, 19, "writing PDU error index: %ld", pdu->err_idx); res = snmp_asn1_write_int(p, buf, buflen, asn1_type, pdu->err_idx, 0); if (res < 0) { return -1; } /* Variable bindings list */ pr_trace_msg(trace_channel, 19, "writing PDU variable binding list: (%u %s)", pdu->varlistlen, pdu->varlistlen != 1 ? "variables" : "variable"); res = snmp_smi_write_vars(p, buf, buflen, pdu->varlist, snmp_version); if (res < 0) { return -1; } break; } /* Rewrite the PDU header, this time with the length of the entire PDU. */ asn1_type = pdu->request_type; asn1_len = (*buf - pdu_hdr_end); pr_trace_msg(trace_channel, 18, "updating PDU header to have length %u", asn1_len); res = snmp_asn1_write_header(p, &pdu_hdr_start, &pdu_hdr_startlen, asn1_type, asn1_len, flags); if (res < 0) { return -1; } return 0; }