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