redisdovecot

Dovecot Failed to init dict: dict redis: Invalid IP


I'm using Dovecot v2.3.9.3. By looking to the dovecot docs I see that specification for Redis is the following:

redis:param=value:param2=value2:...

Here one of the parameters is host: Redis server host (default: 127.0.0.1).

In my configuration I have specified uri = redis:host=redis:port=6379:

Feb 23 20:48:32 auth: Fatal: dict /etc/dovecot/dovecot-dict-auth.conf.ext: Failed to init dict: dict redis: Invalid IP: redis

redis hostname on my server resolves to the IP without any problem:

# getent hosts redis
192.168.48.2      redis  redis

Is there a way to use hostname (maybe some hidden setting which enables the resolution), or they just bluntly did not implement a support for that? :/


Solution

  • TL;DR

    Dovecot redis dict driver accepts undocumented parameter path in which you can specify the unix socket. You may then create a proxy to the hostname redis serving on the tcp port 6379 via the unix socket /run/redis.soc:

    socat unix-listen:/run/redis.soc,reuseaddr,fork,perm=0644,user=dovecot tcp:redis:6379 &
    

    Dovecot config becomes:

    # Dictionary URI
    uri = redis:path=/run/redis.soc
    

    The following is my analysis of the problem. I don't code in C, so my understanding was limited.

    Code which covers my error (Invalid IP: redis) is the following:

    } else if (str_begins(*args, "host=")) {
      if (net_addr2ip(*args+5, &ip) < 0) {
        *error_r = t_strdup_printf("Invalid IP: %s",
                  *args+5);
        ret = -1;
      }
    }
    

    It relies on the net_addr2ip function which depends on the net_addr2ip_inet4_fast. Both of these functions do not seem to do, what their names are suggesting (they do not turn addr to ip):

    static bool net_addr2ip_inet4_fast(const char *addr, struct ip_addr *ip)
    {
        uint8_t *saddr = (void *)&ip->u.ip4.s_addr;
        unsigned int i, num;
    
        if (str_parse_uint(addr, &num, &addr) < 0)
            return FALSE;
        if (*addr == '\0' && num <= 0xffffffff) {
            /* single-number IPv4 address */
            ip->u.ip4.s_addr = htonl(num);
            ip->family = AF_INET;
            return TRUE;
        }
    
        /* try to parse as a.b.c.d */
        i = 0;
        for (;;) {
            if (num >= 256)
                return FALSE;
            saddr[i] = num;
            if (i == 3)
                break;
            i++;
            if (*addr != '.')
                return FALSE;
            addr++;
            if (str_parse_uint(addr, &num, &addr) < 0)
                return FALSE;
        }
        if (*addr != '\0')
            return FALSE;
        ip->family = AF_INET;
        return TRUE;
    }
    
    int net_addr2ip(const char *addr, struct ip_addr *ip)
    {
        int ret;
    
        if (net_addr2ip_inet4_fast(addr, ip))
            return 0;
    
        if (strchr(addr, ':') != NULL) {
            /* IPv6 */
            T_BEGIN {
                if (addr[0] == '[') {
                    /* allow [ipv6 addr] */
                    size_t len = strlen(addr);
                    if (addr[len-1] == ']')
                        addr = t_strndup(addr+1, len-2);
                }
                ret = inet_pton(AF_INET6, addr, &ip->u.ip6);
            } T_END;
            if (ret == 0)
                return -1;
            ip->family = AF_INET6;
        } else {
            /* IPv4 */
            if (inet_aton(addr, &ip->u.ip4) == 0)
                return -1;
            ip->family = AF_INET;
        }
        return 0;
    }
    

    Therefore host parameter in dovecot's redis dict driver cannot be anything else but an IP address ¯_(ツ)_/¯