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