1/*
2 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
3 * All Rights Reserved.  See COPYRIGHT.
4 */
5
6#ifdef HAVE_CONFIG_H
7#include "config.h"
8#endif /* HAVE_CONFIG_H */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <errno.h>
14#include <string.h>
15#include <unistd.h>
16#include <ctype.h>
17#include <sys/socket.h>
18#include <netinet/in.h>
19#include <arpa/inet.h>
20#ifdef HAVE_GETIFADDRS
21#include <ifaddrs.h>
22#endif
23
24#include <atalk/logger.h>
25#include <atalk/util.h>
26#include <atalk/dsi.h>
27#include <atalk/afp.h>
28#include <atalk/compat.h>
29#include <atalk/server_child.h>
30#include <atalk/globals.h>
31#include <atalk/errchk.h>
32#include <atalk/netatalk_conf.h>
33#include <atalk/fce_api.h>
34
35#ifdef HAVE_LDAP
36#include <atalk/ldapconfig.h>
37#endif
38
39#include "afp_config.h"
40#include "uam_auth.h"
41#include "status.h"
42#include "volume.h"
43#include "afp_zeroconf.h"
44
45/*!
46 * Free and cleanup config and DSI
47 *
48 * "dsi" can be NULL in which case all DSI objects and the config object is freed,
49 * otherwise its an afpd session child and only any unneeded DSI objects are freed
50 */
51void configfree(AFPObj *obj, DSI *dsi)
52{
53    DSI *p, *q;
54
55    if (!dsi) {
56        /* Master afpd reloading config */
57        auth_unload();
58        if (! (obj->options.flags & OPTION_NOZEROCONF)) {
59            zeroconf_deregister();
60        }
61    }
62
63    unload_volumes(obj);
64
65    /* Master and child releasing unneeded DSI handles */
66    for (p = obj->dsi; p; p = q) {
67        q = p->next;
68        if (p == dsi)
69            continue;
70        dsi_free(p);
71        free(p);
72    }
73    obj->dsi = NULL;
74
75    /* afpd session child passes dsi handle to obj handle */
76    if (dsi) {
77        dsi->next = NULL;
78        obj->dsi = dsi;
79    }
80}
81
82/*!
83 * Get everything running
84 */
85int configinit(AFPObj *obj)
86{
87    EC_INIT;
88    DSI *dsi = NULL;
89    DSI **next = &obj->dsi;
90    char *p = NULL, *q = NULL, *savep;
91    const char *r;
92    struct ifaddrs *ifaddr, *ifa;
93    int family, s;
94    static char interfaddr[NI_MAXHOST];
95
96    auth_load(obj->options.uampath, obj->options.uamlist);
97    set_signature(&obj->options);
98#ifdef HAVE_LDAP
99    acl_ldap_freeconfig();
100#endif /* HAVE_LDAP */
101
102    LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, listen: %s, interfaces: %s, port: %s",
103        obj->options.hostname,
104        obj->options.listen ? obj->options.listen : "-",
105        obj->options.interfaces ? obj->options.interfaces : "-",
106        obj->options.port);
107
108    /*
109     * Setup addresses we listen on from hostname and/or "afp listen" option
110     */
111    if (obj->options.listen) {
112        EC_NULL( q = p = strdup(obj->options.listen) );
113        EC_NULL( p = strtok_r(p, ", ", &savep) );
114        while (p) {
115            if ((dsi = dsi_init(obj, obj->options.hostname, p, obj->options.port)) == NULL)
116                break;
117
118            status_init(obj, dsi);
119            *next = dsi;
120            next = &dsi->next;
121            dsi->AFPobj = obj;
122
123            LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on %s:%d",
124                getip_string((struct sockaddr *)&dsi->server),
125                getip_port((struct sockaddr *)&dsi->server));
126
127            p = strtok_r(NULL, ", ", &savep);
128        }
129        if (q) {
130            free(q);
131            q = NULL;
132        }
133    }
134
135   /*
136    * Setup addresses we listen on from "afp interfaces".
137    * We use getifaddrs() instead of if_nameindex() because the latter appears still
138    * to be unable to return ipv4 addresses
139    */
140    if (obj->options.interfaces) {
141#ifndef HAVE_GETIFADDRS
142        LOG(log_error, logtype_afpd, "option \"afp interfaces\" not supported");
143#else
144        if (getifaddrs(&ifaddr) == -1) {
145            LOG(log_error, logtype_afpd, "getinterfaddr: getifaddrs() failed: %s", strerror(errno));
146            EC_FAIL;
147        }
148
149        EC_NULL( q = p = strdup(obj->options.interfaces) );
150        EC_NULL( p = strtok_r(p, ", ", &savep) );
151        while (p) {
152            for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
153                if (ifa->ifa_addr == NULL)
154                    continue;
155                if (STRCMP(ifa->ifa_name, !=, p))
156                    continue;
157
158                family = ifa->ifa_addr->sa_family;
159                if (family == AF_INET || family == AF_INET6) {
160                    if (getnameinfo(ifa->ifa_addr,
161                                    (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
162                                    interfaddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) {
163                        LOG(log_error, logtype_afpd, "getinterfaddr: getnameinfo() failed %s", gai_strerror(errno));
164                        continue;
165                    }
166
167                    if ((dsi = dsi_init(obj, obj->options.hostname, interfaddr, obj->options.port)) == NULL)
168                        continue;
169
170                    status_init(obj, dsi);
171                    *next = dsi;
172                    next = &dsi->next;
173                    dsi->AFPobj = obj;
174
175                    LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on interface %s with address %s:%d",
176                        p,
177                        getip_string((struct sockaddr *)&dsi->server),
178                        getip_port((struct sockaddr *)&dsi->server));
179                } /* if (family == AF_INET || family == AF_INET6) */
180            } /* for (ifa != NULL) */
181            p = strtok_r(NULL, ", ", &savep);
182        }
183        freeifaddrs(ifaddr);
184#endif
185    }
186
187    /*
188     * Check whether we got a valid DSI from options.listen or options.interfaces,
189     * if not add a DSI that accepts all connections and goes though the list of
190     * network interaces for determining an IP we can advertise in DSIStatus
191     */
192    if (dsi == NULL) {
193        if ((dsi = dsi_init(obj, obj->options.hostname, NULL, obj->options.port)) == NULL)
194            EC_FAIL_LOG("no suitable network address found, use \"afp listen\" or \"afp interfaces\"", 0);
195        status_init(obj, dsi);
196        *next = dsi;
197        next = &dsi->next;
198        dsi->AFPobj = obj;
199
200        LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on %s:%d",
201            getip_string((struct sockaddr *)&dsi->server),
202            getip_port((struct sockaddr *)&dsi->server));
203    }
204
205#ifdef HAVE_LDAP
206    /* Parse afp.conf */
207    acl_ldap_readconfig(obj->iniconfig);
208#endif /* HAVE_LDAP */
209
210    /* Now register with zeroconf, we also need the volumes for that */
211    if (! (obj->options.flags & OPTION_NOZEROCONF)) {
212        load_volumes(obj);
213        zeroconf_register(obj);
214    }
215
216    if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce listener", NULL))) {
217		LOG(log_note, logtype_afpd, "Adding FCE listener: %s", r);
218		fce_add_udp_socket(r);
219    }
220    if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce coalesce", NULL))) {
221		LOG(log_note, logtype_afpd, "Fce coalesce: %s", r);
222		fce_set_coalesce(r);
223    }
224    if ((r = atalk_iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "fce events", NULL))) {
225		LOG(log_note, logtype_afpd, "Fce events: %s", r);
226		fce_set_events(r);
227    }
228
229EC_CLEANUP:
230    if (q)
231        free(q);
232    EC_EXIT;
233}
234