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