]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ypldap/yp.c
bhnd(9): Fix a few mandoc related issues
[FreeBSD/FreeBSD.git] / usr.sbin / ypldap / yp.c
1 /*      $OpenBSD: yp.c,v 1.14 2015/02/11 01:26:00 pelikan Exp $ */
2 /*      $FreeBSD$ */
3 /*
4  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <sys/types.h>
20 #include <sys/param.h>
21 #include <sys/queue.h>
22 #include <sys/socket.h>
23 #include <sys/select.h>
24 #include <sys/tree.h>
25
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28
29 #include <errno.h>
30 #include <event.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <pwd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <limits.h>
38
39 #include <rpc/rpc.h>
40 #include <rpc/xdr.h>
41 #include <rpc/pmap_clnt.h>
42 #include <rpc/pmap_prot.h>
43 #include <rpc/pmap_rmt.h>
44 #include <rpcsvc/yp.h>
45 #include <rpcsvc/ypclnt.h>
46
47 #include "ypldap.h"
48
49 void    yp_dispatch(struct svc_req *, SVCXPRT *);
50 void    yp_disable_events(void);
51 void    yp_fd_event(int, short, void *);
52 int     yp_check(struct svc_req *);
53 int     yp_valid_domain(char *, struct ypresp_val *);
54 void    yp_make_val(struct ypresp_val *, char *, int);
55 void    yp_make_keyval(struct ypresp_key_val *, char *, char *);
56
57 static struct env       *env;
58
59 struct yp_event {
60         TAILQ_ENTRY(yp_event)    ye_entry;
61         struct event             ye_event;
62 };
63
64 struct yp_data {
65         SVCXPRT                 *yp_trans_udp;
66         SVCXPRT                 *yp_trans_tcp;
67         TAILQ_HEAD(, yp_event)   yd_events;
68 };
69
70 void
71 yp_disable_events(void)
72 {
73         struct yp_event *ye;
74
75         while ((ye = TAILQ_FIRST(&env->sc_yp->yd_events)) != NULL) {
76                 TAILQ_REMOVE(&env->sc_yp->yd_events, ye, ye_entry);
77                 event_del(&ye->ye_event);
78                 free(ye);
79         }
80 }
81
82 void
83 yp_enable_events(void)
84 {
85         int i;
86         struct yp_event *ye;
87
88         for (i = 0; i < getdtablesize(); i++) {
89                 if ((ye = calloc(1, sizeof(*ye))) == NULL)
90                         fatal(NULL);
91                 event_set(&ye->ye_event, i, EV_READ, yp_fd_event, NULL);
92                 event_add(&ye->ye_event, NULL);
93                 TAILQ_INSERT_TAIL(&env->sc_yp->yd_events, ye, ye_entry);
94         }
95 }
96
97 void
98 yp_fd_event(int fd, short event, void *p)
99 {
100         svc_getreq_common(fd);
101         yp_disable_events();
102         yp_enable_events();
103 }
104
105 void
106 yp_init(struct env *x_env)
107 {
108         struct yp_data  *yp;
109
110         if ((yp = calloc(1, sizeof(*yp))) == NULL)
111                 fatal(NULL);
112         TAILQ_INIT(&yp->yd_events);
113
114         env = x_env;
115         env->sc_yp = yp;
116         
117         (void)pmap_unset(YPPROG, YPVERS);
118
119         if ((yp->yp_trans_udp = svcudp_create(RPC_ANYSOCK)) == NULL)
120                 fatal("cannot create udp service");
121         if ((yp->yp_trans_tcp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL)
122                 fatal("cannot create tcp service");
123
124         if (!svc_register(yp->yp_trans_udp, YPPROG, YPVERS,
125             yp_dispatch, IPPROTO_UDP)) {
126                 fatal("unable to register (YPPROG, YPVERS, udp)");
127         }
128         if (!svc_register(yp->yp_trans_tcp, YPPROG, YPVERS,
129             yp_dispatch, IPPROTO_TCP)) {
130                 fatal("unable to register (YPPROG, YPVERS, tcp)");
131         }
132 }
133
134 /*
135  * lots of inspiration from ypserv by Mats O Jansson
136  */
137 void
138 yp_dispatch(struct svc_req *req, SVCXPRT *trans)
139 {
140         xdrproc_t                xdr_argument;
141         xdrproc_t                xdr_result;
142         char                    *result;
143         char                    *(*cb)(char *, struct svc_req *);
144         union {
145                 domainname       ypproc_domain_2_arg;
146                 domainname       ypproc_domain_nonack_2_arg;
147                 ypreq_key        ypproc_match_2_arg;
148                 ypreq_nokey      ypproc_first_2_arg;
149                 ypreq_key        ypproc_next_2_arg;
150                 ypreq_xfr        ypproc_xfr_2_arg;
151                 ypreq_nokey      ypproc_all_2_arg;
152                 ypreq_nokey      ypproc_master_2_arg;
153                 ypreq_nokey      ypproc_order_2_arg;
154                 domainname       ypproc_maplist_2_arg;
155         } argument;
156
157         xdr_argument = (xdrproc_t) xdr_void;
158         xdr_result = (xdrproc_t) xdr_void;
159         cb = NULL;
160         switch (req->rq_proc) {
161         case YPPROC_NULL:
162                 xdr_argument = (xdrproc_t) xdr_void;
163                 xdr_result = (xdrproc_t) xdr_void;
164                 if (yp_check(req) == -1)
165                         return;
166                 result = NULL;
167                 if (!svc_sendreply(trans, (xdrproc_t) xdr_void,
168                     (void *)&result))
169                         svcerr_systemerr(trans);
170                 return;
171         case YPPROC_DOMAIN:
172                 xdr_argument = (xdrproc_t) xdr_domainname;
173                 xdr_result = (xdrproc_t) xdr_bool;
174                 if (yp_check(req) == -1)
175                         return;
176                 cb = (void *)ypproc_domain_2_svc;
177                 break;
178         case YPPROC_DOMAIN_NONACK:
179                 xdr_argument = (xdrproc_t) xdr_domainname;
180                 xdr_result = (xdrproc_t) xdr_bool;
181                 if (yp_check(req) == -1)
182                         return;
183                 cb = (void *)ypproc_domain_nonack_2_svc;
184                 break;
185         case YPPROC_MATCH:
186                 xdr_argument = (xdrproc_t) xdr_ypreq_key;
187                 xdr_result = (xdrproc_t) xdr_ypresp_val;
188                 if (yp_check(req) == -1)
189                         return;
190                 cb = (void *)ypproc_match_2_svc;
191                 break;
192         case YPPROC_FIRST:
193                 xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
194                 xdr_result = (xdrproc_t) xdr_ypresp_key_val;
195                 if (yp_check(req) == -1)
196                         return;
197                 cb = (void *)ypproc_first_2_svc;
198                 break;
199         case YPPROC_NEXT:
200                 xdr_argument = (xdrproc_t) xdr_ypreq_key;
201                 xdr_result = (xdrproc_t) xdr_ypresp_key_val;
202                 if (yp_check(req) == -1)
203                         return;
204                 cb = (void *)ypproc_next_2_svc;
205                 break;
206         case YPPROC_XFR:
207                 if (yp_check(req) == -1)
208                         return;
209                 svcerr_noproc(trans);
210                 return;
211         case YPPROC_CLEAR:
212                 log_debug("ypproc_clear");
213                 if (yp_check(req) == -1)
214                         return;
215                 svcerr_noproc(trans);
216                 return;
217         case YPPROC_ALL:
218                 log_debug("ypproc_all");
219                 xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
220                 xdr_result = (xdrproc_t) xdr_ypresp_all;
221                 if (yp_check(req) == -1)
222                         return;
223                 cb = (void *)ypproc_all_2_svc;
224                 break;
225         case YPPROC_MASTER:
226                 log_debug("ypproc_master");
227                 xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
228                 xdr_result = (xdrproc_t) xdr_ypresp_master;
229                 if (yp_check(req) == -1)
230                         return;
231                 cb = (void *)ypproc_master_2_svc;
232                 break;
233         case YPPROC_ORDER:
234                 log_debug("ypproc_order");
235                 if (yp_check(req) == -1)
236                         return;
237                 svcerr_noproc(trans);
238                 return;
239         case YPPROC_MAPLIST:
240                 log_debug("ypproc_maplist");
241                 xdr_argument = (xdrproc_t) xdr_domainname;
242                 xdr_result = (xdrproc_t) xdr_ypresp_maplist;
243                 if (yp_check(req) == -1)
244                         return;
245                 cb = (void *)ypproc_maplist_2_svc;
246                 break;
247         default:
248                 svcerr_noproc(trans);
249                 return;
250         }
251         (void)memset(&argument, 0, sizeof(argument));
252
253         if (!svc_getargs(trans, xdr_argument, (caddr_t)&argument)) {
254                 svcerr_decode(trans);
255                 return;
256         }
257         result = (*cb)((char *)&argument, req);
258         if (result != NULL && !svc_sendreply(trans, xdr_result, result))
259                 svcerr_systemerr(trans);
260         if (!svc_freeargs(trans, xdr_argument, (caddr_t)&argument)) {
261                 /*
262                  * ypserv does it too.
263                  */
264                 fatal("unable to free arguments");
265         }
266 }
267
268 int
269 yp_check(struct svc_req *req)
270 {
271         struct sockaddr_in      *caller;
272
273         caller = svc_getcaller(req->rq_xprt);
274         /*
275          * We might want to know who we allow here.
276          */
277         return (0);
278 }
279
280 int
281 yp_valid_domain(char *domain, struct ypresp_val *res)
282 {
283         if (domain == NULL) {
284                 log_debug("NULL domain !");
285                 return (-1);
286         }
287         if (strcmp(domain, env->sc_domainname) != 0) {
288                 res->stat = YP_NODOM;
289                 return (-1);
290         }
291         return (0);
292 }
293
294 bool_t *
295 ypproc_domain_2_svc(domainname *arg, struct svc_req *req)
296 {
297         static bool_t   res;
298         
299         res = (bool_t)1;
300         if (strcmp(*arg, env->sc_domainname) != 0)
301                 res = (bool_t)0;
302         return (&res);
303 }
304
305 bool_t *
306 ypproc_domain_nonack_2_svc(domainname *arg, struct svc_req *req)
307 {
308         static bool_t   res;
309         
310         if (strcmp(*arg, env->sc_domainname) != 0)
311                 return NULL;
312         res = (bool_t)1;
313         return (&res);
314 }
315
316 ypresp_val *
317 ypproc_match_2_svc(ypreq_key *arg, struct svc_req *req)
318 {
319         struct userent           ukey;
320         struct userent          *ue;
321         struct groupent          gkey;
322         struct groupent         *ge;
323         static struct ypresp_val res;
324         const char              *estr;
325         char                    *bp, *cp;
326         char                     *key;
327
328         log_debug("matching '%.*s' in map %s", arg->key.keydat_len,
329            arg->key.keydat_val, arg->map);
330
331         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
332                 return (&res);
333
334         if (env->sc_user_names == NULL) {
335                 /*
336                  * tree not ready.
337                  */
338                 return (NULL);
339         }
340
341         if (arg->key.keydat_len > YPMAXRECORD) {
342                 log_debug("argument too long");
343                 return (NULL);
344         }
345         key = calloc(arg->key.keydat_len + 1, 1);
346         if (key == NULL)
347                 return (NULL);
348         (void)strncpy(key, arg->key.keydat_val, arg->key.keydat_len);
349
350         if (strcmp(arg->map, "passwd.byname") == 0 ||
351             strcmp(arg->map, "master.passwd.byname") == 0) {
352                 ukey.ue_line = key;
353                 if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
354                     &ukey)) == NULL) {
355                         res.stat = YP_NOKEY;
356                         goto out;
357                 }
358
359                 yp_make_val(&res, ue->ue_line, 1);
360                 goto out;
361         } else if (strcmp(arg->map, "passwd.byuid") == 0 ||
362                    strcmp(arg->map, "master.passwd.byuid") == 0) {
363                 ukey.ue_uid = strtonum(key, 0, UID_MAX, &estr); 
364                 if (estr) {
365                         res.stat = YP_BADARGS;
366                         goto out;
367                 }
368
369                 if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
370                     &ukey)) == NULL) {
371                         res.stat = YP_NOKEY;
372                         goto out;
373                 }
374
375                 yp_make_val(&res, ue->ue_line, 1);
376                 return (&res);
377         } else if (strcmp(arg->map, "group.bygid") == 0) {
378                 gkey.ge_gid = strtonum(key, 0, GID_MAX, &estr); 
379                 if (estr) {
380                         res.stat = YP_BADARGS;
381                         goto out;
382                 }
383                 if ((ge = RB_FIND(group_gid_tree, &env->sc_group_gids,
384                     &gkey)) == NULL) {
385                         res.stat = YP_NOKEY;
386                         goto out;
387                 }
388
389                 yp_make_val(&res, ge->ge_line, 1);
390                 return (&res);
391         } else if (strcmp(arg->map, "group.byname") == 0) {
392                 gkey.ge_line = key;
393                 if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
394                     &gkey)) == NULL) {
395                         res.stat = YP_NOKEY;
396                         goto out;
397                 }
398
399                 yp_make_val(&res, ge->ge_line, 1);
400                 return (&res);
401         } else if (strcmp(arg->map, "netid.byname") == 0) {
402                 bp = cp = key;
403
404                 if (strncmp(bp, "unix.", strlen("unix.")) != 0) {
405                         res.stat = YP_BADARGS;
406                         goto out;
407                 }
408
409                 bp += strlen("unix.");
410
411                 if (*bp == '\0') {
412                         res.stat = YP_BADARGS;
413                         goto out;
414                 }
415
416                 if (!(cp = strsep(&bp, "@"))) {
417                         res.stat = YP_BADARGS;
418                         goto out;
419                 }
420
421                 if (strcmp(bp, arg->domain) != 0) {
422                         res.stat = YP_BADARGS;
423                         goto out;
424                 }
425
426                 ukey.ue_uid = strtonum(cp, 0, UID_MAX, &estr); 
427                 if (estr) {
428                         res.stat = YP_BADARGS;
429                         goto out;
430                 }
431
432                 if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
433                     &ukey)) == NULL) {
434                         res.stat = YP_NOKEY;
435                         goto out;
436                 }
437
438                 yp_make_val(&res, ue->ue_netid_line, 0);
439                 goto out;
440         
441         } else {
442                 log_debug("unknown map %s", arg->map);
443                 res.stat = YP_NOMAP;
444                 goto out;
445         }
446 out:
447         free(key);
448         return (&res);
449 }
450
451 ypresp_key_val *
452 ypproc_first_2_svc(ypreq_nokey *arg, struct svc_req *req)
453 {
454         static struct ypresp_key_val    res;
455
456         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
457                 return (&res);
458
459         if (strcmp(arg->map, "passwd.byname") == 0 ||
460             strcmp(arg->map, "master.passwd.byname") == 0) {
461                 if (env->sc_user_lines == NULL)
462                         return (NULL);
463
464                 yp_make_keyval(&res, env->sc_user_lines, env->sc_user_lines);
465         } else if (strcmp(arg->map, "group.byname") == 0) {
466                 if (env->sc_group_lines == NULL)
467                         return (NULL);
468
469                 yp_make_keyval(&res, env->sc_group_lines, env->sc_group_lines);
470         } else {
471                 log_debug("unknown map %s", arg->map);
472                 res.stat = YP_NOMAP;
473         }
474
475         return (&res);
476 }
477
478 ypresp_key_val *
479 ypproc_next_2_svc(ypreq_key *arg, struct svc_req *req)
480 {
481         struct userent                   ukey;
482         struct userent                  *ue;
483         struct groupent                  gkey;
484         struct groupent                 *ge;
485         char                            *line;
486         static struct ypresp_key_val     res;
487         char                             *key;
488
489         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
490                 return (&res);
491
492         key = NULL;
493         if (strcmp(arg->map, "passwd.byname") == 0 ||
494             strcmp(arg->map, "master.passwd.byname") == 0) {
495                 key = calloc(arg->key.keydat_len + 1, 1);
496                 if (key == NULL) {
497                         res.stat = YP_YPERR;
498                         return (&res);
499                 }
500                 (void)strncpy(key, arg->key.keydat_val,
501                     arg->key.keydat_len);
502                 ukey.ue_line = key;
503                 if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
504                     &ukey)) == NULL) {
505                         /*
506                          * canacar's trick:
507                          * the user might have been deleted in between calls
508                          * to next since the tree may be modified by a reload.
509                          * next should still return the next user in
510                          * lexicographical order, hence insert the search key
511                          * and look up the next field, then remove it again.
512                          */
513                         RB_INSERT(user_name_tree, env->sc_user_names, &ukey);
514                         if ((ue = RB_NEXT(user_name_tree, &env->sc_user_names,
515                             &ukey)) == NULL) {
516                                 RB_REMOVE(user_name_tree, env->sc_user_names,
517                                     &ukey);
518                                 res.stat = YP_NOKEY;
519                                 free(key);
520                                 return (&res);
521                         }
522                         RB_REMOVE(user_name_tree, env->sc_user_names, &ukey);
523                 }
524                 line = ue->ue_line + (strlen(ue->ue_line) + 1);
525                 line = line + (strlen(line) + 1);
526                 yp_make_keyval(&res, line, line);
527                 free(key);
528                 return (&res);
529
530
531         } else if (strcmp(arg->map, "group.byname") == 0) {
532                 key = calloc(arg->key.keydat_len + 1, 1);
533                 if (key == NULL) {
534                         res.stat = YP_YPERR;
535                         return (&res);
536                 }
537                 (void)strncpy(key, arg->key.keydat_val,
538                     arg->key.keydat_len);
539                 
540                 gkey.ge_line = key;
541                 if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
542                     &gkey)) == NULL) {
543                         /*
544                          * canacar's trick reloaded.
545                          */
546                         RB_INSERT(group_name_tree, env->sc_group_names, &gkey);
547                         if ((ge = RB_NEXT(group_name_tree, &env->sc_group_names,
548                             &gkey)) == NULL) {
549                                 RB_REMOVE(group_name_tree, env->sc_group_names,
550                                     &gkey);
551                                 res.stat = YP_NOKEY;
552                                 free(key);
553                                 return (&res);
554                         }
555                         RB_REMOVE(group_name_tree, env->sc_group_names, &gkey);
556                 }
557
558                 line = ge->ge_line + (strlen(ge->ge_line) + 1);
559                 line = line + (strlen(line) + 1);
560                 yp_make_keyval(&res, line, line);
561                 free(key);
562                 return (&res);
563         } else {
564                 log_debug("unknown map %s", arg->map);
565                 res.stat = YP_NOMAP;
566                 return (&res);
567         }
568 }
569
570 ypresp_all *
571 ypproc_all_2_svc(ypreq_nokey *arg, struct svc_req *req)
572 {
573         static struct ypresp_all        res;
574
575         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
576                 return (&res);
577
578         svcerr_auth(req->rq_xprt, AUTH_FAILED);
579         return (NULL);
580 }
581
582 ypresp_master *
583 ypproc_master_2_svc(ypreq_nokey *arg, struct svc_req *req)
584 {
585         static struct ypresp_master      res;
586         static char master[YPMAXPEER + 1];
587
588         memset(&res, 0, sizeof(res));
589         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
590                 return (&res);
591         
592         if (gethostname(master, sizeof(master)) == 0) {
593                 res.peer = (peername)master;
594                 res.stat = YP_TRUE;
595         } else
596                 res.stat = YP_NOKEY;
597
598         return (&res);
599 }
600
601 ypresp_maplist *
602 ypproc_maplist_2_svc(domainname *arg, struct svc_req *req)
603 {
604         size_t                   i;
605         static struct {
606                 char            *name;
607                 int              cond;
608         }                        mapnames[] = {
609                 { "passwd.byname",              YPMAP_PASSWD_BYNAME },
610                 { "passwd.byuid",               YPMAP_PASSWD_BYUID },
611                 { "master.passwd.byname",       YPMAP_MASTER_PASSWD_BYNAME },
612                 { "master.passwd.byuid",        YPMAP_MASTER_PASSWD_BYUID },
613                 { "group.byname",               YPMAP_GROUP_BYNAME },
614                 { "group.bygid",                YPMAP_GROUP_BYGID },
615                 { "netid.byname",               YPMAP_NETID_BYNAME },
616         };
617         static ypresp_maplist    res;
618         static struct ypmaplist  maps[nitems(mapnames)];
619         
620         if (yp_valid_domain(*arg, (struct ypresp_val *)&res) == -1)
621                 return (&res);
622
623         res.stat = YP_TRUE;
624         res.maps = NULL;
625         for (i = 0; i < nitems(mapnames); i++) {
626                 if (!(env->sc_flags & mapnames[i].cond))
627                         continue;
628                 maps[i].map = mapnames[i].name;
629                 maps[i].next = res.maps;
630                 res.maps = &maps[i];
631         }
632
633         return (&res);
634 }
635
636 void
637 yp_make_val(struct ypresp_val *res, char *line, int replacecolon)
638 {
639         static char              buf[LINE_WIDTH];
640
641         memset(buf, 0, sizeof(buf));
642
643         if (replacecolon)
644                 line[strlen(line)] = ':';
645         (void)strlcpy(buf, line, sizeof(buf));
646         if (replacecolon)
647                 line[strcspn(line, ":")] = '\0';
648         log_debug("sending out %s", buf);
649
650         res->stat = YP_TRUE;
651         res->val.valdat_len = strlen(buf);
652         res->val.valdat_val = buf;
653 }
654
655 void
656 yp_make_keyval(struct ypresp_key_val *res, char *key, char *line)
657 {
658         static char     keybuf[YPMAXRECORD+1];
659         static char     buf[LINE_WIDTH];
660
661         memset(keybuf, 0, sizeof(keybuf));
662         memset(buf, 0, sizeof(buf));
663         
664         (void)strlcpy(keybuf, key, sizeof(keybuf));
665         res->key.keydat_len = strlen(keybuf);
666         res->key.keydat_val = keybuf;
667
668         if (*line == '\0') {
669                 res->stat = YP_NOMORE;
670                 return;
671         }
672         res->stat = YP_TRUE;
673         line[strlen(line)] = ':';
674         (void)strlcpy(buf, line, sizeof(buf));
675         line[strcspn(line, ":")] = '\0';
676         log_debug("sending out %s => %s", keybuf, buf);
677
678         res->val.valdat_len = strlen(buf);
679         res->val.valdat_val = buf;
680 }