📄 protocol.c
字号:
/* * Request is still waiting for more data off of the network. * Setup to receive another pkt, and wait for the recv event * to occur. */ case PA_CONTPEND: (*p->continuation)(p->datap, pkt, p->security_handle); /* FALLTHROUGH */ case PA_PENDING: proto_debug(1, _("protocol: state_machine: p %p state %s: timeout %d\n"), p, pstate2str(p->state), (int)p->timeout); /* * Get the security layer to register a receive event for this * security handle on our behalf. Have it timeout in p->timeout * seconds. */ security_recvpkt(p->security_handle, recvpkt_callback, p, (int)p->timeout); return; /* * Request has moved to another state. Loop and run it again. */ case PA_CONTINUE: assert(p->state != curstate); proto_debug(1, _("protocol: state_machine: p %p: moved from %s to %s\n"), p, pstate2str(curstate), pstate2str(p->state)); continue; /* * Request has failed in some way locally. The security_handle will * contain an appropriate error message via security_geterror(). Set * pkt to NULL to indicate failure to the callback, and then * fall through to the common finish code. * * Note that remote failures finish via PA_FINISH, because they did * complete successfully locally. */ case PA_ABORT: pkt = NULL; /* FALLTHROUGH */ /* * Request has completed successfully. * Free up resources the request has used, call the continuation * function specified by the caller and quit. */ case PA_FINISH: (*p->continuation)(p->datap, pkt, p->security_handle); security_close(p->security_handle); amfree(p->hostname); amfree(p->req.body); amfree(p); return; default: assert(0); break; /* in case asserts are turned off */ } /*NOTREACHED*/ } /*NOTREACHED*/}/* * The request send state. Here, the packet is actually transmitted * across the network. After setting up timeouts, the request * moves to the acknowledgement wait state. We return from the state * machine at this point, and let the request be received from the network. */static p_action_ts_sendreq( proto_t * p, p_action_t action, pkt_t * pkt){ assert(p != NULL); (void)action; /* Quiet unused parameter warning */ (void)pkt; /* Quiet unused parameter warning */ if (security_sendpkt(p->security_handle, &p->req) < 0) { /* XXX should retry */ security_seterror(p->security_handle, _("error sending REQ: %s"), security_geterror(p->security_handle)); return (PA_ABORT); } /* * Remember when this request was first sent */ p->curtime = CURTIME; /* * Move to the ackwait state */ p->state = s_ackwait; p->timeout = ACK_WAIT; return (PA_PENDING);}/* * The acknowledge wait state. We can enter here two ways: * * - the caller has received a packet, located the request for * that packet, and called us with an action of PA_RCVDATA. * * - the caller has determined that a request has timed out, * and has called us with PA_TIMEOUT. * * Here we process the acknowledgment, which usually means that * the client has agreed to our request and is working on it. * It will later send a reply when finished. */static p_action_ts_ackwait( proto_t * p, p_action_t action, pkt_t * pkt){ assert(p != NULL); /* * The timeout case. If our retry count has gone to zero * fail this request. Otherwise, move to the send state * to retry the request. */ if (action == PA_TIMEOUT) { assert(pkt == NULL); if (--p->reqtries == 0) { security_seterror(p->security_handle, _("timeout waiting for ACK")); return (PA_ABORT); } p->state = s_sendreq; return (PA_CONTINUE); } assert(action == PA_RCVDATA); assert(pkt != NULL); /* * The packet-received state. Determine what kind of * packet we received, and act based on the reply type. */ switch (pkt->type) { /* * Received an ACK. Everything's good. The client is * now working on the request. We queue up again and * wait for the reply. */ case P_ACK: p->state = s_repwait; p->timeout = p->repwait; return (PA_PENDING); /* * Received a NAK. The request failed, so free up the * resources associated with it and return. * * This should NOT return PA_ABORT because it is not a local failure. */ case P_NAK: return (PA_FINISH); /* * The client skipped the ACK, and replied right away. * Move to the reply state to handle it. */ case P_REP: case P_PREP: p->state = s_repwait; return (PA_CONTINUE); /* * Unexpected packet. Requeue this request and hope * we get what we want later. */ default: return (PA_PENDING); }}/* * The reply wait state. We enter here much like we do with s_ackwait. */static p_action_ts_repwait( proto_t * p, p_action_t action, pkt_t * pkt){ pkt_t ack; /* * Timeout waiting for a reply. */ if (action == PA_TIMEOUT) { assert(pkt == NULL); /* * If we've blown our timeout limit, free up this packet and * return. */ if (p->resettries == 0 || DROP_DEAD_TIME(p->origtime)) { security_seterror(p->security_handle, _("timeout waiting for REP")); return (PA_ABORT); } /* * We still have some tries left. Resend the request. */ p->resettries--; p->state = s_sendreq; p->reqtries = getconf_int(CNF_REQ_TRIES); return (PA_CONTINUE); } assert(action == PA_RCVDATA); /* Finish if we get a NAK */ if (pkt->type == P_NAK) return (PA_FINISH); /* * We've received some data. If we didn't get a reply, * requeue the packet and retry. Otherwise, acknowledge * the reply, cleanup this packet, and return. */ if (pkt->type != P_REP && pkt->type != P_PREP) return (PA_PENDING); if(pkt->type == P_REP) { pkt_init_empty(&ack, P_ACK); if (security_sendpkt(p->security_handle, &ack) < 0) { /* XXX should retry */ amfree(ack.body); security_seterror(p->security_handle, _("error sending ACK: %s"), security_geterror(p->security_handle)); return (PA_ABORT); } amfree(ack.body); return (PA_FINISH); } else if(pkt->type == P_PREP) { p->timeout = p->repwait - CURTIME + p->curtime + 1; return (PA_CONTPEND); } /* should never go here, shut up compiler warning */ return (PA_FINISH);}/* * event callback that receives a packet */static voidrecvpkt_callback( void * cookie, pkt_t * pkt, security_status_t status){ proto_t *p = cookie; assert(p != NULL); switch (status) { case S_OK: state_machine(p, PA_RCVDATA, pkt); break; case S_TIMEOUT: state_machine(p, PA_TIMEOUT, NULL); break; case S_ERROR: state_machine(p, PA_ABORT, NULL); break; default: assert(0); break; }}/* * -------------- * Misc functions *//* * Convert a pstate_t into a printable form. */static const char *pstate2str( pstate_t pstate){ static const struct { pstate_t type; const char name[12]; } pstates[] = {#define X(s) { s, stringize(s) } X(s_sendreq), X(s_ackwait), X(s_repwait),#undef X }; int i; for (i = 0; i < ASIZE(pstates); i++) if (pstate == pstates[i].type) return (pstates[i].name); return (_("BOGUS PSTATE"));}/* * Convert an p_action_t into a printable form */static const char *action2str( p_action_t action){ static const struct { p_action_t type; const char name[12]; } actions[] = {#define X(s) { s, stringize(s) } X(PA_START), X(PA_TIMEOUT), X(PA_ERROR), X(PA_RCVDATA), X(PA_CONTPEND), X(PA_PENDING), X(PA_CONTINUE), X(PA_FINISH), X(PA_ABORT),#undef X }; int i; for (i = 0; i < ASIZE(actions); i++) if (action == actions[i].type) return (actions[i].name); return (_("BOGUS ACTION"));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -