]> 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     dsi->dsireadbuf = options->dsireadbuf;
214
215     if (options->flags & OPTION_PROXY) {
216         LOG(log_note, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
217             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
218     } else {
219         LOG(log_note, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
220             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
221     }
222
223 #ifdef USE_SRVLOC
224     dsi->srvloc_url[0] = '\0';  /*  Mark that we haven't registered.  */
225     if (!(options->flags & OPTION_NOSLP)) {
226         SLPError err;
227         SLPError callbackerr;
228         SLPHandle hslp;
229         unsigned int afp_port;
230         int   l;
231         char *srvloc_hostname;
232         const char *hostname;
233
234         err = SLPOpen("en", SLP_FALSE, &hslp);
235         if (err != SLP_OK) {
236             LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
237             goto srvloc_reg_err;
238         }
239
240         /* XXX We don't want to tack on the port number if we don't have to.
241          * Why?
242          * Well, this seems to break MacOS < 10.  If the user _really_ wants to
243          * use a non-default port, they can, but be aware, this server might
244          * not show up int the Network Browser.
245          */
246         afp_port = getip_port((struct sockaddr *)&dsi->server);
247         /* If specified use the FQDN to register with srvloc, otherwise use IP. */
248         p = NULL;
249         if (options->fqdn) {
250             hostname = options->fqdn;
251             p = strchr(hostname, ':');
252         }       
253         else 
254             hostname = getip_string((struct sockaddr *)&dsi->server);
255
256         srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
257
258         if ((p) || afp_port == 548) {
259             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
260         }
261         else {
262             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
263         }
264
265         if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
266             LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
267             dsi->srvloc_url[0] = '\0';
268             goto srvloc_reg_err;
269         }
270
271         err = SLPReg(hslp,
272                      dsi->srvloc_url,
273                      SLP_LIFETIME_MAXIMUM,
274                      "afp",
275                      "",
276                      SLP_TRUE,
277                      SRVLOC_callback,
278                      &callbackerr);
279         if (err != SLP_OK) {
280             LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
281             dsi->srvloc_url[0] = '\0';
282             goto srvloc_reg_err;
283         }
284
285         if (callbackerr != SLP_OK) {
286             LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
287             dsi->srvloc_url[0] = '\0';
288             goto srvloc_reg_err;
289         }
290
291         LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
292         config->server_cleanup = dsi_cleanup;
293
294 srvloc_reg_err:
295         SLPClose(hslp);
296     }
297 #endif /* USE_SRVLOC */
298
299     config->fd = dsi->serversock;
300     config->obj.handle = dsi;
301     config->obj.config = config;
302     config->obj.proto = AFPPROTO_DSI;
303
304     memcpy(&config->obj.options, options, sizeof(struct afp_options));
305     /* get rid of any appletalk info. we use the fact that the DSI
306      * stuff is done after the ASP stuff. */
307     p = config->obj.options.server;
308     if (p && (q = strchr(p, ':')))
309         *q = '\0';
310
311     config->optcount = refcount;
312     (*refcount)++;
313
314     config->server_start = dsi_start;
315     return config;
316 }
317
318 /* allocate server configurations. this should really store the last
319  * entry in config->last or something like that. that would make
320  * supporting multiple dsi transports easier. */
321 static AFPConfig *AFPConfigInit(struct afp_options *options,
322                                 const struct afp_options *defoptions)
323 {
324     AFPConfig *next = NULL;
325     unsigned char *refcount;
326
327     if ((refcount = (unsigned char *)
328                     calloc(1, sizeof(unsigned char))) == NULL) {
329         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
330         return NULL;
331     }
332
333     /* set signature */
334     set_signature(options);
335
336     /* handle dsi transports and dsi proxies. we only proxy
337      * for DSI connections. */
338
339     /* this should have something like the following:
340      * for (i=mindsi; i < maxdsi; i++)
341      *   if (options->transports & (1 << i) && 
342      *     (next = DSIConfigInit(options, refcount, i)))
343      *     next->defoptions = defoptions;
344      */
345     if ( (options->transports & AFPTRANS_TCP)
346          &&
347          ((options->flags & OPTION_PROXY) == 0)
348          &&
349          (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
350         next->defoptions = defoptions;
351
352     /* load in all the authentication modules. we can load the same
353        things multiple times if necessary. however, loading different
354        things with the same names will cause complaints. by not loading
355        in any uams with proxies, we prevent ddp connections from succeeding.
356     */
357     auth_load(options->uampath, options->uamlist);
358
359     /* this should be able to accept multiple dsi transports. i think
360      * the only thing that gets affected is the net addresses. */
361     status_init(next, options);
362
363     return next;
364 }
365
366 /* fill in the appropriate bits for each interface */
367 AFPConfig *configinit(struct afp_options *cmdline)
368 {
369     FILE *fp;
370     char buf[LINESIZE + 1], *p, have_option = 0;
371     size_t len;
372     struct afp_options options;
373     AFPConfig *config=NULL, *first = NULL; 
374
375     /* if config file doesn't exist, load defaults */
376     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
377     {
378         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
379             cmdline->configfile);
380         return AFPConfigInit(cmdline, cmdline);
381     }
382
383     /* scan in the configuration file */
384     len = 0;
385     while (!feof(fp)) {
386         if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
387             continue;
388         len = strlen(buf);
389         if ( len >= 2 && buf[len-2] == '\\' ) {
390             len -= 2;
391             continue;
392         } else
393             len = 0;
394
395         /* a little pre-processing to get rid of spaces and end-of-lines */
396         p = buf;
397         while (p && isspace(*p))
398             p++;
399         if (!p || (*p == '\0'))
400             continue;
401
402         have_option = 1;
403
404         memcpy(&options, cmdline, sizeof(options));
405         if (!afp_options_parseline(p, &options))
406             continue;
407
408         /* AFPConfigInit can return two linked configs due to DSI and ASP */
409         if (!first) {
410             if ((first = AFPConfigInit(&options, cmdline)))
411                 config = first->next ? first->next : first;
412         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
413             config = config->next->next ? config->next->next : config->next;
414         }
415     }
416
417 #ifdef HAVE_LDAP
418     /* Parse afp_ldap.conf */
419     acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
420 #endif /* HAVE_LDAP */
421
422     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
423     fclose(fp);
424
425     if (!have_option)
426         first = AFPConfigInit(cmdline, cmdline);
427
428     /* Now register with zeroconf, we also need the volumes for that */
429     if (! (first->obj.options.flags & OPTION_NOZEROCONF)) {
430         load_volumes(&first->obj);
431         zeroconf_register(first);
432     }
433
434     return first;
435 }