2 * $Id: afp_config.c,v 1.22.6.5 2004-04-27 22:47:32 didg Exp $
4 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
20 #else /* STDC_HEADERS */
24 #endif /* HAVE_STRCHR */
25 char *strchr (), *strrchr ();
27 #define memcpy(d,s,n) bcopy ((s), (d), (n))
28 #define memmove(d,s,n) bcopy ((s), (d), (n))
29 #endif /* ! HAVE_MEMCPY */
30 #endif /* STDC_HEADERS */
34 #endif /* HAVE_UNISTD_H */
36 #include <atalk/logger.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
42 #include <atalk/dsi.h>
43 #include <atalk/atp.h>
44 #include <atalk/asp.h>
45 #include <atalk/nbp.h>
46 #include <atalk/afp.h>
47 #include <atalk/compat.h>
48 #include <atalk/server_child.h>
51 #endif /* USE_SRVLOC */
54 #include "afp_config.h"
60 /* get rid of unneeded configurations. i use reference counts to deal
61 * w/ multiple configs sharing the same afp_options. oh, to dream of
62 * garbage collection ... */
63 void configfree(AFPConfig *configs, const AFPConfig *config)
67 for (p = configs; p; p = q) {
72 /* do a little reference counting */
73 if (--(*p->optcount) < 1) {
74 afp_options_free(&p->obj.options, p->defoptions);
78 switch (p->obj.proto) {
84 atp_close(((ASP) p->obj.handle)->asp_atp);
87 #endif /* no afp/asp */
98 static void SRVLOC_callback(SLPHandle hslp, SLPError errcode, void *cookie) {
99 *(SLPError*)cookie = errcode;
102 static char hex[17] = "0123456789abcdef";
104 static char * srvloc_encode(const struct afp_options *options, const char *name)
106 static char buf[512];
111 char *Obj, *Type = "", *Zone = "";
114 /* Convert name to maccharset */
115 if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
116 name, strlen(name), &conv_name)) )
119 /* Escape characters */
121 while (*p && i<(sizeof(buf)-4)) {
124 else if (isspace(*p)) {
130 else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
132 buf[i++] = hex[*p >> 4];
133 buf[i++] = hex[*p++ & 15];
143 if (nbp_name(options->server, &Obj, &Type, &Zone )) {
144 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
147 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
154 #endif /* USE_SRVLOC */
157 static void dsi_cleanup(const AFPConfig *config)
160 SLPError callbackerr;
162 DSI *dsi = (DSI *)config->obj.handle;
164 /* Do nothing if we didn't register. */
165 if (!dsi || dsi->srvloc_url[0] == '\0')
168 err = SLPOpen("en", SLP_FALSE, &hslp);
170 LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
171 goto srvloc_dereg_err;
179 LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
180 goto srvloc_dereg_err;
183 if (callbackerr != SLP_OK) {
184 LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
185 goto srvloc_dereg_err;
189 dsi->srvloc_url[0] = '\0';
192 #endif /* USE_SRVLOC */
195 static void asp_cleanup(const AFPConfig *config)
197 /* we need to stop tickle handler */
199 nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
200 &config->obj.options.ddpaddr);
203 /* these two are almost identical. it should be possible to collapse them
204 * into one with minimal junk. */
205 static int asp_start(AFPConfig *config, AFPConfig *configs,
206 server_child *server_children)
210 if (!(asp = asp_getsession(config->obj.handle, server_children,
211 config->obj.options.tickleval))) {
212 LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
217 configfree(configs, config); /* free a bunch of stuff */
218 afp_over_asp(&config->obj);
224 #endif /* no afp/asp */
226 static int dsi_start(AFPConfig *config, AFPConfig *configs,
227 server_child *server_children)
231 if (!(dsi = dsi_getsession(config->obj.handle, server_children,
232 config->obj.options.tickleval))) {
233 LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
239 configfree(configs, config);
240 afp_over_dsi(&config->obj); /* start a session */
248 static AFPConfig *ASPConfigInit(const struct afp_options *options,
249 unsigned char *refcount)
254 char *Obj, *Type = "AFPServer", *Zone = "*";
256 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
259 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
260 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
265 if ((asp = asp_init( atp )) == NULL) {
266 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
272 /* register asp server */
273 Obj = (char *) options->hostname;
274 if (nbp_name(options->server, &Obj, &Type, &Zone )) {
275 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
276 goto serv_free_return;
279 /* dup Obj, Type and Zone as they get assigned to a single internal
280 * buffer by nbp_name */
281 if ((config->obj.Obj = strdup(Obj)) == NULL)
282 goto serv_free_return;
284 if ((config->obj.Type = strdup(Type)) == NULL) {
285 free(config->obj.Obj);
286 goto serv_free_return;
289 if ((config->obj.Zone = strdup(Zone)) == NULL) {
290 free(config->obj.Obj);
291 free(config->obj.Type);
292 goto serv_free_return;
295 /* make sure we're not registered */
296 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
297 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
298 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
299 free(config->obj.Obj);
300 free(config->obj.Type);
301 free(config->obj.Zone);
302 goto serv_free_return;
305 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
306 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
307 atp_sockaddr( atp )->sat_addr.s_node,
308 atp_sockaddr( atp )->sat_port, VERSION );
310 config->fd = atp_fileno(atp);
311 config->obj.handle = asp;
312 config->obj.config = config;
313 config->obj.proto = AFPPROTO_ASP;
315 memcpy(&config->obj.options, options, sizeof(struct afp_options));
316 config->optcount = refcount;
319 config->server_start = asp_start;
320 config->server_cleanup = asp_cleanup;
329 #endif /* no afp/asp */
332 static AFPConfig *DSIConfigInit(const struct afp_options *options,
333 unsigned char *refcount,
334 const dsi_proto protocol)
341 SLPError callbackerr;
343 struct servent *afpovertcp;
345 char *srvloc_hostname, *hostname;
346 #endif /* USE_SRVLOC */
348 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
349 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
353 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
354 options->ipaddr, options->port,
355 options->flags & OPTION_PROXY,
356 options->server_quantum)) == NULL) {
357 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
362 if (options->flags & OPTION_PROXY) {
363 LOG(log_info, logtype_afpd, "ASIP proxy initialized for %s:%d (%s)",
364 inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
367 LOG(log_info, logtype_afpd, "ASIP started on %s:%d(%d) (%s)",
368 inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
369 dsi->serversock, VERSION);
373 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
374 if (!(options->flags & OPTION_NOSLP)) {
375 err = SLPOpen("en", SLP_FALSE, &hslp);
377 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
381 /* XXX We don't want to tack on the port number if we don't have to.
383 * Well, this seems to break MacOS < 10. If the user _really_ wants to
384 * use a non-default port, they can, but be aware, this server might
385 * not show up int the Network Browser.
387 afpovertcp = getservbyname("afpovertcp", "tcp");
388 if (afpovertcp != NULL) {
389 afp_port = afpovertcp->s_port;
391 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
394 hostname = options->fqdn;
395 p = strchr(hostname, ':');
398 hostname = inet_ntoa(dsi->server.sin_addr);
399 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
401 if (strlen(srvloc_hostname) > (sizeof(dsi->srvloc_url) - strlen(hostname) - 21)) {
402 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
403 dsi->srvloc_url[0] = '\0';
406 if ((p) || dsi->server.sin_port == afp_port) {
407 sprintf(dsi->srvloc_url, "afp://%s/?NAME=%s", hostname, srvloc_hostname);
410 sprintf(dsi->srvloc_url, "afp://%s:%d/?NAME=%s", hostname, ntohs(dsi->server.sin_port), srvloc_hostname);
415 SLP_LIFETIME_MAXIMUM,
422 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
423 dsi->srvloc_url[0] = '\0';
427 if (callbackerr != SLP_OK) {
428 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
429 dsi->srvloc_url[0] = '\0';
433 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
438 #endif /* USE_SRVLOC */
441 config->fd = dsi->serversock;
442 config->obj.handle = dsi;
443 config->obj.config = config;
444 config->obj.proto = AFPPROTO_DSI;
446 memcpy(&config->obj.options, options, sizeof(struct afp_options));
447 /* get rid of any appletalk info. we use the fact that the DSI
448 * stuff is done after the ASP stuff. */
449 p = config->obj.options.server;
450 if (p && (q = strchr(p, ':')))
453 config->optcount = refcount;
456 config->server_start = dsi_start;
458 config->server_cleanup = dsi_cleanup;
463 /* allocate server configurations. this should really store the last
464 * entry in config->last or something like that. that would make
465 * supporting multiple dsi transports easier. */
466 static AFPConfig *AFPConfigInit(const struct afp_options *options,
467 const struct afp_options *defoptions)
469 AFPConfig *config = NULL, *next = NULL;
470 unsigned char *refcount;
472 if ((refcount = (unsigned char *)
473 calloc(1, sizeof(unsigned char))) == NULL) {
474 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
479 /* handle asp transports */
480 if ((options->transports & AFPTRANS_DDP) &&
481 (config = ASPConfigInit(options, refcount)))
482 config->defoptions = defoptions;
485 /* handle dsi transports and dsi proxies. we only proxy
486 * for DSI connections. */
488 /* this should have something like the following:
489 * for (i=mindsi; i < maxdsi; i++)
490 * if (options->transports & (1 << i) &&
491 * (next = DSIConfigInit(options, refcount, i)))
492 * next->defoptions = defoptions;
494 if ((options->transports & AFPTRANS_TCP) &&
495 (((options->flags & OPTION_PROXY) == 0) ||
496 ((options->flags & OPTION_PROXY) && config))
497 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
498 next->defoptions = defoptions;
500 /* load in all the authentication modules. we can load the same
501 things multiple times if necessary. however, loading different
502 things with the same names will cause complaints. by not loading
503 in any uams with proxies, we prevent ddp connections from succeeding.
505 auth_load(options->uampath, options->uamlist);
507 /* this should be able to accept multiple dsi transports. i think
508 * the only thing that gets affected is the net addresses. */
509 status_init(config, next, options);
511 /* attach dsi config to tail of asp config */
520 /* fill in the appropriate bits for each interface */
521 AFPConfig *configinit(struct afp_options *cmdline)
524 char buf[LINESIZE + 1], *p, have_option = 0;
525 struct afp_options options;
526 AFPConfig *config, *first = NULL;
529 /* if config file doesn't exist, load defaults */
530 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
532 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
533 cmdline->configfile);
534 return AFPConfigInit(cmdline, cmdline);
537 LOG(log_debug, logtype_afpd, "Loading ConfigFile");
539 /* scan in the configuration file */
541 if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
544 /* a little pre-processing to get rid of spaces and end-of-lines */
546 while (p && isspace(*p))
548 if (!p || (*p == '\0'))
553 memcpy(&options, cmdline, sizeof(options));
554 if (!afp_options_parseline(p, &options))
557 /* this should really get a head and a tail to simplify things. */
559 if ((first = AFPConfigInit(&options, cmdline)))
560 config = first->next ? first->next : first;
561 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
562 config = config->next->next ? config->next->next : config->next;
566 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
570 return AFPConfigInit(cmdline, cmdline);