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