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