]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
Merge master
[netatalk.git] / etc / afpd / afp_config.c
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
21 #ifdef USE_SRVLOC
22 #include <slp.h>
23 #endif /* USE_SRVLOC */
24
25 #include <atalk/logger.h>
26 #include <atalk/util.h>
27 #include <atalk/dsi.h>
28 #include <atalk/afp.h>
29 #include <atalk/compat.h>
30 #include <atalk/server_child.h>
31
32 #ifdef HAVE_LDAP
33 #include <atalk/ldapconfig.h>
34 #endif
35
36 #include "globals.h"
37 #include "afp_config.h"
38 #include "uam_auth.h"
39 #include "status.h"
40 #include "volume.h"
41 #include "afp_zeroconf.h"
42
43 #define LINESIZE 1024  
44
45 /* get rid of unneeded configurations. i use reference counts to deal
46  * w/ multiple configs sharing the same afp_options. oh, to dream of
47  * garbage collection ... */
48 void configfree(AFPConfig *configs, const AFPConfig *config)
49 {
50     AFPConfig *p, *q;
51
52     for (p = configs; p; p = q) {
53         q = p->next;
54         if (p == config)
55             continue;
56
57         /* do a little reference counting */
58         if (--(*p->optcount) < 1) {
59             afp_options_free(&p->obj.options, p->defoptions);
60             free(p->optcount);
61         }
62
63         switch (p->obj.proto) {
64         case AFPPROTO_DSI:
65             close(p->fd);
66             free(p->obj.handle);
67             break;
68         }
69         free(p);
70     }
71
72     /* the master loaded the volumes for zeroconf, get rid of that */
73     unload_volumes_and_extmap();
74 }
75
76 #ifdef USE_SRVLOC
77 static void SRVLOC_callback(SLPHandle hslp _U_, SLPError errcode, void *cookie) {
78     *(SLPError*)cookie = errcode;
79 }
80
81 static char hex[17] = "0123456789abcdef";
82
83 static char * srvloc_encode(const struct afp_options *options, const char *name)
84 {
85         static char buf[512];
86         char *conv_name;
87         unsigned char *p;
88         unsigned int i = 0;
89
90         /* Convert name to maccharset */
91         if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
92                          name, -1, &conv_name)) )
93                 return (char*)name;
94
95         /* Escape characters */
96         p = conv_name;
97         while (*p && i<(sizeof(buf)-4)) {
98             if (*p == '@')
99                 break;
100             else if (isspace(*p)) {
101                 buf[i++] = '%';
102                 buf[i++] = '2';
103                 buf[i++] = '0';
104                 p++;
105             }   
106             else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
107                 buf[i++] = '%';
108                 buf[i++] = hex[*p >> 4];
109                 buf[i++] = hex[*p++ & 15];
110             }
111             else {
112                 buf[i++] = *p++;
113             }
114         }
115         buf[i] = '\0';
116
117         free (conv_name);
118
119         return buf;
120 }
121 #endif /* USE_SRVLOC */
122
123 static void dsi_cleanup(const AFPConfig *config)
124 {
125 #ifdef USE_SRVLOC
126     SLPError err;
127     SLPError callbackerr;
128     SLPHandle hslp;
129     DSI *dsi = (DSI *)config->obj.handle;
130
131     /*  Do nothing if we didn't register.  */
132     if (!dsi || dsi->srvloc_url[0] == '\0')
133         return;
134
135     err = SLPOpen("en", SLP_FALSE, &hslp);
136     if (err != SLP_OK) {
137         LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
138         goto srvloc_dereg_err;
139     }
140
141     err = SLPDereg(hslp,
142                    dsi->srvloc_url,
143                    SRVLOC_callback,
144                    &callbackerr);
145     if (err != SLP_OK) {
146         LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
147         goto srvloc_dereg_err;
148     }
149
150     if (callbackerr != SLP_OK) {
151         LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
152         goto srvloc_dereg_err;
153     }
154
155 srvloc_dereg_err:
156     dsi->srvloc_url[0] = '\0';
157     SLPClose(hslp);
158 #endif /* USE_SRVLOC */
159 }
160
161 static afp_child_t *dsi_start(AFPConfig *config, AFPConfig *configs,
162                               server_child *server_children)
163 {
164     DSI *dsi = config->obj.handle;
165     afp_child_t *child = NULL;
166
167     if (!(child = dsi_getsession(dsi,
168                                  server_children,
169                                  config->obj.options.tickleval))) {
170         LOG(log_error, logtype_afpd, "dsi_start: session error: %s", strerror(errno));
171         return NULL;
172     }
173
174     /* we've forked. */
175     if (parent_or_child == 1) {
176         configfree(configs, config);
177         config->obj.ipc_fd = child->ipc_fds[1];
178         close(child->ipc_fds[0]); /* Close parent IPC fd */
179         free(child);
180         afp_over_dsi(&config->obj); /* start a session */
181         exit (0);
182     }
183
184     return child;
185 }
186
187 static AFPConfig *DSIConfigInit(const struct afp_options *options,
188                                 unsigned char *refcount,
189                                 const dsi_proto protocol)
190 {
191     AFPConfig *config;
192     DSI *dsi;
193     char *p, *q;
194
195     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
196         LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
197         return NULL;
198     }
199
200     LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
201         options->hostname,
202         options->ipaddr ? options->ipaddr : "default",
203         options->port ? options->port : "548");
204
205     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
206                         options->ipaddr, options->port,
207                         options->flags & OPTION_PROXY,
208                         options->server_quantum)) == NULL) {
209         LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
210         free(config);
211         return NULL;
212     }
213
214     if (options->flags & OPTION_PROXY) {
215         LOG(log_note, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
216             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
217     } else {
218         LOG(log_note, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
219             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
220     }
221
222 #ifdef USE_SRVLOC
223     dsi->srvloc_url[0] = '\0';  /*  Mark that we haven't registered.  */
224     if (!(options->flags & OPTION_NOSLP)) {
225         SLPError err;
226         SLPError callbackerr;
227         SLPHandle hslp;
228         unsigned int afp_port;
229         int   l;
230         char *srvloc_hostname;
231         const char *hostname;
232
233         err = SLPOpen("en", SLP_FALSE, &hslp);
234         if (err != SLP_OK) {
235             LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
236             goto srvloc_reg_err;
237         }
238
239         /* XXX We don't want to tack on the port number if we don't have to.
240          * Why?
241          * Well, this seems to break MacOS < 10.  If the user _really_ wants to
242          * use a non-default port, they can, but be aware, this server might
243          * not show up int the Network Browser.
244          */
245         afp_port = getip_port((struct sockaddr *)&dsi->server);
246         /* If specified use the FQDN to register with srvloc, otherwise use IP. */
247         p = NULL;
248         if (options->fqdn) {
249             hostname = options->fqdn;
250             p = strchr(hostname, ':');
251         }       
252         else 
253             hostname = getip_string((struct sockaddr *)&dsi->server);
254
255         srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
256
257         if ((p) || afp_port == 548) {
258             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
259         }
260         else {
261             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
262         }
263
264         if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
265             LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
266             dsi->srvloc_url[0] = '\0';
267             goto srvloc_reg_err;
268         }
269
270         err = SLPReg(hslp,
271                      dsi->srvloc_url,
272                      SLP_LIFETIME_MAXIMUM,
273                      "afp",
274                      "",
275                      SLP_TRUE,
276                      SRVLOC_callback,
277                      &callbackerr);
278         if (err != SLP_OK) {
279             LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
280             dsi->srvloc_url[0] = '\0';
281             goto srvloc_reg_err;
282         }
283
284         if (callbackerr != SLP_OK) {
285             LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
286             dsi->srvloc_url[0] = '\0';
287             goto srvloc_reg_err;
288         }
289
290         LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
291         config->server_cleanup = dsi_cleanup;
292
293 srvloc_reg_err:
294         SLPClose(hslp);
295     }
296 #endif /* USE_SRVLOC */
297
298     config->fd = dsi->serversock;
299     config->obj.handle = dsi;
300     config->obj.config = config;
301     config->obj.proto = AFPPROTO_DSI;
302
303     memcpy(&config->obj.options, options, sizeof(struct afp_options));
304     /* get rid of any appletalk info. we use the fact that the DSI
305      * stuff is done after the ASP stuff. */
306     p = config->obj.options.server;
307     if (p && (q = strchr(p, ':')))
308         *q = '\0';
309
310     config->optcount = refcount;
311     (*refcount)++;
312
313     config->server_start = dsi_start;
314     return config;
315 }
316
317 /* allocate server configurations. this should really store the last
318  * entry in config->last or something like that. that would make
319  * supporting multiple dsi transports easier. */
320 static AFPConfig *AFPConfigInit(struct afp_options *options,
321                                 const struct afp_options *defoptions)
322 {
323     AFPConfig *next = NULL;
324     unsigned char *refcount;
325
326     if ((refcount = (unsigned char *)
327                     calloc(1, sizeof(unsigned char))) == NULL) {
328         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
329         return NULL;
330     }
331
332     /* set signature */
333     set_signature(options);
334
335     /* handle dsi transports and dsi proxies. we only proxy
336      * for DSI connections. */
337
338     /* this should have something like the following:
339      * for (i=mindsi; i < maxdsi; i++)
340      *   if (options->transports & (1 << i) && 
341      *     (next = DSIConfigInit(options, refcount, i)))
342      *     next->defoptions = defoptions;
343      */
344     if ( (options->transports & AFPTRANS_TCP)
345          &&
346          ((options->flags & OPTION_PROXY) == 0)
347          &&
348          (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
349         next->defoptions = defoptions;
350
351     /* load in all the authentication modules. we can load the same
352        things multiple times if necessary. however, loading different
353        things with the same names will cause complaints. by not loading
354        in any uams with proxies, we prevent ddp connections from succeeding.
355     */
356     auth_load(options->uampath, options->uamlist);
357
358     /* this should be able to accept multiple dsi transports. i think
359      * the only thing that gets affected is the net addresses. */
360     status_init(next, options);
361
362     return next;
363 }
364
365 /* fill in the appropriate bits for each interface */
366 AFPConfig *configinit(struct afp_options *cmdline)
367 {
368     FILE *fp;
369     char buf[LINESIZE + 1], *p, have_option = 0;
370     size_t len;
371     struct afp_options options;
372     AFPConfig *config=NULL, *first = NULL; 
373
374     /* if config file doesn't exist, load defaults */
375     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
376     {
377         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
378             cmdline->configfile);
379         return AFPConfigInit(cmdline, cmdline);
380     }
381
382     /* scan in the configuration file */
383     len = 0;
384     while (!feof(fp)) {
385         if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
386             continue;
387         len = strlen(buf);
388         if ( len >= 2 && buf[len-2] == '\\' ) {
389             len -= 2;
390             continue;
391         } else
392             len = 0;
393
394         /* a little pre-processing to get rid of spaces and end-of-lines */
395         p = buf;
396         while (p && isspace(*p))
397             p++;
398         if (!p || (*p == '\0'))
399             continue;
400
401         have_option = 1;
402
403         memcpy(&options, cmdline, sizeof(options));
404         if (!afp_options_parseline(p, &options))
405             continue;
406
407         /* AFPConfigInit can return two linked configs due to DSI and ASP */
408         if (!first) {
409             if ((first = AFPConfigInit(&options, cmdline)))
410                 config = first->next ? first->next : first;
411         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
412             config = config->next->next ? config->next->next : config->next;
413         }
414     }
415
416 #ifdef HAVE_LDAP
417     /* Parse afp_ldap.conf */
418     acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
419 #endif /* HAVE_LDAP */
420
421     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
422     fclose(fp);
423
424     if (!have_option)
425         first = AFPConfigInit(cmdline, cmdline);
426
427     /* Now register with zeroconf, we also need the volumes for that */
428     load_volumes(&first->obj);
429     zeroconf_register(first);
430
431     return first;
432 }