1 /* daemon.c -- Server daemon
2  * Created: Fri Feb 28 18:17:56 1997 by faith@dict.org
3  * Copyright 1997, 1998, 1999, 2000, 2002 Rickard E. Faith (faith@dict.org)
4  * Copyright 2002-2008 Aleksey Cheusov (vle@gmx.net)
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 1, or (at your option) any
9  * later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #include "dictd.h"
22 #include "index.h"
23 #include "data.h"
24 #include "md5.h"
25 #include "regex.h"
26 #include "dictdplugin.h"
27 #include "strategy.h"
28 #include "plugin.h"
29 
30 #include <ctype.h>
31 #include <setjmp.h>
32 
33 int stdin2stdout_mode = 0; /* copy stdin to stdout ( dict_dictd function ) */
34 
35 static int          _dict_defines, _dict_matches;
36 static int          daemonS_in  = 0;
37 static int          daemonS_out = 1;
38 static const char   *daemonHostname  = NULL;
39 static const char   *daemonIP        = NULL;
40 static int          daemonPort       = -1;
41 static char         daemonStamp[256] = "";
42 static jmp_buf      env;
43 
44 static int          daemonMime;
45 
46 static void daemon_define( const char *cmdline, int argc, const char **argv );
47 static void daemon_match( const char *cmdline, int argc, const char **argv );
48 static void daemon_show_db( const char *cmdline, int argc, const char **argv );
49 static void daemon_show_strat( const char *cmdline, int argc, const char **argv );
50 void daemon_show_info( const char *cmdline, int argc, const char **argv );
51 static void daemon_show_server( const char *cmdline, int argc, const char **argv );
52 static void daemon_show( const char *cmdline, int argc, const char **argv );
53 static void daemon_option_mime( const char *cmdline, int argc, const char **argv );
54 static void daemon_option( const char *cmdline, int argc, const char **argv );
55 static void daemon_client( const char *cmdline, int argc, const char **argv );
56 static void daemon_auth( const char *cmdline, int argc, const char **argv );
57 static void daemon_status( const char *cmdline, int argc, const char **argv );
58 static void daemon_help( const char *cmdline, int argc, const char **argv );
59 static void daemon_quit( const char *cmdline, int argc, const char **argv );
60 
61 typedef void (*command_t)( const char *cmdline, int argc, const char **argv );
62 
63 #define MAXARGCS 3
64 static struct {
65    int        argc;
66    const char *name[MAXARGCS];
67    command_t  f;
68 } commandInfo[] = {
69    { 1, {"define"},             daemon_define },
70    { 1, {"d"},                  daemon_define },
71    { 1, {"match"},              daemon_match },
72    { 1, {"m"},                  daemon_match },
73    { 2, {"show", "db"},         daemon_show_db },
74    { 2, {"show", "databases"},  daemon_show_db },
75    { 2, {"show", "strat"},      daemon_show_strat },
76    { 2, {"show", "strategies"}, daemon_show_strat },
77    { 2, {"show", "info"},       daemon_show_info },
78    { 2, {"show", "server"},     daemon_show_server },
79    { 1, {"show"},               daemon_show },
80    { 2, {"option", "mime"},     daemon_option_mime },
81    { 1, {"option"},             daemon_option },
82    { 1, {"client"},             daemon_client },
83    { 1, {"auth"},               daemon_auth },
84    { 1, {"status"},             daemon_status },
85    { 1, {"s"},                  daemon_status },
86    { 1, {"help"},               daemon_help },
87    { 1, {"h"},                  daemon_help },
88    { 1, {"quit"},               daemon_quit },
89    { 1, {"q"},                  daemon_quit },
90 };
91 #define COMMANDS (sizeof(commandInfo)/sizeof(commandInfo[0]))
92 
lookup_command(int argc,const char ** argv)93 static command_t lookup_command( int argc, const char **argv )
94 {
95    size_t i;
96    int j;
97    int err;
98 
99    for (i = 0; i < COMMANDS; i++) {
100       if (argc >= commandInfo[i].argc) {
101 	 for (err = 0, j = 0; j < commandInfo[i].argc; j++) {
102 	    if (strcasecmp(argv[j], commandInfo[i].name[j])) {
103 	       err = 1;
104 	       break;
105 	    }
106 	 }
107 	 if (!err) return commandInfo[i].f;
108       }
109    }
110    return NULL;
111 }
112 
daemon_compute_mask(int bits)113 static unsigned long daemon_compute_mask(int bits)
114 {
115    unsigned long mask = 0xffffffff;
116 
117    if (bits < 1) return 0;
118    if (bits < 32) mask -= (1 << (32-bits)) - 1;
119    return mask;
120 }
121 
daemon_log(int type,const char * format,...)122 static void daemon_log( int type, const char *format, ... )
123 {
124    va_list ap;
125    char    buf[8*1024];
126    char    *buf2;
127    size_t  len;
128    char    *s, *d;
129    int     c;
130    char    marker = '?';
131 
132    switch (type) {
133    case DICT_LOG_TERM:
134       if (!flg_test(LOG_STATS))    return; marker = 'I'; break;
135    case DICT_LOG_TRACE:
136       if (!flg_test(LOG_SERVER))   return; marker = 'I'; break;
137    case DICT_LOG_CLIENT:
138       if (!flg_test(LOG_CLIENT))   return; marker = 'C'; break;
139    case DICT_LOG_CONNECT:
140       if (!flg_test(LOG_CONNECT))  return; marker = 'K'; break;
141    case DICT_LOG_DEFINE:
142       if (!flg_test(LOG_FOUND))    return; marker = 'D'; break;
143    case DICT_LOG_MATCH:
144       if (!flg_test(LOG_FOUND))    return; marker = 'M'; break;
145    case DICT_LOG_NOMATCH:
146       if (!flg_test(LOG_NOTFOUND)) return; marker = 'N'; break;
147    case DICT_LOG_COMMAND:
148       if (!flg_test(LOG_COMMAND))  return; marker = 'T'; break;
149    case DICT_LOG_AUTH:
150       if (!flg_test(LOG_AUTH))     return; marker = 'A'; break;
151    }
152 
153    if (dbg_test(DBG_PORT))
154       snprintf(
155 	 buf, sizeof (buf)/2,
156 	 ":%c: %s:%d ", marker, daemonHostname, daemonPort );
157    else if (flg_test(LOG_HOST))
158       snprintf(
159 	 buf, sizeof (buf)/2,
160 	 ":%c: %s ", marker, daemonHostname );
161    else
162       snprintf(
163 	 buf, sizeof (buf)/2,
164 	 ":%c: ", marker );
165 
166    len = strlen( buf );
167 
168    va_start( ap, format );
169    vsnprintf( buf+len, sizeof (buf)/2-len, format, ap );
170    va_end( ap );
171    len = strlen( buf );
172 
173    if (len >= sizeof (buf)/2) {
174       log_info( ":E: buffer overflow (%d)\n", len );
175       buf[2048] = '\0';
176       len = strlen(buf);
177    }
178 
179    buf2 = alloca( 3*(len+3) );
180 
181    for (s = buf, d = buf2; *s; s++) {
182       c = (unsigned char)*s;
183       if (c == '\t')      *d++ = ' ';
184       else if (c == '\n') *d++ = c;
185       else {
186 	 if (c < 32)        { *d++ = '^'; *d++ = c + 64;           }
187 	 else if (c == 127) { *d++ = '^'; *d++ = '?';              }
188 	 else                 *d++ = c;
189       }
190    }
191    *d = '\0';
192    log_info( "%s", buf2 );
193 
194    if (d != buf2) d[-1] = '\0';	/* kill newline */
195    dict_setproctitle( "dictd %s", buf2 );
196 }
197 
daemon_check_mask(const char * spec,const char * ip)198 static int daemon_check_mask(const char *spec, const char *ip)
199 {
200    char           *tmp = alloca(strlen(spec) + 1);
201    char           *pt;
202    char           tstring[64], mstring[64];
203    struct in_addr target, mask;
204    int            bits;
205    unsigned long  bitmask;
206 
207    strcpy(tmp, spec);
208    if (!(pt = strchr(tmp, '/'))) {
209       log_info( ":E: No / in %s, denying access to %s\n", spec, ip);
210       return DICT_DENY;
211    }
212    *pt++ = '\0';
213    if (!*pt) {
214       log_info( ":E: No bit count after / in %s, denying access to %s\n",
215                 spec, ip);
216       return DICT_DENY;
217    }
218 
219    inet_aton(ip, &target);
220    inet_aton(tmp, &mask);
221    bits = strtol(pt, NULL, 10);
222    strcpy(tstring, inet_ntoa(target));
223    strcpy(mstring, inet_ntoa(mask));
224    if (bits < 0 || bits > 32) {
225       log_info( ":E: Bit count (%d) out of range, denying access to %s\n",
226                 bits, ip);
227       return DICT_DENY;
228    }
229 
230    bitmask = daemon_compute_mask(bits);
231    if ((ntohl(target.s_addr) & bitmask) == (ntohl(mask.s_addr) & bitmask)) {
232       PRINTF(DBG_AUTH, ("%s matches %s/%d\n", tstring, mstring, bits));
233       return DICT_MATCH;
234    }
235    PRINTF(DBG_AUTH, ("%s does NOT match %s/%d\n", tstring, mstring, bits));
236    return DICT_NOMATCH;
237 }
238 
daemon_check_range(const char * spec,const char * ip)239 static int daemon_check_range(const char *spec, const char *ip)
240 {
241    char           *tmp = alloca(strlen(spec) + 1);
242    char           *pt;
243    char           tstring[64], minstring[64], maxstring[64];
244    struct in_addr target, min, max;
245 
246    strcpy(tmp, spec);
247    if (!(pt = strchr(tmp, ':'))) {
248       log_info( ":E: No : in range %s, denying access to %s\n", spec, ip);
249       return DICT_DENY;
250    }
251    *pt++ = '\0';
252    if (strchr(pt, ':')) {
253       log_info( ":E: More than one : in range %s, denying access to %s\n",
254                 spec, ip);
255       return DICT_DENY;
256    }
257    if (!*pt) {
258       log_info( ":E: Misformed range %s, denying access to %s\n", spec, ip);
259       return DICT_DENY;
260    }
261 
262    inet_aton(ip, &target);
263    inet_aton(tmp, &min);
264    inet_aton(pt, &max);
265    strcpy(tstring, inet_ntoa(target));
266    strcpy(minstring, inet_ntoa(min));
267    strcpy(maxstring, inet_ntoa(max));
268    if (ntohl(target.s_addr) >= ntohl(min.s_addr)
269        && ntohl(target.s_addr) <= ntohl(max.s_addr)) {
270       PRINTF(DBG_AUTH,("%s in range from %s to %s\n",
271                        tstring, minstring, maxstring));
272       return DICT_MATCH;
273    }
274    PRINTF(DBG_AUTH,("%s NOT in range from %s to %s\n",
275                     tstring, minstring, maxstring));
276    return DICT_NOMATCH;
277 }
278 
daemon_check_wildcard(const char * spec,const char * ip)279 static int daemon_check_wildcard(const char *spec, const char *ip)
280 {
281    char         regbuf[256];
282    char         erbuf[100];
283    int          err;
284    const char   *s;
285    char         *d;
286    regex_t      re;
287 
288    for (d = regbuf, s = spec; s && *s; ++s) {
289       switch (*s) {
290       case '*': *d++ = '.';  *d++ = '*'; break;
291       case '.': *d++ = '\\'; *d++ = '.'; break;
292       case '?': *d++ = '.';              break;
293       default:  *d++ = *s;               break;
294       }
295    }
296    *d = '\0';
297    if ((err = regcomp(&re, regbuf, REG_ICASE|REG_NOSUB))) {
298       regerror(err, &re, erbuf, sizeof(erbuf));
299       log_info( ":E: regcomp(%s): %s\n", regbuf, erbuf );
300       return DICT_DENY;	/* Err on the side of safety */
301    }
302    if (!regexec(&re, daemonHostname, 0, NULL, 0)
303        || !regexec(&re, daemonIP, 0, NULL, 0)) {
304       PRINTF(DBG_AUTH,
305              ("Match %s with %s/%s\n", spec, daemonHostname, daemonIP));
306       regfree(&re);
307       return DICT_MATCH;
308    }
309    regfree(&re);
310    PRINTF(DBG_AUTH,
311           ("No match (%s with %s/%s)\n", spec, daemonHostname, daemonIP));
312    return DICT_NOMATCH;
313 }
314 
daemon_check_list(const char * user,lst_List acl)315 static int daemon_check_list( const char *user, lst_List acl )
316 {
317    lst_Position p;
318    dictAccess   *a;
319    int          retcode;
320 
321    if (!acl)
322       return DICT_ALLOW;
323 
324    for (p = lst_init_position(acl); p; p = lst_next_position(p)) {
325       a = lst_get_position(p);
326       switch (a->type) {
327       case DICT_DENY:
328       case DICT_ALLOW:
329       case DICT_AUTHONLY:
330          if (strchr(a->spec, '/'))
331             retcode = daemon_check_mask(a->spec, daemonIP);
332          else if  (strchr(a->spec, ':'))
333             retcode = daemon_check_range(a->spec, daemonIP);
334          else
335             retcode = daemon_check_wildcard(a->spec, daemonIP);
336          switch (retcode) {
337          case DICT_DENY:
338             return DICT_DENY;
339          case DICT_MATCH:
340             if (a->type == DICT_DENY) {
341                 daemon_log( DICT_LOG_AUTH, "spec %s denies %s/%s\n",
342                             a->spec, daemonHostname, daemonIP);
343             }
344             return a->type;
345          }
346 	 break;
347       case DICT_USER:
348 	 if (user && !strcmp(user,a->spec)) return DICT_ALLOW;
349       case DICT_GROUP:		/* Groups are not yet implemented. */
350 	 break;
351       }
352    }
353    return DICT_DENY;
354 }
355 
daemon_check_auth(const char * user)356 static int daemon_check_auth( const char *user )
357 {
358    lst_Position p;
359    lst_List     dbl = DictConfig->dbl;
360    dictDatabase *db;
361 
362    switch (daemon_check_list( user, DictConfig->acl )) {
363    default:
364    case DICT_DENY:
365       return 1;
366    case DICT_AUTHONLY:
367       if (!user) return 0;
368    case DICT_ALLOW:
369       for (p = lst_init_position(dbl); p; p = lst_next_position(p)) {
370 	 db = lst_get_position(p);
371 	 switch (daemon_check_list(user, db->acl)) {
372 	 case DICT_ALLOW: db->available = 1; continue;
373 	 default:         db->available = 0; continue;
374 	 }
375       }
376       break;
377    }
378    return 0;
379 }
380 
daemon_terminate(int sig,const char * name)381 void daemon_terminate( int sig, const char *name )
382 {
383    alarm(0);
384    tim_stop( "t" );
385    close(daemonS_in);
386    close(daemonS_out);
387    if (name) {
388       daemon_log( DICT_LOG_TERM,
389 		  "%s: d/m/c = %d/%d/%d; %sr %su %ss\n",
390                   name,
391                   _dict_defines,
392                   _dict_matches,
393                   _dict_comparisons,
394                   dict_format_time( tim_get_real( "t" ) ),
395                   dict_format_time( tim_get_user( "t" ) ),
396                   dict_format_time( tim_get_system( "t" ) ) );
397    } else {
398       daemon_log( DICT_LOG_TERM,
399 		  "signal %d: d/m/c = %d/%d/%d; %sr %su %ss\n",
400                   sig,
401                   _dict_defines,
402                   _dict_matches,
403                   _dict_comparisons,
404                   dict_format_time( tim_get_real( "t" ) ),
405                   dict_format_time( tim_get_user( "t" ) ),
406                   dict_format_time( tim_get_system( "t" ) ) );
407    }
408 
409    log_close();
410    longjmp(env,1);
411    if (sig) exit(sig+128);
412 
413    exit(0);
414 }
415 
416 
daemon_write(const char * buf,int len)417 static void daemon_write( const char *buf, int len )
418 {
419    int left = len;
420    int count;
421 
422    while (left) {
423       if ((count = write(daemonS_out, buf, left)) != left) {
424 	 if (count <= 0) {
425 	    if (errno == EPIPE) {
426 	       daemon_terminate( 0, "pipe" );
427 	    }
428             log_info( ":E: writing %d of %d bytes:"
429                       " retval = %d, errno = %d (%s)\n",
430                       left, len, count, errno, strerror(errno) );
431             daemon_terminate( 0, __func__ );
432          }
433       }
434       left -= count;
435    }
436 }
437 
daemon_crlf(char * d,const char * s,int dot)438 static void daemon_crlf( char *d, const char *s, int dot )
439 {
440    int first = 1;
441 
442    while (*s) {
443       if (*s == '\n') {
444 	 *d++ = '\r';
445 	 *d++ = '\n';
446 	 first = 1;
447 	 ++s;
448       } else {
449 	 if (dot && first && *s == '.' && s[1] == '\n')
450 	    *d++ = '.'; /* double first dot on line */
451 	 first = 0;
452 	 *d++ = *s++;
453       }
454    }
455    if (dot) {                   /* add final . */
456       if (!first){
457 	 *d++ = '\r';
458 	 *d++ = '\n';
459       }
460       *d++ = '.';
461       *d++ = '\r';
462       *d++ = '\n';
463    }
464    *d = '\0';
465 }
466 
daemon_printf(const char * format,...)467 static void daemon_printf( const char *format, ... )
468 {
469    va_list ap;
470    char    buf[BUFFERSIZE];
471    char    *pt;
472    int     len;
473 
474    va_start( ap, format );
475    vsnprintf( buf, sizeof (buf), format, ap );
476    va_end( ap );
477    if ((len = strlen( buf )) >= BUFFERSIZE) {
478       log_info( ":E: buffer overflow: %d\n", len );
479       daemon_terminate( 0, __func__ );
480    }
481 
482    pt = alloca(2*len + 10); /* +10 for the case when buf == "\n"*/
483    daemon_crlf(pt, buf, 0);
484    daemon_write(pt, strlen(pt));
485 }
486 
daemon_mime(void)487 static void daemon_mime( void )
488 {
489    if (daemonMime) daemon_write( "\r\n", 2 );
490 }
491 
daemon_mime_definition(const dictDatabase * db)492 static void daemon_mime_definition (const dictDatabase *db)
493 {
494    if (daemonMime){
495       if (db -> mime_header){
496 	 daemon_printf ("%s", db -> mime_header);
497       }
498 
499       daemon_write ("\r\n", 2);
500    }
501 }
502 
daemon_text(const char * text,int dot)503 static void daemon_text( const char *text, int dot )
504 {
505    char *pt = alloca( 2*strlen(text) + 10 );
506 
507    daemon_crlf(pt, text, dot);
508    daemon_write(pt, strlen(pt));
509 }
510 
daemon_read(char * buf,int count)511 static int daemon_read( char *buf, int count )
512 {
513    return net_read( daemonS_in, buf, count );
514 }
515 
daemon_ok(int code,const char * string,const char * timer)516 static void daemon_ok( int code, const char *string, const char *timer )
517 {
518    static int lastDefines     = 0;
519    static int lastMatches     = 0;
520    static int lastComparisons = 0;
521 
522    if (code == CODE_STATUS) {
523       lastDefines     = 0;
524       lastMatches     = 0;
525       lastComparisons = 0;
526    }
527 
528    if (!timer) {
529       daemon_printf("%d %s\n", code, string);
530    } else {
531       tim_stop( timer );
532       daemon_printf("%d %s [d/m/c = %d/%d/%d; %sr %su %ss]\n",
533                     code,
534                     string,
535                     _dict_defines - lastDefines,
536                     _dict_matches - lastMatches,
537                     _dict_comparisons - lastComparisons,
538                     dict_format_time( tim_get_real( timer ) ),
539                     dict_format_time( tim_get_user( timer ) ),
540                     dict_format_time( tim_get_system( timer ) ) );
541    }
542 
543    lastDefines     = _dict_defines;
544    lastMatches     = _dict_matches;
545    lastComparisons = _dict_comparisons;
546 }
547 
daemon_count_defs(lst_List list)548 static int daemon_count_defs( lst_List list )
549 {
550    lst_Position  p;
551    dictWord      *dw;
552    unsigned long previousStart = 0;
553    unsigned long previousEnd   = 0;
554    const char   *previousDef   = NULL;
555    int           count         = 0;
556 
557    LST_ITERATE(list,p,dw) {
558       if (
559 	 previousStart == dw->start &&
560 	 previousEnd   == dw->end &&
561 	 previousDef   == dw->def)
562       {
563 	 continue;
564       }
565 
566       previousStart = dw->start;
567       previousEnd   = dw->end;
568       previousDef   = dw->def;
569 
570       ++count;
571    }
572    return count;
573 }
574 
daemon_dump_defs(lst_List list)575 static void daemon_dump_defs( lst_List list )
576 {
577    lst_Position  p;
578    char          *buf;
579    dictWord      *dw;
580    const dictDatabase  *db         = NULL;
581    const dictDatabase  *db_visible = NULL;
582    unsigned long previousStart = 0;
583    unsigned long previousEnd   = 0;
584    const char *  previousDef   = NULL;
585    int count;
586 
587    LST_ITERATE(list,p,dw) {
588       db = dw->database;
589 
590       if (
591 	 previousStart == dw->start &&
592 	 previousEnd   == dw->end &&
593 	 previousDef   == dw->def)
594       {
595 	 continue;
596       }
597 
598       previousStart = dw->start;
599       previousEnd   = dw->end;
600       previousDef   = dw->def;
601 
602       buf = dict_data_obtain ( db, dw );
603 
604       if (dw -> database_visible){
605 	 db_visible = dw -> database_visible;
606       }else{
607 	 db_visible = db;
608       }
609 
610       daemon_printf (
611 	  "%d \"%s\" %s \"%s\"\n",
612 	  CODE_DEFINITION_FOLLOWS,
613 	  dw->word,
614 	  db_visible -> invisible ? "*" : db_visible -> databaseName,
615 	  db_visible -> invisible ? ""  : db_visible -> databaseShort);
616 
617       daemon_mime_definition (db);
618 
619       if (db->filter){
620 	 count = strlen(buf);
621 	 daemon_log( DICT_LOG_AUTH, "filtering with: %s\ncount: %d\n",
622 		     db->filter, count );
623 
624 	 dict_data_filter(buf, &count, strlen (buf), db->filter);
625 	 buf[count] = '\0';
626       }
627 
628       daemon_text(buf, 1);
629       xfree( buf );
630    }
631 }
632 
daemon_count_matches(lst_List list)633 static int daemon_count_matches( lst_List list )
634 {
635    lst_Position p;
636    dictWord     *dw;
637    const char   *prevword     = NULL;
638    const dictDatabase *prevdb = NULL;
639    int           count        = 0;
640 
641    LST_ITERATE(list,p,dw) {
642       if (prevdb == dw->database && prevword && !strcmp(prevword,dw->word))
643 	 continue;
644 
645       prevword = dw->word;
646       prevdb   = dw->database;
647 
648       ++count;
649    }
650    return count;
651 }
652 
daemon_dump_matches(lst_List list)653 static void daemon_dump_matches( lst_List list )
654 {
655    lst_Position p;
656    dictWord     *dw;
657    const char   *prevword     = NULL;
658    const dictDatabase *prevdb = NULL;
659    const dictDatabase *db     = NULL;
660 
661    daemon_mime();
662    LST_ITERATE(list,p,dw) {
663       db = dw -> database;
664 
665       if (prevdb == dw->database && prevword && !strcmp(prevword,dw->word))
666 	  continue;
667 
668       prevword = dw->word;
669       prevdb   = dw->database;
670 
671       if (dw -> database_visible){
672 	 db = dw -> database_visible;
673       }
674 
675       daemon_printf (
676 	  "%s \"%s\"\n",
677 	  db -> invisible ? "*" : db -> databaseName,
678 	  dw -> word );
679    }
680    daemon_printf( ".\n" );
681 }
682 
daemon_banner(void)683 static void daemon_banner( void )
684 {
685    time_t         t;
686 
687    time(&t);
688 
689    snprintf( daemonStamp, sizeof (daemonStamp), "<%d.%d.%lu@%s>",
690 	    _dict_forks,
691 	    (int) getpid(),
692 	    (long unsigned)t,
693 	     net_hostname() );
694    daemon_printf( "%d %s %s <auth.mime> %s\n",
695 		  CODE_HELLO,
696                   net_hostname(),
697 		  dict_get_banner(0),
698 		  daemonStamp );
699 }
700 
daemon_define(const char * cmdline,int argc,const char ** argv)701 static void daemon_define( const char *cmdline, int argc, const char **argv )
702 {
703    lst_List       list = lst_create();
704    int            matches = 0;
705    const char    *word;
706    const char    *databaseName;
707    int            extension = (argv[0][0] == 'd' && argv[0][1] == '\0');
708    int            db_found = 0;
709 
710    if (extension) {
711       switch (argc) {
712       case 2:  databaseName = "*";     word = argv[1]; break;
713       case 3:  databaseName = argv[1]; word = argv[2]; break;
714       default:
715 	 daemon_printf( "%d syntax error, illegal parameters\n",
716 			CODE_ILLEGAL_PARAM );
717 	 return;
718       }
719    } else if (argc == 3) {
720       databaseName = argv[1];
721       word = argv[2];
722    } else {
723       daemon_printf( "%d syntax error, illegal parameters\n",
724 		     CODE_ILLEGAL_PARAM );
725       return;
726    }
727 
728    matches = abs(dict_search_databases (
729       list, NULL,
730       databaseName, word, DICT_STRAT_EXACT,
731       &db_found));
732 
733    if (db_found && matches > 0) {
734       int actual_matches = daemon_count_defs( list );
735 
736       _dict_defines += actual_matches;
737       daemon_log( DICT_LOG_DEFINE,
738 		  "%s \"%s\" %d\n", databaseName, word, actual_matches);
739       daemon_printf( "%d %d definitions retrieved\n",
740 		     CODE_DEFINITIONS_FOUND,
741 		     actual_matches );
742       daemon_dump_defs( list );
743       daemon_ok( CODE_OK, "ok", "c" );
744 #ifdef USE_PLUGIN
745       call_dictdb_free (DictConfig->dbl);
746 #endif
747       dict_destroy_list( list );
748       return;
749    }
750 
751    if (!db_found) {
752 #ifdef USE_PLUGIN
753       call_dictdb_free (DictConfig->dbl);
754 #endif
755       dict_destroy_list( list );
756       daemon_printf( "%d invalid database, use SHOW DB for list\n",
757 		     CODE_INVALID_DB );
758       return;
759    }
760 
761 #ifdef USE_PLUGIN
762    call_dictdb_free (DictConfig->dbl);
763 #endif
764    dict_destroy_list( list );
765    daemon_log( DICT_LOG_NOMATCH,
766 	       "%s exact \"%s\"\n", databaseName, word );
767    daemon_ok( CODE_NO_MATCH, "no match", "c" );
768 }
769 
daemon_match(const char * cmdline,int argc,const char ** argv)770 static void daemon_match( const char *cmdline, int argc, const char **argv )
771 {
772    lst_List       list = lst_create();
773    int            matches = 0;
774    const char     *word;
775    const char     *databaseName;
776    const char     *strategy;
777    int            strategyNumber;
778    int            extension = (argv[0][0] == 'm' && argv[0][1] == '\0');
779    int            db_found = 0;
780 
781    if (extension) {
782       switch (argc) {
783       case 2:databaseName = "*";     strategy = ".";     word = argv[1]; break;
784       case 3:databaseName = "*";     strategy = argv[1]; word = argv[2]; break;
785       case 4:databaseName = argv[1]; strategy = argv[2]; word = argv[3]; break;
786       default:
787 	 daemon_printf( "%d syntax error, illegal parameters\n",
788 			CODE_ILLEGAL_PARAM );
789 	 return;
790       }
791    } else if (argc == 4) {
792       databaseName = argv[1];
793       strategy = argv[2];
794       word = argv[3];
795    } else {
796       daemon_printf( "%d syntax error, illegal parameters\n",
797 		     CODE_ILLEGAL_PARAM );
798       return;
799    }
800 
801    if ((strategyNumber = lookup_strategy(strategy)) < 0) {
802       daemon_printf( "%d invalid strategy, use SHOW STRAT for a list\n",
803 		     CODE_INVALID_STRATEGY );
804       return;
805    }
806 
807    matches = abs(dict_search_databases (
808       list, NULL,
809       databaseName, word, strategyNumber | DICT_MATCH_MASK,
810       &db_found));
811 
812    if (db_found && matches > 0) {
813       int actual_matches = daemon_count_matches( list );
814 
815       _dict_matches += actual_matches;
816       daemon_log( DICT_LOG_MATCH,
817 		  "%s %s \"%s\" %d\n",
818 		  databaseName, strategy, word, actual_matches);
819       daemon_printf( "%d %d matches found\n",
820 		     CODE_MATCHES_FOUND, actual_matches );
821       daemon_dump_matches( list );
822       daemon_ok( CODE_OK, "ok", "c" );
823 #ifdef USE_PLUGIN
824       call_dictdb_free (DictConfig->dbl);
825 #endif
826       dict_destroy_list( list );
827       return;
828    }
829 
830    if (!db_found) {
831 #ifdef USE_PLUGIN
832       call_dictdb_free (DictConfig->dbl);
833 #endif
834       dict_destroy_list( list );
835       daemon_printf( "%d invalid database, use SHOW DB for list\n",
836 		     CODE_INVALID_DB );
837       return;
838    }
839 
840 #ifdef USE_PLUGIN
841    call_dictdb_free (DictConfig->dbl);
842 #endif
843    dict_destroy_list( list );
844    daemon_log( DICT_LOG_NOMATCH,
845 	       "%s %s \"%s\"\n", databaseName, strategy, word );
846    daemon_ok( CODE_NO_MATCH, "no match", "c" );
847 }
848 
first_database_pos(void)849 static lst_Position first_database_pos (void)
850 {
851    return lst_init_position (DictConfig->dbl);
852 }
853 
next_database(lst_Position * databasePosition,const char * name)854 static dictDatabase *next_database (
855    lst_Position *databasePosition,
856    const char *name)
857 {
858    dictDatabase *db = NULL;
859 
860    assert (databasePosition);
861 
862    if (!name)
863       return NULL;
864 
865    if (*name == '*' || *name == '!') {
866       if (*databasePosition) {
867 	 do {
868 	    db = lst_get_position( *databasePosition );
869 
870 	    *databasePosition = lst_next_position( *databasePosition );
871 	 } while ( db && (!db->available || db->invisible));
872       }
873       return db;
874    } else {
875       while (*databasePosition) {
876          db = lst_get_position( *databasePosition );
877 	 *databasePosition = lst_next_position( *databasePosition );
878 
879          if (db){
880 	    if (
881 	       !db -> invisible && db->available &&
882 	       !strcmp(db -> databaseName,name))
883 	    {
884 	       return db;
885 	    }
886 	 }else{
887 	    return NULL;
888 	 }
889       }
890 
891       return NULL;
892    }
893 }
894 
count_databases(void)895 static int count_databases( void )
896 {
897    int count = 0;
898    const dictDatabase *db;
899 
900    lst_Position databasePosition = first_database_pos ();
901 
902    while (NULL != (db = next_database (&databasePosition, "*"))){
903       assert (!db -> invisible);
904 
905       if (!db -> exit_db)
906 	 ++count;
907    }
908 
909    return count;
910 }
911 
destroy_word_list(lst_List l)912 static void destroy_word_list (lst_List l)
913 {
914    char *word;
915 
916    while (lst_length (l)){
917       word = lst_pop (l);
918       if (word)
919 	 xfree (word);
920    }
921    lst_destroy (l);
922 }
923 
924 /*
925   Search for all words in word_list in the database db
926  */
dict_search_words(lst_List * l,lst_List word_list,const dictDatabase * db,int strategy,int * error,int * result,const dictPluginData ** extra_result,int * extra_result_size)927 static int dict_search_words (
928    lst_List *l,
929    lst_List word_list,
930    const dictDatabase *db,
931    int strategy,
932    int *error, int *result,
933    const dictPluginData **extra_result, int *extra_result_size)
934 {
935    lst_Position word_list_pos;
936    int mc = 0;
937    int matches_count = 0;
938    const char *word;
939 
940    word_list_pos = lst_init_position (word_list);
941    while (word_list_pos){
942       word = lst_get_position (word_list_pos);
943 
944       if (word){
945 	 matches_count = dict_search (
946 	    l, word, db, strategy,
947 	    daemonMime,
948 	    result, extra_result, extra_result_size);
949 
950 	 if (*result == DICT_PLUGIN_RESULT_PREPROCESS){
951 	    assert (matches_count > 0);
952 
953 	    xfree (lst_get_position (word_list_pos));
954 	    lst_set_position (word_list_pos, NULL);
955 	 }
956 
957 	 if (matches_count < 0){
958 	    *error = 1;
959 	    matches_count = abs (matches_count);
960 	    mc += matches_count;
961 	    break;
962 	 }
963 
964 	 mc += matches_count;
965       }
966 
967       word_list_pos = lst_next_position (word_list_pos);
968    }
969 
970    return mc;
971 }
972 
dict_search_databases(lst_List * l,lst_Position databasePosition,const char * databaseName,const char * word,int strategy,int * db_found)973 int dict_search_databases (
974    lst_List *l,
975    lst_Position databasePosition, /* NULL for global database list */
976    const char *databaseName, const char *word, int strategy,
977    int *db_found)
978 {
979    int matches       = -1;
980    int matches_count = 0;
981    int error         = 0;
982 
983 
984    const dictDatabase *db;
985    dictWord *dw;
986    char *p;
987 
988    lst_List preprocessed_words;
989    int i;
990 
991    int                   result;
992    const dictPluginData *extra_result;
993    int                   extra_result_size;
994 
995    *db_found = 0;
996 
997    if (!databasePosition)
998       databasePosition = first_database_pos ();
999 
1000    preprocessed_words = lst_create ();
1001    lst_append (preprocessed_words, xstrdup(word));
1002 
1003    while (!error && (db = next_database (&databasePosition, databaseName))) {
1004       if (db -> exit_db)
1005 	 /* dictionary_exit */
1006 	 break;
1007 
1008       *db_found = 1;
1009 
1010       result = DICT_PLUGIN_RESULT_NOTFOUND;
1011 
1012       matches_count = dict_search_words (
1013 	 l,
1014 	 preprocessed_words, db, strategy,
1015 	 &error,
1016 	 &result, &extra_result, &extra_result_size);
1017 
1018       if (matches < 0)
1019 	 matches = 0;
1020 
1021       if (result == DICT_PLUGIN_RESULT_PREPROCESS){
1022 	 for (i=0; i < matches_count; ++i){
1023 	    dw = lst_pop (l);
1024 	    switch (dw -> def_size){
1025 	    case -1:
1026 	       p = xstrdup (dw -> word);
1027 	       lst_append (preprocessed_words, p);
1028 	       break;
1029 	    case 0:
1030 	       break;
1031 	    default:
1032 	       p = xmalloc (1 + dw -> def_size);
1033 	       memcpy (p, dw -> def, dw -> def_size);
1034 	       p [dw -> def_size] = 0;
1035 
1036 	       lst_append (preprocessed_words, p);
1037 	    }
1038 
1039 	    dict_destroy_datum (dw);
1040 	 }
1041       }else{
1042 	 matches += matches_count;
1043 
1044 	 if (result == DICT_PLUGIN_RESULT_EXIT)
1045 	    break;
1046 
1047 	 if (*databaseName == '*')
1048 	    continue;
1049 	 else if (!matches && *databaseName == '!')
1050 	    continue;
1051 
1052 	 break;
1053       }
1054    }
1055 
1056    destroy_word_list (preprocessed_words);
1057 
1058    return error ? -matches : matches;
1059 }
1060 
daemon_show_db(const char * cmdline,int argc,const char ** argv)1061 static void daemon_show_db( const char *cmdline, int argc, const char **argv )
1062 {
1063    int          count;
1064    const dictDatabase *db;
1065 
1066    lst_Position databasePosition;
1067 
1068    if (argc != 2) {
1069       daemon_printf( "%d syntax error, illegal parameters\n",
1070 		     CODE_ILLEGAL_PARAM );
1071       return;
1072    }
1073 
1074    if (!(count = count_databases())) {
1075       daemon_printf( "%d no databases present\n", CODE_NO_DATABASES );
1076    } else {
1077       daemon_printf( "%d %d databases present\n",
1078 		     CODE_DATABASE_LIST, count );
1079 
1080       databasePosition = first_database_pos ();
1081 
1082       daemon_mime();
1083       while ((db = next_database(&databasePosition, "*"))) {
1084 	 assert (!db->invisible);
1085 
1086 	 if (db -> exit_db)
1087 	    continue;
1088 
1089 	 daemon_printf(
1090 	    "%s \"%s\"\n",
1091 	    db->databaseName, db->databaseShort );
1092       }
1093       daemon_printf( ".\n" );
1094       daemon_ok( CODE_OK, "ok", NULL );
1095    }
1096 }
1097 
daemon_show_strat(const char * cmdline,int argc,const char ** argv)1098 static void daemon_show_strat( const char *cmdline, int argc, const char **argv )
1099 {
1100    int i;
1101 
1102    int strat_count        = get_strategy_count ();
1103    dictStrategy const * const * strats = get_strategies ();
1104 
1105    if (argc != 2) {
1106       daemon_printf( "%d syntax error, illegal parameters\n",
1107 		     CODE_ILLEGAL_PARAM );
1108       return;
1109    }
1110 
1111    if (strat_count){
1112       daemon_printf( "%d %d strategies present\n",
1113 		     CODE_STRATEGY_LIST, strat_count );
1114       daemon_mime();
1115 
1116       for (i = 0; i < strat_count; i++) {
1117 	 daemon_printf( "%s \"%s\"\n",
1118 			strats [i] -> name, strats [i] -> description );
1119       }
1120 
1121       daemon_printf( ".\n" );
1122       daemon_ok( CODE_OK, "ok", NULL );
1123    }else{
1124       daemon_printf( "%d no strategies available\n", CODE_NO_STRATEGIES );
1125    }
1126 }
1127 
daemon_show_info(const char * cmdline,int argc,const char ** argv)1128 void daemon_show_info(
1129    const char *cmdline,
1130    int argc, const char **argv )
1131 {
1132    char         *buf=NULL;
1133    dictWord     *dw;
1134    const dictDatabase *db;
1135    lst_List     list;
1136    const char  *info_entry_name = DICT_INFO_ENTRY_NAME;
1137 
1138    lst_Position databasePosition = first_database_pos ();
1139 
1140    if (argc != 3) {
1141       daemon_printf( "%d syntax error, illegal parameters\n",
1142 		     CODE_ILLEGAL_PARAM );
1143       return;
1144    }
1145 
1146    if ((argv[2][0] == '*' || argv[2][0] == '!') && argv[2][1] == '\0') {
1147       daemon_printf( "%d invalid database, use SHOW DB for list\n",
1148 		     CODE_INVALID_DB );
1149       return;
1150    }
1151 
1152    list = lst_create();
1153    while ((db = next_database(&databasePosition, argv[2]))) {
1154       if (db -> databaseInfo && db -> databaseInfo [0] != '@'){
1155 	 daemon_printf( "%d information for %s\n",
1156 			CODE_DATABASE_INFO, argv[2] );
1157 	 daemon_mime();
1158 	 daemon_text(db -> databaseInfo, 1);
1159 	 daemon_ok( CODE_OK, "ok", NULL );
1160 	 return;
1161       }
1162 
1163       if (db -> databaseInfo && db -> databaseInfo [0] == '@')
1164 	 info_entry_name = db -> databaseInfo + 1;
1165 
1166       if (dict_search (
1167 	 list,
1168 	 info_entry_name,
1169 	 db, DICT_STRAT_EXACT, 0,
1170 	 NULL, NULL, NULL))
1171       {
1172 	 int i=1;
1173 	 int list_size = lst_length (list);
1174 
1175 	 daemon_printf( "%d information for %s\n",
1176 			CODE_DATABASE_INFO, argv[2] );
1177 	 daemon_mime();
1178 
1179 	 if (db -> virtual_db){
1180 	    daemon_printf ("The virtual dictionary `%s' includes the following:\n\n",
1181 			   db -> databaseName);
1182 	 }
1183 
1184 	 for (i=1; i <= list_size; ++i){
1185 	    dw = lst_nth_get( list, i );
1186 
1187 	    daemon_printf ("============ %s ============\n",
1188 			   dw -> database -> databaseName);
1189 
1190 	    buf = dict_data_obtain( dw -> database, dw );
1191 
1192 #ifdef USE_PLUGIN
1193 	    call_dictdb_free (DictConfig->dbl);
1194 #endif
1195 
1196 	    if (buf)
1197 	       daemon_text (buf, 0);
1198 	 }
1199 
1200 	 daemon_text ("\n", 1);
1201 	 daemon_ok( CODE_OK, "ok", NULL );
1202 
1203 	 dict_destroy_list (list);
1204 
1205 	 return;
1206       } else {
1207 #ifdef USE_PLUGIN
1208 	 call_dictdb_free (DictConfig->dbl);
1209 #endif
1210 
1211 	 dict_destroy_list( list );
1212 	 daemon_printf( "%d information for %s\n",
1213 			CODE_DATABASE_INFO, argv[2] );
1214 	 daemon_mime();
1215 	 daemon_text( "No information available\n" , 1);
1216 	 daemon_ok( CODE_OK, "ok", NULL );
1217 	 return;
1218       }
1219    }
1220 
1221    dict_destroy_list( list );
1222    daemon_printf( "%d invalid database, use SHOW DB for list\n",
1223 		  CODE_INVALID_DB );
1224 }
1225 
daemon_get_max_dbname_length()1226 static int daemon_get_max_dbname_length ()
1227 {
1228    size_t max_len  = 0;
1229    size_t curr_len = 0;
1230 
1231    const dictDatabase *db;
1232 
1233    lst_Position databasePosition = first_database_pos ();
1234 
1235    while (NULL != (db = next_database (&databasePosition, "*"))){
1236       assert (!db -> invisible);
1237 
1238       if (db -> databaseName){
1239 	 curr_len = strlen (db -> databaseName);
1240 
1241 	 if (curr_len > max_len){
1242 	    max_len = curr_len;
1243 	 }
1244       }
1245    }
1246 
1247    return (int) max_len;
1248 }
1249 
daemon_show_server(const char * cmdline,int argc,const char ** argv)1250 static void daemon_show_server (
1251    const char *cmdline,
1252    int argc, const char **argv)
1253 {
1254    FILE          *str;
1255    char          buffer[1024];
1256    const dictDatabase  *db;
1257    double        uptime;
1258 
1259    int headwords;
1260 
1261    int index_size;
1262    char index_size_uom;
1263 
1264    int data_size;
1265    char data_size_uom;
1266    int data_length;
1267    char data_length_uom;
1268 
1269    int max_dbname_len;
1270 
1271    lst_Position databasePosition = first_database_pos ();
1272 
1273    daemon_printf( "%d server information\n", CODE_SERVER_INFO );
1274    daemon_mime();
1275 
1276    /* banner: dictd and OS */
1277    if (!site_info_no_banner){
1278       daemon_printf( "%s\n", dict_get_banner(0) );
1279    }
1280 
1281    /* uptime and forks */
1282    if (!site_info_no_uptime && !inetd){
1283       tim_stop("dictd");
1284       uptime = tim_get_real("dictd");
1285 
1286       daemon_printf (
1287 	 "On %s: up %s, %d fork%s (%0.1f/hour)\n",
1288 	 net_hostname(),
1289 	 dict_format_time( uptime ),
1290 	 _dict_forks,
1291 	 _dict_forks > 1 ? "s" : "",
1292 	 (_dict_forks/uptime)*3600.0 );
1293 
1294       daemon_printf ("\n");
1295    }
1296 
1297    if (!site_info_no_dblist && count_databases()) {
1298       daemon_printf( "Database      Headwords         Index          Data  Uncompressed\n" );
1299 
1300       databasePosition = first_database_pos ();
1301 
1302       while (db = next_database (&databasePosition, "*"),
1303 	     db != NULL)
1304       {
1305 	 headwords       = (db->index ? db->index->headwords : 0);
1306 
1307 	 index_size      = 0;
1308 	 index_size_uom = 'k';
1309 
1310 	 data_size       = 0;
1311 	 data_size_uom  = 'k';
1312 	 data_length     = 0;
1313 	 data_length_uom= 'k';
1314 
1315 	 max_dbname_len = 0;
1316 
1317 	 assert (!db -> invisible);
1318 
1319 	 if (db->index){
1320 	    index_size = db->index->size/1024 > 10240 ?
1321 	       db->index->size/1024/1024 : db->index->size/1024;
1322 	    index_size_uom = db->index->size/1024 > 10240 ? 'M' : 'k';
1323 	 }
1324 
1325 	 if (db->data){
1326 	    data_size = db->data->size/1024 > 10240 ?
1327 	       db->data->size/1024/1024 : db->data->size/1024;
1328 	    data_size_uom = db->data->size/1024 > 10240 ? 'M' : 'k';
1329 
1330 	    data_length = db->data->length/1024 > 10240 ?
1331 	       db->data->length/1024/1024 : db->data->length/1024;
1332 	    data_length_uom = db->data->length/1024 > 10240 ? 'M' : 'k';
1333 	 }
1334 
1335 	 max_dbname_len = daemon_get_max_dbname_length ();
1336 
1337 	 daemon_printf(
1338 	    "%-*.*s %10i %10i %cB %10i %cB %10i %cB\n",
1339 	    max_dbname_len,
1340 	    max_dbname_len,
1341 
1342 	    db->databaseName,
1343 	    headwords,
1344 
1345 	    index_size,
1346 	    index_size_uom,
1347 
1348 	    data_size,
1349 	    data_size_uom,
1350 
1351 	    data_length,
1352 	    data_length_uom);
1353       }
1354 
1355       daemon_printf ("\n");
1356    }
1357 
1358    if (site_info && (str = fopen( site_info, "r" ))) {
1359       while ((fgets( buffer, 1000, str ))) daemon_printf( "%s", buffer );
1360       fclose( str );
1361    }
1362    daemon_printf( ".\n" );
1363    daemon_ok( CODE_OK, "ok", NULL );
1364 }
1365 
daemon_show(const char * cmdline,int argc,const char ** argv)1366 static void daemon_show( const char *cmdline, int argc, const char **argv )
1367 {
1368    daemon_printf( "%d syntax error, illegal parameters\n",
1369 		  CODE_ILLEGAL_PARAM );
1370 }
1371 
daemon_option_mime(const char * cmdline,int argc,const char ** argv)1372 static void daemon_option_mime( const char *cmdline, int argc, const char **argv )
1373 {
1374    ++daemonMime;
1375    daemon_ok( CODE_OK, "ok - using MIME headers", NULL );
1376 }
1377 
daemon_option(const char * cmdline,int argc,const char ** argv)1378 static void daemon_option( const char *cmdline, int argc, const char **argv )
1379 {
1380    daemon_printf( "%d syntax error, illegal parameters\n",
1381 		  CODE_ILLEGAL_PARAM );
1382 }
1383 
daemon_client(const char * cmdline,int argc,const char ** argv)1384 static void daemon_client( const char *cmdline, int argc, const char **argv )
1385 {
1386    const char *pt = strchr( cmdline, ' ' );
1387 
1388    if (pt)
1389       daemon_log( DICT_LOG_CLIENT, "%.200s\n", pt + 1 );
1390    else
1391       daemon_log( DICT_LOG_CLIENT, "%.200s\n", cmdline );
1392    daemon_ok( CODE_OK, "ok", NULL );
1393 }
1394 
daemon_auth(const char * cmdline,int argc,const char ** argv)1395 static void daemon_auth( const char *cmdline, int argc, const char **argv )
1396 {
1397    char              *buf;
1398    hsh_HashTable     h = DictConfig->usl;
1399    const char        *secret;
1400    struct MD5Context ctx;
1401    unsigned char     digest[16];
1402    char              hex[33];
1403    int               i;
1404    int               buf_size;
1405 
1406    if (argc != 3)
1407       daemon_printf( "%d syntax error, illegal parameters\n",
1408 		     CODE_ILLEGAL_PARAM );
1409    if (!h || !(secret = hsh_retrieve(h, argv[1]))) {
1410       daemon_log( DICT_LOG_AUTH, "%s@%s/%s denied: invalid username\n",
1411                   argv[1], daemonHostname, daemonIP );
1412       daemon_printf( "%d auth denied\n", CODE_AUTH_DENIED );
1413       return;
1414    }
1415 
1416    buf_size = strlen(daemonStamp) + strlen(secret) + 10;
1417    buf = alloca(buf_size);
1418    snprintf( buf, buf_size, "%s%s", daemonStamp, secret );
1419 
1420    MD5Init(&ctx);
1421    MD5Update(&ctx, (const unsigned char *) buf, strlen(buf));
1422    MD5Final(digest, &ctx);
1423 
1424    for (i = 0; i < 16; i++)
1425       snprintf( hex+2*i, 3, "%02x", digest[i] );
1426 
1427    hex[32] = '\0';
1428 
1429    PRINTF(DBG_AUTH,("Got %s expected %s\n", argv[2], hex ));
1430 
1431    if (strcmp(hex,argv[2])) {
1432       daemon_log( DICT_LOG_AUTH, "%s@%s/%s denied: hash mismatch\n",
1433                   argv[1], daemonHostname, daemonIP );
1434       daemon_printf( "%d auth denied\n", CODE_AUTH_DENIED );
1435    } else {
1436       daemon_printf( "%d authenticated\n", CODE_AUTH_OK );
1437       daemon_check_auth( argv[1] );
1438    }
1439 }
1440 
daemon_status(const char * cmdline,int argc,const char ** argv)1441 static void daemon_status( const char *cmdline, int argc, const char **argv )
1442 {
1443    daemon_ok( CODE_STATUS, "status", "t" );
1444 }
1445 
daemon_help(const char * cmdline,int argc,const char ** argv)1446 static void daemon_help( const char *cmdline, int argc, const char **argv )
1447 {
1448    daemon_printf( "%d help text follows\n", CODE_HELP );
1449    daemon_mime();
1450    daemon_text(
1451     "DEFINE database word         -- look up word in database\n"
1452     "MATCH database strategy word -- match word in database using strategy\n"
1453     "SHOW DB                      -- list all accessible databases\n"
1454     "SHOW DATABASES               -- list all accessible databases\n"
1455     "SHOW STRAT                   -- list available matching strategies\n"
1456     "SHOW STRATEGIES              -- list available matching strategies\n"
1457     "SHOW INFO database           -- provide information about the database\n"
1458     "SHOW SERVER                  -- provide site-specific information\n"
1459     "OPTION MIME                  -- use MIME headers\n"
1460     "CLIENT info                  -- identify client to server\n"
1461     "AUTH user string             -- provide authentication information\n"
1462     "STATUS                       -- display timing information\n"
1463     "HELP                         -- display this help information\n"
1464     "QUIT                         -- terminate connection\n\n"
1465     "The following commands are unofficial server extensions for debugging\n"
1466     "only.  You may find them useful if you are using telnet as a client.\n"
1467     "If you are writing a client, you MUST NOT use these commands, since\n"
1468     "they won't be supported on any other server!\n\n"
1469     "D word                       -- DEFINE * word\n"
1470     "D database word              -- DEFINE database word\n"
1471     "M word                       -- MATCH * . word\n"
1472     "M strategy word              -- MATCH * strategy word\n"
1473     "M database strategy word     -- MATCH database strategy word\n"
1474     "S                            -- STATUS\n"
1475     "H                            -- HELP\n"
1476     "Q                            -- QUIT\n"
1477    , 1);
1478    daemon_ok( CODE_OK, "ok", NULL );
1479 }
1480 
daemon_quit(const char * cmdline,int argc,const char ** argv)1481 static void daemon_quit( const char *cmdline, int argc, const char **argv )
1482 {
1483    daemon_ok( CODE_GOODBYE, "bye", "t" );
1484    daemon_terminate( 0, "quit" );
1485 }
1486 
1487 /* The whole sub should be moved here, but I want to keep the diff small. */
1488 int _handleconn (int error);
1489 
dict_inetd(int error)1490 int dict_inetd (int error)
1491 {
1492    if (setjmp(env)) return 0;
1493 
1494    daemonPort = -1;
1495    daemonIP   = "inetd";
1496 
1497    daemonHostname = daemonIP;
1498 
1499    daemonS_in        = 0;
1500    daemonS_out       = 1;
1501 
1502    return _handleconn (error);
1503 }
1504 
dict_daemon(int s,struct sockaddr_storage * csin,int error)1505 int dict_daemon( int s, struct sockaddr_storage *csin, int error )
1506 {
1507    struct hostent *h;
1508    int err = 0;
1509    socklen_t csin_len;
1510    static char hostname[NI_MAXHOST], service[NI_MAXSERV];
1511 
1512    if (setjmp(env)) return 0;
1513 
1514    // We calculate csin_len for NetBSD-9.0 and Solaris-10/11 where
1515    // getnameinfo(*,sizeof(sockaddr_storage),*,*,*,*,*) does not work.
1516    if (((struct sockaddr *)csin)->sa_family == AF_INET)
1517       csin_len = sizeof(struct sockaddr_in);
1518    else
1519       csin_len = sizeof(struct sockaddr_in6);
1520 
1521    //
1522    err = getnameinfo ((const struct sockaddr *)csin, csin_len,
1523 		      hostname, NI_MAXHOST, service, NI_MAXSERV,
1524 		      NI_NUMERICSERV);
1525    if (err){
1526       log_error(__func__, ":E: getnameinfo(3) failed: %s\n", gai_strerror(err));
1527       exit(1);
1528    }
1529 
1530    daemonPort = strtol (service, NULL, 10);
1531    daemonIP = str_find (inet_ntopW ((struct sockaddr *)csin));
1532    daemonHostname = str_find (hostname);
1533    daemonS_in = daemonS_out = s;
1534 
1535    return _handleconn(error);
1536 }
1537 
_handleconn(int error)1538 int _handleconn (int error) {
1539    int            query_count = 0;
1540    char           buf[4096];
1541    int            count;
1542    arg_List       cmdline;
1543    int            argc;
1544    char         **argv;
1545    void           (*command)(const char *, int, const char **);
1546 
1547    _dict_defines     = 0;
1548    _dict_matches     = 0;
1549    _dict_comparisons = 0;
1550 
1551    tim_start( "t" );
1552    daemon_log( DICT_LOG_TRACE, "connected\n" );
1553    daemon_log( DICT_LOG_CONNECT, "%s/%s connected on port %d\n",
1554                daemonHostname, daemonIP, daemonPort );
1555    dict_setproctitle( "dictd: %s connected", daemonHostname );
1556 
1557    if (error) {
1558       daemon_printf( "%d server temporarily unavailable\n",
1559 		     CODE_TEMPORARILY_UNAVAILABLE );
1560       daemon_terminate( 0, "temporarily unavailable" );
1561    }
1562 
1563    if (daemon_check_auth( NULL )) {
1564       daemon_log( DICT_LOG_AUTH, "%s/%s denied: ip/hostname rules\n",
1565                   daemonHostname, daemonIP );
1566       daemon_printf( "%d access denied\n", CODE_ACCESS_DENIED );
1567       daemon_terminate( 0, "access denied" );
1568    }
1569 
1570    daemon_banner();
1571 
1572    if (!_dict_daemon_limit_time)
1573       alarm (client_delay);
1574 
1575    while (count = daemon_read( buf, 4000 ), count >= 0) {
1576       ++query_count;
1577 
1578       if (_dict_daemon_limit_queries &&
1579 	  query_count >= _dict_daemon_limit_queries)
1580       {
1581 	 daemon_terminate (0, "query limit");
1582       }
1583 
1584       if (stdin2stdout_mode){
1585 	 daemon_printf( "# %s\n", buf );
1586       }
1587 
1588       if (!_dict_daemon_limit_time)
1589 	 alarm(0);
1590 
1591       tim_start( "c" );
1592       if (!count) {
1593 #if 0
1594          daemon_ok( CODE_OK, "ok", "c" );
1595 #endif
1596 	 continue;
1597       }
1598 
1599       daemon_log( DICT_LOG_COMMAND, "%.80s\n", buf );
1600       cmdline = arg_argify(buf,0);
1601       arg_get_vector( cmdline, &argc, &argv );
1602       if ((command = lookup_command (argc, (const char **) argv))) {
1603 	 command(buf, argc, (const char **) argv);
1604       } else {
1605 	 daemon_printf( "%d unknown command\n", CODE_SYNTAX_ERROR );
1606       }
1607       arg_destroy(cmdline);
1608 
1609       if (!_dict_daemon_limit_time)
1610 	 alarm (client_delay);
1611    }
1612 #if 0
1613    printf( "%d %d\n", count, errno );
1614 #endif
1615    daemon_terminate( 0, "close" );
1616    return 0;
1617 }
1618