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