int TnmSnmpEncode(Tcl_Interp *interp, TnmSnmp *session, TnmSnmpPdu *pdu, TnmSnmpRequestProc *proc, ClientData clientData) { int retry = 0, packetlen = 0, code = 0; u_char packet[TNM_SNMP_MAXSIZE]; TnmBer *ber; memset((char *) packet, 0, sizeof(packet)); /* * Some special care must be taken to conform to SNMPv1 sessions: * SNMPv2 getbulk requests must be turned into getnext requests * and SNMPv2 error codes must be mapped on SNMPv1 error codes * (e.g. genErr as nothing more appropriate is available). * * This is based on the mapping presented in Marhall Rose and * Keith McCloghrie: "How to Manage your Network using SNMP" * page 95. */ if (session->version == TNM_SNMPv1) { if (pdu->type == ASN1_SNMP_GETBULK) { pdu->type = ASN1_SNMP_GETNEXT; pdu->errorStatus = TNM_SNMP_NOERROR; pdu->errorIndex = 0; } if (pdu->type == ASN1_SNMP_INFORM || pdu->type == ASN1_SNMP_TRAP2) { pdu->type = ASN1_SNMP_TRAP1; } if (pdu->errorStatus > TNM_SNMP_GENERR) { switch (pdu->errorStatus) { case TNM_SNMP_NOACCESS: case TNM_SNMP_NOCREATION: case TNM_SNMP_AUTHORIZATIONERROR: case TNM_SNMP_NOTWRITABLE: case TNM_SNMP_INCONSISTENTNAME: pdu->errorStatus = TNM_SNMP_NOSUCHNAME; break; case TNM_SNMP_WRONGTYPE: case TNM_SNMP_WRONGLENGTH: case TNM_SNMP_WRONGENCODING: case TNM_SNMP_WRONGVALUE: case TNM_SNMP_INCONSISTENTVALUE: pdu->errorStatus = TNM_SNMP_BADVALUE; break; case TNM_SNMP_RESOURCEUNAVAILABLE: case TNM_SNMP_COMMITFAILED: case TNM_SNMP_UNDOFAILED: pdu->errorStatus = TNM_SNMP_GENERR; break; default: pdu->errorStatus = TNM_SNMP_GENERR; break; } } } /* * Encode message into ASN1 BER transfer syntax. Authentication or * encryption is done within the following procedures if it is an * authentic or private message. */ ber = TnmBerCreate(packet, sizeof(packet)); code = EncodeMessage(interp, session, pdu, ber); if (code != TCL_OK) { TnmBerDelete(ber); return TCL_ERROR; } packetlen = TnmBerSize(ber); TnmBerDelete(ber); switch (pdu->type) { case ASN1_SNMP_GET: tnmSnmpStats.snmpOutGetRequests++; break; case ASN1_SNMP_GETNEXT: tnmSnmpStats.snmpOutGetNexts++; break; case ASN1_SNMP_SET: tnmSnmpStats.snmpOutSetRequests++; break; case ASN1_SNMP_RESPONSE: tnmSnmpStats.snmpOutGetResponses++; break; case ASN1_SNMP_TRAP1: tnmSnmpStats.snmpOutTraps++; break; } /* * Show the contents of the PDU - mostly for debugging. */ TnmSnmpEvalBinding(interp, session, pdu, TNM_SNMP_SEND_EVENT); TnmSnmpDumpPDU(interp, pdu); /* * A trap message or a response? - send it and we are done! */ if (pdu->type == ASN1_SNMP_TRAP1 || pdu->type == ASN1_SNMP_TRAP2 || pdu->type == ASN1_SNMP_RESPONSE || pdu->type == ASN1_SNMP_REPORT) { #ifdef TNM_SNMPv2U if (session->version == TNM_SNMPv2U) { TnmSnmpUsecAuth(session, packet, packetlen); } #endif code = TnmSnmpSend(interp, session, packet, packetlen, &pdu->addr, TNM_SNMP_ASYNC); if (code != TCL_OK) { return TCL_ERROR; } Tcl_ResetResult(interp); return TCL_OK; } /* * Asychronous request: queue request and we are done. */ if (proc) { TnmSnmpRequest *rPtr; rPtr = TnmSnmpCreateRequest(pdu->requestId, packet, packetlen, proc, clientData, interp); TnmSnmpQueueRequest(session, rPtr); Tcl_SetObjResult (interp, Tcl_NewIntObj (pdu->requestId)); return TCL_OK; } /* * Synchronous request: send packet and wait for response. */ for (retry = 0; retry <= session->retries; retry++) { int id, status, index; #ifdef TNM_SNMP_BENCH TnmSnmpMark stats; memset((char *) &stats, 0, sizeof(stats)); #endif repeat: #ifdef TNM_SNMPv2U if (session->version == TNM_SNMPv2U) { TnmSnmpUsecAuth(session, packet, packetlen); } #endif TnmSnmpDelay(session); code = TnmSnmpSend(interp, session, packet, packetlen, &pdu->addr, TNM_SNMP_SYNC); if (code != TCL_OK) { return TCL_ERROR; } #ifdef TNM_SNMP_BENCH if (stats.sendSize == 0) { stats.sendSize = tnmSnmpBenchMark.sendSize; stats.sendTime = tnmSnmpBenchMark.sendTime; } #endif while (TnmSnmpWait(session->timeout * 1000 / (session->retries + 1), TNM_SNMP_SYNC) > 0) { u_char packet[TNM_SNMP_MAXSIZE]; int rc, packetlen = TNM_SNMP_MAXSIZE; struct sockaddr_in from; code = TnmSnmpRecv(interp, packet, &packetlen, &from, TNM_SNMP_SYNC); if (code != TCL_OK) { return TCL_ERROR; } rc = TnmSnmpDecode(interp, packet, packetlen, &from, session, &id, &status, &index); if (rc == TCL_BREAK) { if (retry++ <= session->retries + 1) { goto repeat; } } if (rc == TCL_OK) { if (id == pdu->requestId) { #ifdef TNM_SNMP_BENCH stats.recvSize = tnmSnmpBenchMark.recvSize; stats.recvTime = tnmSnmpBenchMark.recvTime; session->stats = stats; #endif return TCL_OK; } rc = TCL_CONTINUE; } if (rc == TCL_CONTINUE) { if (hexdump) { fprintf(stderr, "%s\n", Tcl_GetStringResult(interp)); } continue; } if (rc == TCL_ERROR) { pdu->errorStatus = status; pdu->errorIndex = index; return TCL_ERROR; } } } Tcl_SetResult(interp, "noResponse 0 {}", TCL_STATIC); return TCL_ERROR; }
void TnmSnmpDeleteRequest(TnmSnmpRequest *request) { TnmSnmpRequest *rPtr, **rPtrPtr; TnmSnmp *session; /* * Check whether the request still exists. It may have been * removed because the session for this request has been * destroyed during callback processing. */ for (rPtr = queueHead; rPtr; rPtr = rPtr->nextPtr) { if (rPtr == request) break; } if (! rPtr) return; /* * Check whether the session is still in the session list. * We sometimes get called when the session has already been * destroyed as a side effect of evaluating callbacks. */ for (session = tnmSnmpList; session; session = session->nextPtr) { if (session == request->session) break; } if (session) { if (request->sends) { session->active--; } else { session->waiting--; } } /* * Remove the request from the list of outstanding requests. * and free the resources allocated for this request. */ rPtrPtr = &queueHead; while (*rPtrPtr && *rPtrPtr != request) { rPtrPtr = &(*rPtrPtr)->nextPtr; } if (*rPtrPtr) { *rPtrPtr = request->nextPtr; if (request->timer) { Tcl_DeleteTimerHandler(request->timer); request->timer = NULL; } Tcl_EventuallyFree((ClientData) request, RequestDestroyProc); } /* * Update the request queue. This will activate async requests * that have been queued because of the window size. */ if (session) { TnmSnmpQueueRequest(session, NULL); } }