and added files
[bcm963xx.git] / userapps / opensource / net-snmp / snmplib / snmpCallbackDomain.c
1 #include <net-snmp/net-snmp-config.h>
2
3 #include <stdio.h>
4 #include <sys/types.h>
5 #include <ctype.h>
6 #include <errno.h>
7
8 #if HAVE_STRING_H
9 #include <string.h>
10 #else
11 #include <strings.h>
12 #endif
13 #if HAVE_STDLIB_H
14 #include <stdlib.h>
15 #endif
16 #if HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #if HAVE_SYS_SOCKET_H
20 #include <sys/socket.h>
21 #endif
22 #if HAVE_SYS_UN_H
23 #include <sys/un.h>
24 #endif
25 #if HAVE_IO_H
26 #include <io.h>
27 #endif
28 #if HAVE_FCNTL_H
29 #include <fcntl.h>
30 #endif
31
32 #if HAVE_DMALLOC_H
33 #include <dmalloc.h>
34 #endif
35
36 #include <net-snmp/types.h>
37 #include <net-snmp/output_api.h>
38 #include <net-snmp/config_api.h>
39 #include <net-snmp/utilities.h>
40
41 #include <net-snmp/library/snmp_transport.h>
42 #include <net-snmp/library/snmpUnixDomain.h>
43 #include <net-snmp/library/snmp_api.h>
44 #include <net-snmp/library/snmp_client.h>
45 #include <net-snmp/library/snmpCallbackDomain.h>
46
47 #ifndef NETSNMP_STREAM_QUEUE_LEN
48 #define NETSNMP_STREAM_QUEUE_LEN  5
49 #endif
50
51 static netsnmp_transport_list *trlist = NULL;
52
53 static int      callback_count = 0;
54
55 typedef struct callback_hack_s {
56     void           *orig_transport_data;
57     netsnmp_pdu    *pdu;
58 } callback_hack;
59
60 typedef struct callback_queue_s {
61     int             callback_num;
62     netsnmp_callback_pass *item;
63     struct callback_queue_s *next, *prev;
64 } callback_queue;
65
66 callback_queue *thequeue;
67
68 static netsnmp_transport *
69 find_transport_from_callback_num(int num)
70 {
71     static netsnmp_transport_list *ptr;
72     for (ptr = trlist; ptr; ptr = ptr->next)
73         if (((netsnmp_callback_info *) ptr->transport->data)->
74             callback_num == num)
75             return ptr->transport;
76     return NULL;
77 }
78
79 static void
80 callback_debug_pdu(const char *ourstring, netsnmp_pdu *pdu)
81 {
82     netsnmp_variable_list *vb;
83     int             i = 1;
84     DEBUGMSGTL((ourstring,
85                 "PDU: command = %d, errstat = %d, errindex = %d\n",
86                 pdu->command, pdu->errstat, pdu->errindex));
87     for (vb = pdu->variables; vb; vb = vb->next_variable) {
88         DEBUGMSGTL((ourstring, "  var %d:", i++));
89         DEBUGMSGVAR((ourstring, vb));
90         DEBUGMSG((ourstring, "\n"));
91     }
92 }
93
94 void
95 callback_push_queue(int num, netsnmp_callback_pass *item)
96 {
97     callback_queue *newitem = SNMP_MALLOC_TYPEDEF(callback_queue);
98     callback_queue *ptr;
99
100     newitem->callback_num = num;
101     newitem->item = item;
102     if (thequeue) {
103         for (ptr = thequeue; ptr && ptr->next; ptr = ptr->next) {
104         }
105         ptr->next = newitem;
106         newitem->prev = ptr;
107     } else {
108         thequeue = newitem;
109     }
110     DEBUGIF("dump_send_callback_transport") {
111         callback_debug_pdu("dump_send_callback_transport", item->pdu);
112     }
113 }
114
115 netsnmp_callback_pass *
116 callback_pop_queue(int num)
117 {
118     netsnmp_callback_pass *cp;
119     callback_queue *ptr;
120
121     for (ptr = thequeue; ptr; ptr = ptr->next) {
122         if (ptr->callback_num == num) {
123             if (ptr->prev) {
124                 ptr->prev->next = ptr->next;
125             } else {
126                 thequeue = ptr->next;
127             }
128             if (ptr->next) {
129                 ptr->next->prev = ptr->prev;
130             }
131             cp = ptr->item;
132             free(ptr);
133             DEBUGIF("dump_recv_callback_transport") {
134                 callback_debug_pdu("dump_recv_callback_transport",
135                                    cp->pdu);
136             }
137             return cp;
138         }
139     }
140     return NULL;
141 }
142
143 /*
144  * Return a string representing the address in data, or else the "far end"
145  * address if data is NULL.  
146  */
147
148 char *
149 netsnmp_callback_fmtaddr(netsnmp_transport *t, void *data, int len)
150 {
151     char buf[SPRINT_MAX_LEN];
152     netsnmp_callback_info *mystuff;
153
154     if (!t)
155         return strdup("callback: unknown");
156
157     mystuff = (netsnmp_callback_info *) t->data;
158
159     if (!mystuff)
160         return strdup("callback: unknown");
161
162     snprintf(buf, SPRINT_MAX_LEN, "callback: %d on fd %d",
163              mystuff->callback_num, mystuff->pipefds[0]);
164     return strdup(buf);
165 }
166
167
168
169 /*
170  * You can write something into opaque that will subsequently get passed back 
171  * to your send function if you like.  For instance, you might want to
172  * remember where a PDU came from, so that you can send a reply there...  
173  */
174
175 int
176 netsnmp_callback_recv(netsnmp_transport *t, void *buf, int size,
177                       void **opaque, int *olength)
178 {
179     int rc = -1;
180     char newbuf[1];
181     netsnmp_callback_info *mystuff = (netsnmp_callback_info *) t->data;
182
183     DEBUGMSGTL(("transport_callback", "hook_recv enter\n"));
184
185     while (rc < 0) {
186         rc = read(mystuff->pipefds[0], newbuf, 1);
187         if (rc < 0 && errno != EINTR) {
188             break;
189         }
190     }
191
192     if (mystuff->linkedto) {
193         /*
194          * we're the client.  We don't need to do anything. 
195          */
196     } else {
197         /*
198          * malloc the space here, but it's filled in by
199          * snmp_callback_created_pdu() below 
200          */
201         int            *returnnum = (int *) calloc(1, sizeof(int));
202         *opaque = returnnum;
203         *olength = sizeof(int);
204     }
205     DEBUGMSGTL(("transport_callback", "hook_recv exit\n"));
206     return rc;
207 }
208
209
210
211 int
212 netsnmp_callback_send(netsnmp_transport *t, void *buf, int size,
213                       void **opaque, int *olength)
214 {
215     int from, rc = -1;
216     netsnmp_callback_info *mystuff = (netsnmp_callback_info *) t->data;
217     netsnmp_callback_pass *cp;
218
219     /*
220      * extract the pdu from the hacked buffer 
221      */
222     netsnmp_transport *other_side;
223     callback_hack  *ch = (callback_hack *) * opaque;
224     netsnmp_pdu    *pdu = ch->pdu;
225     *opaque = ch->orig_transport_data;
226     free(ch);
227
228     DEBUGMSGTL(("transport_callback", "hook_send enter\n"));
229
230     cp = SNMP_MALLOC_TYPEDEF(netsnmp_callback_pass);
231     if (!cp)
232         return -1;
233
234     cp->pdu = snmp_clone_pdu(pdu);
235     if (cp->pdu->transport_data) {
236         /*
237          * not needed and not properly freed later 
238          */
239         SNMP_FREE(cp->pdu->transport_data);
240     }
241
242     if (cp->pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE)
243         cp->pdu->flags ^= UCD_MSG_FLAG_EXPECT_RESPONSE;
244
245     /*
246      * push the sent pdu onto the stack 
247      */
248     /*
249      * AND send a bogus byte to the remote callback receiver's pipe 
250      */
251     if (mystuff->linkedto) {
252         /*
253          * we're the client, send it to the parent 
254          */
255         cp->return_transport_num = mystuff->callback_num;
256
257         other_side = find_transport_from_callback_num(mystuff->linkedto);
258         if (!other_side)
259             return -1;
260
261         while (rc < 0) {
262             rc = write(((netsnmp_callback_info *)other_side->data)->pipefds[1],
263                        " ", 1);
264             if (rc < 0 && errno != EINTR) {
265                 break;
266             }
267         }
268         callback_push_queue(mystuff->linkedto, cp);
269         /*
270          * we don't need the transport data any more 
271          */
272         if (*opaque) {
273             free(*opaque);
274             *opaque = NULL;
275         }
276     } else {
277         /*
278          * we're the server, send it to the person that sent us the request 
279          */
280         from = **((int **) opaque);
281         /*
282          * we don't need the transport data any more 
283          */
284         if (*opaque) {
285             free(*opaque);
286             *opaque = NULL;
287         }
288         other_side = find_transport_from_callback_num(from);
289         if (!other_side)
290             return -1;
291         while (rc < 0) {
292             rc = write(((netsnmp_callback_info *)other_side->data)->pipefds[1],
293                        " ", 1);
294             if (rc < 0 && errno != EINTR) {
295                 break;
296             }
297         }
298         callback_push_queue(from, cp);
299     }
300
301     DEBUGMSGTL(("transport_callback", "hook_send exit\n"));
302     return 0;
303 }
304
305
306
307 int
308 netsnmp_callback_close(netsnmp_transport *t)
309 {
310     int             rc;
311     netsnmp_callback_info *mystuff = (netsnmp_callback_info *) t->data;
312     DEBUGMSGTL(("transport_callback", "hook_close enter\n"));
313
314     rc = close(mystuff->pipefds[0]);
315     rc = close(mystuff->pipefds[1]);
316
317     netsnmp_transport_remove_from_list(&trlist, t);
318
319     DEBUGMSGTL(("transport_callback", "hook_close exit\n"));
320     return rc;
321 }
322
323
324
325 int
326 netsnmp_callback_accept(netsnmp_transport *t)
327 {
328     DEBUGMSGTL(("transport_callback", "hook_accept enter\n"));
329     DEBUGMSGTL(("transport_callback", "hook_accept exit\n"));
330     return 0;
331 }
332
333
334
335 /*
336  * Open a Callback-domain transport for SNMP.  Local is TRUE if addr
337  * is the local address to bind to (i.e. this is a server-type
338  * session); otherwise addr is the remote address to send things to
339  * (and we make up a temporary name for the local end of the
340  * connection).  
341  */
342
343 netsnmp_transport *
344 netsnmp_callback_transport(int to)
345 {
346
347     netsnmp_transport *t = NULL;
348     netsnmp_callback_info *mydata;
349     int             rc;
350
351     /*
352      * transport 
353      */
354     t = SNMP_MALLOC_TYPEDEF(netsnmp_transport);
355     if (!t)
356         return NULL;
357
358     /*
359      * our stuff 
360      */
361     mydata = SNMP_MALLOC_TYPEDEF(netsnmp_callback_info);
362     mydata->linkedto = to;
363     mydata->callback_num = ++callback_count;
364     mydata->data = NULL;
365     t->data = mydata;
366
367 #ifdef WIN32
368     rc = _pipe(mydata->pipefds, 1024, O_BINARY);
369 #else
370     rc = pipe(mydata->pipefds);
371 #endif
372     t->sock = mydata->pipefds[0];
373
374     if (rc) {
375         free(mydata);
376         free(t);
377         return NULL;
378     }
379
380     t->f_recv    = netsnmp_callback_recv;
381     t->f_send    = netsnmp_callback_send;
382     t->f_close   = netsnmp_callback_close;
383     t->f_accept  = netsnmp_callback_accept;
384     t->f_fmtaddr = netsnmp_callback_fmtaddr;
385
386     netsnmp_transport_add_to_list(&trlist, t);
387
388     if (to)
389         DEBUGMSGTL(("transport_callback", "initialized %d linked to %d\n",
390                     mydata->callback_num, to));
391     else
392         DEBUGMSGTL(("transport_callback",
393                     "initialized master listening on %d\n",
394                     mydata->callback_num));
395     return t;
396 }
397
398 int
399 netsnmp_callback_hook_parse(netsnmp_session * sp,
400                             netsnmp_pdu *pdu,
401                             u_char * packetptr, size_t len)
402 {
403     if (SNMP_MSG_RESPONSE == pdu->command ||
404         SNMP_MSG_REPORT == pdu->command)
405         pdu->flags |= UCD_MSG_FLAG_RESPONSE_PDU;
406     else
407         pdu->flags &= (~UCD_MSG_FLAG_RESPONSE_PDU);
408
409     return SNMP_ERR_NOERROR;
410 }
411
412 int
413 netsnmp_callback_hook_build(netsnmp_session * sp,
414                             netsnmp_pdu *pdu, u_char * ptk, size_t * len)
415 {
416     /*
417      * very gross hack, as this is passed later to the transport_send
418      * function 
419      */
420     callback_hack  *ch = SNMP_MALLOC_TYPEDEF(callback_hack);
421     DEBUGMSGTL(("transport_callback", "hook_build enter\n"));
422     ch->pdu = pdu;
423     ch->orig_transport_data = pdu->transport_data;
424     pdu->transport_data = ch;
425     switch (pdu->command) {
426     case SNMP_MSG_RESPONSE:
427     case SNMP_MSG_TRAP:
428     case SNMP_MSG_TRAP2:
429         pdu->flags &= (~UCD_MSG_FLAG_EXPECT_RESPONSE);
430         break;
431     }
432     *len = 1;
433     DEBUGMSGTL(("transport_callback", "hook_build exit\n"));
434     return 1;
435 }
436
437 int
438 netsnmp_callback_check_packet(u_char * pkt, size_t len)
439 {
440     return 1;
441 }
442
443 netsnmp_pdu    *
444 netsnmp_callback_create_pdu(netsnmp_transport *transport,
445                             void *opaque, size_t olength)
446 {
447     netsnmp_pdu    *pdu;
448     netsnmp_callback_pass *cp =
449         callback_pop_queue(((netsnmp_callback_info *) transport->data)->
450                            callback_num);
451     if (!cp)
452         return NULL;
453     pdu = cp->pdu;
454     pdu->transport_data = opaque;
455     pdu->transport_data_length = olength;
456     if (opaque)                 /* if created, we're the server */
457         *((int *) opaque) = cp->return_transport_num;
458     free(cp);
459     return pdu;
460 }
461
462 netsnmp_session *
463 netsnmp_callback_open(int attach_to,
464                       int (*return_func) (int op,
465                                           netsnmp_session * session,
466                                           int reqid, netsnmp_pdu *pdu,
467                                           void *magic),
468                       int (*fpre_parse) (netsnmp_session *,
469                                          struct netsnmp_transport_s *,
470                                          void *, int),
471                       int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *,
472                                           int))
473 {
474     netsnmp_session callback_sess, *callback_ss;
475     netsnmp_transport *callback_tr;
476
477     callback_tr = netsnmp_callback_transport(attach_to);
478     snmp_sess_init(&callback_sess);
479     callback_sess.callback = return_func;
480     if (attach_to) {
481         /*
482          * client 
483          */
484         /*
485          * trysess.community = (u_char *) callback_ss; 
486          */
487     } else {
488         callback_sess.isAuthoritative = SNMP_SESS_AUTHORITATIVE;
489     }
490     callback_sess.remote_port = 0;
491     callback_sess.retries = 0;
492     callback_sess.timeout = 30000000;
493     callback_sess.version = SNMP_DEFAULT_VERSION;       /* (mostly) bogus */
494     callback_ss = snmp_add_full(&callback_sess, callback_tr,
495                                 fpre_parse,
496                                 netsnmp_callback_hook_parse, fpost_parse,
497                                 netsnmp_callback_hook_build,
498                                 NULL,
499                                 netsnmp_callback_check_packet,
500                                 netsnmp_callback_create_pdu);
501     if (callback_ss)
502         callback_ss->local_port =
503             ((netsnmp_callback_info *) callback_tr->data)->callback_num;
504     return callback_ss;
505 }