]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
Fixed some problems in the SRVLOC code. OS 9 doesn't like a port number
[netatalk.git] / etc / afpd / afp_config.c
1 /* 
2  * $Id: afp_config.c,v 1.6 2001-09-19 23:17:23 jmarcus Exp $
3  *
4  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14
15 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24 #ifndef HAVE_MEMCPY
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif /* HAVE_UNISTD_H */
33 #include <ctype.h>
34 #include <syslog.h>
35
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39
40 #include <atalk/dsi.h>
41 #include <atalk/atp.h>
42 #include <atalk/asp.h>
43 #include <atalk/nbp.h>
44 #include <atalk/afp.h>
45 #include <atalk/compat.h>
46 #include <atalk/server_child.h>
47 #ifdef USE_SRVLOC
48 #include <slp.h>
49 static char srvloc_url[512];
50 #endif
51
52 #include "globals.h"
53 #include "afp_config.h"
54 #include "uam_auth.h"
55 #include "status.h"
56
57 #define LINESIZE 1024  
58
59 /* get rid of unneeded configurations. i use reference counts to deal
60  * w/ multiple configs sharing the same afp_options. oh, to dream of
61  * garbage collection ... */
62 void configfree(AFPConfig *configs, const AFPConfig *config)
63 {
64   AFPConfig *p, *q;
65
66   for (p = configs; p; p = q) {
67     q = p->next;
68     if (p == config)
69       continue;
70
71     /* do a little reference counting */
72     if (--(*p->optcount) < 1) {
73       afp_options_free(&p->obj.options, p->defoptions);
74       free(p->optcount);
75     }
76
77     switch (p->obj.proto) {
78 #ifndef NO_DDP
79     case AFPPROTO_ASP:
80       free(p->obj.Obj);
81       free(p->obj.Type);
82       free(p->obj.Zone);
83       atp_close(((ASP) p->obj.handle)->asp_atp);
84       free(p->obj.handle);
85       break;
86 #endif /* no afp/asp */
87     case AFPPROTO_DSI:
88       close(p->fd);
89       free(p->obj.handle);
90       break;
91     }
92     free(p);
93   }
94 }
95
96 #ifdef USE_SRVLOC
97 static void SRVLOC_callback(SLPHandle hslp, SLPError errcode, void *cookie) {
98         *(SLPError*)cookie = errcode;
99 }
100 #endif
101
102 #ifdef USE_SRVLOC
103 static void dsi_cleanup(const AFPConfig *config)
104 {
105   SLPError err;
106   SLPError callbackerr;
107   SLPHandle hslp;
108   err = SLPOpen("en", SLP_FALSE, &hslp);
109   if (err != SLP_OK) {
110     syslog(LOG_ERR, "Error opening SRVLOC handle");
111     goto srvloc_dereg_err;
112   } 
113
114   err = SLPDereg(hslp,
115                  srvloc_url,
116                  SRVLOC_callback,
117                                  &callbackerr);
118   if (err != SLP_OK) {
119           syslog(LOG_ERR, "Error unregistering %s from SRVLOC", srvloc_url);
120           goto srvloc_dereg_err;
121   }
122
123   if (callbackerr != SLP_OK) {
124     syslog(LOG_ERR, "Error in callback while trying to unregister %s from SRVLOC (%i)", srvloc_url, callbackerr);
125         goto srvloc_dereg_err;
126   }
127
128   srvloc_dereg_err:
129     SLPClose(hslp);
130 }
131 #endif
132
133 #ifndef NO_DDP
134 static void asp_cleanup(const AFPConfig *config)
135 {
136   nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
137               &config->obj.options.ddpaddr);
138 }
139
140 /* these two are almost identical. it should be possible to collapse them
141  * into one with minimal junk. */
142 static int asp_start(AFPConfig *config, AFPConfig *configs, 
143                      server_child *server_children) 
144 {
145   ASP asp;
146
147   if (!(asp = asp_getsession(config->obj.handle, server_children, 
148                              config->obj.options.tickleval))) {
149     syslog( LOG_ERR, "main: asp_getsession: %m" );
150     exit( 1 );
151   } 
152   
153   if (asp->child) {
154     configfree(configs, config); /* free a bunch of stuff */
155     afp_over_asp(&config->obj);
156     exit (0);
157   }
158
159   return 0;
160 }
161 #endif /* no afp/asp */
162
163 static int dsi_start(AFPConfig *config, AFPConfig *configs,
164                      server_child *server_children)
165 {
166   DSI *dsi;
167
168   if (!(dsi = dsi_getsession(config->obj.handle, server_children,
169                              config->obj.options.tickleval))) {
170     syslog( LOG_ERR, "main: dsi_getsession: %m" );
171     exit( 1 );
172   } 
173   
174   /* we've forked. */
175   if (dsi->child) {
176     configfree(configs, config);
177     afp_over_dsi(&config->obj); /* start a session */
178     exit (0);
179   }
180
181   return 0;
182 }
183
184 #ifndef NO_DDP
185 static AFPConfig *ASPConfigInit(const struct afp_options *options,
186                                 unsigned char *refcount)
187 {
188   AFPConfig *config;
189   ATP atp;
190   ASP asp;
191   char *Obj, *Type = "AFPServer", *Zone = "*";
192   
193   if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
194     return NULL;
195   
196   if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
197     syslog( LOG_ERR, "main: atp_open: %m");
198     free(config);
199     return NULL;
200   }
201   
202   if ((asp = asp_init( atp )) == NULL) {
203     syslog( LOG_ERR, "main: asp_init: %m" );
204     atp_close(atp);
205     free(config);
206     return NULL;
207   }
208   
209   /* register asp server */
210   Obj = (char *) options->hostname;
211   if (nbp_name(options->server, &Obj, &Type, &Zone )) {
212     syslog( LOG_ERR, "main: can't parse %s", options->server );
213     goto serv_free_return;
214   }
215
216   /* dup Obj, Type and Zone as they get assigned to a single internal
217    * buffer by nbp_name */
218   if ((config->obj.Obj  = strdup(Obj)) == NULL) 
219     goto serv_free_return;
220
221   if ((config->obj.Type = strdup(Type)) == NULL) {
222     free(config->obj.Obj);
223     goto serv_free_return;
224   }
225
226   if ((config->obj.Zone = strdup(Zone)) == NULL) {
227     free(config->obj.Obj);
228     free(config->obj.Type);
229     goto serv_free_return;
230   }
231   
232   /* make sure we're not registered */
233   nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr); 
234   if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
235     syslog( LOG_ERR, "Can't register %s:%s@%s", Obj, Type, Zone );
236     free(config->obj.Obj);
237     free(config->obj.Type);
238     free(config->obj.Zone);
239     goto serv_free_return;
240   }
241
242   syslog( LOG_INFO, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
243           ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
244           atp_sockaddr( atp )->sat_addr.s_node,
245           atp_sockaddr( atp )->sat_port, VERSION );
246   
247   config->fd = atp_fileno(atp);
248   config->obj.handle = asp;
249   config->obj.config = config;
250   config->obj.proto = AFPPROTO_ASP;
251
252   memcpy(&config->obj.options, options, sizeof(struct afp_options));
253   config->optcount = refcount;
254   (*refcount)++;
255
256   config->server_start = asp_start;
257   config->server_cleanup = asp_cleanup;
258
259   return config;
260
261 serv_free_return:
262   asp_close(asp);
263   free(config);
264   return NULL;
265 }
266 #endif /* no afp/asp */
267
268
269 static AFPConfig *DSIConfigInit(const struct afp_options *options,
270                                 unsigned char *refcount,
271                                 const dsi_proto protocol) 
272 {
273   AFPConfig *config;
274   DSI *dsi;
275   char *p, *q;
276 #ifdef USE_SRVLOC
277   SLPError err;
278   SLPError callbackerr;
279   SLPHandle hslp;
280   struct servent *afpovertcp;
281 #endif
282
283   if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
284     syslog( LOG_ERR, "DSIConfigInit: malloc(config): %m" );
285     return NULL;
286   }
287
288   if ((dsi = dsi_init(protocol, "afpd", options->hostname,
289                       options->ipaddr, options->port, 
290                       options->flags & OPTION_PROXY, 
291                       options->server_quantum)) == NULL) {
292     syslog( LOG_ERR, "main: dsi_init: %m" );
293     free(config);
294     return NULL;
295   }
296
297   if (options->flags & OPTION_PROXY) {
298     syslog(LOG_INFO, "ASIP proxy initialized for %s:%d (%s)",
299            inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), 
300            VERSION);
301   } else {
302     syslog(LOG_INFO, "ASIP started on %s:%d(%d) (%s)",
303            inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), 
304            dsi->serversock, VERSION);
305   }
306
307 #ifdef USE_SRVLOC
308   err = SLPOpen("en", SLP_FALSE, &hslp);
309   if (err != SLP_OK) {
310           syslog(LOG_ERR, "Error opening SRVLOC handle");
311           goto srvloc_reg_err;
312   }
313
314   /* XXX We don't want to tack on the port number if we don't have to.  Why?
315    * Well, this seems to break MacOS < 10.  If the user _really_ wants to
316    * use a non-default port, they can, but be aware, this server might not
317    * show up int the Network Browser. */
318   afpovertcp = getservbyname("afpovertcp", "tcp");
319   if (dsi->server.sin_port == afpovertcp->s_port) {
320         snprintf(srvloc_url, sizeof(srvloc_url), "afp://%s/?NAME=%s", inet_ntoa(dsi->server.sin_addr), options->hostname);
321   }
322   else {
323         snprintf(srvloc_url, sizeof(srvloc_url), "afp://%s:%d/?NAME=%s", inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port), options->hostname);
324   }
325
326   err = SLPReg(hslp,
327                srvloc_url,
328                            SLP_LIFETIME_MAXIMUM,
329                            "",
330                            "",
331                            SLP_TRUE,
332                            SRVLOC_callback,
333                            &callbackerr);
334   if (err != SLP_OK) {
335           syslog(LOG_ERR, "Error registering %s with SRVLOC", srvloc_url);
336           goto srvloc_reg_err;
337   }
338
339   if (callbackerr != SLP_OK) {
340           syslog(LOG_ERR, "Error in callback trying to register %s with SRVLOC", srvloc_url);
341           goto srvloc_reg_err;
342   }
343
344   syslog(LOG_INFO, "Sucessfully registered %s with SRVLOC", srvloc_url);
345
346   srvloc_reg_err:
347     SLPClose(hslp);
348 #endif
349
350
351   config->fd = dsi->serversock;
352   config->obj.handle = dsi;
353   config->obj.config = config;
354   config->obj.proto = AFPPROTO_DSI;
355
356   memcpy(&config->obj.options, options, sizeof(struct afp_options));
357   /* get rid of any appletalk info. we use the fact that the DSI
358    * stuff is done after the ASP stuff. */
359   p = config->obj.options.server;
360   if (p && (q = strchr(p, ':')))
361     *q = '\0';
362     
363   config->optcount = refcount;
364   (*refcount)++;
365
366   config->server_start = dsi_start;
367 #ifdef USE_SRVLOC
368   config->server_cleanup = dsi_cleanup;
369 #endif
370   return config;
371 }
372
373 /* allocate server configurations. this should really store the last
374  * entry in config->last or something like that. that would make
375  * supporting multiple dsi transports easier. */
376 static AFPConfig *AFPConfigInit(const struct afp_options *options,
377                                 const struct afp_options *defoptions)
378 {
379   AFPConfig *config = NULL, *next = NULL;
380   unsigned char *refcount;
381
382   if ((refcount = (unsigned char *) 
383        calloc(1, sizeof(unsigned char))) == NULL) {
384     syslog( LOG_ERR, "AFPConfigInit: calloc(refcount): %m" );
385     return NULL;
386   }
387
388 #ifndef NO_DDP
389   /* handle asp transports */
390   if ((options->transports & AFPTRANS_DDP) && 
391       (config = ASPConfigInit(options, refcount)))
392     config->defoptions = defoptions;
393 #endif /* NO_DDP */
394
395   /* handle dsi transports and dsi proxies. we only proxy
396    * for DSI connections. */
397
398   /* this should have something like the following:
399    * for (i=mindsi; i < maxdsi; i++)
400    *   if (options->transports & (1 << i) && 
401    *     (next = DSIConfigInit(options, refcount, i)))
402    *     next->defoptions = defoptions;
403    */
404   if ((options->transports & AFPTRANS_TCP) &&
405       (((options->flags & OPTION_PROXY) == 0) ||
406        ((options->flags & OPTION_PROXY) && config))
407       && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
408     next->defoptions = defoptions;
409
410   /* load in all the authentication modules. we can load the same
411      things multiple times if necessary. however, loading different
412      things with the same names will cause complaints. by not loading
413      in any uams with proxies, we prevent ddp connections from succeeding.
414   */
415   auth_load(options->uampath, options->uamlist);
416
417   /* this should be able to accept multiple dsi transports. i think
418    * the only thing that gets affected is the net addresses. */
419   status_init(config, next, options);
420
421   /* attach dsi config to tail of asp config */
422   if (config) {
423     config->next = next;
424     return config;
425   } 
426     
427   return next;
428 }
429
430 /* fill in the appropriate bits for each interface */
431 AFPConfig *configinit(struct afp_options *cmdline)
432 {
433   FILE *fp;
434   char buf[LINESIZE + 1], *p, have_option = 0;
435   struct afp_options options;
436   AFPConfig *config, *first = NULL;
437
438   /* if config file doesn't exist, load defaults */
439   if ((fp = fopen(cmdline->configfile, "r")) == NULL) 
440     return AFPConfigInit(cmdline, cmdline);
441
442   /* scan in the configuration file */
443   while (!feof(fp)) {
444     if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
445       continue;
446     
447     /* a little pre-processing to get rid of spaces and end-of-lines */
448     p = buf;
449     while (p && isspace(*p)) 
450       p++;
451     if (!p || (*p == '\0'))
452       continue;
453
454     have_option = 1;
455
456     memcpy(&options, cmdline, sizeof(options));
457     if (!afp_options_parseline(p, &options))
458       continue;
459
460     /* this should really get a head and a tail to simplify things. */
461     if (!first) {
462       if ((first = AFPConfigInit(&options, cmdline)))
463         config = first->next ? first->next : first;
464     } else if ((config->next = AFPConfigInit(&options, cmdline))) {
465       config = config->next->next ? config->next->next : config->next;
466     }
467   }
468
469   fclose(fp);
470
471   if (!have_option)
472     return AFPConfigInit(cmdline, cmdline);
473
474   return first;
475 }