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 <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/time.h>
29 #include <time.h>
30 #include <stdlib.h>
31 #include <netdb.h>
32 #include <arpa/inet.h>
33 #include <unistd.h>
34 #if defined(USE_PTHREAD)
35 # include <pthread.h>
36 #endif
37 #if defined(HAVE_SYS_SELECT_H)
38 # include <sys/select.h>
39 #endif
40 #include <snmp/asn1.h>
41 #include <snmp/snmp.h>
42 #include <snmp/snmp_intern.h>
43
44
45 int snmp_errno;
46 struct snmp_def snmp_def = {
47 0, /* req_id */
48 3, /* retries */
49 3, /* timeout */
50 };
51
52 #if defined(USE_PTHREAD)
53 static pthread_once_t snmp_errno_once = PTHREAD_ONCE_INIT;
54 static pthread_key_t snmp_errno_key;
55
56 static void
snmp_errno_destroy(void * ptr)57 snmp_errno_destroy(void *ptr)
58 {
59 free(ptr);
60 }
61
62 static void
snmp_errno_create()63 snmp_errno_create()
64 {
65 pthread_key_create(&snmp_errno_key, snmp_errno_destroy);
66 }
67
68 int *
__snmp_errno_location()69 __snmp_errno_location()
70 {
71 int *p;
72 pthread_once(&snmp_errno_once, snmp_errno_create);
73 p = pthread_getspecific(snmp_errno_key);
74 if (!p) {
75 p = malloc(sizeof(*p));
76 if (!p)
77 abort ();
78 *p = 0;
79 pthread_setspecific(snmp_errno_key, p);
80 }
81 return p;
82 }
83 #else
84 static int __snmp_errno;
85
86 int *
__snmp_errno_location()87 __snmp_errno_location()
88 {
89 return &__snmp_errno;
90 }
91 #endif
92
93 int
snmp_req_id()94 snmp_req_id()
95 {
96 if (snmp_def.req_id == 0) {
97 srand(time(NULL));
98 snmp_def.req_id = random();
99 }
100 return snmp_def.req_id++;
101 }
102
103 int
snmp_fdset(struct snmp_session * sp,fd_set * fdset)104 snmp_fdset(struct snmp_session *sp, fd_set *fdset)
105 {
106 int fdmax;
107
108 fdmax = -1;
109 FD_ZERO(fdset);
110 for (; sp; sp = sp->next)
111 if (sp->sd != -1 && sp->request_list) {
112 FD_SET(sp->sd, fdset);
113 if (sp->sd > fdmax)
114 fdmax = sp->sd;
115 }
116 return fdmax+1;
117 }
118
119 void
snmp_init(int retries,int timeout,snmp_alloc_t memalloc,snmp_free_t memfree)120 snmp_init(int retries, int timeout, snmp_alloc_t memalloc, snmp_free_t memfree)
121 {
122 if (retries)
123 snmp_def.retries = retries;
124 if (timeout)
125 snmp_def.timeout = timeout;
126 if (memalloc)
127 __snmp_alloc_fp = memalloc;
128 if (memfree)
129 __snmp_free_fp = memfree;
130 }
131
132 struct snmp_session *
snmp_session_create(char * community,char * host,int port,snmp_cfn cfn,void * closure)133 snmp_session_create(char *community, char *host, int port,
134 snmp_cfn cfn, void *closure)
135 {
136 struct snmp_session *sp;
137 int len;
138
139 sp = snmp_alloc(sizeof(*sp));
140
141 sp->version = SNMP_VERSION_1;
142 len = strlen(community);
143 sp->community.str = snmp_alloc(len+1);
144 if (!sp->community.str) {
145 SNMP_SET_ERRNO(E_SNMP_NOMEM);
146 snmp_free(sp);
147 return NULL;
148 }
149 strcpy(sp->community.str, community);
150 sp->community.len = len;
151
152 sp->retries = snmp_def.retries;
153 sp->timeout = snmp_def.timeout;
154
155 len = strlen(host) + 1;
156 sp->remote_host = snmp_alloc(len);
157 if (!sp->remote_host) {
158 SNMP_SET_ERRNO(E_SNMP_NOMEM);
159 snmp_free(sp->community.str);
160 snmp_free(sp);
161 return NULL;
162 }
163
164 strcpy(sp->remote_host, host);
165 sp->remote_port = port;
166 sp->local_port = 0;
167
168 sp->converse = cfn;
169 sp->app_closure = closure;
170 sp->pdu = NULL;
171 sp->sd = -1;
172
173 sp->next = NULL;
174 sp->request_list = NULL;
175
176 return sp;
177 }
178
179 int
snmp_session_open(struct snmp_session * sp,ip_addr_t local_ip,int local_port,int timeout,int retries)180 snmp_session_open(struct snmp_session *sp, ip_addr_t local_ip,
181 int local_port, int timeout, int retries)
182 {
183 ip_addr_t addr;
184 u_short port;
185 struct sockaddr_in local_sin;
186
187 if (!timeout)
188 sp->timeout = snmp_def.timeout;
189 if (!retries)
190 sp->retries = snmp_def.retries;
191 if (local_ip == 0)
192 local_ip = INADDR_ANY;
193 sp->sd = socket(PF_INET, SOCK_DGRAM, 0);
194 if (sp->sd < 0) {
195 SNMP_SET_ERRNO(E_SNMP_SOCKET);
196 snmp_session_free(sp);
197 return -1;
198 }
199 #ifdef SO_BSDCOMPAT
200 /* For Linux only? Prevents failing UPD packets from getting ICMP
201 response. */
202 {
203 int one = 1;
204 setsockopt(sp->sd, SOL_SOCKET, SO_BSDCOMPAT,
205 &one, sizeof(one));
206 }
207 #endif /* SO_BSDCOMPAT */
208
209 addr = inet_addr(sp->remote_host);
210 if (addr = (ip_addr_t)-1) {
211 struct hostent *hp = gethostbyname(sp->remote_host);
212 if (!hp) {
213 SNMP_SET_ERRNO(E_SNMP_BAD_ADDRESS);
214 snmp_session_close(sp);
215 return -1;
216 }
217 addr = *(ip_addr_t *)hp->h_addr;
218 }
219 sp->remote_sin.sin_addr.s_addr = addr;
220 sp->remote_sin.sin_family = AF_INET;
221
222 if (sp->remote_port == 0) {
223 struct servent *servp = getservbyname("snmp", "udp");
224 if (!servp)
225 port = htons(SNMP_PORT);
226 else
227 port = servp->s_port;
228 } else
229 port = htons(sp->remote_port);
230 sp->remote_sin.sin_port = port;
231
232 memset(&local_sin, '\0', sizeof(local_sin));
233 local_sin.sin_family = AF_INET;
234 local_sin.sin_addr.s_addr = local_ip ? INADDR_ANY : htonl(local_ip);
235 local_sin.sin_port = htons(sp->local_port);
236
237 if (bind(sp->sd, (struct sockaddr *) &local_sin, sizeof(local_sin)) < 0) {
238 SNMP_SET_ERRNO(E_SNMP_BIND);
239 snmp_session_close(sp);
240 return -1;
241 }
242
243 return 0;
244 }
245
246 void
snmp_session_close(struct snmp_session * sess)247 snmp_session_close(struct snmp_session *sess)
248 {
249 if (sess->sd != -1)
250 close(sess->sd);
251 snmp_session_free(sess);
252 }
253
254 void
snmp_session_free(struct snmp_session * sess)255 snmp_session_free(struct snmp_session *sess)
256 {
257 if (!sess)
258 return;
259
260 snmp_pdu_free(sess->pdu);
261 snmp_request_free_list(sess->request_list);
262 snmp_free(sess->remote_host);
263 snmp_free(sess->community.str);
264 snmp_free(sess);
265 }
266
267 void
snmp_request_free(struct snmp_request * req)268 snmp_request_free(struct snmp_request *req)
269 {
270 if (req) {
271 snmp_pdu_free(req->pdu);
272 snmp_free(req);
273 }
274 }
275
276 void
snmp_request_free_list(struct snmp_request * req)277 snmp_request_free_list(struct snmp_request *req)
278 {
279 struct snmp_request *next;
280
281 while (req) {
282 next = req->next;
283 snmp_free(req);
284 req = next;
285 }
286 }
287
288