1/*
2 * Copyright (c) 1999 Adrian Sun (asun@zoology.washington.edu)
3 * Copyright (c) 1990,1993 Regents of The University of Michigan.
4 * All Rights Reserved.  See COPYRIGHT.
5 *
6 * modified from main.c. this handles afp over tcp.
7 */
8
9#ifdef HAVE_CONFIG_H
10#include "config.h"
11#endif /* HAVE_CONFIG_H */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <signal.h>
16#include <string.h>
17#include <errno.h>
18#ifdef HAVE_UNISTD_H
19#include <unistd.h>
20#endif /* HAVE_UNISTD_H */
21#include <sys/socket.h>
22#include <sys/time.h>
23#ifdef HAVE_SYS_STAT_H
24#include <sys/stat.h>
25#endif /* HAVE_SYS_STAT_H */
26#include <netinet/in.h>
27#include <netinet/tcp.h>
28#include <arpa/inet.h>
29#include <setjmp.h>
30#include <time.h>
31
32#include <atalk/logger.h>
33#include <atalk/dsi.h>
34#include <atalk/compat.h>
35#include <atalk/util.h>
36#include <atalk/uuid.h>
37#include <atalk/paths.h>
38#include <atalk/server_ipc.h>
39#include <atalk/fce_api.h>
40
41#include <atalk/globals.h>
42#include "switch.h"
43#include "auth.h"
44#include "fork.h"
45#include "dircache.h"
46
47#define WOL
48#ifdef WOL
49time_t last_wol=0;
50#define WOL_INTERVAL  300
51#endif
52#ifdef FORCE_UIDGID
53#warning UIDGID
54#include "uid.h"
55#endif /* FORCE_UIDGID */
56
57#ifndef SOL_TCP
58#define SOL_TCP IPPROTO_TCP
59#endif
60
61/*
62 * We generally pass this from afp_over_dsi to all afp_* funcs, so it should already be
63 * available everywhere. Unfortunately some funcs (eg acltoownermode) need acces to it
64 * but are deeply nested in the function chain with the caller already without acces to it.
65 * Changing this would require adding a reference to the caller which itself might be
66 * called in many places (eg acltoownermode is called from accessmode).
67 * The only sane way out is providing a copy of it here:
68 */
69AFPObj *AFPobj = NULL;
70
71typedef struct {
72    uint16_t DSIreqID;
73    uint8_t  AFPcommand;
74    uint32_t result;
75} rc_elem_t;
76
77/*
78 * AFP replay cache:
79 * - fix sized array
80 * - indexed just by taking DSIreqID mod REPLAYCACHE_SIZE
81 */
82static rc_elem_t replaycache[REPLAYCACHE_SIZE];
83
84static sigjmp_buf recon_jmp;
85static void afp_dsi_close(AFPObj *obj)
86{
87    DSI *dsi = obj->handle;
88    sigset_t sigs;
89
90    close(obj->ipc_fd);
91    obj->ipc_fd = -1;
92
93    /* we may have been called from a signal handler caught when afpd was running
94     * as uid 0, that's the wrong user for volume's prexec_close scripts if any,
95     * restore our login user
96     */
97    if (geteuid() != obj->uid) {
98        if (seteuid( obj->uid ) < 0) {
99            LOG(log_error, logtype_afpd, "can't seteuid(%u) back %s: uid: %u, euid: %u",
100                obj->uid, strerror(errno), getuid(), geteuid());
101            exit(EXITERR_SYS);
102        }
103    }
104
105    close_all_vol();
106
107    if (obj->logout) {
108        /* Block sigs, PAM/systemd/whoever might send us a SIG??? in (*obj->logout)() -> pam_close_session() */
109        sigfillset(&sigs);
110        pthread_sigmask(SIG_BLOCK, &sigs, NULL);
111        (*obj->logout)();
112    }
113
114    LOG(log_note, logtype_afpd, "AFP statistics: %.2f KB read, %.2f KB written",
115        dsi->read_count/1024.0, dsi->write_count/1024.0);
116    log_dircache_stat();
117
118    dsi_close(dsi);
119}
120
121/* -------------------------------
122 * SIGTERM
123 * a little bit of code duplication.
124 */
125static void afp_dsi_die(int sig)
126{
127    DSI *dsi = (DSI *)AFPobj->handle;
128
129    if (dsi->flags & DSI_RECONINPROG) {
130        /* Primary reconnect succeeded, got SIGTERM from afpd parent */
131        dsi->flags &= ~DSI_RECONINPROG;
132        return; /* this returns to afp_disconnect */
133    }
134
135    if (dsi->flags & DSI_DISCONNECTED) {
136        LOG(log_note, logtype_afpd, "Disconnected session terminating");
137        exit(0);
138    }
139
140    dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN);
141    afp_dsi_close(AFPobj);
142   if (sig) /* if no signal, assume dieing because logins are disabled &
143                don't log it (maintenance mode)*/
144        LOG(log_info, logtype_afpd, "Connection terminated");
145    if (sig == SIGTERM || sig == SIGALRM) {
146        exit( 0 );
147    }
148    else {
149        exit(sig);
150    }
151}
152
153/* SIGQUIT handler */
154static void ipc_reconnect_handler(int sig _U_)
155{
156    DSI *dsi = (DSI *)AFPobj->handle;
157
158    if (reconnect_ipc(AFPobj) != 0) {
159        LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC reconnect");
160        afp_dsi_close(AFPobj);
161        exit(EXITERR_SYS);
162    }
163
164    if (ipc_child_write(AFPobj->ipc_fd, IPC_GETSESSION, AFPobj->sinfo.clientid_len, AFPobj->sinfo.clientid) != 0) {
165        LOG(log_error, logtype_afpd, "ipc_reconnect_handler: failed IPC ID resend");
166        afp_dsi_close(AFPobj);
167        exit(EXITERR_SYS);
168    }
169    LOG(log_note, logtype_afpd, "ipc_reconnect_handler: IPC reconnect done");
170}
171
172/* SIGURG handler (primary reconnect) */
173static void afp_dsi_transfer_session(int sig _U_)
174{
175    uint16_t dsiID;
176    int socket;
177    DSI *dsi = (DSI *)AFPobj->handle;
178
179    LOG(log_debug, logtype_afpd, "afp_dsi_transfer_session: got SIGURG, trying to receive session");
180
181    if (readt(AFPobj->ipc_fd, &dsiID, 2, 0, 2) != 2) {
182        LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive DSI id, goodbye");
183        afp_dsi_close(AFPobj);
184        exit(EXITERR_SYS);
185    }
186
187    if ((socket = recv_fd(AFPobj->ipc_fd, 1)) == -1) {
188        LOG(log_error, logtype_afpd, "afp_dsi_transfer_session: couldn't receive session fd, goodbye");
189        afp_dsi_close(AFPobj);
190        exit(EXITERR_SYS);
191    }
192
193    LOG(log_debug, logtype_afpd, "afp_dsi_transfer_session: received socket fd: %i", socket);
194
195    dsi->proto_close(dsi);
196    dsi->socket = socket;
197    dsi->flags = DSI_RECONSOCKET;
198    dsi->datalen = 0;
199    dsi->eof = dsi->start = dsi->buffer;
200    dsi->in_write = 0;
201    dsi->header.dsi_requestID = dsiID;
202    dsi->header.dsi_command = DSIFUNC_CMD;
203
204    /*
205     * The session transfer happens in the middle of FPDisconnect old session, thus we
206     * have to send the reply now.
207     */
208    if (!dsi_cmdreply(dsi, AFP_OK)) {
209        LOG(log_error, logtype_afpd, "dsi_cmdreply: %s", strerror(errno) );
210        afp_dsi_close(AFPobj);
211        exit(EXITERR_CLNT);
212    }
213
214    LOG(log_note, logtype_afpd, "afp_dsi_transfer_session: succesfull primary reconnect");
215    /*
216     * Now returning from this signal handler return to dsi_receive which should start
217     * reading/continuing from the connected socket that was passed via the parent from
218     * another session. The parent will terminate that session.
219     */
220    siglongjmp(recon_jmp, 1);
221}
222
223/* ------------------- */
224static void afp_dsi_timedown(int sig _U_)
225{
226    struct sigaction	sv;
227    struct itimerval	it;
228    DSI                 *dsi = (DSI *)AFPobj->handle;
229    dsi->flags |= DSI_DIE;
230    /* shutdown and don't reconnect. server going down in 5 minutes. */
231    setmessage("The server is going down for maintenance.");
232    if (dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
233                  AFPATTN_MESG | AFPATTN_TIME(5)) < 0) {
234        DSI *dsi = (DSI *)AFPobj->handle;
235        dsi->down_request = 1;
236    }
237
238    it.it_interval.tv_sec = 0;
239    it.it_interval.tv_usec = 0;
240    it.it_value.tv_sec = 300;
241    it.it_value.tv_usec = 0;
242
243    if ( setitimer( ITIMER_REAL, &it, NULL ) < 0 ) {
244        LOG(log_error, logtype_afpd, "afp_timedown: setitimer: %s", strerror(errno) );
245        afp_dsi_die(EXITERR_SYS);
246    }
247    memset(&sv, 0, sizeof(sv));
248    sv.sa_handler = afp_dsi_die;
249    sigemptyset( &sv.sa_mask );
250    sigaddset(&sv.sa_mask, SIGHUP);
251    sigaddset(&sv.sa_mask, SIGTERM);
252    sv.sa_flags = SA_RESTART;
253    if ( sigaction( SIGALRM, &sv, NULL ) < 0 ) {
254        LOG(log_error, logtype_afpd, "afp_timedown: sigaction: %s", strerror(errno) );
255        afp_dsi_die(EXITERR_SYS);
256    }
257
258    /* ignore myself */
259    sv.sa_handler = SIG_IGN;
260    sigemptyset( &sv.sa_mask );
261    sv.sa_flags = SA_RESTART;
262    if ( sigaction( SIGUSR1, &sv, NULL ) < 0 ) {
263        LOG(log_error, logtype_afpd, "afp_timedown: sigaction SIGHUP: %s", strerror(errno) );
264        afp_dsi_die(EXITERR_SYS);
265    }
266}
267
268/* ---------------------------------
269 * SIGHUP reload configuration file
270 */
271volatile int reload_request = 0;
272
273static void afp_dsi_reload(int sig _U_)
274{
275    reload_request = 1;
276}
277
278/* ---------------------------------
279 * SIGINT: enable max_debug LOGging
280 */
281static volatile sig_atomic_t debug_request = 0;
282
283static void afp_dsi_debug(int sig _U_)
284{
285    debug_request = 1;
286}
287
288/* ---------------------- */
289static void afp_dsi_getmesg (int sig _U_)
290{
291    DSI *dsi = (DSI *)AFPobj->handle;
292
293    dsi->msg_request = 1;
294    if (dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5)) < 0)
295        dsi->msg_request = 2;
296}
297
298static void alarm_handler(int sig _U_)
299{
300    int err;
301    DSI *dsi = (DSI *)AFPobj->handle;
302
303    /* we have to restart the timer because some libraries may use alarm() */
304    setitimer(ITIMER_REAL, &dsi->timer, NULL);
305
306    /* we got some traffic from the client since the previous timer tick. */
307    if ((dsi->flags & DSI_DATA)) {
308        dsi->flags &= ~DSI_DATA;
309        return;
310    }
311
312    dsi->tickle++;
313    LOG(log_maxdebug, logtype_afpd, "alarm: tickles: %u, flags: %s|%s|%s|%s|%s|%s|%s|%s|%s",
314        dsi->tickle,
315        (dsi->flags & DSI_DATA) ?         "DSI_DATA" : "-",
316        (dsi->flags & DSI_RUNNING) ?      "DSI_RUNNING" : "-",
317        (dsi->flags & DSI_SLEEPING) ?     "DSI_SLEEPING" : "-",
318        (dsi->flags & DSI_EXTSLEEP) ?     "DSI_EXTSLEEP" : "-",
319        (dsi->flags & DSI_DISCONNECTED) ? "DSI_DISCONNECTED" : "-",
320        (dsi->flags & DSI_DIE) ?          "DSI_DIE" : "-",
321        (dsi->flags & DSI_NOREPLY) ?      "DSI_NOREPLY" : "-",
322        (dsi->flags & DSI_RECONSOCKET) ?  "DSI_RECONSOCKET" : "-",
323        (dsi->flags & DSI_RECONINPROG) ?  "DSI_RECONINPROG" : "-");
324
325    if (dsi->flags & DSI_SLEEPING) {
326        if (dsi->tickle > AFPobj->options.sleep) {
327            LOG(log_note, logtype_afpd, "afp_alarm: sleep time ended");
328            afp_dsi_die(EXITERR_CLNT);
329        }
330        return;
331    }
332
333    if (dsi->flags & DSI_DISCONNECTED) {
334        if (geteuid() == 0) {
335            LOG(log_note, logtype_afpd, "afp_alarm: unauthenticated user, connection problem");
336            afp_dsi_die(EXITERR_CLNT);
337        }
338        if (dsi->tickle > AFPobj->options.disconnected) {
339            LOG(log_error, logtype_afpd, "afp_alarm: reconnect timer expired, goodbye");
340            afp_dsi_die(EXITERR_CLNT);
341        }
342        return;
343    }
344
345    /* if we're in the midst of processing something, don't die. */
346    if (dsi->tickle >= AFPobj->options.timeout) {
347        LOG(log_error, logtype_afpd, "afp_alarm: child timed out, entering disconnected state");
348        if (dsi_disconnect(dsi) != 0)
349            afp_dsi_die(EXITERR_CLNT);
350        return;
351    }
352
353    if ((err = pollvoltime(AFPobj)) == 0)
354        LOG(log_debug, logtype_afpd, "afp_alarm: sending DSI tickle");
355        err = dsi_tickle(AFPobj->handle);
356    if (err <= 0) {
357        if (geteuid() == 0) {
358            LOG(log_note, logtype_afpd, "afp_alarm: unauthenticated user, connection problem");
359            afp_dsi_die(EXITERR_CLNT);
360        }
361        LOG(log_error, logtype_afpd, "afp_alarm: connection problem, entering disconnected state");
362        if (dsi_disconnect(dsi) != 0)
363            afp_dsi_die(EXITERR_CLNT);
364    }
365}
366
367/* -----------------
368   if dsi->in_write is set attention, tickle (and close?) msg
369   aren't sent. We don't care about tickle
370*/
371static void pending_request(DSI *dsi)
372{
373    /* send pending attention */
374
375    /* read msg if any, it could be done in afp_getsrvrmesg */
376    if (dsi->msg_request) {
377        if (dsi->msg_request == 2) {
378            /* didn't send it in signal handler */
379            dsi_attention(AFPobj->handle, AFPATTN_MESG | AFPATTN_TIME(5));
380        }
381        dsi->msg_request = 0;
382        readmessage(AFPobj);
383    }
384    if (dsi->down_request) {
385        dsi->down_request = 0;
386        dsi_attention(AFPobj->handle, AFPATTN_SHUTDOWN | AFPATTN_NORECONNECT |
387                  AFPATTN_MESG | AFPATTN_TIME(5));
388    }
389}
390
391void afp_over_dsi_sighandlers(AFPObj *obj)
392{
393    DSI *dsi = (DSI *) obj->handle;
394    struct sigaction action;
395
396    memset(&action, 0, sizeof(action));
397    sigfillset(&action.sa_mask);
398    action.sa_flags = SA_RESTART;
399
400    /* install SIGHUP */
401    action.sa_handler = afp_dsi_reload;
402    if ( sigaction( SIGHUP, &action, NULL ) < 0 ) {
403        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
404        afp_dsi_die(EXITERR_SYS);
405    }
406
407    /* install SIGURG */
408    action.sa_handler = afp_dsi_transfer_session;
409    if ( sigaction( SIGURG, &action, NULL ) < 0 ) {
410        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
411        afp_dsi_die(EXITERR_SYS);
412    }
413
414    /* install SIGTERM */
415    action.sa_handler = afp_dsi_die;
416    if ( sigaction( SIGTERM, &action, NULL ) < 0 ) {
417        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
418        afp_dsi_die(EXITERR_SYS);
419    }
420
421    /* install SIGQUIT */
422    action.sa_handler = ipc_reconnect_handler;
423    if ( sigaction(SIGQUIT, &action, NULL ) < 0 ) {
424        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
425        afp_dsi_die(EXITERR_SYS);
426    }
427
428    /* SIGUSR2 - server message support */
429    action.sa_handler = afp_dsi_getmesg;
430    if ( sigaction( SIGUSR2, &action, NULL) < 0 ) {
431        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
432        afp_dsi_die(EXITERR_SYS);
433    }
434
435    /*  SIGUSR1 - set down in 5 minutes  */
436    action.sa_handler = afp_dsi_timedown;
437    action.sa_flags = SA_RESTART;
438    if ( sigaction( SIGUSR1, &action, NULL) < 0 ) {
439        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
440        afp_dsi_die(EXITERR_SYS);
441    }
442
443    /*  SIGINT - enable max_debug LOGging to /tmp/afpd.PID.XXXXXX */
444    action.sa_handler = afp_dsi_debug;
445    if ( sigaction( SIGINT, &action, NULL) < 0 ) {
446        LOG(log_error, logtype_afpd, "afp_over_dsi: sigaction: %s", strerror(errno) );
447        afp_dsi_die(EXITERR_SYS);
448    }
449
450#ifndef DEBUGGING
451    /* SIGALRM - tickle handler */
452    action.sa_handler = alarm_handler;
453    if ((sigaction(SIGALRM, &action, NULL) < 0) ||
454            (setitimer(ITIMER_REAL, &dsi->timer, NULL) < 0)) {
455        afp_dsi_die(EXITERR_SYS);
456    }
457#endif /* DEBUGGING */
458}
459
460/* -------------------------------------------
461 afp over dsi. this never returns.
462*/
463void afp_over_dsi(AFPObj *obj)
464{
465    DSI *dsi = (DSI *) obj->handle;
466    int rc_idx;
467    u_int32_t err, cmd;
468    u_int8_t function;
469#ifdef WOL
470    time_t now;
471#endif
472
473    AFPobj = obj;
474    obj->exit = afp_dsi_die;
475    obj->reply = (int (*)()) dsi_cmdreply;
476    obj->attention = (int (*)(void *, AFPUserBytes)) dsi_attention;
477    dsi->tickle = 0;
478
479    afp_over_dsi_sighandlers(obj);
480
481    if (dircache_init(obj->options.dircachesize) != 0)
482        afp_dsi_die(EXITERR_SYS);
483
484    /* set TCP snd/rcv buf */
485    if (obj->options.tcp_rcvbuf) {
486        if (setsockopt(dsi->socket,
487                       SOL_SOCKET,
488                       SO_RCVBUF,
489                       &obj->options.tcp_rcvbuf,
490                       sizeof(obj->options.tcp_rcvbuf)) != 0) {
491            LOG(log_error, logtype_dsi, "afp_over_dsi: setsockopt(SO_RCVBUF): %s", strerror(errno));
492        }
493    }
494    if (obj->options.tcp_sndbuf) {
495        if (setsockopt(dsi->socket,
496                       SOL_SOCKET,
497                       SO_SNDBUF,
498                       &obj->options.tcp_sndbuf,
499                       sizeof(obj->options.tcp_sndbuf)) != 0) {
500            LOG(log_error, logtype_dsi, "afp_over_dsi: setsockopt(SO_SNDBUF): %s", strerror(errno));
501        }
502    }
503
504    /* set TCP_NODELAY */
505    int flag = 1;
506    setsockopt(dsi->socket, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
507
508    /* get stuck here until the end */
509    while (1) {
510        if (sigsetjmp(recon_jmp, 1) != 0)
511            /* returning from SIGALARM handler for a primary reconnect */
512            continue;
513
514        /* Blocking read on the network socket */
515        cmd = dsi_stream_receive(dsi);
516
517        if (cmd == 0) {
518            /* cmd == 0 is the error condition */
519            if (dsi->flags & DSI_RECONSOCKET) {
520                /* we just got a reconnect so we immediately try again to receive on the new fd */
521                dsi->flags &= ~DSI_RECONSOCKET;
522                continue;
523            }
524
525            /* the client sometimes logs out (afp_logout) but doesn't close the DSI session */
526            if (dsi->flags & DSI_AFP_LOGGED_OUT) {
527                LOG(log_note, logtype_afpd, "afp_over_dsi: client logged out, terminating DSI session");
528                afp_dsi_close(obj);
529                exit(0);
530            }
531
532#if 0
533            /*  got ECONNRESET in read from client => exit*/
534            if (dsi->flags & DSI_GOT_ECONNRESET) {
535                LOG(log_note, logtype_afpd, "afp_over_dsi: client connection reset");
536                afp_dsi_close(obj);
537                exit(0);
538            }
539#endif
540
541            if (dsi->flags & DSI_RECONINPROG) {
542                LOG(log_note, logtype_afpd, "afp_over_dsi: failed reconnect");
543                afp_dsi_close(obj);
544                exit(0);
545            }
546
547            /* Some error on the client connection, enter disconnected state */
548            if (dsi_disconnect(dsi) != 0)
549                afp_dsi_die(EXITERR_CLNT);
550
551            while (dsi->flags & DSI_DISCONNECTED)
552                pause(); /* gets interrupted by SIGALARM or SIGURG tickle */
553            continue; /* continue receiving until disconnect timer expires
554                       * or a primary reconnect succeeds  */
555        }
556
557        if (!(dsi->flags & DSI_EXTSLEEP) && (dsi->flags & DSI_SLEEPING)) {
558            LOG(log_debug, logtype_afpd, "afp_over_dsi: got data, ending normal sleep");
559            dsi->flags &= ~DSI_SLEEPING;
560            dsi->tickle = 0;
561        }
562
563        if (reload_request) {
564            reload_request = 0;
565            load_volumes(AFPobj);
566        }
567
568        /* The first SIGINT enables debugging, the next restores the config */
569        if (debug_request) {
570            static int debugging = 0;
571            debug_request = 0;
572
573            dircache_dump();
574            uuidcache_dump();
575
576            if (debugging) {
577                if (obj->options.logconfig)
578                    setuplog(obj->options.logconfig);
579                else
580                    setuplog("default log_note");
581                debugging = 0;
582            } else {
583                char logstr[50];
584                debugging = 1;
585                sprintf(logstr, "default log_maxdebug /tmp/afpd.%u.XXXXXX", getpid());
586                setuplog(logstr);
587            }
588        }
589
590
591        dsi->flags |= DSI_DATA;
592        dsi->tickle = 0;
593
594        switch(cmd) {
595
596        case DSIFUNC_CLOSE:
597            LOG(log_debug, logtype_afpd, "DSI: close session request");
598            afp_dsi_close(obj);
599            LOG(log_note, logtype_afpd, "done");
600            exit(0);
601
602        case DSIFUNC_TICKLE:
603            dsi->flags &= ~DSI_DATA; /* thats no data in the sense we use it in alarm_handler */
604            LOG(log_debug, logtype_afpd, "DSI: client tickle");
605            /* timer is not every 30 seconds anymore, so we don't get killed on the client side. */
606            if ((dsi->flags & DSI_DIE))
607                dsi_tickle(dsi);
608            break;
609
610        case DSIFUNC_CMD:
611#ifdef AFS
612            if ( writtenfork ) {
613                if ( flushfork( writtenfork ) < 0 ) {
614                    LOG(log_error, logtype_afpd, "main flushfork: %s", strerror(errno) );
615                }
616                writtenfork = NULL;
617            }
618#endif /* AFS */
619
620            function = (u_char) dsi->commands[0];
621
622            /* AFP replay cache */
623            rc_idx = dsi->clientID % REPLAYCACHE_SIZE;
624            LOG(log_debug, logtype_dsi, "DSI request ID: %u", dsi->clientID);
625
626            if (replaycache[rc_idx].DSIreqID == dsi->clientID
627                && replaycache[rc_idx].AFPcommand == function) {
628                LOG(log_note, logtype_afpd, "AFP Replay Cache match: id: %u / cmd: %s",
629                    dsi->clientID, AfpNum2name(function));
630                err = replaycache[rc_idx].result;
631            /* AFP replay cache end */
632            } else {
633                /* send off an afp command. in a couple cases, we take advantage
634                 * of the fact that we're a stream-based protocol. */
635                if (afp_switch[function]) {
636                    dsi->datalen = DSI_DATASIZ;
637                    dsi->flags |= DSI_RUNNING;
638
639                    LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
640
641                    err = (*afp_switch[function])(obj,
642                                                  (char *)&dsi->commands, dsi->cmdlen,
643                                                  (char *)&dsi->data, &dsi->datalen);
644
645                    LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
646                        AfpNum2name(function), AfpErr2name(err));
647
648                    dir_free_invalid_q();
649
650#ifdef FORCE_UIDGID
651                    /* bring everything back to old euid, egid */
652                    if (obj->force_uid)
653                        restore_uidgid ( &obj->uidgid );
654#endif /* FORCE_UIDGID */
655                    dsi->flags &= ~DSI_RUNNING;
656
657                    /* Add result to the AFP replay cache */
658                    replaycache[rc_idx].DSIreqID = dsi->clientID;
659                    replaycache[rc_idx].AFPcommand = function;
660                    replaycache[rc_idx].result = err;
661                } else {
662                    LOG(log_error, logtype_afpd, "bad function %X", function);
663                    dsi->datalen = 0;
664                    err = AFPERR_NOOP;
665                }
666            }
667
668            /* single shot toggle that gets set by dsi_readinit. */
669            if (dsi->flags & DSI_NOREPLY) {
670                dsi->flags &= ~DSI_NOREPLY;
671                break;
672            } else if (!dsi_cmdreply(dsi, err)) {
673                LOG(log_error, logtype_afpd, "dsi_cmdreply(%d): %s", dsi->socket, strerror(errno) );
674                if (dsi_disconnect(dsi) != 0)
675                    afp_dsi_die(EXITERR_CLNT);
676            }
677            break;
678
679        case DSIFUNC_WRITE: /* FPWrite and FPAddIcon */
680            function = (u_char) dsi->commands[0];
681            if ( afp_switch[ function ] != NULL ) {
682                dsi->datalen = DSI_DATASIZ;
683                dsi->flags |= DSI_RUNNING;
684
685                LOG(log_debug, logtype_afpd, "<== Start AFP command: %s", AfpNum2name(function));
686
687                err = (*afp_switch[function])(obj,
688                                              (char *)&dsi->commands, dsi->cmdlen,
689                                              (char *)&dsi->data, &dsi->datalen);
690
691                LOG(log_debug, logtype_afpd, "==> Finished AFP command: %s -> %s",
692                    AfpNum2name(function), AfpErr2name(err));
693
694#ifdef WOL
695               time(&now);
696			   if(difftime(now,last_wol)>= WOL_INTERVAL)
697               {
698                    system("wol");
699                    last_wol=now;
700                }
701
702#endif
703                dsi->flags &= ~DSI_RUNNING;
704#ifdef FORCE_UIDGID
705            	/* bring everything back to old euid, egid */
706		if (obj->force_uid)
707            	    restore_uidgid ( &obj->uidgid );
708#endif /* FORCE_UIDGID */
709            } else {
710                LOG(log_error, logtype_afpd, "(write) bad function %x", function);
711                dsi->datalen = 0;
712                err = AFPERR_NOOP;
713            }
714
715            if (!dsi_wrtreply(dsi, err)) {
716                LOG(log_error, logtype_afpd, "dsi_wrtreply: %s", strerror(errno) );
717                if (dsi_disconnect(dsi) != 0)
718                    afp_dsi_die(EXITERR_CLNT);
719            }
720            break;
721
722        case DSIFUNC_ATTN: /* attention replies */
723            break;
724
725            /* error. this usually implies a mismatch of some kind
726             * between server and client. if things are correct,
727             * we need to flush the rest of the packet if necessary. */
728        default:
729            LOG(log_info, logtype_afpd,"afp_dsi: spurious command %d", cmd);
730            dsi_writeinit(dsi, dsi->data, DSI_DATASIZ);
731            dsi_writeflush(dsi);
732            break;
733        }
734        pending_request(dsi);
735
736        fce_pending_events(obj);
737    }
738
739    /* error */
740    afp_dsi_die(EXITERR_CLNT);
741}
742