1 /*
2 This file is part of GNU Radius SNMP Library.
3 Copyright (C) 2001,2003,2004,2007 Free Software Foundation, Inc.
4
5 Written by Sergey Poznyakoff
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 3 of the
10 License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; see the file COPYING.LIB. If not,
19 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <snmp/asn1.h>
28 #include <snmp/snmp.h>
29
30 int
snmp_send(struct snmp_session * sess,struct snmp_pdu * pdu)31 snmp_send(struct snmp_session *sess, struct snmp_pdu *pdu)
32 {
33 struct snmp_request *req;
34
35 /* if the session isn't initialized do it now */
36 if (sess->sd == -1 &&
37 snmp_session_open(sess, 0, 0, 0, 0))
38 return -1;
39
40 switch (pdu->type) {
41 case SNMP_PDU_GET:
42 case SNMP_PDU_GETNEXT:
43 case SNMP_PDU_RESPONSE:
44 case SNMP_PDU_SET:
45 if (pdu->req_id == 0)
46 pdu->req_id = snmp_req_id();
47 break;
48 default:
49 abort();
50 }
51
52 req = snmp_alloc(sizeof(*req));
53 if (!req) {
54 SNMP_SET_ERRNO(E_SNMP_NOMEM);
55 snmp_pdu_free(pdu); /*FIXME?*/
56 return -1;
57 }
58 req->next = sess->request_list;
59 sess->request_list = req;
60
61 req->retries = 1;
62 req->timeout = sess->timeout;
63 req->pdu = pdu;
64 return snmp_request_xmit(sess, req);
65 }
66
67 int
snmp_request_xmit(struct snmp_session * sess,struct snmp_request * req)68 snmp_request_xmit(struct snmp_session *sess, struct snmp_request *req)
69 {
70 u_char packet_buf[SNMP_PACKET_LENGTH];
71 int length;
72 struct timeval tv;
73
74 length = sizeof(packet_buf);
75 if (snmp_encode_request(sess, req->pdu, packet_buf, &length))
76 return -1;
77
78 gettimeofday(&tv, (struct timezone *) 0);
79 if (sendto(sess->sd, (char *) packet_buf, length, 0,
80 (struct sockaddr *) &sess->remote_sin,
81 sizeof(sess->remote_sin)) < 0) {
82 SNMP_SET_ERRNO(E_SNMP_SEND);
83 return -1;
84 }
85
86 tv.tv_usec += req->timeout;
87 tv.tv_sec += tv.tv_usec / 1000000L;
88 tv.tv_usec %= 1000000L;
89 req->expire = tv;
90
91 return 0;
92 }
93
94 /* As per RFCs 1901, 1157:
95 Message ::=
96 SEQUENCE {
97 version INTEGER
98 community OCTET STRING
99 data
100 } */
101
102 int
snmp_encode_request(struct snmp_session * sess,struct snmp_pdu * pdu,u_char * packet_buf,int * length)103 snmp_encode_request(struct snmp_session *sess, struct snmp_pdu *pdu,
104 u_char *packet_buf, int *length)
105 {
106 u_char *buf, *msg_start, *pdu_header_ptr, *pdu_data_start,
107 *var_header_ptr, *var_data_start;
108
109 #define BAIL_OUT if (!buf) return -1
110
111 buf = packet_buf;
112 buf = asn_encode_header(buf, length,
113 (ASN_SEQUENCE | ASN_CONSTRUCTOR), 0xffff);
114 BAIL_OUT;
115
116 msg_start = buf;
117 /* version */
118 buf = asn_encode_int(buf, length,
119 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
120 sess->version);
121 BAIL_OUT;
122
123 /* community */
124 buf = asn_encode_string(buf, length,
125 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
126 sess->community.str,
127 sess->community.len);
128 BAIL_OUT;
129
130 /* data */
131 pdu_header_ptr = buf;
132 buf = asn_encode_header(buf, length, pdu->type, 0xffff);
133 BAIL_OUT;
134
135 pdu_data_start = buf;
136 buf = snmp_pdu_encode(buf, length, pdu);
137 BAIL_OUT;
138
139 var_header_ptr = buf;
140 buf = asn_encode_header(buf, length,
141 (ASN_SEQUENCE | ASN_CONSTRUCTOR),
142 0xffff);
143 BAIL_OUT;
144
145 var_data_start = buf;
146 buf = snmp_var_encode(buf, length, pdu->var, sess->version);
147
148 /* Fixup lengths */
149 asn_recode_length(pdu_header_ptr+1,
150 (int) (buf - pdu_data_start));
151 asn_recode_length(packet_buf+1,
152 (int)(buf-msg_start));
153 asn_recode_length(var_header_ptr+1,
154 (buf-var_data_start));
155 *length = buf - packet_buf;
156 return 0;
157 }
158
159 int
snmp_decode_request(struct snmp_session * sess,struct snmp_pdu * pdu,u_char * packet,int length,char * comm,int * comm_len)160 snmp_decode_request(struct snmp_session *sess, struct snmp_pdu *pdu,
161 u_char *packet, int length, char *comm, int *comm_len)
162 {
163 int vers;
164 u_char *buf;
165 u_char type;
166
167 buf = asn_decode_header(packet, &length, &type);
168 if (!buf)
169 return -1;
170 if (type != (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
171 SNMP_SET_ERRNO(E_SNMP_DECODE);
172 return -1;
173 }
174
175 buf = asn_decode_int(buf, &length, &type, &vers, sizeof(vers));
176 if (!buf)
177 return -1;
178
179 buf = asn_decode_string(buf, &length, &type, comm, comm_len);
180 if (!buf)
181 return -1;
182
183 comm[*comm_len] = 0;
184
185 if (vers != SNMP_VERSION_1) {
186 SNMP_SET_ERRNO(E_SNMP_BAD_VERSION);
187 return -1;
188 }
189 buf = snmp_pdu_decode(buf, &length, pdu);
190 if (!buf)
191 return -1;
192
193 buf = snmp_var_decode(buf, &length, &pdu->var, vers);
194 if (!buf)
195 return -1;
196
197 return 0;
198 }
199