added smux_failures && smux_max_failures to 'rad_snmp'
[freeradius.git] / src / main / smux.c
1 /* SNMP support
2  * Copyright (C) 2000 Jochen Friedrich <jochen@scram.de>
3  * Copyright (C) 1999 Kunihiro Ishiguro <kunihiro@zebra.org>
4  *
5  * You should have received a copy of the GNU General Public License
6  * along with GNU Zebra; see the file COPYING.  If not, write to the Free
7  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
8  * 02111-1307, USA.  
9  */
10
11 static const char rcsid[] =
12 "$Id$";
13
14 #include "autoconf.h"
15
16 #ifdef WITH_SNMP
17
18 #include "libradius.h"
19
20 #include <sys/socket.h>
21 #include <sys/file.h>
22
23 #if HAVE_NETINET_IN_H
24 #include <netinet/in.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <netdb.h>
31 #include <fcntl.h>
32 #include <ctype.h>
33
34 #include "radiusd.h"
35 #include "radius_snmp.h"
36 #include "smux.h"
37
38 #define min(A,B) ((A) < (B) ? (A) : (B))
39
40 \f
41
42 /* internal prototypes */
43 static int oid_compare (oid *, int, oid *, int);
44
45 /* SMUX subtree vector. */
46 static struct list *treelist = NULL;
47
48 /* SMUX oid. */
49 static oid *smux_oid;
50 static size_t smux_oid_len;
51
52 \f
53 static void *
54 oid_copy (void *dest, void *src, size_t size)
55 {
56   return memcpy (dest, src, size * sizeof (oid));
57 }
58
59 #if 0
60 static void
61 oid2in_addr (oid my_oid[], int len, struct in_addr *addr)
62 {
63   int i;
64   u_char *pnt;
65   
66   if (len == 0)
67     return;
68
69   pnt = (u_char *) addr;
70
71   for (i = 0; i < len; i++)
72     *pnt++ = my_oid[i];
73 }
74
75 static void
76 oid_copy_addr (oid my_oid[], struct in_addr *addr, int len)
77 {
78   int i;
79   u_char *pnt;
80   
81   if (len == 0)
82     return;
83
84   pnt = (u_char *) addr;
85
86   for (i = 0; i < len; i++)
87     my_oid[i] = *pnt++;
88 }
89 #endif /* NOT USED */
90
91 static int
92 oid_compare (oid *o1, int o1_len, oid *o2, int o2_len)
93 {
94   int i;
95
96   for (i = 0; i < min (o1_len, o2_len); i++)
97     {
98       if (o1[i] < o2[i])
99         return -1;
100       else if (o1[i] > o2[i])
101         return 1;
102     }
103   if (o1_len < o2_len)
104     return -1;
105   if (o1_len > o2_len)
106     return 1;
107
108   return 0;
109 }
110
111 static int
112 oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len)
113 {
114   int i;
115
116   for (i = 0; i < min (o1_len, o2_len); i++)
117     {
118       if (o1[i] < o2[i])
119         return -1;
120       else if (o1[i] > o2[i])
121         return 1;
122     }
123   if (o1_len < o2_len)
124     return -1;
125
126   return 0;
127 }
128 \f
129 static void
130 smux_oid_dump(const char *prefix, oid *my_oid, size_t oid_len)
131 {
132   int i;
133   int first = 1;
134   char buf[MAX_OID_LEN * 3];
135
136   buf[0] = '\0';
137
138   for (i = 0; i < oid_len; i++)
139     {
140       sprintf (buf + strlen (buf), "%s%d", first ? "" : ".", (int) my_oid[i]);
141       first = 0;
142     }
143   DEBUG2 ("%s: %s", prefix, buf); 
144 }
145
146 static int
147 smux_sock (void)
148 {
149   int ret;
150   int on = 1;
151 #ifdef HAVE_IPV6
152   struct addrinfo hints, *res0, *res;
153   int gai;
154 #else
155   struct sockaddr_in serv;
156   struct servent *sp;
157 #endif
158   int fd;
159
160 #ifdef HAVE_IPV6
161   memset(&hints, 0, sizeof(hints));
162   hints.ai_family = PF_UNSPEC;
163   hints.ai_socktype = SOCK_STREAM;
164   gai = getaddrinfo(NULL, "smux", &hints, &res0);
165   if (gai == EAI_SERVICE)
166     {
167       char servbuf[NI_MAXSERV];
168       sprintf(servbuf,"%d",SMUX_PORT_DEFAULT);
169       gai = getaddrinfo(NULL, servbuf, &hints, &res0);
170     }
171   if (gai)
172     {
173       DEBUG("Cannot locate loopback service smux");
174       return -1;
175     }
176   for(res=res0; res; res=res->ai_next)
177     {
178       fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
179       if (fd < 0)
180         continue;
181       setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof (on));
182 #ifdef SO_REUSEPORT
183       setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof (on));
184 #endif
185       ret = connect (fd, res->ai_addr, res->ai_addrlen);
186       if (ret < 0)
187         {
188           close(fd);
189           fd = -1;
190           continue;
191         }
192       break;
193     }
194   freeaddrinfo(res0);
195   if (fd < 0)
196     DEBUG ("Can't connect to SNMP agent with SMUX");
197 #else
198   fd = socket (AF_INET, SOCK_STREAM, 0);
199   if (fd < 0)
200     {
201       DEBUG ("Can't make socket for SNMP");
202       return -1;
203     }
204
205   memset (&serv, 0, sizeof (struct sockaddr_in));
206   serv.sin_family = AF_INET;
207 #ifdef HAVE_SIN_LEN
208   serv.sin_len = sizeof (struct sockaddr_in);
209 #endif /* HAVE_SIN_LEN */
210
211   sp = getservbyname ("smux", "tcp");
212   if (sp != NULL) 
213     serv.sin_port = sp->s_port;
214   else
215     serv.sin_port = htons (SMUX_PORT_DEFAULT);
216
217   serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
218
219   setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof (on));
220 #ifdef SO_REUSEPORT
221   setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof (on));
222 #endif
223
224   ret = connect (fd, (struct sockaddr *) &serv, sizeof (struct sockaddr_in));
225   if (ret < 0)
226     {
227       close (fd);
228       DEBUG ("Can't connect to SNMP agent with SMUX: %s", strerror(errno));
229       fd = -1;
230     }
231 #endif
232   return fd;
233 }
234
235 static void
236 smux_getresp_send (oid objid[], size_t objid_len, long reqid, long errstat,
237                    long errindex, u_char val_type, const void *arg, size_t arg_len)
238 {
239   int ret;
240   u_char buf[BUFSIZ];
241   u_char *ptr, *h1, *h1e, *h2, *h2e;
242   int len, length;
243
244   ptr = buf;
245   len = BUFSIZ;
246   length = len;
247
248   DEBUG2("SMUX GETRSP send");
249   DEBUG2("SMUX GETRSP reqid: %ld", reqid);
250
251   h1 = ptr;
252   /* Place holder h1 for complete sequence */
253   ptr = asn_build_sequence (ptr, &len, (u_char) SMUX_GETRSP, 0);
254   h1e = ptr;
255  
256   ptr = asn_build_int (ptr, &len,
257                        (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
258                        &reqid, sizeof (reqid));
259
260   DEBUG2("SMUX GETRSP errstat: %ld", errstat);
261
262   ptr = asn_build_int (ptr, &len,
263                        (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
264                        &errstat, sizeof (errstat));
265   DEBUG2("SMUX GETRSP errindex: %ld", errindex);
266
267   ptr = asn_build_int (ptr, &len,
268                        (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
269                        &errindex, sizeof (errindex));
270
271   h2 = ptr;
272   /* Place holder h2 for one variable */
273   ptr = asn_build_sequence (ptr, &len, 
274                            (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR),
275                            0);
276   h2e = ptr;
277
278   ptr = snmp_build_var_op (ptr, objid, &objid_len, 
279                            val_type, arg_len, arg, &len);
280
281   /* Now variable size is known, fill in size */
282   asn_build_sequence(h2,&length,(u_char)(ASN_SEQUENCE|ASN_CONSTRUCTOR),ptr-h2e);
283
284   /* Fill in size of whole sequence */
285   asn_build_sequence(h1,&length,(u_char)SMUX_GETRSP,ptr-h1e);
286
287   DEBUG2("SMUX getresp send: %d", ptr - buf);
288   
289   ret = send (rad_snmp.smux_fd, buf, (ptr - buf), 0);
290 }
291
292 static char *
293 smux_var (char *ptr, int len, oid objid[], size_t *objid_len,
294           size_t *var_val_len,
295           u_char *var_val_type,
296           void **var_value)
297 {
298   u_char type;
299   u_char val_type;
300   size_t val_len;
301   u_char *val;
302
303   DEBUG2("SMUX var parse: len %d", len);
304
305   /* Parse header. */
306   ptr = asn_parse_header (ptr, &len, &type);
307   
308   DEBUG2("SMUX var parse: type %d len %d", type, len);
309   DEBUG2("SMUX var parse: type must be %d", (ASN_SEQUENCE | ASN_CONSTRUCTOR));
310
311   /* Parse var option. */
312   *objid_len = MAX_OID_LEN;
313   ptr = snmp_parse_var_op(ptr, objid, objid_len, &val_type, 
314                           &val_len, &val, &len);
315
316   if (var_val_len)
317     *var_val_len = val_len;
318
319   if (var_value)
320     *var_value = (void*) val;
321
322   if (var_val_type)
323     *var_val_type = val_type;
324
325   /* Requested object id length is objid_len. */
326   smux_oid_dump ("Request OID", objid, *objid_len);
327
328   DEBUG2 ("SMUX val_type: %d", val_type);
329
330   /* Check request value type. */
331   switch (val_type)
332     {
333     case ASN_NULL:
334       /* In case of SMUX_GET or SMUX_GET_NEXT val_type is set to
335          ASN_NULL. */
336       DEBUG2 ("ASN_NULL");
337       break;
338
339     case ASN_INTEGER:
340       DEBUG2 ("ASN_INTEGER");
341       break;
342     case ASN_COUNTER:
343     case ASN_GAUGE:
344     case ASN_TIMETICKS:
345     case ASN_UINTEGER:
346       DEBUG2 ("ASN_COUNTER");
347       break;
348     case ASN_COUNTER64:
349       DEBUG2 ("ASN_COUNTER64");
350       break;
351     case ASN_IPADDRESS:
352       DEBUG2 ("ASN_IPADDRESS");
353       break;
354     case ASN_OCTET_STR:
355       DEBUG2 ("ASN_OCTET_STR");
356       break;
357     case ASN_OPAQUE:
358     case ASN_NSAP:
359     case ASN_OBJECT_ID:
360       DEBUG2 ("ASN_OPAQUE");
361       break;
362     case SNMP_NOSUCHOBJECT:
363       DEBUG2 ("SNMP_NOSUCHOBJECT");
364       break;
365     case SNMP_NOSUCHINSTANCE:
366       DEBUG2 ("SNMP_NOSUCHINSTANCE");
367       break;
368     case SNMP_ENDOFMIBVIEW:
369       DEBUG2 ("SNMP_ENDOFMIBVIEW");
370       break;
371     case ASN_BIT_STR:
372       DEBUG2 ("ASN_BIT_STR");
373       break;
374     default:
375       DEBUG2 ("Unknown type");
376       break;
377     }
378   return ptr;
379 }
380
381 /* NOTE: all 3 functions (smux_set, smux_get & smux_getnext) are based on
382    ucd-snmp smux and as such suppose, that the peer receives in the message
383    only one variable. Fortunately, IBM seems to do the same in AIX. */
384
385 static int
386 smux_set (oid *reqid, size_t *reqid_len,
387           u_char val_type, void *val, size_t val_len, int action)
388 {
389   int j;
390   struct subtree *subtree;
391   struct variable *v;
392   struct list *l;
393   int subresult;
394   oid *suffix;
395   int suffix_len;
396   int result;
397   const unsigned char *statP = NULL;
398   WriteMethod *write_method = NULL;
399
400   if (!rad_snmp.snmp_write_access)
401     return SNMP_ERR_NOSUCHNAME;
402
403   /* Check */
404   for (l = treelist; l; l=l->next)
405     {
406       subtree = l->data;
407       subresult = oid_compare_part (reqid, *reqid_len,
408                                     subtree->name, subtree->name_len);
409
410       /* Subtree matched. */
411       if (subresult == 0)
412         {
413           /* Prepare suffix. */
414           suffix = reqid + subtree->name_len;
415           suffix_len = *reqid_len - subtree->name_len;
416           result = subresult;
417
418           /* Check variables. */
419           for (j = 0; j < subtree->variables_num; j++)
420             {
421               v = &subtree->variables[j];
422
423               /* Always check suffix */
424               result = oid_compare_part (suffix, suffix_len,
425                                          v->name, v->namelen);
426
427               /* This is exact match so result must be zero. */
428               if (result == 0)
429                 {
430                   DEBUG2 ("SMUX function call index is %d", v->magic);
431
432                   statP = (*v->findVar) (v, suffix, &suffix_len, 1,
433                     &val_len, &write_method);
434
435                   if (write_method)
436                     {
437                       return (*write_method)(action, val, val_type, val_len, statP, suffix, suffix_len);
438
439                     }
440                   else
441                     {
442                       return SNMP_ERR_READONLY;
443                     }
444                 }
445
446               /* If above execution is failed or oid is small (so
447                  there is no further match). */
448               if (result < 0)
449                 return SNMP_ERR_NOSUCHNAME;
450             }
451         }
452     }
453   return SNMP_ERR_NOSUCHNAME;
454 }
455
456 static int
457 smux_get (oid *reqid, size_t *reqid_len, int exact, 
458           u_char *val_type,const void **val, size_t *val_len)
459 {
460   int j;
461   struct subtree *subtree;
462   struct variable *v;
463   struct list *l;
464   int subresult;
465   oid *suffix;
466   int suffix_len;
467   int result;
468   WriteMethod *write_method=NULL;
469
470   /* Check */
471   for (l = treelist; l; l=l->next)
472     {
473       subtree = l->data;
474       subresult = oid_compare_part (reqid, *reqid_len, 
475                                     subtree->name, subtree->name_len);
476
477       /* Subtree matched. */
478       if (subresult == 0)
479         {
480           /* Prepare suffix. */
481           suffix = reqid + subtree->name_len;
482           suffix_len = *reqid_len - subtree->name_len;
483           result = subresult;
484
485           /* Check variables. */
486           for (j = 0; j < subtree->variables_num; j++)
487             {
488               v = &subtree->variables[j];
489
490               /* Always check suffix */
491               result = oid_compare_part (suffix, suffix_len,
492                                          v->name, v->namelen);
493
494               /* This is exact match so result must be zero. */
495               if (result == 0)
496                 {
497                   DEBUG2 ("SMUX function call index is %d", v->magic);
498
499                   *val = (*v->findVar) (v, suffix, &suffix_len, exact,
500                     val_len, &write_method);
501
502                   /* There is no instance. */
503                   if (*val == NULL)
504                     return SNMP_ERR_NOSUCHNAME;
505
506                   /* Call is suceed. */
507                   *val_type = v->type;
508
509                   return 0;
510                 }
511
512               /* If above execution is failed or oid is small (so
513                  there is no further match). */
514               if (result < 0)
515                 return SNMP_ERR_NOSUCHNAME;
516             }
517         }
518     }
519   return SNMP_ERR_NOSUCHNAME;
520 }
521
522 static int
523 smux_getnext (oid *reqid, size_t *reqid_len, int exact, 
524                  u_char *val_type, const void **val, size_t *val_len)
525 {
526   int j;
527   oid save[MAX_OID_LEN];
528   int savelen = 0;
529   struct subtree *subtree;
530   struct variable *v;
531   struct list *l;
532   int subresult;
533   oid *suffix;
534   int suffix_len;
535   int result;
536   WriteMethod *write_method=NULL;
537
538   /* Save incoming request. */
539   oid_copy (save, reqid, *reqid_len);
540   savelen = *reqid_len;
541
542   /* Check for best matching subtree */
543
544   for (l = treelist; l; l=l->next)
545     {
546       subtree = l->data;
547
548       subresult = oid_compare_part (reqid, *reqid_len, 
549                                     subtree->name, subtree->name_len);
550
551      /* If request is in the tree. The agent has to make sure we
552         only receive requests we have registered for. */
553      /* Unfortunately, that's not true. In fact, a SMUX subagent has to
554         behave as if it manages the whole SNMP MIB tree itself. It's the
555         duty of the master agent to collect the best answer and return it
556         to the manager. See RFC 1227 chapter 3.1.6 for the glory details
557         :-). ucd-snmp really behaves bad here as it actually might ask
558         multiple times for the same GETNEXT request as it throws away the
559         answer when it expects it in a different subtree and might come
560         back later with the very same request. --jochen */
561
562       if (subresult <= 0)
563         {
564           /* Prepare suffix. */
565           suffix = reqid + subtree->name_len;
566           suffix_len = *reqid_len - subtree->name_len;
567           if (subresult < 0)
568             {
569               oid_copy(reqid, subtree->name, subtree->name_len);
570               *reqid_len = subtree->name_len;
571             }
572           for (j = 0; j < subtree->variables_num; j++)
573             {
574               result = subresult;
575               v = &subtree->variables[j];
576
577               /* Next then check result >= 0. */
578               if (result == 0)
579                 result = oid_compare_part (suffix, suffix_len,
580                                            v->name, v->namelen);
581
582               if (result <= 0)
583                 {
584                   DEBUG2 ("SMUX function call index is %d", v->magic);
585                   if(result<0)
586                     {
587                       oid_copy(suffix, v->name, v->namelen);
588                       suffix_len = v->namelen;
589                     }
590                   *val = (*v->findVar) (v, suffix, &suffix_len, exact,
591                     val_len, &write_method);
592                   *reqid_len = suffix_len + subtree->name_len;
593                   if (*val)
594                     {
595                       *val_type = v->type;
596                       return 0;
597                     }
598                 }
599             }
600         }
601     }
602   memcpy (reqid, save, savelen * sizeof(oid));
603   *reqid_len = savelen;
604
605   return SNMP_ERR_NOSUCHNAME;
606 }
607
608 /* GET message header. */
609 static char *
610 smux_parse_get_header (char *ptr, size_t *len, long *reqid)
611 {
612   u_char type;
613   long errstat;
614   long errindex;
615
616   /* Request ID. */
617   ptr = asn_parse_int (ptr, len, &type, reqid, sizeof (*reqid));
618
619   DEBUG2 ("SMUX GET reqid: %ld len: %d", *reqid, (int) *len);
620
621   /* Error status. */
622   ptr = asn_parse_int (ptr, len, &type, &errstat, sizeof (errstat));
623
624   DEBUG2 ("SMUX GET errstat %ld len: %d", errstat, *len);
625
626   /* Error index. */
627   ptr = asn_parse_int (ptr, len, &type, &errindex, sizeof (errindex));
628
629   DEBUG2 ("SMUX GET errindex %ld len: %d", errindex, *len);
630
631   return ptr;
632 }
633
634 static void
635 smux_parse_set (char *ptr, size_t len, int action)
636 {
637   long reqid;
638   oid my_oid[MAX_OID_LEN];
639   size_t oid_len;
640   u_char val_type;
641   void *val;
642   size_t val_len;
643   int ret;
644
645   DEBUG2 ("SMUX SET(%s) message parse: len %d",
646     (RESERVE1 == action) ? "RESERVE1" : ((FREE == action) ? "FREE" : "COMMIT"),
647     len);
648
649   /* Parse SET message header. */
650   ptr = smux_parse_get_header (ptr, &len, &reqid);
651
652   /* Parse SET message object ID. */
653   ptr = smux_var (ptr, len, my_oid, &oid_len, &val_len, &val_type, &val);
654
655   ret = smux_set (my_oid, &oid_len, val_type, val, val_len, action);
656   DEBUG2 ("SMUX SET ret %d", ret);
657
658   /* Return result. */
659   if (RESERVE1 == action)
660     smux_getresp_send (my_oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
661 }
662
663 static void
664 smux_parse_get (char *ptr, size_t len, int exact)
665 {
666   long reqid;
667   oid my_oid[MAX_OID_LEN];
668   size_t oid_len;
669   u_char val_type;
670   const void *val;
671   size_t val_len;
672   int ret;
673
674   DEBUG2 ("SMUX GET message parse: len %d", len);
675   
676   /* Parse GET message header. */
677   ptr = smux_parse_get_header (ptr, &len, &reqid);
678   
679   /* Parse GET message object ID. We needn't the value come */
680   ptr = smux_var (ptr, len, my_oid, &oid_len, NULL, NULL, NULL);
681
682   /* Traditional getstatptr. */
683   if (exact)
684     ret = smux_get (my_oid, &oid_len, exact, &val_type, &val, &val_len);
685   else
686     ret = smux_getnext (my_oid, &oid_len, exact, &val_type, &val, &val_len);
687
688   /* Return result. */
689   if (ret == 0)
690     smux_getresp_send (my_oid, oid_len, reqid, 0, 0, val_type, val, val_len);
691   else
692     smux_getresp_send (my_oid, oid_len, reqid, ret, 3, ASN_NULL, NULL, 0);
693 }
694
695 /* Parse SMUX_CLOSE message. */
696 static void
697 smux_parse_close (char *ptr, int len)
698 {
699   long reason = 0;
700
701   while (len--)
702     {
703       reason = (reason << 8) | (long) *ptr;
704       ptr++;
705     }
706   DEBUG ("SMUX_CLOSE with reason: %ld", reason);
707 }
708
709 /* SMUX_RRSP message. */
710 static void
711 smux_parse_rrsp (char *ptr, int len)
712 {
713   char val;
714   long errstat;
715   
716   ptr = asn_parse_int (ptr, &len, &val, &errstat, sizeof (errstat));
717
718   DEBUG2 ("SMUX_RRSP value: %d errstat: %ld", val, errstat);
719 }
720
721 /* Parse SMUX message. */
722 static int
723 smux_parse (char *ptr, int len)
724 {
725   /* this buffer we'll use for SOUT message. We could allocate it with malloc and 
726      save only static pointer/lenght, but IMHO static buffer is a faster solusion */
727   static u_char sout_save_buff[SMUXMAXPKTSIZE];
728   static int sout_save_len = 0;
729
730   int len_income = len; /* see note below: YYY */
731   u_char type;
732   u_char rollback;
733
734   rollback = ptr[2]; /* important only for SMUX_SOUT */
735
736 process_rest: /* see note below: YYY */
737
738   /* Parse SMUX message type and subsequent length. */
739   ptr = asn_parse_header (ptr, &len, &type);
740
741   DEBUG2 ("SMUX message received type: %d rest len: %d", type, len);
742
743   switch (type)
744     {
745     case SMUX_OPEN:
746       /* Open must be not send from SNMP agent. */
747       DEBUG ("SMUX_OPEN received: resetting connection.");
748       return -1;
749       break;
750     case SMUX_RREQ:
751       /* SMUX_RREQ message is invalid for us. */
752       DEBUG ("SMUX_RREQ received: resetting connection.");
753       return -1;
754       break;
755     case SMUX_SOUT:
756       /* SMUX_SOUT message is now valied for us. */
757       DEBUG2 ("SMUX_SOUT(%s)", rollback ? "rollback" : "commit");
758
759       if (sout_save_len > 0)
760         {
761           smux_parse_set (sout_save_buff, sout_save_len, rollback ? FREE : COMMIT);
762           sout_save_len = 0;
763         }
764       else
765         DEBUG ("SMUX_SOUT sout_save_len=%d - invalid", (int) sout_save_len);
766
767       if (len_income > 3) 
768         {
769           /* YYY: this strange code has to solve the "slow peer"
770              problem: When agent sends SMUX_SOUT message it doesn't
771              wait any responce and may send some next message to
772              subagent. Then the peer in 'smux_read()' will recieve
773              from socket the 'concatenated' buffer, contaning both
774              SMUX_SOUT message and the next one
775              (SMUX_GET/SMUX_GETNEXT/SMUX_GET). So we should check: if
776              the buffer is longer than 3 ( length of SMUX_SOUT ), we
777              must process the rest of it.  This effect may be observed
778              if DEBUG is set to >1 */
779           ptr++;
780           len = len_income - 3;
781           goto process_rest;
782         }
783       break;
784     case SMUX_GETRSP:
785       /* SMUX_GETRSP message is invalid for us. */
786       DEBUG ("SMUX_GETRSP received: resetting connection.");
787       return -1;
788       break;
789     case SMUX_CLOSE:
790       /* Close SMUX connection. */
791       DEBUG2 ("SMUX_CLOSE");
792       smux_parse_close (ptr, len);
793       return -1;
794       break;
795     case SMUX_RRSP:
796       /* This is response for register message. */
797       DEBUG2 ("SMUX_RRSP");
798       smux_parse_rrsp (ptr, len);
799       break;
800     case SMUX_GET:
801       /* Exact request for object id. */
802       DEBUG2 ("SMUX_GET");
803       smux_parse_get (ptr, len, 1);
804       break;
805     case SMUX_GETNEXT:
806       /* Next request for object id. */
807       DEBUG2 ("SMUX_GETNEXT");
808       smux_parse_get (ptr, len, 0);
809       break;
810     case SMUX_SET:
811       /* SMUX_SET is supported with some limitations. */
812       DEBUG2 ("SMUX_SET");
813
814       /* save the data for future SMUX_SOUT */
815       memcpy (sout_save_buff, ptr, len);
816       sout_save_len = len;
817       smux_parse_set (ptr, len, RESERVE1);
818       break;
819     default:
820       DEBUG ("Unknown type: %d", type);
821       break;
822     }
823   return 0;
824 }
825
826 /* SMUX message read function. */
827 int
828 smux_read ()
829 {
830   int len;
831   u_char buf[SMUXMAXPKTSIZE];
832   int ret;
833
834   rad_snmp.smux_event=SMUX_NONE;
835   DEBUG2 ("SMUX read start");
836
837   /* Read message from SMUX socket. */
838   len = recv (rad_snmp.smux_fd, buf, SMUXMAXPKTSIZE, 0);
839
840   if (len < 0)
841     {
842       DEBUG ("Can't read all SMUX packet: %s", strerror (errno));
843       close (rad_snmp.smux_fd);
844       rad_snmp.smux_fd = -1;
845       rad_snmp.smux_event=SMUX_CONNECT;
846       return -1;
847     }
848
849   if (len == 0)
850     {
851       DEBUG ("SMUX connection closed: %d", rad_snmp.smux_fd);
852       close (rad_snmp.smux_fd);
853       rad_snmp.smux_fd = -1;
854       rad_snmp.smux_event=SMUX_CONNECT;
855       return -1;
856     }
857
858   DEBUG2 ("SMUX read len: %d", len);
859
860   /* Parse the message. */
861   ret = smux_parse (buf, len);
862
863   if (ret < 0)
864     {
865       close (rad_snmp.smux_fd);
866       rad_snmp.smux_fd = -1;
867       rad_snmp.smux_event=SMUX_CONNECT;
868       return -1;
869     }
870
871   rad_snmp.smux_event=SMUX_READ;
872
873   return 0;
874 }
875
876 int
877 smux_open(void)
878 {
879   u_char buf[BUFSIZ];
880   u_char *ptr;
881   int len;
882   u_long version;
883   u_char progname[] = "radiusd";
884
885   smux_oid_dump ("SMUX open oid", smux_oid, smux_oid_len);
886   DEBUG2 ("SMUX open progname: %s", progname);
887   DEBUG2 ("SMUX open password: %s", rad_snmp.smux_password);
888
889   ptr = buf;
890   len = BUFSIZ;
891
892   /* SMUX Header.  As placeholder. */
893   ptr = asn_build_header (ptr, &len, (u_char) SMUX_OPEN, 0);
894
895   /* SMUX Open. */
896   version = 0;
897   ptr = asn_build_int (ptr, &len, 
898                        (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
899                        &version, sizeof (u_long));
900
901   /* SMUX connection oid. */
902   ptr = asn_build_objid (ptr, &len,
903                          (u_char) 
904                          (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
905                          smux_oid, smux_oid_len);
906
907   /* SMUX connection description. */
908   ptr = asn_build_string (ptr, &len, 
909                           (u_char)
910                           (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
911                           progname, strlen (progname));
912
913   /* SMUX connection password. */
914   ptr = asn_build_string (ptr, &len, 
915                           (u_char)
916                           (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
917                           rad_snmp.smux_password, strlen(rad_snmp.smux_password));
918
919   /* Fill in real SMUX header.  We exclude ASN header size (2). */
920   len = BUFSIZ;
921   asn_build_header (buf, &len, (u_char) SMUX_OPEN, (ptr - buf) - 2);
922
923   return send (rad_snmp.smux_fd, buf, (ptr - buf), 0);
924 }
925
926 int
927 smux_register(void)
928 {
929   u_char buf[BUFSIZ];
930   u_char *ptr;
931   int len, ret;
932   long priority;
933   long operation;
934   struct subtree *subtree;
935   struct list *l;
936
937   ret = 0;
938
939   for (l = treelist; l; l=l->next)
940     {
941       subtree = l->data;
942
943       ptr = buf;
944       len = BUFSIZ;
945
946       /* SMUX RReq Header. */
947       ptr = asn_build_header (ptr, &len, (u_char) SMUX_RREQ, 0);
948
949       /* Register MIB tree. */
950       ptr = asn_build_objid (ptr, &len,
951                             (u_char)
952                             (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
953                             subtree->name, subtree->name_len);
954
955       /* Priority. */
956       priority = -1;
957       ptr = asn_build_int (ptr, &len, 
958                           (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
959                           &priority, sizeof (u_long));
960
961       /* Operation. */
962       operation = rad_snmp.snmp_write_access ? 2 : 1; /* Register R/O or R/W */
963       ptr = asn_build_int (ptr, &len, 
964                           (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
965                           &operation, sizeof (u_long));
966
967       smux_oid_dump ("SMUX register oid", subtree->name, subtree->name_len);
968       DEBUG2 ("SMUX register priority: %ld", priority);
969       DEBUG2 ("SMUX register operation: %ld", operation);
970
971       len = BUFSIZ;
972       asn_build_header (buf, &len, (u_char) SMUX_RREQ, (ptr - buf) - 2);
973       ret = send (rad_snmp.smux_fd, buf, (ptr - buf), 0);
974       if (ret < 0)
975         return ret;
976     }
977   return ret;
978 }
979
980 /* Try to connect to SNMP agent. */
981 int
982 smux_connect ()
983 {
984   int ret;
985
986   rad_snmp.smux_event=SMUX_NONE;
987   DEBUG2 ("SMUX connect try %d", rad_snmp.smux_failures + 1);
988
989   /* Make socket.  Try to connect. */
990   rad_snmp.smux_fd = smux_sock ();
991   if (rad_snmp.smux_fd < 0) {
992           if (++rad_snmp.smux_failures < rad_snmp.smux_max_failures)
993                   rad_snmp.smux_event=SMUX_CONNECT;
994           return 0;
995   }
996
997   /* Send OPEN PDU. */
998   ret = smux_open ();
999   if (ret < 0) {
1000           DEBUG ("SMUX open message send failed: %s", strerror (errno));
1001           close (rad_snmp.smux_fd);
1002           rad_snmp.smux_fd = -1;
1003           rad_snmp.smux_event=SMUX_CONNECT;
1004           return -1;
1005   }
1006
1007   /* Send any outstanding register PDUs. */
1008   ret = smux_register ();
1009   if (ret < 0)
1010     {
1011       DEBUG ("SMUX register message send failed: %s", strerror (errno));
1012       close (rad_snmp.smux_fd);
1013       rad_snmp.smux_fd = -1;
1014       rad_snmp.smux_event=SMUX_CONNECT;
1015       return -1;
1016     }
1017
1018   /* Everything goes fine. */
1019   rad_snmp.smux_event=SMUX_READ;
1020
1021   return 0;
1022 }
1023
1024 /* Clear all SMUX related resources. */
1025 void
1026 smux_stop(void)
1027 {
1028   rad_snmp.smux_event=SMUX_NONE;
1029   if (rad_snmp.smux_fd >= 0)
1030     close (rad_snmp.smux_fd);
1031   rad_snmp.smux_fd = -1;
1032 }
1033 \f
1034 int
1035 smux_str2oid (char *str, oid *my_oid, size_t *oid_len)
1036 {
1037   int len;
1038   int val;
1039
1040   len = 0;
1041   val = 0;
1042   *oid_len = 0;
1043
1044   if (*str == '.')
1045     str++;
1046   if (*str == '\0')
1047     return 0;
1048
1049   while (1)
1050     {
1051       if (! isdigit (*str))
1052         return -1;
1053
1054       while (isdigit (*str))
1055         {
1056           val *= 10;
1057           val += (*str - '0');
1058           str++;
1059         }
1060
1061       if (*str == '\0')
1062         break;
1063       if (*str != '.')
1064         return -1;
1065
1066       my_oid[len++] = val;
1067       val = 0;
1068       str++;
1069     }
1070
1071   my_oid[len++] = val;
1072   *oid_len = len;
1073
1074   return 0;
1075 }
1076
1077 oid *
1078 smux_oid_dup (oid *objid, size_t objid_len)
1079 {
1080   oid *new;
1081
1082   new = (oid *)rad_malloc(sizeof (oid) * objid_len);
1083   oid_copy (new, objid, objid_len);
1084
1085   return new;
1086 }
1087
1088 int
1089 smux_header_generic (struct variable *v, oid *name, size_t *length, int exact,
1090                  size_t *var_len, WriteMethod **write_method)
1091 {
1092   oid fulloid[MAX_OID_LEN];
1093   int ret;
1094
1095   oid_copy (fulloid, v->name, v->namelen);
1096   fulloid[v->namelen] = 0;
1097   /* Check against full instance. */
1098   ret = oid_compare (name, *length, fulloid, v->namelen + 1);
1099
1100   /* Check single instance. */
1101   if ((exact && (ret != 0)) || (!exact && (ret >= 0)))
1102         return MATCH_FAILED;
1103
1104   /* In case of getnext, fill in full instance. */
1105   memcpy (name, fulloid, (v->namelen + 1) * sizeof (oid));
1106   *length = v->namelen + 1;
1107
1108   *write_method = 0;
1109   *var_len = sizeof(long);    /* default to 'long' results */
1110
1111   return MATCH_SUCCEEDED;
1112 }
1113
1114 /* Initialize some values then schedule first SMUX connection. */
1115 void
1116 smux_init (oid defoid[], size_t defoid_len)
1117 {
1118   smux_oid = defoid;
1119   smux_oid_len = defoid_len;
1120 }
1121
1122 /* Register subtree to smux master tree. */
1123 void
1124 smux_register_mib(const char *descr, struct variable *var, size_t width,
1125                   int num, oid name[], size_t namelen)
1126 {
1127   struct subtree *tree, *tt;
1128   struct list *l, *ll;
1129
1130   tree = (struct subtree *)rad_malloc(sizeof(struct subtree));
1131   oid_copy (tree->name, name, namelen);
1132   tree->name_len = namelen;
1133   tree->variables = var;
1134   tree->variables_num = num;
1135   tree->variables_width = width;
1136   tree->registered = 0;
1137   l = (struct list *)rad_malloc(sizeof(struct list));
1138   l->data = tree;
1139   l->next = NULL;
1140 /* Build a treelist sorted by the name. This makes GETNEXT simpler */
1141   if (treelist == NULL)
1142     {
1143       treelist = l;
1144       return;
1145     }
1146   tt = (struct subtree*) treelist->data;
1147   if (oid_compare(name, namelen, tt->name, tt->name_len) < 0)
1148     {
1149       l->next = treelist;
1150       treelist = l;
1151       return;
1152     }
1153   for (ll = treelist; ll->next; ll=ll->next)
1154     {
1155       tt = (struct subtree*) ll->next->data;
1156       if (oid_compare(name, namelen, tt->name, tt->name_len) < 0)
1157         {
1158           l->next = ll->next;
1159           ll->next = l;
1160           return;
1161         }
1162     }
1163   ll->next = l;
1164 }
1165
1166 void
1167 smux_start(void)
1168 {
1169   rad_snmp.smux_event=SMUX_CONNECT;
1170   smux_connect();
1171 }
1172 #endif /* WITH_SNMP */