import from branch_1_1:
[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 *, int, oid *, int);
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, int o1_len, oid *o2, int o2_len)
94 {
95         int 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, int o1_len, oid *o2, int o2_len)
113 {
114         int 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         int 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, const void *arg, size_t arg_len)
230 {
231         int ret;
232         u_char buf[BUFSIZ];
233         u_char *ptr, *h1, *h1e, *h2, *h2e;
234         int 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 char *
285 smux_var (char *ptr, int 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         int 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,const void **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         int 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, const void **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         int 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 char *
582 smux_parse_get_header (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 (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 (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         const void *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 (char *ptr, int 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 (char *ptr, int len)
683 {
684         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 (char *ptr, int 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 int 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         int len;
845         u_long smux_proto_version;
846         u_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                         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                         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         int len, ret;
895         long priority;
896         long operation;
897         struct subtree *subtree;
898         struct list *l;
899
900         ret = 0;
901
902         for (l = treelist; l; l=l->next) {
903                 subtree = l->data;
904
905                 ptr = buf;
906                 len = BUFSIZ;
907
908                 /* SMUX RReq Header. */
909                 ptr = asn_build_header (ptr, &len, (u_char) SMUX_RREQ, 0);
910
911                 /* Register MIB tree. */
912                 ptr = asn_build_objid (ptr, &len,
913                                 (u_char)
914                                 (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
915                                 subtree->name, subtree->name_len);
916
917                 /* Priority. */
918                 priority = -1;
919                 ptr = asn_build_int (ptr, &len,
920                                 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
921                                 &priority, sizeof (u_long));
922
923                 /* Operation. */
924                 operation = rad_snmp.snmp_write_access ? 2 : 1; /* Register R/O or R/W */
925                 ptr = asn_build_int (ptr, &len,
926                                 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
927                                 &operation, sizeof (u_long));
928
929                 smux_oid_dump ("SMUX register oid", subtree->name, subtree->name_len);
930                 DEBUG2 ("SMUX register priority: %ld", priority);
931                 DEBUG2 ("SMUX register operation: %ld", operation);
932
933                 len = BUFSIZ;
934                 asn_build_header (buf, &len, (u_char) SMUX_RREQ, (ptr - buf) - 2);
935                 ret = send (rad_snmp.smux_fd, buf, (ptr - buf), 0);
936                 if (ret < 0) {
937                         return ret;
938                 }
939         }
940         return ret;
941 }
942
943 /* Try to connect to SNMP agent. */
944 int
945 smux_connect ()
946 {
947         int ret;
948
949         rad_snmp.smux_event=SMUX_NONE;
950         DEBUG2 ("SMUX connect try %d", rad_snmp.smux_failures + 1);
951
952         /* Make socket. Try to connect. */
953         rad_snmp.smux_fd = smux_sock ();
954         if (rad_snmp.smux_fd < 0) {
955                 if (++rad_snmp.smux_failures < rad_snmp.smux_max_failures)
956                         rad_snmp.smux_event=SMUX_CONNECT;
957                 return 0;
958         }
959
960         /* Send OPEN PDU. */
961         ret = smux_open ();
962         if (ret < 0) {
963                 DEBUG ("SMUX open message send failed: %s", strerror (errno));
964                 close (rad_snmp.smux_fd);
965                 rad_snmp.smux_fd = -1;
966                 rad_snmp.smux_event=SMUX_CONNECT;
967                 return -1;
968         }
969
970         /* Send any outstanding register PDUs. */
971         ret = smux_register ();
972         if (ret < 0) {
973                 DEBUG ("SMUX register 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         /* Everything goes fine. */
981         rad_snmp.smux_event=SMUX_READ;
982
983         return 0;
984 }
985
986 /* Clear all SMUX related resources. */
987 void
988 smux_stop(void)
989 {
990         rad_snmp.smux_event=SMUX_NONE;
991         if (rad_snmp.smux_fd >= 0)
992                 close (rad_snmp.smux_fd);
993         rad_snmp.smux_fd = -1;
994 }
995
996 int
997 smux_str2oid (char *str, oid *my_oid, size_t *oid_len)
998 {
999         int len;
1000         int val;
1001
1002         len = 0;
1003         val = 0;
1004         *oid_len = 0;
1005
1006         if (*str == '.')
1007                 str++;
1008         if (*str == '\0')
1009                 return 0;
1010
1011         while (1) {
1012                 if (! isdigit ((int) *str))
1013                         return -1;
1014
1015                 while (isdigit ((int) *str)) {
1016                         val *= 10;
1017                         val += (*str - '0');
1018                         str++;
1019                 }
1020
1021                 if (*str == '\0')
1022                         break;
1023                 if (*str != '.')
1024                         return -1;
1025
1026                 my_oid[len++] = val;
1027                 val = 0;
1028                 str++;
1029         }
1030
1031         my_oid[len++] = val;
1032         *oid_len = len;
1033
1034         return 0;
1035 }
1036
1037 oid *
1038 smux_oid_dup (oid *objid, size_t objid_len)
1039 {
1040         oid *new;
1041
1042         new = (oid *)rad_malloc(sizeof (oid) * objid_len);
1043         oid_copy (new, objid, objid_len);
1044
1045         return new;
1046 }
1047
1048 int
1049 smux_header_generic (struct variable *v, oid *name, size_t *length, int exact,
1050                 size_t *var_len, WriteMethod **write_method)
1051 {
1052         oid fulloid[MAX_OID_LEN];
1053         int ret;
1054
1055         oid_copy (fulloid, v->name, v->namelen);
1056         fulloid[v->namelen] = 0;
1057         /* Check against full instance. */
1058         ret = oid_compare (name, *length, fulloid, v->namelen + 1);
1059
1060         /* Check single instance. */
1061         if ((exact && (ret != 0)) || (!exact && (ret >= 0)))
1062         return MATCH_FAILED;
1063
1064         /* In case of getnext, fill in full instance. */
1065         memcpy (name, fulloid, (v->namelen + 1) * sizeof (oid));
1066         *length = v->namelen + 1;
1067
1068         *write_method = 0;
1069         *var_len = sizeof(long);                /* default to 'long' results */
1070
1071         return MATCH_SUCCEEDED;
1072 }
1073
1074 /* Initialize some values then schedule first SMUX connection. */
1075 void
1076 smux_init (oid defoid[], size_t defoid_len)
1077 {
1078         smux_oid = defoid;
1079         smux_oid_len = defoid_len;
1080 }
1081
1082 /* Register subtree to smux master tree. */
1083 void
1084 smux_register_mib(UNUSED const char *descr, struct variable *var, size_t width,
1085                 int num, oid name[], size_t namelen)
1086 {
1087         struct subtree *tree, *tt;
1088         struct list *l, *ll;
1089
1090         tree = (struct subtree *)rad_malloc(sizeof(struct subtree));
1091         oid_copy (tree->name, name, namelen);
1092         tree->name_len = namelen;
1093         tree->variables = var;
1094         tree->variables_num = num;
1095         tree->variables_width = width;
1096         tree->registered = 0;
1097         l = (struct list *)rad_malloc(sizeof(struct list));
1098         l->data = tree;
1099         l->next = NULL;
1100 /* Build a treelist sorted by the name. This makes GETNEXT simpler */
1101         if (treelist == NULL) {
1102                 treelist = l;
1103                 return;
1104         }
1105         tt = (struct subtree*) treelist->data;
1106         if (oid_compare(name, namelen, tt->name, tt->name_len) < 0) {
1107                 l->next = treelist;
1108                 treelist = l;
1109                 return;
1110         }
1111         for (ll = treelist; ll->next; ll=ll->next) {
1112                 tt = (struct subtree*) ll->next->data;
1113                 if (oid_compare(name, namelen, tt->name, tt->name_len) < 0) {
1114                         l->next = ll->next;
1115                         ll->next = l;
1116                         return;
1117                 }
1118         }
1119         ll->next = l;
1120 }
1121
1122 void
1123 smux_start(void)
1124 {
1125         rad_snmp.smux_event=SMUX_CONNECT;
1126         smux_connect();
1127 }
1128 #endif /* WITH_SNMP */