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